Is it possible to compile a single C# code file with the .NET Core Roslyn compiler?
Yes, it is possible to compile a single file with csc or vbc compilers in .NET Core.
To invoke the Roslyn compiler directly it is necessary to use the command line driver csc.{exe|dll} and since Roslyn in contrast to the old csc.exe does not reference mscorlib.dll implicitly it is necessary to pass a reference to the required dependencies, i.e. System.Runtime
and System.Private.CoreLib
libraries and any other required references. The following listing shows how to compile the following Hello, World! program.
using System;
namespace HelloWorld
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello, World!");
}
}
}
Using WSL with Ubuntu 16.04 (Xenial Xerus) and dotnet-sdk-2.0.0 installed:
time dotnet /usr/share/dotnet/sdk/2.0.0/Roslyn/csc.exe -r:/usr/share/dotnet/shared/Microsoft.NETCore.App/2.0.0/System.Private.CoreLib.dll -r:/usr/share/dotnet/shared/Microsoft.NETCore.App/2.0.0/System.Console.dll -r:/usr/share/dotnet/shared/Microsoft.NETCore.App/2.0.0/System.Runtime.dll HelloWorld.cs
Microsoft (R) Visual C# Compiler version 2.3.2.61921 (ad0efbb6)
Copyright (C) Microsoft Corporation. All rights reserved.
real 0m0.890s
user 0m0.641s
sys 0m0.250s
ls -li
total 4
4785074604720852 -rw-rw-rw- 1 developer developer 178 Dec 7 15:07 HelloWorld.cs
11821949022487213 -rw-rw-rw- 1 developer developer 4096 Dec 7 15:13 HelloWorld.exe
The required dependencies, which are passed to the compiler, are different on different platforms, i.e. on Windows it is enough to pass System.Runtime.dll
and System.Console.dll
while on Ubuntu 16.04 it is necessary to pass in addition System.Private.CoreLib.dll
. Different SDK versions will have Roslyn and command line drivers located in different places - the SDK layout changes between versions - and the newest 2.2.2 SDK ships with csc.dll
and vbc.dll
instead of csc.exe
and vbc.exe
. Therefore, before using this method it is necessary to check your SDK layout.
Detailed explanation
The Roslyn compiler was designed in a bit different way than the previously used csc.exe
and vbc.exe
compilers. First of all, Roslyn is written in C# and VB.NET and is a managed .NET application. On Windows it used mainly as a common service running in a server process VBCSCompiler.exe
(.dll). However, Roslyn ships with managed command line drivers, csc.exe
and vbc.exe
(the latest .NET SDK versions ship with csc.dll
and vbc.dll
) which can be used to compile source files directly from the command line. Anyway, it is exactly what the build system in .NET does, invoking Roslyn via the command line. Running a simple dotnet csc.exe -help
command will print usage information which will guide in using the compiler directly from the command line (see the last listing).
The major difference between old native compilers and Roslyn is due to the fact that the latter is a managed application is a startup time. Roslyn, even after being compiled to R2R native assemblies (Ready To Run
), would need to start by loading the whole .NET framework, initializing it and then loading Roslyn assemblies and starting the compilation process. It is always a bit slower than running the native compiler, however, as can be seen from above timings, not that much slower.
There was a new documentation article added to the corefx
repository describing Advanced scenario - Build and run application code with csc/vbc and CoreRun. Anyone interested can use it as a guideline how to work at the low level of .NET Core.
Microsoft (R) Visual C# Compiler version 2.3.2.61921 (ad0efbb6)
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, 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
/warn:<n> Set warning level (0-4) (Short form: /w)
/nowarn:<warn list> Disable specific warning messages
/ruleset:<file> Specify a ruleset file that disables specific
diagnostics.
/errorlog:<file> Specify a file to log all compiler and analyzer
diagnostics.
/reportanalyzer Report additional analyzer information, such as
execution time.
- LANGUAGE -
/checked[+|-] Generate overflow checks
/unsafe[+|-] Allow 'unsafe' code
/define:<symbol list> Define conditional compilation symbol(s) (Short
form: /d)
/langversion:<string> Specify language version mode: ISO-1, ISO-2, 3,
4, 5, 6, 7, 7.1, Default, or Latest
- 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 (default) or SHA256.
/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.
/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
The accepted answer refers to using System.Private.CoreLib.dll
which is a runtime assembly and is not recommended. From C# compiler developer's comments:
Attempting to use runtime assemblies as compile references is not supported and frequently breaks do to the structure of the runtime assemblies
Instead, reference assemblies should be used. Reference assemblies are fetched from NuGet during dotnet build
and a full csc
invocation can be seen when running the dotnet
CLI with increased verbosity (dotnet build --verbosity normal
). One might see references to assemblies like System.Runtime.dll
and System.Console.dll
from microsoft.netcore.app
NuGet package.
However, for a simple single file Hello, World! compilation, one can reference netstandard.dll
which for .NET Core 2.2 exists under <installation-directory>/sdk/2.2.203/ref/netstandard.dll
.
Note that in order to run the resulting executable with dotnet HelloWorld.exe
a corresponding HelloWorld.runtimeconfig.json
has to be created, containing the targeting .NET Core runtime version. We will simplify it by creating a common runtimeconfig for console (NETCoreApp) apps, and an accompanying alias csc_run
.
Add the following in your ~/.profile
:
#!/usr/bin/env sh
# IMPORTANT: make sure dotnet is present in PATH before the next lines
# prepare csc alias
DOTNETDIR=$(dirname $(dirname $(dotnet --info | grep "Base Path" | cut -d' ' -f 6)))
CSCPATH=$(find $DOTNETDIR -name csc.dll -print | sort | tail -n1)
NETSTANDARDPATH=$(find $DOTNETDIR -path *sdk/*/ref/netstandard.dll ! -path *NuGetFallback* -print | sort | tail -n1)
alias csc='dotnet $CSCPATH /r:$NETSTANDARDPATH '
# prepare csc_run alias
if [ ! -w "$DOTNETDIR" ]; then
mkdir -p $HOME/.dotnet
DOTNETDIR=$HOME/.dotnet
fi
DOTNETCSCRUNTIMECONFIG=$DOTNETDIR/csc-console-apps.runtimeconfig.json
alias csc_run='dotnet exec --runtimeconfig $DOTNETCSCRUNTIMECONFIG '
if [ ! -f $DOTNETCSCRUNTIMECONFIG ]; then
DOTNETRUNTIMEVERSION=$(dotnet --list-runtimes |
grep Microsoft\.NETCore\.App | tail -1 | cut -d' ' -f2)
cat << EOF > $DOTNETCSCRUNTIMECONFIG
{
"runtimeOptions": {
"framework": {
"name": "Microsoft.NETCore.App",
"version": "$DOTNETRUNTIMEVERSION"
}
}
}
EOF
fi
Exit and start shell to reload profile (or source it . ~/.profile
if you don't want to leave the current session).
Usage:
cat << EOF > ./Program.cs
class Program
{
static void Main() => System.Console.WriteLine("Hello World!");
}
EOF
csc -out:hwapp.exe Program.cs
csc_run hwapp.exe
# Hello World!
The compiler can be directly invoked using
$ /usr/local/share/dotnet/sdk/2.0.0/Roslyn/RunCsc.sh
However, this particular command may not be very helpful without a supporting project infrastructure because you'd need to pass in all .NET Core or .NET Standard reference assemblies in manually, which is normally handled by the SDK and NuGet. You'll get errors like this:
$ /usr/local/share/dotnet/sdk/2.0.0/Roslyn/RunCsc.sh Program.cs
Microsoft (R) Visual C# Compiler version 2.3.2.61921 (ad0efbb6)
Copyright (C) Microsoft Corporation. All rights reserved.
Program.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?)
Program.cs(5,11): error CS0518: Predefined type 'System.Object' is not defined or imported
Program.cs(7,26): error CS0518: Predefined type 'System.String' is not defined or imported
Program.cs(7,16): error CS0518: Predefined type 'System.Void' is not defined or imported