/// <inheritdoc /> public override void PostBuild(TaskGraph graph, BuildOptions buildOptions) { base.PostBuild(graph, buildOptions); // If building engine executable for platform doesn't support referencing it when linking game shared libraries if (UseSeparateMainExecutable(buildOptions)) { // Build additional executable with Main module only that uses shared library using (new ProfileEventScope("BuildExecutable")) { BuildMainExecutable(graph, buildOptions); } // Restore state from PreBuild Modules.Add("Main"); } // Mono on Linux is using dynamic linking and needs additional link files if (buildOptions.Platform.Target == TargetPlatform.Linux && Platform.BuildTargetPlatform == TargetPlatform.Linux && !IsPreBuilt) { var task = graph.Add <Task>(); task.PrerequisiteFiles.Add(Path.Combine(buildOptions.OutputFolder, "libmonosgen-2.0.so")); task.ProducedFiles.Add(Path.Combine(buildOptions.OutputFolder, "libmonosgen-2.0.so.1")); task.WorkingDirectory = buildOptions.OutputFolder; task.CommandPath = "ln"; task.CommandArguments = "-s -f libmonosgen-2.0.so libmonosgen-2.0.so.1"; task = graph.Add <Task>(); task.PrerequisiteFiles.Add(Path.Combine(buildOptions.OutputFolder, "libmonosgen-2.0.so")); task.ProducedFiles.Add(Path.Combine(buildOptions.OutputFolder, "libmonosgen-2.0.so.1.0.0")); task.WorkingDirectory = buildOptions.OutputFolder; task.CommandPath = "ln"; task.CommandArguments = "-s -f libmonosgen-2.0.so libmonosgen-2.0.so.1.0.0"; } }
/// <summary> /// Links the files into an archive (static library). /// </summary> protected virtual Task CreateArchive(TaskGraph graph, BuildOptions options, string outputFilePath) { var linkEnvironment = options.LinkEnv; var task = graph.Add <LinkTask>(); // Setup arguments var args = new List <string>(); { args.Add("rcs"); args.Add(string.Format("-o \"{0}\"", outputFilePath)); } SetupArchiveFilesArgs(graph, options, args, outputFilePath); // Input files task.PrerequisiteFiles.AddRange(linkEnvironment.InputFiles); foreach (var file in linkEnvironment.InputFiles) { args.Add(string.Format("\"{0}\"", file.Replace('\\', '/'))); } // Use a response file (it can contain any commands that you would specify on the command line) bool useResponseFile = true; string responseFile = null; if (useResponseFile) { responseFile = Path.Combine(options.IntermediateFolder, Path.GetFileName(outputFilePath) + ".response"); task.PrerequisiteFiles.Add(responseFile); Utilities.WriteFileIfChanged(responseFile, string.Join(Environment.NewLine, args)); } // Link object into archive task.WorkingDirectory = options.WorkingDirectory; task.CommandPath = ArPath; task.CommandArguments = useResponseFile ? string.Format("@\"{0}\"", responseFile) : string.Join(" ", args); task.Cost = task.PrerequisiteFiles.Count; task.ProducedFiles.Add(outputFilePath); // Generate an index to the contents of an archive /*task = graph.Add<LinkTask>(); * task.CommandPath = RanlibPath; * task.CommandArguments = string.Format("\"{0}\"", outputFilePath); * task.InfoMessage = "Linking " + outputFilePath; * task.PrerequisiteFiles.Add(outputFilePath); * task.ProducedFiles.Add(outputFilePath);*/ return(task); }
/// <inheritdoc /> public override void PreBuild(TaskGraph graph, BuildOptions options) { base.PreBuild(graph, options); // Compile and include resource file if need to if (options.Target.Win32ResourceFile != null && !options.Target.IsPreBuilt && options.Target.OutputType == TargetOutputType.Executable) { var task = graph.Add <CompileCppTask>(); var args = new List <string>(); var sourceFile = options.Target.Win32ResourceFile; // Suppress Startup Banner args.Add("/nologo"); // Language args.Add("/l 0x0409"); // Add preprocessor definitions foreach (var definition in options.CompileEnv.PreprocessorDefinitions) { args.Add(string.Format("/D \"{0}\"", definition)); } // Add include paths foreach (var includePath in options.CompileEnv.IncludePaths) { AddIncludePath(args, includePath); } // Add the resource file to the produced item list var outputFile = Path.Combine(options.IntermediateFolder, Path.GetFileNameWithoutExtension(sourceFile) + ".res"); args.Add(string.Format("/Fo\"{0}\"", outputFile)); options.LinkEnv.InputFiles.Add(outputFile); // Request included files to exist var includes = IncludesCache.FindAllIncludedFiles(sourceFile); task.PrerequisiteFiles.AddRange(includes); // Add the source file args.Add(string.Format("\"{0}\"", sourceFile)); task.ProducedFiles.Add(outputFile); task.WorkingDirectory = options.WorkingDirectory; task.CommandPath = _resourceCompilerPath; task.CommandArguments = string.Join(" ", args); task.PrerequisiteFiles.Add(sourceFile); task.Cost = 1; } }
/// <inheritdoc /> public override void LinkFiles(TaskGraph graph, BuildOptions options, string outputFilePath) { outputFilePath = outputFilePath.Replace('\\', '/'); Task linkTask; switch (options.LinkEnv.Output) { case LinkerOutput.Executable: case LinkerOutput.SharedLibrary: linkTask = CreateBinary(graph, options, outputFilePath); break; case LinkerOutput.StaticLibrary: case LinkerOutput.ImportLibrary: linkTask = CreateArchive(graph, options, outputFilePath); break; default: throw new ArgumentOutOfRangeException(); } if (!options.LinkEnv.DebugInformation) { // Strip debug symbols var task = graph.Add <Task>(); task.ProducedFiles.Add(outputFilePath); task.WorkingDirectory = options.WorkingDirectory; task.CommandPath = StripPath; task.CommandArguments = string.Format("--strip-debug \"{0}\"", outputFilePath); task.InfoMessage = "Striping " + outputFilePath; task.Cost = 1; task.DisableCache = true; task.DependentTasks = new HashSet <Task>(); task.DependentTasks.Add(linkTask); } }
/// <summary> /// Links the files into a binary (executable file or dynamic library). /// </summary> protected virtual Task CreateBinary(TaskGraph graph, BuildOptions options, string outputFilePath) { var linkEnvironment = options.LinkEnv; var task = graph.Add <LinkTask>(); // Setup arguments var args = new List <string>(); { args.Add(string.Format("-o \"{0}\"", outputFilePath)); if (!options.LinkEnv.DebugInformation) { args.Add("-Wl,--strip-debug"); } // TODO: linkEnvironment.LinkTimeCodeGeneration // TODO: linkEnvironment.Optimization // TODO: linkEnvironment.UseIncrementalLinking } SetupLinkFilesArgs(graph, options, args, outputFilePath); args.Add("-Wl,--start-group"); // Input libraries var libraryPaths = new HashSet <string>(); foreach (var library in linkEnvironment.InputLibraries) { var dir = Path.GetDirectoryName(library); var ext = Path.GetExtension(library); if (string.IsNullOrEmpty(dir)) { args.Add(string.Format("\"-l{0}\"", library)); } else if (string.IsNullOrEmpty(ext)) { // Skip executable } else if (ext == ".so") { // Link against dynamic library task.PrerequisiteFiles.Add(library); libraryPaths.Add(dir); args.Add(string.Format("\"-l{0}\"", GetLibName(library))); } else { task.PrerequisiteFiles.Add(library); args.Add(string.Format("\"{0}\"", GetLibName(library))); } } foreach (var library in options.Libraries) { var dir = Path.GetDirectoryName(library); var ext = Path.GetExtension(library); if (string.IsNullOrEmpty(dir)) { args.Add(string.Format("\"-l{0}\"", library)); } else if (string.IsNullOrEmpty(ext)) { // Skip executable } else if (ext == ".so") { // Link against dynamic library task.PrerequisiteFiles.Add(library); libraryPaths.Add(dir); args.Add(string.Format("\"-l{0}\"", GetLibName(library))); } else { task.PrerequisiteFiles.Add(library); args.Add(string.Format("\"{0}\"", GetLibName(library))); } } // Input files task.PrerequisiteFiles.AddRange(linkEnvironment.InputFiles); foreach (var file in linkEnvironment.InputFiles) { args.Add(string.Format("\"{0}\"", file.Replace('\\', '/'))); } // Additional lib paths libraryPaths.AddRange(linkEnvironment.LibraryPaths); foreach (var path in libraryPaths) { args.Add(string.Format("-L\"{0}\"", path.Replace('\\', '/'))); } args.Add("-Wl,--end-group"); // Use a response file (it can contain any commands that you would specify on the command line) bool useResponseFile = true; string responseFile = null; if (useResponseFile) { responseFile = Path.Combine(options.IntermediateFolder, Path.GetFileName(outputFilePath) + ".response"); task.PrerequisiteFiles.Add(responseFile); Utilities.WriteFileIfChanged(responseFile, string.Join(Environment.NewLine, args)); } // Link task.WorkingDirectory = options.WorkingDirectory; task.CommandPath = LdPath; task.CommandArguments = useResponseFile ? string.Format("@\"{0}\"", responseFile) : string.Join(" ", args); task.InfoMessage = "Linking " + outputFilePath; task.Cost = task.PrerequisiteFiles.Count; task.ProducedFiles.Add(outputFilePath); return(task); }
/// <inheritdoc /> public override CompileOutput CompileCppFiles(TaskGraph graph, BuildOptions options, List <string> sourceFiles, string outputPath) { var compileEnvironment = options.CompileEnv; var output = new CompileOutput(); // Setup arguments shared by all source files var commonArgs = new List <string>(); SetupCompileCppFilesArgs(graph, options, commonArgs, outputPath); { commonArgs.Add("-c"); commonArgs.Add("-pipe"); commonArgs.Add("-x"); commonArgs.Add("c++"); commonArgs.Add("-std=c++14"); commonArgs.Add("-Wdelete-non-virtual-dtor"); commonArgs.Add("-fno-math-errno"); commonArgs.Add("-fdiagnostics-format=msvc"); if (Architecture == TargetArchitecture.ARM || Architecture == TargetArchitecture.ARM64) { commonArgs.Add("-fsigned-char"); } if (compileEnvironment.RuntimeTypeInfo) { commonArgs.Add("-frtti"); } else { commonArgs.Add("-fno-rtti"); } if (compileEnvironment.TreatWarningsAsErrors) { commonArgs.Add("-Wall -Werror"); } // TODO: compileEnvironment.IntrinsicFunctions // TODO: compileEnvironment.FunctionLevelLinking // TODO: compileEnvironment.FavorSizeOrSpeed // TODO: compileEnvironment.RuntimeChecks // TODO: compileEnvironment.StringPooling // TODO: compileEnvironment.BufferSecurityCheck if (compileEnvironment.DebugInformation) { commonArgs.Add("-glldb"); } commonArgs.Add("-pthread"); if (compileEnvironment.Optimization) { commonArgs.Add("-O2"); } else { commonArgs.Add("-O0"); } if (!compileEnvironment.Inlining) { commonArgs.Add("-fno-inline-functions"); commonArgs.Add("-fno-inline"); } if (compileEnvironment.EnableExceptions) { commonArgs.Add("-fexceptions"); } else { commonArgs.Add("-fno-exceptions"); } } // Add preprocessor definitions foreach (var definition in compileEnvironment.PreprocessorDefinitions) { commonArgs.Add(string.Format("-D \"{0}\"", definition)); } // Add include paths foreach (var includePath in compileEnvironment.IncludePaths) { commonArgs.Add(string.Format("-I\"{0}\"", includePath.Replace('\\', '/'))); } // Compile all C++ files var args = new List <string>(); foreach (var sourceFile in sourceFiles) { var sourceFilename = Path.GetFileNameWithoutExtension(sourceFile); var task = graph.Add <CompileCppTask>(); // Use shared arguments args.Clear(); args.AddRange(commonArgs); // Object File Name var objFile = Path.Combine(outputPath, sourceFilename + ".o"); args.Add(string.Format("-o \"{0}\"", objFile.Replace('\\', '/'))); output.ObjectFiles.Add(objFile); task.ProducedFiles.Add(objFile); // Source File Name args.Add("\"" + sourceFile.Replace('\\', '/') + "\""); // Request included files to exist var includes = IncludesCache.FindAllIncludedFiles(sourceFile); task.PrerequisiteFiles.AddRange(includes); // Compile task.WorkingDirectory = options.WorkingDirectory; task.CommandPath = ClangPath; task.CommandArguments = string.Join(" ", args); task.PrerequisiteFiles.Add(sourceFile); task.InfoMessage = Path.GetFileName(sourceFile); task.Cost = task.PrerequisiteFiles.Count; // TODO: include source file size estimation to improve tasks sorting } return(output); }
private static void BuildTargetBindings(RulesAssembly rules, TaskGraph graph, BuildData buildData) { var workspaceRoot = buildData.TargetOptions.WorkingDirectory; var args = new List <string>(); var referencesToCopy = new HashSet <KeyValuePair <string, string> >(); var buildOptions = buildData.TargetOptions; foreach (var binaryModule in buildData.BinaryModules) { var binaryModuleName = binaryModule.Key; using (new ProfileEventScope(binaryModuleName)) { // TODO: add support for extending this code and support generating bindings projects for other scripting languages var project = GetModuleProject(binaryModule.First(), buildData); // Get source files var sourceFiles = new List <string>(); foreach (var module in binaryModule) { sourceFiles.AddRange(buildData.Modules[module].SourceFiles.Where(x => x.EndsWith(".cs"))); } sourceFiles.RemoveAll(x => x.EndsWith(BuildFilesPostfix)); var moduleGen = Path.Combine(project.ProjectFolderPath, "Source", binaryModuleName + ".Gen.cs"); if (!sourceFiles.Contains(moduleGen)) { sourceFiles.Add(moduleGen); } sourceFiles.Sort(); // Setup build options var buildPlatform = Platform.BuildPlatform.Target; var outputPath = Path.GetDirectoryName(buildData.Target.GetOutputFilePath(buildOptions)); var outputFile = Path.Combine(outputPath, binaryModuleName + ".CSharp.dll"); var outputDocFile = Path.Combine(outputPath, binaryModuleName + ".CSharp.xml"); var monoRoot = Path.Combine(Globals.EngineRoot, "Source", "Platforms", "Editor", "Windows", "Mono"); var cscPath = Path.Combine(monoRoot, "lib", "mono", "4.5", "csc.exe"); var exePath = buildPlatform == TargetPlatform.Windows ? Path.Combine(monoRoot, "bin", "mono.exe") : "mono"; var referenceAssemblies = Path.Combine(monoRoot, "lib", "mono", "4.5-api"); var references = new HashSet <string>(buildOptions.ScriptingAPI.FileReferences); foreach (var module in binaryModule) { if (!buildData.Modules.TryGetValue(module, out var moduleBuildOptions)) { continue; } // Find references based on the modules dependencies foreach (var dependencyName in moduleBuildOptions.PublicDependencies.Concat(moduleBuildOptions.PrivateDependencies)) { var dependencyModule = buildData.Rules.GetModule(dependencyName); if (dependencyModule != null && !string.IsNullOrEmpty(dependencyModule.BinaryModuleName) && dependencyModule.BinaryModuleName != binaryModuleName && buildData.Modules.TryGetValue(dependencyModule, out var dependencyModuleOptions)) { foreach (var x in buildData.BinaryModules) { if (x.Key == null || x.Key != dependencyModule.BinaryModuleName) { continue; } // Reference module output binary references.Add(Path.Combine(outputPath, dependencyModule.BinaryModuleName + ".CSharp.dll")); } foreach (var e in buildData.ReferenceBuilds) { foreach (var q in e.Value.BuildInfo.BinaryModules) { if (q.Name == dependencyModule.BinaryModuleName && !string.IsNullOrEmpty(q.ManagedPath)) { // Reference binary module build build for referenced target references.Add(q.ManagedPath); break; } } } } } } // Setup C# compiler arguments args.Clear(); args.Add("/nologo"); args.Add("/target:library"); args.Add("/platform:AnyCPU"); args.Add("/debug+"); args.Add("/debug:portable"); args.Add("/errorreport:prompt"); args.Add("/preferreduilang:en-US"); args.Add("/highentropyva+"); args.Add("/deterministic"); args.Add("/nostdlib+"); args.Add("/errorendlocation"); args.Add("/utf8output"); args.Add("/warn:4"); args.Add("/unsafe"); args.Add("/fullpaths"); args.Add("/langversion:7.3"); if (buildOptions.ScriptingAPI.IgnoreMissingDocumentationWarnings) { args.Add("-nowarn:1591"); } args.Add(buildData.Configuration == TargetConfiguration.Debug ? "/optimize-" : "/optimize+"); args.Add(string.Format("/out:\"{0}\"", outputFile)); args.Add(string.Format("/doc:\"{0}\"", outputDocFile)); if (buildOptions.ScriptingAPI.Defines.Count != 0) { args.Add("/define:" + string.Join(";", buildOptions.ScriptingAPI.Defines)); } if (buildData.Configuration == TargetConfiguration.Debug) { args.Add("/define:DEBUG"); } args.Add(string.Format("/reference:\"{0}{1}mscorlib.dll\"", referenceAssemblies, Path.DirectorySeparatorChar)); foreach (var reference in buildOptions.ScriptingAPI.SystemReferences) { args.Add(string.Format("/reference:\"{0}{2}{1}.dll\"", referenceAssemblies, reference, Path.DirectorySeparatorChar)); } foreach (var reference in references) { args.Add(string.Format("/reference:\"{0}\"", reference)); } foreach (var sourceFile in sourceFiles) { args.Add("\"" + sourceFile + "\""); } // Generate response file with source files paths and compilation arguments string responseFile = Path.Combine(buildOptions.IntermediateFolder, binaryModuleName + ".CSharp.response"); Utilities.WriteFileIfChanged(responseFile, string.Join(Environment.NewLine, args)); // Create C# compilation task var task = graph.Add <Task>(); task.PrerequisiteFiles.Add(responseFile); task.PrerequisiteFiles.AddRange(sourceFiles); task.PrerequisiteFiles.AddRange(references); task.ProducedFiles.Add(outputFile); task.WorkingDirectory = workspaceRoot; task.CommandPath = exePath; task.CommandArguments = $"\"{cscPath}\" /noconfig @\"{responseFile}\""; task.InfoMessage = "Compiling C# API for " + binaryModuleName; task.Cost = task.PrerequisiteFiles.Count; // Copy referenced assemblies foreach (var reference in buildOptions.ScriptingAPI.FileReferences) { var dstFile = Path.Combine(outputPath, Path.GetFileName(reference)); if (dstFile == reference) { continue; } referencesToCopy.Add(new KeyValuePair <string, string>(dstFile, reference)); } } } // Copy files (using hash set to prevent copying the same file twice when building multiple scripting modules using the same files) foreach (var e in referencesToCopy) { var dst = e.Key; var src = e.Value; graph.AddCopyFile(dst, src); var srcPdb = Path.ChangeExtension(src, "pdb"); if (File.Exists(srcPdb)) { graph.AddCopyFile(Path.ChangeExtension(dst, "pdb"), srcPdb); } var srcXml = Path.ChangeExtension(src, "xml"); if (File.Exists(srcXml)) { graph.AddCopyFile(Path.ChangeExtension(dst, "xml"), srcXml); } } }
/// <inheritdoc /> public override void LinkFiles(TaskGraph graph, BuildOptions options, string outputFilePath) { var linkEnvironment = options.LinkEnv; var task = graph.Add <LinkTask>(); // Setup arguments var args = new List <string>(); SetupLinkFilesArgs(graph, options, args); { // Suppress startup banner args.Add("/NOLOGO"); // Report internal compiler errors args.Add("/ERRORREPORT:PROMPT"); // Output File Name args.Add(string.Format("/OUT:\"{0}\"", outputFilePath)); // Specify target platform switch (Architecture) { case TargetArchitecture.x86: args.Add("/MACHINE:x86"); break; case TargetArchitecture.x64: args.Add("/MACHINE:x64"); break; case TargetArchitecture.ARM: case TargetArchitecture.ARM64: args.Add("/MACHINE:ARM"); break; default: throw new InvalidArchitectureException(Architecture); } // Specify subsystem args.Add("/SUBSYSTEM:WINDOWS"); // Generate Windows Metadata if (linkEnvironment.GenerateWindowsMetadata) { args.Add("/WINMD"); args.Add(string.Format("/WINMDFILE:\"{0}\"", Path.ChangeExtension(outputFilePath, "winmd"))); args.Add("/APPCONTAINER"); if (linkEnvironment.Output == LinkerOutput.SharedLibrary) { args.Add("/DYNAMICBASE"); } } if (linkEnvironment.LinkTimeCodeGeneration) { // Link-time code generation args.Add("/LTCG"); } if (linkEnvironment.Output == LinkerOutput.ImportLibrary) { // Create an import library args.Add("/DEF"); // Ignore libraries args.Add("/NODEFAULTLIB"); // Specify the name args.Add(string.Format("/NAME:\"{0}\"", Path.GetFileNameWithoutExtension(outputFilePath))); // Ignore warnings about files with no public symbols args.Add("/IGNORE:4221"); } else { // Don't create Side-by-Side Assembly Manifest args.Add("/MANIFEST:NO"); // Fixed Base Address args.Add("/FIXED:NO"); if (Architecture == TargetArchitecture.x86) { // Handle Large Addresses args.Add("/LARGEADDRESSAWARE"); } // Compatible with Data Execution Prevention args.Add("/NXCOMPAT"); // Allow delay-loaded DLLs to be explicitly unloaded args.Add("/DELAY:UNLOAD"); if (linkEnvironment.Output == LinkerOutput.SharedLibrary) { // Build a DLL args.Add("/DLL"); } // Redirect imports LIB file auto-generated for EXE/DLL var libFile = Path.ChangeExtension(outputFilePath, Platform.StaticLibraryFileExtension); args.Add("/IMPLIB:\"" + libFile + "\""); task.ProducedFiles.Add(libFile); // Don't embed the full PDB path args.Add("/PDBALTPATH:%_PDB%"); // Optimize if (linkEnvironment.Optimization && !linkEnvironment.UseIncrementalLinking) { // Generate an EXE checksum args.Add("/RELEASE"); // Eliminate unreferenced symbols args.Add("/OPT:REF"); // Remove redundant COMDATs args.Add("/OPT:ICF"); } else { // Keep symbols that are unreferenced args.Add("/OPT:NOREF"); // Disable identical COMDAT folding args.Add("/OPT:NOICF"); } // Link Incrementally if (linkEnvironment.UseIncrementalLinking) { args.Add("/INCREMENTAL"); } else { args.Add("/INCREMENTAL:NO"); } if (linkEnvironment.DebugInformation) { // Generate debug information if (Toolset != WindowsPlatformToolset.v140 && linkEnvironment.UseFastPDBLinking) { args.Add("/DEBUG:FASTLINK"); } else if (linkEnvironment.UseFullDebugInformation) { args.Add("/DEBUG:FULL"); } else { args.Add("/DEBUG"); } // Use Program Database var pdbFile = Path.ChangeExtension(outputFilePath, Platform.ProgramDatabaseFileExtension); args.Add(string.Format("/PDB:\"{0}\"", pdbFile)); task.ProducedFiles.Add(pdbFile); } } } // Delay-load DLLs if (linkEnvironment.Output == LinkerOutput.Executable || linkEnvironment.Output == LinkerOutput.SharedLibrary) { foreach (var dll in options.DelayLoadLibraries) { args.Add(string.Format("/DELAYLOAD:\"{0}\"", dll)); } } // Additional lib paths foreach (var libpath in linkEnvironment.LibraryPaths) { args.Add(string.Format("/LIBPATH:\"{0}\"", libpath)); } // Input libraries task.PrerequisiteFiles.AddRange(linkEnvironment.InputLibraries); foreach (var library in linkEnvironment.InputLibraries) { args.Add(string.Format("\"{0}\"", library)); } // Input files task.PrerequisiteFiles.AddRange(linkEnvironment.InputFiles); foreach (var file in linkEnvironment.InputFiles) { args.Add(string.Format("\"{0}\"", file)); } // Use a response file (it can contain any commands that you would specify on the command line) bool useResponseFile = true; string responseFile = null; if (useResponseFile) { responseFile = Path.Combine(options.IntermediateFolder, Path.GetFileName(outputFilePath) + ".response"); task.PrerequisiteFiles.Add(responseFile); Utilities.WriteFileIfChanged(responseFile, string.Join(Environment.NewLine, args)); } // Link task.WorkingDirectory = options.WorkingDirectory; task.CommandPath = linkEnvironment.Output == LinkerOutput.ImportLibrary ? _libToolPath : _linkerPath; task.CommandArguments = useResponseFile ? string.Format("@\"{0}\"", responseFile) : string.Join(" ", args); if (linkEnvironment.Output == LinkerOutput.ImportLibrary) { task.InfoMessage = "Building import library " + outputFilePath; } else { task.InfoMessage = "Linking " + outputFilePath; } task.Cost = task.PrerequisiteFiles.Count; task.ProducedFiles.Add(outputFilePath); // Check if need to generate documentation if (linkEnvironment.GenerateDocumentation) { args.Clear(); var docTask = graph.Add <Task>(); // Use old input format args.Add("/old"); args.Add(string.Format("\"{0}\"", Path.GetFileNameWithoutExtension(outputFilePath))); // Suppress copyright message args.Add("/nologo"); // Output file var outputDocFile = Path.ChangeExtension(outputFilePath, "xml"); docTask.ProducedFiles.Add(outputDocFile); args.Add(string.Format("/Fo\"{0}\"", outputDocFile)); // Input files docTask.PrerequisiteFiles.AddRange(linkEnvironment.DocumentationFiles); foreach (var file in linkEnvironment.DocumentationFiles) { args.Add(string.Format("/Fs\"{0}\"", file)); } // Generate docs docTask.WorkingDirectory = options.WorkingDirectory; docTask.CommandPath = _xdcmakePath; docTask.CommandArguments = string.Join(" ", args); docTask.Cost = linkEnvironment.DocumentationFiles.Count; } // Check if need to generate metadata file if (linkEnvironment.GenerateWindowsMetadata) { var configFile = Path.Combine(options.IntermediateFolder, Path.GetFileNameWithoutExtension(outputFilePath) + ".priconfig.xml"); var manifestFile = Path.Combine(options.IntermediateFolder, Path.GetFileNameWithoutExtension(outputFilePath) + ".AppxManifest.xml"); var priFile = Path.ChangeExtension(outputFilePath, "pri"); // Generate pri config file var priConfigTask = graph.Add <Task>(); priConfigTask.WorkingDirectory = options.WorkingDirectory; priConfigTask.CommandPath = _makepriPath; priConfigTask.CommandArguments = string.Format("createconfig /cf \"{0}\" /dq en-US /o", configFile); priConfigTask.Cost = 1; priConfigTask.ProducedFiles.Add(configFile); // Create AppxManifest file { using (var stringWriter = new StringWriterWithEncoding(Encoding.UTF8)) using (var xmlTextWriter = XmlWriter.Create(stringWriter, new XmlWriterSettings { Encoding = Encoding.UTF8, Indent = true, })) { xmlTextWriter.WriteStartDocument(); // Package { xmlTextWriter.WriteStartElement("Package", "http://schemas.microsoft.com/appx/2010/manifest"); // Identity { xmlTextWriter.WriteStartElement("Identity"); xmlTextWriter.WriteAttributeString("Name", "FlaxGame"); xmlTextWriter.WriteAttributeString("Publisher", "CN=Flax, O=Flax, C=Poland"); xmlTextWriter.WriteAttributeString("Version", "1.0.0.0"); // TODO: get Flax version number switch (Architecture) { case TargetArchitecture.AnyCPU: xmlTextWriter.WriteAttributeString("ProcessorArchitecture", "neutral"); break; case TargetArchitecture.x86: xmlTextWriter.WriteAttributeString("ProcessorArchitecture", "x86"); break; case TargetArchitecture.x64: xmlTextWriter.WriteAttributeString("ProcessorArchitecture", "x64"); break; case TargetArchitecture.ARM: case TargetArchitecture.ARM64: xmlTextWriter.WriteAttributeString("ProcessorArchitecture", "arm"); break; default: throw new InvalidArchitectureException(Architecture); } xmlTextWriter.WriteEndElement(); } // Properties { xmlTextWriter.WriteStartElement("Properties"); // TODO: better logo handling var logoSrcPath = Path.Combine(Globals.EngineRoot, "Source", "Logo.png"); var logoDstPath = Path.Combine(options.IntermediateFolder, "Logo.png"); if (!File.Exists(logoDstPath)) { Utilities.FileCopy(logoSrcPath, logoDstPath); } xmlTextWriter.WriteElementString("DisplayName", "FlaxGame"); xmlTextWriter.WriteElementString("PublisherDisplayName", "Flax"); xmlTextWriter.WriteElementString("Logo", "Logo.png"); xmlTextWriter.WriteEndElement(); } // Resources { xmlTextWriter.WriteStartElement("Resources"); xmlTextWriter.WriteStartElement("Resource"); xmlTextWriter.WriteAttributeString("Language", "en-us"); xmlTextWriter.WriteEndElement(); xmlTextWriter.WriteEndElement(); } // Prerequisites { xmlTextWriter.WriteStartElement("Prerequisites"); xmlTextWriter.WriteElementString("OSMinVersion", "6.2"); xmlTextWriter.WriteElementString("OSMaxVersionTested", "6.2"); xmlTextWriter.WriteEndElement(); } } xmlTextWriter.WriteEndDocument(); xmlTextWriter.Flush(); // Save manifest to file var contents = stringWriter.GetStringBuilder().ToString(); Utilities.WriteFileIfChanged(manifestFile, contents); } } var dummyWorkspace = Path.Combine(options.IntermediateFolder, "Dummy"); if (!Directory.Exists(dummyWorkspace)) { Directory.CreateDirectory(dummyWorkspace); } // Generate pri file var priNewFile = graph.Add <Task>(); priNewFile.WorkingDirectory = options.WorkingDirectory; priNewFile.CommandPath = _makepriPath; priNewFile.CommandArguments = string.Format("new /cf \"{0}\" /pr \"{1}\" /of \"{2}\" /mn \"{3}\" /o", configFile, dummyWorkspace, priFile, manifestFile); priNewFile.Cost = 1; priNewFile.PrerequisiteFiles.Add(configFile); priNewFile.ProducedFiles.Add(priFile); } }
/// <inheritdoc /> public override CompileOutput CompileCppFiles(TaskGraph graph, BuildOptions options, List <string> sourceFiles, string outputPath) { var compileEnvironment = options.CompileEnv; var output = new CompileOutput(); // Setup arguments shared by all source files var commonArgs = new List <string>(); SetupCompileCppFilesArgs(graph, options, commonArgs); { // Suppress Startup Banner commonArgs.Add("/nologo"); // Compile Without Linking commonArgs.Add("/c"); // Generate Intrinsic Functions if (compileEnvironment.IntrinsicFunctions) { commonArgs.Add("/Oi"); } // Enable Function-Level Linking if (compileEnvironment.FunctionLevelLinking) { commonArgs.Add("/Gy"); } else { commonArgs.Add("/Gy-"); } // List Include Files //commonArgs.Add("/showIncludes"); // Code Analysis commonArgs.Add("/analyze-"); // Remove unreferenced COMDAT commonArgs.Add("/Zc:inline"); // Favor Small Code, Favor Fast Code if (compileEnvironment.FavorSizeOrSpeed == FavorSizeOrSpeed.FastCode) { commonArgs.Add("/Ot"); } else if (compileEnvironment.FavorSizeOrSpeed == FavorSizeOrSpeed.SmallCode) { commonArgs.Add("/Os"); } // Run-Time Error Checks if (compileEnvironment.RuntimeChecks && !compileEnvironment.CompileAsWinRT) { commonArgs.Add("/RTC1"); } // Enable Additional Security Checks if (compileEnvironment.RuntimeChecks) { commonArgs.Add("/sdl"); } // Inline Function Expansion if (compileEnvironment.Inlining) { commonArgs.Add("/Ob2"); } if (compileEnvironment.DebugInformation) { // Debug Information Format commonArgs.Add("/Zi"); // Enhance Optimized Debugging commonArgs.Add("/Zo"); } if (compileEnvironment.Optimization) { // Enable Most Speed Optimizations commonArgs.Add("/Ox"); // Generate Intrinsic Functions commonArgs.Add("/Oi"); // Frame-Pointer Omission commonArgs.Add("/Oy"); if (compileEnvironment.WholeProgramOptimization) { // Whole Program Optimization commonArgs.Add("/GL"); } } else { // Disable compiler optimizations (Debug) commonArgs.Add("/Od"); // Frame-Pointer Omission commonArgs.Add("/Oy-"); } // Full Path of Source Code File in Diagnostics commonArgs.Add("/FC"); // Report Internal Compiler Errors commonArgs.Add("/errorReport:prompt"); // Exception Handling Model if (!compileEnvironment.CompileAsWinRT) { if (compileEnvironment.EnableExceptions) { commonArgs.Add("/EHsc"); } else { commonArgs.Add("/D_HAS_EXCEPTIONS=0"); } } // Eliminate Duplicate Strings if (compileEnvironment.StringPooling) { commonArgs.Add("/GF"); } else { commonArgs.Add("/GF-"); } // Use Run-Time Library if (compileEnvironment.UseDebugCRT) { commonArgs.Add("/MDd"); } else { commonArgs.Add("/MD"); } // Specify floating-point behavior commonArgs.Add("/fp:fast"); commonArgs.Add("/fp:except-"); // Buffer Security Check if (compileEnvironment.BufferSecurityCheck) { commonArgs.Add("/GS"); } else { commonArgs.Add("/GS-"); } // Enable Run-Time Type Information if (compileEnvironment.RuntimeTypeInfo) { commonArgs.Add("/GR"); } else { commonArgs.Add("/GR-"); } // Treats all compiler warnings as errors if (compileEnvironment.TreatWarningsAsErrors) { commonArgs.Add("/WX"); } else { commonArgs.Add("/WX-"); } // Show warnings // TODO: compile with W4 and fix all warnings commonArgs.Add("/W3"); // Silence macro redefinition warning commonArgs.Add("/wd\"4005\""); // wchar_t is Native Type commonArgs.Add("/Zc:wchar_t"); // Common Language Runtime Compilation if (compileEnvironment.CompileAsWinRT) { commonArgs.Add("/clr"); } // Windows Runtime Compilation if (compileEnvironment.WinRTComponentExtensions) { commonArgs.Add("/ZW"); //commonArgs.Add("/ZW:nostdlib"); var dir = GetCppCXMetadataDirectory(); if (dir != null) { commonArgs.Add(string.Format("/AI\"{0}\"", dir)); commonArgs.Add(string.Format("/FU\"{0}\\platform.winmd\"", dir)); } } } // Add preprocessor definitions foreach (var definition in compileEnvironment.PreprocessorDefinitions) { commonArgs.Add(string.Format("/D \"{0}\"", definition)); } // Add include paths foreach (var includePath in compileEnvironment.IncludePaths) { AddIncludePath(commonArgs, includePath); } // Compile all C++ files var args = new List <string>(); foreach (var sourceFile in sourceFiles) { var sourceFilename = Path.GetFileNameWithoutExtension(sourceFile); var task = graph.Add <CompileCppTask>(); // Use shared arguments args.Clear(); args.AddRange(commonArgs); if (compileEnvironment.DebugInformation) { // Program Database File Name var pdbFile = Path.Combine(outputPath, sourceFilename + ".pdb"); args.Add(string.Format("/Fd\"{0}\"", pdbFile)); output.DebugDataFiles.Add(pdbFile); } if (compileEnvironment.GenerateDocumentation) { // Process Documentation Comments var docFile = Path.Combine(outputPath, sourceFilename + ".xdc"); args.Add(string.Format("/doc\"{0}\"", docFile)); output.DocumentationFiles.Add(docFile); } // Object File Name var objFile = Path.Combine(outputPath, sourceFilename + ".obj"); args.Add(string.Format("/Fo\"{0}\"", objFile)); output.ObjectFiles.Add(objFile); task.ProducedFiles.Add(objFile); // Source File Name args.Add("\"" + sourceFile + "\""); // Request included files to exist var includes = IncludesCache.FindAllIncludedFiles(sourceFile); task.PrerequisiteFiles.AddRange(includes); // Compile task.WorkingDirectory = options.WorkingDirectory; task.CommandPath = _compilerPath; task.CommandArguments = string.Join(" ", args); task.PrerequisiteFiles.Add(sourceFile); task.Cost = task.PrerequisiteFiles.Count; // TODO: include source file size estimation to improve tasks sorting } return(output); }
/// <inheritdoc /> public override void LinkFiles(TaskGraph graph, BuildOptions options, string outputFilePath) { var linkEnvironment = options.LinkEnv; var task = graph.Add <LinkTask>(); var isArchive = linkEnvironment.Output == LinkerOutput.StaticLibrary || linkEnvironment.Output == LinkerOutput.ImportLibrary; // Setup arguments var args = new List <string>(); { args.Add(string.Format("-o \"{0}\"", outputFilePath)); AddArgsCommon(options, args); if (isArchive) { args.Add("-static"); } else { args.Add("-dead_strip"); args.Add("-rpath @executable_path/"); if (linkEnvironment.Output == LinkerOutput.SharedLibrary) { args.Add("-dynamiclib"); } } } // Input libraries var libraryPaths = new HashSet <string>(); var dylibs = new HashSet <string>(); foreach (var library in linkEnvironment.InputLibraries) { var dir = Path.GetDirectoryName(library); var ext = Path.GetExtension(library); if (ext == ".framework") { args.Add(string.Format("-framework {0}", library.Substring(0, library.Length - ext.Length))); } else if (string.IsNullOrEmpty(dir)) { args.Add(string.Format("\"-l{0}\"", library)); } else if (string.IsNullOrEmpty(ext)) { // Skip executable } else if (ext == ".dylib") { // Link against dynamic library dylibs.Add(library); task.PrerequisiteFiles.Add(library); libraryPaths.Add(dir); args.Add(string.Format("\"{0}\"", library)); } else { task.PrerequisiteFiles.Add(library); args.Add(string.Format("\"{0}\"", GetLibName(library))); } } foreach (var library in options.Libraries) { var dir = Path.GetDirectoryName(library); var ext = Path.GetExtension(library); if (ext == ".framework") { args.Add(string.Format("-framework {0}", library.Substring(0, library.Length - ext.Length))); } else if (string.IsNullOrEmpty(dir)) { args.Add(string.Format("\"-l{0}\"", library)); } else if (string.IsNullOrEmpty(ext)) { // Skip executable } else if (ext == ".dylib") { // Link against dynamic library dylibs.Add(library); task.PrerequisiteFiles.Add(library); libraryPaths.Add(dir); args.Add(string.Format("\"{0}\"", library)); } else { task.PrerequisiteFiles.Add(library); args.Add(string.Format("\"{0}\"", GetLibName(library))); } } // Input files task.PrerequisiteFiles.AddRange(linkEnvironment.InputFiles); foreach (var file in linkEnvironment.InputFiles) { var ext = Path.GetExtension(file); if (ext == ".framework") { args.Add(string.Format("-framework {0}", file.Substring(0, file.Length - ext.Length))); } else { args.Add(string.Format("\"{0}\"", file.Replace('\\', '/'))); } } // Additional lib paths libraryPaths.AddRange(linkEnvironment.LibraryPaths); foreach (var path in libraryPaths) { args.Add(string.Format("-L\"{0}\"", path.Replace('\\', '/'))); } // Use a response file (it can contain any commands that you would specify on the command line) bool useResponseFile = true; string responseFile = null; if (useResponseFile) { responseFile = Path.Combine(options.IntermediateFolder, Path.GetFileName(outputFilePath) + ".response"); task.PrerequisiteFiles.Add(responseFile); Utilities.WriteFileIfChanged(responseFile, string.Join(Environment.NewLine, args)); } // Link task.WorkingDirectory = options.WorkingDirectory; task.CommandPath = isArchive ? ArchiverPath : LinkerPath; task.CommandArguments = useResponseFile ? string.Format("@\"{0}\"", responseFile) : string.Join(" ", args); task.InfoMessage = "Linking " + outputFilePath; task.Cost = task.PrerequisiteFiles.Count; task.ProducedFiles.Add(outputFilePath); Task lastTask = task; if (options.LinkEnv.Output == LinkerOutput.Executable) { // Fix rpath for dynamic libraries foreach (var library in dylibs) { var rpathTask = graph.Add <Task>(); rpathTask.ProducedFiles.Add(outputFilePath); rpathTask.WorkingDirectory = options.WorkingDirectory; rpathTask.CommandPath = "install_name_tool"; var filename = Path.GetFileName(library); var outputFolder = Path.GetDirectoryName(outputFilePath); rpathTask.CommandArguments = string.Format("-change \"{0}/{1}\" \"@loader_path/{1}\" {2}", outputFolder, filename, outputFilePath); rpathTask.InfoMessage = "Fixing rpath to " + filename; rpathTask.Cost = 1; rpathTask.DisableCache = true; rpathTask.DependentTasks = new HashSet <Task>(); rpathTask.DependentTasks.Add(lastTask); lastTask = rpathTask; } } if (!options.LinkEnv.DebugInformation) { // Strip debug symbols var stripTask = graph.Add <Task>(); stripTask.ProducedFiles.Add(outputFilePath); stripTask.WorkingDirectory = options.WorkingDirectory; stripTask.CommandPath = "strip"; stripTask.CommandArguments = string.Format("\"{0}\" -S", outputFilePath); stripTask.InfoMessage = "Striping " + outputFilePath; stripTask.Cost = 1; stripTask.DisableCache = true; stripTask.DependentTasks = new HashSet <Task>(); stripTask.DependentTasks.Add(lastTask); } }
/// <inheritdoc /> public override CompileOutput CompileCppFiles(TaskGraph graph, BuildOptions options, List <string> sourceFiles, string outputPath) { var compileEnvironment = options.CompileEnv; var output = new CompileOutput(); // Setup arguments shared by all source files var commonArgs = new List <string>(); { commonArgs.Add("-c"); commonArgs.Add("-fmessage-length=0"); commonArgs.Add("-pipe"); commonArgs.Add("-x"); commonArgs.Add("objective-c++"); commonArgs.Add("-std=c++14"); commonArgs.Add("-stdlib=libc++"); AddArgsCommon(options, commonArgs); switch (Architecture) { case TargetArchitecture.x64: commonArgs.Add("-msse2"); break; } commonArgs.Add("-Wdelete-non-virtual-dtor"); commonArgs.Add("-fno-math-errno"); commonArgs.Add("-fasm-blocks"); commonArgs.Add("-fpascal-strings"); commonArgs.Add("-fdiagnostics-format=msvc"); commonArgs.Add("-Wno-absolute-value"); commonArgs.Add("-Wno-nullability-completeness"); commonArgs.Add("-Wno-undef-prefix"); commonArgs.Add("-Wno-expansion-to-defined"); commonArgs.Add("-Wno-non-virtual-dtor"); // Hide all symbols by default commonArgs.Add("-fvisibility-inlines-hidden"); commonArgs.Add("-fvisibility-ms-compat"); if (compileEnvironment.RuntimeTypeInfo) { commonArgs.Add("-frtti"); } else { commonArgs.Add("-fno-rtti"); } if (compileEnvironment.TreatWarningsAsErrors) { commonArgs.Add("-Wall -Werror"); } // TODO: compileEnvironment.IntrinsicFunctions // TODO: compileEnvironment.FunctionLevelLinking // TODO: compileEnvironment.FavorSizeOrSpeed // TODO: compileEnvironment.RuntimeChecks // TODO: compileEnvironment.StringPooling // TODO: compileEnvironment.BufferSecurityCheck if (compileEnvironment.DebugInformation) { commonArgs.Add("-gdwarf-2"); } commonArgs.Add("-pthread"); if (compileEnvironment.Optimization) { commonArgs.Add("-O3"); } else { commonArgs.Add("-O0"); } if (!compileEnvironment.Inlining) { commonArgs.Add("-fno-inline-functions"); commonArgs.Add("-fno-inline"); } if (compileEnvironment.EnableExceptions) { commonArgs.Add("-fexceptions"); } else { commonArgs.Add("-fno-exceptions"); } } // Add preprocessor definitions foreach (var definition in compileEnvironment.PreprocessorDefinitions) { commonArgs.Add(string.Format("-D \"{0}\"", definition)); } // Add include paths foreach (var includePath in compileEnvironment.IncludePaths) { commonArgs.Add(string.Format("-I\"{0}\"", includePath.Replace('\\', '/'))); } // Compile all C++ files var args = new List <string>(); foreach (var sourceFile in sourceFiles) { var sourceFilename = Path.GetFileNameWithoutExtension(sourceFile); var task = graph.Add <CompileCppTask>(); // Use shared arguments args.Clear(); args.AddRange(commonArgs); // Object File Name var objFile = Path.Combine(outputPath, sourceFilename + ".o"); args.Add(string.Format("-o \"{0}\"", objFile.Replace('\\', '/'))); output.ObjectFiles.Add(objFile); task.ProducedFiles.Add(objFile); // Source File Name args.Add("\"" + sourceFile.Replace('\\', '/') + "\""); // Request included files to exist var includes = IncludesCache.FindAllIncludedFiles(sourceFile); task.PrerequisiteFiles.AddRange(includes); // Compile task.WorkingDirectory = options.WorkingDirectory; task.CommandPath = ClangPath; task.CommandArguments = string.Join(" ", args); task.PrerequisiteFiles.Add(sourceFile); task.InfoMessage = Path.GetFileName(sourceFile); task.Cost = task.PrerequisiteFiles.Count; // TODO: include source file size estimation to improve tasks sorting } return(output); }
private static void BuildDotNet(TaskGraph graph, BuildData buildData, NativeCpp.BuildOptions buildOptions, string name, List <string> sourceFiles, HashSet <string> fileReferences = null) { // Setup build options var buildPlatform = Platform.BuildTargetPlatform; var outputPath = Path.GetDirectoryName(buildData.Target.GetOutputFilePath(buildOptions)); var outputFile = Path.Combine(outputPath, name + ".dll"); var outputDocFile = Path.Combine(outputPath, name + ".xml"); string monoRoot, monoPath, cscPath; switch (buildPlatform) { case TargetPlatform.Windows: { monoRoot = Path.Combine(Globals.EngineRoot, "Source", "Platforms", "Editor", "Windows", "Mono"); // Prefer installed Roslyn C# compiler over Mono one monoPath = null; cscPath = Path.Combine(Path.GetDirectoryName(VCEnvironment.MSBuildPath), "Roslyn", "csc.exe"); if (!File.Exists(cscPath)) { // Fallback to Mono binaries monoPath = Path.Combine(monoRoot, "bin", "mono.exe"); cscPath = Path.Combine(monoRoot, "lib", "mono", "4.5", "csc.exe"); } break; } case TargetPlatform.Linux: monoRoot = Path.Combine(Globals.EngineRoot, "Source", "Platforms", "Editor", "Linux", "Mono"); monoPath = Path.Combine(monoRoot, "bin", "mono"); cscPath = Path.Combine(monoRoot, "lib", "mono", "4.5", "csc.exe"); break; case TargetPlatform.Mac: monoRoot = Path.Combine(Globals.EngineRoot, "Source", "Platforms", "Editor", "Mac", "Mono"); monoPath = Path.Combine(monoRoot, "bin", "mono"); cscPath = Path.Combine(monoRoot, "lib", "mono", "4.5", "csc.exe"); break; default: throw new InvalidPlatformException(buildPlatform); } var referenceAssemblies = Path.Combine(monoRoot, "lib", "mono", "4.5-api"); if (fileReferences == null) { fileReferences = buildOptions.ScriptingAPI.FileReferences; } else { fileReferences.AddRange(buildOptions.ScriptingAPI.FileReferences); } // Setup C# compiler arguments var args = new List <string>(); args.Clear(); args.Add("/nologo"); args.Add("/target:library"); args.Add("/platform:AnyCPU"); args.Add("/debug+"); args.Add("/debug:portable"); args.Add("/errorreport:prompt"); args.Add("/preferreduilang:en-US"); args.Add("/highentropyva+"); args.Add("/deterministic"); args.Add("/nostdlib+"); args.Add("/errorendlocation"); args.Add("/utf8output"); args.Add("/warn:4"); args.Add("/unsafe"); args.Add("/fullpaths"); args.Add("/langversion:7.3"); if (buildOptions.ScriptingAPI.IgnoreMissingDocumentationWarnings) { args.Add("-nowarn:1591"); } args.Add(buildData.Configuration == TargetConfiguration.Debug ? "/optimize-" : "/optimize+"); args.Add(string.Format("/out:\"{0}\"", outputFile)); args.Add(string.Format("/doc:\"{0}\"", outputDocFile)); if (buildOptions.ScriptingAPI.Defines.Count != 0) { args.Add("/define:" + string.Join(";", buildOptions.ScriptingAPI.Defines)); } if (buildData.Configuration == TargetConfiguration.Debug) { args.Add("/define:DEBUG"); } args.Add(string.Format("/reference:\"{0}{1}mscorlib.dll\"", referenceAssemblies, Path.DirectorySeparatorChar)); foreach (var reference in buildOptions.ScriptingAPI.SystemReferences) { args.Add(string.Format("/reference:\"{0}{2}{1}.dll\"", referenceAssemblies, reference, Path.DirectorySeparatorChar)); } foreach (var reference in fileReferences) { args.Add(string.Format("/reference:\"{0}\"", reference)); } foreach (var sourceFile in sourceFiles) { args.Add("\"" + sourceFile + "\""); } // Generate response file with source files paths and compilation arguments string responseFile = Path.Combine(buildOptions.IntermediateFolder, name + ".response"); Utilities.WriteFileIfChanged(responseFile, string.Join(Environment.NewLine, args)); // Create C# compilation task var task = graph.Add <Task>(); task.PrerequisiteFiles.Add(responseFile); task.PrerequisiteFiles.AddRange(sourceFiles); task.PrerequisiteFiles.AddRange(fileReferences); task.ProducedFiles.Add(outputFile); task.WorkingDirectory = buildData.TargetOptions.WorkingDirectory; task.InfoMessage = "Compiling " + outputFile; task.Cost = task.PrerequisiteFiles.Count; if (monoPath != null) { task.CommandPath = monoPath; task.CommandArguments = $"\"{cscPath}\" /noconfig @\"{responseFile}\""; } else { // The "/shared" flag enables the compiler server support: // https://github.com/dotnet/roslyn/blob/main/docs/compilers/Compiler%20Server.md task.CommandPath = cscPath; task.CommandArguments = $"/noconfig /shared @\"{responseFile}\""; } // Copy referenced assemblies foreach (var srcFile in buildOptions.ScriptingAPI.FileReferences) { var dstFile = Path.Combine(outputPath, Path.GetFileName(srcFile)); if (dstFile == srcFile || graph.HasCopyTask(dstFile, srcFile)) { continue; } graph.AddCopyFile(dstFile, srcFile); var srcPdb = Path.ChangeExtension(srcFile, "pdb"); if (File.Exists(srcPdb)) { graph.AddCopyFile(Path.ChangeExtension(dstFile, "pdb"), srcPdb); } var srcXml = Path.ChangeExtension(srcFile, "xml"); if (File.Exists(srcXml)) { graph.AddCopyFile(Path.ChangeExtension(dstFile, "xml"), srcXml); } } }
private static void BuildTargetDotNet(RulesAssembly rules, TaskGraph graph, Target target, Platform platform, TargetConfiguration configuration) { // Check if use custom project file if (!string.IsNullOrEmpty(target.CustomExternalProjectFilePath)) { // Use msbuild to compile it var task = graph.Add <Task>(); task.WorkingDirectory = Globals.Root; task.InfoMessage = "Building " + Path.GetFileName(target.CustomExternalProjectFilePath); task.Cost = 100; task.DisableCache = true; task.CommandPath = VCEnvironment.MSBuildPath; task.CommandArguments = string.Format("\"{0}\" /m /t:Build /p:Configuration=\"{1}\" /p:Platform=\"{2}\" {3} /nologo", target.CustomExternalProjectFilePath, configuration.ToString(), "AnyCPU", VCEnvironment.Verbosity); return; } // Warn if target has no valid modules if (target.Modules.Count == 0) { Log.Warning(string.Format("Target {0} has no modules to build", target.Name)); } // Pick a project var project = Globals.Project; if (target is ProjectTarget projectTarget) { project = projectTarget.Project; } if (project == null) { throw new Exception($"Cannot build target {target.Name}. The project file is missing (.flaxproj located in the folder above)."); } // Setup build environment for the target var targetBuildOptions = GetBuildOptions(target, platform, null, TargetArchitecture.AnyCPU, configuration, project.ProjectFolderPath); using (new ProfileEventScope("PreBuild")) { // Pre build target.PreBuild(graph, targetBuildOptions); PreBuild?.Invoke(graph, targetBuildOptions); // Ensure that target build directories exist if (!target.IsPreBuilt && !Directory.Exists(targetBuildOptions.IntermediateFolder)) { Directory.CreateDirectory(targetBuildOptions.IntermediateFolder); } if (!target.IsPreBuilt && !Directory.Exists(targetBuildOptions.OutputFolder)) { Directory.CreateDirectory(targetBuildOptions.OutputFolder); } } // Setup building common data container var buildData = new BuildData { Project = project, Graph = graph, Rules = rules, Target = target, TargetOptions = targetBuildOptions, Platform = platform, Architecture = TargetArchitecture.AnyCPU, Configuration = configuration, }; // Collect all modules using (new ProfileEventScope("CollectModules")) { foreach (var moduleName in target.Modules) { var module = rules.GetModule(moduleName); if (module != null) { CollectModules(buildData, module, true); } else { Log.Warning(string.Format("Missing module {0} (or invalid name specified)", moduleName)); } } } // Build all modules from target binary modules but in order of collecting (from independent to more dependant ones) var sourceFiles = new List <string>(); using (new ProfileEventScope("BuildModules")) { foreach (var module in buildData.ModulesOrderList) { if (buildData.BinaryModules.Any(x => x.Contains(module))) { var moduleOptions = BuildModule(buildData, module); // Get source files sourceFiles.AddRange(moduleOptions.SourceFiles.Where(x => x.EndsWith(".cs"))); // Merge module into target environment foreach (var e in moduleOptions.OutputFiles) { buildData.TargetOptions.LinkEnv.InputFiles.Add(e); } foreach (var e in moduleOptions.DependencyFiles) { buildData.TargetOptions.DependencyFiles.Add(e); } foreach (var e in moduleOptions.OptionalDependencyFiles) { buildData.TargetOptions.OptionalDependencyFiles.Add(e); } buildData.TargetOptions.Libraries.AddRange(moduleOptions.Libraries); buildData.TargetOptions.DelayLoadLibraries.AddRange(moduleOptions.DelayLoadLibraries); buildData.TargetOptions.ScriptingAPI.Add(moduleOptions.ScriptingAPI); } } } // Build var outputTargetFilePath = target.GetOutputFilePath(targetBuildOptions); var outputPath = Path.GetDirectoryName(outputTargetFilePath); using (new ProfileEventScope("Build")) { // Cleanup source files sourceFiles.RemoveAll(x => x.EndsWith(BuildFilesPostfix)); sourceFiles.Sort(); // Build assembly BuildDotNet(graph, buildData, targetBuildOptions, target.OutputName, sourceFiles); } // Deploy files if (!target.IsPreBuilt) { using (new ProfileEventScope("DeployFiles")) { foreach (var srcFile in targetBuildOptions.OptionalDependencyFiles.Where(File.Exists).Union(targetBuildOptions.DependencyFiles)) { var dstFile = Path.Combine(outputPath, Path.GetFileName(srcFile)); graph.AddCopyFile(dstFile, srcFile); } } } using (new ProfileEventScope("PostBuild")) { // Post build PostBuild?.Invoke(graph, targetBuildOptions); target.PostBuild(graph, targetBuildOptions); } }