| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
| file LICENSE.rst or https://cmake.org/licensing for details. */ | |
| #include "cmNinjaTargetGenerator.h" | |
| #include <algorithm> | |
| #include <array> | |
| #include <cassert> | |
| #include <functional> | |
| #include <iterator> | |
| #include <map> | |
| #include <ostream> | |
| #include <unordered_map> | |
| #include <unordered_set> | |
| #include <utility> | |
| #include <cm/memory> | |
| #include <cm/optional> | |
| #include <cm/string_view> | |
| #include <cmext/algorithm> | |
| #include <cmext/string_view> | |
| #include <cm3p/json/value.h> | |
| #include <cm3p/json/writer.h> | |
| #include "cmBuildDatabase.h" | |
| #include "cmComputeLinkInformation.h" | |
| #include "cmCustomCommand.h" | |
| #include "cmCustomCommandGenerator.h" | |
| #include "cmDiagnostics.h" | |
| #include "cmDyndepCollation.h" | |
| #include "cmFileSetMetadata.h" | |
| #include "cmGeneratedFileStream.h" | |
| #include "cmGeneratorExpression.h" | |
| #include "cmGeneratorFileSet.h" | |
| #include "cmGeneratorFileSets.h" | |
| #include "cmGeneratorOptions.h" | |
| #include "cmGeneratorTarget.h" | |
| #include "cmGlobalCommonGenerator.h" | |
| #include "cmGlobalNinjaGenerator.h" | |
| #include "cmList.h" | |
| #include "cmListFileCache.h" | |
| #include "cmLocalGenerator.h" | |
| #include "cmLocalNinjaGenerator.h" | |
| #include "cmMakefile.h" | |
| #include "cmMessageType.h" | |
| #include "cmNinjaNormalTargetGenerator.h" | |
| #include "cmNinjaUtilityTargetGenerator.h" | |
| #include "cmOutputConverter.h" | |
| #include "cmPolicies.h" | |
| #include "cmRulePlaceholderExpander.h" | |
| #include "cmSourceFile.h" | |
| #include "cmSourceFileLocationKind.h" | |
| #include "cmState.h" | |
| #include "cmStringAlgorithms.h" | |
| #include "cmSystemTools.h" | |
| #include "cmTarget.h" | |
| #include "cmTargetDepend.h" | |
| #include "cmTargetTypes.h" | |
| #include "cmValue.h" | |
| #include "cmake.h" | |
| std::unique_ptr<cmNinjaTargetGenerator> cmNinjaTargetGenerator::New( | |
| cmGeneratorTarget* target) | |
| { | |
| switch (target->GetType()) { | |
| case cm::TargetType::EXECUTABLE: | |
| case cm::TargetType::SHARED_LIBRARY: | |
| case cm::TargetType::STATIC_LIBRARY: | |
| case cm::TargetType::MODULE_LIBRARY: | |
| case cm::TargetType::OBJECT_LIBRARY: | |
| return cm::make_unique<cmNinjaNormalTargetGenerator>(target); | |
| case cm::TargetType::INTERFACE_LIBRARY: | |
| if (target->HaveCxx20ModuleSources()) { | |
| return cm::make_unique<cmNinjaNormalTargetGenerator>(target); | |
| } | |
| CM_FALLTHROUGH; | |
| case cm::TargetType::UTILITY: | |
| case cm::TargetType::GLOBAL_TARGET: | |
| return cm::make_unique<cmNinjaUtilityTargetGenerator>(target); | |
| default: | |
| return std::unique_ptr<cmNinjaTargetGenerator>(); | |
| } | |
| } | |
| cmNinjaTargetGenerator::cmNinjaTargetGenerator(cmGeneratorTarget* target) | |
| : cmCommonTargetGenerator(target) | |
| , OSXBundleGenerator(nullptr) | |
| , LocalGenerator( | |
| static_cast<cmLocalNinjaGenerator*>(target->GetLocalGenerator())) | |
| { | |
| for (auto const& fileConfig : this->LocalGenerator->GetConfigNames()) { | |
| this->Configs[fileConfig].MacOSXContentGenerator = | |
| cm::make_unique<MacOSXContentGeneratorType>(this, fileConfig); | |
| } | |
| } | |
| cmNinjaTargetGenerator::~cmNinjaTargetGenerator() = default; | |
| cmGeneratedFileStream& cmNinjaTargetGenerator::GetImplFileStream( | |
| std::string const& config) const | |
| { | |
| return *this->GetGlobalGenerator()->GetImplFileStream(config); | |
| } | |
| cmGeneratedFileStream& cmNinjaTargetGenerator::GetCommonFileStream() const | |
| { | |
| return *this->GetGlobalGenerator()->GetCommonFileStream(); | |
| } | |
| cmGeneratedFileStream& cmNinjaTargetGenerator::GetRulesFileStream() const | |
| { | |
| return *this->GetGlobalGenerator()->GetRulesFileStream(); | |
| } | |
| cmGlobalNinjaGenerator* cmNinjaTargetGenerator::GetGlobalGenerator() const | |
| { | |
| return this->LocalGenerator->GetGlobalNinjaGenerator(); | |
| } | |
| std::string cmNinjaTargetGenerator::LanguageCompilerRule( | |
| std::string const& lang, std::string const& config, | |
| WithScanning withScanning) const | |
| { | |
| return cmStrCat( | |
| lang, "_COMPILER__", | |
| cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()), | |
| withScanning == WithScanning::Yes ? "_scanned_" : "_unscanned_", config); | |
| } | |
| std::string cmNinjaTargetGenerator::LanguagePreprocessAndScanRule( | |
| std::string const& lang, std::string const& config) const | |
| { | |
| return cmStrCat( | |
| lang, "_PREPROCESS_SCAN__", | |
| cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()), | |
| '_', config); | |
| } | |
| std::string cmNinjaTargetGenerator::LanguageScanRule( | |
| std::string const& lang, std::string const& config) const | |
| { | |
| return cmStrCat( | |
| lang, "_SCAN__", | |
| cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()), | |
| '_', config); | |
| } | |
| bool cmNinjaTargetGenerator::NeedExplicitPreprocessing( | |
| std::string const& lang) const | |
| { | |
| return lang == "Fortran" || lang == "Swift"; | |
| } | |
| bool cmNinjaTargetGenerator::CompileWithDefines(std::string const& lang) const | |
| { | |
| return this->Makefile->IsOn( | |
| cmStrCat("CMAKE_", lang, "_COMPILE_WITH_DEFINES")); | |
| } | |
| std::string cmNinjaTargetGenerator::LanguageDyndepRule( | |
| std::string const& lang, std::string const& config) const | |
| { | |
| return cmStrCat( | |
| lang, "_DYNDEP__", | |
| cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()), | |
| '_', config); | |
| } | |
| std::string cmNinjaTargetGenerator::OrderDependsTargetForTarget( | |
| std::string const& config) | |
| { | |
| return this->GetGlobalGenerator()->OrderDependsTargetForTarget( | |
| this->GeneratorTarget, config); | |
| } | |
| std::string cmNinjaTargetGenerator::OrderDependsTargetForTargetPrivate( | |
| std::string const& config) | |
| { | |
| return this->GetGlobalGenerator()->OrderDependsTargetForTargetPrivate( | |
| this->GeneratorTarget, config); | |
| } | |
| // TODO: Most of the code is picked up from | |
| // void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink), | |
| // void cmMakefileTargetGenerator::WriteTargetLanguageFlags() | |
| // Refactor it. | |
| std::string cmNinjaTargetGenerator::ComputeFlagsForObject( | |
| cmSourceFile const* source, std::string const& language, | |
| std::string const& config, std::string const& objectFileName) | |
| { | |
| std::unordered_map<std::string, std::string> pchSources; | |
| std::vector<std::string> pchArchs = | |
| this->GeneratorTarget->GetPchArchs(config, language); | |
| std::string filterArch; | |
| for (std::string const& arch : pchArchs) { | |
| std::string const pchSource = | |
| this->GeneratorTarget->GetPchSource(config, language, arch); | |
| if (pchSource == source->GetFullPath()) { | |
| filterArch = arch; | |
| } | |
| if (!pchSource.empty()) { | |
| pchSources.insert(std::make_pair(pchSource, arch)); | |
| } | |
| } | |
| std::string flags; | |
| // Explicitly add the explicit language flag before any other flag | |
| // so user flags can override it. | |
| this->GeneratorTarget->AddExplicitLanguageFlags(flags, *source, config); | |
| if (!flags.empty()) { | |
| flags += " "; | |
| } | |
| flags += this->GetFlags(language, config, filterArch); | |
| // Add Fortran format flags. | |
| if (language == "Fortran") { | |
| this->AppendFortranFormatFlags(flags, *source); | |
| this->AppendFortranPreprocessFlags(flags, *source, | |
| PreprocessFlagsRequired::NO); | |
| } | |
| // Add source file specific flags. | |
| cmGeneratorExpressionInterpreter genexInterpreter( | |
| this->LocalGenerator, config, this->GeneratorTarget, language); | |
| std::string const COMPILE_FLAGS("COMPILE_FLAGS"); | |
| if (cmValue cflags = source->GetProperty(COMPILE_FLAGS)) { | |
| this->LocalGenerator->AppendFlags( | |
| flags, genexInterpreter.Evaluate(*cflags, COMPILE_FLAGS)); | |
| } | |
| std::string const COMPILE_OPTIONS("COMPILE_OPTIONS"); | |
| if (cmValue coptions = source->GetProperty(COMPILE_OPTIONS)) { | |
| this->LocalGenerator->AppendCompileOptions( | |
| flags, genexInterpreter.Evaluate(*coptions, COMPILE_OPTIONS)); | |
| } | |
| auto const* const fileSet = | |
| this->GeneratorTarget->GetGeneratorFileSets()->GetFileSetForSource(config, | |
| source); | |
| if (fileSet) { | |
| auto options = fileSet->BelongsTo(this->GeneratorTarget) | |
| ? fileSet->GetCompileOptions(config, language) | |
| : fileSet->GetInterfaceCompileOptions(config, language); | |
| if (!options.empty()) { | |
| this->LocalGenerator->AppendCompileOptions(flags, | |
| cm::remove_BT(options)); | |
| } | |
| } | |
| // Add precompile headers compile options. | |
| if (!pchSources.empty() && | |
| !((fileSet && fileSet->GetProperty("SKIP_PRECOMPILE_HEADERS")) || | |
| source->GetProperty("SKIP_PRECOMPILE_HEADERS"))) { | |
| std::string pchOptions; | |
| auto pchIt = pchSources.find(source->GetFullPath()); | |
| if (pchIt != pchSources.end()) { | |
| pchOptions = this->GeneratorTarget->GetPchCreateCompileOptions( | |
| config, language, pchIt->second); | |
| } else { | |
| pchOptions = | |
| this->GeneratorTarget->GetPchUseCompileOptions(config, language); | |
| } | |
| this->LocalGenerator->AppendCompileOptions( | |
| flags, genexInterpreter.Evaluate(pchOptions, COMPILE_OPTIONS)); | |
| } | |
| if (fileSet && fileSet->GetType() == cm::FileSetMetadata::CXX_MODULES) { | |
| if (source->GetLanguage() != "CXX"_s) { | |
| this->GetMakefile()->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat("Target \"", this->GeneratorTarget->Target->GetName(), | |
| "\" contains the source\n ", source->GetFullPath(), | |
| "\nin a file set of type \"", fileSet->GetType(), | |
| R"(" but the source is not classified as a "CXX" source.)")); | |
| } | |
| if (!this->GeneratorTarget->Target->IsNormal() && | |
| !this->GeneratorTarget->Target->CxxModuleNeedsInterfaceObjects()) { | |
| if (this->GetMakefile() | |
| ->GetDefinition("CMAKE_CXX_COMPILE_BMI") | |
| .IsEmpty()) { | |
| auto flag = this->GetMakefile()->GetSafeDefinition( | |
| "CMAKE_CXX_MODULE_BMI_ONLY_FLAG"); | |
| cmRulePlaceholderExpander::RuleVariables compileObjectVars; | |
| compileObjectVars.Object = objectFileName.c_str(); | |
| auto rulePlaceholderExpander = | |
| this->GetLocalGenerator()->CreateRulePlaceholderExpander(); | |
| rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(), | |
| flag, compileObjectVars); | |
| this->LocalGenerator->AppendCompileOptions(flags, flag); | |
| } | |
| } | |
| } | |
| return flags; | |
| } | |
| void cmNinjaTargetGenerator::AddIncludeFlags(std::string& languageFlags, | |
| std::string const& language, | |
| std::string const& config) | |
| { | |
| std::vector<std::string> includes; | |
| this->LocalGenerator->GetIncludeDirectories(includes, this->GeneratorTarget, | |
| language, config); | |
| // Add include directory flags. | |
| std::string includeFlags = this->LocalGenerator->GetIncludeFlags( | |
| includes, this->GeneratorTarget, language, config, false); | |
| if (this->GetGlobalGenerator()->IsGCCOnWindows()) { | |
| std::replace(includeFlags.begin(), includeFlags.end(), '\\', '/'); | |
| } | |
| this->LocalGenerator->AppendFlags(languageFlags, includeFlags); | |
| } | |
| // TODO: Refactor with | |
| // void cmMakefileTargetGenerator::WriteTargetLanguageFlags(). | |
| std::string cmNinjaTargetGenerator::ComputeDefines(cmSourceFile const* source, | |
| std::string const& language, | |
| std::string const& config) | |
| { | |
| std::set<std::string> defines; | |
| cmGeneratorExpressionInterpreter genexInterpreter( | |
| this->LocalGenerator, config, this->GeneratorTarget, language); | |
| // Seriously?? | |
| if (this->GetGlobalGenerator()->IsMultiConfig()) { | |
| defines.insert(cmStrCat("CMAKE_INTDIR=\"", config, '"')); | |
| } | |
| std::string const COMPILE_DEFINITIONS("COMPILE_DEFINITIONS"); | |
| if (cmValue compile_defs = source->GetProperty(COMPILE_DEFINITIONS)) { | |
| this->LocalGenerator->AppendDefines( | |
| defines, genexInterpreter.Evaluate(*compile_defs, COMPILE_DEFINITIONS)); | |
| } | |
| std::string defPropName = | |
| cmStrCat("COMPILE_DEFINITIONS_", cmSystemTools::UpperCase(config)); | |
| if (cmValue config_compile_defs = source->GetProperty(defPropName)) { | |
| this->LocalGenerator->AppendDefines( | |
| defines, | |
| genexInterpreter.Evaluate(*config_compile_defs, COMPILE_DEFINITIONS)); | |
| } | |
| if (auto const* fileSet = | |
| this->GeneratorTarget->GetGeneratorFileSets()->GetFileSetForSource( | |
| config, source)) { | |
| auto fsDefines = fileSet->BelongsTo(this->GeneratorTarget) | |
| ? fileSet->GetCompileDefinitions(config, language) | |
| : fileSet->GetInterfaceCompileDefinitions(config, language); | |
| if (!fsDefines.empty()) { | |
| this->LocalGenerator->AppendDefines(defines, fsDefines); | |
| } | |
| } | |
| std::string definesString = this->GetDefines(language, config); | |
| this->LocalGenerator->JoinDefines(defines, definesString, language); | |
| return definesString; | |
| } | |
| std::string cmNinjaTargetGenerator::ComputeIncludes( | |
| cmSourceFile const* source, std::string const& language, | |
| std::string const& config) | |
| { | |
| std::vector<std::string> includes; | |
| if (auto const* fileSet = | |
| this->GeneratorTarget->GetGeneratorFileSets()->GetFileSetForSource( | |
| config, source)) { | |
| auto fsIncludes = fileSet->BelongsTo(this->GeneratorTarget) | |
| ? fileSet->GetIncludeDirectories(config, language) | |
| : fileSet->GetInterfaceIncludeDirectories(config, language); | |
| if (!fsIncludes.empty()) { | |
| this->LocalGenerator->AppendIncludeDirectories( | |
| includes, cm::remove_BT(fsIncludes), *source); | |
| } | |
| } | |
| cmGeneratorExpressionInterpreter genexInterpreter( | |
| this->LocalGenerator, config, this->GeneratorTarget, language); | |
| std::string const INCLUDE_DIRECTORIES("INCLUDE_DIRECTORIES"); | |
| if (cmValue cincludes = source->GetProperty(INCLUDE_DIRECTORIES)) { | |
| this->LocalGenerator->AppendIncludeDirectories( | |
| includes, genexInterpreter.Evaluate(*cincludes, INCLUDE_DIRECTORIES), | |
| *source); | |
| } | |
| std::string includesString = this->LocalGenerator->GetIncludeFlags( | |
| includes, this->GeneratorTarget, language, config, false); | |
| this->LocalGenerator->AppendFlags(includesString, | |
| this->GetIncludes(language, config)); | |
| return includesString; | |
| } | |
| cmNinjaDeps cmNinjaTargetGenerator::ComputeLinkDeps( | |
| std::string const& linkLanguage, std::string const& config, | |
| bool ignoreType) const | |
| { | |
| // Static libraries never depend on other targets for linking. | |
| if (!ignoreType && | |
| (this->GeneratorTarget->GetType() == cm::TargetType::STATIC_LIBRARY || | |
| this->GeneratorTarget->GetType() == cm::TargetType::OBJECT_LIBRARY)) { | |
| return cmNinjaDeps(); | |
| } | |
| cmComputeLinkInformation* cli = | |
| this->GeneratorTarget->GetLinkInformation(config); | |
| if (!cli) { | |
| return cmNinjaDeps(); | |
| } | |
| std::vector<std::string> const& deps = cli->GetDepends(); | |
| cmNinjaDeps result(deps.size()); | |
| std::transform(deps.begin(), deps.end(), result.begin(), | |
| this->MapToNinjaPath()); | |
| // Add a dependency on the link definitions file, if any. | |
| if (cmGeneratorTarget::ModuleDefinitionInfo const* mdi = | |
| this->GeneratorTarget->GetModuleDefinitionInfo(config)) { | |
| for (cmSourceFile const* src : mdi->Sources) { | |
| result.push_back(this->ConvertToNinjaPath(src->GetFullPath())); | |
| } | |
| } | |
| // Add a dependency on user-specified manifest files, if any. | |
| std::vector<cmSourceFile const*> manifest_srcs; | |
| this->GeneratorTarget->GetManifests(manifest_srcs, config); | |
| for (cmSourceFile const* manifest_src : manifest_srcs) { | |
| result.push_back(this->ConvertToNinjaPath(manifest_src->GetFullPath())); | |
| } | |
| // Add user-specified dependencies. | |
| std::vector<std::string> linkDeps; | |
| this->GeneratorTarget->GetLinkDepends(linkDeps, config, linkLanguage); | |
| std::transform(linkDeps.begin(), linkDeps.end(), std::back_inserter(result), | |
| this->MapToNinjaPath()); | |
| return result; | |
| } | |
| std::string cmNinjaTargetGenerator::GetCompiledSourceNinjaPath( | |
| cmSourceFile const* source) const | |
| { | |
| // Pass source files to the compiler by absolute path. | |
| return this->ConvertToNinjaAbsPath(source->GetFullPath()); | |
| } | |
| std::string cmNinjaTargetGenerator::GetObjectFileDir( | |
| std::string const& config) const | |
| { | |
| return this->LocalGenerator->MaybeRelativeToTopBinDir( | |
| cmStrCat(this->GeneratorTarget->GetSupportDirectory(), | |
| this->GetGlobalGenerator()->GetConfigDirectory(config))); | |
| } | |
| std::string cmNinjaTargetGenerator::GetObjectFilePath( | |
| cmSourceFile const* source, std::string const& config) const | |
| { | |
| std::string const& objectName = this->GeneratorTarget->GetObjectName(source); | |
| return cmStrCat(this->GetObjectFileDir(config), '/', objectName); | |
| } | |
| std::string cmNinjaTargetGenerator::GetBmiFilePath( | |
| cmSourceFile const* source, std::string const& config) const | |
| { | |
| auto& importedConfigInfo = this->Configs.at(config).ImportedCxxModules; | |
| if (!importedConfigInfo.Initialized()) { | |
| std::string configUpper = cmSystemTools::UpperCase(config); | |
| std::string propName = cmStrCat("IMPORTED_CXX_MODULES_", configUpper); | |
| auto value = this->GeneratorTarget->GetSafeProperty(propName); | |
| importedConfigInfo.Initialize(value); | |
| } | |
| std::string bmiName = | |
| importedConfigInfo.BmiNameForSource(source->GetFullPath()); | |
| return cmStrCat(this->GetObjectFileDir(config), '/', bmiName); | |
| } | |
| std::string cmNinjaTargetGenerator::GetClangTidyReplacementsFilePath( | |
| std::string const& directory, cmSourceFile const& source, | |
| std::string const& config) const | |
| { | |
| auto const& objectName = this->GeneratorTarget->GetObjectName(&source); | |
| return cmStrCat(directory, '/', | |
| this->LocalGenerator->CreateSafeObjectFileName( | |
| this->GetObjectFileDir(config)), | |
| '/', objectName, ".yaml"); | |
| } | |
| std::string cmNinjaTargetGenerator::GetPreprocessedFilePath( | |
| cmSourceFile const* source, std::string const& config) const | |
| { | |
| // Choose an extension to compile already-preprocessed source. | |
| std::string ppExt = source->GetExtension(); | |
| if (cmHasPrefix(ppExt, 'F')) { | |
| // Some Fortran compilers automatically enable preprocessing for | |
| // upper-case extensions. Since the source is already preprocessed, | |
| // use a lower-case extension. | |
| ppExt = cmSystemTools::LowerCase(ppExt); | |
| } | |
| if (ppExt == "fpp") { | |
| // Some Fortran compilers automatically enable preprocessing for | |
| // the ".fpp" extension. Since the source is already preprocessed, | |
| // use the ".f" extension. | |
| ppExt = "f"; | |
| } | |
| // Take the object file name and replace the extension. | |
| std::string const& objName = this->GeneratorTarget->GetObjectName(source); | |
| std::string const& objExt = | |
| this->GetGlobalGenerator()->GetLanguageOutputExtension(*source); | |
| assert(objName.size() >= objExt.size()); | |
| std::string const ppName = | |
| cmStrCat(objName.substr(0, objName.size() - objExt.size()), "-pp.", ppExt); | |
| return cmStrCat(this->GetObjectFileDir(config), '/', ppName); | |
| } | |
| std::string cmNinjaTargetGenerator::GetDyndepFilePath( | |
| std::string const& lang, std::string const& config) const | |
| { | |
| return cmStrCat(this->GetObjectFileDir(config), '/', lang, ".dd"); | |
| } | |
| bool cmNinjaTargetGenerator::HasCxxModuleDyndep( | |
| std::string const& config) const | |
| { | |
| auto const it = this->Configs.find(config); | |
| return it != this->Configs.end() && | |
| it->second.ScanningInfo.count("CXX") != 0; | |
| } | |
| std::string cmNinjaTargetGenerator::GetTargetDependInfoPath( | |
| std::string const& lang, std::string const& config) const | |
| { | |
| return cmStrCat(this->GeneratorTarget->GetSupportDirectory(), | |
| this->GetGlobalGenerator()->ConfigDirectory(config), '/', | |
| lang, "DependInfo.json"); | |
| } | |
| std::string cmNinjaTargetGenerator::GetTargetOutputDir( | |
| std::string const& config) const | |
| { | |
| std::string dir = this->GeneratorTarget->GetDirectory(config); | |
| return this->ConvertToNinjaPath(dir); | |
| } | |
| std::string cmNinjaTargetGenerator::GetTargetFilePath( | |
| std::string const& name, std::string const& config) const | |
| { | |
| std::string path = this->GetTargetOutputDir(config); | |
| if (path.empty() || path == ".") { | |
| return name; | |
| } | |
| path = cmStrCat(std::move(path), '/', name); | |
| return path; | |
| } | |
| std::string cmNinjaTargetGenerator::GetTargetName() const | |
| { | |
| return this->GeneratorTarget->GetName(); | |
| } | |
| std::string cmNinjaTargetGenerator::ConvertToOutputFormatForShell( | |
| cm::string_view path) const | |
| { | |
| return this->LocalGenerator->ConvertToOutputFormat(path, | |
| cmOutputConverter::SHELL); | |
| } | |
| bool cmNinjaTargetGenerator::SetMsvcTargetPdbVariable( | |
| cmNinjaVars& vars, std::string const& config) const | |
| { | |
| cmMakefile* mf = this->GetMakefile(); | |
| bool supportsPDB = mf->GetDefinition("MSVC_C_ARCHITECTURE_ID") || | |
| mf->GetDefinition("MSVC_CXX_ARCHITECTURE_ID") || | |
| mf->GetDefinition("MSVC_CUDA_ARCHITECTURE_ID"); | |
| if (!supportsPDB) { | |
| std::string const linkLanguage = | |
| this->GeneratorTarget->GetLinkerLanguage(config); | |
| supportsPDB = | |
| mf->IsOn(cmStrCat("CMAKE_", linkLanguage, "_LINKER_SUPPORTS_PDB")); | |
| } | |
| if (supportsPDB) { | |
| std::string pdbPath; | |
| std::string compilePdbPath = this->ComputeTargetCompilePDB(config); | |
| if (this->GeneratorTarget->GetType() == cm::TargetType::EXECUTABLE || | |
| this->GeneratorTarget->GetType() == cm::TargetType::STATIC_LIBRARY || | |
| this->GeneratorTarget->GetType() == cm::TargetType::SHARED_LIBRARY || | |
| this->GeneratorTarget->GetType() == cm::TargetType::MODULE_LIBRARY) { | |
| pdbPath = cmStrCat(this->GeneratorTarget->GetPDBDirectory(config), '/', | |
| this->GeneratorTarget->GetPDBName(config)); | |
| } | |
| vars["TARGET_PDB"] = | |
| this->ConvertToOutputFormatForShell(this->ConvertToNinjaPath(pdbPath)); | |
| vars["TARGET_COMPILE_PDB"] = this->ConvertToOutputFormatForShell( | |
| this->ConvertToNinjaPath(compilePdbPath)); | |
| this->EnsureParentDirectoryExists(pdbPath); | |
| this->EnsureParentDirectoryExists(compilePdbPath); | |
| return true; | |
| } | |
| return false; | |
| } | |
| void cmNinjaTargetGenerator::WriteLanguageRules(std::string const& language, | |
| std::string const& config) | |
| { | |
| #ifdef NINJA_GEN_VERBOSE_FILES | |
| this->GetRulesFileStream() << "# Rules for language " << language << "\n\n"; | |
| #endif | |
| this->WriteCompileRule(language, config); | |
| } | |
| namespace { | |
| // Create the command to run the dependency scanner | |
| std::string GetScanCommand( | |
| cm::string_view cmakeCmd, cm::string_view tdi, cm::string_view lang, | |
| cm::string_view srcFile, cm::string_view ddiFile, | |
| cm::optional<cm::string_view> srcOrigFile = cm::nullopt) | |
| { | |
| return cmStrCat(cmakeCmd, " -E cmake_ninja_depends --tdi=", tdi, | |
| " --lang=", lang, " --src=", srcFile, | |
| " --out=$out" | |
| " --dep=$DEP_FILE --obj=$OBJ_FILE --ddi=", | |
| ddiFile, | |
| srcOrigFile ? cmStrCat(" --src-orig=", *srcOrigFile) : ""); | |
| } | |
| // Helper function to create dependency scanning rule that may or may | |
| // not perform explicit preprocessing too. | |
| cmNinjaRule GetScanRule( | |
| std::string const& ruleName, std::string const& ppFileName, | |
| std::string const& deptype, | |
| cmRulePlaceholderExpander::RuleVariables const& vars, | |
| std::string const& responseFlag, std::string const& flags, | |
| cmRulePlaceholderExpander* const rulePlaceholderExpander, | |
| cmLocalNinjaGenerator* generator, std::vector<std::string> scanCmds, | |
| std::string const& outputConfig) | |
| { | |
| cmNinjaRule rule(ruleName); | |
| // Scanning always uses a depfile for preprocessor dependencies. | |
| if (deptype == "msvc"_s) { | |
| rule.DepType = deptype; | |
| rule.DepFile.clear(); | |
| } else { | |
| rule.DepType.clear(); // no deps= for multiple outputs | |
| rule.DepFile = "$DEP_FILE"; | |
| } | |
| cmRulePlaceholderExpander::RuleVariables scanVars; | |
| scanVars.CMTargetName = vars.CMTargetName; | |
| scanVars.CMTargetType = vars.CMTargetType; | |
| scanVars.Language = vars.Language; | |
| scanVars.Object = "$OBJ_FILE"; | |
| scanVars.PreprocessedSource = ppFileName.c_str(); | |
| scanVars.DynDepFile = "$DYNDEP_INTERMEDIATE_FILE"; | |
| scanVars.DependencyFile = rule.DepFile.c_str(); | |
| scanVars.DependencyTarget = "$out"; | |
| scanVars.Config = vars.Config; | |
| // Scanning needs the same preprocessor settings as direct compilation would. | |
| scanVars.Source = vars.Source; | |
| scanVars.Defines = vars.Defines; | |
| scanVars.Includes = vars.Includes; | |
| // Scanning needs the compilation flags too. | |
| std::string scanFlags = flags; | |
| // If using a response file, move defines, includes, and flags into it. | |
| if (!responseFlag.empty()) { | |
| rule.RspFile = "$RSP_FILE"; | |
| rule.RspContent = | |
| cmStrCat(' ', scanVars.Defines, ' ', scanVars.Includes, ' ', scanFlags); | |
| scanFlags = cmStrCat(responseFlag, rule.RspFile); | |
| scanVars.Defines = ""; | |
| scanVars.Includes = ""; | |
| } | |
| scanVars.Flags = scanFlags.c_str(); | |
| // Rule for scanning a source file. | |
| for (std::string& scanCmd : scanCmds) { | |
| rulePlaceholderExpander->ExpandRuleVariables(generator, scanCmd, scanVars); | |
| } | |
| rule.Command = | |
| generator->BuildCommandLine(scanCmds, outputConfig, outputConfig); | |
| return rule; | |
| } | |
| void SetupResponseFile(cmNinjaRule& rule, | |
| cmRulePlaceholderExpander::RuleVariables& vars, | |
| std::string& flags, std::string const& lang, | |
| std::string const& responseFlag) | |
| { | |
| rule.RspFile = "$RSP_FILE"; | |
| rule.RspContent = | |
| cmStrCat(' ', vars.Defines, ' ', vars.Includes, ' ', flags); | |
| flags = cmStrCat(responseFlag, rule.RspFile); | |
| vars.Defines = ""; | |
| vars.Includes = ""; | |
| // Swift consumes all source files in a module at once, which reaches | |
| // command line length limits pretty quickly. Inject source files into the | |
| // response file in this case as well. | |
| if (lang == "Swift") { | |
| rule.RspContent = cmStrCat(rule.RspContent, ' ', vars.Source); | |
| vars.Source = ""; | |
| } | |
| } | |
| cmList ExpandRuleCommands(std::string const& command, | |
| cmRulePlaceholderExpander::RuleVariables const& vars, | |
| cmMakefile const* mf, std::string const& lang, | |
| std::string const& launcher, | |
| std::string const& cldeps, | |
| cmLocalGenerator* localGenerator, | |
| cmRulePlaceholderExpander* rulePlaceholderExpander) | |
| { | |
| std::string const extraCommands = | |
| mf->GetSafeDefinition(cmStrCat("CMAKE_", lang, "_DEPENDS_EXTRA_COMMANDS")); | |
| cmList commands(command); | |
| if (!commands.empty()) { | |
| commands.front().insert(0, "${CODE_CHECK}"); | |
| commands.front().insert(0, "${LAUNCHER}"); | |
| } | |
| if (!commands.empty()) { | |
| commands.front().insert(0, cldeps); | |
| } | |
| if (!extraCommands.empty()) { | |
| commands.append(extraCommands); | |
| } | |
| for (std::string& cmd : commands) { | |
| cmd = cmStrCat(launcher, std::move(cmd)); | |
| rulePlaceholderExpander->ExpandRuleVariables(localGenerator, cmd, vars); | |
| } | |
| return commands; | |
| } | |
| } | |
| void cmNinjaTargetGenerator::WriteCompileRule(std::string const& lang, | |
| std::string const& config) | |
| { | |
| // For some cases we scan to dynamically discover dependencies. | |
| bool const needDyndep = this->GetGeneratorTarget()->NeedDyndep(lang, config); | |
| if (needDyndep) { | |
| this->WriteCompileRule(lang, config, WithScanning::Yes); | |
| } | |
| this->WriteCompileRule(lang, config, WithScanning::No); | |
| } | |
| std::string cmNinjaTargetGenerator::GetCompileTemplateVar( | |
| std::string const& lang) const | |
| { | |
| std::string cmdVar = cmStrCat("CMAKE_", lang, "_COMPILE_OBJECT"); | |
| if (!this->GetGeneratorTarget()->IsNormal()) { | |
| std::string bmiCmdVar = cmStrCat("CMAKE_", lang, "_COMPILE_BMI"); | |
| if (!this->GetMakefile()->GetDefinition(bmiCmdVar).IsEmpty()) { | |
| cmdVar = std::move(bmiCmdVar); | |
| } | |
| } | |
| return cmdVar; | |
| } | |
| void cmNinjaTargetGenerator::WriteCompileRule(std::string const& lang, | |
| std::string const& config, | |
| WithScanning withScanning) | |
| { | |
| cmRulePlaceholderExpander::RuleVariables vars; | |
| vars.CMTargetName = this->GetGeneratorTarget()->GetName().c_str(); | |
| vars.CMTargetType = | |
| cmState::GetTargetTypeName(this->GetGeneratorTarget()->GetType()).c_str(); | |
| vars.Language = lang.c_str(); | |
| vars.Source = "$in"; | |
| vars.Object = "$out"; | |
| vars.Defines = "$DEFINES"; | |
| vars.Includes = "$INCLUDES"; | |
| vars.TargetPDB = "$TARGET_PDB"; | |
| vars.TargetCompilePDB = "$TARGET_COMPILE_PDB"; | |
| vars.ObjectDir = "$OBJECT_DIR"; | |
| vars.TargetSupportDir = "$TARGET_SUPPORT_DIR"; | |
| vars.ObjectFileDir = "$OBJECT_FILE_DIR"; | |
| vars.CudaCompileMode = "$CUDA_COMPILE_MODE"; | |
| vars.ISPCHeader = "$ISPC_HEADER_FILE"; | |
| vars.Config = "$CONFIG"; | |
| vars.RustEmit = "$RUST_EMIT"; | |
| cmMakefile* mf = this->GetMakefile(); | |
| // For some cases we scan to dynamically discover dependencies. | |
| bool const compilationPreprocesses = !this->NeedExplicitPreprocessing(lang); | |
| std::string flags = "$FLAGS"; | |
| std::string responseFlag; | |
| bool const lang_supports_response = lang != "RC"; | |
| if (lang_supports_response && this->ForceResponseFile()) { | |
| std::string const responseFlagVar = | |
| cmStrCat("CMAKE_", lang, "_RESPONSE_FILE_FLAG"); | |
| responseFlag = this->Makefile->GetSafeDefinition(responseFlagVar); | |
| if (responseFlag.empty() && lang != "CUDA") { | |
| responseFlag = "@"; | |
| } | |
| } | |
| std::string const modmapFormatVar = | |
| cmStrCat("CMAKE_", lang, "_MODULE_MAP_FORMAT"); | |
| std::string const modmapFormat = | |
| this->Makefile->GetSafeDefinition(modmapFormatVar); | |
| auto rulePlaceholderExpander = | |
| this->GetLocalGenerator()->CreateRulePlaceholderExpander(); | |
| std::string const tdi = this->ConvertToOutputFormatForShell( | |
| this->ConvertToNinjaPath(this->GetTargetDependInfoPath(lang, config))); | |
| std::string launcher; | |
| std::string val = this->GetLocalGenerator()->GetRuleLauncher( | |
| this->GetGeneratorTarget(), "RULE_LAUNCH_COMPILE", config); | |
| if (cmNonempty(val)) { | |
| launcher = cmStrCat(val, ' '); | |
| } | |
| std::string const cmakeCmd = | |
| this->ConvertToOutputFormatForShell(cmSystemTools::GetCMakeCommand()); | |
| if (withScanning == WithScanning::Yes) { | |
| auto const& scanDepType = this->GetMakefile()->GetSafeDefinition( | |
| cmStrCat("CMAKE_", lang, "_SCANDEP_DEPFILE_FORMAT")); | |
| // Rule to scan dependencies of sources that need preprocessing. | |
| { | |
| cmList scanCommands; | |
| std::string scanRuleName; | |
| std::string ppFileName; | |
| if (compilationPreprocesses) { | |
| scanRuleName = this->LanguageScanRule(lang, config); | |
| ppFileName = "$PREPROCESSED_OUTPUT_FILE"; | |
| std::string const& scanCommand = mf->GetRequiredDefinition( | |
| cmStrCat("CMAKE_", lang, "_SCANDEP_SOURCE")); | |
| scanCommands.assign(scanCommand); | |
| for (auto& i : scanCommands) { | |
| i = cmStrCat(launcher, std::move(i)); | |
| } | |
| } else { | |
| scanRuleName = this->LanguagePreprocessAndScanRule(lang, config); | |
| ppFileName = "$out"; | |
| std::string const& ppCommand = mf->GetRequiredDefinition( | |
| cmStrCat("CMAKE_", lang, "_PREPROCESS_SOURCE")); | |
| scanCommands.assign(ppCommand); | |
| for (auto& i : scanCommands) { | |
| i = cmStrCat(launcher, std::move(i)); | |
| } | |
| scanCommands.emplace_back(GetScanCommand( | |
| cmakeCmd, tdi, lang, "$out", "$DYNDEP_INTERMEDIATE_FILE", "$in")); | |
| } | |
| auto scanRule = GetScanRule( | |
| scanRuleName, ppFileName, scanDepType, vars, responseFlag, flags, | |
| rulePlaceholderExpander.get(), this->GetLocalGenerator(), | |
| std::move(scanCommands), config); | |
| scanRule.Comment = | |
| cmStrCat("Rule for generating ", lang, " dependencies."); | |
| if (compilationPreprocesses) { | |
| scanRule.Description = | |
| cmStrCat("Scanning $in for ", lang, " dependencies"); | |
| } else { | |
| scanRule.Description = | |
| cmStrCat("Building ", lang, " preprocessed $out"); | |
| } | |
| this->GetGlobalGenerator()->AddRule(scanRule); | |
| } | |
| if (!compilationPreprocesses) { | |
| // Compilation will not preprocess, so it does not need the defines | |
| // unless the compiler wants them for some other purpose. | |
| if (!this->CompileWithDefines(lang)) { | |
| vars.Defines = ""; | |
| } | |
| // Rule to scan dependencies of sources that do not need preprocessing. | |
| std::string const& scanRuleName = this->LanguageScanRule(lang, config); | |
| std::vector<std::string> scanCommands; | |
| scanCommands.emplace_back( | |
| GetScanCommand(cmakeCmd, tdi, lang, "$in", "$out")); | |
| auto scanRule = | |
| GetScanRule(scanRuleName, "", scanDepType, vars, "", flags, | |
| rulePlaceholderExpander.get(), this->GetLocalGenerator(), | |
| std::move(scanCommands), config); | |
| // Write the rule for generating dependencies for the given language. | |
| scanRule.Comment = cmStrCat("Rule for generating ", lang, | |
| " dependencies on non-preprocessed files."); | |
| scanRule.Description = | |
| cmStrCat("Generating ", lang, " dependencies for $in"); | |
| this->GetGlobalGenerator()->AddRule(scanRule); | |
| } | |
| // Write the rule for ninja dyndep file generation. | |
| cmNinjaRule rule(this->LanguageDyndepRule(lang, config)); | |
| // Command line length is almost always limited -> use response file for | |
| // dyndep rules | |
| rule.RspFile = "$out.rsp"; | |
| rule.RspContent = "$in"; | |
| // Ninja's collator writes all outputs using `cmGeneratedFileStream`, so | |
| // they are only updated if contents actually change. Avoid running | |
| // dependent jobs if the contents don't change by telling `ninja` to check | |
| // the timestamp again. | |
| rule.Restat = "1"; | |
| // Run CMake dependency scanner on the source file (using the preprocessed | |
| // source if that was performed). | |
| std::string ddModmapArg; | |
| if (!modmapFormat.empty()) { | |
| ddModmapArg = | |
| cmStrCat(std::move(ddModmapArg), " --modmapfmt=", modmapFormat); | |
| } | |
| { | |
| std::vector<std::string> ddCmds; | |
| { | |
| std::string ccmd = cmStrCat( | |
| cmakeCmd, " -E cmake_ninja_dyndep --tdi=", tdi, " --lang=", lang, | |
| ddModmapArg, " --dd=$out @", rule.RspFile); | |
| ddCmds.emplace_back(std::move(ccmd)); | |
| } | |
| rule.Command = | |
| this->GetLocalGenerator()->BuildCommandLine(ddCmds, config, config); | |
| } | |
| rule.Comment = | |
| cmStrCat("Rule to generate ninja dyndep files for ", lang, '.'); | |
| rule.Description = cmStrCat("Generating ", lang, " dyndep file $out"); | |
| this->GetGlobalGenerator()->AddRule(rule); | |
| } | |
| cmNinjaRule rule(this->LanguageCompilerRule(lang, config, withScanning)); | |
| // If using a response file, move defines, includes, and flags into it. | |
| if (!responseFlag.empty()) { | |
| SetupResponseFile(rule, vars, flags, lang, responseFlag); | |
| } | |
| // Tell ninja dependency format so all deps can be loaded into a database | |
| std::string cldeps; | |
| if (!compilationPreprocesses) { | |
| // The compiler will not do preprocessing, so it has no such dependencies. | |
| } else if (mf->IsOn(cmStrCat("CMAKE_NINJA_CMCLDEPS_", lang))) { | |
| // For the MS resource compiler we need cmcldeps, but skip dependencies | |
| // for source-file try_compile cases because they are always fresh. | |
| if (!mf->GetIsSourceFileTryCompile()) { | |
| rule.DepType = "gcc"; | |
| rule.DepFile = "$DEP_FILE"; | |
| cmValue d = mf->GetDefinition("CMAKE_C_COMPILER"); | |
| std::string const cl = | |
| d ? *d : mf->GetSafeDefinition("CMAKE_CXX_COMPILER"); | |
| std::string cmcldepsPath; | |
| cmSystemTools::GetShortPath(cmSystemTools::GetCMClDepsCommand(), | |
| cmcldepsPath); | |
| cldeps = cmStrCat(cmcldepsPath, ' ', lang, ' ', vars.Source, | |
| " $DEP_FILE $out \"", | |
| mf->GetSafeDefinition("CMAKE_CL_SHOWINCLUDES_PREFIX"), | |
| "\" \"", cl, "\" "); | |
| } | |
| } else { | |
| auto const& depType = this->GetMakefile()->GetSafeDefinition( | |
| cmStrCat("CMAKE_", lang, "_DEPFILE_FORMAT")); | |
| if (depType == "msvc"_s) { | |
| rule.DepType = "msvc"; | |
| rule.DepFile.clear(); | |
| } else { | |
| rule.DepType = "gcc"; | |
| rule.DepFile = "$DEP_FILE"; | |
| } | |
| vars.DependencyFile = rule.DepFile.c_str(); | |
| vars.DependencyTarget = "$out"; | |
| std::string const flagsName = cmStrCat("CMAKE_DEPFILE_FLAGS_", lang); | |
| std::string depfileFlags = mf->GetSafeDefinition(flagsName); | |
| if (!depfileFlags.empty()) { | |
| rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(), | |
| depfileFlags, vars); | |
| flags = cmStrCat(std::move(flags), ' ', depfileFlags); | |
| } | |
| } | |
| if (withScanning == WithScanning::Yes && !modmapFormat.empty()) { | |
| std::string modmapFlags = | |
| mf->GetRequiredDefinition(cmStrCat("CMAKE_", lang, "_MODULE_MAP_FLAG")); | |
| cmSystemTools::ReplaceString(modmapFlags, "<MODULE_MAP_FILE>", | |
| "$DYNDEP_MODULE_MAP_FILE"); | |
| flags = cmStrCat(std::move(flags), ' ', modmapFlags); | |
| } | |
| vars.Flags = flags.c_str(); | |
| vars.DependencyFile = rule.DepFile.c_str(); | |
| std::string cudaCompileMode; | |
| if (lang == "CUDA") { | |
| if (this->GeneratorTarget->GetPropertyAsBool( | |
| "CUDA_SEPARABLE_COMPILATION")) { | |
| std::string const& rdcFlag = | |
| this->Makefile->GetRequiredDefinition("_CMAKE_CUDA_RDC_FLAG"); | |
| cudaCompileMode = cmStrCat(std::move(cudaCompileMode), rdcFlag, ' '); | |
| } | |
| static std::array<cm::string_view, 4> const compileModes{ | |
| { "PTX"_s, "CUBIN"_s, "FATBIN"_s, "OPTIX"_s } | |
| }; | |
| bool useNormalCompileMode = true; | |
| for (cm::string_view mode : compileModes) { | |
| auto propName = cmStrCat("CUDA_", mode, "_COMPILATION"); | |
| auto defName = cmStrCat("_CMAKE_CUDA_", mode, "_FLAG"); | |
| if (this->GeneratorTarget->GetPropertyAsBool(propName)) { | |
| std::string const& flag = | |
| this->Makefile->GetRequiredDefinition(defName); | |
| cudaCompileMode = cmStrCat(std::move(cudaCompileMode), flag); | |
| useNormalCompileMode = false; | |
| break; | |
| } | |
| } | |
| if (useNormalCompileMode) { | |
| std::string const& wholeFlag = | |
| this->Makefile->GetRequiredDefinition("_CMAKE_CUDA_WHOLE_FLAG"); | |
| cudaCompileMode = cmStrCat(std::move(cudaCompileMode), wholeFlag); | |
| } | |
| vars.CudaCompileMode = cudaCompileMode.c_str(); | |
| } | |
| // Rule for compiling object file. | |
| std::string const cmdVar = this->GetCompileTemplateVar(lang); | |
| std::string const& compileCmd = mf->GetRequiredDefinition(cmdVar); | |
| cmList compileCmds = ExpandRuleCommands(compileCmd, vars, mf, lang, launcher, | |
| cldeps, this->GetLocalGenerator(), | |
| rulePlaceholderExpander.get()); | |
| rule.Command = | |
| this->GetLocalGenerator()->BuildCommandLine(compileCmds, config, config); | |
| // Write the rule for compiling file of the given language. | |
| rule.Comment = cmStrCat("Rule for compiling ", lang, " files."); | |
| rule.Description = cmStrCat("Building ", lang, " object $out"); | |
| this->GetGlobalGenerator()->AddRule(rule); | |
| } | |
| void cmNinjaTargetGenerator::WriteObjectBuildStatements( | |
| std::string const& config, std::string const& fileConfig, | |
| bool firstForConfig) | |
| { | |
| this->GeneratorTarget->CheckCxxModuleStatus(config); | |
| // Write comments. | |
| cmGlobalNinjaGenerator::WriteDivider(this->GetImplFileStream(fileConfig)); | |
| this->GetImplFileStream(fileConfig) | |
| << "# Object build statements for " | |
| << cmState::GetTargetTypeName(this->GetGeneratorTarget()->GetType()) | |
| << " target " << this->GetTargetName() << "\n\n"; | |
| std::vector<cmCustomCommand const*> customCommands; | |
| { | |
| std::vector<cmSourceFile const*> customCommandSources; | |
| this->GeneratorTarget->GetCustomCommands(customCommandSources, config); | |
| for (cmSourceFile const* sf : customCommandSources) { | |
| cmCustomCommand const* cc = sf->GetCustomCommand(); | |
| this->GetLocalGenerator()->AddCustomCommandTarget( | |
| cc, this->GetGeneratorTarget()); | |
| customCommands.push_back(cc); | |
| } | |
| } | |
| { | |
| std::vector<cmSourceFile const*> headerSources; | |
| this->GeneratorTarget->GetHeaderSources(headerSources, config); | |
| this->OSXBundleGenerator->GenerateMacOSXContentStatements( | |
| headerSources, this->Configs[fileConfig].MacOSXContentGenerator.get(), | |
| config); | |
| } | |
| { | |
| std::vector<cmSourceFile const*> extraSources; | |
| this->GeneratorTarget->GetExtraSources(extraSources, config); | |
| this->OSXBundleGenerator->GenerateMacOSXContentStatements( | |
| extraSources, this->Configs[fileConfig].MacOSXContentGenerator.get(), | |
| config); | |
| } | |
| if (firstForConfig) { | |
| cmValue pchExtension = | |
| this->GetMakefile()->GetDefinition("CMAKE_PCH_EXTENSION"); | |
| std::vector<cmSourceFile const*> externalObjects; | |
| this->GeneratorTarget->GetExternalObjects(externalObjects, config); | |
| for (cmSourceFile const* sf : externalObjects) { | |
| auto objectFileName = this->GetGlobalGenerator()->ExpandCFGIntDir( | |
| this->ConvertToNinjaPath(sf->GetFullPath()), config); | |
| if (!cmHasSuffix(objectFileName, pchExtension)) { | |
| this->Configs[config].Objects.push_back(objectFileName); | |
| } | |
| } | |
| } | |
| if (!this->GetGlobalGenerator()->SupportsCWDDepend()) { | |
| // Ensure that the object directory exists. If there are no objects in the | |
| // target (e.g., an empty `OBJECT` library), the directory is still listed | |
| // as an order-only depends in the build files. Alternate `ninja` | |
| // implementations may not allow this (such as `samu`). See #25526. | |
| auto const objectDir = this->GetObjectFileDir(config); | |
| this->EnsureDirectoryExists(objectDir); | |
| } | |
| { | |
| cmNinjaBuild build("phony"); | |
| build.Comment = | |
| cmStrCat("Order-only phony target for ", this->GetTargetName()); | |
| build.Outputs.push_back(this->OrderDependsTargetForTarget(config)); | |
| // Gather order-only dependencies on custom command outputs. | |
| std::vector<std::string> ccouts; | |
| std::vector<std::string> ccouts_private; | |
| bool usePrivateGeneratedSources = this->GeneratorTarget->HasFileSets() && | |
| this->GetGeneratorTarget()->GetPolicyStatusCMP0154() == cmPolicies::NEW; | |
| for (cmCustomCommand const* cc : customCommands) { | |
| cmCustomCommandGenerator ccg(*cc, config, this->GetLocalGenerator()); | |
| std::vector<std::string> const& ccoutputs = ccg.GetOutputs(); | |
| std::vector<std::string> const& ccbyproducts = ccg.GetByproducts(); | |
| auto const nPreviousOutputs = ccouts.size(); | |
| ccouts.insert(ccouts.end(), ccoutputs.begin(), ccoutputs.end()); | |
| ccouts.insert(ccouts.end(), ccbyproducts.begin(), ccbyproducts.end()); | |
| if (usePrivateGeneratedSources) { | |
| auto it = ccouts.begin(); | |
| // Skip over outputs that were already detected. | |
| std::advance(it, nPreviousOutputs); | |
| while (it != ccouts.end()) { | |
| cmGeneratorFileSet const* fileset = | |
| this->GeneratorTarget->GetFileSetForSource( | |
| config, this->Makefile->GetOrCreateGeneratedSource(*it)); | |
| if (!fileset) { | |
| // use private order dependency | |
| ccouts_private.push_back(*it); | |
| it = ccouts.erase(it); | |
| continue; | |
| } | |
| using DependencyMode = cm::FileSetMetadata::DependencyMode; | |
| cmValue independentFiles = fileset->GetProperty("INDEPENDENT_FILES"); | |
| // retrieve default mode | |
| DependencyMode dependencyMode = | |
| cm::FileSetMetadata::GetDependencyMode(fileset->GetType()); | |
| // if property is defined, try to enforce mode requested | |
| if (independentFiles) { | |
| dependencyMode = cm::FileSetMetadata::GetDependencyMode( | |
| fileset->GetType(), | |
| independentFiles.IsOn() ? DependencyMode::IndependentFiles | |
| : DependencyMode::Includables); | |
| } | |
| if (independentFiles.IsOn() && | |
| dependencyMode != DependencyMode::IndependentFiles) { | |
| // requested dependency mode not supported | |
| this->GetMakefile()->IssueDiagnostic( | |
| cmDiagnostics::CMD_AUTHOR, | |
| cmStrCat(R"(the "INDEPENDENT_FILES" property of the file set ")", | |
| fileset->GetName(), R"(" of the target ")", | |
| this->GeneratorTarget->GetName(), | |
| R"(" will be ignored because it is incompatible with )" | |
| R"(the file set type ")", | |
| fileset->GetType(), R"(".)")); | |
| } | |
| if (dependencyMode == DependencyMode::Includables) { | |
| if (fileset->IsForInterface()) { | |
| // use public order dependency | |
| ++it; | |
| } else { | |
| // use private order dependency | |
| ccouts_private.push_back(*it); | |
| it = ccouts.erase(it); | |
| } | |
| continue; | |
| } | |
| // no order dependency is required | |
| it = ccouts.erase(it); | |
| } | |
| } | |
| } | |
| cmNinjaDeps& orderOnlyDeps = build.OrderOnlyDeps; | |
| this->GetLocalGenerator()->AppendTargetDepends( | |
| this->GeneratorTarget, orderOnlyDeps, config, fileConfig, | |
| DependOnTargetOrdering); | |
| // Add order-only dependencies on other files associated with the target. | |
| cm::append(orderOnlyDeps, this->Configs[config].ExtraFiles); | |
| // Add order-only dependencies on custom command outputs. | |
| std::transform(ccouts.begin(), ccouts.end(), | |
| std::back_inserter(orderOnlyDeps), this->MapToNinjaPath()); | |
| std::sort(orderOnlyDeps.begin(), orderOnlyDeps.end()); | |
| orderOnlyDeps.erase( | |
| std::unique(orderOnlyDeps.begin(), orderOnlyDeps.end()), | |
| orderOnlyDeps.end()); | |
| // The phony target must depend on at least one input or ninja will explain | |
| // that "output ... of phony edge with no inputs doesn't exist" and | |
| // consider the phony output "dirty". | |
| if (orderOnlyDeps.empty()) { | |
| std::string tgtDir; | |
| if (this->GetGlobalGenerator()->SupportsCWDDepend()) { | |
| tgtDir = "."; | |
| } else { | |
| // Any path that always exists will work here. | |
| tgtDir = this->GetObjectFileDir(config); | |
| } | |
| orderOnlyDeps.push_back(this->ConvertToNinjaPath(tgtDir)); | |
| } | |
| this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig), | |
| build); | |
| // Add order-only dependencies on custom command outputs that are | |
| // private to this target. | |
| this->HasPrivateGeneratedSources = !ccouts_private.empty(); | |
| if (this->HasPrivateGeneratedSources) { | |
| cmNinjaBuild buildPrivate("phony"); | |
| cmNinjaDeps& orderOnlyDepsPrivate = buildPrivate.OrderOnlyDeps; | |
| orderOnlyDepsPrivate.push_back( | |
| this->OrderDependsTargetForTarget(config)); | |
| buildPrivate.Outputs.push_back( | |
| this->OrderDependsTargetForTargetPrivate(config)); | |
| std::transform(ccouts_private.begin(), ccouts_private.end(), | |
| std::back_inserter(orderOnlyDepsPrivate), | |
| this->MapToNinjaPath()); | |
| std::sort(orderOnlyDepsPrivate.begin(), orderOnlyDepsPrivate.end()); | |
| orderOnlyDepsPrivate.erase( | |
| std::unique(orderOnlyDepsPrivate.begin(), orderOnlyDepsPrivate.end()), | |
| orderOnlyDepsPrivate.end()); | |
| this->GetGlobalGenerator()->WriteBuild( | |
| this->GetImplFileStream(fileConfig), buildPrivate); | |
| } | |
| } | |
| { | |
| std::vector<cmSourceFile const*> objectSources; | |
| this->GeneratorTarget->GetObjectSources(objectSources, config); | |
| std::vector<cmSourceFile const*> swiftSources; | |
| for (cmSourceFile const* sf : objectSources) { | |
| if (this->GetLocalGenerator()->IsSplitSwiftBuild() && | |
| this->GeneratorTarget->GetSourceFileLanguage(sf, config) == | |
| "Swift") { | |
| swiftSources.push_back(sf); | |
| } else { | |
| this->WriteObjectBuildStatement(sf, config, fileConfig, | |
| firstForConfig); | |
| } | |
| } | |
| WriteSwiftObjectBuildStatement(swiftSources, config, fileConfig, | |
| firstForConfig); | |
| } | |
| { | |
| std::vector<cmSourceFile const*> bmiOnlySources; | |
| this->GeneratorTarget->GetCxxModuleSources(bmiOnlySources, config); | |
| for (cmSourceFile const* sf : bmiOnlySources) { | |
| this->WriteCxxModuleBmiBuildStatement(sf, config, fileConfig, | |
| firstForConfig); | |
| } | |
| } | |
| // Detect sources in `CXX_MODULES` which are not compiled. | |
| { | |
| std::vector<cmSourceFile*> sources; | |
| this->GeneratorTarget->GetSourceFiles(sources, config); | |
| for (cmSourceFile const* sf : sources) { | |
| cmGeneratorFileSet const* fs = | |
| this->GeneratorTarget->GetFileSetForSource(config, sf); | |
| if (!fs) { | |
| continue; | |
| } | |
| if (fs->GetType() != cm::FileSetMetadata::CXX_MODULES) { | |
| continue; | |
| } | |
| if (sf->GetLanguage().empty()) { | |
| this->GeneratorTarget->Makefile->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat("Target \"", this->GeneratorTarget->GetName(), | |
| "\" has source file\n ", sf->GetFullPath(), | |
| "\nin a \"FILE_SET TYPE ", cm::FileSetMetadata::CXX_MODULES, | |
| "\" but it is not scheduled for compilation.")); | |
| } | |
| } | |
| } | |
| // Check if there are Fortran objects which need to participate in forwarding | |
| // module requirements. | |
| if (this->GeneratorTarget->HaveFortranSources(config) && | |
| !this->Configs[config].ScanningInfo.count("Fortran")) { | |
| ScanningFiles files; | |
| this->Configs[config].ScanningInfo["Fortran"].emplace_back(files); | |
| this->WriteCompileRule("Fortran", config, WithScanning::Yes); | |
| } | |
| for (auto const& langScanningFiles : this->Configs[config].ScanningInfo) { | |
| std::string const& language = langScanningFiles.first; | |
| std::vector<ScanningFiles> const& scanningFiles = langScanningFiles.second; | |
| cmNinjaBuild build(this->LanguageDyndepRule(language, config)); | |
| build.Outputs.push_back(this->GetDyndepFilePath(language, config)); | |
| build.ImplicitOuts.emplace_back( | |
| cmStrCat(this->GetObjectFileDir(config), '/', language, "Modules.json")); | |
| // Only C++ collation produces the interface objects response file. | |
| if (language == "CXX") { | |
| bool multiConfig = this->GetGlobalGenerator()->IsMultiConfig(); | |
| std::string configDir = multiConfig ? cmStrCat('/', config) : ""; | |
| std::string rspPath = | |
| cmStrCat(this->GeneratorTarget->GetSupportDirectory(), configDir, | |
| "/CXXInterfaceObjects.rsp"); | |
| bool hasInterfaceObjects = false; | |
| for (auto const& pair : | |
| this->GeneratorTarget->GetSyntheticDeps(config)) { | |
| if (pair.first->Target->CxxModuleNeedsInterfaceObjects()) { | |
| hasInterfaceObjects = true; | |
| break; | |
| } | |
| } | |
| if (hasInterfaceObjects) { | |
| build.ImplicitOuts.emplace_back(this->ConvertToNinjaPath(rspPath)); | |
| } | |
| } | |
| build.ImplicitDeps.emplace_back( | |
| this->GetTargetDependInfoPath(language, config)); | |
| { | |
| auto bdb_path = | |
| this->GeneratorTarget->BuildDatabasePath(language, config); | |
| if (!bdb_path.empty()) { | |
| build.ImplicitOuts.emplace_back(this->ConvertToNinjaPath(bdb_path)); | |
| } | |
| } | |
| auto bdb_path = this->GeneratorTarget->BuildDatabasePath(language, config); | |
| if (!bdb_path.empty()) { | |
| auto db = cmBuildDatabase::ForTarget(this->GeneratorTarget, config); | |
| auto mcdb_template_path = cmStrCat(bdb_path, ".in"); | |
| db.Write(mcdb_template_path); | |
| build.ImplicitDeps.emplace_back(std::move(mcdb_template_path)); | |
| build.ImplicitOuts.emplace_back(std::move(bdb_path)); | |
| } | |
| for (auto const& scanFiles : scanningFiles) { | |
| if (!scanFiles.ScanningOutput.empty()) { | |
| build.ExplicitDeps.push_back(scanFiles.ScanningOutput); | |
| } | |
| if (!scanFiles.ModuleMapFile.empty()) { | |
| build.ImplicitOuts.push_back(scanFiles.ModuleMapFile); | |
| } | |
| } | |
| this->WriteTargetDependInfo(language, config); | |
| // Non-imported synthetic targets read module info from their native target | |
| // Add as implicit dependency. | |
| if (this->GeneratorTarget->IsSynthetic()) { | |
| if (cmGeneratorTarget const* native_gt = | |
| this->LocalGenerator->FindGeneratorTargetToUse( | |
| this->GeneratorTarget->Target->GetTemplateName())) { | |
| if (!native_gt->IsImported()) { | |
| std::string native_dir = native_gt->GetSupportDirectory(); | |
| if (this->GetGlobalGenerator()->IsMultiConfig()) { | |
| native_dir = cmStrCat(std::move(native_dir), '/', config); | |
| } | |
| build.ImplicitDeps.emplace_back(this->ConvertToNinjaPath( | |
| cmStrCat(native_dir, '/', language, "Modules.json"))); | |
| } | |
| } | |
| } | |
| auto const linked_directories = | |
| this->GetLinkedTargetDirectories(language, config); | |
| for (std::string const& l : linked_directories.Direct) { | |
| build.ImplicitDeps.emplace_back( | |
| this->ConvertToNinjaPath(cmStrCat(l, '/', language, "Modules.json"))); | |
| } | |
| for (std::string const& l : linked_directories.Forward) { | |
| build.ImplicitDeps.emplace_back( | |
| this->ConvertToNinjaPath(cmStrCat(l, '/', language, "Modules.json"))); | |
| } | |
| this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig), | |
| build); | |
| } | |
| this->GetImplFileStream(fileConfig) << '\n'; | |
| } | |
| void cmNinjaTargetGenerator::GenerateSwiftOutputFileMap( | |
| std::string const& config, std::string& flags) | |
| { | |
| if (this->Configs[config].SwiftOutputMap.empty()) { | |
| return; | |
| } | |
| std::string const targetSwiftDepsPath = [this, config]() -> std::string { | |
| cmGeneratorTarget const* target = this->GeneratorTarget; | |
| if (cmValue name = target->GetProperty("Swift_DEPENDENCIES_FILE")) { | |
| return *name; | |
| } | |
| return this->ConvertToNinjaPath(cmStrCat(target->GetSupportDirectory(), | |
| '/', config, '/', | |
| target->GetName(), ".swiftdeps")); | |
| }(); | |
| std::string mapFilePath = | |
| cmStrCat(this->GeneratorTarget->GetSupportDirectory(), '/', config, | |
| "/" | |
| "output-file-map.json"); | |
| // build the global target dependencies | |
| // https://github.com/apple/swift/blob/master/docs/Driver.md#output-file-maps | |
| Json::Value deps(Json::objectValue); | |
| deps["swift-dependencies"] = targetSwiftDepsPath; | |
| this->Configs[config].SwiftOutputMap[""] = deps; | |
| cmGeneratedFileStream output(mapFilePath); | |
| output.SetCopyIfDifferent(true); | |
| output << this->Configs[config].SwiftOutputMap; | |
| // Add flag | |
| this->LocalGenerator->AppendFlags(flags, "-output-file-map"); | |
| this->LocalGenerator->AppendFlags( | |
| flags, | |
| this->ConvertToOutputFormatForShell(ConvertToNinjaPath(mapFilePath))); | |
| } | |
| namespace { | |
| cmNinjaBuild GetScanBuildStatement(std::string const& ruleName, | |
| std::string const& ppFileName, | |
| bool compilePP, bool compilePPWithDefines, | |
| bool compilationPreprocesses, | |
| cmNinjaBuild& objBuild, cmNinjaVars& vars, | |
| std::string const& objectFileName, | |
| cmNinjaTargetGenerator* tg) | |
| { | |
| cmNinjaBuild scanBuild(ruleName); | |
| if (compilePP) { | |
| // Move compilation dependencies to the scan/preprocessing build statement. | |
| std::swap(scanBuild.ExplicitDeps, objBuild.ExplicitDeps); | |
| std::swap(scanBuild.ImplicitDeps, objBuild.ImplicitDeps); | |
| std::swap(scanBuild.OrderOnlyDeps, objBuild.OrderOnlyDeps); | |
| std::swap(scanBuild.Variables["IN_ABS"], vars["IN_ABS"]); | |
| // The actual compilation will now use the preprocessed source. | |
| objBuild.ExplicitDeps.push_back(ppFileName); | |
| } else { | |
| // Copy compilation dependencies to the scan/preprocessing build statement. | |
| scanBuild.ExplicitDeps = objBuild.ExplicitDeps; | |
| scanBuild.ImplicitDeps = objBuild.ImplicitDeps; | |
| scanBuild.OrderOnlyDeps = objBuild.OrderOnlyDeps; | |
| scanBuild.Variables["IN_ABS"] = vars["IN_ABS"]; | |
| } | |
| // Scanning and compilation generally use the same flags. | |
| scanBuild.Variables["FLAGS"] = vars["FLAGS"]; | |
| if (compilePP && !compilePPWithDefines) { | |
| // Move preprocessor definitions to the scan/preprocessor build statement. | |
| std::swap(scanBuild.Variables["DEFINES"], vars["DEFINES"]); | |
| } else { | |
| // Copy preprocessor definitions to the scan/preprocessor build statement. | |
| scanBuild.Variables["DEFINES"] = vars["DEFINES"]; | |
| } | |
| // Copy include directories to the preprocessor build statement. The | |
| // Fortran compilation build statement still needs them for the INCLUDE | |
| // directive. | |
| scanBuild.Variables["INCLUDES"] = vars["INCLUDES"]; | |
| // Tell dependency scanner the object file that will result from | |
| // compiling the source. | |
| scanBuild.Variables["OBJ_FILE"] = | |
| tg->ConvertToOutputFormatForShell(objectFileName); | |
| // Tell dependency scanner where to store dyndep intermediate results. | |
| std::string ddiFileName = cmStrCat(objectFileName, ".ddi"); | |
| scanBuild.Variables["DYNDEP_INTERMEDIATE_FILE"] = | |
| tg->ConvertToOutputFormatForShell(ddiFileName); | |
| scanBuild.RspFile = cmStrCat(ddiFileName, ".rsp"); | |
| // Outputs of the scan/preprocessor build statement. | |
| if (compilePP) { | |
| scanBuild.Outputs.push_back(ppFileName); | |
| scanBuild.ImplicitOuts.push_back(ddiFileName); | |
| } else { | |
| scanBuild.Outputs.push_back(ddiFileName); | |
| scanBuild.Variables["PREPROCESSED_OUTPUT_FILE"] = | |
| tg->ConvertToOutputFormatForShell(ppFileName); | |
| if (!compilationPreprocesses) { | |
| // Compilation does not preprocess and we are not compiling an | |
| // already-preprocessed source. Make compilation depend on the scan | |
| // results to honor implicit dependencies discovered during scanning | |
| // (such as Fortran INCLUDE directives). | |
| objBuild.ImplicitDeps.emplace_back(ddiFileName); | |
| } | |
| } | |
| // Scanning always provides a depfile for preprocessor dependencies. This | |
| // variable is unused in `msvc`-deptype scanners. | |
| tg->AddDepfileBinding(scanBuild.Variables, | |
| cmStrCat(scanBuild.Outputs.front(), ".d")); | |
| if (compilePP) { | |
| // The actual compilation does not need a depfile because it | |
| // depends on the already-preprocessed source. | |
| tg->RemoveDepfileBinding(vars); | |
| } | |
| return scanBuild; | |
| } | |
| } | |
| void cmNinjaTargetGenerator::WriteObjectBuildStatement( | |
| cmSourceFile const* source, std::string const& config, | |
| std::string const& fileConfig, bool firstForConfig) | |
| { | |
| std::string const language = | |
| this->GeneratorTarget->GetSourceFileLanguage(source, config); | |
| std::string const sourceFilePath = this->GetCompiledSourceNinjaPath(source); | |
| std::string const targetSupportDir = | |
| this->ConvertToNinjaPath(this->GeneratorTarget->GetCMFSupportDirectory()); | |
| std::string const objectDir = | |
| this->ConvertToNinjaPath(this->GetObjectFileDir(config)); | |
| std::string const objectFileName = | |
| this->ConvertToNinjaPath(this->GetObjectFilePath(source, config)); | |
| std::string const objectFileDir = | |
| cmSystemTools::GetFilenamePath(objectFileName); | |
| std::string cmakeVarLang = cmStrCat("CMAKE_", language); | |
| // build response file name | |
| std::string cmakeLinkVar = cmStrCat(cmakeVarLang, "_RESPONSE_FILE_FLAG"); | |
| cmValue flag = this->GetMakefile()->GetDefinition(cmakeLinkVar); | |
| bool const lang_supports_response = | |
| !(language == "RC" || (language == "CUDA" && !flag)); | |
| int const commandLineLengthLimit = | |
| ((lang_supports_response && this->ForceResponseFile())) ? -1 : 0; | |
| cmValue pchExtension = | |
| this->GetMakefile()->GetDefinition("CMAKE_PCH_EXTENSION"); | |
| bool const isPch = cmHasSuffix(objectFileName, pchExtension); | |
| bool const needDyndep = !isPch && | |
| this->GeneratorTarget->NeedDyndepForSource(language, config, source); | |
| WithScanning withScanning = | |
| needDyndep ? WithScanning::Yes : WithScanning::No; | |
| cmNinjaBuild objBuild( | |
| this->LanguageCompilerRule(language, config, withScanning)); | |
| cmNinjaVars& vars = objBuild.Variables; | |
| vars["FLAGS"] = | |
| this->ComputeFlagsForObject(source, language, config, objectFileName); | |
| vars["DEFINES"] = this->ComputeDefines(source, language, config); | |
| vars["INCLUDES"] = this->ComputeIncludes(source, language, config); | |
| vars["CONFIG"] = config; | |
| if (this->GetGeneratorTarget()->GetUseShortObjectNames()) { | |
| vars.emplace( | |
| "description", | |
| cmStrCat("Compiling object ", objectFileName, " from source ", | |
| this->GetLocalGenerator()->GetRelativeSourceFileName(*source))); | |
| } | |
| auto compilerLauncher = this->GetCompilerLauncher(language, config); | |
| cmGeneratorFileSet const* fileSet = | |
| this->GeneratorTarget->GetFileSetForSource(config, source); | |
| cmValue const fsSkipCodeCheckVal = | |
| fileSet ? fileSet->GetProperty("SKIP_LINTING") : nullptr; | |
| cmValue const srcSkipCodeCheckVal = source->GetProperty("SKIP_LINTING"); | |
| bool const skipCodeCheck = fsSkipCodeCheckVal.IsSet() | |
| ? fsSkipCodeCheckVal.IsOn() | |
| : (srcSkipCodeCheckVal.IsSet() | |
| ? srcSkipCodeCheckVal.IsOn() | |
| : this->GetGeneratorTarget()->GetPropertyAsBool("SKIP_LINTING")); | |
| if (!skipCodeCheck) { | |
| auto const cmakeCmd = | |
| this->ConvertToOutputFormatForShell(cmSystemTools::GetCMakeCommand()); | |
| vars["CODE_CHECK"] = | |
| this->GenerateCodeCheckRules(*source, compilerLauncher, cmakeCmd, config, | |
| [this](std::string const& path) { | |
| return this->ConvertToNinjaPath(path); | |
| }); | |
| } | |
| // If compiler launcher was specified and not consumed above, it | |
| // goes to the beginning of the command line. | |
| if (!compilerLauncher.empty()) { | |
| vars["LAUNCHER"] = | |
| cmStrCat(this->ConvertLauncherToShell(compilerLauncher), ' '); | |
| } | |
| if (this->GetMakefile()->GetSafeDefinition( | |
| cmStrCat("CMAKE_", language, "_DEPFILE_FORMAT")) != "msvc"_s) { | |
| bool replaceExt = false; | |
| if (!language.empty()) { | |
| std::string repVar = | |
| cmStrCat("CMAKE_", language, "_DEPFILE_EXTENSION_REPLACE"); | |
| replaceExt = this->Makefile->IsOn(repVar); | |
| } | |
| this->AddDepfileBinding( | |
| vars, | |
| replaceExt ? cmStrCat(objectFileDir, '/', | |
| cmSystemTools::GetFilenameWithoutLastExtension( | |
| objectFileName), | |
| ".d") | |
| : cmStrCat(objectFileName, ".d")); | |
| } | |
| this->SetMsvcTargetPdbVariable(vars, config); | |
| if (firstForConfig) { | |
| this->ExportObjectCompileCommand( | |
| language, sourceFilePath, objectDir, targetSupportDir, objectFileName, | |
| objectFileDir, vars["FLAGS"], vars["DEFINES"], vars["INCLUDES"], | |
| vars["TARGET_COMPILE_PDB"], vars["TARGET_PDB"], config, withScanning); | |
| } | |
| objBuild.Outputs.push_back(objectFileName); | |
| if (firstForConfig && !isPch) { | |
| // Add this object to the list of object files. | |
| this->Configs[config].Objects.push_back(objectFileName); | |
| } | |
| objBuild.ExplicitDeps.push_back(sourceFilePath); | |
| // Add precompile headers dependencies | |
| std::vector<std::string> depList; | |
| std::vector<std::string> pchArchs = | |
| this->GeneratorTarget->GetPchArchs(config, language); | |
| std::unordered_set<std::string> pchSources; | |
| for (std::string const& arch : pchArchs) { | |
| std::string const pchSource = | |
| this->GeneratorTarget->GetPchSource(config, language, arch); | |
| if (!pchSource.empty()) { | |
| pchSources.insert(pchSource); | |
| } | |
| } | |
| if (!pchSources.empty() && | |
| !((fileSet && fileSet->GetProperty("SKIP_PRECOMPILE_HEADERS")) || | |
| source->GetProperty("SKIP_PRECOMPILE_HEADERS"))) { | |
| for (std::string const& arch : pchArchs) { | |
| depList.push_back( | |
| this->GeneratorTarget->GetPchHeader(config, language, arch)); | |
| if (pchSources.find(source->GetFullPath()) == pchSources.end()) { | |
| depList.push_back( | |
| this->GeneratorTarget->GetPchFile(config, language, arch)); | |
| } | |
| } | |
| } | |
| if (cmValue objectDeps = source->GetProperty("OBJECT_DEPENDS")) { | |
| cmList objDepList{ *objectDeps }; | |
| std::copy(objDepList.begin(), objDepList.end(), | |
| std::back_inserter(depList)); | |
| } | |
| if (!depList.empty()) { | |
| for (std::string& odi : depList) { | |
| if (cmSystemTools::FileIsFullPath(odi)) { | |
| odi = cmSystemTools::CollapseFullPath(odi); | |
| } | |
| } | |
| std::transform(depList.begin(), depList.end(), | |
| std::back_inserter(objBuild.ImplicitDeps), | |
| this->MapToNinjaPath()); | |
| } | |
| if (this->HasPrivateGeneratedSources) { | |
| objBuild.OrderOnlyDeps.push_back( | |
| this->OrderDependsTargetForTargetPrivate(config)); | |
| } else { | |
| objBuild.OrderOnlyDeps.push_back( | |
| this->OrderDependsTargetForTarget(config)); | |
| } | |
| // If the source file is GENERATED and does not have a custom command | |
| // (either attached to this source file or another one), assume that one of | |
| // the target dependencies, OBJECT_DEPENDS or header file custom commands | |
| // will rebuild the file. | |
| if (source->GetIsGenerated() && | |
| !source->GetPropertyAsBool("__CMAKE_GENERATED_BY_CMAKE") && | |
| !source->GetCustomCommand() && | |
| !this->GetGlobalGenerator()->HasCustomCommandOutput(sourceFilePath)) { | |
| this->GetGlobalGenerator()->AddAssumedSourceDependencies( | |
| sourceFilePath, objBuild.OrderOnlyDeps); | |
| } | |
| // For some cases we scan to dynamically discover dependencies. | |
| bool const compilationPreprocesses = | |
| !this->NeedExplicitPreprocessing(language); | |
| std::string modmapFormat; | |
| if (needDyndep) { | |
| std::string const modmapFormatVar = | |
| cmStrCat("CMAKE_", language, "_MODULE_MAP_FORMAT"); | |
| modmapFormat = this->Makefile->GetSafeDefinition(modmapFormatVar); | |
| } | |
| if (needDyndep) { | |
| // If source/target has preprocessing turned off, we still need to | |
| // generate an explicit dependency step | |
| auto const srcpp = source->GetSafeProperty("Fortran_PREPROCESS"); | |
| cmOutputConverter::FortranPreprocess preprocess = | |
| cmOutputConverter::GetFortranPreprocess(srcpp); | |
| if (preprocess == cmOutputConverter::FortranPreprocess::Unset) { | |
| auto const& tgtpp = | |
| this->GeneratorTarget->GetSafeProperty("Fortran_PREPROCESS"); | |
| preprocess = cmOutputConverter::GetFortranPreprocess(tgtpp); | |
| } | |
| bool const compilePP = !compilationPreprocesses && | |
| (preprocess != cmOutputConverter::FortranPreprocess::NotNeeded); | |
| bool const compilePPWithDefines = | |
| compilePP && this->CompileWithDefines(language); | |
| std::string scanRuleName; | |
| std::string ppFileName; | |
| if (compilePP) { | |
| scanRuleName = this->LanguagePreprocessAndScanRule(language, config); | |
| ppFileName = this->ConvertToNinjaPath( | |
| this->GetPreprocessedFilePath(source, config)); | |
| } else { | |
| scanRuleName = this->LanguageScanRule(language, config); | |
| ppFileName = cmStrCat(objectFileName, ".ddi.i"); | |
| } | |
| cmNinjaBuild ppBuild = GetScanBuildStatement( | |
| scanRuleName, ppFileName, compilePP, compilePPWithDefines, | |
| compilationPreprocesses, objBuild, vars, objectFileName, this); | |
| if (compilePP) { | |
| // In case compilation requires flags that are incompatible with | |
| // preprocessing, include them here. | |
| std::string const& postFlag = this->Makefile->GetSafeDefinition( | |
| cmStrCat("CMAKE_", language, "_POSTPROCESS_FLAG")); | |
| this->LocalGenerator->AppendFlags(vars["FLAGS"], postFlag); | |
| // Prepend source file's original directory as an include directory | |
| // so e.g. Fortran INCLUDE statements can look for files in it. | |
| std::vector<std::string> sourceDirectory; | |
| sourceDirectory.push_back( | |
| cmSystemTools::GetParentDirectory(source->GetFullPath())); | |
| std::string sourceDirectoryFlag = this->LocalGenerator->GetIncludeFlags( | |
| sourceDirectory, this->GeneratorTarget, language, config, false); | |
| vars["INCLUDES"] = cmStrCat(sourceDirectoryFlag, ' ', vars["INCLUDES"]); | |
| } | |
| ScanningFiles scanningFiles; | |
| if (firstForConfig) { | |
| scanningFiles.ScanningOutput = cmStrCat(objectFileName, ".ddi"); | |
| } | |
| this->addPoolNinjaVariable("JOB_POOL_COMPILE", config, | |
| this->GetGeneratorTarget(), source, | |
| ppBuild.Variables); | |
| this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig), | |
| ppBuild, commandLineLengthLimit); | |
| std::string const dyndep = this->GetDyndepFilePath(language, config); | |
| objBuild.OrderOnlyDeps.push_back(dyndep); | |
| vars["dyndep"] = dyndep; | |
| if (!modmapFormat.empty()) { | |
| // XXX(modmap): If changing this path construction, change | |
| // `cmGlobalNinjaGenerator::WriteDyndep` and | |
| // `cmNinjaTargetGenerator::ExportObjectCompileCommand` to expect the | |
| // corresponding file path. | |
| std::string ddModmapFile = cmStrCat(objectFileName, ".modmap"); | |
| vars["DYNDEP_MODULE_MAP_FILE"] = | |
| this->ConvertToOutputFormatForShell(ddModmapFile); | |
| objBuild.ImplicitDeps.push_back(ddModmapFile); | |
| scanningFiles.ModuleMapFile = std::move(ddModmapFile); | |
| } | |
| if (!scanningFiles.IsEmpty()) { | |
| this->Configs[config].ScanningInfo[language].emplace_back(scanningFiles); | |
| } | |
| } | |
| this->EnsureParentDirectoryExists(objectFileName); | |
| vars["OBJECT_DIR"] = this->ConvertToOutputFormatForShell(objectDir); | |
| vars["TARGET_SUPPORT_DIR"] = | |
| this->ConvertToOutputFormatForShell(targetSupportDir); | |
| vars["OBJECT_FILE_DIR"] = this->ConvertToOutputFormatForShell(objectFileDir); | |
| this->addPoolNinjaVariable("JOB_POOL_COMPILE", config, | |
| this->GetGeneratorTarget(), source, vars); | |
| if (!pchSources.empty() && | |
| !((fileSet && fileSet->GetProperty("SKIP_PRECOMPILE_HEADERS")) || | |
| source->GetProperty("SKIP_PRECOMPILE_HEADERS"))) { | |
| auto pchIt = pchSources.find(source->GetFullPath()); | |
| if (pchIt != pchSources.end()) { | |
| this->addPoolNinjaVariable("JOB_POOL_PRECOMPILE_HEADER", config, | |
| this->GetGeneratorTarget(), nullptr, vars); | |
| } | |
| } | |
| objBuild.RspFile = cmStrCat(objectFileName, ".rsp"); | |
| if (language == "ISPC") { | |
| std::string const& objectName = | |
| this->GeneratorTarget->GetObjectName(source); | |
| std::string ispcSource = | |
| cmSystemTools::GetFilenameWithoutLastExtension(objectName); | |
| ispcSource = cmSystemTools::GetFilenameWithoutLastExtension(ispcSource); | |
| cmValue ispcSuffixProp = | |
| this->GeneratorTarget->GetProperty("ISPC_HEADER_SUFFIX"); | |
| assert(ispcSuffixProp); | |
| std::string ispcHeaderDirectory = | |
| this->GeneratorTarget->GetObjectDirectory(config); | |
| if (cmValue prop = | |
| this->GeneratorTarget->GetProperty("ISPC_HEADER_DIRECTORY")) { | |
| ispcHeaderDirectory = | |
| cmStrCat(this->LocalGenerator->GetBinaryDirectory(), '/', *prop); | |
| } | |
| std::string ispcHeader = | |
| cmStrCat(ispcHeaderDirectory, '/', ispcSource, *ispcSuffixProp); | |
| ispcHeader = this->ConvertToNinjaPath(ispcHeader); | |
| // Make sure ninja knows what command generates the header | |
| objBuild.ImplicitOuts.push_back(ispcHeader); | |
| // Make sure ninja knows how to clean the generated header | |
| this->GetGlobalGenerator()->AddAdditionalCleanFile(ispcHeader, config); | |
| auto ispcSuffixes = | |
| detail::ComputeISPCObjectSuffixes(this->GeneratorTarget); | |
| if (ispcSuffixes.size() > 1) { | |
| std::string rootObjectDir = | |
| this->GeneratorTarget->GetObjectDirectory(config); | |
| auto ispcSideEffectObjects = detail::ComputeISPCExtraObjects( | |
| objectName, rootObjectDir, ispcSuffixes); | |
| for (auto sideEffect : ispcSideEffectObjects) { | |
| sideEffect = this->ConvertToNinjaPath(sideEffect); | |
| objBuild.ImplicitOuts.emplace_back(sideEffect); | |
| this->GetGlobalGenerator()->AddAdditionalCleanFile(sideEffect, config); | |
| } | |
| } | |
| vars["ISPC_HEADER_FILE"] = this->ConvertToOutputFormatForShell(ispcHeader); | |
| } else { | |
| auto headers = this->GeneratorTarget->GetGeneratedISPCHeaders(config); | |
| if (!headers.empty()) { | |
| std::transform(headers.begin(), headers.end(), headers.begin(), | |
| this->MapToNinjaPath()); | |
| objBuild.OrderOnlyDeps.insert(objBuild.OrderOnlyDeps.end(), | |
| headers.begin(), headers.end()); | |
| } | |
| } | |
| if (language == "Rust") { | |
| cmValue const rustEmit = source->GetRustEmitProperty(); | |
| vars["RUST_EMIT"] = rustEmit; | |
| } | |
| if (language == "Swift") { | |
| this->EmitSwiftDependencyInfo(source, config); | |
| } else { | |
| this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig), | |
| objBuild, commandLineLengthLimit); | |
| } | |
| if (cmValue objectOutputs = source->GetProperty("OBJECT_OUTPUTS")) { | |
| std::string evaluatedObjectOutputs = cmGeneratorExpression::Evaluate( | |
| *objectOutputs, this->LocalGenerator, config); | |
| if (!evaluatedObjectOutputs.empty()) { | |
| cmNinjaBuild build("phony"); | |
| build.Comment = "Additional output files."; | |
| build.Outputs = cmList{ evaluatedObjectOutputs }.data(); | |
| std::transform(build.Outputs.begin(), build.Outputs.end(), | |
| build.Outputs.begin(), this->MapToNinjaPath()); | |
| build.ExplicitDeps = objBuild.Outputs; | |
| this->GetGlobalGenerator()->WriteBuild( | |
| this->GetImplFileStream(fileConfig), build); | |
| } | |
| } | |
| } | |
| void cmNinjaTargetGenerator::WriteCxxModuleBmiBuildStatement( | |
| cmSourceFile const* source, std::string const& config, | |
| std::string const& fileConfig, bool firstForConfig) | |
| { | |
| std::string const language = source->GetLanguage(); | |
| if (language != "CXX"_s) { | |
| this->GetMakefile()->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat("Source file '", source->GetFullPath(), "' of target '", | |
| this->GetTargetName(), "' is a '", language, | |
| "' source but must be 'CXX' in order to have a BMI build " | |
| "statement generated.")); | |
| return; | |
| } | |
| std::string const sourceFilePath = this->GetCompiledSourceNinjaPath(source); | |
| std::string const targetSupportDir = | |
| this->ConvertToNinjaPath(this->GeneratorTarget->GetCMFSupportDirectory()); | |
| std::string const bmiDir = this->ConvertToNinjaPath( | |
| cmStrCat(this->GeneratorTarget->GetSupportDirectory(), | |
| this->GetGlobalGenerator()->ConfigDirectory(config))); | |
| std::string const bmiFileName = | |
| this->ConvertToNinjaPath(this->GetBmiFilePath(source, config)); | |
| std::string const bmiFileDir = cmSystemTools::GetFilenamePath(bmiFileName); | |
| int const commandLineLengthLimit = this->ForceResponseFile() ? -1 : 0; | |
| cmNinjaBuild bmiBuild( | |
| this->LanguageCompilerRule(language, config, WithScanning::Yes)); | |
| cmNinjaVars& vars = bmiBuild.Variables; | |
| vars["FLAGS"] = | |
| this->ComputeFlagsForObject(source, language, config, bmiFileName); | |
| vars["DEFINES"] = this->ComputeDefines(source, language, config); | |
| vars["INCLUDES"] = this->ComputeIncludes(source, language, config); | |
| vars["CONFIG"] = config; | |
| if (this->GetMakefile()->GetSafeDefinition( | |
| cmStrCat("CMAKE_", language, "_DEPFILE_FORMAT")) != "msvc"_s) { | |
| bool replaceExt = false; | |
| if (!language.empty()) { | |
| std::string repVar = | |
| cmStrCat("CMAKE_", language, "_DEPFILE_EXTENSION_REPLACE"); | |
| replaceExt = this->Makefile->IsOn(repVar); | |
| } | |
| this->AddDepfileBinding( | |
| vars, | |
| replaceExt | |
| ? cmStrCat(bmiFileDir, '/', | |
| cmSystemTools::GetFilenameWithoutLastExtension(bmiFileName), | |
| ".d") | |
| : cmStrCat(bmiFileName, ".d")); | |
| } | |
| std::string d = | |
| this->GeneratorTarget->GetClangTidyExportFixesDirectory(language); | |
| if (!d.empty()) { | |
| this->GlobalCommonGenerator->AddClangTidyExportFixesDir(d); | |
| std::string fixesFile = | |
| this->GetClangTidyReplacementsFilePath(d, *source, config); | |
| this->GlobalCommonGenerator->AddClangTidyExportFixesFile(fixesFile); | |
| cmSystemTools::MakeDirectory(cmSystemTools::GetFilenamePath(fixesFile)); | |
| fixesFile = this->ConvertToNinjaPath(fixesFile); | |
| vars["CLANG_TIDY_EXPORT_FIXES"] = fixesFile; | |
| } | |
| this->SetMsvcTargetPdbVariable(vars, config); | |
| if (firstForConfig) { | |
| this->ExportObjectCompileCommand( | |
| language, sourceFilePath, bmiDir, targetSupportDir, bmiFileName, | |
| bmiFileDir, vars["FLAGS"], vars["DEFINES"], vars["INCLUDES"], | |
| vars["TARGET_COMPILE_PDB"], vars["TARGET_PDB"], config, | |
| WithScanning::Yes); | |
| } | |
| bmiBuild.Outputs.push_back(bmiFileName); | |
| bmiBuild.ExplicitDeps.push_back(sourceFilePath); | |
| std::vector<std::string> depList; | |
| bmiBuild.OrderOnlyDeps.push_back(this->OrderDependsTargetForTarget(config)); | |
| // For some cases we scan to dynamically discover dependencies. | |
| std::string modmapFormat; | |
| if (true) { | |
| std::string const modmapFormatVar = | |
| cmStrCat("CMAKE_", language, "_MODULE_MAP_FORMAT"); | |
| modmapFormat = this->Makefile->GetSafeDefinition(modmapFormatVar); | |
| } | |
| { | |
| bool const compilePPWithDefines = this->CompileWithDefines(language); | |
| std::string scanRuleName = this->LanguageScanRule(language, config); | |
| std::string ppFileName = cmStrCat(bmiFileName, ".ddi.i"); | |
| cmNinjaBuild ppBuild = GetScanBuildStatement( | |
| scanRuleName, ppFileName, false, compilePPWithDefines, true, bmiBuild, | |
| vars, bmiFileName, this); | |
| ScanningFiles scanningFiles; | |
| if (firstForConfig) { | |
| scanningFiles.ScanningOutput = cmStrCat(bmiFileName, ".ddi"); | |
| } | |
| this->addPoolNinjaVariable("JOB_POOL_COMPILE", config, | |
| this->GetGeneratorTarget(), source, | |
| ppBuild.Variables); | |
| this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig), | |
| ppBuild, commandLineLengthLimit); | |
| std::string const dyndep = this->GetDyndepFilePath(language, config); | |
| bmiBuild.OrderOnlyDeps.push_back(dyndep); | |
| vars["dyndep"] = dyndep; | |
| if (!modmapFormat.empty()) { | |
| std::string ddModmapFile = cmStrCat(bmiFileName, ".modmap"); | |
| vars["DYNDEP_MODULE_MAP_FILE"] = | |
| this->ConvertToOutputFormatForShell(ddModmapFile); | |
| scanningFiles.ModuleMapFile = std::move(ddModmapFile); | |
| } | |
| if (!scanningFiles.IsEmpty()) { | |
| this->Configs[config].ScanningInfo[language].emplace_back(scanningFiles); | |
| } | |
| } | |
| this->EnsureParentDirectoryExists(bmiFileName); | |
| vars["OBJECT_DIR"] = this->ConvertToOutputFormatForShell(bmiDir); | |
| vars["TARGET_SUPPORT_DIR"] = | |
| this->ConvertToOutputFormatForShell(targetSupportDir); | |
| vars["OBJECT_FILE_DIR"] = this->ConvertToOutputFormatForShell(bmiFileDir); | |
| this->addPoolNinjaVariable("JOB_POOL_COMPILE", config, | |
| this->GetGeneratorTarget(), source, vars); | |
| bmiBuild.RspFile = cmStrCat(bmiFileName, ".rsp"); | |
| this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig), | |
| bmiBuild, commandLineLengthLimit); | |
| } | |
| void cmNinjaTargetGenerator::WriteSwiftObjectBuildStatement( | |
| std::vector<cmSourceFile const*> const& sources, std::string const& config, | |
| std::string const& fileConfig, bool firstForConfig) | |
| { | |
| // Swift sources are compiled as a module, not individually like with C/C++. | |
| // Flags, header search paths, and definitions are passed to the entire | |
| // module build, but we still need to emit compile-commands for each source | |
| // file in order to support CMAKE_EXPORT_COMPILE_COMMANDS. | |
| // In whole-module mode, with a single thread, the Swift compiler will | |
| // only emit a single object file, but if more than one thread is specified, | |
| // or building in other modes, the compiler will emit multiple object files. | |
| // When building a single-output, we do not provide an output-file-map (OFM), | |
| // and instead pass `-o` to tell the compiler where to write the object. | |
| // When building multiple outputs, we provide an OFM to tell the compiler | |
| // where to put each object. | |
| // | |
| // | |
| // Per-Target (module): | |
| // - Flags | |
| // - Definitions | |
| // - Include paths | |
| // - (single-output) output object filename | |
| // - Swiftmodule (for importable targets), produced either by the compile | |
| // edge or by a separate emit-module edge | |
| // | |
| // Per-File: | |
| // - compile-command | |
| // - (multi-output) OFM data | |
| // - (multi-output) output object filename | |
| // | |
| // Note: Due to the differences in the build models, we are only able to | |
| // build the object build-graph if we know what mode the target is built in. | |
| // For that, we need the "NEW" behavior for CMP0157. Otherwise, we have to | |
| // fall back on the old "linker" build. Otherwise, this should be | |
| // indistinguishable from the old behavior. | |
| if (sources.empty()) { | |
| return; | |
| } | |
| cmSwiftCompileMode compileMode; | |
| if (cm::optional<cmSwiftCompileMode> optionalCompileMode = | |
| this->LocalGenerator->GetSwiftCompileMode(this->GeneratorTarget, | |
| config)) { | |
| compileMode = *optionalCompileMode; | |
| } else { | |
| // CMP0157 is not NEW, bailing early! | |
| return; | |
| } | |
| std::string const language = "Swift"; | |
| cmGeneratorTarget const& target = *this->GeneratorTarget; | |
| std::string const moduleName = target.GetSwiftModuleName(); | |
| std::string const moduleFilePath = | |
| this->ConvertToNinjaPath(target.GetSwiftModulePath(config)); | |
| std::string const objectDir = this->ConvertToNinjaPath( | |
| cmStrCat(this->GeneratorTarget->GetSupportDirectory(), | |
| this->GetGlobalGenerator()->ConfigDirectory(config))); | |
| std::string const moduleObjectFilename = this->ConvertToNinjaPath(cmStrCat( | |
| objectDir, '/', moduleName, | |
| this->GetGlobalGenerator()->GetLanguageOutputExtension(language))); | |
| // Does this swift target emit a module file for importing into other | |
| // targets? | |
| auto isImportableTarget = [](cmGeneratorTarget const& tgt) -> bool { | |
| // Everything except for executables that don't export anything should emit | |
| // some way to import them. | |
| if (tgt.GetType() == cm::TargetType::EXECUTABLE) { | |
| return tgt.IsExecutableWithExports(); | |
| } | |
| return true; | |
| }; | |
| bool const targetIsImportable = isImportableTarget(target); | |
| // num-threads 0 && wmo -> one object file for the entire module | |
| bool const isSingleOutput = [this, compileMode]() -> bool { | |
| bool isMultiThread = false; | |
| if (cmValue numThreadStr = | |
| this->GetMakefile()->GetDefinition("CMAKE_Swift_NUM_THREADS")) { | |
| unsigned long numThreads; | |
| cmStrToULong(*numThreadStr, &numThreads); | |
| // numThreads == 1 is multi-threaded according to swiftc | |
| isMultiThread = numThreads > 0; | |
| } | |
| return !isMultiThread && compileMode == cmSwiftCompileMode::Wholemodule; | |
| }(); | |
| bool const emitModuleSeparately = [&]() -> bool { | |
| cmValue prop = | |
| this->GeneratorTarget->GetProperty("Swift_SEPARATE_MODULE_EMISSION"); | |
| if (prop) { | |
| return prop.IsOn(); | |
| } | |
| return this->GeneratorTarget->GetPolicyStatusCMP0215() == cmPolicies::NEW; | |
| }(); | |
| // 1. Common: | |
| // Variables: | |
| // - Flags: | |
| // - module-name | |
| // - module-link-name | |
| // - parse as library | |
| // - Swift_FLAGS | |
| // - output-file-map | |
| // - Defines (all) | |
| // - Include directories (all) | |
| // - restat: 1 | |
| // Source List | |
| // | |
| // 2. Module Build Specific: | |
| // Variables: | |
| // - Flags | |
| // - emit-module-path | |
| // - module build description | |
| // - Module response file | |
| // - Module Outputs | |
| // | |
| // 3. Object Build Specific: | |
| // Variables: | |
| // - Flags: | |
| // - -o | |
| // - -c | |
| // - Object response file | |
| // - Object Outputs | |
| // | |
| //// Common variables | |
| // | |
| cmNinjaVars commonVariables; | |
| cmNinjaDeps commonExplicitDeps; | |
| cmNinjaDeps objectOutputs; | |
| { | |
| this->LocalGenerator->AppendFlags(commonVariables["FLAGS"], | |
| cmStrCat("-module-name ", moduleName)); | |
| if (target.GetType() != cm::TargetType::EXECUTABLE) { | |
| std::string const libraryLinkNameFlag = "-module-link-name "; | |
| std::string const libraryLinkName = | |
| this->GetGeneratorTarget()->GetLibraryNames(config).Base; | |
| this->LocalGenerator->AppendFlags( | |
| commonVariables["FLAGS"], | |
| cmStrCat(libraryLinkNameFlag, libraryLinkName)); | |
| this->LocalGenerator->AppendFlags(commonVariables["FLAGS"], | |
| "-parse-as-library"); | |
| } | |
| if (target.GetType() == cm::TargetType::STATIC_LIBRARY) { | |
| this->LocalGenerator->AppendFlags(commonVariables["FLAGS"], "-static"); | |
| } | |
| this->LocalGenerator->AppendFlags(commonVariables["FLAGS"], | |
| this->GetFlags(language, config)); | |
| commonVariables.emplace("DEFINES", this->GetDefines(language, config)); | |
| commonVariables.emplace("INCLUDES", this->GetIncludes(language, config)); | |
| commonVariables.emplace("CONFIG", config); | |
| commonVariables.emplace("restat", "1"); | |
| for (cmSourceFile const* sf : sources) { | |
| std::string const sourceFilePath = this->GetCompiledSourceNinjaPath(sf); | |
| commonExplicitDeps.push_back(sourceFilePath); | |
| if (!isSingleOutput) { | |
| std::string const objectFilePath = | |
| this->ConvertToNinjaPath(this->GetObjectFilePath(sf, config)); | |
| this->EnsureParentDirectoryExists(objectFilePath); | |
| objectOutputs.push_back(objectFilePath); | |
| this->Configs[config].Objects.push_back(objectFilePath); | |
| this->EmitSwiftDependencyInfo(sf, config); | |
| } | |
| } | |
| if (isSingleOutput) { | |
| objectOutputs.push_back(moduleObjectFilename); | |
| this->Configs[config].Objects.push_back(moduleObjectFilename); | |
| } else { | |
| this->GenerateSwiftOutputFileMap(config, commonVariables["FLAGS"]); | |
| } | |
| } | |
| // | |
| //// Module build | |
| // | |
| cmNinjaVars moduleVariables; | |
| cmNinjaDeps moduleOutputs; | |
| cmNinjaDeps importedModuleDeps; | |
| { | |
| for (cmTargetDepend const& dep : | |
| this->GetGlobalGenerator()->GetTargetDirectDepends(&target)) { | |
| if (!dep->IsLanguageUsed("Swift", config)) { | |
| continue; | |
| } | |
| // If the dependency emits a swiftmodule, add a dependency edge on that | |
| // swiftmodule to the ninja build graph. | |
| if (isImportableTarget(*dep)) { | |
| std::string const depModuleFilepath = | |
| this->ConvertToNinjaPath(dep->GetSwiftModulePath(config)); | |
| importedModuleDeps.push_back(depModuleFilepath); | |
| } | |
| } | |
| if (targetIsImportable) { | |
| this->LocalGenerator->AppendFlags(moduleVariables["FLAGS"], | |
| "-emit-module"); | |
| this->Configs[config].SwiftModuleOutput = moduleFilePath; | |
| std::string const moduleOutputPath = | |
| this->LocalGenerator->ConvertToOutputFormat(moduleFilePath, | |
| cmOutputConverter::SHELL); | |
| this->LocalGenerator->AppendFlags( | |
| moduleVariables["FLAGS"], | |
| cmStrCat("-emit-module-path ", moduleOutputPath)); | |
| this->EnsureParentDirectoryExists(moduleFilePath); | |
| moduleOutputs.push_back(moduleFilePath); | |
| if (emitModuleSeparately) { | |
| cmNinjaBuild moduleBuild( | |
| this->LanguageCompilerRule(language, config, WithScanning::No)); | |
| moduleBuild.Variables = moduleVariables; | |
| this->LocalGenerator->AppendFlags(moduleBuild.Variables["FLAGS"], | |
| commonVariables["FLAGS"]); | |
| this->LocalGenerator->AppendFlags(moduleBuild.Variables["DEFINES"], | |
| commonVariables["DEFINES"]); | |
| this->LocalGenerator->AppendFlags(moduleBuild.Variables["INCLUDES"], | |
| commonVariables["INCLUDES"]); | |
| moduleBuild.Variables.insert(commonVariables.begin(), | |
| commonVariables.end()); | |
| moduleBuild.ExplicitDeps = commonExplicitDeps; | |
| moduleBuild.ImplicitDeps = importedModuleDeps; | |
| moduleBuild.Variables.emplace( | |
| "description", | |
| cmStrCat("Building Swift Module '", moduleName, "' with ", | |
| sources.size(), | |
| sources.size() == 1 ? " source" : " sources")); | |
| moduleBuild.Outputs = moduleOutputs; | |
| moduleBuild.RspFile = cmStrCat(moduleFilePath, ".rsp"); | |
| moduleBuild.OrderOnlyDeps.push_back( | |
| this->OrderDependsTargetForTarget(config)); | |
| this->GetGlobalGenerator()->WriteBuild( | |
| this->GetImplFileStream(fileConfig), moduleBuild, | |
| this->ForceResponseFile() ? -1 : 0); | |
| } | |
| } | |
| } | |
| // | |
| //// Object build | |
| // | |
| { | |
| cmNinjaBuild objectBuild( | |
| this->LanguageCompilerRule(language, config, WithScanning::No)); | |
| cmNinjaVars& objectVariables = objectBuild.Variables; | |
| this->LocalGenerator->AppendFlags(objectVariables["FLAGS"], "-c"); | |
| if (isSingleOutput) { | |
| this->LocalGenerator->AppendFlags(objectVariables["FLAGS"], | |
| cmStrCat("-o ", moduleObjectFilename)); | |
| } | |
| this->LocalGenerator->AppendFlags(objectBuild.Variables["FLAGS"], | |
| commonVariables["FLAGS"]); | |
| this->LocalGenerator->AppendFlags(objectBuild.Variables["DEFINES"], | |
| commonVariables["DEFINES"]); | |
| this->LocalGenerator->AppendFlags(objectBuild.Variables["INCLUDES"], | |
| commonVariables["INCLUDES"]); | |
| objectBuild.Variables.insert(commonVariables.begin(), | |
| commonVariables.end()); | |
| objectVariables.emplace( | |
| "description", | |
| cmStrCat("Building Swift Module '", moduleName, "' objects with ", | |
| sources.size(), sources.size() == 1 ? " source" : " sources")); | |
| objectBuild.ExplicitDeps = commonExplicitDeps; | |
| objectBuild.ImplicitDeps = importedModuleDeps; | |
| objectBuild.Outputs = objectOutputs; | |
| objectBuild.RspFile = cmStrCat(moduleObjectFilename, ".swift.rsp"); | |
| objectBuild.OrderOnlyDeps.push_back( | |
| this->OrderDependsTargetForTarget(config)); | |
| if (emitModuleSeparately && targetIsImportable) { | |
| // Both edges share the same -output-file-map; serialize the compile | |
| // edge after emit-module so they do not race on the module .swiftdeps. | |
| objectBuild.OrderOnlyDeps.push_back(moduleFilePath); | |
| } else { | |
| this->LocalGenerator->AppendFlags(objectBuild.Variables["FLAGS"], | |
| moduleVariables["FLAGS"]); | |
| objectBuild.Outputs.insert(objectBuild.Outputs.end(), | |
| moduleOutputs.begin(), moduleOutputs.end()); | |
| } | |
| if (firstForConfig) { | |
| this->ExportSwiftObjectCompileCommand( | |
| sources, moduleObjectFilename, | |
| cmStrCat(commonVariables["FLAGS"], ' ', moduleVariables["FLAGS"], ' ', | |
| objectVariables["FLAGS"]), | |
| cmStrCat(commonVariables["DEFINES"], ' ', moduleVariables["DEFINES"], | |
| ' ', objectVariables["DEFINES"]), | |
| cmStrCat(commonVariables["INCLUDES"], ' ', moduleVariables["INCLUDES"], | |
| ' ', objectVariables["INCLUDES"]), | |
| config, isSingleOutput); | |
| } | |
| this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig), | |
| objectBuild, | |
| this->ForceResponseFile() ? -1 : 0); | |
| } | |
| } | |
| void cmNinjaTargetGenerator::WriteTargetDependInfo(std::string const& lang, | |
| std::string const& config) | |
| { | |
| Json::Value tdi(Json::objectValue); | |
| tdi["language"] = lang; | |
| tdi["compiler-id"] = this->Makefile->GetSafeDefinition( | |
| cmStrCat("CMAKE_", lang, "_COMPILER_ID")); | |
| tdi["compiler-simulate-id"] = this->Makefile->GetSafeDefinition( | |
| cmStrCat("CMAKE_", lang, "_SIMULATE_ID")); | |
| tdi["compiler-frontend-variant"] = this->Makefile->GetSafeDefinition( | |
| cmStrCat("CMAKE_", lang, "_COMPILER_FRONTEND_VARIANT")); | |
| std::string mod_dir; | |
| if (lang == "Fortran") { | |
| mod_dir = this->GeneratorTarget->GetFortranModuleDirectory( | |
| this->Makefile->GetHomeOutputDirectory()); | |
| } else if (lang == "CXX") { | |
| mod_dir = this->GetGlobalGenerator()->ExpandCFGIntDir( | |
| cmSystemTools::CollapseFullPath(this->GeneratorTarget->ObjectDirectory), | |
| config); | |
| } | |
| if (mod_dir.empty()) { | |
| mod_dir = this->Makefile->GetCurrentBinaryDirectory(); | |
| } | |
| tdi["module-dir"] = mod_dir; | |
| if (lang == "Fortran") { | |
| tdi["submodule-sep"] = | |
| this->Makefile->GetSafeDefinition("CMAKE_Fortran_SUBMODULE_SEP"); | |
| tdi["submodule-ext"] = | |
| this->Makefile->GetSafeDefinition("CMAKE_Fortran_SUBMODULE_EXT"); | |
| tdi["building-intrinsic-modules"] = | |
| this->GeneratorTarget->IsFortranBuildingIntrinsicModules(); | |
| } else if (lang == "CXX") { | |
| // No extra information necessary. | |
| } | |
| tdi["dir-cur-bld"] = this->Makefile->GetCurrentBinaryDirectory(); | |
| tdi["dir-cur-src"] = this->Makefile->GetCurrentSourceDirectory(); | |
| tdi["dir-top-bld"] = this->Makefile->GetHomeOutputDirectory(); | |
| tdi["dir-top-src"] = this->Makefile->GetHomeDirectory(); | |
| Json::Value& tdi_include_dirs = tdi["include-dirs"] = Json::arrayValue; | |
| std::vector<std::string> includes; | |
| this->LocalGenerator->GetIncludeDirectories(includes, this->GeneratorTarget, | |
| lang, config); | |
| for (std::string const& i : includes) { | |
| // Convert the include directories the same way we do for -I flags. | |
| // See upstream ninja issue 1251. | |
| tdi_include_dirs.append(this->ConvertToNinjaPath(i)); | |
| } | |
| Json::Value& tdi_linked_target_dirs = tdi["linked-target-dirs"] = | |
| Json::arrayValue; | |
| auto const linked_directories = | |
| this->GetLinkedTargetDirectories(lang, config); | |
| for (std::string const& l : linked_directories.Direct) { | |
| tdi_linked_target_dirs.append(l); | |
| } | |
| Json::Value& tdi_forward_modules_from_target_dirs = | |
| tdi["forward-modules-from-target-dirs"] = Json::arrayValue; | |
| for (std::string const& l : linked_directories.Forward) { | |
| tdi_forward_modules_from_target_dirs.append(l); | |
| } | |
| // Record the native target support directory for non-imported synthetic | |
| // targets | |
| if (this->GeneratorTarget->IsSynthetic()) { | |
| if (cmGeneratorTarget* nativeGT = | |
| this->LocalGenerator->FindGeneratorTargetToUse( | |
| this->GeneratorTarget->Target->GetTemplateName())) { | |
| if (!nativeGT->IsImported()) { | |
| std::string nativeDir = nativeGT->GetSupportDirectory(); | |
| if (this->GetGlobalGenerator()->IsMultiConfig()) { | |
| nativeDir = cmStrCat(std::move(nativeDir), '/', config); | |
| } | |
| tdi["native-target-dir"] = nativeDir; | |
| } | |
| } | |
| } | |
| cmDyndepGeneratorCallbacks cb; | |
| cb.ObjectFilePath = [this](cmSourceFile const* sf, std::string const& cnf) { | |
| return this->GetObjectFilePath(sf, cnf); | |
| }; | |
| cb.BmiFilePath = [this](cmSourceFile const* sf, std::string const& cnf) { | |
| return this->GetBmiFilePath(sf, cnf); | |
| }; | |
| #if !defined(CMAKE_BOOTSTRAP) | |
| cmDyndepCollation::AddCollationInformation(tdi, this->GeneratorTarget, | |
| config, cb); | |
| #endif | |
| // Collect interface objects only for C++ collation. | |
| if (lang == "CXX") { | |
| auto const& synthDeps = this->GeneratorTarget->GetSyntheticDeps(config); | |
| if (!synthDeps.empty()) { | |
| bool multiConfig = this->GetGlobalGenerator()->IsMultiConfig(); | |
| std::string configDir = multiConfig ? cmStrCat('/', config) : ""; | |
| std::string configUpper = cmSystemTools::UpperCase(config); | |
| std::string modulesProp = cmStrCat("IMPORTED_CXX_MODULES_", configUpper); | |
| Json::Value interfaceObjects(Json::objectValue); | |
| Json::Value moduleObjects(Json::objectValue); | |
| bool hasInterfaceObjects = false; | |
| for (auto const& pair : synthDeps) { | |
| auto const* nativeGT = pair.first; | |
| if (!nativeGT->Target->CxxModuleNeedsInterfaceObjects()) { | |
| continue; | |
| } | |
| hasInterfaceObjects = true; | |
| for (auto const* synthGT : pair.second) { | |
| std::string objDir = | |
| cmStrCat(synthGT->GetSupportDirectory(), configDir); | |
| cmValue importedModules = synthGT->Target->GetProperty(modulesProp); | |
| if (!importedModules) { | |
| continue; | |
| } | |
| for (auto const& entry : cmList{ *importedModules }) { | |
| auto nameSep = entry.find('='); | |
| if (nameSep == std::string::npos) { | |
| continue; | |
| } | |
| auto moduleName = entry.substr(0, nameSep); | |
| auto nameAndPath = entry.substr(nameSep + 1); | |
| auto commaSep = nameAndPath.find(','); | |
| std::string sourcePath = commaSep == std::string::npos | |
| ? nameAndPath | |
| : nameAndPath.substr(0, commaSep); | |
| cmSourceFile const* sf = synthGT->Makefile->GetSource( | |
| sourcePath, cmSourceFileLocationKind::Known); | |
| if (!sf) { | |
| continue; | |
| } | |
| std::string const& objName = synthGT->GetObjectName(sf); | |
| std::string objPath = cmStrCat(objDir, '/', objName); | |
| moduleObjects[moduleName] = objPath; | |
| } | |
| } | |
| } | |
| if (hasInterfaceObjects) { | |
| std::string rspPath = | |
| cmStrCat(this->GeneratorTarget->GetSupportDirectory(), configDir, | |
| "/CXXInterfaceObjects.rsp"); | |
| interfaceObjects["rsp-file"] = rspPath; | |
| interfaceObjects["module-objects"] = moduleObjects; | |
| tdi["cxx-interface-objects"] = interfaceObjects; | |
| } | |
| } | |
| } | |
| std::string const tdin = this->GetTargetDependInfoPath(lang, config); | |
| cmGeneratedFileStream tdif(tdin); | |
| tdif.SetCopyIfDifferent(true); | |
| tdif << tdi; | |
| } | |
| void cmNinjaTargetGenerator::EmitSwiftDependencyInfo( | |
| cmSourceFile const* source, std::string const& config) | |
| { | |
| std::string const sourceFilePath = this->GetCompiledSourceNinjaPath(source); | |
| std::string const objectFilePath = | |
| this->ConvertToNinjaPath(this->GetObjectFilePath(source, config)); | |
| std::string const swiftDepsPath = [source, objectFilePath]() -> std::string { | |
| if (cmValue name = source->GetProperty("Swift_DEPENDENCIES_FILE")) { | |
| return *name; | |
| } | |
| return cmStrCat(objectFilePath, ".swiftdeps"); | |
| }(); | |
| std::string const swiftDiaPath = [source, objectFilePath]() -> std::string { | |
| if (cmValue name = source->GetProperty("Swift_DIAGNOSTICS_FILE")) { | |
| return *name; | |
| } | |
| return cmStrCat(objectFilePath, ".dia"); | |
| }(); | |
| std::string const makeDepsPath = [this, source, config]() -> std::string { | |
| cmLocalNinjaGenerator const* local = this->GetLocalGenerator(); | |
| std::string const objectFileName = | |
| this->ConvertToNinjaPath(this->GetObjectFilePath(source, config)); | |
| std::string const objectFileDir = | |
| cmSystemTools::GetFilenamePath(objectFileName); | |
| if (this->Makefile->IsOn("CMAKE_Swift_DEPFLE_EXTNSION_REPLACE")) { | |
| std::string dependFileName = cmStrCat( | |
| cmSystemTools::GetFilenameWithoutLastExtension(objectFileName), ".d"); | |
| return local->ConvertToOutputFormat( | |
| cmStrCat(objectFileDir, '/', dependFileName), | |
| cmOutputConverter::SHELL); | |
| } | |
| return local->ConvertToOutputFormat(cmStrCat(objectFileName, ".d"), | |
| cmOutputConverter::SHELL); | |
| }(); | |
| // build the source file mapping | |
| // https://github.com/apple/swift/blob/master/docs/Driver.md#output-file-maps | |
| Json::Value entry = Json::Value(Json::objectValue); | |
| entry["object"] = objectFilePath; | |
| entry["dependencies"] = makeDepsPath; | |
| entry["swift-dependencies"] = swiftDepsPath; | |
| entry["diagnostics"] = swiftDiaPath; | |
| this->Configs[config].SwiftOutputMap[sourceFilePath] = entry; | |
| } | |
| void cmNinjaTargetGenerator::ExportObjectCompileCommand( | |
| std::string const& language, std::string const& sourceFileName, | |
| std::string const& objectDir, std::string const& targetSupportDir, | |
| std::string const& objectFileName, std::string const& objectFileDir, | |
| std::string const& flags, std::string const& defines, | |
| std::string const& includes, std::string const& targetCompilePdb, | |
| std::string const& targetPdb, std::string const& outputConfig, | |
| WithScanning withScanning) | |
| { | |
| if (!this->GeneratorTarget->GetPropertyAsBool("EXPORT_COMPILE_COMMANDS")) { | |
| return; | |
| } | |
| cmRulePlaceholderExpander::RuleVariables compileObjectVars; | |
| compileObjectVars.Language = language.c_str(); | |
| std::string escapedSourceFileName = sourceFileName; | |
| if (!cmSystemTools::FileIsFullPath(sourceFileName)) { | |
| escapedSourceFileName = | |
| cmSystemTools::CollapseFullPath(escapedSourceFileName, | |
| this->GetGlobalGenerator() | |
| ->GetCMakeInstance() | |
| ->GetHomeOutputDirectory()); | |
| } | |
| escapedSourceFileName = this->LocalGenerator->ConvertToOutputFormat( | |
| escapedSourceFileName, cmOutputConverter::SHELL); | |
| std::string fullFlags = flags; | |
| if (withScanning == WithScanning::Yes) { | |
| std::string const modmapFormatVar = | |
| cmStrCat("CMAKE_", language, "_MODULE_MAP_FORMAT"); | |
| std::string const modmapFormat = | |
| this->Makefile->GetSafeDefinition(modmapFormatVar); | |
| if (!modmapFormat.empty()) { | |
| std::string modmapFlags = this->GetMakefile()->GetRequiredDefinition( | |
| cmStrCat("CMAKE_", language, "_MODULE_MAP_FLAG")); | |
| // XXX(modmap): If changing this path construction, change | |
| // `cmGlobalNinjaGenerator::WriteDyndep` and | |
| // `cmNinjaTargetGenerator::WriteObjectBuildStatement` to expect the | |
| // corresponding file path. | |
| cmSystemTools::ReplaceString(modmapFlags, "<MODULE_MAP_FILE>", | |
| cmStrCat(objectFileName, ".modmap")); | |
| fullFlags = cmStrCat(std::move(fullFlags), ' ', modmapFlags); | |
| } | |
| } | |
| compileObjectVars.Source = escapedSourceFileName.c_str(); | |
| std::string escapedObjectFileName = | |
| this->LocalGenerator->ConvertToOutputFormat(objectFileName, | |
| cmOutputConverter::SHELL); | |
| compileObjectVars.Object = escapedObjectFileName.c_str(); | |
| compileObjectVars.ObjectDir = objectDir.c_str(); | |
| compileObjectVars.TargetSupportDir = targetSupportDir.c_str(); | |
| compileObjectVars.ObjectFileDir = objectFileDir.c_str(); | |
| compileObjectVars.Flags = fullFlags.c_str(); | |
| compileObjectVars.Defines = defines.c_str(); | |
| compileObjectVars.Includes = includes.c_str(); | |
| compileObjectVars.TargetCompilePDB = targetCompilePdb.c_str(); | |
| compileObjectVars.TargetPDB = targetPdb.c_str(); | |
| // Rule for compiling object file. | |
| std::string cudaCompileMode; | |
| if (language == "CUDA") { | |
| if (this->GeneratorTarget->GetPropertyAsBool( | |
| "CUDA_SEPARABLE_COMPILATION")) { | |
| std::string const& rdcFlag = | |
| this->Makefile->GetRequiredDefinition("_CMAKE_CUDA_RDC_FLAG"); | |
| cudaCompileMode = cmStrCat(std::move(cudaCompileMode), rdcFlag, ' '); | |
| } | |
| static std::array<cm::string_view, 4> const compileModes{ | |
| { "PTX"_s, "CUBIN"_s, "FATBIN"_s, "OPTIX"_s } | |
| }; | |
| bool useNormalCompileMode = true; | |
| for (cm::string_view mode : compileModes) { | |
| auto propName = cmStrCat("CUDA_", mode, "_COMPILATION"); | |
| auto defName = cmStrCat("_CMAKE_CUDA_", mode, "_FLAG"); | |
| if (this->GeneratorTarget->GetPropertyAsBool(propName)) { | |
| std::string const& flag = | |
| this->Makefile->GetRequiredDefinition(defName); | |
| cudaCompileMode = cmStrCat(std::move(cudaCompileMode), flag); | |
| useNormalCompileMode = false; | |
| break; | |
| } | |
| } | |
| if (useNormalCompileMode) { | |
| std::string const& wholeFlag = | |
| this->Makefile->GetRequiredDefinition("_CMAKE_CUDA_WHOLE_FLAG"); | |
| cudaCompileMode = cmStrCat(std::move(cudaCompileMode), wholeFlag); | |
| } | |
| compileObjectVars.CudaCompileMode = cudaCompileMode.c_str(); | |
| } | |
| std::string const cmdVar = this->GetCompileTemplateVar(language); | |
| std::string const& compileCmd = | |
| this->Makefile->GetRequiredDefinition(cmdVar); | |
| cmList compileCmds(compileCmd); | |
| auto rulePlaceholderExpander = | |
| this->GetLocalGenerator()->CreateRulePlaceholderExpander(); | |
| for (auto& i : compileCmds) { | |
| // no launcher for CMAKE_EXPORT_COMPILE_COMMANDS | |
| rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(), i, | |
| compileObjectVars); | |
| } | |
| std::string cmdLine = this->GetLocalGenerator()->BuildCommandLine( | |
| compileCmds, outputConfig, outputConfig, {}, nullptr, | |
| cmLocalNinjaGenerator::CommandLineContext::CompileCommands); | |
| this->GetGlobalGenerator()->AddCXXCompileCommand(cmdLine, sourceFileName, | |
| objectFileName); | |
| } | |
| void cmNinjaTargetGenerator::ExportSwiftObjectCompileCommand( | |
| std::vector<cmSourceFile const*> const& moduleSourceFiles, | |
| std::string const& moduleObjectFilename, std::string const& flags, | |
| std::string const& defines, std::string const& includes, | |
| std::string const& outputConfig, bool singleOutput) | |
| { | |
| if (!this->GeneratorTarget->GetPropertyAsBool("EXPORT_COMPILE_COMMANDS")) { | |
| return; | |
| } | |
| auto escapeSourceFileName = [this](std::string srcFilename) -> std::string { | |
| if (!cmSystemTools::FileIsFullPath(srcFilename)) { | |
| srcFilename = | |
| cmSystemTools::CollapseFullPath(srcFilename, | |
| this->GetGlobalGenerator() | |
| ->GetCMakeInstance() | |
| ->GetHomeOutputDirectory()); | |
| } | |
| return this->LocalGenerator->ConvertToOutputFormat( | |
| srcFilename, cmOutputConverter::SHELL); | |
| }; | |
| auto escapedModuleObjectFilename = | |
| this->ConvertToNinjaPath(moduleObjectFilename); | |
| cmRulePlaceholderExpander::RuleVariables compileObjectVars; | |
| compileObjectVars.Language = "Swift"; | |
| compileObjectVars.Flags = flags.c_str(); | |
| compileObjectVars.Defines = defines.c_str(); | |
| compileObjectVars.Includes = includes.c_str(); | |
| // Build up the list of source files in the module | |
| std::vector<std::string> filenames; | |
| filenames.reserve(moduleSourceFiles.size()); | |
| for (cmSourceFile const* sf : moduleSourceFiles) { | |
| filenames.emplace_back( | |
| escapeSourceFileName(this->GetCompiledSourceNinjaPath(sf))); | |
| } | |
| // Note that `escapedSourceFilenames` must remain alive until the | |
| // compileObjectVars is consumed or Source will be a dangling pointer. | |
| std::string const escapedSourceFilenames = cmJoin(filenames, " "); | |
| compileObjectVars.Source = escapedSourceFilenames.c_str(); | |
| std::string const& compileCommand = | |
| this->Makefile->GetRequiredDefinition("CMAKE_Swift_COMPILE_OBJECT"); | |
| cmList compileCmds(compileCommand); | |
| auto rulePlaceholderExpander = | |
| this->GetLocalGenerator()->CreateRulePlaceholderExpander(); | |
| for (cmSourceFile const* sf : moduleSourceFiles) { | |
| std::string const sourceFilename = this->GetCompiledSourceNinjaPath(sf); | |
| std::string objectFilename = escapedModuleObjectFilename; | |
| if (!singleOutput) { | |
| // If it's not single-output, each source file gets a separate object | |
| objectFilename = | |
| this->ConvertToNinjaPath(this->GetObjectFilePath(sf, outputConfig)); | |
| } | |
| compileObjectVars.Objects = objectFilename.c_str(); | |
| for (std::string& cmd : compileCmds) { | |
| rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(), | |
| cmd, compileObjectVars); | |
| } | |
| std::string commandLine = this->GetLocalGenerator()->BuildCommandLine( | |
| compileCmds, outputConfig, outputConfig, {}, nullptr, | |
| cmLocalNinjaGenerator::CommandLineContext::CompileCommands); | |
| this->GetGlobalGenerator()->AddCXXCompileCommand( | |
| commandLine, sourceFilename, objectFilename); | |
| } | |
| } | |
| void cmNinjaTargetGenerator::AdditionalCleanFiles(std::string const& config) | |
| { | |
| if (cmValue prop_value = | |
| this->GeneratorTarget->GetProperty("ADDITIONAL_CLEAN_FILES")) { | |
| cmLocalNinjaGenerator* lg = this->LocalGenerator; | |
| cmList cleanFiles(cmGeneratorExpression::Evaluate(*prop_value, lg, config, | |
| this->GeneratorTarget)); | |
| std::string const& binaryDir = lg->GetCurrentBinaryDirectory(); | |
| cmGlobalNinjaGenerator* gg = lg->GetGlobalNinjaGenerator(); | |
| for (auto const& cleanFile : cleanFiles) { | |
| // Support relative paths | |
| gg->AddAdditionalCleanFile( | |
| cmSystemTools::CollapseFullPath(cleanFile, binaryDir), config); | |
| } | |
| } | |
| } | |
| cmNinjaDeps cmNinjaTargetGenerator::GetObjects(std::string const& config) const | |
| { | |
| auto const it = this->Configs.find(config); | |
| if (it != this->Configs.end()) { | |
| return it->second.Objects; | |
| } | |
| return {}; | |
| } | |
| std::string cmNinjaTargetGenerator::GetSwiftModuleOutput( | |
| std::string const& config) const | |
| { | |
| auto const it = this->Configs.find(config); | |
| if (it != this->Configs.end()) { | |
| return it->second.SwiftModuleOutput; | |
| } | |
| return {}; | |
| } | |
| void cmNinjaTargetGenerator::EnsureDirectoryExists( | |
| std::string const& path) const | |
| { | |
| if (cmSystemTools::FileIsFullPath(path)) { | |
| cmSystemTools::MakeDirectory(path); | |
| } else { | |
| cmGlobalNinjaGenerator* gg = this->GetGlobalGenerator(); | |
| std::string fullPath = gg->GetCMakeInstance()->GetHomeOutputDirectory(); | |
| // Also ensures there is a trailing slash. | |
| gg->StripNinjaOutputPathPrefixAsSuffix(fullPath); | |
| fullPath += path; | |
| cmSystemTools::MakeDirectory(fullPath); | |
| } | |
| } | |
| void cmNinjaTargetGenerator::EnsureParentDirectoryExists( | |
| std::string const& path) const | |
| { | |
| this->EnsureDirectoryExists(cmSystemTools::GetParentDirectory(path)); | |
| } | |
| void cmNinjaTargetGenerator::MacOSXContentGeneratorType::operator()( | |
| cmSourceFile const& source, char const* pkgloc, std::string const& config) | |
| { | |
| // Skip OS X content when not building a Framework or Bundle. | |
| if (!this->Generator->GetGeneratorTarget()->IsBundleOnApple()) { | |
| return; | |
| } | |
| std::string macdir = | |
| this->Generator->OSXBundleGenerator->InitMacOSXContentDirectory(pkgloc, | |
| config); | |
| // Reject files that collide with files from the Ninja file's native config. | |
| if (config != this->FileConfig) { | |
| std::string nativeMacdir = | |
| this->Generator->OSXBundleGenerator->InitMacOSXContentDirectory( | |
| pkgloc, this->FileConfig); | |
| if (macdir == nativeMacdir) { | |
| return; | |
| } | |
| } | |
| // Get the input file location. | |
| std::string input = source.GetFullPath(); | |
| input = this->Generator->GetGlobalGenerator()->ConvertToNinjaPath(input); | |
| // Get the output file location. | |
| std::string output = | |
| cmStrCat(macdir, '/', cmSystemTools::GetFilenameName(input)); | |
| output = this->Generator->GetGlobalGenerator()->ConvertToNinjaPath(output); | |
| // Write a build statement to copy the content into the bundle. | |
| this->Generator->GetGlobalGenerator()->WriteMacOSXContentBuild( | |
| input, output, this->FileConfig); | |
| // Add as a dependency to the target so that it gets called. | |
| this->Generator->Configs[config].ExtraFiles.push_back(std::move(output)); | |
| } | |
| void cmNinjaTargetGenerator::AddDepfileBinding(cmNinjaVars& vars, | |
| std::string depfile) const | |
| { | |
| std::string depfileForShell = this->ConvertToOutputFormatForShell(depfile); | |
| if (depfile != depfileForShell) { | |
| vars["depfile"] = std::move(depfile); | |
| } | |
| vars["DEP_FILE"] = std::move(depfileForShell); | |
| } | |
| void cmNinjaTargetGenerator::RemoveDepfileBinding(cmNinjaVars& vars) const | |
| { | |
| vars.erase("DEP_FILE"); | |
| vars.erase("depfile"); | |
| } | |
| void cmNinjaTargetGenerator::addPoolNinjaVariable( | |
| std::string const& pool_property, std::string const& config, | |
| cmGeneratorTarget* target, cmSourceFile const* source, cmNinjaVars& vars) | |
| { | |
| // First check file set properties, then if not found the current source | |
| // properties, then if not found, its target ones. Allows to override a | |
| // target-wide compile pool with file set-specific or source-specific one. | |
| cmValue pool = {}; | |
| if (source) { | |
| cmGeneratorFileSet const* fileSet = | |
| target->GetFileSetForSource(config, source); | |
| if (fileSet) { | |
| pool = fileSet->GetProperty(pool_property); | |
| } | |
| if (!pool) { | |
| pool = source->GetProperty(pool_property); | |
| } | |
| } | |
| if (!pool) { | |
| pool = target->GetProperty(pool_property); | |
| } | |
| if (pool) { | |
| vars["pool"] = *pool; | |
| } | |
| } | |
| bool cmNinjaTargetGenerator::ForceResponseFile() | |
| { | |
| static std::string const forceRspFile = "CMAKE_NINJA_FORCE_RESPONSE_FILE"; | |
| return (this->GetMakefile()->IsDefinitionSet(forceRspFile) || | |
| cmSystemTools::HasEnv(forceRspFile)); | |
| } |