| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
| file LICENSE.rst or https://cmake.org/licensing for details. */ |
| |
| #include "cmGlobalFastbuildGenerator.h" |
| |
| #include <algorithm> |
| #include <cstdlib> |
| #include <initializer_list> |
| #include <iterator> |
| #include <queue> |
| #include <sstream> |
| |
| #include <cm/memory> |
| |
| #include "cmsys/FStream.hxx" |
| #include "cmsys/RegularExpression.hxx" |
| |
| #include "cmFastbuildLinkLineComputer.h" |
| #include "cmFastbuildTargetGenerator.h" // IWYU pragma: keep |
| #include "cmGeneratedFileStream.h" |
| #include "cmGeneratorTarget.h" |
| #include "cmGlobCacheEntry.h" |
| #include "cmGlobalGenerator.h" |
| #include "cmGlobalGeneratorFactory.h" |
| #include "cmList.h" |
| #include "cmLocalFastbuildGenerator.h" |
| #include "cmLocalGenerator.h" |
| #include "cmMakefile.h" |
| #include "cmMessageType.h" |
| #include "cmState.h" |
| #include "cmStateDirectory.h" |
| #include "cmStateSnapshot.h" |
| #include "cmStringAlgorithms.h" |
| #include "cmSystemTools.h" |
| #include "cmValue.h" |
| #include "cmVersion.h" |
| #include "cmake.h" |
| |
| #if defined(_WIN32) |
| # include <future> |
| |
| # include <objbase.h> |
| # include <shellapi.h> |
| |
| #endif |
| |
| class cmLinkLineComputer; |
| |
| #define FASTBUILD_REBUILD_BFF_TARGET_NAME "rebuild-bff" |
| #define FASTBUILD_GLOB_CHECK_TARGET "glob-check" |
| #define FASTBUILD_ENV_VAR_NAME "LocalEnv" |
| |
| // IDE support |
| #define FASTBUILD_XCODE_BASE_PATH "XCode/Projects" |
| #define FASTBUILD_VS_BASE_PATH "VisualStudio/Projects" |
| |
| #define FASTBUILD_IDE_VS_COMMAND_PREFIX "cd ^$(SolutionDir).. && " |
| #define FASTBUILD_IDE_BUILD_ARGS " -ide -cache -summary -dist " |
| |
| constexpr auto FASTBUILD_CAPTURE_SYSTEM_ENV = |
| "CMAKE_FASTBUILD_CAPTURE_SYSTEM_ENV"; |
| constexpr auto FASTBUILD_ENV_OVERRIDES = "CMAKE_FASTBUILD_ENV_OVERRIDES"; |
| |
| // Inherits from "CMAKE_FASTBUILD_VERBOSE_GENERATOR" env variable. |
| constexpr auto FASTBUILD_VERBOSE_GENERATOR = |
| "CMAKE_FASTBUILD_VERBOSE_GENERATOR"; |
| constexpr auto FASTBUILD_CACHE_PATH = "CMAKE_FASTBUILD_CACHE_PATH"; |
| // Compiler settings. |
| constexpr auto FASTBUILD_COMPILER_EXTRA_FILES = |
| "CMAKE_FASTBUILD_COMPILER_EXTRA_FILES"; |
| constexpr auto FASTBUILD_USE_LIGHTCACHE = "CMAKE_FASTBUILD_USE_LIGHTCACHE"; |
| constexpr auto FASTBUILD_USE_RELATIVE_PATHS = |
| "CMAKE_FASTBUILD_USE_RELATIVE_PATHS"; |
| constexpr auto FASTBUILD_USE_DETERMINISTIC_PATHS = |
| "CMAKE_FASTBUILD_USE_DETERMINISTIC_PATHS"; |
| constexpr auto FASTBUILD_SOURCE_MAPPING = "CMAKE_FASTBUILD_SOURCE_MAPPING"; |
| constexpr auto FASTBUILD_CLANG_REWRITE_INCLUDES = |
| "CMAKE_FASTBUILD_CLANG_REWRITE_INCLUDES"; |
| constexpr auto FASTBUILD_CLANG_GCC_UPDATE_XLANG_ARG = |
| "CMAKE_FASTBUILD_CLANG_GCC_UPDATE_XLANG_ARG"; |
| constexpr auto FASTBUILD_ALLOW_RESPONSE_FILE = |
| "CMAKE_FASTBUILD_ALLOW_RESPONSE_FILE"; |
| constexpr auto FASTBUILD_FORCE_RESPONSE_FILE = |
| "CMAKE_FASTBUILD_FORCE_RESPONSE_FILE"; |
| |
| static std::map<std::string, std::string> const compilerIdToFastbuildFamily = { |
| { "MSVC", "msvc" }, { "Clang", "clang" }, { "AppleClang", "clang" }, |
| { "GNU", "gcc" }, { "NVIDIA", "cuda-nvcc" }, { "Clang-cl", "clang-cl" }, |
| }; |
| |
| static std::set<std::string> const supportedLanguages = { "C", "CXX", "CUDA", |
| "OBJC", "OBJCXX" }; |
| |
| template <class T> |
| FastbuildAliasNode generateAlias(std::string const& name, char const* postfix, |
| T const& nodes) |
| { |
| FastbuildAliasNode alias; |
| alias.Name = name + postfix; |
| for (auto const& node : nodes) { |
| alias.PreBuildDependencies.emplace(node.Name); |
| } |
| return alias; |
| } |
| |
| void FastbuildTarget::GenerateAliases() |
| { |
| // -deps |
| this->DependenciesAlias.Name = |
| this->Name + FASTBUILD_DEPS_ARTIFACTS_ALIAS_POSTFIX; |
| for (auto const& dep : this->PreBuildDependencies) { |
| if (dep.Type != FastbuildTargetDepType::ORDER_ONLY) { |
| this->DependenciesAlias.PreBuildDependencies.emplace(dep); |
| } |
| } |
| |
| // PRE/POST/REST |
| if (!this->PreBuildExecNodes.PreBuildDependencies.empty()) { |
| this->PreBuildExecNodes.Name = |
| this->Name + FASTBUILD_PRE_BUILD_ALIAS_POSTFIX; |
| } |
| if (!this->PreLinkExecNodes.Nodes.empty()) { |
| this->PreLinkExecNodes.Alias = |
| generateAlias(this->Name, FASTBUILD_PRE_LINK_ALIAS_POSTFIX, |
| this->PreLinkExecNodes.Nodes); |
| } |
| if (!this->PostBuildExecNodes.Alias.PreBuildDependencies.empty()) { |
| this->PostBuildExecNodes.Alias.Name = |
| this->Name + FASTBUILD_POST_BUILD_ALIAS_POSTFIX; |
| } |
| if (!this->ExecNodes.PreBuildDependencies.empty()) { |
| this->ExecNodes.Name = this->Name + FASTBUILD_CUSTOM_COMMAND_ALIAS_POSTFIX; |
| } |
| |
| // If we don't have any node that we can build by name (e.g. no static / |
| // dynamic lib or executable) -> create an alias so that we can build this |
| // target by name. |
| if (LinkerNode.empty()) { |
| FastbuildAliasNode alias; |
| alias.Name = this->Name; |
| if (LinkerNode.empty()) { |
| for (FastbuildObjectListNode const& objListNode : ObjectListNodes) { |
| alias.PreBuildDependencies.emplace(objListNode.Name); |
| } |
| } else { |
| for (FastbuildLinkerNode const& linkerNode : LinkerNode) { |
| alias.PreBuildDependencies.emplace(linkerNode.Name); |
| } |
| } |
| AliasNodes.emplace_back(std::move(alias)); |
| } |
| |
| // Link artifacts (should not be added to all |
| // since on Windows it might contain Import Lib and FASTBuild doesn't know |
| // how to create it, so "-all" will fail). |
| AliasNodes.emplace_back(generateAlias( |
| this->Name, FASTBUILD_OBJECTS_ALIAS_POSTFIX, this->ObjectListNodes)); |
| |
| for (auto const& linkerNode : this->LinkerNode) { |
| if (linkerNode.Type == FastbuildLinkerNode::SHARED_LIBRARY || |
| linkerNode.Type == FastbuildLinkerNode::STATIC_LIBRARY || |
| linkerNode.Type == FastbuildLinkerNode::EXECUTABLE) { |
| std::string postfix = FASTBUILD_LINK_ARTIFACTS_ALIAS_POSTFIX; |
| if (!linkerNode.Arch.empty()) { |
| postfix += cmStrCat('-', linkerNode.Arch); |
| } |
| #ifdef _WIN32 |
| // On Windows DLL and Executables must be linked via Import Lib file |
| // (.lib). |
| if (linkerNode.Type == FastbuildLinkerNode::SHARED_LIBRARY || |
| linkerNode.Type == FastbuildLinkerNode::EXECUTABLE) { |
| FastbuildAliasNode linkAlias; |
| linkAlias.Name = this->Name + FASTBUILD_LINK_ARTIFACTS_ALIAS_POSTFIX; |
| linkAlias.PreBuildDependencies.emplace( |
| FASTBUILD_DOLLAR_TAG "TargetOutputImplib" FASTBUILD_DOLLAR_TAG); |
| AliasNodes.emplace_back(std::move(linkAlias)); |
| continue; |
| } |
| #endif |
| FastbuildAliasNode alias; |
| alias.Name = this->Name + postfix; |
| alias.PreBuildDependencies.emplace(linkerNode.LinkerOutput); |
| AliasNodes.emplace_back(std::move(alias)); |
| } |
| } |
| } |
| |
| cmGlobalFastbuildGenerator::cmGlobalFastbuildGenerator(cmake* cm) |
| : cmGlobalCommonGenerator(cm) |
| , BuildFileStream(nullptr) |
| { |
| #ifdef _WIN32 |
| cm->GetState()->SetWindowsShell(true); |
| #endif |
| this->FindMakeProgramFile = "CMakeFastbuildFindMake.cmake"; |
| cm->GetState()->SetFastbuildMake(true); |
| cm->GetState()->SetIsGeneratorMultiConfig(false); |
| } |
| |
| void cmGlobalFastbuildGenerator::ReadCompilerOptions( |
| FastbuildCompiler& compiler, cmMakefile* mf) |
| { |
| if (compiler.CompilerFamily == "custom") { |
| return; |
| } |
| |
| if (cmIsOn(mf->GetSafeDefinition(FASTBUILD_USE_LIGHTCACHE))) { |
| compiler.UseLightCache = true; |
| } |
| if (cmIsOn(mf->GetSafeDefinition(FASTBUILD_USE_RELATIVE_PATHS))) { |
| compiler.UseRelativePaths = true; |
| UsingRelativePaths = true; |
| } |
| if (cmIsOn(mf->GetSafeDefinition(FASTBUILD_USE_DETERMINISTIC_PATHS))) { |
| compiler.UseDeterministicPaths = true; |
| } |
| std::string sourceMapping = mf->GetSafeDefinition(FASTBUILD_SOURCE_MAPPING); |
| if (!sourceMapping.empty()) { |
| compiler.SourceMapping = std::move(sourceMapping); |
| } |
| auto const clangRewriteIncludesDef = |
| mf->GetDefinition(FASTBUILD_CLANG_REWRITE_INCLUDES); |
| if (clangRewriteIncludesDef.IsSet() && clangRewriteIncludesDef.IsOff()) { |
| compiler.ClangRewriteIncludes = false; |
| } |
| if (cmIsOn(mf->GetSafeDefinition(FASTBUILD_CLANG_GCC_UPDATE_XLANG_ARG))) { |
| compiler.ClangGCCUpdateXLanguageArg = true; |
| } |
| if (cmIsOn(mf->GetSafeDefinition(FASTBUILD_ALLOW_RESPONSE_FILE))) { |
| compiler.AllowResponseFile = true; |
| } |
| if (cmIsOn(mf->GetSafeDefinition(FASTBUILD_FORCE_RESPONSE_FILE))) { |
| compiler.ForceResponseFile = true; |
| } |
| } |
| |
| void cmGlobalFastbuildGenerator::ProcessEnvironment() |
| { |
| bool const CaptureSystemEnv = |
| !this->GetGlobalSetting(FASTBUILD_CAPTURE_SYSTEM_ENV).IsSet() || |
| this->GetGlobalSetting(FASTBUILD_CAPTURE_SYSTEM_ENV).IsOn(); |
| // On Windows environment is needed for MSVC, but preserve ability to discard |
| // it from the generated file if requested. |
| if (CaptureSystemEnv) { |
| LocalEnvironment = cmSystemTools::GetEnvironmentVariables(); |
| } |
| // FASTBuild strips off "-isysroot" command line option (see : |
| // https://github.com/fastbuild/fastbuild/issues/1066). |
| // If 'SDK_ROOT' is not set via env and '-isysroot' is absent, AppleClang |
| // seems to use MacOS SDK by default (even though FBuild flattens includes |
| // before compiling). It breaks cross-compilation for iOS. Tested in |
| // "RunCMake.Framework" test. |
| std::string const osxRoot = this->GetSafeGlobalSetting("CMAKE_OSX_SYSROOT"); |
| if (!osxRoot.empty()) { |
| LocalEnvironment.emplace_back("SDKROOT=" + osxRoot); |
| } |
| |
| auto const EnvOverrides = |
| this->GetSafeGlobalSetting(FASTBUILD_ENV_OVERRIDES); |
| |
| if (!EnvOverrides.empty()) { |
| auto const overrideEnvVar = [this](std::string const& prefix, |
| std::string val) { |
| auto const iter = |
| std::find_if(LocalEnvironment.begin(), LocalEnvironment.end(), |
| [&prefix](std::string const& value) { |
| return cmSystemTools::StringStartsWith(value.c_str(), |
| prefix.c_str()); |
| }); |
| if (iter != LocalEnvironment.end()) { |
| *iter = std::move(val); |
| } else { |
| LocalEnvironment.emplace_back(std::move(val)); |
| } |
| }; |
| for (auto const& val : cmList{ EnvOverrides }) { |
| auto const pos = val.find('='); |
| if (pos != std::string::npos && ((pos + 1) < val.size())) { |
| overrideEnvVar(val.substr(0, pos + 1), val); |
| } |
| } |
| } |
| |
| // Empty strings are not allowed. |
| LocalEnvironment.erase( |
| std::remove_if(LocalEnvironment.begin(), LocalEnvironment.end(), |
| [](std::string const& s) { return s.empty(); }), |
| LocalEnvironment.end()); |
| } |
| |
| std::unique_ptr<cmGlobalGeneratorFactory> |
| cmGlobalFastbuildGenerator::NewFactory() |
| { |
| return std::unique_ptr<cmGlobalGeneratorFactory>( |
| new cmGlobalGeneratorSimpleFactory<cmGlobalFastbuildGenerator>()); |
| } |
| |
| void cmGlobalFastbuildGenerator::EnableLanguage( |
| std::vector<std::string> const& lang, cmMakefile* mf, bool optional) |
| { |
| this->cmGlobalGenerator::EnableLanguage(lang, mf, optional); |
| for (std::string const& l : lang) { |
| if (l == "NONE") { |
| continue; |
| } |
| this->ResolveLanguageCompiler(l, mf, optional); |
| } |
| } |
| |
| bool cmGlobalFastbuildGenerator::FindMakeProgram(cmMakefile* mf) |
| { |
| if (!cmGlobalGenerator::FindMakeProgram(mf)) { |
| return false; |
| } |
| if (auto fastbuildCommand = mf->GetDefinition("CMAKE_MAKE_PROGRAM")) { |
| this->FastbuildCommand = *fastbuildCommand; |
| std::vector<std::string> command; |
| command.push_back(this->FastbuildCommand); |
| command.emplace_back("-version"); |
| std::string version; |
| std::string error; |
| if (!cmSystemTools::RunSingleCommand(command, &version, &error, nullptr, |
| nullptr, |
| cmSystemTools::OUTPUT_NONE)) { |
| mf->IssueMessage(MessageType::FATAL_ERROR, |
| "Running\n '" + cmJoin(command, "' '") + |
| "'\n" |
| "failed with:\n " + |
| error); |
| cmSystemTools::SetFatalErrorOccurred(); |
| return false; |
| } |
| cmsys::RegularExpression versionRegex(R"(^FASTBuild v([0-9]+\.[0-9]+))"); |
| versionRegex.find(version); |
| this->FastbuildVersion = versionRegex.match(1); |
| } |
| return true; |
| } |
| |
| std::unique_ptr<cmLocalGenerator> |
| cmGlobalFastbuildGenerator::CreateLocalGenerator(cmMakefile* makefile) |
| { |
| return std::unique_ptr<cmLocalGenerator>( |
| cm::make_unique<cmLocalFastbuildGenerator>(this, makefile)); |
| } |
| |
| std::vector<cmGlobalGenerator::GeneratedMakeCommand> |
| cmGlobalFastbuildGenerator::GenerateBuildCommand( |
| std::string const& makeProgram, std::string const& /*projectName*/, |
| std::string const& projectDir, std::vector<std::string> const& targetNames, |
| std::string const& /*config*/, int /*jobs*/, bool verbose, |
| cmBuildOptions /*buildOptions*/, std::vector<std::string> const& makeOptions) |
| { |
| GeneratedMakeCommand makeCommand; |
| this->FastbuildCommand = this->SelectMakeProgram(makeProgram); |
| makeCommand.Add(this->FastbuildCommand); |
| // A build command for fastbuild looks like this: |
| // fbuild.exe [make-options] [-config projectName.bff] <target> |
| |
| std::string configFile = cmStrCat(projectDir, '/', FASTBUILD_BUILD_FILE); |
| |
| // Push in the make options |
| makeCommand.Add(makeOptions.begin(), makeOptions.end()); |
| |
| if (!configFile.empty()) { |
| makeCommand.Add("-config", configFile); |
| } |
| // Tested in "RunCMake.SymlinkTrees" test. |
| makeCommand.Add("-continueafterdbmove"); |
| |
| // Tested in RunCMake.LinkWhatYouUse on Linux. (We need to see output of |
| // LinkerStampExe process). |
| // In general, it might be useful to see output of external processes |
| // regardless of their outcome. |
| makeCommand.Add("-showcmdoutput"); |
| |
| // Add the target-config to the command |
| for (auto const& tname : targetNames) { |
| if (!tname.empty()) { |
| makeCommand.Add(tname); |
| } |
| } |
| if (verbose) { |
| makeCommand.Add("-verbose"); |
| } |
| |
| // Make "rebuild-bff" target up-to-date before running the build. |
| |
| std::string output; |
| ExecuteFastbuildTarget(projectDir, FASTBUILD_REBUILD_BFF_TARGET_NAME, output, |
| { "-why" }); |
| |
| // If fbuild.bff was re-generated we need to "restat" it. |
| if (output.find("Need to build") != std::string::npos) { |
| // Let the user know that re-generation happened (and why it |
| // happened). |
| cmSystemTools::Stdout(output); |
| // FASTBuild will consider the target out-of-date in case some of the |
| // inputs have changes after re-generation which might happen if, for |
| // example, configuration depends on some files generated during |
| // the configuration itself. |
| AskCMakeToMakeRebuildBFFUpToDate(projectDir); |
| } |
| |
| return { std::move(makeCommand) }; |
| } |
| |
| void cmGlobalFastbuildGenerator::ComputeTargetObjectDirectory( |
| cmGeneratorTarget* gt) const |
| { |
| // Compute full path to object file directory for this target. |
| std::string dir = |
| cmStrCat(gt->GetSupportDirectory(), '/', this->GetCMakeCFGIntDir(), '/'); |
| gt->ObjectDirectory = std::move(dir); |
| } |
| |
| void cmGlobalFastbuildGenerator::AppendDirectoryForConfig( |
| std::string const& prefix, std::string const& config, |
| std::string const& suffix, std::string& dir) |
| { |
| if (!config.empty() && this->IsMultiConfig()) { |
| dir += cmStrCat(prefix, config, suffix); |
| } |
| } |
| |
| cmDocumentationEntry cmGlobalFastbuildGenerator::GetDocumentation() |
| { |
| return { cmGlobalFastbuildGenerator::GetActualName(), |
| "Generates fbuild.bff files." }; |
| } |
| |
| void cmGlobalFastbuildGenerator::Generate() |
| { |
| // Check minimum Fastbuild version. |
| if (cmSystemTools::VersionCompare(cmSystemTools::OP_LESS, |
| this->FastbuildVersion, |
| RequiredFastbuildVersion())) { |
| std::ostringstream msg; |
| msg << "The detected version of Fastbuild (" << this->FastbuildVersion; |
| msg << ") is less than the version of Fastbuild required by CMake ("; |
| msg << this->RequiredFastbuildVersion() << ")."; |
| this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, |
| msg.str()); |
| return; |
| } |
| this->ProcessEnvironment(); |
| |
| this->OpenBuildFileStream(); |
| |
| this->WriteSettings(); |
| this->WriteEnvironment(); |
| |
| // Execute the standard generate process |
| cmGlobalGenerator::Generate(); |
| |
| // Write compilers |
| this->WriteCompilers(); |
| |
| this->WriteTargets(); |
| |
| this->CloseBuildFileStream(); |
| |
| if (cmSystemTools::GetErrorOccurredFlag()) { |
| return; |
| } |
| |
| this->RemoveUnknownClangTidyExportFixesFiles(); |
| |
| if (this->GetCMakeInstance()->GetRegenerateDuringBuild()) { |
| return; |
| } |
| // TODO: figure out how to skip this in TryCompile |
| // Make "rebuild-bff" target up-to-date after the generation. |
| // This is actually a noop, it just asks CMake to touch the generated file |
| // so FASTBuild would consider the target as up-to-date. |
| AskCMakeToMakeRebuildBFFUpToDate( |
| this->GetCMakeInstance()->GetHomeOutputDirectory()); |
| } |
| |
| void cmGlobalFastbuildGenerator::AskCMakeToMakeRebuildBFFUpToDate( |
| std::string const& workingDir) const |
| { |
| // "restat" the generated build file. |
| // The idea here is to mimic what Ninja's "restat" command does. |
| // We need to make the "rebuild.bff" target up-to-date, so the regeneration |
| // will only be triggered when CMake files have actually changed. |
| // Tested in "RunCMake.Configure" test. |
| cmsys::ofstream{ |
| cmStrCat(workingDir, '/', FASTBUILD_RESTAT_FILE).c_str(), |
| std::ios::out | std::ios::binary |
| } << cmStrCat(workingDir, '/', FASTBUILD_BUILD_FILE); |
| std::string output; |
| ExecuteFastbuildTarget(workingDir, FASTBUILD_REBUILD_BFF_TARGET_NAME, |
| output); |
| } |
| |
| void cmGlobalFastbuildGenerator::ExecuteFastbuildTarget( |
| std::string const& dir, std::string const& target, std::string& output, |
| std::vector<std::string> const& fbuildOptions) const |
| { |
| std::vector<std::string> command; |
| |
| command.emplace_back(this->FastbuildCommand); |
| command.emplace_back("-config"); |
| std::string const file = cmStrCat(dir, '/', FASTBUILD_BUILD_FILE); |
| command.emplace_back(file); |
| command.emplace_back(target); |
| if (!fbuildOptions.empty()) { |
| command.emplace_back(cmJoin(fbuildOptions, " ")); |
| } |
| |
| int retVal = 0; |
| if (!cmSystemTools::RunSingleCommand(command, &output, nullptr, &retVal, |
| dir.c_str(), |
| cmSystemTools::OUTPUT_NONE) || |
| retVal != 0) { |
| cmSystemTools::Error(cmStrCat("Failed to run FASTBuild command:\n '", |
| cmJoin(command, "' '"), "'\nOutput:\n", |
| output)); |
| cmSystemTools::Stdout(output); |
| std::exit(retVal); |
| } |
| } |
| |
| void cmGlobalFastbuildGenerator::WriteSettings() |
| { |
| // Define some placeholder |
| WriteDivider(); |
| *this->BuildFileStream << "// Helper variables\n\n"; |
| |
| WriteVariable("FB_INPUT_1_PLACEHOLDER", Quote("\"%1\"")); |
| WriteVariable("FB_INPUT_1_0_PLACEHOLDER", Quote("\"%1[0]\"")); |
| WriteVariable("FB_INPUT_1_1_PLACEHOLDER", Quote("\"%1[1]\"")); |
| WriteVariable("FB_INPUT_2_PLACEHOLDER", Quote("\"%2\"")); |
| WriteVariable("FB_INPUT_3_PLACEHOLDER", Quote("\"%3\"")); |
| |
| std::string cacheDir; |
| // If explicitly set from CMake. |
| auto val = this->GetSafeGlobalSetting(FASTBUILD_CACHE_PATH); |
| if (!val.empty()) { |
| cacheDir = std::move(val); |
| cmSystemTools::ConvertToOutputSlashes(cacheDir); |
| } |
| |
| WriteDivider(); |
| *this->BuildFileStream << "// Settings\n\n"; |
| |
| WriteCommand("Settings"); |
| *this->BuildFileStream << "{\n"; |
| if (!cacheDir.empty()) { |
| WriteVariable("CachePath", Quote(cacheDir), 1); |
| } |
| // Concurrency groups. |
| WriteStruct( |
| FASTBUILD_UTIL_CONCURRENCY_GROUP_NAME, |
| { { "ConcurrencyGroupName", Quote(FASTBUILD_UTIL_CONCURRENCY_GROUP_NAME) }, |
| { "ConcurrencyLimit", "1" } }, |
| 1); |
| |
| WriteArray("ConcurrencyGroups", |
| { "." FASTBUILD_UTIL_CONCURRENCY_GROUP_NAME }, 1); |
| |
| *this->BuildFileStream << "}\n"; |
| } |
| |
| void cmGlobalFastbuildGenerator::WriteEnvironment() |
| { |
| if (!LocalEnvironment.empty()) { |
| WriteArray(FASTBUILD_ENV_VAR_NAME, Wrap(LocalEnvironment), 0); |
| } |
| } |
| |
| void cmGlobalFastbuildGenerator::WriteDivider() |
| { |
| *this->BuildFileStream << "// ======================================" |
| "=======================================\n"; |
| } |
| |
| void cmGlobalFastbuildGenerator::Indent(int count) |
| { |
| for (int i = 0; i < count; ++i) { |
| *this->BuildFileStream << " "; |
| } |
| } |
| |
| void cmGlobalFastbuildGenerator::WriteComment(std::string const& comment, |
| int indent) |
| { |
| if (comment.empty()) { |
| return; |
| } |
| |
| std::string::size_type lpos = 0; |
| std::string::size_type rpos; |
| *this->BuildFileStream << "\n"; |
| Indent(indent); |
| *this->BuildFileStream << "/////////////////////////////////////////////\n"; |
| while ((rpos = comment.find('\n', lpos)) != std::string::npos) { |
| Indent(indent); |
| *this->BuildFileStream << "// " << comment.substr(lpos, rpos - lpos) |
| << "\n"; |
| lpos = rpos + 1; |
| } |
| Indent(indent); |
| *this->BuildFileStream << "// " << comment.substr(lpos) << "\n\n"; |
| } |
| |
| void cmGlobalFastbuildGenerator::WriteVariable(std::string const& key, |
| std::string const& value, |
| int indent) |
| { |
| WriteVariable(key, value, "=", indent); |
| } |
| |
| void cmGlobalFastbuildGenerator::WriteVariable(std::string const& key, |
| std::string const& value, |
| std::string const& op, |
| int indent) |
| { |
| Indent(indent); |
| *this->BuildFileStream << "." << key << " " + op + (value.empty() ? "" : " ") |
| << value << "\n"; |
| } |
| |
| void cmGlobalFastbuildGenerator::WriteCommand(std::string const& command, |
| std::string const& value, |
| int indent) |
| { |
| Indent(indent); |
| *this->BuildFileStream << command; |
| if (!value.empty()) { |
| *this->BuildFileStream << "(" << value << ")"; |
| } |
| *this->BuildFileStream << "\n"; |
| } |
| |
| void cmGlobalFastbuildGenerator::WriteArray( |
| std::string const& key, std::vector<std::string> const& values, int indent) |
| { |
| WriteArray(key, values, "=", indent); |
| } |
| |
| void cmGlobalFastbuildGenerator::WriteArray( |
| std::string const& key, std::vector<std::string> const& values, |
| std::string const& op, int indent) |
| { |
| WriteVariable(key, "", op, indent); |
| Indent(indent); |
| *this->BuildFileStream << "{\n"; |
| char const* sep = ""; |
| for (std::string const& value : values) { |
| *this->BuildFileStream << sep; |
| sep = ",\n"; |
| Indent(indent + 1); |
| *this->BuildFileStream << value; |
| } |
| *this->BuildFileStream << "\n"; |
| Indent(indent); |
| *this->BuildFileStream << "}\n"; |
| } |
| |
| void cmGlobalFastbuildGenerator::WriteStruct( |
| std::string const& name, |
| std::vector<std::pair<std::string, std::string>> const& variables, |
| int indent) |
| { |
| WriteVariable(name, "", "=", indent); |
| Indent(indent); |
| *this->BuildFileStream << "[\n"; |
| for (auto const& val : variables) { |
| auto const& key = val.first; |
| auto const& value = val.second; |
| WriteVariable(key, value, "=", indent + 1); |
| } |
| Indent(indent); |
| *this->BuildFileStream << "]\n"; |
| } |
| |
| std::string cmGlobalFastbuildGenerator::Quote(std::string const& str, |
| std::string const& quotation) |
| { |
| std::string result = str; |
| cmSystemTools::ReplaceString(result, quotation, "^" + quotation); |
| cmSystemTools::ReplaceString(result, FASTBUILD_DOLLAR_TAG, "$"); |
| return quotation + result + quotation; |
| } |
| std::string cmGlobalFastbuildGenerator::QuoteIfHasSpaces(std::string str) |
| { |
| if (str.find(' ') != std::string::npos) { |
| return '"' + str + '"'; |
| } |
| return str; |
| } |
| |
| struct WrapHelper |
| { |
| std::string Prefix; |
| std::string Suffix; |
| bool EscapeDollar; |
| |
| std::string operator()(std::string in) |
| { |
| // If we have ^ in env variable - need to escape it. |
| cmSystemTools::ReplaceString(in, "^", "^^"); |
| // Those all are considered as line ends by FASTBuild. |
| cmSystemTools::ReplaceString(in, "\n", "\\n"); |
| cmSystemTools::ReplaceString(in, "\r", "\\r"); |
| // Escaping of single quotes tested in "RunCMake.CompilerArgs" test. |
| cmSystemTools::ReplaceString(in, "'", "^'"); |
| std::string result = Prefix + in + Suffix; |
| if (EscapeDollar) { |
| cmSystemTools::ReplaceString(result, "$", "^$"); |
| cmSystemTools::ReplaceString(result, FASTBUILD_DOLLAR_TAG, "$"); |
| } |
| return result; |
| } |
| std::string operator()(FastbuildTargetDep const& in) |
| { |
| return (*this)(in.Name); |
| } |
| }; |
| template <class T> |
| std::vector<std::string> cmGlobalFastbuildGenerator::Wrap( |
| T const& in, std::string const& prefix, std::string const& suffix, |
| bool const escape_dollar) |
| { |
| |
| std::vector<std::string> result; |
| |
| WrapHelper helper = { prefix, suffix, escape_dollar }; |
| |
| std::transform(in.begin(), in.end(), std::back_inserter(result), helper); |
| |
| return result; |
| } |
| |
| void cmGlobalFastbuildGenerator::TopologicalSort( |
| std::vector<FastbuildTargetPtrT>& nodes) |
| { |
| std::unordered_map<std::string, int> inDegree; |
| std::unordered_map<std::string, std::set<std::string>> reverseDeps; |
| std::unordered_map<std::string, std::size_t> originalIndex; |
| |
| // Track original positions |
| for (std::size_t i = 0; i < nodes.size(); ++i) { |
| auto const& node = nodes[i]; |
| inDegree[node->Name] = 0; |
| originalIndex[node->Name] = i; |
| } |
| |
| // Build reverse dependency graph and in-degree map |
| for (auto const& node : nodes) { |
| for (auto const& dep : node->PreBuildDependencies) { |
| if (inDegree.count(dep.Name)) { |
| reverseDeps[dep.Name].insert(node->Name); |
| ++inDegree[node->Name]; |
| } |
| } |
| } |
| |
| // Min-heap based on original position |
| auto const cmp = [&](std::string const& a, std::string const& b) { |
| return originalIndex[a] > originalIndex[b]; |
| }; |
| std::priority_queue<std::string, std::vector<std::string>, decltype(cmp)> |
| zeroInDegree(cmp); |
| |
| for (auto const& val : inDegree) { |
| auto const& degree = val.second; |
| auto const& name = val.first; |
| if (degree == 0) { |
| zeroInDegree.push(name); |
| } |
| } |
| |
| std::vector<std::string> sorted; |
| while (!zeroInDegree.empty()) { |
| std::string node = zeroInDegree.top(); |
| zeroInDegree.pop(); |
| sorted.push_back(node); |
| for (auto const& dep : reverseDeps[node]) { |
| if (--inDegree[dep] == 0) { |
| zeroInDegree.push(dep); |
| } |
| } |
| } |
| |
| if (sorted.size() != nodes.size()) { |
| cmSystemTools::Error("Failed to sort (Cyclic dependency)"); |
| cmSystemTools::Error(cmStrCat("Sorted size: ", sorted.size())); |
| cmSystemTools::Error(cmStrCat("nodes size: ", nodes.size())); |
| for (auto const& node : nodes) { |
| cmSystemTools::Error("Node: " + node->Name); |
| for (auto const& dep : reverseDeps[node->Name]) { |
| cmSystemTools::Error("\tReverse dep: " + dep); |
| } |
| for (auto const& child : node->PreBuildDependencies) { |
| cmSystemTools::Error("\tChild: " + child.Name); |
| } |
| } |
| for (auto const& node : sorted) { |
| cmSystemTools::Error("Sorted: " + node); |
| } |
| for (auto const& node : nodes) { |
| cmSystemTools::Error("In node: " + node->Name); |
| } |
| } |
| |
| // Reconstruct sorted nodes |
| std::vector<FastbuildTargetPtrT> result; |
| for (auto const& name : sorted) { |
| auto it = std::find_if( |
| nodes.begin(), nodes.end(), [&name](FastbuildTargetPtrT const& node) { |
| return node /* the node might be in moved-from state*/ && |
| node->Name == name; |
| }); |
| if (it != nodes.end()) { |
| result.emplace_back(std::move(*it)); |
| } |
| } |
| |
| std::swap(result, nodes); |
| } |
| |
| void cmGlobalFastbuildGenerator::WriteDisclaimer() |
| { |
| *this->BuildFileStream << "// CMAKE generated file: DO NOT EDIT!\n" |
| << "// Generated by \"" << this->GetName() << "\"" |
| << " Generator, CMake Version " |
| << cmVersion::GetMajorVersion() << "." |
| << cmVersion::GetMinorVersion() << "\n\n"; |
| } |
| |
| void cmGlobalFastbuildGenerator::OpenBuildFileStream() |
| { |
| // Compute Fastbuild's build file path. |
| std::string buildFilePath = |
| this->GetCMakeInstance()->GetHomeOutputDirectory(); |
| buildFilePath += "/"; |
| buildFilePath += FASTBUILD_BUILD_FILE; |
| |
| // Get a stream where to generate things. |
| if (!this->BuildFileStream) { |
| this->BuildFileStream = cm::make_unique<cmGeneratedFileStream>( |
| buildFilePath, false, this->GetMakefileEncoding()); |
| if (!this->BuildFileStream) { |
| // An error message is generated by the constructor if it cannot |
| // open the file. |
| return; |
| } |
| } |
| |
| // Write the do not edit header. |
| this->WriteDisclaimer(); |
| |
| // Write a comment about this file. |
| *this->BuildFileStream |
| << "// This file contains all the build statements\n\n"; |
| } |
| |
| void cmGlobalFastbuildGenerator::CloseBuildFileStream() |
| { |
| if (this->BuildFileStream) { |
| this->BuildFileStream.reset(); |
| } else { |
| cmSystemTools::Error("Build file stream was not open."); |
| } |
| } |
| |
| void cmGlobalFastbuildGenerator::WriteCompilers() |
| { |
| WriteDivider(); |
| *this->BuildFileStream << "// Compilers\n\n"; |
| for (auto const& val : Compilers) { |
| auto const& compilerDef = val.second; |
| |
| std::string compilerPath = compilerDef.Executable; |
| |
| // Write out the compiler that has been configured |
| WriteCommand("Compiler", Quote(compilerDef.Name)); |
| *this->BuildFileStream << "{\n"; |
| for (auto const& extra : compilerDef.ExtraVariables) { |
| auto const& extraKey = extra.first; |
| auto const& extraVal = extra.second; |
| WriteVariable(extraKey, Quote(extraVal), 1); |
| } |
| WriteVariable("Executable", Quote(compilerPath), 1); |
| WriteVariable("CompilerFamily", Quote(compilerDef.CompilerFamily), 1); |
| |
| if (compilerDef.UseLightCache && compilerDef.CompilerFamily == "msvc") { |
| WriteVariable("UseLightCache_Experimental", "true", 1); |
| } |
| if (compilerDef.UseRelativePaths) { |
| WriteVariable("UseRelativePaths_Experimental", "true", 1); |
| } |
| if (compilerDef.UseDeterministicPaths) { |
| WriteVariable("UseDeterministicPaths_Experimental", "true", 1); |
| } |
| |
| if (!compilerDef.SourceMapping.empty()) { |
| WriteVariable("SourceMapping_Experimental", |
| Quote(compilerDef.SourceMapping), 1); |
| } |
| |
| auto const isClang = [&compilerDef] { |
| return compilerDef.CompilerFamily == "clang" || |
| compilerDef.CompilerFamily == "clang-cl"; |
| }; |
| |
| if (!compilerDef.ClangRewriteIncludes && isClang()) { |
| WriteVariable("ClangRewriteIncludes", "false", 1); |
| } |
| if (compilerDef.ClangGCCUpdateXLanguageArg && |
| (isClang() || compilerDef.CompilerFamily == "gcc")) { |
| WriteVariable("ClangGCCUpdateXLanguageArg", "true", 1); |
| } |
| |
| if (compilerDef.AllowResponseFile) { |
| WriteVariable("AllowResponseFile", "true", 1); |
| } |
| if (compilerDef.ForceResponseFile) { |
| |
| WriteVariable("ForceResponseFile", "true", 1); |
| } |
| |
| if (compilerDef.DontUseEnv) { |
| LogMessage("Not using system environment"); |
| } else { |
| if (!LocalEnvironment.empty()) { |
| WriteVariable("Environment", "." FASTBUILD_ENV_VAR_NAME, 1); |
| } |
| } |
| if (!compilerDef.ExtraFiles.empty()) { |
| // Do not escape '$' sign, CMAKE_${LANG}_FASTBUILD_EXTRA_FILES might |
| // contain FB variables to be expanded (we do use some internally). |
| // Besides a path cannot contain a '$' |
| WriteArray("ExtraFiles", Wrap(compilerDef.ExtraFiles, "'", "'", false), |
| 1); |
| } |
| *this->BuildFileStream << "}\n"; |
| |
| auto const compilerId = compilerDef.Name; |
| WriteVariable(compilerId, Quote(compilerDef.Name)); |
| *this->BuildFileStream << "\n"; |
| } |
| // We need this because the Library command needs a compiler |
| // even if don't compile anything |
| if (!this->Compilers.empty()) { |
| WriteVariable("Compiler_dummy", |
| Quote(this->Compilers.begin()->second.Name)); |
| } |
| } |
| |
| void cmGlobalFastbuildGenerator::AddCompiler(std::string const& language, |
| cmMakefile* mf) |
| { |
| if (this->Compilers.find(FASTBUILD_COMPILER_PREFIX + language) != |
| this->Compilers.end()) { |
| return; |
| } |
| |
| // Calculate the root location of the compiler |
| std::string const variableString = "CMAKE_" + language + "_COMPILER"; |
| std::string const compilerLocation = mf->GetSafeDefinition(variableString); |
| if (compilerLocation.empty()) { |
| return; |
| } |
| |
| // Calculate the i18n number. |
| std::string i18nNum = "1033"; |
| |
| // Add the language to the compiler's name |
| FastbuildCompiler compilerDef; |
| compilerDef.ExtraVariables["Root"] = |
| cmSystemTools::GetFilenamePath(compilerLocation); |
| compilerDef.Name = FASTBUILD_COMPILER_PREFIX + language; |
| compilerDef.Executable = compilerLocation; |
| compilerDef.CmakeCompilerID = |
| mf->GetSafeDefinition("CMAKE_" + language + "_COMPILER_ID"); |
| if (compilerDef.CmakeCompilerID == "Clang" && |
| mf->GetSafeDefinition("CMAKE_" + language + |
| "_COMPILER_FRONTEND_VARIANT") == "MSVC") { |
| compilerDef.CmakeCompilerID = "Clang-cl"; |
| } |
| |
| compilerDef.CmakeCompilerVersion = |
| mf->GetSafeDefinition("CMAKE_" + language + "_COMPILER_VERSION"); |
| compilerDef.Language = language; |
| |
| cmExpandList(mf->GetSafeDefinition(FASTBUILD_COMPILER_EXTRA_FILES), |
| compilerDef.ExtraFiles); |
| |
| if (supportedLanguages.find(language) != supportedLanguages.end()) { |
| auto const iter = |
| compilerIdToFastbuildFamily.find(compilerDef.CmakeCompilerID); |
| if (iter != compilerIdToFastbuildFamily.end()) { |
| compilerDef.CompilerFamily = iter->second; |
| } |
| } |
| |
| // Has to be called after we determined 'CompilerFamily'. |
| ReadCompilerOptions(compilerDef, mf); |
| |
| // If FASTBUILD_COMPILER_EXTRA_FILES is not set - automatically add extra |
| // files based on compiler (see |
| // https://fastbuild.org/docs/functions/compiler.html) |
| if (compilerDef.ExtraFiles.empty() && |
| (language == "C" || language == "CXX") && |
| compilerDef.CmakeCompilerID == "MSVC") { |
| // https://cmake.org/cmake/help/latest/variable/MSVC_VERSION.html |
| |
| // Visual Studio 17 (19.30 to 19.39) |
| // TODO |
| |
| // Visual Studio 16 (19.20 to 19.29) |
| if (cmSystemTools::VersionCompare(cmSystemTools::OP_GREATER_EQUAL, |
| compilerDef.CmakeCompilerVersion, |
| "19.20")) { |
| compilerDef.ExtraFiles.push_back("$Root$/c1.dll"); |
| compilerDef.ExtraFiles.push_back("$Root$/c1xx.dll"); |
| compilerDef.ExtraFiles.push_back("$Root$/c2.dll"); |
| compilerDef.ExtraFiles.push_back( |
| "$Root$/atlprov.dll"); // Only needed if using ATL |
| compilerDef.ExtraFiles.push_back("$Root$/msobj140.dll"); |
| compilerDef.ExtraFiles.push_back("$Root$/mspdb140.dll"); |
| compilerDef.ExtraFiles.push_back("$Root$/mspdbcore.dll"); |
| compilerDef.ExtraFiles.push_back("$Root$/mspdbsrv.exe"); |
| compilerDef.ExtraFiles.push_back("$Root$/mspft140.dll"); |
| compilerDef.ExtraFiles.push_back("$Root$/msvcp140.dll"); |
| compilerDef.ExtraFiles.push_back( |
| "$Root$/msvcp140_atomic_wait.dll"); // Required circa 16.8.3 |
| // (14.28.29333) |
| compilerDef.ExtraFiles.push_back( |
| "$Root$/tbbmalloc.dll"); // Required as of 16.2 (14.22.27905) |
| compilerDef.ExtraFiles.push_back("$Root$/vcruntime140.dll"); |
| compilerDef.ExtraFiles.push_back( |
| "$Root$/vcruntime140_1.dll"); // Required as of 16.5.1 (14.25.28610) |
| compilerDef.ExtraFiles.push_back("$Root$/" + i18nNum + "/clui.dll"); |
| compilerDef.ExtraFiles.push_back( |
| "$Root$/" + i18nNum + "/mspft140ui.dll"); // Localized messages for |
| // static analysis |
| } |
| // Visual Studio 15 (19.10 to 19.19) |
| else if (cmSystemTools::VersionCompare(cmSystemTools::OP_GREATER_EQUAL, |
| compilerDef.CmakeCompilerVersion, |
| "19.10")) { |
| compilerDef.ExtraFiles.push_back("$Root$/c1.dll"); |
| compilerDef.ExtraFiles.push_back("$Root$/c1xx.dll"); |
| compilerDef.ExtraFiles.push_back("$Root$/c2.dll"); |
| compilerDef.ExtraFiles.push_back( |
| "$Root$/atlprov.dll"); // Only needed if using ATL |
| compilerDef.ExtraFiles.push_back("$Root$/msobj140.dll"); |
| compilerDef.ExtraFiles.push_back("$Root$/mspdb140.dll"); |
| compilerDef.ExtraFiles.push_back("$Root$/mspdbcore.dll"); |
| compilerDef.ExtraFiles.push_back("$Root$/mspdbsrv.exe"); |
| compilerDef.ExtraFiles.push_back("$Root$/mspft140.dll"); |
| compilerDef.ExtraFiles.push_back("$Root$/msvcp140.dll"); |
| compilerDef.ExtraFiles.push_back("$Root$/vcruntime140.dll"); |
| compilerDef.ExtraFiles.push_back("$Root$/" + i18nNum + "/clui.dll"); |
| } |
| } |
| // TODO: Handle Intel compiler |
| |
| this->Compilers[compilerDef.Name] = std::move(compilerDef); |
| } |
| |
| void cmGlobalFastbuildGenerator::AddLauncher(std::string const& prefix, |
| std::string const& launcher, |
| std::string const& language, |
| std::string const& args) |
| { |
| if (this->Compilers.find(prefix + language) != this->Compilers.end()) { |
| return; |
| } |
| LogMessage("Launcher: " + launcher); |
| LogMessage("Launcher args: " + args); |
| FastbuildCompiler compilerDef; |
| compilerDef.Name = prefix + language; |
| compilerDef.Args = args; |
| if (cmSystemTools::FileIsFullPath(launcher)) { |
| compilerDef.Executable = launcher; |
| } else { |
| // FASTBuild needs an absolute path to the executable. |
| compilerDef.Executable = cmSystemTools::FindProgram(launcher); |
| if (compilerDef.Executable.empty()) { |
| cmSystemTools::Error("Failed to find path to " + launcher); |
| return; |
| } |
| } |
| // When CTest is used as a launcher, there is an interesting env variable |
| // "CTEST_LAUNCH_LOGS" which is set by parent CTest process and is expected |
| // to be read from global (sic!) env by the launched CTest process. So we |
| // will need to make this global env available for CTest executable used as a |
| // "launcher". Tested in RunCMake.ctest_labels_for_subprojects test.. |
| compilerDef.DontUseEnv = true; |
| this->Compilers[compilerDef.Name] = std::move(compilerDef); |
| } |
| |
| std::string cmGlobalFastbuildGenerator::ConvertToFastbuildPath( |
| std::string const& path) const |
| { |
| cmLocalGenerator const* root = LocalGenerators[0].get(); |
| return root->MaybeRelativeToWorkDir(cmSystemTools::FileIsFullPath(path) |
| ? cmSystemTools::CollapseFullPath(path) |
| : path); |
| } |
| |
| std::unique_ptr<cmLinkLineComputer> |
| cmGlobalFastbuildGenerator::CreateLinkLineComputer( |
| cmOutputConverter* outputConverter, |
| cmStateDirectory const& /* stateDir */) const |
| { |
| return cm::make_unique<cmFastbuildLinkLineComputer>( |
| outputConverter, |
| this->LocalGenerators[0]->GetStateSnapshot().GetDirectory(), this); |
| } |
| |
| void cmGlobalFastbuildGenerator::WriteExec(FastbuildExecNode const& Exec, |
| int indent) |
| { |
| auto const identPlus1 = indent + 1; |
| WriteCommand("Exec", Exec.Name.empty() ? std::string{} : Quote(Exec.Name), |
| indent); |
| Indent(indent); |
| *BuildFileStream << "{\n"; |
| { |
| if (!Exec.PreBuildDependencies.empty()) { |
| WriteArray("PreBuildDependencies", Wrap(Exec.PreBuildDependencies), |
| identPlus1); |
| } |
| WriteVariable("ExecExecutable", Quote(Exec.ExecExecutable), identPlus1); |
| if (!Exec.ExecArguments.empty()) { |
| WriteVariable("ExecArguments", Quote(Exec.ExecArguments), identPlus1); |
| } |
| if (!Exec.ExecWorkingDir.empty()) { |
| WriteVariable("ExecWorkingDir", Quote(Exec.ExecWorkingDir), identPlus1); |
| } |
| if (!Exec.ExecInput.empty()) { |
| WriteArray("ExecInput", Wrap(Exec.ExecInput), identPlus1); |
| } |
| if (Exec.ExecUseStdOutAsOutput) { |
| WriteVariable("ExecUseStdOutAsOutput", "true", identPlus1); |
| } |
| if (!Exec.ExecInputPath.empty()) { |
| WriteArray("ExecInputPath", Wrap(Exec.ExecInputPath), identPlus1); |
| } |
| if (!Exec.ExecInputPattern.empty()) { |
| WriteArray("ExecInputPattern", Wrap(Exec.ExecInputPattern), identPlus1); |
| } |
| WriteVariable("ExecAlwaysShowOutput", "true", identPlus1); |
| WriteVariable("ExecOutput", Quote(Exec.ExecOutput), identPlus1); |
| WriteVariable("ExecAlways", Exec.ExecAlways ? "true" : "false", |
| identPlus1); |
| if (!Exec.ConcurrencyGroupName.empty()) { |
| WriteVariable("ConcurrencyGroupName", Quote(Exec.ConcurrencyGroupName), |
| identPlus1); |
| } |
| } |
| Indent(indent); |
| *BuildFileStream << "}\n"; |
| static bool const verbose = GlobalSettingIsOn(FASTBUILD_VERBOSE_GENERATOR) || |
| cmSystemTools::HasEnv(FASTBUILD_VERBOSE_GENERATOR); |
| // Those aliases are only used for troubleshooting the generated file. |
| if (verbose) { |
| WriteAlias(Exec.OutputsAlias); |
| WriteAlias(Exec.ByproductsAlias); |
| } |
| } |
| |
| void cmGlobalFastbuildGenerator::WriteUnity(FastbuildUnityNode const& Unity) |
| { |
| WriteCommand("Unity", Quote(Unity.Name), 1); |
| Indent(1); |
| *BuildFileStream << "{\n"; |
| { |
| WriteVariable("UnityOutputPath", Quote(Unity.UnityOutputPath), 2); |
| WriteVariable("UnityOutputPattern", Quote(Unity.UnityOutputPattern), 2); |
| WriteArray("UnityInputFiles", Wrap(Unity.UnityInputFiles), 2); |
| if (!Unity.UnityInputIsolatedFiles.empty()) { |
| WriteArray("UnityInputIsolatedFiles", |
| Wrap(Unity.UnityInputIsolatedFiles), 2); |
| } |
| if (UsingRelativePaths) { |
| WriteVariable("UseRelativePaths_Experimental", "true", 2); |
| } |
| } |
| Indent(1); |
| *BuildFileStream << "}\n"; |
| } |
| |
| void cmGlobalFastbuildGenerator::WriteObjectList( |
| FastbuildObjectListNode const& ObjectList, bool allowDistribution) |
| { |
| WriteCommand("ObjectList", Quote(ObjectList.Name), 1); |
| Indent(1); |
| *BuildFileStream << "{\n"; |
| { |
| if (!allowDistribution) { |
| WriteVariable("AllowDistribution", "false", 2); |
| } |
| if (!ObjectList.PreBuildDependencies.empty()) { |
| WriteArray("PreBuildDependencies", Wrap(ObjectList.PreBuildDependencies), |
| 2); |
| } |
| WriteVariable("Compiler", ObjectList.Compiler, 2); |
| // If only PCH output is present - this node reuses existing PCH. |
| if (!ObjectList.PCHOutputFile.empty()) { |
| WriteVariable("PCHOutputFile", Quote(ObjectList.PCHOutputFile), 2); |
| } |
| // If PCHInputFile and PCHOptions are present - this node creates PCH. |
| if (!ObjectList.PCHInputFile.empty() && !ObjectList.PCHOptions.empty()) { |
| WriteVariable("PCHInputFile", Quote(ObjectList.PCHInputFile), 2); |
| WriteVariable("PCHOptions", Quote(ObjectList.PCHOptions), 2); |
| } |
| WriteVariable("CompilerOptions", Quote(ObjectList.CompilerOptions), 2); |
| WriteVariable("CompilerOutputPath", Quote(ObjectList.CompilerOutputPath), |
| 2); |
| WriteVariable("CompilerOutputExtension", |
| Quote(ObjectList.CompilerOutputExtension), 2); |
| WriteVariable("CompilerOutputKeepBaseExtension", "true", 2); |
| if (!ObjectList.CompilerInputUnity.empty()) { |
| WriteArray("CompilerInputUnity", Wrap(ObjectList.CompilerInputUnity), 2); |
| } |
| if (!ObjectList.CompilerInputFiles.empty()) { |
| WriteArray("CompilerInputFiles", Wrap(ObjectList.CompilerInputFiles), 2); |
| } |
| if (!ObjectList.AllowCaching) { |
| WriteVariable("AllowCaching", "false", 2); |
| } |
| if (!ObjectList.AllowDistribution) { |
| WriteVariable("AllowDistribution", "false", 2); |
| } |
| if (ObjectList.Hidden) { |
| WriteVariable("Hidden", "true", 2); |
| } |
| } |
| Indent(1); |
| *BuildFileStream << "}\n"; |
| } |
| |
| void cmGlobalFastbuildGenerator::WriteLinker( |
| FastbuildLinkerNode const& LinkerNode, bool allowDistribution) |
| { |
| WriteCommand( |
| LinkerNode.Type == FastbuildLinkerNode::EXECUTABLE ? "Executable" |
| : LinkerNode.Type == FastbuildLinkerNode::SHARED_LIBRARY ? "DLL" |
| : "Library", |
| (!LinkerNode.Name.empty() && LinkerNode.Name != LinkerNode.LinkerOutput) |
| ? Quote(LinkerNode.Name) |
| : "", |
| 1); |
| Indent(1); |
| *BuildFileStream << "{\n"; |
| { |
| if (!LinkerNode.PreBuildDependencies.empty()) { |
| WriteArray("PreBuildDependencies", Wrap(LinkerNode.PreBuildDependencies), |
| 2); |
| } |
| if (!allowDistribution) { |
| WriteVariable("AllowDistribution", "false", 2); |
| } |
| |
| if (!LinkerNode.Compiler.empty() && |
| LinkerNode.Type == FastbuildLinkerNode::STATIC_LIBRARY) { |
| WriteVariable("Compiler", LinkerNode.Compiler, 2); |
| WriteVariable("CompilerOptions", Quote(LinkerNode.CompilerOptions), 2); |
| WriteVariable("CompilerOutputPath", Quote("."), 2); |
| } |
| if (!LocalEnvironment.empty()) { |
| WriteVariable("Environment", "." FASTBUILD_ENV_VAR_NAME, 2); |
| } |
| |
| WriteVariable(LinkerNode.Type == FastbuildLinkerNode::STATIC_LIBRARY |
| ? "Librarian" |
| : "Linker", |
| Quote(LinkerNode.Linker), 2); |
| |
| WriteVariable(LinkerNode.Type == FastbuildLinkerNode::STATIC_LIBRARY |
| ? "LibrarianOptions" |
| : "LinkerOptions", |
| Quote(LinkerNode.LinkerOptions), 2); |
| |
| WriteVariable(LinkerNode.Type == FastbuildLinkerNode::STATIC_LIBRARY |
| ? "LibrarianOutput" |
| : "LinkerOutput", |
| Quote(LinkerNode.LinkerOutput), 2); |
| |
| if (!LinkerNode.LibrarianAdditionalInputs.empty()) { |
| WriteArray(LinkerNode.Type == FastbuildLinkerNode::STATIC_LIBRARY |
| ? "LibrarianAdditionalInputs" |
| : "Libraries", |
| Wrap(LinkerNode.LibrarianAdditionalInputs), 2); |
| } |
| if (!LinkerNode.Libraries2.empty()) { |
| WriteArray("Libraries2", Wrap(LinkerNode.Libraries2), 2); |
| } |
| if (!LinkerNode.LibrarianAdditionalInputs.empty()) { |
| |
| if (!LinkerNode.LinkerType.empty()) { |
| WriteVariable("LinkerType", Quote(LinkerNode.LinkerType), 2); |
| } |
| } |
| if (LinkerNode.Type == FastbuildLinkerNode::EXECUTABLE || |
| LinkerNode.Type == FastbuildLinkerNode::SHARED_LIBRARY) { |
| WriteVariable("LinkerLinkObjects", |
| LinkerNode.LinkerLinkObjects ? "true" : "false", 2); |
| |
| if (!LinkerNode.LinkerStampExe.empty()) { |
| WriteVariable("LinkerStampExe", Quote(LinkerNode.LinkerStampExe), 2); |
| if (!LinkerNode.LinkerStampExeArgs.empty()) { |
| WriteVariable("LinkerStampExeArgs", |
| Quote(LinkerNode.LinkerStampExeArgs), 2); |
| } |
| } |
| } |
| Indent(1); |
| *BuildFileStream << "}\n"; |
| } |
| } |
| |
| void cmGlobalFastbuildGenerator::WriteAlias(FastbuildAliasNode const& Alias, |
| int indent) |
| { |
| if (Alias.PreBuildDependencies.empty()) { |
| return; |
| } |
| auto const identPlus1 = indent + 1; |
| WriteCommand("Alias", Quote(Alias.Name), indent); |
| Indent(indent); |
| *BuildFileStream << "{\n"; |
| WriteArray("Targets", Wrap(Alias.PreBuildDependencies), identPlus1); |
| if (Alias.Hidden) { |
| WriteVariable("Hidden", "true", identPlus1); |
| } |
| Indent(indent); |
| *BuildFileStream << "}\n"; |
| } |
| |
| void cmGlobalFastbuildGenerator::WriteCopy(FastbuildCopyNode const& Copy) |
| { |
| cmGlobalFastbuildGenerator::WriteCommand( |
| Copy.CopyDir ? "CopyDir" : "Copy", |
| cmGlobalFastbuildGenerator::Quote(Copy.Name), 1); |
| cmGlobalFastbuildGenerator::Indent(1); |
| |
| *BuildFileStream << "{\n"; |
| WriteVariable("PreBuildDependencies", |
| cmGlobalFastbuildGenerator::Quote(Copy.PreBuildDependencies), |
| 2); |
| WriteVariable(Copy.CopyDir ? "SourcePaths" : "Source", |
| cmGlobalFastbuildGenerator::Quote(Copy.Source), 2); |
| WriteVariable("Dest", cmGlobalFastbuildGenerator::Quote(Copy.Dest), 2); |
| cmGlobalFastbuildGenerator::Indent(1); |
| *BuildFileStream << "}\n"; |
| } |
| |
| void cmGlobalFastbuildGenerator::WriteTarget(FastbuildTarget const& target) |
| { |
| for (auto const& val : target.Variables) { |
| auto const& key = val.first; |
| auto const& value = val.second; |
| WriteVariable(key, cmGlobalFastbuildGenerator::Quote(value), 1); |
| } |
| // add_custom_commands(...) |
| for (auto const& alias : { target.ExecNodes }) { |
| this->WriteAlias(alias); |
| } |
| |
| // -deps Alias. |
| this->WriteAlias(target.DependenciesAlias); |
| |
| // PRE_BUILD. |
| for (auto const& alias : { target.PreBuildExecNodes }) { |
| this->WriteAlias(alias); |
| } |
| |
| // Copy commands. |
| |
| for (FastbuildCopyNode const& node : target.CopyNodes) { |
| this->WriteCopy(node); |
| } |
| |
| // Unity. |
| for (FastbuildUnityNode const& unity : target.UnityNodes) { |
| this->WriteUnity(unity); |
| } |
| |
| // Objects. |
| for (FastbuildObjectListNode const& objectList : target.ObjectListNodes) { |
| this->WriteObjectList(objectList, target.AllowDistribution); |
| } |
| |
| if (!target.PreLinkExecNodes.Nodes.empty()) { |
| for (auto const& exec : target.PreLinkExecNodes.Nodes) { |
| this->WriteExec(exec); |
| } |
| this->WriteAlias(target.PreLinkExecNodes.Alias); |
| } |
| |
| // Libraries / executables. |
| if (!target.LinkerNode.empty()) { |
| for (auto const& linkerNode : target.LinkerNode) { |
| this->WriteLinker(linkerNode, target.AllowDistribution); |
| } |
| } |
| |
| if (!target.PostBuildExecNodes.Nodes.empty()) { |
| for (auto const& exec : target.PostBuildExecNodes.Nodes) { |
| this->WriteExec(exec); |
| } |
| this->WriteAlias(target.PostBuildExecNodes.Alias); |
| } |
| |
| // Aliases (if any). |
| for (FastbuildAliasNode const& alias : target.AliasNodes) { |
| this->WriteAlias(alias); |
| } |
| } |
| void cmGlobalFastbuildGenerator::WriteIDEProjects() |
| { |
| for (auto const& proj : IDEProjects) { |
| (void)proj; |
| // VS |
| #if defined(_WIN32) |
| auto const& VSProj = proj.second.first; |
| WriteCommand("VCXProject", Quote(VSProj.Alias)); |
| *this->BuildFileStream << "{\n"; |
| WriteVariable("ProjectOutput", Quote(VSProj.ProjectOutput), 1); |
| WriteIDEProjectConfig(VSProj.ProjectConfigs); |
| WriteVSBuildCommands(); |
| WriteIDEProjectCommon(VSProj); |
| *this->BuildFileStream << "}\n\n"; |
| |
| // XCode |
| #elif defined(__APPLE__) |
| auto const& XCodeProj = proj.second.second; |
| WriteCommand("XCodeProject", Quote(XCodeProj.Alias), 0); |
| *this->BuildFileStream << "{\n"; |
| WriteVariable("ProjectOutput", Quote(XCodeProj.ProjectOutput), 1); |
| WriteIDEProjectConfig(XCodeProj.ProjectConfigs); |
| WriteXCodeBuildCommands(); |
| WriteIDEProjectCommon(XCodeProj); |
| *this->BuildFileStream << "}\n\n"; |
| #endif |
| } |
| |
| #if defined(_WIN32) |
| this->WriteSolution(); |
| #elif defined(__APPLE__) |
| this->WriteXCodeTopLevelProject(); |
| #endif |
| } |
| |
| void cmGlobalFastbuildGenerator::WriteVSBuildCommands() |
| { |
| WriteVariable("ProjectBuildCommand", |
| Quote(FASTBUILD_IDE_VS_COMMAND_PREFIX + |
| this->FastbuildCommand + |
| FASTBUILD_IDE_BUILD_ARGS " ^$(ProjectName)"), |
| 1); |
| WriteVariable("ProjectRebuildCommand", |
| Quote(FASTBUILD_IDE_VS_COMMAND_PREFIX + |
| this->FastbuildCommand + |
| FASTBUILD_IDE_BUILD_ARGS "-clean ^$(ProjectName)"), |
| 1); |
| WriteVariable("ProjectCleanCommand", |
| Quote(FASTBUILD_IDE_VS_COMMAND_PREFIX + |
| this->FastbuildCommand + " -ide clean"), |
| 1); |
| } |
| void cmGlobalFastbuildGenerator::WriteXCodeBuildCommands() |
| { |
| WriteVariable("XCodeBuildToolPath", Quote(this->FastbuildCommand), 1); |
| WriteVariable("XCodeBuildToolArgs", |
| Quote(FASTBUILD_IDE_BUILD_ARGS "^$(FASTBUILD_TARGET)"), 1); |
| WriteVariable("XCodeBuildWorkingDir", |
| Quote(this->CMakeInstance->GetHomeOutputDirectory()), 1); |
| } |
| |
| void cmGlobalFastbuildGenerator::WriteIDEProjectCommon( |
| IDEProjectCommon const& project) |
| { |
| WriteVariable("ProjectBasePath", Quote(project.ProjectBasePath), 1); |
| // So Fastbuild will pick up files relative to CMakeLists.txt |
| WriteVariable("ProjectInputPaths", Quote(project.ProjectBasePath), 1); |
| } |
| |
| void cmGlobalFastbuildGenerator::WriteIDEProjectConfig( |
| std::vector<IDEProjectConfig> const& configs, std::string const& keyName) |
| { |
| std::vector<std::string> allConfigVariables; |
| for (auto const& config : configs) { |
| std::string configName = "Config" + config.Config; |
| WriteVariable(configName, "", 1); |
| Indent(1); |
| *this->BuildFileStream << "[\n"; |
| WriteVariable("Config", Quote(config.Config), 2); |
| if (!config.Target.empty()) { |
| WriteVariable("Target", Quote(config.Target), 2); |
| } |
| if (!config.Platform.empty()) { |
| WriteVariable("Platform", Quote(config.Platform), 2); |
| } |
| Indent(1); |
| *this->BuildFileStream << "]\n"; |
| allConfigVariables.emplace_back(std::move(configName)); |
| } |
| WriteArray(keyName, Wrap(allConfigVariables, ".", ""), 1); |
| } |
| |
| void cmGlobalFastbuildGenerator::AddTargetAll() |
| { |
| FastbuildAliasNode allAliasNode; |
| allAliasNode.Name = FASTBUILD_ALL_TARGET_NAME; |
| |
| for (auto const& targetBase : FastbuildTargets) { |
| if (targetBase->Type == FastbuildTargetType::LINK) { |
| auto const& target = static_cast<FastbuildTarget const&>(*targetBase); |
| // Add non-global and non-excluded targets to "all" |
| if (!target.IsGlobal && !target.ExcludeFromAll) { |
| allAliasNode.PreBuildDependencies.emplace(target.Name); |
| } |
| } else if (targetBase->Type == FastbuildTargetType::ALIAS) { |
| auto const& target = static_cast<FastbuildAliasNode const&>(*targetBase); |
| if (!target.ExcludeFromAll) { |
| allAliasNode.PreBuildDependencies.emplace(target.Name); |
| } |
| } |
| } |
| if (allAliasNode.PreBuildDependencies.empty()) { |
| allAliasNode.PreBuildDependencies.emplace(FASTBUILD_NOOP_FILE_NAME); |
| } |
| this->AddTarget(std::move(allAliasNode)); |
| } |
| |
| void cmGlobalFastbuildGenerator::AddGlobCheckExec() |
| { |
| // Tested in "RunCMake.file" test. |
| std::string const globScript = |
| this->GetCMakeInstance()->GetGlobVerifyScript(); |
| if (!globScript.empty()) { |
| |
| FastbuildExecNode globCheck; |
| globCheck.Name = FASTBUILD_GLOB_CHECK_TARGET; |
| globCheck.ExecExecutable = cmSystemTools::GetCMakeCommand(); |
| globCheck.ExecArguments = "-P " FASTBUILD_1_INPUT_PLACEHOLDER; |
| globCheck.ExecInput = { this->ConvertToFastbuildPath(globScript) }; |
| globCheck.ExecAlways = false; |
| globCheck.ExecUseStdOutAsOutput = false; |
| auto const cache = this->GetCMakeInstance()->GetGlobCacheEntries(); |
| for (auto const& entry : cache) { |
| auto path = cmSystemTools::GetFilenamePath(entry.Expression); |
| auto expression = cmSystemTools::GetFilenameName(entry.Expression); |
| if (std::find(globCheck.ExecInputPath.begin(), |
| globCheck.ExecInputPath.end(), |
| path) == globCheck.ExecInputPath.end()) { |
| globCheck.ExecInputPath.emplace_back(std::move(path)); |
| } |
| if (std::find(globCheck.ExecInputPattern.begin(), |
| globCheck.ExecInputPattern.end(), |
| expression) == globCheck.ExecInputPattern.end()) { |
| globCheck.ExecInputPattern.emplace_back(std::move(expression)); |
| } |
| } |
| globCheck.ExecOutput = this->ConvertToFastbuildPath( |
| this->GetCMakeInstance()->GetGlobVerifyStamp()); |
| this->AddTarget(std::move(globCheck)); |
| } |
| } |
| void cmGlobalFastbuildGenerator::WriteSolution() |
| { |
| std::string const solutionName = LocalGenerators[0]->GetProjectName(); |
| std::map<std::string /*folder*/, std::vector<std::string>> VSProjects; |
| std::vector<std::string> VSProjectsWithoutFolder; |
| |
| for (auto const& IDEProj : IDEProjects) { |
| auto const VSProj = IDEProj.second.first; |
| VSProjects[VSProj.folder].emplace_back(VSProj.Alias); |
| } |
| |
| WriteCommand("VSSolution", Quote("solution")); |
| *this->BuildFileStream << "{\n"; |
| |
| WriteVariable("SolutionOutput", |
| Quote(cmJoin({ "VisualStudio", solutionName + ".sln" }, "/")), |
| 1); |
| |
| auto const& configs = IDEProjects.begin()->second.first.ProjectConfigs; |
| WriteIDEProjectConfig(configs, "SolutionConfigs"); |
| int folderNumber = 0; |
| std::vector<std::string> folders; |
| for (auto& item : VSProjects) { |
| auto const& pathToFolder = item.first; |
| auto& projectsInFolder = item.second; |
| if (pathToFolder.empty()) { |
| std::move(projectsInFolder.begin(), projectsInFolder.end(), |
| std::back_inserter(VSProjectsWithoutFolder)); |
| } else { |
| std::string folderName = cmStrCat("Folder_", ++folderNumber); |
| WriteStruct( |
| folderName, |
| { { "Path", Quote(pathToFolder) }, |
| { "Projects", |
| cmStrCat("{", cmJoin(Wrap(projectsInFolder), ","), "}") } }, |
| 1); |
| folders.emplace_back(std::move(folderName)); |
| } |
| } |
| if (!folders.empty()) { |
| WriteArray("SolutionFolders ", Wrap(folders, ".", ""), 1); |
| } |
| if (!VSProjectsWithoutFolder.empty()) { |
| WriteArray("SolutionProjects", Wrap(VSProjectsWithoutFolder), 1); |
| } |
| |
| *this->BuildFileStream << "}\n"; |
| } |
| |
| void cmGlobalFastbuildGenerator::WriteXCodeTopLevelProject() |
| { |
| std::string const projectName = LocalGenerators[0]->GetProjectName(); |
| std::vector<std::string> XCodeProjects; |
| for (auto const& IDEProj : IDEProjects) { |
| auto const XCodeProj = IDEProj.second.second; |
| XCodeProjects.emplace_back(XCodeProj.Alias); |
| } |
| |
| WriteCommand("XCodeProject", Quote("xcode")); |
| *this->BuildFileStream << "{\n"; |
| |
| WriteVariable( |
| "ProjectOutput", |
| Quote( |
| cmJoin({ "XCode", projectName + ".xcodeproj", "project.pbxproj" }, "/")), |
| 1); |
| WriteVariable("ProjectBasePath", Quote(FASTBUILD_XCODE_BASE_PATH), 1); |
| |
| auto const& configs = IDEProjects.begin()->second.second.ProjectConfigs; |
| WriteIDEProjectConfig(configs); |
| WriteArray("ProjectFiles", Wrap(XCodeProjects), 1); |
| |
| *this->BuildFileStream << "}\n"; |
| } |
| |
| void cmGlobalFastbuildGenerator::LogMessage(std::string const& m) const |
| { |
| static bool const verbose = GlobalSettingIsOn(FASTBUILD_VERBOSE_GENERATOR) || |
| cmSystemTools::HasEnv(FASTBUILD_VERBOSE_GENERATOR); |
| if (verbose) { |
| cmSystemTools::Message(m); |
| } |
| } |
| void cmGlobalFastbuildGenerator::AddFileToClean(std::string const& file) |
| { |
| AllFilesToClean.insert(file); |
| } |
| |
| std::string cmGlobalFastbuildGenerator::GetExternalShellExecutable() |
| { |
| // FindProgram is expensive - touches filesystem and makes syscalls, so cache |
| // it. |
| static std::string const cached = |
| #ifdef _WIN32 |
| cmSystemTools::FindProgram( |
| "cmd.exe", std::vector<std::string>{ "C:\\Windows\\System32" }); |
| #else |
| cmSystemTools::FindProgram("sh", std::vector<std::string>{ "/bin" }); |
| #endif |
| return cached; |
| } |
| |
| void cmGlobalFastbuildGenerator::WriteTargetRebuildBFF() |
| { |
| std::vector<std::string> implicitDeps; |
| for (auto& lg : LocalGenerators) { |
| std::vector<std::string> const& lf = lg->GetMakefile()->GetListFiles(); |
| for (auto const& dep : lf) { |
| implicitDeps.push_back(this->ConvertToFastbuildPath(dep)); |
| } |
| } |
| auto const* cmake = this->GetCMakeInstance(); |
| std::string outDir = cmake->GetHomeOutputDirectory() + '/'; |
| |
| implicitDeps.push_back(outDir + "CMakeCache.txt"); |
| |
| FastbuildExecNode rebuildBFF; |
| rebuildBFF.Name = FASTBUILD_REBUILD_BFF_TARGET_NAME; |
| if (!this->GetCMakeInstance()->GetGlobVerifyScript().empty()) { |
| implicitDeps.emplace_back(this->GetCMakeInstance()->GetGlobVerifyStamp()); |
| } |
| |
| std::sort(implicitDeps.begin(), implicitDeps.end()); |
| implicitDeps.erase(std::unique(implicitDeps.begin(), implicitDeps.end()), |
| implicitDeps.end()); |
| std::string args = |
| cmStrCat("--regenerate-during-build", |
| (this->GetCMakeInstance()->GetIgnoreCompileWarningAsError() |
| ? " --compile-no-warning-as-error" |
| : ""), |
| (this->GetCMakeInstance()->GetIgnoreLinkWarningAsError() |
| ? " --link-no-warning-as-error" |
| : ""), |
| " -S", QuoteIfHasSpaces(cmake->GetHomeDirectory()), " -B", |
| QuoteIfHasSpaces(cmake->GetHomeOutputDirectory())); |
| |
| rebuildBFF.ExecArguments = std::move(args); |
| rebuildBFF.ExecInput = implicitDeps; |
| rebuildBFF.ExecExecutable = cmSystemTools::GetCMakeCommand(); |
| rebuildBFF.ExecWorkingDir = outDir; |
| rebuildBFF.ExecOutput = outDir + FASTBUILD_BUILD_FILE; |
| this->WriteExec(rebuildBFF, 0); |
| } |
| |
| void cmGlobalFastbuildGenerator::WriteCleanScript() |
| { |
| std::string const path = |
| cmStrCat(this->GetCMakeInstance()->GetHomeOutputDirectory(), '/', |
| FASTBUILD_CLEAN_SCRIPT_NAME); |
| cmsys::ofstream scriptFile(path.c_str(), std::ios::out | std::ios::binary); |
| if (!scriptFile.is_open()) { |
| cmSystemTools::Error("Failed to open: " FASTBUILD_CLEAN_SCRIPT_NAME); |
| return; |
| } |
| for (std::string const& file : AllFilesToClean) { |
| #if defined(_WIN32) |
| scriptFile << "del /f /q " |
| << cmSystemTools::ConvertToWindowsOutputPath(file) << "\n"; |
| #else |
| scriptFile << "rm -f " << file << '\n'; |
| #endif |
| } |
| } |
| |
| void cmGlobalFastbuildGenerator::WriteTargetClean() |
| { |
| if (AllFilesToClean.empty()) { |
| FastbuildAliasNode clean; |
| clean.Name = FASTBUILD_CLEAN_TARGET_NAME; |
| clean.PreBuildDependencies.emplace(FASTBUILD_CLEAN_FILE_NAME); |
| WriteAlias(clean, 0); |
| return; |
| } |
| WriteCleanScript(); |
| FastbuildExecNode clean; |
| clean.Name = FASTBUILD_CLEAN_TARGET_NAME; |
| clean.ExecExecutable = GetExternalShellExecutable(); |
| clean.ExecArguments = |
| FASTBUILD_SCRIPT_FILE_ARG FASTBUILD_1_INPUT_PLACEHOLDER; |
| clean.ExecInput = { FASTBUILD_CLEAN_SCRIPT_NAME }; |
| clean.ExecAlways = true; |
| clean.ExecUseStdOutAsOutput = true; |
| clean.ExecOutput = FASTBUILD_CLEAN_FILE_NAME; |
| clean.ExecWorkingDir = this->GetCMakeInstance()->GetHomeOutputDirectory(); |
| WriteExec(clean, 0); |
| } |
| |
| void cmGlobalFastbuildGenerator::WriteTargets() |
| { |
| std::string const outputDir = this->CMakeInstance->GetHomeOutputDirectory(); |
| LogMessage("GetHomeOutputDirectory: " + outputDir); |
| // Noop file that 'all' can alias to if we don't have any other targets... |
| // The exact location of the "noop" file is verified in one of the tests in |
| // "RunCMake.CMakePresetsPackage" test suite. |
| cmSystemTools::Touch(cmStrCat(this->CMakeInstance->GetHomeOutputDirectory(), |
| '/', FASTBUILD_NOOP_FILE_NAME), |
| true); |
| cmSystemTools::Touch(cmStrCat(this->CMakeInstance->GetHomeOutputDirectory(), |
| '/', FASTBUILD_CLEAN_FILE_NAME), |
| true); |
| // Add "all" utility target before sorting, so we can correctly sort |
| // targets that depend on it |
| AddTargetAll(); |
| TopologicalSort(FastbuildTargets); |
| |
| AddGlobCheckExec(); |
| |
| for (auto const& targetBase : FastbuildTargets) { |
| this->WriteComment("Target definition: " + targetBase->Name); |
| // Target start. |
| *BuildFileStream << "{\n"; |
| |
| if (targetBase->Type == FastbuildTargetType::EXEC) { |
| this->WriteExec(static_cast<FastbuildExecNode const&>(*targetBase)); |
| } else if (targetBase->Type == FastbuildTargetType::ALIAS) { |
| this->WriteAlias(static_cast<FastbuildAliasNode const&>(*targetBase)); |
| } else if (targetBase->Type == FastbuildTargetType::LINK) { |
| auto const& target = static_cast<FastbuildTarget const&>(*targetBase); |
| this->WriteTarget(target); |
| } |
| // Target end. |
| *BuildFileStream << "}\n"; |
| } |
| |
| if (!this->GetCMakeInstance()->GetIsInTryCompile()) { |
| if (!IDEProjects.empty()) { |
| this->WriteIDEProjects(); |
| } |
| } |
| this->WriteTargetClean(); |
| this->WriteTargetRebuildBFF(); |
| } |
| |
| std::string cmGlobalFastbuildGenerator::GetTargetName( |
| cmGeneratorTarget const* GeneratorTarget) const |
| { |
| std::string targetName = |
| GeneratorTarget->GetLocalGenerator()->GetCurrentBinaryDirectory(); |
| targetName += "/"; |
| targetName += GeneratorTarget->GetName(); |
| targetName = this->ConvertToFastbuildPath(targetName); |
| return targetName; |
| } |
| |
| cm::optional<FastbuildTarget> |
| cmGlobalFastbuildGenerator::GetTargetByOutputName( |
| std::string const& output) const |
| { |
| for (auto const& targetBase : FastbuildTargets) { |
| if (targetBase->Type == FastbuildTargetType::LINK) { |
| auto const& target = static_cast<FastbuildTarget const&>(*targetBase); |
| if (std::any_of(target.LinkerNode.begin(), target.LinkerNode.end(), |
| [&output](FastbuildLinkerNode const& target_) { |
| return target_.LinkerOutput == output; |
| })) { |
| return target; |
| } |
| } |
| } |
| return cm::nullopt; |
| } |
| |
| void cmGlobalFastbuildGenerator::AddIDEProject(FastbuildTarget const& target, |
| std::string const& config) |
| { |
| auto const& configs = GetConfigNames(); |
| if (std::find(configs.begin(), configs.end(), config) == configs.end()) { |
| LogMessage("Config " + config + " doesn't exist, IDE projest for " + |
| target.Name + " won't be generated"); |
| return; |
| } |
| auto& IDEProject = IDEProjects[target.BaseName]; |
| auto const relativeSubdir = cmSystemTools::RelativePath( |
| this->GetCMakeInstance()->GetHomeDirectory(), target.BasePath); |
| // VS |
| auto& VSProject = IDEProject.first; |
| VSProject.Alias = target.BaseName + "-vcxproj"; |
| VSProject.ProjectOutput = cmStrCat("VisualStudio/Projects/", relativeSubdir, |
| '/', target.BaseName + ".vcxproj"); |
| VSProject.ProjectBasePath = target.BasePath; |
| VSProject.folder = relativeSubdir; |
| // XCode |
| auto& XCodeProject = IDEProject.second; |
| XCodeProject.Alias = target.BaseName + "-xcodeproj"; |
| XCodeProject.ProjectOutput = |
| cmStrCat("XCode/Projects/", relativeSubdir, '/', |
| target.BaseName + ".xcodeproj/project.pbxproj"); |
| XCodeProject.ProjectBasePath = target.BasePath; |
| |
| IDEProjectConfig VSConfig; |
| VSConfig.Platform = "X64"; |
| IDEProjectConfig XCodeConfig; |
| VSConfig.Target = XCodeConfig.Target = target.Name; |
| VSConfig.Config = XCodeConfig.Config = config.empty() ? "DEFAULT" : config; |
| |
| VSProject.ProjectConfigs.emplace_back(std::move(VSConfig)); |
| XCodeProject.ProjectConfigs.emplace_back(std::move(XCodeConfig)); |
| } |
| |
| bool cmGlobalFastbuildGenerator::IsExcluded(cmGeneratorTarget* target) |
| { |
| return cmGlobalGenerator::IsExcluded(LocalGenerators[0].get(), target); |
| } |
| std::vector<std::string> const& cmGlobalFastbuildGenerator::GetConfigNames() |
| const |
| { |
| return static_cast<cmLocalFastbuildGenerator const*>( |
| this->LocalGenerators.front().get()) |
| ->GetConfigNames(); |
| } |
| |
| bool cmGlobalFastbuildGenerator::Open(std::string const& bindir, |
| std::string const& projectName, |
| bool dryRun) |
| { |
| #ifdef _WIN32 |
| std::string sln = bindir + "/VisualStudio/" + projectName + ".sln"; |
| |
| if (dryRun) { |
| return cmSystemTools::FileExists(sln, true); |
| } |
| |
| sln = cmSystemTools::ConvertToOutputPath(sln); |
| |
| auto OpenSolution = [](std::string pathToSolution) { |
| HRESULT comInitialized = |
| CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); |
| if (FAILED(comInitialized)) { |
| return false; |
| } |
| |
| HINSTANCE hi = ShellExecuteA(NULL, "open", pathToSolution.c_str(), NULL, |
| NULL, SW_SHOWNORMAL); |
| |
| CoUninitialize(); |
| |
| return reinterpret_cast<intptr_t>(hi) > 32; |
| }; |
| |
| return std::async(std::launch::async, OpenSolution, sln).get(); |
| #else |
| return cmGlobalCommonGenerator::Open(bindir, projectName, dryRun); |
| #endif |
| } |