/// <summary> /// Compile /// </summary> public Task <CompileResults> CompileAsync(CompileArguments args) { // Set the working directory to the output directory var workingDirectory = args.RootDirectory; var commandArgs = BuildCompilerArguments(args, workingDirectory); Log.Verbose($"PWD={workingDirectory}"); Log.Verbose($"{CompilerPath} {commandArgs}"); using (Process process = new Process()) { process.StartInfo.UseShellExecute = false; process.StartInfo.RedirectStandardOutput = true; process.StartInfo.FileName = CompilerPath; process.StartInfo.WorkingDirectory = workingDirectory; process.StartInfo.Arguments = commandArgs; process.Start(); while (!process.StandardOutput.EndOfStream) { string line = process.StandardOutput.ReadLine(); ProcessLine(line); } process.WaitForExit(); if (process.ExitCode != 0) { throw new InvalidOperationException(); } return(Task.FromResult(new CompileResults())); } }
/// <summary> /// Compile /// </summary> public Task <CompileResults> CompileAsync(CompileArguments args) { string compiler = "g++-m"; using (Process process = new Process()) { process.StartInfo.UseShellExecute = false; process.StartInfo.RedirectStandardOutput = true; process.StartInfo.FileName = compiler; process.StartInfo.WorkingDirectory = args.RootDirectory; process.StartInfo.Arguments = BuildCompilerArguments(args); process.Start(); while (!process.StandardOutput.EndOfStream) { string line = process.StandardOutput.ReadLine(); ProcessLine(line); } process.WaitForExit(); if (process.ExitCode != 0) { throw new InvalidOperationException(); } return(Task.FromResult(new CompileResults())); } }
protected void BuildArgs() { var args = new List <string>(); if (!string.IsNullOrEmpty(CompileArguments)) { args.AddRange(CompileArguments.Split(' ')); } if (this.selectedCgProfile == Cg.CG_PROFILE_VS_1_1) { // Need the 'dcls' argument whenever we use this profile // otherwise compilation of the assembler will fail var dclsFound = args.Contains("dcls"); if (!dclsFound) { args.Add("-profileopts"); args.Add("dcls"); } } args.Add(null); this.cgArguments = args.ToArray(); }
private static string BuildCompilerArguments(CompileArguments args, string workingDirectory) { // Calculate object output file var rootPath = Path.GetRelativePath(workingDirectory, args.RootDirectory); var commandArgs = new List <string>(); // Set the Standard Library implementation // commandArgs.Add("-stdlib=libc++"); // Set the language version commandArgs.Add("-std=c++2a"); // Enable experimental modules commandArgs.Add("-fmodules-ts"); // Set the preprocessor definitions foreach (var definition in args.PreprocessorDefinitions) { commandArgs.Add($"-D{definition}"); } // Only compile the source commandArgs.Add("-c"); // Lastly add the file commandArgs.AddRange( args.SourceFiles.Select(file => Path.Combine(rootPath, file))); return(string.Join(" ", commandArgs)); }
public void Compile_Simple() { var uut = new Compiler( new Path("C:/bin/mock.csc.exe")); var arguments = new CompileArguments() { Target = new Path("bin/Target.dll"), ReferenceTarget = new Path("ref/Target.dll"), SourceRootDirectory = new Path("C:/source/"), TargetRootDirectory = new Path("C:/target/"), ObjectDirectory = new Path("ObjectDir/"), SourceFiles = new List <Path>() { new Path("File.cs"), }, }; var result = uut.CreateCompileOperations(arguments); // Verify result var expected = new List <BuildOperation>() { new BuildOperation( "WriteFile [./ObjectDir/CompileArguments.rsp]", new Path("C:/target/"), new Path("./writefile.exe"), "\"./ObjectDir/CompileArguments.rsp\" \"/unsafe- /checked- /fullpaths /nostdlib+ /errorreport:prompt /warn:5 /errorendlocation /preferreduilang:en-US /highentropyva+ /nullable:enable /debug+ /debug:portable /filealign:512 /optimize- /out:\"C:/target/bin/Target.dll\" /refout:\"C:/target/ref/Target.dll\" /target:library /warnaserror- /utf8output /deterministic+ /langversion:9.0 \"./File.cs\"\"", new List <Path>(), new List <Path>() { new Path("./ObjectDir/CompileArguments.rsp"), }), new BuildOperation( "Compile - ./bin/Target.dll", new Path("C:/source/"), new Path("C:/bin/mock.csc.exe"), "@C:/target/ObjectDir/CompileArguments.rsp /noconfig", new List <Path>() { new Path("C:/target/ObjectDir/CompileArguments.rsp"), new Path("./File.cs"), }, new List <Path>() { new Path("C:/target/bin/Target.dll"), new Path("C:/target/ref/Target.dll"), new Path("C:/target/bin/Target.pdb"), }), }; Assert.Equal(expected, result); }
/// <summary> /// Compile /// </summary> public IList <BuildOperation> CreateCompileOperations( CompileArguments arguments) { var operations = new List <BuildOperation>(); // Write the shared arguments to the response file var responseFile = arguments.ObjectDirectory + new Path("CompileArguments.rsp"); var sharedCommandArguments = ArgumentBuilder.BuildSharedCompilerArguments(arguments); var writeSharedArgumentsOperation = SharedOperations.CreateWriteFileOperation( arguments.TargetRootDirectory, responseFile, string.Join(" ", sharedCommandArguments)); operations.Add(writeSharedArgumentsOperation); var symbolFile = new Path(arguments.Target.ToString()); symbolFile.SetFileExtension("pdb"); var targetResponseFile = arguments.TargetRootDirectory + responseFile; // Build up the input/output sets var inputFiles = new List <Path>(); inputFiles.Add(targetResponseFile); inputFiles.AddRange(arguments.SourceFiles); inputFiles.AddRange(arguments.ReferenceLibraries); var outputFiles = new List <Path>() { arguments.TargetRootDirectory + arguments.Target, arguments.TargetRootDirectory + arguments.ReferenceTarget, arguments.TargetRootDirectory + symbolFile, }; // Generate the compile build operation var uniqueCommandArguments = ArgumentBuilder.BuildUniqueCompilerArguments(); var commandArguments = $"@{targetResponseFile} {string.Join(" ", uniqueCommandArguments)}"; var buildOperation = new BuildOperation( $"Compile - {arguments.Target}", arguments.SourceRootDirectory, _compilerExecutable, commandArguments, inputFiles, outputFiles); operations.Add(buildOperation); return(operations); }
/// <summary> /// Compile /// </summary> public async Task <CompileResults> CompileAsync(CompileArguments args) { // Compile each file individually var allIncludes = new List <HeaderInclude>(); foreach (var file in args.SourceFiles) { var includes = await CompileAsync(file, args); allIncludes.Add(includes); } return(new CompileResults() { HeaderIncludeFiles = allIncludes, }); }
public void BuildSharedCompilerArguments_SingleArgument_PreprocessorDefinitions() { var arguments = new CompileArguments() { Target = new Path("bin/Target.dll"), ReferenceTarget = new Path("ref/Target.dll"), PreprocessorDefinitions = new List <string>() { "DEBUG", "VERSION=1" }, }; var actualArguments = ArgumentBuilder.BuildSharedCompilerArguments( arguments); var expectedArguments = new List <string>() { "/unsafe-", "/checked-", "/fullpaths", "/nostdlib+", "/errorreport:prompt", "/warn:5", "/define:DEBUG;VERSION=1", "/errorendlocation", "/preferreduilang:en-US", "/highentropyva+", "/nullable:enable", "/debug+", "/debug:portable", "/filealign:512", "/optimize-", "/out:\"./bin/Target.dll\"", "/refout:\"./ref/Target.dll\"", "/target:library", "/warnaserror-", "/utf8output", "/deterministic+", "/langversion:9.0", }; Assert.Equal(expectedArguments, actualArguments); }
private static string BuildCompilerArguments(CompileArguments args) { var commandArgs = new List <string>(); // Set the Standard Library implementation // commandArgs.Add("-stdlib=libc++"); // Set the language version commandArgs.Add("-std=c++20"); // Enable experimental modules commandArgs.Add("-fmodules-ts"); commandArgs.Add("-c"); // Lastly add the file commandArgs.AddRange(args.SourceFiles); return(string.Join(" ", commandArgs)); }
/// <summary> /// Compile /// </summary> public IList <BuildOperation> CreateCompileOperations(CompileArguments arguments) { _compileRequests.Add(arguments); var result = new List <BuildOperation>(); result.Add( new BuildOperation( $"MockCompile: {_compileRequests.Count}", new Path("MockWorkingDirectory"), new Path("MockCompiler.exe"), "Arguments", new List <Path>() { new Path("InputFile.in"), }, new List <Path>() { new Path("OutputFile.out"), })); return(result); }
private Task <HeaderInclude> CompileAsync(string file, CompileArguments args) { // Set the working directory to the output directory var workingDirectory = Path.Combine(args.RootDirectory, args.OutputDirectory); string compiler = Path.Combine(_toolsPath, @"bin/clang++.exe"); var commandArgs = BuildCompilerArguments(file, args, workingDirectory); Log.Info($"{file}"); Log.Verbose($"PWD={workingDirectory}"); Log.Verbose($"{compiler} {commandArgs}"); // Add a single root element if includes requested if (args.GenerateIncludeTree) { _currentIncludes.Push(new HeaderInclude() { Filename = file, }); } _inWarningMessage = false; _inErrorMessage = false; using (Process process = new Process()) { process.StartInfo.UseShellExecute = false; process.StartInfo.RedirectStandardOutput = true; process.StartInfo.RedirectStandardError = true; process.StartInfo.FileName = compiler; process.StartInfo.WorkingDirectory = workingDirectory; process.StartInfo.Arguments = commandArgs; process.OutputDataReceived += ProcessOutputDataReceived; process.ErrorDataReceived += ProcessErrorDataReceived; process.Start(); process.BeginOutputReadLine(); process.BeginErrorReadLine(); process.WaitForExit(); if (process.ExitCode != 0) { throw new InvalidOperationException(); } HeaderInclude result = null; // Check if requested include headers if (args.GenerateIncludeTree) { // Move up to the root node while (_currentIncludes.Count > 1) { _currentIncludes.Pop(); } if (_currentIncludes.Count != 1) { throw new InvalidOperationException("Expected one root includes node."); } result = _currentIncludes.Pop(); } return(Task.FromResult(result)); } }
/// <summary> /// Compile the supporting source files /// </summary> private async Task <bool> CheckCompileSourceAsync( string path, Recipe recipe, BuildState buildState, IList <string> includePaths, string objectDirectory, string binaryDirectory, bool force) { var modules = new List <string>(); var defines = new List <string>(); defines.Add("SOUP_BUILD"); if (recipe.Type == RecipeType.Library) { // Add a reference to our own modules interface definition var modulePath = Path.Combine( objectDirectory, $"{Path.GetFileNameWithoutExtension(recipe.Public)}.{_compiler.ModuleFileExtension}"); modules.Add(modulePath); defines.Add(BuildRecipeNamespaceDefine(recipe)); } // Add all of the direct dependencies as module references // and set their version defintions // TODO: MSVC requires all trasnsitive modules also bool isRecursive = _compiler.Name == "MSVC"; await BuildDependencyModuleReferences(path, binaryDirectory, recipe, modules, defines, isRecursive); var source = new List <string>(); // All files are dependent on the parent module and all referenced modules var sharedDependecies = new List <string>(); sharedDependecies.AddRange(modules); // Check if the precompiled module should be compiled if (recipe.Type == RecipeType.Library) { // Add the precompile module to the list of shared dependencies // TODO: Could optimize this to not do file datetime checks over again var moduleFile = Path.Combine(path, objectDirectory, $"{Path.GetFileNameWithoutExtension(recipe.Public)}.{_compiler.ModuleFileExtension}"); sharedDependecies.Add(moduleFile); var moduleOutputFile = Path.Combine(path, objectDirectory, $"{Path.GetFileNameWithoutExtension(recipe.Public)}.{_compiler.ObjectFileExtension}"); if (force || BuildRequiredChecker.IsOutdated(path, moduleOutputFile, sharedDependecies)) { // HACK if (_compiler.Name == "Clang") { source.Add(moduleFile); } } } // Check if each source file is out of date and requires a rebuild foreach (var sourceFile in recipe.Source) { var outputFile = Path.Combine(objectDirectory, $"{Path.GetFileNameWithoutExtension(sourceFile)}.{_compiler.ObjectFileExtension}"); if (force || BuildRequiredChecker.IsSourceFileOutdated(path, buildState, outputFile, sourceFile, sharedDependecies)) { source.Add(sourceFile); } } if (source.Count == 0) { Log.Info("All source is up to date."); return(false); } else { Log.Info("Compile Source"); var args = new CompileArguments() { Standard = Compiler.LanguageStandard.Latest, RootDirectory = path, OutputDirectory = objectDirectory, PreprocessorDefinitions = defines, SourceFiles = source, IncludeDirectories = includePaths, Modules = modules, GenerateIncludeTree = true, }; // Ensure the object directory exists var objectDirectry = Path.Combine(args.RootDirectory, objectDirectory); if (!Directory.Exists(objectDirectry)) { Directory.CreateDirectory(objectDirectry); } // Compile each file var result = await _compiler.CompileAsync(args); // Save the build state if (result.HeaderIncludeFiles != null) { buildState.UpdateIncludeTree(result.HeaderIncludeFiles); } return(true); } }
/// <summary> /// Compile the module file /// </summary> private async Task CompileModuleAsync( string path, Recipe recipe, BuildState buildState, IList <string> includePaths, string objectDirectory, string binaryDirectory) { Log.Info("Compile Module"); if (string.IsNullOrEmpty(recipe.Public)) { throw new InvalidOperationException("The public file was not set."); } var modules = new List <string>(); var defines = new List <string>(); defines.Add("SOUP_BUILD"); // Set the active version namespace defines.Add(BuildRecipeNamespaceDefine(recipe)); // Add all of the direct dependencies as module references // and set their version defintions // TODO: MSVC requires all trasnsitive modules also bool isRecursive = _compiler.Name == "MSVC"; await BuildDependencyModuleReferences(path, binaryDirectory, recipe, modules, defines, isRecursive); // TODO: Clang wants modules to be cppm var publicFile = recipe.Public; if (_compiler.Name == "Clang") { publicFile = Path.GetFileNameWithoutExtension(publicFile) + ".cppm"; } var args = new CompileArguments() { Standard = Compiler.LanguageStandard.Latest, RootDirectory = path, OutputDirectory = objectDirectory, PreprocessorDefinitions = defines, SourceFiles = new List <string>() { publicFile }, IncludeDirectories = includePaths, Modules = modules, ExportModule = true, GenerateIncludeTree = true, }; // Ensure the object directory exists var objectDirectry = Path.Combine(args.RootDirectory, objectDirectory); if (!Directory.Exists(objectDirectry)) { Directory.CreateDirectory(objectDirectry); } // Compile each file var result = await _compiler.CompileAsync(args); // Save the build state if (result.HeaderIncludeFiles != null) { buildState.UpdateIncludeTree(result.HeaderIncludeFiles); } }
public void Build_Executable() { // Register the test process manager var processManager = new MockProcessManager(); // Register the test listener var testListener = new TestTraceListener(); using (var scopedTraceListener = new ScopedTraceListenerRegister(testListener)) using (var scopedProcesManager = new ScopedSingleton <IProcessManager>(processManager)) { // Register the mock compiler var compiler = new Mock.Compiler(); // Setup the build arguments var arguments = new BuildArguments(); arguments.TargetName = "Program"; arguments.TargetType = BuildTargetType.Executable; arguments.SourceRootDirectory = new Path("C:/source/"); arguments.TargetRootDirectory = new Path("C:/target/"); arguments.ObjectDirectory = new Path("obj/"); arguments.BinaryDirectory = new Path("bin/"); arguments.SourceFiles = new List <Path>() { new Path("TestFile.cs"), }; arguments.OptimizationLevel = BuildOptimizationLevel.None; arguments.LinkDependencies = new List <Path>() { new Path("../Other/bin/OtherModule1.mock.a"), new Path("../OtherModule2.mock.a"), }; var uut = new BuildEngine(compiler); var fileSystemState = new FileSystemState(); var readAccessList = new List <Path>(); var writeAccessList = new List <Path>(); var buildState = new BuildState(new ValueTable(), fileSystemState, readAccessList, writeAccessList); var result = uut.Execute(buildState, arguments); // Verify expected process manager requests Assert.Equal( new List <string>() { "GetCurrentProcessFileName", "GetCurrentProcessFileName", "GetCurrentProcessFileName", }, processManager.GetRequests()); // Verify expected logs Assert.Equal( new List <string>() { }, testListener.GetMessages()); var expectedCompileArguments = new CompileArguments() { Target = new Path("./bin/Program.mock.dll"), ReferenceTarget = new Path("./bin/ref/Program.mock.dll"), TargetType = LinkTarget.Executable, ObjectDirectory = new Path("obj/"), SourceRootDirectory = new Path("C:/source/"), TargetRootDirectory = new Path("C:/target/"), SourceFiles = new List <Path>() { new Path("TestFile.cs"), }, ReferenceLibraries = new List <Path>() { new Path("../Other/bin/OtherModule1.mock.a"), new Path("../OtherModule2.mock.a"), }, NullableState = NullableState.Enabled, }; // Verify expected compiler calls var val = compiler.GetCompileRequests()[0]; var areEqual = val == expectedCompileArguments; var areEqual2 = val.ObjectDirectory == expectedCompileArguments.ObjectDirectory; Assert.Equal( new List <CompileArguments>() { expectedCompileArguments, }, compiler.GetCompileRequests()); var expectedBuildOperations = new List <BuildOperation>() { new BuildOperation( "MakeDir [./obj/]", new Path("C:/target/"), new Path("C:/mkdir.exe"), "\"./obj/\"", new List <Path>(), new List <Path>() { new Path("./obj/"), }), new BuildOperation( "MakeDir [./bin/]", new Path("C:/target/"), new Path("C:/mkdir.exe"), "\"./bin/\"", new List <Path>(), new List <Path>() { new Path("./bin/"), }), new BuildOperation( "MakeDir [./bin/ref/]", new Path("C:/target/"), new Path("C:/mkdir.exe"), "\"./bin/ref/\"", new List <Path>(), new List <Path>() { new Path("./bin/ref/"), }), new BuildOperation( "MockCompile: 1", new Path("MockWorkingDirectory"), new Path("MockCompiler.exe"), "Arguments", new List <Path>() { new Path("./InputFile.in"), }, new List <Path>() { new Path("./OutputFile.out"), }), new BuildOperation( "WriteFile [./bin/Program.runtimeconfig.json]", new Path("C:/target/"), new Path("./writefile.exe"), @"""./bin/Program.runtimeconfig.json"" ""{ ""runtimeOptions"": { ""tfm"": ""net5.0"", ""framework"": { ""name"": ""Microsoft.NETCore.App"", ""version"": ""5.0.0"" } } }""", new List <Path>(), new List <Path>() { new Path("./bin/Program.runtimeconfig.json"), }), }; Assert.Equal( expectedBuildOperations, result.BuildOperations); Assert.Equal( new List <Path>(), result.LinkDependencies); Assert.Equal( new List <Path>() { new Path("C:/target/bin/Program.mock.dll"), }, result.RuntimeDependencies); } }
public void Build_Library_MultipleFiles() { // Register the test process manager var processManager = new MockProcessManager(); // Register the test listener var testListener = new TestTraceListener(); using (var scopedTraceListener = new ScopedTraceListenerRegister(testListener)) using (var scopedProcesManager = new ScopedSingleton <IProcessManager>(processManager)) { // Register the mock compiler var compiler = new Mock.Compiler(); // Setup the build arguments var arguments = new BuildArguments(); arguments.TargetName = "Library"; arguments.TargetType = BuildTargetType.Library; arguments.SourceRootDirectory = new Path("C:/source/"); arguments.TargetRootDirectory = new Path("C:/target/"); arguments.ObjectDirectory = new Path("obj/"); arguments.BinaryDirectory = new Path("bin/"); arguments.SourceFiles = new List <Path>() { new Path("TestFile1.cs"), new Path("TestFile2.cs"), new Path("TestFile3.cs"), }; arguments.OptimizationLevel = BuildOptimizationLevel.Size; arguments.LinkDependencies = new List <Path>() { new Path("../Other/bin/OtherModule1.mock.a"), new Path("../OtherModule2.mock.a"), }; arguments.NullableState = BuildNullableState.Disabled; var uut = new BuildEngine(compiler); var fileSystemState = new FileSystemState(); var readAccessList = new List <Path>(); var writeAccessList = new List <Path>(); var buildState = new BuildState(new ValueTable(), fileSystemState, readAccessList, writeAccessList); var result = uut.Execute(buildState, arguments); // Verify expected process manager requests Assert.Equal( new List <string>() { "GetCurrentProcessFileName", "GetCurrentProcessFileName", "GetCurrentProcessFileName", }, processManager.GetRequests()); // Verify expected logs Assert.Equal( new List <string>() { }, testListener.GetMessages()); // Setup the shared arguments var expectedCompileArguments = new CompileArguments() { Target = new Path("./bin/Library.mock.dll"), ReferenceTarget = new Path("./bin/ref/Library.mock.dll"), SourceRootDirectory = new Path("C:/source/"), TargetRootDirectory = new Path("C:/target/"), ObjectDirectory = new Path("obj/"), SourceFiles = new List <Path>() { new Path("TestFile1.cs"), new Path("TestFile2.cs"), new Path("TestFile3.cs"), }, ReferenceLibraries = new List <Path>() { new Path("../Other/bin/OtherModule1.mock.a"), new Path("../OtherModule2.mock.a"), }, NullableState = NullableState.Disabled, }; // Verify expected compiler calls Assert.Equal( new List <CompileArguments>() { expectedCompileArguments, }, compiler.GetCompileRequests()); // Verify build state var expectedBuildOperations = new List <BuildOperation>() { new BuildOperation( "MakeDir [./obj/]", new Path("C:/target/"), new Path("C:/mkdir.exe"), "\"./obj/\"", new List <Path>(), new List <Path>() { new Path("./obj/"), }), new BuildOperation( "MakeDir [./bin/]", new Path("C:/target/"), new Path("C:/mkdir.exe"), "\"./bin/\"", new List <Path>(), new List <Path>() { new Path("./bin/"), }), new BuildOperation( "MakeDir [./bin/ref/]", new Path("C:/target/"), new Path("C:/mkdir.exe"), "\"./bin/ref/\"", new List <Path>(), new List <Path>() { new Path("./bin/ref/"), }), new BuildOperation( "MockCompile: 1", new Path("MockWorkingDirectory"), new Path("MockCompiler.exe"), "Arguments", new List <Path>() { new Path("InputFile.in"), }, new List <Path>() { new Path("OutputFile.out"), }), }; Assert.Equal( expectedBuildOperations, result.BuildOperations); Assert.Equal( new List <Path>() { new Path("C:/target/bin/ref/Library.mock.dll"), }, result.LinkDependencies); Assert.Equal( new List <Path>() { new Path("C:/target/bin/Library.mock.dll"), }, result.RuntimeDependencies); Assert.Equal( new Path("C:/target/bin/Library.mock.dll"), result.TargetFile); } }
public static IList <string> BuildSharedCompilerArguments(CompileArguments arguments) { // Calculate object output file var commandArguments = new List <string>(); // Disable 'unsafe' code AddFlag(commandArguments, "unsafe-"); // Disable generate overflow checks AddFlag(commandArguments, "checked-"); // Disable warnings if (arguments.DisabledWarnings.Count > 0) { var noWarnList = string.Join(",", arguments.DisabledWarnings); AddParameter(commandArguments, "nowarn", noWarnList); } // Generate fully qualified paths AddFlag(commandArguments, "fullpaths"); // Do not reference standard library (mscorlib.dll) AddFlag(commandArguments, "nostdlib+"); // Specify how to handle internal compiler errors AddParameter(commandArguments, "errorreport", "prompt"); // Set the warning level AddParameter(commandArguments, "warn", "5"); // Define conditional compilation symbol(s) if (arguments.PreprocessorDefinitions.Count > 0) { var definesList = string.Join(";", arguments.PreprocessorDefinitions); AddParameter(commandArguments, "define", definesList); } // Output line and column of the end location of each error AddFlag(commandArguments, "errorendlocation"); // Specify the preferred output language name. AddParameter(commandArguments, "preferreduilang", "en-US"); // Enable high-entropy ASLR AddFlag(commandArguments, "highentropyva+"); // Specify nullable context option enabled switch (arguments.NullableState) { case NullableState.Disabled: AddParameter(commandArguments, "nullable", "disable"); break; case NullableState.Enabled: AddParameter(commandArguments, "nullable", "enable"); break; case NullableState.Warnings: AddParameter(commandArguments, "nullable", "warnings"); break; case NullableState.Annotations: AddParameter(commandArguments, "nullable", "annotations"); break; default: throw new InvalidOperationException("Unknown Nullable State"); } // Add the reference libraries foreach (var file in arguments.ReferenceLibraries) { AddParameterWithQuotes(commandArguments, "reference", file.ToString()); } // Emit debugging information AddFlag(commandArguments, "debug+"); AddParameter(commandArguments, "debug", "portable"); // Specify the alignment used for output file sections AddParameter(commandArguments, "filealign", "512"); // Enable optimizations if (arguments.EnableOptimizations) { AddFlag(commandArguments, "optimize+"); } else { AddFlag(commandArguments, "optimize-"); } // Specify output file name var absoluteTarget = arguments.TargetRootDirectory + arguments.Target; AddParameterWithQuotes(commandArguments, "out", absoluteTarget.ToString()); // Reference assembly output to generate // Note: Modules cannot use refout if (arguments.TargetType != LinkTarget.Module) { var absoluteReferenceTarget = arguments.TargetRootDirectory + arguments.ReferenceTarget; AddParameterWithQuotes(commandArguments, "refout", absoluteReferenceTarget.ToString()); } switch (arguments.TargetType) { case LinkTarget.Library: AddParameter(commandArguments, "target", "library"); break; case LinkTarget.Executable: AddParameter(commandArguments, "target", "exe"); break; case LinkTarget.Module: AddParameter(commandArguments, "target", "module"); break; default: throw new InvalidOperationException($"Unknown Target Type {arguments.TargetType}"); } // Report all warnings as errors if (arguments.EnableWarningsAsErrors) { AddFlag(commandArguments, "warnaserror+"); } else { AddFlag(commandArguments, "warnaserror-"); } // Output compiler messages in UTF-8 encoding AddFlag(commandArguments, "utf8output"); // Produce a deterministic assembly AddFlag(commandArguments, "deterministic+"); // Specify language version AddParameter(commandArguments, "langversion", "9.0"); // Add the source files foreach (var file in arguments.SourceFiles) { AddValueWithQuotes(commandArguments, file.ToString()); } return(commandArguments); }
private string BuildCompilerArguments(CompileArguments args, string rootPath) { var commandArgs = new List <string>(); // Disable the logo text commandArgs.Add("-nologo"); // Set the language standard switch (args.Standard) { case LanguageStandard.CPP11: throw new NotSupportedException("11 is not supported any longer. 14 is now the default."); case LanguageStandard.CPP14: // Default value commandArgs.Add("-std:c++14"); break; case LanguageStandard.CPP17: commandArgs.Add("-std:c++17"); break; case LanguageStandard.Latest: commandArgs.Add("-std:c++latest"); commandArgs.Add("-experimental:module"); break; default: throw new NotSupportedException("Unknown language standard."); } // TODO: For now we allow exports to be large commandArgs.Add("/bigobj"); // Only run preprocess, compile, and assemble steps commandArgs.Add("-c"); // Set the include paths foreach (var directory in args.IncludeDirectories) { commandArgs.Add($"-I\"{directory}\""); } // Set the preprocessor definitions foreach (var definition in args.PreprocessorDefinitions) { commandArgs.Add($"-D{definition}"); } // Ignore Standard Include Paths to prevent pulling in accidental headers commandArgs.Add("-X"); // Add in the std include paths // TODO : May want to place behind flag // TODO : Investigate placing these as environment variables before calling exe // C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.15.26726\ATLMFC\include // C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.15.26726\include // C:\Program Files(x86)\Windows Kits\NETFXSDK\4.6.1\include\um; // C:\Program Files (x86)\Windows Kits\10\include\10.0.17134.0\ucrt // C:\Program Files (x86)\Windows Kits\10\include\10.0.17134.0\shared // C:\Program Files (x86)\Windows Kits\10\include\10.0.17134.0\um // C:\Program Files (x86)\Windows Kits\10\include\10.0.17134.0\winrt // C:\Program Files (x86)\Windows Kits\10\include\10.0.17134.0\cppwinrt commandArgs.Add($"-I\"{Path.Combine(_toolsPath, "ATLMFC/include")}\""); commandArgs.Add($"-I\"{Path.Combine(_toolsPath, "include")}\""); commandArgs.Add($"-I\"{Path.Combine(WindowsKitsPath, "10/include", _windowsTargetVersion, "ucrt")}\""); commandArgs.Add($"-I\"{Path.Combine(WindowsKitsPath, "10/include", _windowsTargetVersion, "shared")}\""); commandArgs.Add($"-I\"{Path.Combine(WindowsKitsPath, "10/include", _windowsTargetVersion, "um")}\""); commandArgs.Add($"-I\"{Path.Combine(WindowsKitsPath, _windowsTargetVersion, _windowsTargetVersion, "winrt")}\""); commandArgs.Add($"-I\"{Path.Combine(WindowsKitsPath, "10/include", _windowsTargetVersion, "cppwinrt")}\""); // Add the object output file var objectPath = args.OutputDirectory.EnsureTrailingSlash().Replace(@"\", @"\\"); commandArgs.Add($"-Fo\"{objectPath}\""); // Enable c++ exceptions commandArgs.Add("-EHs"); // Add the module references foreach (var module in args.Modules) { commandArgs.Add("-module:reference"); commandArgs.Add(Path.Combine(rootPath, module)); } if (args.ExportModule) { commandArgs.Add("-module:export"); // There must be only one source file if (args.SourceFiles.Count != 1) { throw new ArgumentException("Export module expects only one source file."); } // Place the ifc in the output directory var sourceFile = args.SourceFiles[0]; var outputFile = $"{args.OutputDirectory.EnsureTrailingSlash()}{Path.GetFileNameWithoutExtension(sourceFile)}.ifc"; commandArgs.AddRange(new string[] { "-module:output", outputFile }); } // Lastly add the file commandArgs.AddRange(args.SourceFiles); return(string.Join(" ", commandArgs)); }
public void Build_Library_MultipleFiles() { // Register the test process manager var processManager = new MockProcessManager(); // Register the test listener var testListener = new TestTraceListener(); using (var scopedTraceListener = new ScopedTraceListenerRegister(testListener)) using (var scopedProcesManager = new ScopedSingleton <IProcessManager>(processManager)) { // Setup the input build state var buildState = new MockBuildState(); var state = buildState.ActiveState; // Setup build table var buildTable = new ValueTable(); state.Add("Build", new Value(buildTable)); buildTable.Add("TargetName", new Value("Library")); buildTable.Add("TargetType", new Value((long)BuildTargetType.Library)); buildTable.Add("SourceRootDirectory", new Value("C:/source/")); buildTable.Add("TargetRootDirectory", new Value("C:/target/")); buildTable.Add("ObjectDirectory", new Value("obj/")); buildTable.Add("BinaryDirectory", new Value("bin/")); buildTable.Add("Source", new Value(new ValueList() { new Value("TestFile1.cpp"), new Value("TestFile2.cpp"), new Value("TestFile3.cpp"), })); buildTable.Add("IncludeDirectories", new Value(new ValueList() { new Value("Folder"), new Value("AnotherFolder/Sub"), })); buildTable.Add("ModuleDependencies", new Value(new ValueList() { new Value("../Other/bin/OtherModule1.mock.bmi"), new Value("../OtherModule2.mock.bmi"), })); buildTable.Add("OptimizationLevel", new Value((long)BuildOptimizationLevel.None)); // Setup parameters table var parametersTable = new ValueTable(); state.Add("Parameters", new Value(parametersTable)); parametersTable.Add("Architecture", new Value("x64")); parametersTable.Add("Compiler", new Value("MOCK")); // Register the mock compiler var compiler = new Compiler.Mock.Compiler(); var compilerFactory = new Dictionary <string, Func <IValueTable, ICompiler> >(); compilerFactory.Add("MOCK", (IValueTable state) => { return(compiler); }); var factory = new ValueFactory(); var uut = new BuildTask(buildState, factory, compilerFactory); uut.Execute(); // Verify expected process manager requests Assert.Equal( new List <string>() { "GetCurrentProcessFileName", "GetCurrentProcessFileName", "GetCurrentProcessFileName", }, processManager.GetRequests()); // Verify expected logs Assert.Equal( new List <string>() { "INFO: Build Generate Done", }, testListener.GetMessages()); // Setup the shared arguments var expectedCompileArguments = new CompileArguments() { Target = new Path("./bin/Library.mock.dll"), ReferenceTarget = new Path("./bin/ref/Library.mock.dll"), SourceRootDirectory = new Path("C:/source/"), TargetRootDirectory = new Path("C:/target/"), ObjectDirectory = new Path("obj/"), SourceFiles = new List <Path>() { new Path("TestFile1.cpp"), new Path("TestFile2.cpp"), new Path("TestFile3.cpp"), }, }; // Verify expected compiler calls Assert.Equal( new List <CompileArguments>() { expectedCompileArguments, }, compiler.GetCompileRequests()); // Verify build state var expectedBuildOperations = new List <BuildOperation>() { new BuildOperation( "MakeDir [./obj/]", new Path("C:/target/"), new Path("C:/mkdir.exe"), "\"./obj/\"", new List <Path>(), new List <Path>() { new Path("./obj/"), }), new BuildOperation( "MakeDir [./bin/]", new Path("C:/target/"), new Path("C:/mkdir.exe"), "\"./bin/\"", new List <Path>(), new List <Path>() { new Path("./bin/"), }), new BuildOperation( "MakeDir [./bin/ref/]", new Path("C:/target/"), new Path("C:/mkdir.exe"), "\"./bin/ref/\"", new List <Path>(), new List <Path>() { new Path("./bin/ref/"), }), new BuildOperation( "MockCompile: 1", new Path("MockWorkingDirectory"), new Path("MockCompiler.exe"), "Arguments", new List <Path>() { new Path("./InputFile.in"), }, new List <Path>() { new Path("./OutputFile.out"), }), }; Assert.Equal( expectedBuildOperations, buildState.GetBuildOperations()); } }
public void Build_Executable() { // Register the test process manager var processManager = new MockProcessManager(); // Register the test listener var testListener = new TestTraceListener(); using (var scopedTraceListener = new ScopedTraceListenerRegister(testListener)) using (var scopedProcesManager = new ScopedSingleton <IProcessManager>(processManager)) { // Setup the input build state var buildState = new MockBuildState(); var state = buildState.ActiveState; // Setup build table var buildTable = new ValueTable(); state.Add("Build", new Value(buildTable)); buildTable.Add("TargetName", new Value("Program")); buildTable.Add("TargetType", new Value((long)BuildTargetType.Executable)); buildTable.Add("SourceRootDirectory", new Value("C:/source/")); buildTable.Add("TargetRootDirectory", new Value("C:/target/")); buildTable.Add("ObjectDirectory", new Value("obj/")); buildTable.Add("BinaryDirectory", new Value("bin/")); buildTable.Add( "Source", new Value(new ValueList() { new Value("TestFile.cs"), })); // Setup parameters table var parametersTable = new ValueTable(); state.Add("Parameters", new Value(parametersTable)); parametersTable.Add("Architecture", new Value("x64")); parametersTable.Add("Compiler", new Value("MOCK")); // Register the mock compiler var compiler = new Compiler.Mock.Compiler(); var compilerFactory = new Dictionary <string, Func <IValueTable, ICompiler> >(); compilerFactory.Add("MOCK", (IValueTable state) => { return(compiler); }); var factory = new ValueFactory(); var uut = new BuildTask(buildState, factory, compilerFactory); uut.Execute(); // Verify expected process manager requests Assert.Equal( new List <string>() { "GetCurrentProcessFileName", "GetCurrentProcessFileName", "GetCurrentProcessFileName", }, processManager.GetRequests()); // Verify expected logs Assert.Equal( new List <string>() { "INFO: Build Generate Done" }, testListener.GetMessages()); var expectedCompileArguments = new CompileArguments() { Target = new Path("./bin/Program.mock.dll"), ReferenceTarget = new Path("./bin/ref/Program.mock.dll"), TargetType = LinkTarget.Executable, SourceRootDirectory = new Path("C:/source/"), TargetRootDirectory = new Path("C:/target/"), ObjectDirectory = new Path("obj/"), SourceFiles = new List <Path>() { new Path("TestFile.cs") }, }; // Verify expected compiler calls Assert.Equal( new List <CompileArguments>() { expectedCompileArguments, }, compiler.GetCompileRequests()); // Verify build state var expectedBuildOperations = new List <BuildOperation>() { new BuildOperation( "MakeDir [./obj/]", new Path("C:/target/"), new Path("C:/mkdir.exe"), "\"./obj/\"", new List <Path>(), new List <Path>() { new Path("./obj/"), }), new BuildOperation( "MakeDir [./bin/]", new Path("C:/target/"), new Path("C:/mkdir.exe"), "\"./bin/\"", new List <Path>(), new List <Path>() { new Path("./bin/"), }), new BuildOperation( "MakeDir [./bin/ref/]", new Path("C:/target/"), new Path("C:/mkdir.exe"), "\"./bin/ref/\"", new List <Path>(), new List <Path>() { new Path("./bin/ref/"), }), new BuildOperation( "MockCompile: 1", new Path("MockWorkingDirectory"), new Path("MockCompiler.exe"), "Arguments", new List <Path>() { new Path("./InputFile.in"), }, new List <Path>() { new Path("./OutputFile.out"), }), new BuildOperation( "WriteFile [./bin/Program.runtimeconfig.json]", new Path("C:/target/"), new Path("./writefile.exe"), @"""./bin/Program.runtimeconfig.json"" ""{ ""runtimeOptions"": { ""tfm"": ""net5.0"", ""framework"": { ""name"": ""Microsoft.NETCore.App"", ""version"": ""5.0.0"" } } }""", new List <Path>(), new List <Path>() { new Path("./bin/Program.runtimeconfig.json"), }), }; Assert.Equal( expectedBuildOperations, buildState.GetBuildOperations()); } }
private string BuildCompilerArguments(string sourceFile, CompileArguments args, string workingDirectory) { // Calculate object output file var rootPath = Path.GetRelativePath(workingDirectory, args.RootDirectory); var commandArgs = new List <string>(); // Enable Header includes if needed if (args.GenerateIncludeTree) { commandArgs.Add("-H"); } // Enable optimization commandArgs.Add("-O3"); // Enable verbose output // commandArgs.Add("-v"); // Disable ms compatibility (workaround for bug with inplicit types in pcm) // commandArgs.Add("-fno-ms-compatibility"); // Disable warnings commandArgs.Add("-Wno-unknown-attributes"); // Allow public std visible during link time commandArgs.AddRange(new string[] { "-Xclang", "-flto-visibility-public-std" }); // Set the language standard switch (args.Standard) { case LanguageStandard.CPP11: commandArgs.Add("-std=c++11"); break; case LanguageStandard.CPP14: commandArgs.Add("-std=c++14"); break; case LanguageStandard.CPP17: commandArgs.Add("-std=c++17"); break; case LanguageStandard.Latest: // commandArgs.Add("-std=c++17"); commandArgs.Add("-std=c++2a"); break; default: throw new NotSupportedException("Unknown language standard."); } // Set the include paths foreach (var directory in args.IncludeDirectories) { var include = directory; if (!Path.IsPathFullyQualified(directory)) { include = Path.Combine(rootPath, directory); } commandArgs.Add($"-I\"{include}\""); } // Set the preprocessor definitions foreach (var definition in args.PreprocessorDefinitions) { commandArgs.Add($"-D{definition}"); } // Ignore Standard Include Paths to prevent pulling in accidental headers // commandArgs.Add("-X"); // Add in the std include paths // Enable c++ exceptions // commandArgs.Add("-EHs"); // Enable experimental features //if (args.Standard == LanguageStandard.Latest) //{ // commandArgs.Add("-fmodules-ts"); //} // Add the module references foreach (var module in args.Modules) { commandArgs.Add($"-fmodule-file=\"{Path.Combine(rootPath, module)}\""); } if (args.ExportModule) { commandArgs.Add("--precompile"); // There must be only one source file if (args.SourceFiles.Count != 1) { throw new ArgumentException("Export module expects only one source file."); } // Place the ifc in the output directory var outputFile = $"{Path.GetFileNameWithoutExtension(sourceFile)}.{ModuleFileExtension}"; commandArgs.AddRange(new string[] { "-o", outputFile }); } else { // Only run preprocessor, compile and assemble commandArgs.Add("-c"); } // Lastly add the file commandArgs.Add(Path.Combine(rootPath, sourceFile)); return(string.Join(" ", commandArgs)); }