¿Es posible compilar un solo archivo de código C # con el compilador .NET Core Roslyn?

59

En el antiguo .NET solíamos poder ejecutar el csccompilador para compilar un solo archivo .cs o varios archivos.

Con .NET Core tenemos dotnet buildque insiste en tener un archivo de proyecto adecuado. ¿Existe un compilador de línea de comando independiente que permita compilar archivos de código fuente sin tener un proyecto (y enumerar las dependencias referenciadas en la misma línea de comando)?

En Linux, cuando tengo el antiguo cscy el nuevo .NET Core instalado, obtengo estos tiempos:

[[email protected] test]# time dotnet build
Microsoft (R) Build Engine version 15.3.409.57025 for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.

  test -> /root/test/bin/Debug/netcoreapp2.0/test.dll

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:03.94

real    0m7.027s
user    0m5.714s
sys     0m0.838s

[[email protected] test]# time csc Program.cs
Microsoft (R) Visual C# Compiler version 2.3.0.61801 (3722bb71)
Copyright (C) Microsoft Corporation. All rights reserved.


real    0m0.613s
user    0m0.522s
sys     0m0.071s
[[email protected] test]#

Nota 7 segundos con .NET Core frente a varios cientos de milisegundos con el viejo cscpara el mismo archivo, Program.cs.

Me gustaría poder compilar tan rápido con .NET Core como solía hacerlo csc.

4
43

Sí, es posible compilar un solo archivo con compiladores csc o vbc en .NET Core.

Para invocar el compilador de Roslyn directamente es necesario utilizar el controlador de línea de comandos csc. {Exe | dll} y dado que Roslyn, en contraste con el antiguo csc.exe, no hace referencia implícitamente a mscorlib.dll, es necesario pasar una referencia al dependencias, es decir, System.Runtimey System.Private.CoreLiblas bibliotecas y las demás referencias requeridas. La siguiente lista muestra cómo compilar el siguiente archivo Hello, World! programa.

using System;

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

Usando WSL con Ubuntu 16.04 (Xenial Xerus) y dotnet-sdk-2.0.0 instalados:

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

Las dependencias requeridas, que se pasan al compilador, son diferentes en diferentes plataformas, es decir, en Windows es suficiente pasar System.Runtime.dlly System.Console.dllmientras que en Ubuntu 16.04 es necesario pasar además System.Private.CoreLib.dll. Las diferentes versiones del SDK tendrán Roslyn y los controladores de la línea de comandos ubicados en diferentes lugares (el diseño del SDK cambia entre las versiones) y el SDK 2.2.2 más nuevo se envía con csc.dlly en vbc.dlllugar de csc.exey vbc.exe. Por lo tanto, antes de usar este método, es necesario verificar el diseño de su SDK.

Explicación detallada

El compilador de Roslyn se diseñó de una manera un poco diferente a los compiladores csc.exey utilizados anteriormente vbc.exe. En primer lugar, Roslyn está escrito en C # y VB.NET y es una aplicación .NET administrada. En Windows, se utiliza principalmente como un servicio común que se ejecuta en un proceso de servidor VBCSCompiler.exe(.dll). Sin embargo, Roslyn se envía con controladores de línea de comandos administrados csc.exey vbc.exe(las últimas versiones del SDK de .NET incluyen csc.dlly vbc.dll) que se pueden usar para compilar archivos fuente directamente desde la línea de comandos. De todos modos, es exactamente lo que hace el sistema de compilación en .NET, invocando a Roslyn a través de la línea de comandos. Ejecutar un dotnet csc.exe -helpcomando simple imprimirá información de uso que le guiará en el uso del compilador directamente desde la línea de comando (vea la última lista).

La principal diferencia entre los antiguos compiladores nativos y Roslyn se debe al hecho de que este último es una aplicación administrada en un momento de inicio. Roslyn, incluso después de haber sido compilado en ensamblados nativos de R2R ( Ready To Run), necesitaría comenzar cargando todo el marco .NET, inicializándolo y luego cargando ensamblados Roslyn e iniciando el proceso de compilación. Sin embargo, siempre es un poco más lento que ejecutar el compilador nativo, como se puede ver en los tiempos anteriores, no mucho más lento.

Se agregó un nuevo artículo de documentación al corefxrepositorio que describe el escenario avanzado: compile y ejecute el código de la aplicación con csc / vbc y CoreRun . Cualquiera que esté interesado puede usarlo como una guía sobre cómo trabajar en el nivel bajo de .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
2
  • Hola Jack, pero ¿qué pasa si no quiero obtener un archivo dll o exe, pero quiero algo como * .o (C ++) o * .class (Java) - archivo compilado pero no empaquetado en dll o exe, es posible hacer ? Lo necesito, por ejemplo, para empaquetar algunos archivos compilados en el futuro en mi formato de archivo especial ... ¿Es posible? 16/07/19 a las 5:49
  • 1
    ¿El dotnet csc.exe -helpcomando es parte de una herramienta que debe instalarse?
    FilBot3
    10/08/20 a las 20:29
22
+50

La respuesta aceptada se refiere al uso System.Private.CoreLib.dllque es un ensamblado en tiempo de ejecución y no se recomienda. De los comentarios del desarrollador del compilador de C # :

Attempting to use runtime assemblies as compile references is not supported and frequently breaks do to the structure of the runtime assemblies

En su lugar, deben usarse ensamblados de referencia. Los ensamblados de referencia se obtienen de NuGet durante dotnet buildy cscse puede ver una invocación completa al ejecutar la dotnetCLI con mayor verbosidad ( dotnet build --verbosity normal). Se pueden ver referencias a ensamblados como System.Runtime.dlly System.Console.dlldesde el microsoft.netcore.apppaquete NuGet.

Sin embargo, para un solo archivo simple ¡Hola, mundo! compilación, se puede hacer referencia a netstandard.dllcuál para .NET Core 2.2 existe bajo <installation-directory>/sdk/2.2.203/ref/netstandard.dll.

Tenga en cuenta que para ejecutar el ejecutable resultante se debe crear dotnet HelloWorld.exeun archivo correspondiente HelloWorld.runtimeconfig.json, que contenga la versión de tiempo de ejecución de .NET Core de destino. Lo simplificaremos creando una configuración de tiempo de ejecución común para aplicaciones de consola (NETCoreApp) y un alias adjunto csc_run.

Agregue lo siguiente en su ~/.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

Salga e inicie el shell para recargar el perfil (o consígalo . ~/.profilesi no desea salir de la sesión actual).

Uso:

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!
4
  • 2
    Esto es útil, aunque no es necesariamente el caso de que el SDK esté en la versión 2.2.203 (o que el marco sea 2.2.4). Creo que lo siguiente funciona: DOTNETSDKVER=$(ls $DOTNETDIR/sdk | grep "^[0-9]" | sort -rn | head -1)aunque admito que es bastante hacky. 12/07/19 a las 20:21
  • 1
    Siempre se puede correr dotnet --infoy buscar Versiondebajo .NET Core SDKy la Microsoft.NETCore.Appversión debajo.NET Core runtimes installed
    Ivan
    14/07/19 a las 7:37
  • Es bueno saberlo, gracias. Creo que analizar la información de esa salida en un script es aún más complicado, así que me quedaré con el lsenfoque basado en. 14/07/19 a las 7:48
  • 1
    Se actualizó esta respuesta con alias flexibles: cscy csc_runalias. Funciona con aplicaciones de consola. Para obtener referencias de la línea de comandos, consulte las opciones del compilador , que se pueden usar con cscalias. 25 nov 2019 a las 12:21
5

El compilador se puede invocar directamente usando

$ /usr/local/share/dotnet/sdk/2.0.0/Roslyn/RunCsc.sh

Sin embargo, este comando en particular puede no ser muy útil sin una infraestructura de proyecto de soporte porque necesitaría pasar todos los ensamblados de referencia de .NET Core o .NET Standard manualmente, lo que normalmente lo manejan el SDK y NuGet. Obtendrá errores como este:

$ /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
6
  • 9
    Es una pena, desearía que hubiera una forma más sencilla de compilar un solo archivo. 12 sep 2017 a las 17:51
  • No hay forma de decirle al compilador dónde encontrar los ensamblados referenciados en lugar de decirlo , ¿de qué otra manera se puede simplificar esto?
    VMAtm
    7 oct 2017 a las 0:29
  • 1
    @VMAtm probablemente quiso decir que no teníamos este problema con el viejo csc. 3 dic 2017 a las 22:53
  • Sugerencias para resolver los errores que menciona: daveaglick.com/posts/… 17 de septiembre de 2018 a las 21:37
  • 1
    NB El script ya no existe en 2.2.301. 14/07/19 a las 7:55
0

En resumen, no se admite sin un proyecto predefinido.

Pero el comentario de @ Andrew muestra que aún es posible si está listo para enumerar todas las dependencias, incluidas las implícitas del sistema, en las opciones de la línea de comandos.

Del error CS0518: el tipo predefinido 'System.Object' no está definido o importado # 12393 :

At the moment, we have no plan to make it easy to use csc.exe in this manner. The guidance is to use the dotnet CLI tooling for the time being. Even if some modification where to be made here, it would be on the framework to provide unified and/or simplified reference assemblies for the compiler. The compiler will never have more complicated type or assembly resolution than it does now (by design).

Ver también el cerrado Hacer posible invocar al compilador directamente # 7689 .

3
  • Un tiempo de compilación 20 veces más lento es sin duda decepcionante. Pero bueno, es su elección;) Gracias por la respuesta, 3 dic 2017 a las 23:50
  • @AndrewSavinykh, tuve que recurrir al uso de un compilador Roslyn solo para Win incluido en MSBuild para compilar archivos de origen único sin un proyecto preconfigurado: stackoverflow.com/questions/553143/…
    Vadzim
    4 de diciembre de 2017 a las 0:03
  • 1
    Por cierto, para mis propósitos, RunCsc.sh funcionó razonablemente bien. Mira aquí y aquí 4 de diciembre de 2017 a las 0:49
0
This is the scripts:



#!/bin/bash

#dotnethome=`dirname "$0"`
dotnethome=`dirname \`which dotnet\``
sdkver=$(dotnet --version)
fwkver=$(dotnet --list-runtimes | grep Microsoft.NETCore.App | awk '{printf("%s", $2)}')
dotnetlib=$dotnethome/shared/Microsoft.NETCore.App/$fwkver

if [ "$#" -lt 1 ]; then
    dotnet $dotnethome/sdk/$sdkver/Roslyn/bincore/csc.dll -help
    echo dotnethome=$dotnethome
    echo sdkver=$sdkver
    echo fwkver=$fwkver
    echo dotnetlib=$dotnetlib
    exit 1
fi

progfile=$1
prog="${progfile%.*}"
echo -r:$dotnetlib/netstandard.dll > /tmp/$prog.rsp
echo -r:$dotnetlib/System.dll >> /tmp/$prog.rsp
echo -r:$dotnetlib/Microsoft.CSharp.dll >> /tmp/$prog.rsp
for f in  $dotnetlib/System.*.dll; do
    echo -r:$f >> /tmp/$prog.rsp
done

dotnet $dotnethome/sdk/$sdkver/Roslyn/bincore/csc.dll -out:$prog.dll -nologo @/tmp/$prog.rsp $* 
if [ $? -eq 0 ]; then
   if test -f "$prog.dll"; then
    if ! test -f "$prog.runtime.config"; then
        echo "{
  \"runtimeOptions\": {
    \"framework\": {
      \"name\": \"Microsoft.NETCore.App\",
      \"version\": \"$fwkver\"
    }
  }
}"  > "$prog.runtimeconfig.json"
    fi
  fi
fi
echo /tmp/$prog.rsp: 
cat /tmp/$prog.rsp
rm /tmp/$prog.rsp
2