Zona HTML Zona Java Zona PHP Zona ASP Zona Bases de datos
Inicio > Tutoriales > Lenguajes orientados a objeto > C# > El lenguaje de programación C#
-Tutoriales

El lenguaje de programación C#


Tema 20: El compilador de C# de Microsoft

. Introducción

A lo largo de los temas anteriores se han explicando muchos aspectos sobre cómo usar el compilador de C# de Microsoft incluido en el .NET Framework SDK. Sin embargo, una vez descrito el lenguaje por completo es el momento adecuado para explicar pormenorizadamente cómo utilizarlo y qué opciones de compilación admite, pues muchas de ellas se basan en conceptos relacionados con características del lenguaje.

Por otro lado, las diferentes explicaciones dadas sobre él se han ido desperdigando a lo largo de muchos de los temas previos, por lo que es también conviene agruparlas todas en un mismo sitio de modo que sea más fácil localizarlas.

Aunque en un principio lo que se va es a explicar cómo usar el compilador en línea de comandos, dado que Visual Studio.NET también hace uso interno de él para compilar, al final del tema se incluirá un epígrafe dedicado a explicar cómo controlar desde dicha herramienta visual las opciones que se utilizarán al llamarlo.

. Sintaxis general de uso del compilador

El nombre del ejecutable del compilador de C# incluido en el .NET Framework SDK es csc.exe y podrá encontrarlo en la carpeta Microsoft.NET\Framework\v1.0.2914 incluida dentro del directorio de instalación de su versión de Windows. De todas formas, el programa de instalación del SDK lo añade automáticamente al path, por lo que en principio puede llamársele sin problemas desde cualquier directorio.

La forma más básica de llamar al compilador consiste en pasarle como argumentos los nombre de los fuentes a compilar, caso en que intentaría generar en el directorio desde el que se le llame un ejecutable a partir de ellos con el mismo nombre que el primero de los fuentes indicados y extensión .exe Por ejemplo, ante una llamada como:

csc FuenteA.cs FuenteB.cs FuenteC.cs

El compilador intentará generar un fuente FuenteA.exe en el directorio desde el que se lo llamó cuyo código sea el resultante de compilar FuenteA.cs, FuenteB.cs y FuenteC.cs Obviamente, para que ello sea posible el compilador habrá de disponer de permiso de escritura y espacio suficiente en dicho directorio y además alguno de los fuentes indicados tendrá que disponer de un punto de entrada válido.

Este comportamiento por defecto puede variarse especificando en la llamada a csc opciones de compilación adicionales que sigan la sintaxis:

<indicadorOpción><opción>

El <indicadorOpción> puede ser el carácter / o el carácter -, aunque en adelante sólo haremos uso de /. Respecto a <opción>, pueden indicarse dos tipos de opciones:

  • Flags: Son opciones cuya aparición o ausencia tienen un determinado significado para el compilador. Se indican de esta manera:
    <nombreFlag><activado?>

    <activado> es opcional e indica si se desea activar el significado del flag. Puede ser el carácter + para indicar que sí o el carácter - para indicar que no, aunque en realidad darle el valor + es innecesario porque es lo que se toma por defecto. También hay algunos flags que no admiten ninguno de los dos caracteres, pues se considera que siempre que aparezcan en la llamada al compilador es porque se desea activar su significado y si no apareciesen se consideraría que se desea desactivarlo.

    A continuación se muestran algunos ejemplos de uso de un flag llamado /optimize al compilar . No se preocupe por saber ahora para que sirve, sino simplemente fíjese en cómo se usa y note que los dos primeros ejemplos son equivalentes:

    csc /optimize   Fuente.cs
    csc /optimize+  Fuente.cs
    csc /optimize-  Fuente.cs
  • Opciones con valores: A diferencia de los flags, son opciones cuya aparición no es válida por sí misma sino que siempre que se usen han de incluir la especificación de uno o varios valores. La forma en que se especifican es:
    <nombreFlag>:<valores>

    Los <valores> indicados pueden ser cualesquiera, aunque si se desea especificar varios hay que separarlos entre sí con caracteres de coma (,) ó punto y coma (;)

    Como es lógico, en principio los <valores> indicados no pueden incluir caracteres de espacio ya que éstos se interpretarían como separadores de argumentos en la llamada a csc. Sin embargo, lo que sí se permite es incluirlos si previamente se les encierra entre comillas dobles (")

    Obviamente, como las comillas dobles también tiene un significado especial en los argumentos de csc tampoco será posible incluirlas directamente como carácter en <valores>. En este caso, para solventar esto lo que se hace es interpretarlas como caracteres normales si van precedidas de \ y con su significado especial si no.

    De nuevo, esto lleva al problema de que el significado de \ si precede a " también puede ser especial, y para solucionarlo lo ahora que se hace es incluirlo duplicado (\\) si aparece precediendo a un " pero no se desea que tome su significado especial.

    Ejemplos equivalentes de cómo compilar dando valores a una opción /r son:

    csc /r:Lib.dll /r:Lib2.dll Fuente.cs
    csc /r:Lib1.dll,Lib2.dll   Fuente.cs
    csc /r:Lib1.dll;Lib3.dll   Fuente.cs

Aunque en los ejemplos mostrados siempre se han incluido las opciones antes que los nombres de los fuentes a compilar, en realidad ello no tiene porqué ser así y se pueden mezclar libremente y en cualquier orden opciones y nombres de fuentes a compilar (salvo excepciones que en su momento se explicarán)

. Opciones de compilación

Una vez explicado cómo utilizar el compilador en líneas generales es el momento propicio para pasar a explicar cuáles son en concreto las opciones que admite. Esto se hará desglosándolas en diferentes categorías según su utilidad.

Antes de empezar es preciso comentar que la mayoría de estas opciones disponen de dos nombres diferentes: un nombre largo que permite deducir con facilidad su utilidad y un nombre corto menos claro pero que permite especificarlas más abreviadamente. Cuando se haga referencia por primera vez a cada opción se utilizará su nombre largo y entre paréntesis se indicará su nombre corto justo a continuación. El resto de referencias a cada opción se harán usando indistintamente uno u otro de sus nombres.

. Opciones básicas

En este epígrafe se explicarán todas aquellas opciones que suelen usarse con mayor frecuencia a la hora de compilar aplicaciones. Como la mayoría ya se explicaron en detalle en el Tema 2: Introducción a C#, dichas opciones aquí simplemente se resumen:

  • /recurse: Si en vez de indicar el nombre de cada fichero a compilar como se ha dicho se indica como valor de esta opción se consigue que si el compilador no lo encuentra en la ruta indicada lo busque en los subdirectorios de la misma.

    Por ejemplo, la siguiente llamada indica que se desea compilar el fichero fuente.cs ubicado dentro del directorio c:\Mis Documentos o algún subdirectorio suyo:

    csc /recurse:"Mis Documentos"\fuente.cs
  • /target (/t): Por defecto al compilar se genera un ejecutable cuya ejecución provoca la apertura de una ventana de consola si al lanzarlo no hubiese ninguna abierta. Esto puede cambiarse dando uno de los valores indicados en la Tabla 15 a esta opción:
    Valor Tipo de fichero a generar
    exe ó ninguno Ejecutable con ventana de consola (valor por defecto)
    winexe Ejecutable sin ventana de consola. Útil para escribir aplicaciones de ventanas o sin interfaz
    library Librería
    module Módulo de código no perteneciente a ningún ensamblado

    Tabla 15: Valores admitidos por la opción /tde csc

    Tanto las librerías como los ejecutables son simples colecciones de tipos de datos compilados. La única diferencia entre ellos es que los segundos disponen de un método especial (Main()) que sirve de punto de entrada a partir del que puede ejecutarse código usando los mecanismo ofrecidos por el sistema operativo (escribiendo su nombre en la línea de comandos, seleccionándolo gráficamente, etc.)

    La diferencia de un módulo con los anteriores tipos de ficheros es que éste no forma parte de ningún ensamblado mientras que los primeros sí. El CLR no puede trabajar con módulos porque estos carecen de manifiesto, pero crearlos permite disponer de código compilado que pueda añadirse a ensamblados que se generen posteriormente y que podrán acceder a sus miembros internal.

  • /main: Si al compilar un ejecutable hubiese más de un punto de entrada válido entre los tipos definidos en los fuentes a compilar se ha de indicar como valor de esta opción cuál es el nombre del tipo que incluye la definición del Main() a utilizar, pues si no el compilador no sabría con cúal de todas quedarse.

    Como es lógico, lo que nunca puede hacerse es definir más de un punto de entrada en un mismo tipo de dato, pues entonces ni siquiera a través de la opción /main podría resolverse la ambigüedad.

  • /out (/o): Por defecto el resultado de la compilación de un ejecutable es un fichero .exe con el nombre del fuente compilado que contenga el punto de entrada, y el de la compilación de un módulo o librería es un fichero con el nombre del primero de los fuentes a compilar indicados y extensión dependiente del tipo de fichero generado (.netmodule para módulos y .dll para librerías) Si se desea darle otro nombre basta indicarlo como valor de esta opción.

    El valor que se le dé ha de incluir la extensión del fichero a generar, lo que permite compilar ficheros con extensiones diferentes a las de su tipo. Por ejemplo, para crear un módulo A.exe a partir de un fuente A.cs puede hacerse:

    csc /out:A.exe /t:module A.cs

    Obviamente, aunque tenga extensión .exe el fichero generado será un módulo y no un ejecutable, por lo que si se intenta ejecutarlo se producirá un error informando de que no es un ejecutable válido. Como puede deducirse, cambiar la extensión de los ficheros generados no suele ser útil y sólo podría venir bien para dificultar aposta la comprensión del funcionamiento de una aplicación o para identificar ensamblados con algún significado o contenido especial.

  • /reference (/r): Por defecto sólo se buscan definiciones de tipos de datos externas a los fuentes a compilar en la librería mscorlib.dll que forma parte de la BCL. Si alguno de los fuentes a compilar hace uso de tipos públicos definidos en otros ensamblados hay que indicar como valores de /r cuáles son esos ensamblados para que también se busque en ellos.

    En mscorlib.dll se encuentran los tipos de uso más frecuentos incluidos en la BCL. En el poco frecuente caso de que haya definido su propia versión de ellos y no desee que se use la de la BCL, puede puede pasar al compilador el flag /nostdlib para indicarle que no desea que busque implícitamente en mscorlib.dll.

    Puede que termine descubriendo que en realidad tampoco hace falta referenciar a la mayoría de las restantes librerías que forman la BCL. Pues bien, esto no se debe a que también las referencie implícitamente el compilador, sino a que se incluyen en un fichero de respuesta (más adelante se explica lo que son este tipo de ficheros) usado por defecto por el compilador. Si no desea que utilice este fichero puede pasarle el flag /noconfig.

    Cuando se den valores a /r hay que tener en cuenta que por defecto el compilador interpretará cada ruta así indicada de manera relativa respecto al directorio desde el que se le llame. Si no lo encuentra allí lo hará relativamente respecto al directorio donde esté instalado el CLR, que en los sistemas operativos Windows es el subdirectorio Microsoft.NET\Framework\v1.0.2914 del directorio de instalación de Windows. Y si tampoco lo encuentra allí la interpretará respecto a los directorios indicados por la variable de entorno LIB de su sistema operativo.

    Esta política de búsqueda puede modificarse incluyendo opciones /lib al llamar al compilador cuyos valores le indiquen en qué directorios ha de buscar antes de pasar a buscar en los indicados por la variable de entorno LIB.

  • /addmodule: Funciona de forma parecida a /r pero se utiliza cuando lo que usan los fuentes son tipos definidos externamente en módulos en vez de en ensamblados. Incluso a la hora de buscar módulos se sigue la misma política que al buscar ensamblados y se admite el uso de /lib para modificarla.

    Se incluyen opciones /r y /addmodule separadas porque añadir un módulo a una compilación implica decir que se desea que los tipos que incluye formen parte del ensamblado a generar, por lo que los fuentes a compilar podrán acceder a sus miembros internal. Sin embargo, cuando se referencia a otros ensamblados con /r esto no ocurre y los fuentes compilados no podrán acceder a sus miembros internal.

    Es importante señalar que el CLR espera que todos los módulos que se añadan a un ensamblado se distribuyan dentro del mismo directorio que la librería o ejecutable correspondiente al mismo. Si no se haciese así no los podría localizar y en tiempo de ejecución se produciría una System.TypeLoadException si se intentase acceder a los tipos definidos en ellos.

Aunque en principio se ha dicho que no importa cómo se intercalen opciones y nombres de fuentes entre los argumentos pasados a csc, hay una excepción que consiste en que /out y /r siempre han de indicarse antes de algún fuente. Esto permite que en una misma llamada al compilador sea posible solicitar la generación de un ensamblado y múltiples módulos de código, pues se considera que cada aparición de las opciones anteriores hace referencia sólo a los fuentes que le siguen. Por ejemplo, dada:

csc /t:library /out:LibA.dll A.cs /t:module /out:ModB.netmodule B.cs

Esta llamada provocará la compilación de A.cs como librería de nombre LibA.dll y la de B.cs como módulo llamado ModB.netmodule.

Sin embargo, al hacer así compilaciones múltiples hay que tener en cuenta que sólo es válido solicitar que el primer grupo de ficheros indicado se compile como ensamblado. Por tanto, sería incorrecto hacer:

csc /t:module /out:ModB.netmodule B.cs /t:library /out:LibA.dll A.cs

Esta llamada es incorrecta porque indica que se desea que el segundo grupo de ficheros dé lugar a un ensamblado y ello sólo puede hacerse con el primero.

Por otro lado, también hay que tener en cuenta que no es válido que un mismo tipo de dato se defina en varios de los grupos de ficheros indicados. Por ejemplo, si se quisiese compilar A.cs como ejecutable y como módulo podría pensarse en hacer:

csc A.cs /t:library A.cs

Sin embargo, esta llamada no es válida porque los dos grupos de ficheros indicados contienen el mismo fichero y por tanto definiciones comunes de tipos de datos. La única solución posible sería hacer dos llamadas por separado al compilador como:

csc A.cs
csc /t:library A.cs

. Manipulación de recursos

Los ficheros de recursos son archivos que no contienen código sino sólo datos tales como como cadenas de textos, imágenes, vídeos o sonidos. Su utilidad es facilitar el desacople entre las aplicaciones y los datos concretos que usen, de modo que sea fácil reutilizarlos en múltiples aplicaciones, modificarlos sin tener que recompilar los fuentes y desarrollar diferentes versiones de cada aplicación en las que sólo varíen dichos datos.

Estos ficheros son especialmente útiles al hora de internacionalizar aplicaciones, pues si se dejan todos los datos que se utilicen en ficheros de recursos independiente del código, a la hora de crear nuevas versiones en otros idiomas sólo será necesario cambiar los ficheros de recursos y habrá que tocar para nada el código.

El objetivo de este tema no es explicar cómo crear y acceder a ficheros de recursos, sino explicar el significado de las opciones de compilación relacionadas con ellos. Si desea aprender más sobre recursos puede comenzar buscando en el apartado Visual Studio.NET -> .NET Framework -> .NET Framework Tutorials -> Resources and Localization Using the .NET Framework SDK de la ayuda del SDK.

Lo que sí es importante es señalar que aunque en la plataforma .NET pueden crearse ficheros de recursos tanto en formato .txt como .resx, el compilador de C# sólo los admite si están compilados en formato .resources. Para ello, en el SDK se incluye una utilidad llamad resgen.exe que permite compilar en dicho formato ficheros de recursos escritos en cualquiera de los formatos anteriores con sólo pasárselos como argumentos. Por ejemplo, si se le llama así:

resgen misrecursos.resx

Suponiendo que el contenido de misrecursos.resx sea el de un fichero .resx válido, tras esta llamada se habrá generado en el directorio desde el que se le llamó un fichero misrecursos.resources con el contenido de misrecursos.resx.

Para añadir este fichero al ensamblado resultante de una compilación se puede utilizar la opción /linkresource (/linkres) Así por ejemplo, para crear un ensamblado fuente1.dll formado por el código resultante de compilar fuente1.cs y los recursos de misrecursos.resources podría compilarse con:

csc /t:library fuente1.cs /linkres:misrecursos.resources

De este modo el fichero de recursos formará parte del ensamblado generado pero permanecerá en un fichero separado de fuente1.dll. Si se desease incrustarlo en él habría que haber compilado con la opción /resource (/res) en vez de /linkres tal y como se muestra a continuación:

csc /t:library fuente1.cs /res:misrecursos.resources

Como un tipo especial de recurso que comúmente suele incrustarase en los ejecutables de los programas es el icono (fichero gráfico en formato .ico) con el que desde las interfaces gráficas de los sistemas operativos se les representará, csc ofrece una opción específica llamada /win32icon en cuyo valor puede indicársele el icono a incrustar:

csc programa.cs /win32icon:programa.ico

En realidad hay que recordar el uso de ficheros de recursos no es un aspecto introducido en la plataforma .NET sino disponible desde hace tiempo en la plataforma Windows en forma de ficheros .res. Por compatibilidad con este antiguo formato de recursos, csc incorpora una opción /win32res que permite incrustarlos de igual forma a como /res incrusta los novedosos ficheros .resources.

En cualquier caso, hay que señalar que siempre que se añada un fichero de recursos a un ensamblado la visibilidad que se considerará para los recursos que incluya es public.

. Configuración de mensajes de avisos y errores

Cada vez que el compilador detecta algún error en uno de los fuentes a compilar genera un mensaje informando de ello en el que indica en qué fichero de código fuente y en qué posición exacta del mismo (línea y columna) lo ha detectado. Por ejemplo, si en la columna 3 de la línea 7 de un fuente llamado ej.cs se llama a un método con nombre completo A.K() inexistente, se mostrará un mensaje como:

ej.cs(7,3): error CS0117: 'A' does not contain a definition for 'K'

Nótese que del fichero sólo se da su nombre y ello podría no identificarlo unívocamente si se compilaron a la vez varios con el mismo nombre pero pertencientes a directorios diferentes. Para solucionar esto puede usarse la opción /fullpaths, con lo que de los mensajes de error incluirían siempre la ruta completa de los ficheros defectuosos. Por ejemplo, si el fichero del ejemplo anterior se encontraba en C:\Ejemplo, al compilarlo con esta opción se mostraría el mensaje de error así:

C:\Ejemplo\ej.cs(7,3): error CS0117: 'A' does not contain a definition for 'K'

Hay veces que el compilador detecta que se han escrito en el fuente ciertas secciones de tal manera que sin ser erróneas son cuanto menos sospechosas (ya sea por ser absurdas, por prestarse a confusión, etc), y en esos casos lo que hace es emitir mensajes de aviso. Por ejemplo, si en la definición del tipo A del fuente prueba.cs se hubiese incluido:

static void Main(int x)
{}

En principio es una definición de método perfectamente válida. Sin embargo, como se parece mucho a una definición de punto de entrada pero no es válida como tal, el compilador generará el mensaje de aviso que sigue para informar de ello al usuario por si acaso éste lo que quería hacer era definir un punto de entrada y se equivocó:

prueba.cs(7,14): warning CS0028: 'A.Main(int)' has the wrong signature to be an entry point

Como se ve, la estructura de los mensajes de aviso es muy similar a la de los mensajes de error y sólo se diferencia de ésta en que incluye warning en vez de error tras el indicador de posición en el fuente. Incluso como a estos, la opción /fullpaths también les afecta y provoca que se muestren las rutas de los fuentes al completo.

Una diferencia importante entre avisos y errores es que la aparición de mensajes de los segundos durante la compilación aborta la generación del binario, mientras que la aparición de los primeros no (aunque en ambos casos nunca se aborta la compilación sino que tras mostrarlos se sigue analizando los fuentes por si pudiesen detectarse más errores y avisos) Ahora bien, también puede forzarse a que ello ocurra con los de aviso pasando al compilador el flag /warnaserror, con lo que se conseguiría que todo mensaje de aviso se muestrase como error. Ello puede resultar útil porque fuerza a escribir los fuentes de la manera más fiable e inteligentemente posible.

En el laod opuesto, puede que haya ciertos tipos de mensajes de aviso de los que no se desea siquiera que se informe en tanto que la información que aportan ya se conoce y se sabe que no afectará negativamente al programa. En esos casos puede usarse la opción /nowarn indicando como valores suyos los códigos asociados a los mensaje de aviso que no se desea que se reporten. El código asociado a cada tipo de mensaje de aviso se es la palabra de la forma CS<código> que se muestra tras warning en el mensaje de aviso. Así, para compilar el prueba.cs del ejemplo anterior sin que se genere el mensaje de aviso arriba mostrado puede hacerse:

csc prueba.cs /nowarn:0028

En realidad los ceros incluidos a la izquierda del código del aviso en los mensajes de aviso son opcionales, por lo que la compilación anterior es equivalente a:

csc prueba.cs /nowarn:28

Si desea obtener la lista completa de todos los tipos de mensaje de aviso y error con sus respectivos códigos puede consultar dentro de la documentación del .NET Framework SDK en Visual Studio.NET -> Visual Basic and Visual C# -> Visual C# Language -> C# Compiler Options -> Compiler Errors CS0001 to CS9999

Si en lugar de desactivar ciertos tipos de avisos uno por uno desea desactivarlos por grupos según su severidad, entonces puede hacerlo a través de la opción /warn. Esta opción toma como valor un número comprendido entre 0 y 4 que indica cuál es el nivel de avisos con el que se desea trabajar. Por defecto éste vale 4, lo que significa que se mostrarán todos los avisos, pero puede dársele cualquiera de los de la Tabla 16:

Nivel de aviso Avisos mostrados
0 Ninguno
1 Sólo los más graves
2 Los más graves y algunos menos graves como por ejemplo los relativos a ocultaciones de miembros
3 Los de nivel 2 más algunos poco graves como los relativos al uso de expresiones absurdas que siempre produzcan el mismo resultado
4 Todos

Tabla 16: Niveles de mensajes de aviso

Si está interesado en conocer en concreto el nivel de algún tipo de aviso puede remitirse a la descripción sobre el mismo incluida en la documentación del SDK antes comentada

. Ficheros de respuesta

La línea de comandos no es la única forma de pasar información al compilador (tanto ficheros a compilar como opciones de compilación), sino que también es posible almacenar información de este tipo en un fichero y pasársele al compilador como argumento solamente dicho fichero y no toda la información en él contenida. De este modo se facilitaría la labor de pasar como parámetros las opciones de uso más frecuente ya que bastaría sólo indicar cuál es el nombre de un fichero que las especifica.

A este ficheros se les llama ficheros de respuesta, ya que al pasárselos al compilador su contenido puede verse como la respuesta a cuáles son los argumentos a usar durante la compilación. La extensión de estos ficheros suele ser .rsp, y aunque nada obliga a dársela es conveniente hacerlo como ocurre con todo convenio.

Al compilar, por defecto el compilador siempre lee un fichero de respuesta llamado csc.rsp ubicado en el directorio del CLR, por lo que para entender cuál es la sintaxis a seguir para escribir estos ficheros nada mejor que ver cuál es su contenido y así de paso saber cuáles son las opciones que por defecto se añadiran a toda compilación:

# This file contains command-line options that the C#
# command line compiler (CSC) will process as part
# of every compilation, unless the "/noconfig" option
# is specified.
# Reference the common Framework libraries
/r:Accessibility.dll
/r:Microsoft.Vsa.dll
/r:System.Configuration.Install.dll
/r:System.Data.dll
/r:System.Design.dll
/r:System.DirectoryServices.dll
/r:System.dll
/r:System.Drawing.Design.dll
/r:System.Drawing.dll
/r:System.EnterpriseServices.dll
/r:System.Management.dll
/r:System.Messaging.dll
/r:System.Runtime.Remoting.dll
/r:System.Runtime.Serialization.Formatters.Soap.dll
/r:System.Security.dll
/r:System.ServiceProcess.dll
/r:System.Web.dll
/r:System.Web.RegularExpressions.dll
/r:System.Web.Services.dll
/r:System.Windows.Forms.Dll
/r:System.XML.dll

Del contenido de este fichero es fácil deducir que la estructura de los ficheros de respuesta es sencilla: cada opción se incluye en una línea aparte y pueden intercalarse entre ellas comentarios de una línea que comiencen con #. Además, como puede verse este fichero de respuesta usado por defecto añade referencias a las librerías de la BCL de uso más común, lo que evita tener que incluirlas constantemente al compilar.

Tras tomar las opciones de este fichero, el compilador mira si en el directorio desde el que se le llama hay otro csc.rsp y si es así toma sus opciones. Si por alguna razón no nos interesase que se tomasen las opciones de dichos ficheros (por ejemplo, para usar nuevas versiones de tipos incluidos en las librerías que referencian) bastaría pasar el flag /noconfig al compilar para desactivar esta búsqueda por defecto en ellos, aunque hay que señalar que este flag no admite los sufijos + y - admitidos por el resto de flags.

En realidad, la estructura del fichero de respuesta csc.rsp no es la única posible, pues además de opciones también es válido incluir nombres de fuentes a compilar e incluso puede mezclarse múltiples opciones y nombres de fuentes en cada línea del fichero.

Ahora bien, al escribir ficheros de respuesta hay que tener cuidado con dos cosas: no es posible cortar las opciones o nombres de fichero con retornos de carro que provoquen que ocupen varias líneas; y las opciones son pasadas al compilador en el mismo orden en que aparezcan en el fuente, por lo que hay que tener cuidado con cómo se coloquen las opciones /out y /t por lo ya comentado sobre la importacia de su colocación.

Una vez escrito un fichero de respuesta, para indicar al compilador que ha de usarlo basta pasárselo como un nombre de fuente más pero precediendo su nombre del sufijo @. Por ejemplo, para compilar A.cs usando las opciones almacenadas en opc.rsp habría que llamar al compilador con:

csc @opc.rsp A.rsp

También sería posible indicar múltiples ficheros de respuesta, caso en que se tomarían las opciones de cada uno en el mismo orden en que apareciesen en la llamada a csc. Por ejemplo, para compilar A.rsp tomando las opciones de opc1.rsp y luego las de opc2.rsp podría llamarse al compilador con:

csc @opc1.rsp @opc2.rsp A.rsp

Puede ocurrir que las opciones indicadas en un fichero de respuesta contradigan a opciones indicadas en otro fichero de respuesta indicado a continuación o a opciones dadas al compilador en la línea de comandos. Para resolver estas ambigüedades el compilador siempre va procesando los argumentos que se le pasen de izquierda a derecha y se queda con la última especificación dada a cada opción. Así, en el ejemplo anterior las opciones del csc.rsp del directorio desde el que se le llamó -si existiese- tendría preferencia sobre las del csc.rsp del directorio del CLR, las de opc2.rsp tendrían preferencia sobre las de éste, y las de opc1.rsp sobre las de opc2.rsp.

También pueden incluirse en los ficheros de respuesta opciones @ que incluyan a otros ficheros de respuesta, con lo que se tomaría sus opciones antes de continuar tomando las siguientes del fichero que lo incluyó, aunque obviamente nunca se admitirá que un fichero incluido sea el mismo que el que lo incluye o que alguno que incluya a éste, pues entonces se formarían ciclos y nunca acabaría la búsqueda de opciones.

. Opciones de depuración

Sin duda la opción de depuración más importante es el flag /debug, cuya inclusión indica al compilador que ha de generar un fichero .pdb con información sobre la relación entre el fichero binario generado y las líneas de los fuentes a partir de los que se generó. Esta información es muy útil para depurar aplicaciones, pues permite mostrar la instrucción de código fuente que produjo las excepciones en lugar de mostrar las instrucciones de código nativo en que fue traducida.

Para entender mejor la utilidad de este fichero .pdb puede escribir el programa:

class A
{
  public static void Main()
  {throw new System.Exception();}
}

Si lo compila con:

csc A.cs

Al ejecutarlo se producirá una excepción y surgirá una ventana de selección de depurador. Si pulsa No en ella verá en la consola un mensaje como el siguiente:

Unhandled Exception: System.Exception: Exception of type System.Exception was thrown. at A.Main()

Sin embargo, si lo compila con:

csc A.cs /debug

Al ejecutarlo se obtendrá un mensaje mucho más detallado en el que se indicará cuál es la línea exácta del código fuente durante cuya ejecución se produjo la excepción:

Unhandled Exception: System.Exception: Exception of type System.Exception was thrown at A.Main() in E:\c#\Ej\A.cs:line 5

Como es fácil deducir, a partir de esta información es fácil crear herramientas de depuración -como el depurador de Visual Studio.NET o el CLR Debugger del SDK- que muestren la línea exácta del código fuente donde se produjo la excepción lanzada; y obviamente estos datos también pueden tener muchos otros usos, como permitir ejecutar paso a paso los programas mostrando en cada momento cuál es la línea del fuente que se ejecutará a continuación y cosas similares.

También puede usarse /debug como opción con argumentos en vez de cómo flag, lo que permite generar una versión recortada de la información de depuración. Si de esta forma se le da el valor full funcionará exáctamente igual que al activarla como flag, pero si se le da el valor pdbonly entonces la información de depuración generada sólo estará disponible para los depuradores desde los que se haya lanzado la aplicación pero no para los que se le hayan adjuntado dinámicamente una vez lanzada.

Por último, respecto a la depuración de aplicaciones conviene señalar que por defecto el compilador siempre intenta generar el código más compacto y eficiente posible, lo que provoca que compile más lentamente. Sin embargo, como cuando se está depurando suelen realizarse mmuchas recompilaciones de los fuentes puede que en esos casos interese desactivar dichas optimizaciones y así así conseguir recompilar más rápido. Ello puede conseguirse llamando al compilador con /optimize- (/o-)

. Compilación incremental

La compilación incremental consiste en sólo recompilar en cada compilación que se haga de un proyecto aquellos métodos cuya definición haya cambiado respecto a la última compilación realizada, con lo que el proyecto podría compilarse más rápido que haciendo una compilación completa normal.

Para que esto sea posible hacerlo hay que llamar al compilador con el flag /incremental (/incr), lo que provocará la generación de un fichero adicional con el mismo nombre que el binario generado más una extensión .incr. Por ejemplo, dado:

csc /out:fuente.exe /incremental Fuente.cs

Se generará un ejecutable fuente.exe y un fichero adicional fuente.exe.incr. Aunque pueda parecer redundante incluir en el ejemplo la opción /out al llamar al compilador, es necesaria porque al menos en la versión del compilador incluida en la beta 2 del es obligatorio especificarla siempre que se utilice /incr.

El fichero .incr generado incluye información sobre la compilación que permitirá que posteriores compilaciones que se realicen con /incr activado puedan hacerse de manera incremental. Obviamente, si este fichero se elimina será reconstruido en la siguiente compilación que se haga con /incr, pero dicha compilación no se realizará de manera completa por no disponerse del fichero .incr durante ella.

Sin embargo, el hecho de que esté disponible un fichero .incr al compilar un proyecto no implica que se use, pues el compilador puede ignorarlo y realizar una compilación completa si detecta que han cambiado las opciones de compilación especificadas o si detecta que los fuentes han cambiado tanto que es al menos igual de eficiente hacerla así que de manera incremental.

En realidad no es bueno hacer siempre las compilaciones incrementalmente sino que sólo es útil hacerlo en proyectos formados por mútilples fuentes de pequeño tamaño, minetras que en proyectos con pocos y grandes ficheros se gana poco o nada en tiempo de compilación. Además, los ejecutables generados incrementalmente pueden ocupar más que los generados por compilación completa, por lo sólo es recomendable compilar incrementalmente las versiones de prueba de los proyectos pero no las definitivas.

. Opciones relativas al lenguaje

A lo largo de los anteriores temas se ha ido diseminando diversas opciones de compilación relacionadas de manera más o menos directa con el lenguaje C#. En este punto haremos recapitulación de todas ellas mismas y las resumiremos:

  • /define (/d): En el Tema 3: El preprocesador ya se introdujo esta opción cuyos valores recordemos que se utilizan para introducir definiciones de símbolos de preprocesado al principio de todos los fuentes a compilar.

    Por ejemplo, si se desea compilar los fuentes A.cs y B.cs como si al principio de ellos se hubiese incluido las directivas de preprocesado #define PRUEBA y #define VERSION1 podría llamarse al compilador con:

    csc /d:PRUEBA;VERSION1 A.cs B.cs
  • /checked: En los temas 4 y 16 se explicó que todo desbordamiento que ocurra en operaciones aritméticas entre variables enteras es tratado por defecto truncando el resultado. Pues bien, la utilidad de activar esta opción es precisamente forzar a que se incluyan en el código generado las comprobaciones necesarias para que en caso de desbordamiento se lance en su lugar una System.OverflowException.

    Obviamente el código compilado con /checked se ejecutará más lento que el que lo haga sin ella ya que incluirá comprobaciones de desbordamiento adicionales. Sin embago, a cambio con ello se consigue detectar con facilidad errores derivados de desbordamientos que de otra manera podrían pasar inadvertidos.

  • /unsafe: En el Tema 18: Código inseguro ya se explicó que la única utilidad de esta opción es servir al compilador de mecanismo de seguridad gracias al que pueda asegurarse de que el usario sabe lo que hace al compilar código con punteros.
  • /doc: Esta opción ya se introdujo en el Tema 19: Documentación XML, donde se explicó que se usa para indicar al compilador que es desea generar un fichero XML con el contenido de los comentarios de documentación incluidos en los fuentes a compilar. El nombre de ese fichero será el que se dé como valor a esta opción.

    Al usar esta opción hay que tener en cuenta una cosa, y es que para optimizar el tiempo que se tarda en realizar compilaciones incrementales, durante ellas esta opción es ignorada. Por tanto, no tiene mucho sentido combinar /doc y /incr.

. Otras opciones

Aparte de las opciones comentadas, csc admite unas cuantas más aún no descritas ya sea porque su uso es muy poco frecuente o porque no encajan correctamente en ninguno de los subepígrafes tratados. Todas estas opciones se recogen finalmente aquí:

  • /filealign: Los valores dados a esta opción indican el tamaño de las secciones en que se dividirán los ficheros binarios resultantes de la compilación. Puede tomar los valores 512, 1024, 2048, 4096, 8192 ó 16384, y cada sección en los binarios comenzará en un posición que sea múltiplo del valor dado a esta opción.

    Por defecto el valor que se le dé puede variar dependiendo de la implementación que se haga del CLR, aunque darle un valor a medida puede ser útil en el diseño de aplicaciones para dispositivos empotrados con escasa capacidad de almacenamiento ya que puede reducir el tamaño de los ficheros generados.

  • /bugreport: Dado que es muy difícil diseñar un compilador 100% libre de errores, Microsoft proporciona a través de esta opción un mecanismo que facilita a los usuarios el envío de información sobre los errores que descubran en el mismo y facilita a Microsoft la labor de interpretarla para solucionarlos lo antes posible.

    El valor que se dé a esta opción es el nombre de con el que se desea que se genere el fichero con la información relativa al error descubierto durante la compilación. En dicho fichero csc insertará automáticamente la siguiente información:

    • Opciones de compilación utilizadas.
    • Versión del compilador, CLR y sistema operativo usado.
    • Copia de todos los códigos fuentes compilados. Como es lógico, para facilitar la correción a Microsoft se recomienda enviar el programa más compacto posible en el que se produzca el error descubierto.
    • Mensajes de salida mostrados durante la compilación.

    Aparte de toda esta información insertada automáticamente por el compilador, durante la generación del fichero de error también se pedirá al usuario que indique una pequeña descripción sobre el error detectado y cómo cree que podría solucionarse. Dicha información también será añadida de manera automática al fichero de error que se cree.

    Un ejemplo cómo generar información relativa a un error verídico que se produce al compilar un programa error.cs con la Beta 1 del .NET SDK Framework es:

    csc error.cs /bugreport:ErrorUsing.cs

    Tras contestar a las preguntas que el compilador hará al usuario sobre el error encontrado, el contenido del fichero generado es el siguiente:

    ### C# Compiler Defect Report, created 07/12/00 20:14:36
    ### Compiler version: 7.00.9030
    ### Common Language Runtime version: 1.00.2914.16
    ### Operating System: Windows NT 5.0.2195   Service Pack 2
    ### User Name: Administrador
    ### Compiler command line
    csc.exe error.cs /bugreport:ErrorUsing.cs
    ### Source file: 'e:\c#\ej\error.cs'
    using System;
    
    public class R1:IDisposable
    {
      public static void Main()
      {
        using (R1 r1 = new R1())
        {
        }
      }
      
      public void Dispose()
      {}
    }
    ### Compiler output
    error.cs(7,3): error CS1513: } expected
    error.cs(7,26): error CS1002: ; expected
    error.cs(12,9): error CS1518: Expected class, delegate, enum,
    interface, or struct
    error.cs(14,1): error CS1022: Type or namespace definition, or
    end-of-file expected
    ### User description
    No detecta la instruccion using
    
    ### User suggested correct behavior
    Posiblemente no haya sido implementada en esta version del compilador

    Nótese que aunque el error detectado en el ejemplo es verídico, en versiones del compilador posteriores a la Beta 1 no se produce porque ya fue corregido.

  • /baseaddress: Esta opción sólo tiene sentido cuando se solicita la generación de una librería e indica cuál es la dirección de memoria en que se prefiere que ésta se cargue cuando sea enlazada dinámicamente. Nótese que se ha dicho librería, pues si el fichero generado es de cualquier otro tipo será ignorada.

    El valor que se dé a esta opción puede indicarse tanto en hexadecimal como en octal o decimal siguiendo las reglas usadas en C# para la escritura de literales enteros. Sin embargo, hay que tener en cuenta que los bits menos significativos de esta dirección pueden ser redondeados. Por ejemplo, si escribimos:

    csc fichero.cs /baseaddress:0x11110001

    El compilador tratará esta llamada tal y como si se le hubiese pasado:

    csc fichero.cs /baseaddress:0x11110000

    Si no se da valor a esta opción, las librerías se instalarán en el área de memoria que se estime conveniente en cada implementación del CLR.

  • /codepage: Por defecto el compilador acepta fuentes escritos en Unicode, UTF-8 o usando la página de códigos por defecto del sistema operativo. Si se desea compilar fuentes escritos en otras páginas de código hay que indicar como valor de esta opción el identificador de ella.

    Un uso típico de esta opción es permitir compilar fuentes escritos en español con un editor de textos de MS-DOS (como edit.com), caso en que hay que darle el valor 437 para que acepte los caracteres especiales tales como acentos o eñes.

    /utf8output: Su inclusión indica que el compilador ha de mostrar los mensajes usando el juego de caracteres UTF-8, lo que es útil cuando se utilizan ciertos sistemas operativos internacionales en los que por defecto no se muestren correctamente dichos mensajes por la ventana de consola.

    Para poder leerla en esos casos se recomienda usar este flag al compilar y redirigir la salida a un fichero como muestra el siguiente ejemplo donde se compila A.cs redirigiendo los mensajes de compilación a salida.txt y mostrándolos en UTF-8:

    csc A.cs /utf8output  > salida.txt
  • /help (/?): Muestra un mensaje de ayuda resumiendo cuáles son las opciones admitidas por el compilador y para qué sirven. Toda opción o fichero a compilar especificado junto opción son totalmente ignorados.
  • /nologo: Indica que no se desea que al ejecutar el compilador se genere el mensaje que incluye información sobre la versión del compilador y el copyright de Microsoft sobre el mismo que por defecto se muestra.

    Suele usarse cuando la compilación se solicita desde una aplicación o fichero de procesamiento por lotes, pues oculta la ejecución del compilador al usuario y ello puede venir bien para evitar que éste conozca cómo funciona la aplicación o para conseguir un funcionamiento más elegante y transparente de la misma.

. Acceso al compilador desde Visual Studio.NET

Como se explicó en su momento en el Tema 2: Introducción a C#, a las opciones de compilación de un proyecto se accede desde VS.NET a través de las páginas de propiedades del mismo, las cuales tiene el aspecto mostrado en la Ilustración 9 y se obtienen seleccionando el proyecto en el Solution Explorer y pulsando sobre View -> Property Pages en el menú principial de Visual Studio.

Para la mayoría de opciones admitidas por csc.exe se incluye en estas páginas controles tales como cajas de texto y listas desplegables que permiten configurarlas de una manera visual, cómoda e intuitiva. En la Tabla 17 se resume en orden alfabético cuál es el control que en concreto se asocia en estas páginas a cada opción:

Opción Control visual
/baseaddress Configuration Properties -> Advanced -> Base Address
/checked Configuration Properties -> Build -> Check for Arithmetic Overflow/Underflow
/debug Configuration Properties -> Build -> Generate Debugging Information
/define Configuration Properties -> Build -> Conditional Compilation Constants
/doc Configuration Properties -> Build -> XML Documentation File
/filealign Configuration Properties -> Build -> File Alignment
/incremental Configuration Properties -> Advanced -> Incremental Build
/main Common Properties -> General -> Startup Object
/optimize Configuration Properties -> Build -> Optimize code
/out Common Properties -> General -> Assembly Name
/target Common Properties -> General -> Output Type
/unsafe Configuration Properties -> Build -> Allow unsafe code blocks
/warn Configuration Properties -> Build -> Warning Level blocks
/warnaserror Configuration Properties -> Build -> Treat Warnings As Errors
/win32icon Common Properties -> General -> Application Icon

Tabla 17: Controles asociados a opciones de compilación

Como puede observar, desde VS.NET no es posible acceder a muchas de las opciones del compilador en línea de comandos. En los casos de /codepage, /fullpaths, /lib, /help, /nologo, /recurse y /utf8output esto es lógico ya que son opciones que pierden su sentido desde dentro en una interfaz gráfica. Hay otros casos en que ello se debe a que se ofrecen desde el menú principal de VS.NET otros mecanismos alternativos para especificarlas, como son los indicados en la Tabla 18:

Opción Mecanismo de acceso
/bugreport Help -> Customer Feedback
/resource Seleccionar el recurso en Project -> Add Existing Item
/reference Seleccionar la referencia en Project -> Add Reference

Tabla 18: Acceso a opciones fuera de las páginas de propiedades

Finalmente, queda un grupo de opciones que no disponibles simplemente porque la implementación de VS.NET (al menos en la Beta 2) no las contempla, y son @, /linkresource, /nostdlib, /noconfig, /nowarn y /win32res. En este sentido, mención aparte merece el valor module de /t, que tampoco puede usarse en tanto que VS.NET no soporta el trabajo con módulos.

 
Patrocinados
 

Copyright © 1999-2006 Programación en castellano. Todos los derechos reservados.
Formulario de Contacto - Datos legales - Publicidad

Hospedaje web y servidores dedicados linux por Ferca Network