Sharpmake.FunctionalTests/FastBuildFunctionalTest/FastBuildFunctionalTest.sharpmake.cs (570 lines of code) (raw):

// Copyright (c) 2019-2022 Ubisoft Entertainment // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using Sharpmake; using Sharpmake.Generators.FastBuild; namespace SharpmakeGen.FunctionalTests { public static class FunctionalTestArguments { public static bool EnableLinkerMultiStamp = false; [CommandLine.Option("enableLinkerMultiStamp", @"Allow more than one post-build stamper for one executable. Default value: false. ex: /enableLinkerMultiStamp(<true|false>)")] public static void CommandLineEnableLinkerMultiStamp(bool value) { EnableLinkerMultiStamp = value; } } [DebuggerDisplay("\"{Platform}_{DevEnv}\" {Name}")] public class Target : Sharpmake.ITarget { public Platform Platform; public DevEnv DevEnv; public Optimization Optimization; public Blob Blob; public BuildSystem BuildSystem; public Target() { } public Target( Platform platform, DevEnv devEnv, Optimization optimization, Blob blob, BuildSystem buildSystem ) { Platform = platform; DevEnv = devEnv; Optimization = optimization; Blob = blob; BuildSystem = buildSystem; } public override string Name { get { var nameParts = new List<string>(); nameParts.Add(Optimization.ToString()); nameParts.Add(BuildSystem.ToString()); if ((BuildSystem == BuildSystem.FastBuild && Blob == Blob.NoBlob) || Blob == Blob.Blob) nameParts.Add(Blob.ToString()); nameParts.Add(DevEnv.ToString()); return string.Join("_", nameParts); } } public string NameForSolution { get { return Optimization.ToString(); } } public string SolutionPlatformName { get { var nameParts = new List<string>(); nameParts.Add(BuildSystem.ToString()); if (BuildSystem == BuildSystem.FastBuild && Blob == Blob.NoBlob) nameParts.Add(Blob.ToString()); return string.Join("_", nameParts); } } public static ITarget[] GetDefaultTargets() { var targets = new List<ITarget> { new Target( Platform.win64, DevEnv.vs2019, Optimization.Debug | Optimization.Release, Blob.NoBlob, BuildSystem.MSBuild ) }; // make a fastbuild no-blob version of the target targets.Add(targets.First().Clone(BuildSystem.FastBuild)); // and a fastbuild unity version of the target targets.Add(targets.First().Clone(Blob.FastBuildUnitys, BuildSystem.FastBuild)); return targets.ToArray(); } } public class SortableBuildStepCopy : Sharpmake.Project.Configuration.BuildStepCopy { public int Order; public SortableBuildStepCopy(string sourcePath, string destinationPath, bool isNameSpecific = false, string copyPattern = "*", bool fileCopy = true) : base(sourcePath, destinationPath, isNameSpecific, copyPattern, fileCopy) { } public override int CompareTo(object obj) { if (obj == null) return 1; if (obj is SortableBuildStepCopy) return Order.CompareTo((obj as SortableBuildStepCopy).Order); return 0; } } public abstract class CommonProject : Project { public CommonProject() : base(typeof(Target)) { RootPath = @"[project.SharpmakeCsPath]"; SourceRootPath = @"[project.RootPath]\codebase\[project.Name]"; AddTargets(Target.GetDefaultTargets()); } [Configure] public virtual void ConfigureAll(Configuration conf, Target target) { conf.ProjectFileName = "[project.Name]_[target.DevEnv]_[target.Platform]"; conf.ProjectPath = @"[project.SharpmakeCsPath]\projects"; conf.Output = Configuration.OutputType.Lib; conf.IntermediatePath = @"[conf.ProjectPath]\build\[conf.Name]\[project.Name]"; conf.TargetPath = @"[conf.ProjectPath]\output\[conf.Name]"; // .lib files must be with the .obj files when running in fastbuild distributed mode or we'll have missing symbols due to merging of the .pdb conf.TargetLibraryPath = "[conf.IntermediatePath]"; } [Configure(BuildSystem.FastBuild)] public virtual void ConfigureFastBuild(Configuration conf, Target target) { conf.IsFastBuild = true; conf.FastBuildBlobbed = target.Blob == Blob.FastBuildUnitys; // Force writing to pdb from different cl.exe process to go through the pdb server conf.AdditionalCompilerOptions.Add("/FS"); } [Configure(Blob.FastBuildUnitys)] public virtual void FastBuildUnitys(Configuration conf, Target target) { conf.BlobPath = @"[conf.ProjectPath]\unity\[project.Name]"; conf.FastBuildBlobbingStrategy = Configuration.InputFileStrategy.Exclude; conf.FastBuildNoBlobStrategy = Configuration.InputFileStrategy.Include; } [Configure(Blob.NoBlob)] public virtual void BlobNoBlob(Configuration conf, Target target) { } [Configure(Platform.win64)] public virtual void ConfigureWin64(Configuration conf, Target target) { } } public abstract class CommonExeProject : CommonProject { public override void ConfigureAll(Configuration conf, Target target) { base.ConfigureAll(conf, target); conf.Output = Configuration.OutputType.Exe; } public override void ConfigureWin64(Configuration conf, Target target) { base.ConfigureWin64(conf, target); // workaround necessity of rc.exe conf.Options.Add(Options.Vc.Linker.EmbedManifest.No); } } [Generate] public class MixCppAndCExe : CommonExeProject { public MixCppAndCExe() { } } public abstract class SpanMultipleSrcDirs : CommonExeProject { public SpanMultipleSrcDirs() { SourceRootPath = @"[project.RootPath]\codebase\SpanMultipleSrcDirs\main_dir"; AdditionalSourceRootPaths.Add(@"[project.RootPath]\codebase\SpanMultipleSrcDirs\additional_dir"); SourceFiles.Add( @"..\dir_individual_files\floating_class.cpp", @"..\dir_individual_files\floating_class.h", @"..\dir_individual_files\floating_file.cpp" ); } public override void ConfigureAll(Configuration conf, Target target) { base.ConfigureAll(conf, target); // needed to allow the files from the main SourceRootPath to include things from that dir conf.IncludePrivatePaths.Add(@"[project.RootPath]\codebase\SpanMultipleSrcDirs\dir_individual_files"); // needed to allow the files from the main SourceRootPath to include things in AdditionalSourceRootPaths conf.IncludePrivatePaths.AddRange(AdditionalSourceRootPaths); } } [Generate] public class SpanMultipleSrcDirsFBUnityInclude : SpanMultipleSrcDirs { public SpanMultipleSrcDirsFBUnityInclude() { AddFragmentMask(Blob.FastBuildUnitys); } public override void FastBuildUnitys(Configuration conf, Target target) { base.FastBuildUnitys(conf, target); conf.FastBuildBlobbingStrategy = Configuration.InputFileStrategy.Include; } } [Generate] public class SpanMultipleSrcDirsFBUnityExclude : SpanMultipleSrcDirs { public SpanMultipleSrcDirsFBUnityExclude() { AddFragmentMask(Blob.FastBuildUnitys); } public override void FastBuildUnitys(Configuration conf, Target target) { base.FastBuildUnitys(conf, target); conf.FastBuildBlobbingStrategy = Configuration.InputFileStrategy.Exclude; } } [Generate] public class SpanMultipleSrcDirsFBNoBlobInclude : SpanMultipleSrcDirs { public SpanMultipleSrcDirsFBNoBlobInclude() { AddFragmentMask(Blob.NoBlob); } public override void BlobNoBlob(Configuration conf, Target target) { base.BlobNoBlob(conf, target); conf.FastBuildNoBlobStrategy = Configuration.InputFileStrategy.Include; } } [Generate] public class SpanMultipleSrcDirsFBNoBlobExclude : SpanMultipleSrcDirs { public SpanMultipleSrcDirsFBNoBlobExclude() { AddFragmentMask(Blob.NoBlob); } public override void BlobNoBlob(Configuration conf, Target target) { base.BlobNoBlob(conf, target); conf.FastBuildNoBlobStrategy = Configuration.InputFileStrategy.Exclude; } } [Generate] public class SpanMultipleSrcDirsFBUnityIsolate : SpanMultipleSrcDirs { public SpanMultipleSrcDirsFBUnityIsolate() { AddFragmentMask(Blob.FastBuildUnitys); } public override void FastBuildUnitys(Configuration conf, Target target) { base.FastBuildUnitys(conf, target); // Isolating writable files works well only for Perforce conf.FastBuildUnityInputIsolateWritableFiles = false; // Provides a list of files that should be manually isolated. It can be used for Git difference of names, for example conf.FastBuildUnityInputIsolateListFile = @"[project.RootPath]\codebase\SpanMultipleSrcDirs\temp\isolate_list.txt"; } } [Generate] public class UsePrecompExe : CommonExeProject { public UsePrecompExe() { SourceFilesExtensions.Add( ".ceecee", ".ceepeepee" ); SourceFilesCompileExtensions.Add( ".ceecee", ".ceepeepee" ); } public override void ConfigureAll(Configuration conf, Target target) { base.ConfigureAll(conf, target); conf.PrecompHeader = "precomp.h"; conf.PrecompSource = "precomp.cpp"; // FIXME: the following line exposes a bug, since the filename ends with the precomp name... //conf.PrecompSourceExclude.Add("util_noprecomp.cpp"); conf.PrecompSourceExclude.Add("noprecomp_util.cpp"); conf.PrecompSourceExcludeExtension.Add(".ceepeepee"); conf.Defines.Add("SOME_UTILITY_STRING=\"UTIL FUNC\""); } } [Generate] public class RequirePreBuildStep : CommonExeProject { public RequirePreBuildStep() { } public override void ConfigureAll(Configuration conf, Target target) { base.ConfigureAll(conf, target); string generatedHeaderFilename = "header_generated_by_prebuild_step.h"; string relativeGeneratedHeaderFilePath = Path.Combine("generated", generatedHeaderFilename); string absoluteGeneratedHeaderPath = Path.Combine("[conf.ProjectPath]", "generated"); string absoluteGeneratedHeaderFilePath = Path.Combine(absoluteGeneratedHeaderPath, generatedHeaderFilename); // Create a PreBuild step that creates a header file that is required for compilation var preBuildStep = new Configuration.BuildStepExecutable( @"[project.SourceRootPath]\execute.bat", @"[project.SourceRootPath]\main.cpp", absoluteGeneratedHeaderFilePath, "echo #define PREBUILD_GENERATED_DEFINE() 0 > " + relativeGeneratedHeaderFilePath); conf.EventCustomPrebuildExecute.Add("GenerateHeader", preBuildStep); conf.IncludePrivatePaths.Add(absoluteGeneratedHeaderPath); } } [Generate] public class PostBuildCopySingleFileTest : CommonExeProject { public PostBuildCopySingleFileTest() { } public override void ConfigureAll(Configuration conf, Target target) { base.ConfigureAll(conf, target); // Create a PostBuild step that copies the buildoutput to another subfolder. // Note that the target path needs to be a file path, not a folder. Otherwise // Sharpmake will create a CopyDir node instead of a Copy node. var copyFileBuildStep = new Configuration.BuildStepCopy( @"[conf.TargetPath]\[conf.TargetFileName].exe", @"[conf.TargetPath]\file_copy_destination\[conf.TargetFileName].exe"); conf.EventCustomPostBuildExe.Add(copyFileBuildStep); } } [Generate] public class PostBuildCopyDirTest : CommonExeProject { public PostBuildCopyDirTest() { } public override void ConfigureAll(Configuration conf, Target target) { base.ConfigureAll(conf, target); // Create a PostBuild step that copies all .cpp & .txt files from the source dir to another folder // Note that this copy step will not depend on compilation output and thus FastBuild is free // execute the copy operation during or before compilation. var copyDirBuildStep = new Configuration.BuildStepCopy( @"[project.SourceRootPath]", @"[conf.TargetPath]\file_copy_destination"); copyDirBuildStep.IsFileCopy = false; copyDirBuildStep.CopyPattern = "*.cpp *.txt"; conf.EventCustomPostBuildExe.Add(copyDirBuildStep); } } [Generate] public class PostBuildCopyDirNoPatternTest : CommonExeProject { public PostBuildCopyDirNoPatternTest() { } public override void ConfigureAll(Configuration conf, Target target) { base.ConfigureAll(conf, target); // Create a PostBuild step that copies all .cpp & .txt files from the source dir to another folder // Note that this copy step will not depend on compilation output and thus FastBuild is free // execute the copy operation during or before compilation. var copyDirBuildStep = new Configuration.BuildStepCopy( @"[project.SourceRootPath]", @"[conf.TargetPath]\file_copy_destination_no_pattern"); copyDirBuildStep.IsFileCopy = false; copyDirBuildStep.CopyPattern = string.Empty; conf.EventCustomPostBuildExe.Add(copyDirBuildStep); } } [Generate] public class ExplicitlyOrderedPostBuildTest : CommonExeProject { public ExplicitlyOrderedPostBuildTest() { } public override void ConfigureAll(Configuration conf, Target target) { base.ConfigureAll(conf, target); // Copy executable (build output) to another folder. This does not need explicit prebuild dependencies // in FastBuild, since the source path is a file node that is defined by the Executable() function (the linker step). // Therefore linking the executable is implicitly a prebuild dependency of this copy step. var copyExeFileBuildStep = new SortableBuildStepCopy( @"[conf.TargetPath]\[conf.TargetFileName].exe", @"[conf.TargetPath]\explicitly_ordered_postbuild_test\temp_copy\[conf.TargetFileName].exe"); conf.EventCustomPostBuildExe.Add(copyExeFileBuildStep); // Copy the PDB to another folder. FastBuild has no knowledge about the source of this pdb file. // Since we add the BuildStepCopy object to the PostBuildExe list, Sharpmake should create a PreBuildDependency // for the linker step automatically, to make sure the copy is executed only after the linking, which creates the pdb. var copyPdbFileBuildStep = new SortableBuildStepCopy( @"[conf.TargetPath]\[conf.TargetFileName].pdb", @"[conf.TargetPath]\explicitly_ordered_postbuild_test\temp_copy\[conf.TargetFileName].pdb"); conf.EventCustomPostBuildExe.Add(copyPdbFileBuildStep); // Copy both .exe & .pdb to another location. This needs explicit ordering to ensure that the folder is only copied // after the folder is filled with the two previous copy steps. var copyDirBuildStep = new SortableBuildStepCopy( @"[conf.TargetPath]\explicitly_ordered_postbuild_test\temp_copy", @"[conf.TargetPath]\file_copy_destination"); copyDirBuildStep.IsFileCopy = false; copyDirBuildStep.CopyPattern = "*.*"; copyDirBuildStep.Order = 1; conf.EventCustomPostBuildExe.Add(copyDirBuildStep); } } [Generate] public class PostBuildExecuteTest : CommonExeProject { public PostBuildExecuteTest() { } public override void ConfigureAll(Configuration conf, Target target) { base.ConfigureAll(conf, target); // Create an Executable build step that executes the build output. var execBuildStep = new Configuration.BuildStepExecutable( @"[conf.TargetPath]\[conf.TargetFileName].exe", @"", @"[conf.TargetPath]\postbuild_exec_sentinel.txt", @""); execBuildStep.FastBuildUseStdOutAsOutput = true; conf.EventCustomPostBuildExe.Add(execBuildStep); } } [Generate] public class PostBuildTestExecution : CommonExeProject { public PostBuildTestExecution() { } public override void ConfigureAll(Configuration conf, Target target) { base.ConfigureAll(conf, target); // Create a Test build step that executes the build output. // All output from the execution is written to 'test_execution_output.txt'. var testBuildStep = new Configuration.BuildStepTest( @"[conf.TargetPath]\[conf.TargetFileName].exe", @"", @"[conf.TargetPath]\test_execution_output.txt", @""); conf.EventCustomPostBuildExe.Add(testBuildStep); } } [Generate] public class PostBuildStamper : CommonExeProject { public PostBuildStamper() { SourceRootPath = @"[project.RootPath]\codebase\PostBuildStampTest"; } } [Generate] public class PostBuildStampTest : CommonExeProject { public PostBuildStampTest() { } public override void ConfigureAll(Configuration conf, Target target) { base.ConfigureAll(conf, target); conf.AddPublicDependency<PostBuildStamper>(target); if (FunctionalTestArguments.EnableLinkerMultiStamp) { conf.PostBuildStampExes = new List<Configuration.BuildStepExecutable> { new Configuration.BuildStepExecutable( @"[conf.TargetPath]\PostBuildStamper.exe", @"", @"[conf.TargetPath]\[conf.TargetFileName].exe", @"_Stamp", useStdOutAsOutput : true), new Configuration.BuildStepExecutable( @"[conf.TargetPath]\PostBuildStamper.exe", @"", @"[conf.TargetPath]\[conf.TargetFileName].exe", @"_Message", useStdOutAsOutput : true) }; } else { conf.PostBuildStampExe = new Configuration.BuildStepExecutable( @"[conf.TargetPath]\PostBuildStamper.exe", @"", @"[conf.TargetPath]\[conf.TargetFileName].exe", @"_Stamp_Message", useStdOutAsOutput: true); } } } [Generate] public class SimpleLib : CommonProject { public SimpleLib() { } } [Generate] public class SimpleExeWithLib : CommonExeProject { public SimpleExeWithLib() { } public override void ConfigureAll(Configuration conf, Target target) { base.ConfigureAll(conf, target); conf.AddPublicDependency<SimpleLib>(target); } } [Generate] public class AllCppWithDotCExe : CommonExeProject { public AllCppWithDotCExe() { } public override void ConfigureAll(Configuration conf, Target target) { base.ConfigureAll(conf, target); conf.SourceFilesCompileAsCPPRegex.Add(".*\\.c"); } } [Sharpmake.Generate] public class FastBuildFunctionalTestSolution : Sharpmake.Solution { public FastBuildFunctionalTestSolution() : base(typeof(Target)) { Name = "FastBuildFunctionalTest"; AddTargets(Target.GetDefaultTargets()); } [Configure] public void ConfigureAll(Configuration conf, Target target) { conf.SolutionFileName = "[solution.Name]"; conf.SolutionPath = @"[solution.SharpmakeCsPath]\projects"; conf.Name = "[target.NameForSolution]"; conf.PlatformName = "[target.SolutionPlatformName]"; conf.AddProject<MixCppAndCExe>(target); conf.AddProject<UsePrecompExe>(target); conf.AddProject<RequirePreBuildStep>(target); conf.AddProject<PostBuildCopySingleFileTest>(target); conf.AddProject<PostBuildCopyDirNoPatternTest>(target); conf.AddProject<PostBuildCopyDirTest>(target); conf.AddProject<PostBuildExecuteTest>(target); conf.AddProject<PostBuildTestExecution>(target); conf.AddProject<PostBuildStampTest>(target); conf.AddProject<ExplicitlyOrderedPostBuildTest>(target); conf.AddProject<SimpleExeWithLib>(target); conf.AddProject<AllCppWithDotCExe>(target); if (target.Blob == Blob.FastBuildUnitys) { conf.AddProject<SpanMultipleSrcDirsFBUnityInclude>(target); conf.AddProject<SpanMultipleSrcDirsFBUnityExclude>(target); conf.AddProject<SpanMultipleSrcDirsFBUnityIsolate>(target); } else if (target.Blob == Blob.NoBlob) { if (target.BuildSystem == BuildSystem.FastBuild) { conf.AddProject<SpanMultipleSrcDirsFBNoBlobInclude>(target); conf.AddProject<SpanMultipleSrcDirsFBNoBlobExclude>(target); } } } [Sharpmake.Main] public static void SharpmakeMain(Sharpmake.Arguments arguments) { CommandLine.ExecuteOnType(typeof(FunctionalTestArguments)); FileInfo fileInfo = Util.GetCurrentSharpmakeFileInfo(); string sharpmakeRootDirectory = Util.SimplifyPath(Path.Combine(fileInfo.DirectoryName, "..", "..")); FastBuildSettings.FastBuildMakeCommand = Path.Combine(sharpmakeRootDirectory, @"tools\FastBuild\Windows-x64\FBuild.exe"); FastBuildSettings.FastBuildWait = true; FastBuildSettings.WriteAllConfigsSection = true; // This is just to insure that we are able to generate some custom property section when referenced from a Compiler section FastBuildSettings.AdditionalPropertyGroups.Add("function TestCustomProperties()", new List<string> { "Print('Hello Custom Property')", "Print('Hello Custom Property2')" }); FastBuildSettings.AdditionalCompilerPropertyGroups.Add("Compiler-x64-vs2019", "function TestCustomProperties()"); FastBuildSettings.AdditionalCompilerSettings.Add("Compiler-x64-vs2019", new List<string> { "TestCustomProperties()" }); KitsRootPaths.SetUseKitsRootForDevEnv(DevEnv.vs2019, KitsRootEnum.KitsRoot10, Options.Vc.General.WindowsTargetPlatformVersion.v10_0_19041_0); Bff.UnityResolver = new Bff.FragmentUnityResolver(); arguments.Generate<FastBuildFunctionalTestSolution>(); } } }