Generating Source Code and Compiling a Program from a CodeDOM Graph

The System.CodeDom.Compiler namespace provides interfaces for generating source code from CodeDOM object graphs and for managing compilation with supported compilers. A code generator can produce source code in a particular programming language according to a CodeDOM graph. Code generators implement the ICodeGenerator interface. A code compiler can compile source code using the compiler for a particular language. Code compilers implement the ICodeCompiler interface. A class that derives from CodeDomProvider can typically provide instances of a code generator or code compiler for the language the provider supports.

Using a CodeDOM code generator to generate source code

To generate source code in a particular language, you need a CodeDOM graph that represents the structure of the source code to generate, and a code generator.

To obtain a code generator, first create a CodeDomProvider for the target language. Then call the CodeDomProvider.CreateGenerator method to create a code generator.

The following example demonstrate how to create a C# code generator from a CSharpCodeProvider:

Dim provider As New CSharpCodeProvider()
Dim gen As ICodeGenerator = provider.CreateGenerator()
[C#]
CSharpCodeProvider provider = new CSharpCodeProvider();
ICodeGenerator gen = provider.CreateGenerator();

The graph for code generation is typically contained in a CodeCompileUnit. To generate code for a CodeCompileUnit that contains a CodeDOM graph, call the ICodeGenerator.GenerateCodeFromCompileUnit method of the code generator. This method has a parameter for a TextWriter that it uses to generate the source code, so it is sometimes necessary to first create a TextWriter that can be written to. The following example demonstrates generating code from a CodeCompileUnit and writing the generated source code to a file named HelloWorld.cs.

Public Shared Function GenerateCSharpCode( _
    compileunit As CodeCompileUnit) As String
       
    ' Generate the code with the C# code provider.
    Dim provider As CSharpCodeProvider = New CSharpCodeProvider()

    ' Obtain an ICodeGenerator from the CodeDomProvider class.
    Dim gen As ICodeGenerator = provider.CreateGenerator()

    ' Build the output file name.
    Dim sourceFile As String
    If provider.FileExtension.StartsWith(".")
        sourceFile = "HelloWorld" + provider.FileExtension
    Else
        sourceFile = "HelloWorld." + provider.FileExtension
    End If

    ' Create a TextWriter to a StreamWriter to an output file.
    Dim tw As New IndentedTextWriter( _
        New StreamWriter(sourceFile, False), "    ")

    ' Generate source code using the code generator.
    gen.GenerateCodeFromCompileUnit(compileunit, tw, _
        New CodeGeneratorOptions())

    ' Close the output file.
    tw.Close()
        
    Return sourceFile
End Function
   
[C#]
public static String GenerateCSharpCode(CodeCompileUnit compileunit)
{
    // Generate the code with the C# code provider.
    CSharpCodeProvider provider = new CSharpCodeProvider();

    // Obtain an ICodeGenerator from the CodeDomProvider class.
    ICodeGenerator gen = provider.CreateGenerator();

    // Build the output file name.
    String sourceFile;
    if (provider.FileExtension[0] == '.')
    {
        sourceFile = "HelloWorld" + provider.FileExtension;
    }
    else 
    {
        sourceFile = "HelloWorld." + provider.FileExtension;
    }

    // Create a TextWriter to a StreamWriter to the output file.
    IndentedTextWriter tw = new IndentedTextWriter(
            new StreamWriter(sourceFile, false), "    ");
            
    // Generate source code using the code generator.
    gen.GenerateCodeFromCompileUnit(compileunit, tw, 
            new CodeGeneratorOptions());

    // Close the output file.
    tw.Close();            

    return sourceFile;
}
   

Using a CodeDOM code compiler to compile assemblies

The ICodeCompiler interface enables developers to invoke compilation and access the results of compilation using compilers for which an ICodeCompiler interface has been implemented.

An ICodeCompiler implementation can be obtained from a provider class that derives from CodeDomProvider. To obtain a code compiler, first create a CodeDomProvider for the target language. Then call the CreateCompiler method of the derived provider to create an ICodeCompiler interface implementation.

Invoking compilation

To compile an assembly using the ICodeCompiler interface, you must have either source code to compile in a language for which you have a compiler, or a CodeDOM graph that source code to compile can be generated from.

If you are compiling from a CodeDOM graph, pass the CodeCompileUnit containing the graph to the CompileAssemblyFromDom method of the ICodeCompiler. If you have a source code file in a language that the compiler understands, pass the name of the file containing the source code to the CompileAssemblyFromFile method of the ICodeCompiler. You can also pass a string containing source code in a language that the compiler understands to the CompileAssemblyFromSource method of the ICodeCompiler.

Configuring compilation parameters

All of the standard compilation-invoking methods of an ICodeCompiler interface have a parameter of type CompilerParameters that indicates the options to use for compilation.

You can specify a file name for the output assembly in the OutputAssembly property of the CompilerParameters. Otherwise, a default output file name will be used.

By default, a new CompilerParameters is initialized with its GenerateExecutable property set to false. If you are compiling an executable program, you must set the GenerateExecutable property to true. When the GenerateExecutable is set to false, the compiler will generate a class library.

If you are compiling an executable from a CodeDOM graph, a CodeEntryPointMethod must be defined in the graph. If there are multiple code entry points, it may be necessary to set the MainClass property of the CompilerParameters to the name of the class that defines the entry point to use.

To include debug information in a generated executable, set the IncludeDebugInformation property to true.

If your project references any assemblies, you must specify the assembly names as items in a StringCollection as the ReferencedAssemblies property of the CompilerParameters you use when invoking compilation.

You can compile an assembly that is written to memory rather than disk by setting the GenerateInMemory property to true. When an assembly is generated in memory, your code can obtain a reference to the generated assembly from the CompiledAssembly property of a CompilerResults. If an assembly is written to disk, you can obtain the path to the generated assembly from the PathToAssembly property of a CompilerResults.

To specify a custom command-line arguments string to use when invoking the compilation process, set the string in the CompilerOptions property.

If a Win32 security token is required to invoke the compiler process, specify the token in the UserToken property.

To link a Win32 resource file into the compiled assembly, specify the name of the Win32 resource file in the Win32Resource property.

To specify a warning level at which to halt compilation, set the WarningLevel property to an integer that represents the warning level at which to halt compilation. You can also configure the compiler to halt compilation if warnings are encountered by setting the TreatWarningsAsErrors property to true.

The following code example demonstrates compiling a source file using an ICodeCompiler interface.

Public Shared Function CompileCSharpCode(sourceFile As String, _
    exeFile As String) As Boolean

    ' Obtain an ICodeCompiler from the CodeDomProvider class.       
    Dim provider As CSharpCodeProvider = New CSharpCodeProvider()
    Dim compiler As ICodeCompiler = provider.CreateCompiler()
          
    ' Build the parameters for source compilation.
    Dim cp As New CompilerParameters()
         
    ' Add an assembly reference.
    cp.ReferencedAssemblies.Add("System.dll")

    ' Save the assembly as a physical file.
    cp.GenerateInMemory = False

    ' Generate an executable instead of a class library.
    cp.GenerateExecutable = True
          
    ' Set the assembly file name to generate.
    cp.OutputAssembly = exeFile
        
    ' Invoke compilation.
    Dim cr As CompilerResults = _
        compiler.CompileAssemblyFromFile(cp, sourceFile)
          
    If cr.Errors.Count > 0 Then
        ' Display compilation errors.
        Console.WriteLine("Errors building {0} into {1}", _
            sourceFile, cr.PathToAssembly)
        Dim ce As System.CodeDom.Compiler.CompilerError
        For Each ce In  cr.Errors
            Console.WriteLine("  {0}", ce.ToString())
            Console.WriteLine()
        Next ce
    Else
        Console.WriteLine("Source {0} built into {1} successfully.", _
            sourceFile, cr.PathToAssembly)
    End If
          
    ' Return the results of compilation.
    If cr.Errors.Count > 0 Then
        Return False
    Else
        Return True
    End If
End Function
 [C#]
public static bool CompileCSharpCode(String sourceFile, 
    String exeFile)
{
    // Obtain an ICodeCompiler from a CodeDomProvider class.       
    CSharpCodeProvider provider = new CSharpCodeProvider();
    ICodeCompiler compiler = provider.CreateCompiler();

    // Build the parameters for source compilation.
    CompilerParameters cp = new CompilerParameters();

    // Add an assembly reference.
    cp.ReferencedAssemblies.Add( "System.dll" );

    // Generate an executable instead of 
    // a class library.
    cp.GenerateExecutable = true;

    // Set the assembly file name to generate.
    cp.OutputAssembly = exeFile;

    // Save the assembly as a physical file.
    cp.GenerateInMemory = false;
 
    // Invoke compilation.
    CompilerResults cr = compiler.CompileAssemblyFromFile(cp, sourceFile);

    if(cr.Errors.Count > 0)
    {
        // Display compilation errors.
        Console.WriteLine("Errors building {0} into {1}",  
            sourceFile, cr.PathToAssembly);
        foreach(CompilerError ce in cr.Errors)
        {
            Console.WriteLine("  {0}", ce.ToString());
            Console.WriteLine();
        }
    }
    else
    {
        Console.WriteLine("Source {0} built into {1} successfully.",
            sourceFile, cr.PathToAssembly);
    }
          
    // Return the results of compilation.
    if (cr.Errors.Count > 0)
    {
        return false;
    }
    else 
    {
        return true;
    }
}

Languages with Initial Support

The .NET Framework provides code compilers and code generators for the following languages: C#, Visual Basic, Managed Extensions for C++, J#, and JScript. CodeDOM support can be extended to other languages by implementing language-specific code generators and code compilers.

See Also

Programming with the .NET Framework | CodeDOM Quick Reference | System.CodeDom | System.CodeDom.Compiler