Beispiel #1
0
        /// <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()));
            }
        }
Beispiel #2
0
        /// <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()));
            }
        }
Beispiel #3
0
        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();
        }
Beispiel #4
0
        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));
        }
Beispiel #5
0
        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);
        }
Beispiel #6
0
        /// <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);
        }
Beispiel #7
0
        /// <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,
            });
        }
Beispiel #8
0
        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);
        }
Beispiel #9
0
        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));
        }
Beispiel #10
0
        /// <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);
        }
Beispiel #11
0
        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));
            }
        }
Beispiel #12
0
        /// <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);
            }
        }
Beispiel #13
0
        /// <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);
            }
        }
Beispiel #14
0
        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);
                }
        }
Beispiel #15
0
        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);
                }
        }
Beispiel #16
0
        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);
        }
Beispiel #17
0
        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));
        }
Beispiel #18
0
        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());
                }
        }
Beispiel #19
0
        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());
                }
        }
Beispiel #20
0
        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));
        }