Example #1
0
        public static void Main(string[] args)
        {
            Options options = null;

            Parser.Default.ParseArguments <Options>(args)
            .WithParsed(o => options = o);

            if (options == null)
            {
                return;
            }

            XmlConfig config;

            if (!string.IsNullOrEmpty(options.XmlConfigPath))
            {
                config = XmlConfig.Read(options.XmlConfigPath);
            }
            else
            {
                config = new XmlConfig()
                {
                    DontGenerateFieldTypeInfoComment    = options.DontGenerateFieldTypeInfoComment,
                    ForceUserTypesToNewInsteadOfCasting = options.ForceUserTypesToNewInsteadOfCasting,
                    MultiLineProperties                = options.MultiLineProperties,
                    UseDiaSymbolProvider               = options.UseDiaSymbolProvider,
                    GeneratedAssemblyName              = options.GeneratedAssemblyName,
                    GeneratedPropsFileName             = options.GeneratedPropsFileName,
                    CacheStaticUserTypeFields          = options.CacheStaticUserTypeFields,
                    CacheUserTypeFields                = options.CacheUserTypeFields,
                    LazyCacheUserTypeFields            = options.LazyCacheUserTypeFields,
                    GeneratePhysicalMappingOfUserTypes = options.GeneratePhysicalMappingOfUserTypes,
                    Types   = new XmlType[options.Types.Count],
                    Modules = new XmlModule[] { new XmlModule {
                                                    PdbPath = options.PdbPath
                                                } },
                };

                for (int i = 0; i < options.Types.Count; i++)
                {
                    config.Types[i] = new XmlType()
                    {
                        Name = options.Types[i],
                    }
                }
                ;
            }

            Generator.Generate(config);
        }
    }
Example #2
0
 /// <summary>
 /// Initializes members from the specified CodeGen configuration.
 /// </summary>
 /// <param name="xmlConfig">The CodeGen configuration.</param>
 /// <param name="logger">The logger.</param>
 /// <param name="errorLogger">The error logger.</param>
 private void Initialize(XmlConfig xmlConfig, TextWriter logger, TextWriter errorLogger)
 {
     stopwatch            = System.Diagnostics.Stopwatch.StartNew();
     this.logger          = logger;
     this.errorLogger     = errorLogger;
     this.xmlConfig       = xmlConfig;
     xmlModules           = xmlConfig.Modules;
     typeNames            = xmlConfig.Types;
     includedFiles        = xmlConfig.IncludedFiles;
     referencedAssemblies = xmlConfig.ReferencedAssemblies;
     generationOptions    = xmlConfig.GetGenerationFlags();
     userTypeFactory      = new UserTypeFactory(xmlConfig.Transformations);
     userTypes            = new List <UserType>();
 }
Example #3
0
        /// <summary>
        /// Generates the assembly from the specified CodeGen configuration.
        /// </summary>
        /// <param name="xmlConfig">The CodeGen configuration.</param>
        /// <param name="assemblyPath">The output assembly path.</param>
        public void GenerateAssembly(XmlConfig xmlConfig, string assemblyPath)
        {
            using (StringWriter errorOutput = new StringWriter())
            {
                TextWriter consoleOut = Console.Out;

                try
                {
                    Console.SetOut(TextWriter.Null);
                    xmlConfig.MultiFileExport = false;
                    Initialize(xmlConfig, TextWriter.Null, errorOutput);

                    // Generate user types
                    GenerateUserTypes();

                    // Throw exception if there was an error
                    StringBuilder sb = errorOutput.GetStringBuilder();

                    if (sb.Length > 0)
                    {
                        throw new Exception(sb.ToString());
                    }

                    if (!xmlConfig.GenerateAssemblyWithILWriter)
                    {
                        string code        = GenerateSingleFile();
                        var    syntaxTrees = new List <SyntaxTree>();

                        syntaxTrees.Add(CSharpSyntaxTree.ParseText(code, path: "AutoGeneratedFile.cs", encoding: UTF8Encoding.Default));

                        using (FileStream dllStream = new FileStream(assemblyPath, FileMode.CreateNew))
                        {
                            errorLogger = errorOutput;
                            if (!GenerateRoslynAssembly(syntaxTrees, dllStream, null))
                            {
                                throw new Exception(errorOutput.GetStringBuilder().ToString());
                            }
                        }
                    }
                    else
                    {
                        codeWriter.GenerateBinary(userTypes, xmlConfig.GeneratedAssemblyName, !xmlConfig.DisablePdbGeneration, dependentAssemblies.Select(da => ResolveAssemblyPath(da)));
                    }
                }
                finally
                {
                    Console.SetOut(consoleOut);
                }
            }
        }
Example #4
0
        /// <summary>
        /// Generates the assembly from the specified CodeGen configuration.
        /// </summary>
        /// <param name="xmlConfig">The CodeGen configuration.</param>
        /// <returns>Assembly bytes compiled using Roslyn</returns>
        public byte[] GenerateAssembly(XmlConfig xmlConfig)
        {
            string code        = GenerateScriptCode(xmlConfig);
            var    syntaxTrees = new List <SyntaxTree>();

            syntaxTrees.Add(CSharpSyntaxTree.ParseText(code, path: "AutoGeneratedFile.cs", encoding: UTF8Encoding.Default));

            using (MemoryStream dllStream = new MemoryStream())
                using (StringWriter errorOutput = new StringWriter())
                {
                    errorLogger = errorOutput;
                    if (!GenerateRoslynAssembly(syntaxTrees, dllStream, null))
                    {
                        throw new Exception(errorOutput.GetStringBuilder().ToString());
                    }

                    return(dllStream.GetBuffer());
                }
        }
Example #5
0
        /// <summary>
        /// Generates user types using the specified XML configuration.
        /// </summary>
        /// <param name="xmlConfig">The XML configuration.</param>
        /// <param name="logger">The logger text writer. If set to null, Console.Out will be used.</param>
        /// <param name="errorLogger">The error logger text writer. If set to null, Console.Error will be used.</param>
        public void Generate(XmlConfig xmlConfig, TextWriter logger = null, TextWriter errorLogger = null)
        {
            ConcurrentDictionary <string, string> generatedFiles = new ConcurrentDictionary <string, string>();
            var    syntaxTrees      = new ConcurrentBag <SyntaxTree>();
            string currentDirectory = Directory.GetCurrentDirectory();
            string outputDirectory  = currentDirectory + "\\output\\";

            // Check logger and error logger
            if (errorLogger == null)
            {
                errorLogger = Console.Error;
            }

            if (logger == null)
            {
                logger = Console.Out;
            }

            Initialize(xmlConfig, logger, errorLogger);

            // Create output directory
            Directory.CreateDirectory(outputDirectory);

            // Generate user types
            GenerateUserTypes();

            // Code generation and saving it to disk
            logger.Write("Saving code to disk...");

            if (!generationOptions.HasFlag(UserTypeGenerationFlags.SingleFileExport))
            {
                // Generate Code
                Parallel.ForEach(userTypes,
                                 (symbolEntry) =>
                {
                    Tuple <string, string> result = GenerateCode(symbolEntry, userTypeFactory, outputDirectory, errorLogger, generationOptions, generatedFiles);
                    string text     = result.Item1;
                    string filename = result.Item2;

                    if (xmlConfig.GenerateAssemblyWithRoslyn && !string.IsNullOrEmpty(xmlConfig.GeneratedAssemblyName) && !string.IsNullOrEmpty(text))
                    {
                        lock (syntaxTrees)
                        {
                            syntaxTrees.Add(CSharpSyntaxTree.ParseText(text, path: filename, encoding: System.Text.UTF8Encoding.Default));
                        }
                    }
                });
            }
            else
            {
                string filename = string.Format(@"{0}\everything.exported.cs", outputDirectory);
                string code     = GenerateSingleFile();

                generatedFiles.TryAdd(filename.ToLowerInvariant(), filename);
                if (!generationOptions.HasFlag(UserTypeGenerationFlags.DontSaveGeneratedCodeFiles))
                {
                    File.WriteAllText(filename, code);
                }

                if (xmlConfig.GenerateAssemblyWithRoslyn && !string.IsNullOrEmpty(xmlConfig.GeneratedAssemblyName))
                {
                    syntaxTrees.Add(CSharpSyntaxTree.ParseText(code, path: filename, encoding: UTF8Encoding.Default));
                }
            }

            logger.WriteLine(" {0}", stopwatch.Elapsed);

            // Compiling the code
            string[] dependentAssemblies = new string[]
            {
                "CsDebugScript.Engine.dll",
                "CsDebugScript.CommonUserTypes.dll",
            };

            if (xmlConfig.GenerateAssemblyWithRoslyn && !string.IsNullOrEmpty(xmlConfig.GeneratedAssemblyName))
            {
                List <MetadataReference> references = new List <MetadataReference>
                {
                    MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
                    MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location),
                };

                foreach (string dependentAssembly in dependentAssemblies)
                {
                    if (!xmlConfig.ReferencedAssemblies.Any(a => a.Path.EndsWith(dependentAssembly)))
                    {
                        references.Add(MetadataReference.CreateFromFile(ResolveAssemblyPath(dependentAssembly)));
                    }
                }

                references.AddRange(xmlConfig.ReferencedAssemblies.Select(r => MetadataReference.CreateFromFile(ResolveAssemblyPath(r.Path))));

                foreach (var includedFile in includedFiles)
                {
                    syntaxTrees.Add(CSharpSyntaxTree.ParseText(File.ReadAllText(includedFile.Path), path: includedFile.Path, encoding: System.Text.UTF8Encoding.Default));
                }

                CSharpCompilation compilation = CSharpCompilation.Create(
                    Path.GetFileNameWithoutExtension(xmlConfig.GeneratedAssemblyName),
                    syntaxTrees: syntaxTrees,
                    references: references,
                    options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, platform: Platform.AnyCpu));

                logger.WriteLine("Syntax trees: {0}", syntaxTrees.Count);

                string dllFilename = Path.Combine(outputDirectory, xmlConfig.GeneratedAssemblyName);
                string pdbFilename = Path.Combine(outputDirectory, Path.GetFileNameWithoutExtension(dllFilename) + ".pdb");

                using (var dllStream = new FileStream(dllFilename, FileMode.Create))
                    using (var pdbStream = new FileStream(pdbFilename, FileMode.Create))
                    {
                        var result = compilation.Emit(dllStream, !xmlConfig.DisablePdbGeneration ? pdbStream : null);

                        if (!result.Success)
                        {
                            IEnumerable <Diagnostic> failures = result.Diagnostics.Where(diagnostic =>
                                                                                         diagnostic.IsWarningAsError ||
                                                                                         diagnostic.Severity == DiagnosticSeverity.Error);

                            errorLogger.WriteLine("Compile errors (top 1000):");
                            foreach (var diagnostic in failures.Take(1000))
                            {
                                errorLogger.WriteLine(diagnostic);
                            }
                        }
                        else
                        {
                            logger.WriteLine("DLL size: {0}", dllStream.Position);
                            logger.WriteLine("PDB size: {0}", pdbStream.Position);
                        }
                    }

                logger.WriteLine("Compiling: {0}", stopwatch.Elapsed);
            }

            // Check whether we should generate assembly
            if (!xmlConfig.GenerateAssemblyWithRoslyn && !string.IsNullOrEmpty(xmlConfig.GeneratedAssemblyName))
            {
#if NET461
                var codeProvider       = new CSharpCodeProvider();
                var compilerParameters = new CompilerParameters()
                {
                    IncludeDebugInformation = !xmlConfig.DisablePdbGeneration,
                    OutputAssembly          = outputDirectory + xmlConfig.GeneratedAssemblyName,
                };
                List <string> assembliesList = new List <string>();

                foreach (var assemblyPath in AppDomain.CurrentDomain.GetAssemblies().Where(a => !a.IsDynamic).Select(a => a.Location))
                {
                    string assemblyName = Path.GetFileName(assemblyPath);

                    if (!xmlConfig.ReferencedAssemblies.Any(a => a.Path.EndsWith(assemblyName)))
                    {
                        assembliesList.Add(assemblyPath);
                    }
                }

                foreach (string dependentAssembly in dependentAssemblies)
                {
                    if (!xmlConfig.ReferencedAssemblies.Any(a => a.Path.EndsWith(dependentAssembly)))
                    {
                        assembliesList.Add(ResolveAssemblyPath(dependentAssembly));
                    }
                }
                assembliesList.AddRange(xmlConfig.ReferencedAssemblies.Select(r => ResolveAssemblyPath(r.Path)).ToArray());

                const string MicrosoftCSharpDll = "Microsoft.CSharp.dll";

                if (!assembliesList.Where(a => a.Contains(MicrosoftCSharpDll)).Any())
                {
                    assembliesList.Add(MicrosoftCSharpDll);
                }

                Dictionary <string, string> uniqueAssemblies = new Dictionary <string, string>();

                foreach (string assemblyString in assembliesList)
                {
                    string key = Path.GetFileName(assemblyString);

                    if (!uniqueAssemblies.ContainsKey(key))
                    {
                        uniqueAssemblies.Add(key, assemblyString);
                    }
                }

                compilerParameters.ReferencedAssemblies.AddRange(uniqueAssemblies.Values.ToArray());

                var filesToCompile = generatedFiles.Values.Concat(includedFiles.Select(f => f.Path)).ToArray();
                var compileResult  = codeProvider.CompileAssemblyFromFile(compilerParameters, filesToCompile);

                if (compileResult.Errors.Count > 0)
                {
                    errorLogger.WriteLine("Compile errors (top 1000):");
                    foreach (CompilerError err in compileResult.Errors.Cast <CompilerError>().Take(1000))
                    {
                        errorLogger.WriteLine(err);
                    }
                }

                logger.WriteLine("Compiling: {0}", stopwatch.Elapsed);
#else
                throw new Exception(".NET standard must use Roslyn to generate assemblies.");
#endif
            }

            // Generating props file
            if (!string.IsNullOrEmpty(xmlConfig.GeneratedPropsFileName))
            {
                using (TextWriter output = new StreamWriter(outputDirectory + xmlConfig.GeneratedPropsFileName, false /* append */, System.Text.Encoding.UTF8, 16 * 1024 * 1024))
                {
                    output.WriteLine(@"<?xml version=""1.0"" encoding=""utf-8""?>");
                    output.WriteLine(@"<Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">");
                    output.WriteLine(@"  <ItemGroup>");
                    foreach (var file in generatedFiles.Values)
                    {
                        output.WriteLine(@"    <Compile Include=""{0}"" />", file);
                    }
                    output.WriteLine(@" </ItemGroup>");
                    output.WriteLine(@"</Project>");
                }
            }

            logger.WriteLine("Total time: {0}", stopwatch.Elapsed);
        }
Example #6
0
        /// <summary>
        /// Generates user types using the specified XML configuration.
        /// </summary>
        /// <param name="xmlConfig">The XML configuration.</param>
        /// <param name="logger">The logger text writer. If set to null, Console.Out will be used.</param>
        /// <param name="errorLogger">The error logger text writer. If set to null, Console.Error will be used.</param>
        public static void Generate(XmlConfig xmlConfig, TextWriter logger = null, TextWriter errorLogger = null)
        {
            var sw = System.Diagnostics.Stopwatch.StartNew();

            XmlModule[]             xmlModules                   = xmlConfig.Modules;
            XmlType[]               typeNames                    = xmlConfig.Types;
            XmlIncludedFile[]       includedFiles                = xmlConfig.IncludedFiles;
            XmlReferencedAssembly[] referencedAssemblies         = xmlConfig.ReferencedAssemblies;
            UserTypeGenerationFlags generationOptions            = xmlConfig.GetGenerationFlags();
            ConcurrentDictionary <string, string> generatedFiles = new ConcurrentDictionary <string, string>();
            var    syntaxTrees      = new ConcurrentBag <SyntaxTree>();
            string currentDirectory = Directory.GetCurrentDirectory();
            string outputDirectory  = currentDirectory + "\\output\\";

            // Check logger and error logger
            if (errorLogger == null)
            {
                errorLogger = Console.Error;
            }
            if (logger == null)
            {
                logger = Console.Out;
            }

            // Create output directory
            Directory.CreateDirectory(outputDirectory);

            // Verify that included files exist
            if (!string.IsNullOrEmpty(xmlConfig.GeneratedAssemblyName))
            {
                foreach (var file in includedFiles)
                {
                    if (!File.Exists(file.Path))
                    {
                        throw new FileNotFoundException("", file.Path);
                    }
                }
            }

            // Loading modules
            ConcurrentDictionary <Module, XmlModule>   modules = new ConcurrentDictionary <Module, XmlModule>();
            ConcurrentDictionary <XmlModule, Symbol[]> globalTypesPerModule = new ConcurrentDictionary <XmlModule, Symbol[]>();

            logger.Write("Loading modules...");
            Parallel.ForEach(xmlModules, (xmlModule) =>
            {
                Module module = Module.Open(xmlModule);

                modules.TryAdd(module, xmlModule);
            });

            logger.WriteLine(" {0}", sw.Elapsed);

            // Enumerating symbols
            logger.Write("Enumerating symbols...");
            Parallel.ForEach(modules, (mm) =>
            {
                XmlModule xmlModule   = mm.Value;
                Module module         = mm.Key;
                string moduleName     = xmlModule.Name;
                string nameSpace      = xmlModule.Namespace;
                List <Symbol> symbols = new List <Symbol>();

                foreach (var type in typeNames)
                {
                    Symbol[] foundSymbols = module.FindGlobalTypeWildcard(type.NameWildcard);

                    if (foundSymbols.Length == 0)
                    {
                        errorLogger.WriteLine("Symbol not found: {0}", type.Name);
                    }
                    else
                    {
                        symbols.AddRange(foundSymbols);
                    }
                }

                symbols.AddRange(module.GetAllTypes());
                globalTypesPerModule.TryAdd(xmlModule, symbols.ToArray());
            });

            List <Symbol> allSymbols = new List <Symbol>();

            Symbol[][] symbolsPerModule = globalTypesPerModule.Select(ss => ss.Value).ToArray();
            int        maxSymbols       = symbolsPerModule.Max(ss => ss.Length);

            for (int i = 0; i < maxSymbols; i++)
            {
                for (int j = 0; j < symbolsPerModule.Length; j++)
                {
                    if (i < symbolsPerModule[j].Length)
                    {
                        allSymbols.Add(symbolsPerModule[j][i]);
                    }
                }
            }

            logger.WriteLine(" {0}", sw.Elapsed);

#if false
            // Initialize symbol fields and base classes
            logger.Write("Initializing symbol values...");

            Parallel.ForEach(Partitioner.Create(allSymbols), (symbol) =>
            {
                var fields      = symbol.Fields;
                var baseClasses = symbol.BaseClasses;
            });

            logger.WriteLine(" {0}", sw.Elapsed);
#endif

            logger.Write("Deduplicating symbols...");

            // Group duplicated symbols
            Dictionary <string, List <Symbol> > symbolsByName     = new Dictionary <string, List <Symbol> >();
            Dictionary <Symbol, List <Symbol> > duplicatedSymbols = new Dictionary <Symbol, List <Symbol> >();

            foreach (var symbol in allSymbols)
            {
                List <Symbol> symbols;

                if (!symbolsByName.TryGetValue(symbol.Name, out symbols))
                {
                    symbolsByName.Add(symbol.Name, symbols = new List <Symbol>());
                }

                bool found = false;

                foreach (var s in symbols.ToArray())
                {
                    if (s.Size != 0 && symbol.Size != 0 && s.Size != symbol.Size)
                    {
#if DEBUG
                        logger.WriteLine("{0}!{1} ({2}) {3}!{4} ({5})", s.Module.Name, s.Name, s.Size, symbol.Module.Name, symbol.Name, symbol.Size);
#endif
                        continue;
                    }

                    if (s.Size == 0 && symbol.Size != 0)
                    {
                        List <Symbol> duplicates;

                        if (!duplicatedSymbols.TryGetValue(s, out duplicates))
                        {
                            duplicatedSymbols.Add(s, duplicates = new List <Symbol>());
                        }

                        duplicatedSymbols.Remove(s);
                        duplicates.Add(s);
                        duplicatedSymbols.Add(symbol, duplicates);
                        symbols.Remove(s);
                        symbols.Add(symbol);
                    }
                    else
                    {
                        List <Symbol> duplicates;

                        if (!duplicatedSymbols.TryGetValue(s, out duplicates))
                        {
                            duplicatedSymbols.Add(s, duplicates = new List <Symbol>());
                        }
                        duplicates.Add(symbol);
                    }

                    found = true;
                    break;
                }

                if (!found)
                {
                    symbols.Add(symbol);
                }
            }

            // Unlink duplicated symbols if two or more are named the same
            foreach (var symbols in symbolsByName.Values)
            {
                if (symbols.Count <= 1)
                {
                    continue;
                }

                foreach (var s in symbols.ToArray())
                {
                    List <Symbol> duplicates;

                    if (!duplicatedSymbols.TryGetValue(s, out duplicates))
                    {
                        continue;
                    }

                    symbols.AddRange(duplicates);
                    duplicatedSymbols.Remove(s);
                }
            }

            // Extracting deduplicated symbols
            Dictionary <string, Symbol[]> deduplicatedSymbols = new Dictionary <string, Symbol[]>();
            Dictionary <Symbol, string>   symbolNamespaces    = new Dictionary <Symbol, string>();

            foreach (var symbols in symbolsByName.Values)
            {
                if (symbols.Count != 1)
                {
                    foreach (var s in symbols)
                    {
                        symbolNamespaces.Add(s, modules[s.Module].Namespace);
                    }
                }
                else
                {
                    Symbol        symbol = symbols.First();
                    List <Symbol> duplicates;

                    if (!duplicatedSymbols.TryGetValue(symbol, out duplicates))
                    {
                        duplicates = new List <Symbol>();
                    }
                    duplicates.Insert(0, symbol);
                    deduplicatedSymbols.Add(symbol.Name, duplicates.ToArray());

                    foreach (var s in duplicates)
                    {
                        symbolNamespaces.Add(s, xmlConfig.CommonTypesNamespace);
                    }
                }
            }

            var globalTypes = symbolsByName.SelectMany(s => s.Value).ToArray();

            logger.WriteLine(" {0}", sw.Elapsed);
            logger.WriteLine("  Total symbols: {0}", globalTypesPerModule.Sum(gt => gt.Value.Length));
            logger.WriteLine("  Unique symbol names: {0}", symbolsByName.Count);
            logger.WriteLine("  Dedupedlicated symbols: {0}", globalTypes.Length);

            // Initialize GlobalCache with deduplicatedSymbols
            GlobalCache.Update(deduplicatedSymbols);

            // Collecting types
            logger.Write("Collecting types...");

            var             factory   = new UserTypeFactory(xmlConfig.Transformations);
            List <UserType> userTypes = new List <UserType>();

            foreach (var module in modules.Keys)
            {
                userTypes.Add(factory.AddSymbol(module.GlobalScope, new XmlType()
                {
                    Name = "ModuleGlobals"
                }, modules[module].Namespace, generationOptions));
            }

            ConcurrentBag <Symbol> simpleSymbols = new ConcurrentBag <Symbol>();
            Dictionary <Tuple <string, string>, List <Symbol> > templateSymbols = new Dictionary <Tuple <string, string>, List <Symbol> >();

            Parallel.ForEach(Partitioner.Create(globalTypes), (symbol) =>
            {
                string symbolName = symbol.Name;

                // TODO: Add configurable filter
                //
                if (symbolName.StartsWith("$") || symbolName.StartsWith("__vc_attributes") || symbolName.Contains("`anonymous-namespace'") || symbolName.Contains("`anonymous namespace'") || symbolName.Contains("::$") || symbolName.Contains("`"))
                {
                    return;
                }

                // Do not handle template referenced arguments
                if (symbolName.Contains("&"))
                {
                    // TODO: Convert this to function pointer
                    return;
                }

                // TODO: C# doesn't support lengthy names
                if (symbolName.Length > 160)
                {
                    return;
                }

                // TODO: For now remove all unnamed-type symbols
                string scopedClassName = symbol.Namespaces.Last();

                if (scopedClassName.StartsWith("<") || symbolName.Contains("::<"))
                {
                    return;
                }

                // Check if symbol contains template type.
                if (SymbolNameHelper.ContainsTemplateType(symbolName))
                {
                    List <string> namespaces = symbol.Namespaces;
                    string className         = namespaces.Last();
                    var symbolId             = Tuple.Create(symbolNamespaces[symbol], SymbolNameHelper.CreateLookupNameForSymbol(symbol));

                    lock (templateSymbols)
                    {
                        if (templateSymbols.ContainsKey(symbolId) == false)
                        {
                            templateSymbols[symbolId] = new List <Symbol>()
                            {
                                symbol
                            }
                        }
                        ;
                        else
                        {
                            templateSymbols[symbolId].Add(symbol);
                        }
                    }

                    // TODO:
                    // Do not add physical types for template specialization (not now)
                    // do if types contains static fields
                    // nested in templates
                }
                else
                {
                    simpleSymbols.Add(symbol);
                }
            });

            logger.WriteLine(" {0}", sw.Elapsed);

            // Populate Templates
            logger.Write("Populating templates...");
            foreach (List <Symbol> symbols in templateSymbols.Values)
            {
                Symbol symbol     = symbols.First();
                string symbolName = SymbolNameHelper.CreateLookupNameForSymbol(symbol);

                XmlType type = new XmlType()
                {
                    Name = symbolName
                };

                userTypes.AddRange(factory.AddSymbols(symbols, type, symbolNamespaces[symbol], generationOptions));
            }

            logger.WriteLine(" {0}", sw.Elapsed);

            // Specialized class
            logger.Write("Populating specialized classes...");
            foreach (Symbol symbol in simpleSymbols)
            {
                userTypes.Add(factory.AddSymbol(symbol, null, symbolNamespaces[symbol], generationOptions));
            }

            logger.WriteLine(" {0}", sw.Elapsed);

            // To solve template dependencies. Update specialization arguments once all the templates has been populated.
            logger.Write("Updating template arguments...");
            foreach (TemplateUserType templateUserType in userTypes.OfType <TemplateUserType>())
            {
                foreach (TemplateUserType specializedTemplateUserType in templateUserType.SpecializedTypes)
                {
                    if (!specializedTemplateUserType.UpdateTemplateArguments(factory))
                    {
#if DEBUG
                        logger.WriteLine("Template user type cannot be updated: {0}", specializedTemplateUserType.Symbol.Name);
#endif
                    }
                }
            }

            logger.WriteLine(" {0}", sw.Elapsed);

            // Post processing user types (filling DeclaredInType)
            logger.Write("Post processing user types...");
            var namespaceTypes = factory.ProcessTypes(userTypes, symbolNamespaces).ToArray();
            userTypes.AddRange(namespaceTypes);

            logger.WriteLine(" {0}", sw.Elapsed);

            // Code generation and saving it to disk
            logger.Write("Saving code to disk...");

            if (!generationOptions.HasFlag(UserTypeGenerationFlags.SingleFileExport))
            {
                // Generate Code
                Parallel.ForEach(userTypes,
                                 (symbolEntry) =>
                {
                    Tuple <string, string> result = GenerateCode(symbolEntry, factory, outputDirectory, errorLogger, generationOptions, generatedFiles);
                    string text     = result.Item1;
                    string filename = result.Item2;

                    if (xmlConfig.GenerateAssemblyWithRoslyn && !string.IsNullOrEmpty(xmlConfig.GeneratedAssemblyName) && !string.IsNullOrEmpty(text))
                    {
                        lock (syntaxTrees)
                        {
                            syntaxTrees.Add(CSharpSyntaxTree.ParseText(text, path: filename, encoding: System.Text.UTF8Encoding.Default));
                        }
                    }
                });
            }
            else
            {
                string           filename = string.Format(@"{0}\everything.exported.cs", outputDirectory);
                HashSet <string> usings   = new HashSet <string>();
                foreach (var symbolEntry in userTypes)
                {
                    foreach (var u in symbolEntry.Usings)
                    {
                        usings.Add(u);
                    }
                }

                generatedFiles.TryAdd(filename.ToLowerInvariant(), filename);
                using (StringWriter stringOutput = new StringWriter())
                    using (TextWriter masterOutput = !xmlConfig.DontSaveGeneratedCodeFiles ? new StreamWriter(filename, false /* append */, System.Text.Encoding.UTF8, 16 * 1024 * 1024) : TextWriter.Null)
                    {
                        foreach (var u in usings.OrderBy(s => s))
                        {
                            masterOutput.WriteLine("using {0};", u);
                            if (xmlConfig.GenerateAssemblyWithRoslyn && !string.IsNullOrEmpty(xmlConfig.GeneratedAssemblyName))
                            {
                                stringOutput.WriteLine("using {0};", u);
                            }
                        }
                        masterOutput.WriteLine();
                        if (xmlConfig.GenerateAssemblyWithRoslyn)
                        {
                            stringOutput.WriteLine();
                        }

                        ObjectPool <StringWriter> stringWriterPool = new ObjectPool <StringWriter>(() => new StringWriter());

                        Parallel.ForEach(userTypes,
                                         (symbolEntry) =>
                        {
                            var output = stringWriterPool.GetObject();

                            output.GetStringBuilder().Clear();
                            GenerateCodeInSingleFile(output, symbolEntry, factory, errorLogger, generationOptions);
                            string text = output.ToString();

                            if (!string.IsNullOrEmpty(text))
                            {
                                lock (masterOutput)
                                {
                                    masterOutput.WriteLine(text);
                                    if (xmlConfig.GenerateAssemblyWithRoslyn && !string.IsNullOrEmpty(xmlConfig.GeneratedAssemblyName))
                                    {
                                        stringOutput.WriteLine(text);
                                    }
                                }
                            }

                            stringWriterPool.PutObject(output);
                        });

                        if (xmlConfig.GenerateAssemblyWithRoslyn && !string.IsNullOrEmpty(xmlConfig.GeneratedAssemblyName))
                        {
                            syntaxTrees.Add(CSharpSyntaxTree.ParseText(stringOutput.ToString(), path: filename, encoding: UTF8Encoding.Default));
                        }
                    }
            }

            logger.WriteLine(" {0}", sw.Elapsed);

            // Compiling the code
            string binFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

            if (xmlConfig.GenerateAssemblyWithRoslyn && !string.IsNullOrEmpty(xmlConfig.GeneratedAssemblyName))
            {
                List <MetadataReference> references = new List <MetadataReference>
                {
                    MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
                    MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location)
                };

                references.AddRange(xmlConfig.ReferencedAssemblies.Select(r => MetadataReference.CreateFromFile(r.Path)));

                foreach (var includedFile in includedFiles)
                {
                    syntaxTrees.Add(CSharpSyntaxTree.ParseText(File.ReadAllText(includedFile.Path), path: includedFile.Path, encoding: System.Text.UTF8Encoding.Default));
                }

                CSharpCompilation compilation = CSharpCompilation.Create(
                    Path.GetFileNameWithoutExtension(xmlConfig.GeneratedAssemblyName),
                    syntaxTrees: syntaxTrees,
                    references: references,
                    options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, platform: Platform.X64));

                logger.WriteLine("Syntax trees: {0}", syntaxTrees.Count);

                string dllFilename = Path.Combine(outputDirectory, xmlConfig.GeneratedAssemblyName);
                string pdbFilename = Path.Combine(outputDirectory, Path.GetFileNameWithoutExtension(dllFilename) + ".pdb");

                using (var dllStream = new FileStream(dllFilename, FileMode.Create))
                    using (var pdbStream = new FileStream(pdbFilename, FileMode.Create))
                    {
                        var result = compilation.Emit(dllStream, !xmlConfig.DisablePdbGeneration ? pdbStream : null);

                        if (!result.Success)
                        {
                            IEnumerable <Diagnostic> failures = result.Diagnostics.Where(diagnostic =>
                                                                                         diagnostic.IsWarningAsError ||
                                                                                         diagnostic.Severity == DiagnosticSeverity.Error);

                            errorLogger.WriteLine("Compile errors (top 1000):");
                            foreach (var diagnostic in failures.Take(1000))
                            {
                                errorLogger.WriteLine(diagnostic);
                            }
                        }
                        else
                        {
                            logger.WriteLine("DLL size: {0}", dllStream.Position);
                            logger.WriteLine("PDB size: {0}", pdbStream.Position);
                        }
                    }

                logger.WriteLine("Compiling: {0}", sw.Elapsed);
            }

            // Check whether we should generate assembly
            if (!xmlConfig.GenerateAssemblyWithRoslyn && !string.IsNullOrEmpty(xmlConfig.GeneratedAssemblyName))
            {
                var codeProvider       = new CSharpCodeProvider();
                var compilerParameters = new CompilerParameters()
                {
                    IncludeDebugInformation = !xmlConfig.DisablePdbGeneration,
                    OutputAssembly          = outputDirectory + xmlConfig.GeneratedAssemblyName,
                };

                compilerParameters.ReferencedAssemblies.AddRange(AppDomain.CurrentDomain.GetAssemblies().Where(a => !a.IsDynamic).Select(a => a.Location).ToArray());
                //compilerParameters.ReferencedAssemblies.AddRange(referencedAssemblies);

                const string MicrosoftCSharpDll = "Microsoft.CSharp.dll";

                if (!compilerParameters.ReferencedAssemblies.Cast <string>().Where(a => a.Contains(MicrosoftCSharpDll)).Any())
                {
                    compilerParameters.ReferencedAssemblies.Add(MicrosoftCSharpDll);
                }

                compilerParameters.ReferencedAssemblies.Add(Path.Combine(binFolder, "CsDebugScript.Engine.dll"));
                compilerParameters.ReferencedAssemblies.Add(Path.Combine(binFolder, "CsDebugScript.CommonUserTypes.dll"));

                var filesToCompile = generatedFiles.Values.Union(includedFiles.Select(f => f.Path)).ToArray();
                var compileResult  = codeProvider.CompileAssemblyFromFile(compilerParameters, filesToCompile);

                if (compileResult.Errors.Count > 0)
                {
                    errorLogger.WriteLine("Compile errors (top 1000):");
                    foreach (CompilerError err in compileResult.Errors.Cast <CompilerError>().Take(1000))
                    {
                        errorLogger.WriteLine(err);
                    }
                }

                logger.WriteLine("Compiling: {0}", sw.Elapsed);
            }

            // Generating props file
            if (!string.IsNullOrEmpty(xmlConfig.GeneratedPropsFileName))
            {
                using (TextWriter output = new StreamWriter(outputDirectory + xmlConfig.GeneratedPropsFileName, false /* append */, System.Text.Encoding.UTF8, 16 * 1024 * 1024))
                {
                    output.WriteLine(@"<?xml version=""1.0"" encoding=""utf-8""?>");
                    output.WriteLine(@"<Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">");
                    output.WriteLine(@"  <ItemGroup>");
                    foreach (var file in generatedFiles.Values)
                    {
                        output.WriteLine(@"    <Compile Include=""{0}"" />", file);
                    }
                    output.WriteLine(@" </ItemGroup>");
                    output.WriteLine(@"</Project>");
                }
            }

            logger.WriteLine("Total time: {0}", sw.Elapsed);
        }