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)
13483정성태12/14/20236749닷넷: 2184. C# - 하나의 resource 파일을 여러 프로그램에서 (AOT 시에도) 사용하는 방법파일 다운로드1
13482정성태12/13/20237858닷넷: 2183. C# - eFriend Expert OCX 예제를 .NET Core/5+ Console App에서 사용하는 방법 [2]파일 다운로드1
13481정성태12/13/20236746개발 환경 구성: 693. msbuild - .NET Core/5+ 프로젝트에서 resgen을 이용한 리소스 파일 생성 방법파일 다운로드1
13480정성태12/12/20237832개발 환경 구성: 692. Windows WSL 2 + Chrome 웹 브라우저 설치
13479정성태12/11/20236543개발 환경 구성: 691. WSL 2 (Ubuntu) + nginx 환경 설정
13477정성태12/8/20237220닷넷: 2182. C# - .NET 7부터 추가된 Int128, UInt128 [1]파일 다운로드1
13476정성태12/8/20236711닷넷: 2181. C# - .NET 8 JsonStringEnumConverter의 AOT를 위한 개선파일 다운로드1
13475정성태12/7/20236664닷넷: 2180. .NET 8 - 함수 포인터에 대한 Reflection 정보 조회파일 다운로드1
13474정성태12/6/20236449개발 환경 구성: 690. 닷넷 코어/5+ 버전의 ilasm/ildasm 실행 파일 구하는 방법 - 두 번째 이야기
13473정성태12/5/20236835닷넷: 2179. C# - 값 형식(Blittable)을 메모리 복사를 이용해 바이트 배열로 직렬화/역직렬화파일 다운로드1
13472정성태12/4/20236529C/C++: 164. Visual C++ - InterlockedCompareExchange128 사용 방법
13471정성태12/4/20236887Copilot - To enable GitHub Copilot, authorize this extension using GitHub's device flow
13470정성태12/2/20237364닷넷: 2178. C# - .NET 8부터 COM Interop에 대한 자동 소스 코드 생성 도입 [1]파일 다운로드1
13469정성태12/1/20237095닷넷: 2177. C# - (Interop DLL 없이) CoClass를 이용한 COM 개체 생성 방법파일 다운로드1
13468정성태12/1/20236330닷넷: 2176. C# - .NET Core/5+부터 달라진 RCW(Runtime Callable Wrapper) 대응 방식파일 다운로드1
13467정성태11/30/20237011오류 유형: 882. C# - Unhandled exception. System.Runtime.InteropServices.COMException (0x800080A5)파일 다운로드1
13466정성태11/29/20237072닷넷: 2175. C# - DllImport 메서드의 AOT 지원을 위한 LibraryImport 옵션
13465정성태11/28/20236704개발 환경 구성: 689. MSBuild - CopyToOutputDirectory가 "dotnet publish" 시에는 적용되지 않는 문제파일 다운로드1
13464정성태11/28/20236709닷넷: 2174. C# - .NET 7부터 UnmanagedCallersOnly 함수 export 기능을 AOT 빌드에 통합파일 다운로드1
13463정성태11/27/20236546오류 유형: 881. Visual Studio - NU1605: Warning As Error: Detected package downgrade
13462정성태11/27/20236921오류 유형: 880. Visual Studio - error CS0246: The type or namespace name '...' could not be found
13461정성태11/26/20236520닷넷: 2173. .NET Core 3/5+ 기반의 COM Server를 registry 등록 없이 사용하는 방법파일 다운로드1
13460정성태11/26/20236717닷넷: 2172. .NET 6+ 기반의 COM Server 내에 Type Library를 내장하는 방법파일 다운로드1
13459정성태11/26/20236528닷넷: 2171. .NET Core 3/5+ 기반의 COM Server를 기존의 regasm처럼 등록하는 방법파일 다운로드1
13458정성태11/26/20236642닷넷: 2170. .NET Core/5+ 기반의 COM Server를 tlb 파일을 생성하는 방법(tlbexp)
13457정성태11/25/20236459VS.NET IDE: 187. Visual Studio - 16.9 버전부터 추가된 "Display inline type hints" 옵션
1  2  3  4  5  6  7  8  9  10  11  12  13  14  [15]  ...