One of my side-projects requires me to build some of my own tools. In this case: a code generation tool. In a future post, I’ll describe the tool and provide its source, but for now I did want to share some of what I discovered on the way.

First, the tool I am building essentially reads a simple DSL and generates C# source code from it. To be more specific it doesn’t just generate C# source code, it also compiles it into an assembly for you so that it is easy to consume in binary form. It’s this last part I will focus on: taking c# code and making it into an assembly.

I created a simple Console application called DemoCompiler. The entire source is below. I’ll walk you through it section-by-section to illustrate what is going on.

THE SOURCE CODE TO GET STARTED

 

using System;

using System.Collections.Generic;

 

namespace DemoCompiler

{

    class Program

    {

        static void Main(string[] args)

        {

 

 

            // Create the source code

            string source = @"

 

namespace Foo

{

 

    public class Bar

    {

        static void Main(string[] args)

        {

            Bar.SayHello();

        }

 

        public static void SayHello()

        {

            System.Console.WriteLine(""Hello World"");

        }

    }

}

";

 

            // Setup for compiling

            var provider_options = new Dictionary<string, string>

                         {

                             {"CompilerVersion","v3.5"}

                         };

            var provider = new Microsoft.CSharp.CSharpCodeProvider(provider_options);

 

            var compiler_params = new System.CodeDom.Compiler.CompilerParameters();

 

            string outfile = "D:\\Foo.EXE";

            compiler_params.OutputAssembly = outfile ;

            compiler_params.GenerateExecutable = true;

 

            // Compile

            var results = provider.CompileAssemblyFromSource(compiler_params, source);

 

            // Print out any Errors

 

            Console.WriteLine("Output file: {0}", outfile);

            Console.WriteLine("Number of Errors: {0}", results.Errors.Count);

            foreach (System.CodeDom.Compiler.CompilerError err in results.Errors)

            {

                Console.WriteLine("ERROR {0}", err.ErrorText);

 

            }

 

        }

    }

}

 

FYI – pay attention to the two namespaces that are used here:

  • Microsoft.CSharp
  • System.CodeDom.Compiler

Now run the compiler

image

Excellent. No errors.  Ok, we have a EXE.

Let’s run it…

image

And now step through the code

image  

 

TIP: Always check for compile errors

Now let’s play around with it and see what happens.

 

USING OBJECTS ANOTHER ANOTHER ASSEMBLY

First.  let’s try creating a simple Rectangle from System.Drawing.Rectangle and printing it.

 

 

 

            // Create the source code

            string source = @"

 

namespace Foo

{

 

    public class Bar

    {

        static void Main(string[] args)

        {

            Bar.SayHello();

        }

 

        public static void SayHello()

        {

            System.Console.WriteLine(""Hello World"");

            var r = new System.Drawing.Rectangle(0,0,100,100);

            System.Console.WriteLine(r);

 

        }

    }

}

";

This time the compile doesn’t work…

image

 

The reason is that the output assembly doesn’t contain a reference to System.Drawing – we fix that very simply by modifying the compiler parameters

 

 

            string outfile = "D:\\Foo.EXE";

            compiler_params.OutputAssembly = outfile ;

            compiler_params.GenerateExecutable = true;

 

            compiler_params.ReferencedAssemblies.Add("System.Drawing.Dll");

And try compiling…

image

And then running the EXE

 

image

 

USING LINQ

We love LINQ these days, so let’s try using it to print the numbers from 0 to 9

            // Create the source code

            string source = @"

 

using System.Collections.Generic;

using System.Linq;

namespace Foo

{

 

    public class Bar

    {

        static void Main(string[] args)

        {

            Bar.SayHello();

        }

 

        public static void SayHello()

        {

            System.Console.WriteLine(""Hello World"");

            var r = new System.Drawing.Rectangle(0,0,100,100);

            System.Console.WriteLine(r);

            System.Console.WriteLine( string.Join("","", Enumerable.Range(0,10).Select(n=>n.ToString()).ToArray() ) );

 

        }

    }

}

";

 

image

What could be wrong here? We are missing a reference to “System.Core.Dll” (that’s where Linq is)

 

            string outfile = "D:\\Foo.EXE";

            compiler_params.OutputAssembly = outfile ;

            compiler_params.GenerateExecutable = true;

 

            compiler_params.ReferencedAssemblies.Add("System.Drawing.Dll");

            compiler_params.ReferencedAssemblies.Add("System.Core.Dll");

 

Now try compiling.

image

It works, so run the EXE …

image

Perfect.

 

WHAT WE LEARNED

  • the basics of compiling source
  • How to tell if the compilation worked correctly
  • how to specify that an executable is created
  • how to ensure that we can use C# 3.0 syntax
  • how to address common issues in using Linq and referring to other DLLs

 

CREATING A DLL INSTEAD OF AN EXE

This is very simple

 

            string outfile = "D:\\Foo.DLL";

            compiler_params.OutputAssembly = outfile ;

            compiler_params.GenerateExecutable = false;

Just set the output filename to end with “.DLL” and set GenerateExecutable to false.

If you run the compiler now, it will generate a DLL that you can import into another project.

image

 

SOURCE CODE

You can get it here: https://skydrive.live.com/redir.aspx?cid=1ff099edb1c7ebfa&resid=1FF099EDB1C7EBFA!1353&parid=1FF099EDB1C7EBFA!1306&authkey=!AO1E08sYS2zgJYY