Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 

.NET Core/5+ 환경에서 (프로젝트가 아닌) C# 코드 파일을 입력으로 컴파일하는 방법 - 두 번째 이야기

예전에 이에 대해 한 번 설명했는데요,

.NET Core 환경에서 (프로젝트가 아닌) C# 코드 파일을 입력으로 컴파일하는 방법
; https://www.sysnet.pe.kr/2/0/12034

이후 .NET Core 2.2.2 SDK부터 배포되는 Roslyn 컴파일러에는 실행 파일 유형의 csc.exe가 누락되고 csc.dll로만 배포가 돼 위의 내용을 실습할 수 없습니다.

물론, csc.dll은 기존 csc.exe의 DLL 버전이므로 다음과 같은 식으로 직접 실행하는 것도 가능합니다.

// .NET 6 SDK가 설치된 경우 (버전 6.0.302)

c:\temp> dotnet "C:\Program Files\dotnet\sdk\6.0.302\Roslyn\bincore\csc.dll"
Microsoft (R) Visual C# Compiler version 4.2.0-4.22220.5 (432d17a8)
Copyright (C) Microsoft Corporation. All rights reserved.

warning CS2008: No source files specified.
error CS1562: Outputs without source must have the /out option specified

테스트를 위해 예제 C# 파일을 하나 만들고,

// c:\temp\test.cs 파일로 저장되었다고 가정

using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World");
        }
    }
}

빌드해 볼까요? ^^

c:\temp> dotnet "C:\Program Files\dotnet\sdk\6.0.302\Roslyn\bincore\csc.dll" test.cs
Microsoft (R) Visual C# Compiler version 4.2.0-4.22220.5 (432d17a8)
Copyright (C) Microsoft Corporation. All rights reserved.

test.cs(1,7): error CS0246: The type or namespace name 'System' could not be found (are you missing a using directive or an assembly reference?)
test.cs(5,11): error CS0518: Predefined type 'System.Object' is not defined or imported
test.cs(7,26): error CS0518: Predefined type 'System.String' is not defined or imported
test.cs(7,16): error CS0518: Predefined type 'System.Void' is not defined or imported

아쉽게도, 가장 기본적인 System.Object 타입을 정의한 어셈블리조차도 참조를 해주지 않으므로 직접 명령행에 해당 DLL을 함께 전달해야 합니다.

// 옵션 추가
// -r:"C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.7\System.Private.CoreLib.dll"

c:\temp> dotnet "C:\Program Files\dotnet\sdk\6.0.302\Roslyn\bincore\csc.dll" test.cs -r:"C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.7\System.Private.CoreLib.dll"
Microsoft (R) Visual C# Compiler version 4.2.0-4.22220.5 (432d17a8)
Copyright (C) Microsoft Corporation. All rights reserved.

test.cs(10,13): error CS0234: The type or namespace name 'Console' does not exist in the namespace 'System' (are you missing an assembly reference?)

음... Console 타입 역시 System.Console.dll로 분리돼 있으므로 이렇게 한 번 더 참조 추가를 해야 합니다.

// 옵션 추가
// -r:"C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.7\System.Console.dll"

c:\temp> dotnet "C:\Program Files\dotnet\sdk\6.0.302\Roslyn\bincore\csc.dll" test.cs -r:"C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.7\System.Private.CoreLib.dll" -r:"C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.7\System.Console.dll"
Microsoft (R) Visual C# Compiler version 4.2.0-4.22220.5 (432d17a8)
Copyright (C) Microsoft Corporation. All rights reserved.

test.cs(10,28): error CS0012: The type 'Object' is defined in an assembly that is not referenced. You must add a reference to assembly 'System.Runtime, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.
test.cs(10,13): error CS0012: The type 'Decimal' is defined in an assembly that is not referenced. You must add a reference to assembly 'System.Runtime, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.

하지만 ^^; 그래도 오류가 나는군요. 근데 오류 메시지가 잘 이해가 안 됩니다. System.Object의 실제 구현은 System.Private.CoreLib.dll에 있는 것이 맞고, System.Runtime의 경우에는 TypeForwardedTo를 이용해 System.Private.CoreLib.dll의 구현체를 가리키는 형태입니다.

따라서 원칙대로라면 System.Private.CoreLib.dll을 직접 참조하면 사실상 System.Runtime.dll의 참조는 필요하지 않습니다. (혹시 이에 대한 규칙을 아시는 분은 덧글 부탁드립니다. ^^)

암튼, 오류 메시지가 그러하니... 참조 추가를 하면 이제 컴파일이 잘 됩니다. ^^

// 옵션 추가
// -r:"C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.7\System.Runtime.dll"

c:\temp> dotnet "C:\Program Files\dotnet\sdk\6.0.302\Roslyn\bincore\csc.dll" test.cs -r:"C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.7\System.Private.CoreLib.dll" -r:"C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.7\System.Console.dll" -r:"C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.7\System.Runtime.dll"
Microsoft (R) Visual C# Compiler version 4.2.0-4.22220.5 (432d17a8)
Copyright (C) Microsoft Corporation. All rights reserved.




자... 이렇게 해서 test.exe가 생성되었고, 이를 실행해 보면,

c:\temp> test.exe

Unhandled Exception: System.IO.FileNotFoundException: Could not load file or assembly 'System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e' or one of its dependencies. The system cannot find the file specified.

System.Private.CoreLib.dll이 같은 디렉터리에 없어 오류가 발생합니다. 그래서 해당 DLL을 복사해 둔 후 실행하면,

c:\temp> test.exe

Unhandled Exception: System.TypeLoadException: Could not load type 'System.Object' from assembly 'System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e' because the parent does not exist.

이번엔 파일이 아닌, Type을 로드할 수 없다는 오류 메시지가 나옵니다. 딱히 더 해볼 것이 없군요. ^^;

그럼, dotnet 실행 파일을 경유해 실행해 볼까요?

c:\temp> dotnet test.exe
A fatal error was encountered. The library 'hostpolicy.dll' required to execute the application was not found in 'c:\temp\'.
Failed to run as a self-contained app.
  - The application was run as a self-contained app because 'c:\temp\test.runtimeconfig.json' was not found.
  - If this should be a framework-dependent app, add the 'c:\temp\test.runtimeconfig.json' file and specify the appropriate framework.

test.runtimeconfig.json 파일이 없다고 하는데, 이것만 다음의 내용으로 test.exe와 동일한 폴더에 "test.runtimeconfig.json" 파일명으로 넣어주면,

{
  "runtimeOptions": {
    "tfm": "netcoreapp6.0",
    "framework": {
      "name": "Microsoft.NETCore.App",
      "version": "6.0.0"
    }
  }
}

이제서야 정상적으로 프로그램이 실행됩니다.

c:\temp> dir /b
test.cs
test.exe
test.runtimeconfig.json

c:\temp> dotnet test.exe
Hello World




재미있는 건, csc.dll이 생성한 EXE 파일의 "Runtime" 대상이 ".NET Framework 4"라는 점입니다. 즉, 위에서 dotnet을 경유해 실행했을 때 ".NET Framework 4" 바이너리였어도 정상적으로 실행했던 것입니다.

바이너리의 Runtime 대상을 바꾸려면 C# 소스 코드 파일에서 TargetFrameworkAttribute 특성을 이용해 선택할 수 있습니다. 예를 들어 우리가 만든 예제의 경우 다음과 같이 추가하면 됩니다.

using System;
using System.Runtime.Versioning;

[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            // 문자열 출력
            System.Console.WriteLine("Hello World");
        }
    }
}




참고로, 아래는 csc.dll의 /help 출력 결과입니다.

c:\temp> dotnet "C:\Program Files\dotnet\sdk\6.0.302\Roslyn\bincore\csc.dll" /help
Microsoft (R) Visual C# Compiler version 4.2.0-4.22220.5 (432d17a8)
Copyright (C) Microsoft Corporation. All rights reserved.


                             Visual C# Compiler Options

                       - OUTPUT FILES -
-out:<file>                   Specify output file name (default: base name of file with main class or first file)
-target:exe                   Build a console executable (default) (Short form: -t:exe)
-target:winexe                Build a Windows executable (Short form: -t:winexe)
-target:library               Build a library (Short form: -t:library)
-target:module                Build a module that can be added to another assembly (Short form: -t:module)
-target:appcontainerexe       Build an Appcontainer executable (Short form: -t:appcontainerexe)
-target:winmdobj              Build a Windows Runtime intermediate file that is consumed by WinMDExp (Short form: -t:winmdobj)
-doc:<file>                   XML Documentation file to generate
-refout:<file>                Reference assembly output to generate
-platform:<string>            Limit which platforms this code can run on: x86, Itanium, x64, arm, arm64, anycpu32bitpreferred, or anycpu. The default is anycpu.

                       - INPUT FILES -
-recurse:<wildcard>           Include all files in the current directory and subdirectories according to the wildcard specifications
-reference:<alias>=<file>     Reference metadata from the specified assembly file using the given alias (Short form: -r)
-reference:<file list>        Reference metadata from the specified assembly files (Short form: -r)
-addmodule:<file list>        Link the specified modules into this assembly 
-link:<file list>             Embed metadata from the specified interop assembly files (Short form: -l)
-analyzer:<file list>         Run the analyzers from this assembly (Short form: -a)
-additionalfile:<file list>   Additional files that don't directly affect code generation but may be used by analyzers for producing errors or warnings.
-embed                        Embed all source files in the PDB.
-embed:<file list>            Embed specific files in the PDB.

                       - RESOURCES -
-win32res:<file>              Specify a Win32 resource file (.res)
-win32icon:<file>             Use this icon for the output
-win32manifest:<file>         Specify a Win32 manifest file (.xml)
-nowin32manifest              Do not include the default Win32 manifest
-resource:<resinfo>           Embed the specified resource (Short form: -res)
-linkresource:<resinfo>       Link the specified resource to this assembly (Short form: -linkres) Where the resinfo format is <file>[,<string name>[,public|private]]

                       - CODE GENERATION -
-debug[+|-]                   Emit debugging information
-debug:{full|pdbonly|portable|embedded}
                              Specify debugging type ('full' is default, 'portable' is a cross-platform format, 'embedded' is a cross-platform format embedded into the target .dll or .exe)
-optimize[+|-]                Enable optimizations (Short form: -o)
-deterministic                Produce a deterministic assembly (including module version GUID and timestamp)
-refonly                      Produce a reference assembly in place of the main output
-instrument:TestCoverage      Produce an assembly instrumented to collect coverage information
-sourcelink:<file>            Source link info to embed into PDB.

                       - ERRORS AND WARNINGS -
-warnaserror[+|-]             Report all warnings as errors
-warnaserror[+|-]:<warn list> Report specific warnings as errors (use "nullable" for all nullability warnings)
-warn:<n>                     Set warning level (0 or higher) (Short form: -w)
-nowarn:<warn list>           Disable specific warning messages (use "nullable" for all nullability warnings)
-ruleset:<file>               Specify a ruleset file that disables specific diagnostics.
-errorlog:<file>[,version=<sarif_version>]
                              Specify a file to log all compiler and analyzer diagnostics.
                              sarif_version:{1|2|2.1} Default is 1. 2 and 2.1 both mean SARIF version 2.1.0.
-reportanalyzer               Report additional analyzer information, such as execution time.
-skipanalyzers[+|-]           Skip execution of diagnostic analyzers.

                       - LANGUAGE -
-checked[+|-]                 Generate overflow checks
-unsafe[+|-]                  Allow 'unsafe' code
-define:<symbol list>         Define conditional compilation symbol(s) (Short form: -d)
-langversion:?                Display the allowed values for language version
-langversion:<string>         Specify language version such as
                              `latest` (latest version, including minor versions),
                              `default` (same as `latest`),
                              `latestmajor` (latest version, excluding minor versions),
                              `preview` (latest version, including features in unsupported preview),
                              or specific versions like `6` or `7.1`
-nullable[+|-]                Specify nullable context option enable|disable.
-nullable:{enable|disable|warnings|annotations}
                              Specify nullable context option enable|disable|warnings|annotations.

                       - SECURITY -
-delaysign[+|-]               Delay-sign the assembly using only the public portion of the strong name key
-publicsign[+|-]              Public-sign the assembly using only the public portion of the strong name key
-keyfile:<file>               Specify a strong name key file
-keycontainer:<string>        Specify a strong name key container
-highentropyva[+|-]           Enable high-entropy ASLR

                       - MISCELLANEOUS -
@<file>                       Read response file for more options
-help                         Display this usage message (Short form: -?)
-nologo                       Suppress compiler copyright message
-noconfig                     Do not auto include CSC.RSP file
-parallel[+|-]                Concurrent build.
-version                      Display the compiler version number and exit.

                       - ADVANCED -
-baseaddress:<address>        Base address for the library to be built
-checksumalgorithm:<alg>      Specify algorithm for calculating source file checksum stored in PDB. Supported values are: SHA1 or SHA256 (default).
-codepage:<n>                 Specify the codepage to use when opening source files
-utf8output                   Output compiler messages in UTF-8 encoding
-main:<type>                  Specify the type that contains the entry point (ignore all other possible entry points) (Short form: -m)
-fullpaths                    Compiler generates fully qualified paths
-filealign:<n>                Specify the alignment used for output file sections
-pathmap:<K1>=<V1>,<K2>=<V2>,...
                              Specify a mapping for source path names output by the compiler.
-pdb:<file>                   Specify debug information file name (default: output file name with .pdb extension)
-errorendlocation             Output line and column of the end location of each error
-preferreduilang              Specify the preferred output language name.
-nosdkpath                    Disable searching the default SDK path for standard library assemblies.
-nostdlib[+|-]                Do not reference standard library (mscorlib.dll)
-subsystemversion:<string>    Specify subsystem version of this assembly
-lib:<file list>              Specify additional directories to search in for references
-errorreport:<string>         Specify how to handle internal compiler errors: prompt, send, queue, or none. The default is queue.
-appconfig:<file>             Specify an application configuration file containing assembly binding settings
-moduleassemblyname:<string>  Name of the assembly which this module will be a part of
-modulename:<string>          Specify the name of the source module
-generatedfilesout:<dir>      Place files generated during compilation in the specified directory.




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]







[최초 등록일: ]
[최종 수정일: 7/23/2022]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 



2022-07-24 10시51분
비주얼 스튜디오에 포함된 Roslyn의 경우 (csc.dll이 아닌) csc.exe로 제공합니다. 예를 들어, 2022 버전을 설치하면 다음의 경로에서 찾을 수 있습니다.

[Community]
C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Current\Bin\Roslyn\csc.exe

[Enterprise]
C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Current\Bin\Roslyn\csc.exe

또는 예전에 설명한 nuget의 Microsoft.Net.Compilers도 여전히 csc.exe를 함께 배포합니다.

C# 6.0 이상의 소스 코드를 Visual Studio 설치 없이 명령행에서 컴파일하는 방법
; https://www.sysnet.pe.kr/2/0/11266
정성태

1  2  3  4  5  6  7  8  9  10  11  12  13  [14]  15  ...
NoWriterDateCnt.TitleFile(s)
13272정성태2/27/20234216오류 유형: 850. SSMS - mdf 파일을 Attach 시킬 때 Operating system error 5: "5(Access is denied.)" 에러
13271정성태2/25/20234149오류 유형: 849. Sql Server Configuration Manager가 시작 메뉴에 없는 경우
13270정성태2/24/20233758.NET Framework: 2098. dotnet build에 /p 옵션을 적용 시 유의점
13269정성태2/23/20234295스크립트: 46. 파이썬 - uvicorn의 콘솔 출력을 UDP로 전송
13268정성태2/22/20234849개발 환경 구성: 667. WSL 2 내부에서 열고 있는 UDP 서버를 호스트 측에서 접속하는 방법
13267정성태2/21/20234774.NET Framework: 2097. C# - 비동기 소켓 사용 시 메모리 해제가 finalizer 단계에서 발생하는 사례파일 다운로드1
13266정성태2/20/20234376오류 유형: 848. .NET Core/5+ - Process terminated. Couldn't find a valid ICU package installed on the system
13265정성태2/18/20234287.NET Framework: 2096. .NET Core/5+ - PublishSingleFile 유형에 대한 runtimeconfig.json 설정
13264정성태2/17/20235785스크립트: 45. 파이썬 - uvicorn 사용자 정의 Logger 작성
13263정성태2/16/20233918개발 환경 구성: 666. 최신 버전의 ilasm.exe/ildasm.exe 사용하는 방법
13262정성태2/15/20235006디버깅 기술: 191. dnSpy를 이용한 (소스 코드가 없는) 닷넷 응용 프로그램 디버깅 방법 [1]
13261정성태2/15/20234299Windows: 224. Visual Studio - 영문 폰트가 Fullwidth Latin Character로 바뀌는 문제
13260정성태2/14/20234083오류 유형: 847. ilasm.exe 컴파일 오류 - error : syntax error at token '-' in ... -inf
13259정성태2/14/20234209.NET Framework: 2095. C# - .NET5부터 도입된 CollectionsMarshal
13258정성태2/13/20234107오류 유형: 846. .NET Framework 4.8 Developer Pack 설치 실패 - 0x81f40001
13257정성태2/13/20234204.NET Framework: 2094. C# - Job에 Process 포함하는 방법 [1]파일 다운로드1
13256정성태2/10/20235056개발 환경 구성: 665. WSL 2의 네트워크 통신 방법 - 두 번째 이야기
13255정성태2/10/20234350오류 유형: 845. gihub - windows2022 이미지에서 .NET Framework 4.5.2 미만의 프로젝트에 대한 빌드 오류
13254정성태2/10/20234262Windows: 223. (WMI 쿼리를 위한) PowerShell 문자열 escape 처리
13253정성태2/9/20235040Windows: 222. C# - 다른 윈도우 프로그램이 실행되었음을 인식하는 방법파일 다운로드1
13252정성태2/9/20233852오류 유형: 844. ssh로 명령어 수행 시 멈춤 현상
13251정성태2/8/20234317스크립트: 44. 파이썬의 3가지 스레드 ID
13250정성태2/8/20236114오류 유형: 843. System.InvalidOperationException - Unable to configure HTTPS endpoint
13249정성태2/7/20234926오류 유형: 842. 리눅스 - You must wait longer to change your password
13248정성태2/7/20234047오류 유형: 841. 리눅스 - [사용자 계정] is not in the sudoers file. This incident will be reported.
13247정성태2/7/20234962VS.NET IDE: 180. Visual Studio - 닷넷 소스 코드 디버깅 중 "Decompile source code"가 동작하는 않는 문제
1  2  3  4  5  6  7  8  9  10  11  12  13  [14]  15  ...