From f4edc4a523a02d4220cf58191a3d7c7a22c1f4a7 Mon Sep 17 00:00:00 2001 From: Matthew Asplund Date: Mon, 21 Mar 2022 18:49:29 -0700 Subject: [PATCH 1/2] Add full partition closure to imports --- .../Core.UnitTests/BuildEngineUnitTests.cs | 380 +++++++++++++++++- .../Cpp/Compiler/Core/BuildEngine.cs | 37 +- 2 files changed, 407 insertions(+), 10 deletions(-) diff --git a/Source/GenerateSharp/Extensions/Cpp/Compiler/Core.UnitTests/BuildEngineUnitTests.cs b/Source/GenerateSharp/Extensions/Cpp/Compiler/Core.UnitTests/BuildEngineUnitTests.cs index 741f54e0..6a173ae4 100644 --- a/Source/GenerateSharp/Extensions/Cpp/Compiler/Core.UnitTests/BuildEngineUnitTests.cs +++ b/Source/GenerateSharp/Extensions/Cpp/Compiler/Core.UnitTests/BuildEngineUnitTests.cs @@ -792,7 +792,11 @@ public void Build_Library_MultipleFiles() result.BuildOperations); Assert.Equal( - new List(), + new List() + { + new Path("../Other/bin/OtherModule1.mock.bmi"), + new Path("../OtherModule2.mock.bmi"), + }, result.ModuleDependencies); Assert.Equal( @@ -1087,7 +1091,9 @@ public void Build_Library_ModuleInterface() Assert.Equal( new List() { - new Path("C:/target/bin/Library.mock.bmi"), + new Path("../Other/bin/OtherModule1.mock.bmi"), + new Path("../OtherModule2.mock.bmi"), + new Path("C:/target/bin/Library.mock.bmi"), }, result.ModuleDependencies); @@ -1432,6 +1438,8 @@ public void Build_Library_ModuleInterface_WithPartitions() Assert.Equal( new List() { + new Path("../Other/bin/OtherModule1.mock.bmi"), + new Path("../OtherModule2.mock.bmi"), new Path("C:/target/obj/TestFile1.mock.bmi"), new Path("C:/target/obj/TestFile2.mock.bmi"), new Path("C:/target/bin/Library.mock.bmi"), @@ -1457,6 +1465,372 @@ public void Build_Library_ModuleInterface_WithPartitions() } } + + [Fact] + public void Build_Library_ModuleInterface_WithPartitions_TransitiveImport() + { + // 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(processManager)) + { + // Register the mock compiler + var compiler = new Mock.Compiler(); + + // Setup the build arguments + var arguments = new BuildArguments(); + arguments.TargetName = "Library"; + arguments.TargetType = BuildTargetType.StaticLibrary; + arguments.LanguageStandard = LanguageStandard.CPP20; + arguments.SourceRootDirectory = new Path("C:/source/"); + arguments.TargetRootDirectory = new Path("C:/target/"); + arguments.ObjectDirectory = new Path("obj/"); + arguments.BinaryDirectory = new Path("bin/"); + arguments.ModuleInterfacePartitionSourceFiles = new List() + { + new PartitionSourceFile() + { + File = new Path("TestFile1.cpp"), + }, + new PartitionSourceFile() + { + File = new Path("TestFile2.cpp"), + Imports = new List() + { + new Path("TestFile1.cpp"), + } + }, + new PartitionSourceFile() + { + File = new Path("TestFile3.cpp"), + Imports = new List() + { + new Path("TestFile2.cpp"), + } + }, + }; + arguments.ModuleInterfaceSourceFile = new Path("Public.cpp"); + arguments.SourceFiles = new List() + { + new Path("TestFile4.cpp"), + }; + arguments.OptimizationLevel = BuildOptimizationLevel.None; + arguments.IncludeDirectories = new List() + { + new Path("Folder"), + new Path("AnotherFolder/Sub"), + }; + arguments.ModuleDependencies = new List() + { + new Path("../Other/bin/OtherModule1.mock.bmi"), + new Path("../OtherModule2.mock.bmi"), + }; + arguments.LinkDependencies = new List() + { + new Path("../Other/bin/OtherModule1.mock.a"), + new Path("../OtherModule2.mock.a"), + }; + arguments.PreprocessorDefinitions = new List() + { + "DEBUG", + "AWESOME", + }; + + var uut = new BuildEngine(compiler); + var fileSystemState = new FileSystemState(); + var readAccessList = new List(); + var writeAccessList = new List(); + var buildState = new BuildState(new ValueTable(), fileSystemState, readAccessList, writeAccessList); + var result = uut.Execute(buildState, arguments); + + // Verify expected process manager requests + Assert.Equal( + new List() + { + "GetCurrentProcessFileName", + "GetCurrentProcessFileName", + "GetCurrentProcessFileName", + }, + processManager.GetRequests()); + + // Verify expected logs + Assert.Equal( + new List() + { + "INFO: Generate Module Interface Partition Compile Operation: ./TestFile1.cpp", + "INFO: Generate Module Interface Partition Compile Operation: ./TestFile2.cpp", + "INFO: Generate Module Interface Partition Compile Operation: ./TestFile3.cpp", + "INFO: Generate Module Interface Unit Compile: ./Public.cpp", + "INFO: Generate Compile Operation: ./TestFile4.cpp", + "INFO: CoreLink", + "INFO: Linking target", + "INFO: Generate Link Operation: ./bin/Library.mock.lib", + }, + testListener.GetMessages()); + + // Setup the shared arguments + var expectedCompileArguments = new SharedCompileArguments() + { + Standard = LanguageStandard.CPP20, + Optimize = OptimizationLevel.None, + SourceRootDirectory = new Path("C:/source/"), + TargetRootDirectory = new Path("C:/target/"), + ObjectDirectory = new Path("obj/"), + IncludeDirectories = new List() + { + new Path("Folder"), + new Path("AnotherFolder/Sub"), + }, + IncludeModules = new List() + { + new Path("../Other/bin/OtherModule1.mock.bmi"), + new Path("../OtherModule2.mock.bmi"), + }, + PreprocessorDefinitions = new List() + { + "DEBUG", + "AWESOME", + }, + InterfacePartitionUnits = new List() + { + new InterfaceUnitCompileArguments() + { + SourceFile = new Path("TestFile1.cpp"), + TargetFile = new Path("obj/TestFile1.mock.obj"), + ModuleInterfaceTarget = new Path("obj/TestFile1.mock.bmi"), + }, + new InterfaceUnitCompileArguments() + { + SourceFile = new Path("TestFile2.cpp"), + TargetFile = new Path("obj/TestFile2.mock.obj"), + IncludeModules = new List() + { + new Path("C:/target/obj/TestFile1.mock.bmi"), + }, + ModuleInterfaceTarget = new Path("obj/TestFile2.mock.bmi"), + }, + new InterfaceUnitCompileArguments() + { + SourceFile = new Path("TestFile3.cpp"), + TargetFile = new Path("obj/TestFile3.mock.obj"), + IncludeModules = new List() + { + new Path("C:/target/obj/TestFile2.mock.bmi"), + new Path("C:/target/obj/TestFile1.mock.bmi"), + }, + ModuleInterfaceTarget = new Path("obj/TestFile3.mock.bmi"), + }, + }, + InterfaceUnit = new InterfaceUnitCompileArguments() + { + SourceFile = new Path("Public.cpp"), + TargetFile = new Path("obj/Public.mock.obj"), + IncludeModules = new List() + { + new Path("C:/target/obj/TestFile1.mock.bmi"), + new Path("C:/target/obj/TestFile2.mock.bmi"), + new Path("C:/target/obj/TestFile3.mock.bmi"), + }, + ModuleInterfaceTarget = new Path("obj/Public.mock.bmi"), + }, + ImplementationUnits = new List() + { + new TranslationUnitCompileArguments() + { + SourceFile = new Path("TestFile4.cpp"), + TargetFile = new Path("obj/TestFile4.mock.obj"), + }, + }, + }; + + var expectedLinkArguments = new LinkArguments() + { + TargetFile = new Path("bin/Library.mock.lib"), + TargetType = LinkTarget.StaticLibrary, + TargetRootDirectory = new Path("C:/target/"), + ObjectFiles = new List() + { + new Path("obj/TestFile1.mock.obj"), + new Path("obj/TestFile2.mock.obj"), + new Path("obj/TestFile3.mock.obj"), + new Path("obj/Public.mock.obj"), + new Path("obj/TestFile4.mock.obj"), + }, + LibraryFiles = new List(), + }; + + // Verify expected compiler calls + Assert.Equal( + new List() + { + expectedCompileArguments, + }, + compiler.GetCompileRequests()); + Assert.Equal( + new List() + { + expectedLinkArguments, + }, + compiler.GetLinkRequests()); + + // Verify build state + var expectedBuildOperations = new List() + { + new BuildOperation( + "MakeDir [./obj/]", + new Path("C:/target/"), + new Path("C:/mkdir.exe"), + "\"./obj/\"", + new List(), + new List() + { + new Path("obj/"), + }), + new BuildOperation( + "MakeDir [./bin/]", + new Path("C:/target/"), + new Path("C:/mkdir.exe"), + "\"./bin/\"", + new List(), + new List() + { + new Path("bin/"), + }), + new BuildOperation( + "Copy [./obj/Public.mock.bmi] -> [./bin/Library.mock.bmi]", + new Path("C:/target/"), + new Path("C:/copy.exe"), + "\"./obj/Public.mock.bmi\" \"./bin/Library.mock.bmi\"", + new List() + { + new Path("obj/Public.mock.bmi"), + }, + new List() + { + new Path("bin/Library.mock.bmi"), + }), + new BuildOperation( + "MockCompilePartition: 1", + new Path("MockWorkingDirectory"), + new Path("MockCompiler.exe"), + "Arguments", + new List() + { + new Path("TestFile1.cpp"), + }, + new List() + { + new Path("obj/TestFile1.mock.obj"), + new Path("obj/TestFile1.mock.bmi"), + }), + new BuildOperation( + "MockCompilePartition: 1", + new Path("MockWorkingDirectory"), + new Path("MockCompiler.exe"), + "Arguments", + new List() + { + new Path("TestFile2.cpp"), + }, + new List() + { + new Path("obj/TestFile2.mock.obj"), + new Path("obj/TestFile2.mock.bmi"), + }), + new BuildOperation( + "MockCompilePartition: 1", + new Path("MockWorkingDirectory"), + new Path("MockCompiler.exe"), + "Arguments", + new List() + { + new Path("TestFile3.cpp"), + }, + new List() + { + new Path("obj/TestFile3.mock.obj"), + new Path("obj/TestFile3.mock.bmi"), + }), + new BuildOperation( + "MockCompileModule: 1", + new Path("MockWorkingDirectory"), + new Path("MockCompiler.exe"), + "Arguments", + new List() + { + new Path("Public.cpp"), + }, + new List() + { + new Path("obj/Public.mock.obj"), + new Path("obj/Public.mock.bmi"), + }), + new BuildOperation( + "MockCompile: 1", + new Path("MockWorkingDirectory"), + new Path("MockCompiler.exe"), + "Arguments", + new List() + { + new Path("TestFile4.cpp"), + }, + new List() + { + new Path("obj/TestFile4.mock.obj"), + }), + new BuildOperation( + "MockLink: 1", + new Path("MockWorkingDirectory"), + new Path("MockLinker.exe"), + "Arguments", + new List() + { + new Path("InputFile.in"), + }, + new List() + { + new Path("OutputFile.out"), + }), + }; + + Assert.Equal( + expectedBuildOperations, + result.BuildOperations); + + Assert.Equal( + new List() + { + new Path("../Other/bin/OtherModule1.mock.bmi"), + new Path("../OtherModule2.mock.bmi"), + new Path("C:/target/obj/TestFile1.mock.bmi"), + new Path("C:/target/obj/TestFile2.mock.bmi"), + new Path("C:/target/obj/TestFile3.mock.bmi"), + new Path("C:/target/bin/Library.mock.bmi"), + }, + result.ModuleDependencies); + + Assert.Equal( + new List() + { + new Path("../Other/bin/OtherModule1.mock.a"), + new Path("../OtherModule2.mock.a"), + new Path("C:/target/bin/Library.mock.lib"), + }, + result.LinkDependencies); + + Assert.Equal( + new List(), + result.RuntimeDependencies); + + Assert.Equal( + new Path(), + result.TargetFile); + } + } + [Fact] public void Build_Library_ModuleInterfaceNoSource() { @@ -1651,6 +2025,8 @@ public void Build_Library_ModuleInterfaceNoSource() Assert.Equal( new List() { + new Path("../Other/bin/OtherModule1.mock.bmi"), + new Path("../OtherModule2.mock.bmi"), new Path("C:/target/bin/Library.mock.bmi"), }, result.ModuleDependencies); diff --git a/Source/GenerateSharp/Extensions/Cpp/Compiler/Core/BuildEngine.cs b/Source/GenerateSharp/Extensions/Cpp/Compiler/Core/BuildEngine.cs index e7d1a4ef..3acb1b63 100644 --- a/Source/GenerateSharp/Extensions/Cpp/Compiler/Core/BuildEngine.cs +++ b/Source/GenerateSharp/Extensions/Cpp/Compiler/Core/BuildEngine.cs @@ -27,13 +27,8 @@ public BuildResult Execute( { var result = new BuildResult(); - // There is a bug in MSVC that requires all input module interface files, - // Add a copy back into the parent list for now... - // TODO: MSVC requires the entire closure of interfaces - if (_compiler.Name == "MSVC") - { - result.ModuleDependencies = new List(arguments.ModuleDependencies); - } + // All dependencies must include the entire interface dependency closure + result.ModuleDependencies = new List(arguments.ModuleDependencies); // Ensure the output directories exists as the first step result.BuildOperations.Add( @@ -108,6 +103,13 @@ private void CoreCompile( compileArguments.ResourceFile = compileResourceFileArguments; } + // Build up the entire Interface Dependency Closure for each file + var partitionInterfaceDependencyLookup = new Dictionary>(); + foreach (var file in arguments.ModuleInterfacePartitionSourceFiles) + { + partitionInterfaceDependencyLookup.Add(file.File, file.Imports); + } + // Compile the individual module interface partition translation units var compileInterfacePartitionUnits = new List(); var allPartitionInterfaces = new List(); @@ -120,8 +122,15 @@ private void CoreCompile( new Path(file.File.GetFileName()); objectModuleInterfaceFile.SetFileExtension(_compiler.ModuleFileExtension); + var interfaceDependencyClosure = new HashSet(); + BuildClosure(interfaceDependencyClosure, file.File, partitionInterfaceDependencyLookup); + if (interfaceDependencyClosure.Contains(file.File)) + { + throw new InvalidOperationException($"Circular partition references in: {file.File}"); + } + var partitionImports = new List(); - foreach (var import in file.Imports) + foreach (var import in interfaceDependencyClosure) { var importInterface = arguments.ObjectDirectory + new Path(import.GetFileName()); importInterface.SetFileExtension(_compiler.ModuleFileExtension); @@ -425,6 +434,18 @@ private void CopyRuntimeDependencies( } } + private void BuildClosure( + ISet closure, + Path file, + IDictionary> partitionInterfaceDependencyLookup) + { + foreach (var childFile in partitionInterfaceDependencyLookup[file]) + { + closure.Add(childFile); + BuildClosure(closure, childFile, partitionInterfaceDependencyLookup); + } + } + private OptimizationLevel Convert(BuildOptimizationLevel value) { switch (value) From c3373e79047e237d614586e08eba6327971bbbf9 Mon Sep 17 00:00:00 2001 From: Matthew Asplund Date: Mon, 21 Mar 2022 18:49:55 -0700 Subject: [PATCH 2/2] Bump version --- Source/Client/CLI/Recipe.toml | 2 +- Source/Client/CLI/Source/Commands/VersionCommand.h | 2 +- Source/Installer/SoupInstaller/Setup.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Client/CLI/Recipe.toml b/Source/Client/CLI/Recipe.toml index d3c67c95..2be61a7f 100644 --- a/Source/Client/CLI/Recipe.toml +++ b/Source/Client/CLI/Recipe.toml @@ -1,6 +1,6 @@ Name = "Soup" Language = "C++" -Version = "0.17.4" +Version = "0.17.5" Type = "Executable" Source = [ diff --git a/Source/Client/CLI/Source/Commands/VersionCommand.h b/Source/Client/CLI/Source/Commands/VersionCommand.h index 0af86b23..2cb0ad4c 100644 --- a/Source/Client/CLI/Source/Commands/VersionCommand.h +++ b/Source/Client/CLI/Source/Commands/VersionCommand.h @@ -31,7 +31,7 @@ namespace Soup::Client // TODO var version = Assembly.GetExecutingAssembly().GetName().Version; // Log::Message($"{version.Major}.{version.Minor}.{version.Build}"); - Log::HighPriority("0.17.4"); + Log::HighPriority("0.17.5"); } private: diff --git a/Source/Installer/SoupInstaller/Setup.cs b/Source/Installer/SoupInstaller/Setup.cs index d5d20f23..3d4cf8fe 100644 --- a/Source/Installer/SoupInstaller/Setup.cs +++ b/Source/Installer/SoupInstaller/Setup.cs @@ -65,7 +65,7 @@ static public void Main() }; // Upgrade values - project.Version = new Version(0, 17, 4); + project.Version = new Version(0, 17, 5); Compiler.BuildMsi(project); }