// Copyright (c) 2012-2020 Flax Engine. All rights reserved. using System; using System.CodeDom.Compiler; using System.Collections.Generic; using System.Linq; using System.Reflection; namespace Flax.Build { /// /// Utility for building C# assemblies from custom set of source files. /// public class Assembler { /// /// The default assembly references added to the projects. /// public static readonly Assembly[] DefaultReferences = { //typeof(IntPtr).Assembly, // mscorlib.dll typeof(Enumerable).Assembly, // System.Linq.dll typeof(ISet<>).Assembly, // System.dll typeof(Builder).Assembly, // Flax.Build.exe }; /// /// The output assembly path. Use null to store assembly in the process memory. /// public string OutputPath = null; /// /// The source files for compilation. /// public readonly List SourceFiles = new List(); /// /// The external user assembly references to use while compiling /// public readonly List Assemblies = new List(); /// /// The external user assembly file names to use while compiling /// public readonly List References = new List(); /// /// Builds the assembly. /// /// Throws an exception in case of any errors. /// The created and loaded assembly. public Assembly Build() { Dictionary providerOptions = new Dictionary(); providerOptions.Add("CompilerVersion", "v4.0"); CodeDomProvider provider = new Microsoft.CSharp.CSharpCodeProvider(providerOptions); // Collect references HashSet references = new HashSet(); foreach (var defaultReference in DefaultReferences) references.Add(defaultReference.Location); foreach (var assemblyFile in References) references.Add(assemblyFile); foreach (var assembly in Assemblies) { if (!assembly.IsDynamic) references.Add(assembly.Location); } // Setup compilation options CompilerParameters cp = new CompilerParameters(); cp.GenerateExecutable = false; cp.WarningLevel = 4; cp.TreatWarningsAsErrors = false; cp.ReferencedAssemblies.AddRange(references.ToArray()); if (string.IsNullOrEmpty(OutputPath)) { cp.GenerateInMemory = true; cp.IncludeDebugInformation = false; } else { cp.GenerateInMemory = false; cp.IncludeDebugInformation = true; cp.OutputAssembly = OutputPath; } // HACK: C# will give compilation errors if a LIB variable contains non-existing directories Environment.SetEnvironmentVariable("LIB", null); // Run the compilation CompilerResults cr = provider.CompileAssemblyFromFile(cp, SourceFiles.ToArray()); // Process warnings and errors bool hasError = false; foreach (CompilerError ce in cr.Errors) { if (ce.IsWarning) { Log.Warning(string.Format("{0} at {1}: {2}", ce.FileName, ce.Line, ce.ErrorText)); } else { Log.Error(string.Format("{0} at line {1}: {2}", ce.FileName, ce.Line, ce.ErrorText)); hasError = true; } } if (hasError) throw new Exception("Failed to build assembly."); return cr.CompiledAssembly; } } }