/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file LICENSE.rst or https://cmake.org/licensing for details. */ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cmBuildOptions.h" #include "cmGeneratedFileStream.h" #include "cmGlobalCommonGenerator.h" class cmFastbuildTargetGenerator; class cmGeneratorTarget; class cmGlobalGeneratorFactory; class cmLinkLineComputer; class cmLocalGenerator; class cmMakefile; class cmOutputConverter; class cmStateDirectory; class cmake; enum class cmDepfileFormat; struct cmDocumentationEntry; #define FASTBUILD_DOLLAR_TAG "FASTBUILD_DOLLAR_TAG" #define FASTBUILD_1_INPUT_PLACEHOLDER \ FASTBUILD_DOLLAR_TAG "FB_INPUT_1_PLACEHOLDER" FASTBUILD_DOLLAR_TAG #define FASTBUILD_1_0_INPUT_PLACEHOLDER \ FASTBUILD_DOLLAR_TAG "FB_INPUT_1_0_PLACEHOLDER" FASTBUILD_DOLLAR_TAG #define FASTBUILD_1_1_INPUT_PLACEHOLDER \ FASTBUILD_DOLLAR_TAG "FB_INPUT_1_1_PLACEHOLDER" FASTBUILD_DOLLAR_TAG #define FASTBUILD_2_INPUT_PLACEHOLDER \ FASTBUILD_DOLLAR_TAG "FB_INPUT_2_PLACEHOLDER" FASTBUILD_DOLLAR_TAG #define FASTBUILD_3_INPUT_PLACEHOLDER \ FASTBUILD_DOLLAR_TAG "FB_INPUT_3_PLACEHOLDER" FASTBUILD_DOLLAR_TAG // Alias to artifacts that can be consumed by the linker (DLL or Library). #define FASTBUILD_LINK_ARTIFACTS_ALIAS_POSTFIX "-link-artifacts" // Alias to all the ObjectList nodes. #define FASTBUILD_OBJECTS_ALIAS_POSTFIX "-objects" // Alias to all the dependencies of the target. #define FASTBUILD_DEPS_ARTIFACTS_ALIAS_POSTFIX "-deps" #define FASTBUILD_PRE_BUILD_ALIAS_POSTFIX "-pre-build" #define FASTBUILD_PRE_LINK_ALIAS_POSTFIX "-pre-link" #define FASTBUILD_POST_BUILD_ALIAS_POSTFIX "-post-build" // Alias to all other custom commands in the target. #define FASTBUILD_CUSTOM_COMMAND_ALIAS_POSTFIX "-custom-commands" // Alias to outputs produced by a custom command (since FASTBuild exec node // does not support more than 1 output). #define FASTBUILD_OUTPUTS_ALIAS_POSTFIX "-outputs" // Alias to byproducts produced by a custom command (since FASTBuild exec node // does not support more than 1 output). #define FASTBUILD_BYPRODUCTS_ALIAS_POSTFIX "-byproducts" #define FASTBUILD_COMPILER_PREFIX "Compiler_" #define FASTBUILD_LAUNCHER_PREFIX "Launcher_" #define FASTBUILD_LINKER_LAUNCHER_PREFIX "LinkerLauncher_" #define FASTBUILD_RESTAT_FILE "FASTBUILD_RESTAT" #define FASTBUILD_UTIL_CONCURRENCY_GROUP_NAME "Utils" #define FASTBUILD_ALL_TARGET_NAME "all" #define FASTBUILD_CLEAN_TARGET_NAME "clean" #define FASTBUILD_NOOP_FILE_NAME "fbuild_noop" #define FASTBUILD_CLEAN_FILE_NAME "fbuild_clean-out" #define FASTBUILD_BUILD_FILE "fbuild.bff" #define FASTBUILD_DUMMY_OUTPUT_EXTENSION ".fbuild-cc-out" #if defined(_WIN32) # define FASTBUILD_SCRIPT_FILE_EXTENSION ".bat" # define FASTBUILD_SCRIPT_FILE_ARG "/C " # define FASTBUILD_SCRIPT_CD "cd /D " # define FASTBUILD_CLEAN_SCRIPT_NAME "clean" FASTBUILD_SCRIPT_FILE_EXTENSION #else # define FASTBUILD_SCRIPT_FILE_EXTENSION ".sh" # define FASTBUILD_SCRIPT_FILE_ARG "" # define FASTBUILD_SCRIPT_CD "cd " # define FASTBUILD_CLEAN_SCRIPT_NAME "clean" FASTBUILD_SCRIPT_FILE_EXTENSION #endif enum class FastbuildTargetDepType { // Order-only dependency that is not going to appear in the generated file. ORDER_ONLY, // Regular target dep. REGULAR, }; struct FastbuildTargetDep { std::string Name; FastbuildTargetDepType Type = FastbuildTargetDepType::REGULAR; FastbuildTargetDep(std::string n) : Name(std::move(n)) { } bool operator==(FastbuildTargetDep const& rhs) const { return this->Name == rhs.Name; } bool operator<(FastbuildTargetDep const& rhs) const { return this->Name < rhs.Name; } }; enum class FastbuildTargetType { ALIAS, // Alias node EXEC, // Exec node LINK, // Library, DLL or Executable OBJECTLIST, UNITY, }; struct FastbuildTargetBase { // Target name with config postfix. std::string Name; // Target name without config postfix, we use it to locate IDE project for // the given target and add +1 config to it. std::string BaseName; std::string BasePath; std::set PreBuildDependencies; bool Hidden = true; FastbuildTargetType Type; explicit FastbuildTargetBase(FastbuildTargetType TargetType) : Type(TargetType) { } }; using FastbuildTargetPtrT = std::unique_ptr; struct FastbuildAliasNode : public FastbuildTargetBase { bool ExcludeFromAll = false; FastbuildAliasNode() : FastbuildTargetBase(FastbuildTargetType::ALIAS) { } }; struct FastbuildExecNode : public FastbuildTargetBase { std::string ExecExecutable; std::string ExecArguments; std::string ScriptFile; std::string ExecWorkingDir; bool ExecUseStdOutAsOutput = false; std::string ExecOutput; std::vector ExecInput; std::vector ExecInputPath; std::vector ExecInputPattern; bool ExecInputPathRecurse = false; bool ExecAlways = false; FastbuildAliasNode OutputsAlias; FastbuildAliasNode ByproductsAlias; std::string ConcurrencyGroupName; bool ExcludeFromAll = false; FastbuildExecNode() : FastbuildTargetBase(FastbuildTargetType::EXEC) { } bool NeedsDepsCheckExec = false; }; struct FastbuildCompiler { std::map ExtraVariables; std::string Name; std::string Executable; std::string CmakeCompilerID; std::string CompilerFamily = "custom"; std::string CmakeCompilerVersion; std::string Language; std::vector ExtraFiles; bool UseLightCache = false; bool ClangRewriteIncludes = true; bool ClangGCCUpdateXLanguageArg = false; bool AllowResponseFile = false; bool ForceResponseFile = false; bool UseRelativePaths = false; bool UseDeterministicPaths = false; std::string SourceMapping; // Only used for launchers. std::string Args; bool DontUseEnv = false; }; struct FastbuildObjectListNode : public FastbuildTargetBase { std::string Compiler; std::string CompilerOptions; std::string CompilerOutputPath; std::string CompilerOutputExtension; std::vector CompilerInputUnity; std::string PCHInputFile; std::string PCHOutputFile; std::string PCHOptions; std::vector CompilerInputFiles; bool AllowCaching = true; bool AllowDistribution = true; std::set ObjectOutputs; std::set ObjectDepends; // Apple only. std::string arch; FastbuildObjectListNode() : FastbuildTargetBase(FastbuildTargetType::OBJECTLIST) { } }; struct FastbuildUnityNode : public FastbuildTargetBase { std::string UnityOutputPath; std::vector UnityInputFiles; std::string UnityOutputPattern; std::vector UnityInputIsolatedFiles; FastbuildUnityNode() : FastbuildTargetBase(FastbuildTargetType::UNITY) { } }; struct IDEProjectConfig { std::string Config; std::string Target; // VS only. std::string Platform; std::string XCodeBaseSDK; std::string XCodeDebugWorkingDir; std::string XCodeIphoneOSDeploymentTarget; }; struct IDEProjectCommon { std::string Alias; std::string ProjectOutput; std::string ProjectBasePath; std::vector ProjectConfigs; }; struct XCodeProject : public IDEProjectCommon { }; struct VCXProject : public IDEProjectCommon { std::string folder; }; struct FastbuildLinkerNode { enum { EXECUTABLE, SHARED_LIBRARY, STATIC_LIBRARY, NONE } Type = NONE; std::string Name; std::string Compiler; std::string CompilerOptions; std::string Linker; std::string LinkerType; std::string LinkerOutput; std::string LinkerOptions; std::vector LibrarianAdditionalInputs; // We only use Libraries2 for tracking dependencies. std::vector Libraries2; std::set PreBuildDependencies; bool LinkerLinkObjects = false; std::string LinkerStampExe; std::string LinkerStampExeArgs; // Apple only. std::string Arch; }; struct FastbuildCopyNode { std::string Name; std::string Source; std::string Dest; std::string PreBuildDependencies; bool CopyDir = false; }; struct FastbuildExecNodes { std::vector Nodes; FastbuildAliasNode Alias; }; struct FastbuildTarget : public FastbuildTargetBase { std::map Variables; std::vector ObjectListNodes; std::vector UnityNodes; // Potentially multiple libs for different archs (apple only); std::vector LinkerNode; std::string RealOutput; FastbuildAliasNode PreBuildExecNodes, ExecNodes; std::vector AliasNodes; // This alias must be written before all other nodes, since they might need // to refer to it. FastbuildAliasNode DependenciesAlias; std::vector CopyNodes; FastbuildExecNodes PreLinkExecNodes; FastbuildExecNodes PostBuildExecNodes; bool IsGlobal = false; bool ExcludeFromAll = false; bool AllowDistribution = true; FastbuildTarget() : FastbuildTargetBase(FastbuildTargetType::LINK) { } void GenerateAliases(); }; class cmGlobalFastbuildGenerator : public cmGlobalCommonGenerator { public: cmGlobalFastbuildGenerator(cmake* cm); void ReadCompilerOptions(FastbuildCompiler& compiler, cmMakefile* mf); void ProcessEnvironment(); static std::unique_ptr NewFactory(); void Generate() override; bool FindMakeProgram(cmMakefile* mf) override; void EnableLanguage(std::vector const& lang, cmMakefile* mf, bool optional) override; bool IsFastbuild() const override { return true; } std::vector GenerateBuildCommand( std::string const& makeProgram, std::string const& projectName, std::string const& projectDir, std::vector const& targetNames, std::string const& config, int jobs, bool verbose, cmBuildOptions buildOptions = cmBuildOptions(), std::vector const& makeOptions = std::vector()) override; std::unique_ptr CreateLocalGenerator( cmMakefile* makefile) override; std::string GetName() const override { return cmGlobalFastbuildGenerator::GetActualName(); } bool IsMultiConfig() const override { return false; } bool SupportsCustomObjectNames() const override { return false; } void ComputeTargetObjectDirectory(cmGeneratorTarget*) const override; void AppendDirectoryForConfig(std::string const& prefix, std::string const& config, std::string const& suffix, std::string& dir) override; static std::string GetActualName() { return "FASTBuild"; } static std::string RequiredFastbuildVersion() { return "1.14"; } // Setup target names char const* GetAllTargetName() const override { return FASTBUILD_ALL_TARGET_NAME; } char const* GetInstallTargetName() const override { return "install"; } char const* GetCleanTargetName() const override { return FASTBUILD_CLEAN_TARGET_NAME; } char const* GetInstallLocalTargetName() const override { return "install/local"; } char const* GetInstallStripTargetName() const override { return "install/strip"; } char const* GetInstallParallelTargetName() const { return "install/parallel"; } char const* GetTestTargetName() const override { return "test"; } char const* GetPackageTargetName() const override { return "package"; } char const* GetPackageSourceTargetName() const override { return "package_source"; } char const* GetRebuildCacheTargetName() const override { return "rebuild_cache"; } char const* GetCMakeCFGIntDir() const override { return "."; } /// Overloaded methods. @see cmGlobalGenerator::GetDocumentation() static cmDocumentationEntry GetDocumentation(); static bool SupportsToolset() { return false; } static bool SupportsPlatform() { return false; } bool IsIPOSupported() const override { return true; } void OpenBuildFileStream(); void CloseBuildFileStream(); std::vector const& GetConfigNames() const; bool Open(std::string const& bindir, std::string const& projectName, bool dryRun) override; std::string ConvertToFastbuildPath(std::string const& path) const; std::unique_ptr CreateLinkLineComputer( cmOutputConverter* outputConverter, cmStateDirectory const& /* stateDir */) const override; bool SupportsCustomCommandDepfile() const override { return true; } cm::optional DepfileFormat() const override { return cm::nullopt; } static std::string Quote(std::string const& str, std::string const& quotation = "'"); static std::string QuoteIfHasSpaces(std::string str); template static std::vector Wrap(T const& in, std::string const& prefix = "'", std::string const& suffix = "'", bool escape_dollar = true); void WriteDivider(); void WriteComment(std::string const& comment, int indent = 0); /// Write @a count times INDENT level to output stream @a os. void Indent(int count); void WriteVariable(std::string const& key, std::string const& value, std::string const& op, int indent = 0); void WriteVariable(std::string const& key, std::string const& value, int indent = 0); void WriteCommand(std::string const& command, std::string const& value = std::string(), int indent = 0); void WriteArray(std::string const& key, std::vector const& values, int indent = 0); void WriteStruct( std::string const& name, std::vector> const& variables, int indent = 0); void WriteArray(std::string const& key, std::vector const& values, std::string const& op, int indent = 0); template std::vector ConvertToFastbuildPath(T const& container) const { std::vector ret; ret.reserve(container.size()); for (auto const& path : container) { ret.push_back(ConvertToFastbuildPath(path)); } return ret; } // Wrapper to sort array of conforming structs (which have .Name // and .PreBuildDependencies fields). template static void TopologicalSort(std::vector& nodes) { static_assert(std::is_base_of::value, "T must be derived from FastbuildTargetBase"); std::vector tmp; tmp.reserve(nodes.size()); for (auto& node : nodes) { tmp.emplace_back(cm::make_unique(std::move(node))); } nodes.clear(); TopologicalSort(tmp); for (auto& node : tmp) { nodes.emplace_back(std::move(static_cast(*node))); } } // Stable topological sort. static void TopologicalSort(std::vector& nodes); void WriteDisclaimer(); void WriteEnvironment(); void WriteSettings(); void WriteCompilers(); void WriteTargets(); void WriteTarget(FastbuildTarget const& target); void WriteExec(FastbuildExecNode const& Exec, int indent = 1); void WriteUnity(FastbuildUnityNode const& Unity); void WriteObjectList(FastbuildObjectListNode const& ObjectList, bool allowDistribution); void WriteLinker(FastbuildLinkerNode const&, bool); void WriteAlias(FastbuildAliasNode const& Alias, int indent = 1); void WriteCopy(FastbuildCopyNode const& Copy); void WriteIDEProjects(); void WriteVSBuildCommands(); void WriteXCodeBuildCommands(); void WriteIDEProjectCommon(IDEProjectCommon const& project); void WriteIDEProjectConfig(std::vector const& configs, std::string const& keyName = "ProjectConfigs"); void WriteSolution(); void WriteXCodeTopLevelProject(); void WriteTargetRebuildBFF(); void WriteCleanScript(); void WriteTargetClean(); void AddTargetAll(); void AddGlobCheckExec(); void AddCompiler(std::string const& lang, cmMakefile* mf); void AddLauncher(std::string const& prefix, std::string const& launcher, std::string const& lang, std::string const& args); void AddIDEProject(FastbuildTarget const& target, std::string const& config); template void AddTarget(T target) { // Sometimes equivalent CCs are added to different targets. We try to // de-dup it by assigning all execs a name which is a hash computed based // on various properties (like input, output, deps.). Apparently, there are // still some CCs intersection between different targets. auto val = AllGeneratedCommands.emplace(target.Name); if (val.second) { FastbuildTargets.emplace_back(cm::make_unique(std::move(target))); } // Get the intersection of CC's deps. Just mimicking what // cmLocalNinjaGenerator::WriteCustomCommandBuildStatement does. (I don't // think it's right in general case, each CC should be added only to 1 // target, not to multiple ) else { auto it = std::find_if(FastbuildTargets.begin(), FastbuildTargets.end(), [&target](FastbuildTargetPtrT const& existingTarget) { return existingTarget->Name == target.Name; }); assert(it != FastbuildTargets.end()); std::set intersection; std::set_intersection( target.PreBuildDependencies.begin(), target.PreBuildDependencies.end(), (*it)->PreBuildDependencies.begin(), (*it)->PreBuildDependencies.end(), std::inserter(intersection, intersection.end())); (*it)->PreBuildDependencies = std::move(intersection); } } static std::string GetExternalShellExecutable(); std::string GetTargetName(cmGeneratorTarget const* GeneratorTarget) const; cm::optional GetTargetByOutputName( std::string const& output) const; void AskCMakeToMakeRebuildBFFUpToDate(std::string const& workingDir) const; void ExecuteFastbuildTarget( std::string const& dir, std::string const& target, std::string& output, std::vector const& fbuildOptions = {}) const; bool IsExcluded(cmGeneratorTarget* target); void LogMessage(std::string const& m) const; void AddFileToClean(std::string const& file); /// The set of compilers added to the generated build system. std::map Compilers; std::vector FastbuildTargets; /// The file containing the build statement. std::unique_ptr BuildFileStream; std::string FastbuildCommand; std::string FastbuildVersion; std::map> Targets; std::unordered_set AllFoldersToClean; // Sometime we need to keep some files that are generated only during // configuration (like .objs files used to create module definition from // objects). std::unordered_set AllFilesToKeep; bool UsingRelativePaths = false; private: std::unordered_set AllFilesToClean; // https://cmake.org/cmake/help/latest/module/ExternalProject.html#command:externalproject_add_steptargets std::unordered_set AllGeneratedCommands; std::unordered_map> IDEProjects; // Env that we're going to embed to the generated file. std::vector LocalEnvironment; };