The C# Compiler
After that introduction to C# and object orientation in general, it's time to see how C# projects can be compiled. In this section, you learn how to compile more than just one file and how various classes can be combined.
Compiling
Before you see how compiling works, we'll guide you through the steps of the compilation process itself.
The first thing to do is to call the lexer, which prepares tokens for the parser. In the case of Mono, the parser is based on Jay, a Java-based Berkley Port of Yacc. Miguel de Icaza ported Jay to C#. Tokens are code fragments that the system needs for checking the syntax. The syntax check is performed by a parser.
After creating a parse tree, the system starts to look for parent classes and tries to find out more about the meaning of the code. Before this step, the system knows about the code but it does not know what it means yet. A semantic analysis of the data leads to the final code. Therefore, Mono uses the System.Reflection.Emit API.
Compiling More Than Just One File
Compiling a set of files is slightly more complex than compiling just one piece of code. Huge projects are usually not implemented in one file. Therefore, it makes sense to combine multiple source files in one project and to compile the entire code with just one step.
This section teaches you the most basic fundamentals.
To see how things work, we start with an example consisting of just two files. Let's have a look at human.cs:
using System; public class Human { public Human() { Console.WriteLine("a child without name ..."); } public Human(string name) { Console.WriteLine(name + " is born ..."); } }
Now let's see what we can find in main.cs:
class Demo { public static void Main() { Human paul = new Human(); Human hugo = new Human("Hugo"); } }
If we have a closer look at those two files, we'll find out that we include only Humanthe System namespace isn't included because we need it inside our classes anyway.
To compile the code, we can use the following command:
[hs@duron mono]$ mcs /optimize main.cs human.cs /out:human.exe Compilation succeeded
Just list all files in a single command line. To define which file to create, just use /out:. Older versions of Mono supported the -o flag as well, but we recommend using the newer syntax. In our case, the output is sent to human.exe. With the help of /optimize, we tell the C# compiler to optimize the output.
As you can see in the listing, the output can be called just like always:
[hs@duron mono]$ mono human.exe a child without name ... Hugo is born ...
In this example, the entire code has been transformed to one big EXE file. However, it's often useful to compile objects as separate modules. This is no problem for the C# compiler. In the case of Mono, DLL files are used. To generate a DLL, just do as we've done in the following listing:
[hs@duron mono]$ mcs --target library /out:human.dll human.cs Compilation succeeded
Now that the DLL has been created, we can take it and compile the main program;
[hs@duron mono]$ mcs -L . -reference:human.dll ex.cs Compilation succeeded
The process will lead to the desired result:
[hs@duron mono]$ mono ex.exe a child without name ... Hugo is born ...
The more complex your application, the more important it is to make use of modules and DLL files.
Compiler Flags
In this section, we deal with the most important flags of the C# compiler. The syntax of mcs is comparatively simple:
mcs [option] [source-files]
The next section contains an overview of the parameters supported by mcs:
[hs@duron mono]$ mcs --help Mono C# compiler, (C) 2001 Ximian, Inc. mcs [options] source-files --about About the Mono C# compiler -checked[+|-] Set default context to checked -codepage:ID Sets code page to the one in ID (number, 'utf8' or 'reset') -define:S1[;S2] Defines one or more symbols (short: /d:) -debug[+-] Generate debugging information -g Generate debugging information --fatal Makes errors fatal -lib:PATH1,PATH2 Adds the paths to the assembly link path -main:class Specified the class that contains the entry point -noconfig[+|-] Disables implicit references to assemblies -nostdlib[+|-] Does not load core libraries -nowarn:W1[,W2] Disables one or more warnings -out:FNAME Specifies output file --parse Only parses the source file --expect-error X Expect that error X will be encountered -recurse:SPEC Recursively compiles the files in SPEC ([dir]/file) -reference:ASS References the specified assembly (-r:ASS) --stacktrace Shows stack trace at error location -target:KIND Specifies the target (KIND is one of: exe, winexe, library, module), (short: /t:) --timestamp Displays time stamps of various compiler events -unsafe[+|-] Allows unsafe code -warnaserror[+|-] Treat warnings as errors -warn:LEVEL Sets warning level (the highest is 4, the default) -v Verbose parsing (for debugging the parser) Resources: -linkresource:FILE[,ID] Links FILE as a resource -resource:FILE[,ID] Embed FILE as a resource --mcs-debug X Sets MCS debugging level to X @file Read response file for more options Options can be of the form -option or /option
The listing was generated with the help of mcs 0.23. As you can see in the last line, the compiler supports Unix and .NET-style options. However, we recommend sticking to the .NET-compliant options.