diff options
author | Ben Boeckel <ben.boeckel@kitware.com> | 2023-02-01 15:40:19 (GMT) |
---|---|---|
committer | Ben Boeckel <ben.boeckel@kitware.com> | 2023-08-17 18:42:54 (GMT) |
commit | 9b9ec70b5421e8cf91e000d8b6636d7b0b721d1a (patch) | |
tree | aa5bea33938b0edb179ebdab0bcae0290ab3dcd2 /Source | |
parent | 80ef50a1919d62acb82a6423c40042a94edeac60 (diff) | |
download | CMake-9b9ec70b5421e8cf91e000d8b6636d7b0b721d1a.zip CMake-9b9ec70b5421e8cf91e000d8b6636d7b0b721d1a.tar.gz CMake-9b9ec70b5421e8cf91e000d8b6636d7b0b721d1a.tar.bz2 |
Ninja: generate scanning and build rules for C++20 module synthetic targets
Diffstat (limited to 'Source')
-rw-r--r-- | Source/cmGlobalNinjaGenerator.cxx | 12 | ||||
-rw-r--r-- | Source/cmNinjaNormalTargetGenerator.cxx | 97 | ||||
-rw-r--r-- | Source/cmNinjaNormalTargetGenerator.h | 3 | ||||
-rw-r--r-- | Source/cmNinjaTargetGenerator.cxx | 222 | ||||
-rw-r--r-- | Source/cmNinjaTargetGenerator.h | 12 |
5 files changed, 322 insertions, 24 deletions
diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index 9ca1b2e..54c3737 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -2643,7 +2643,9 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile( for (cmScanDepInfo const& object : objects) { for (auto const& p : object.Provides) { std::string mod; - if (!p.CompiledModulePath.empty()) { + if (cmDyndepCollation::IsBmiOnly(export_info, object.PrimaryOutput)) { + mod = object.PrimaryOutput; + } else if (!p.CompiledModulePath.empty()) { // The scanner provided the path to the module file. mod = p.CompiledModulePath; if (!cmSystemTools::FileIsFullPath(mod)) { @@ -2714,8 +2716,12 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile( build.Outputs[0] = this->ConvertToNinjaPath(object.PrimaryOutput); build.ImplicitOuts.clear(); for (auto const& p : object.Provides) { - build.ImplicitOuts.push_back( - this->ConvertToNinjaPath(mod_files[p.LogicalName].BmiPath)); + auto const implicitOut = + this->ConvertToNinjaPath(mod_files[p.LogicalName].BmiPath); + // Ignore the `provides` when the BMI is the output. + if (implicitOut != build.Outputs[0]) { + build.ImplicitOuts.emplace_back(implicitOut); + } } build.ImplicitDeps.clear(); for (auto const& r : object.Requires) { diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index 089498b..48c30b6 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx @@ -62,12 +62,15 @@ cmNinjaNormalTargetGenerator::~cmNinjaNormalTargetGenerator() = default; void cmNinjaNormalTargetGenerator::Generate(const std::string& config) { - std::string lang = this->GeneratorTarget->GetLinkerLanguage(config); - if (this->TargetLinkLanguage(config).empty()) { - cmSystemTools::Error( - cmStrCat("CMake can not determine linker language for target: ", - this->GetGeneratorTarget()->GetName())); - return; + if (this->GetGeneratorTarget()->GetType() != + cmStateEnums::INTERFACE_LIBRARY) { + std::string lang = this->GeneratorTarget->GetLinkerLanguage(config); + if (this->TargetLinkLanguage(config).empty()) { + cmSystemTools::Error( + cmStrCat("CMake can not determine linker language for target: ", + this->GetGeneratorTarget()->GetName())); + return; + } } // Write the rules for each language. @@ -87,6 +90,34 @@ void cmNinjaNormalTargetGenerator::Generate(const std::string& config) if (this->GetGeneratorTarget()->GetType() == cmStateEnums::OBJECT_LIBRARY) { this->WriteObjectLibStatement(config); + } else if (this->GetGeneratorTarget()->GetType() == + cmStateEnums::INTERFACE_LIBRARY) { + bool haveCxxModuleSources = false; + if (this->GetGeneratorTarget()->HaveCxx20ModuleSources()) { + haveCxxModuleSources = true; + } + + if (!haveCxxModuleSources) { + cmSystemTools::Error(cmStrCat( + "Ninja does not support INTERFACE libraries without C++ module " + "sources as a normal target: ", + this->GetGeneratorTarget()->GetName())); + return; + } + + firstForConfig = true; + for (auto const& fileConfig : this->GetConfigNames()) { + if (!this->GetGlobalGenerator() + ->GetCrossConfigs(fileConfig) + .count(config)) { + continue; + } + if (haveCxxModuleSources) { + this->WriteCxxModuleLibraryStatement(config, fileConfig, + firstForConfig); + } + firstForConfig = false; + } } else { firstForConfig = true; for (auto const& fileConfig : this->GetConfigNames()) { @@ -123,12 +154,26 @@ void cmNinjaNormalTargetGenerator::WriteLanguagesRules( #endif // Write rules for languages compiled in this target. - std::set<std::string> languages; - std::vector<cmSourceFile const*> sourceFiles; - this->GetGeneratorTarget()->GetObjectSources(sourceFiles, config); - if (this->HaveRequiredLanguages(sourceFiles, languages)) { - for (std::string const& language : languages) { - this->WriteLanguageRules(language, config); + { + std::set<std::string> languages; + std::vector<cmSourceFile const*> sourceFiles; + this->GetGeneratorTarget()->GetObjectSources(sourceFiles, config); + if (this->HaveRequiredLanguages(sourceFiles, languages)) { + for (std::string const& language : languages) { + this->WriteLanguageRules(language, config); + } + } + } + + // Write rules for languages in BMI-only rules. + { + std::set<std::string> languages; + std::vector<cmSourceFile const*> sourceFiles; + this->GetGeneratorTarget()->GetCxxModuleSources(sourceFiles, config); + if (this->HaveRequiredLanguages(sourceFiles, languages)) { + for (std::string const& language : languages) { + this->WriteLanguageRules(language, config); + } } } } @@ -1637,6 +1682,34 @@ void cmNinjaNormalTargetGenerator::WriteObjectLibStatement( this->GetTargetName(), this->GetGeneratorTarget(), config); } +void cmNinjaNormalTargetGenerator::WriteCxxModuleLibraryStatement( + const std::string& config, const std::string& /*fileConfig*/, + bool firstForConfig) +{ + // TODO: How to use `fileConfig` properly? + + // Write a phony output that depends on the scanning output. + { + cmNinjaBuild build("phony"); + build.Comment = + cmStrCat("Imported C++ module library ", this->GetTargetName()); + this->GetLocalGenerator()->AppendTargetOutputs(this->GetGeneratorTarget(), + build.Outputs, config); + if (firstForConfig) { + this->GetLocalGenerator()->AppendTargetOutputs( + this->GetGeneratorTarget(), + this->GetGlobalGenerator()->GetByproductsForCleanTarget(config), + config); + } + build.ExplicitDeps.emplace_back(this->GetDyndepFilePath("CXX", config)); + this->GetGlobalGenerator()->WriteBuild(this->GetCommonFileStream(), build); + } + + // Add aliases for the target name. + this->GetGlobalGenerator()->AddTargetAlias( + this->GetTargetName(), this->GetGeneratorTarget(), config); +} + cmGeneratorTarget::Names cmNinjaNormalTargetGenerator::TargetNames( const std::string& config) const { diff --git a/Source/cmNinjaNormalTargetGenerator.h b/Source/cmNinjaNormalTargetGenerator.h index 187ea46..3ef0230 100644 --- a/Source/cmNinjaNormalTargetGenerator.h +++ b/Source/cmNinjaNormalTargetGenerator.h @@ -49,6 +49,9 @@ private: const std::string& output); void WriteObjectLibStatement(const std::string& config); + void WriteCxxModuleLibraryStatement(const std::string& config, + const std::string& fileConfig, + bool firstForConfig); std::vector<std::string> ComputeLinkCmd(const std::string& config); std::vector<std::string> ComputeDeviceLinkCmd(); diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index 6792cd7..09f8495 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -28,6 +28,7 @@ #include "cmGeneratedFileStream.h" #include "cmGeneratorExpression.h" #include "cmGeneratorTarget.h" +#include "cmGlobalCommonGenerator.h" #include "cmGlobalNinjaGenerator.h" #include "cmList.h" #include "cmLocalGenerator.h" @@ -59,8 +60,13 @@ std::unique_ptr<cmNinjaTargetGenerator> cmNinjaTargetGenerator::New( case cmStateEnums::OBJECT_LIBRARY: return cm::make_unique<cmNinjaNormalTargetGenerator>(target); - case cmStateEnums::UTILITY: case cmStateEnums::INTERFACE_LIBRARY: + if (target->HaveCxx20ModuleSources()) { + return cm::make_unique<cmNinjaNormalTargetGenerator>(target); + } + CM_FALLTHROUGH; + + case cmStateEnums::UTILITY: case cmStateEnums::GLOBAL_TARGET: return cm::make_unique<cmNinjaUtilityTargetGenerator>(target); @@ -167,7 +173,7 @@ std::string cmNinjaTargetGenerator::OrderDependsTargetForTarget( // Refactor it. std::string cmNinjaTargetGenerator::ComputeFlagsForObject( cmSourceFile const* source, const std::string& language, - const std::string& config) + const std::string& config, const std::string& objectFileName) { std::unordered_map<std::string, std::string> pchSources; std::vector<std::string> architectures = @@ -247,6 +253,18 @@ std::string cmNinjaTargetGenerator::ComputeFlagsForObject( "\nin a file set of type \"", fs->GetType(), R"(" but the source is not classified as a "CXX" source.)")); } + + if (!this->GeneratorTarget->Target->IsNormal()) { + auto flag = this->GetMakefile()->GetSafeDefinition( + "CMAKE_EXPERIMENTAL_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; @@ -394,6 +412,31 @@ std::string cmNinjaTargetGenerator::GetObjectFilePath( return path; } +std::string cmNinjaTargetGenerator::GetBmiFilePath( + cmSourceFile const* source, const std::string& config) const +{ + std::string path = this->LocalGenerator->GetHomeRelativeOutputPath(); + if (!path.empty()) { + path += '/'; + } + + 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()); + + path += cmStrCat( + this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget), + this->GetGlobalGenerator()->ConfigDirectory(config), '/', bmiName); + return path; +} + std::string cmNinjaTargetGenerator::GetClangTidyReplacementsFilePath( std::string const& directory, cmSourceFile const& source, std::string const& config) const @@ -1027,6 +1070,16 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatements( } } + { + std::vector<cmSourceFile const*> bmiOnlySources; + this->GeneratorTarget->GetCxxModuleSources(bmiOnlySources, config); + + for (cmSourceFile const* sf : bmiOnlySources) { + this->WriteCxxModuleBmiBuildStatement(sf, config, fileConfig, + firstForConfig); + } + } + for (auto const& langScanningFiles : this->Configs[config].ScanningInfo) { std::string const& language = langScanningFiles.first; std::vector<ScanningFiles> const& scanningFiles = langScanningFiles.second; @@ -1149,22 +1202,22 @@ cmNinjaBuild GetScanBuildStatement(const std::string& ruleName, scanBuild.Variables["OBJ_FILE"] = objectFileName; // Tell dependency scanner where to store dyndep intermediate results. - std::string const& ddiFile = cmStrCat(objectFileName, ".ddi"); - scanBuild.Variables["DYNDEP_INTERMEDIATE_FILE"] = ddiFile; + std::string ddiFileName = cmStrCat(objectFileName, ".ddi"); + scanBuild.Variables["DYNDEP_INTERMEDIATE_FILE"] = ddiFileName; // Outputs of the scan/preprocessor build statement. if (compilePP) { scanBuild.Outputs.push_back(ppFileName); - scanBuild.ImplicitOuts.push_back(ddiFile); + scanBuild.ImplicitOuts.push_back(ddiFileName); } else { - scanBuild.Outputs.push_back(ddiFile); + scanBuild.Outputs.push_back(ddiFileName); scanBuild.Variables["PREPROCESSED_OUTPUT_FILE"] = 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(ddiFile); + objBuild.ImplicitDeps.emplace_back(ddiFileName); } } @@ -1214,7 +1267,8 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( cmNinjaBuild objBuild(this->LanguageCompilerRule( language, config, needDyndep ? WithScanning::Yes : WithScanning::No)); cmNinjaVars& vars = objBuild.Variables; - vars["FLAGS"] = this->ComputeFlagsForObject(source, language, config); + vars["FLAGS"] = + this->ComputeFlagsForObject(source, language, config, objectFileName); vars["DEFINES"] = this->ComputeDefines(source, language, config); vars["INCLUDES"] = this->ComputeIncludes(source, language, config); @@ -1545,6 +1599,155 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( } } +void cmNinjaTargetGenerator::WriteCxxModuleBmiBuildStatement( + cmSourceFile const* source, const std::string& config, + const std::string& 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 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); + + 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); + } + if (!replaceExt) { + // use original code + vars["DEP_FILE"] = this->GetLocalGenerator()->ConvertToOutputFormat( + cmStrCat(bmiFileName, ".d"), cmOutputConverter::SHELL); + } else { + // Replace the original source file extension with the + // depend file extension. + std::string dependFileName = cmStrCat( + cmSystemTools::GetFilenameWithoutLastExtension(bmiFileName), ".d"); + vars["DEP_FILE"] = this->GetLocalGenerator()->ConvertToOutputFormat( + cmStrCat(bmiFileDir, '/', dependFileName), cmOutputConverter::SHELL); + } + } + + 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; + } + + if (firstForConfig) { + this->ExportObjectCompileCommand( + language, sourceFilePath, bmiDir, bmiFileName, bmiFileDir, vars["FLAGS"], + vars["DEFINES"], vars["INCLUDES"], config); + } + + bmiBuild.Outputs.push_back(bmiFileName); + bmiBuild.ExplicitDeps.push_back(sourceFilePath); + + std::vector<std::string> depList; + + std::vector<std::string> architectures = + this->GeneratorTarget->GetAppleArchs(config, language); + if (architectures.empty()) { + architectures.emplace_back(); + } + + 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_EXPERIMENTAL_", 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->LocalGenerator); + + ScanningFiles scanningFiles; + + if (firstForConfig) { + scanningFiles.ScanningOutput = cmStrCat(bmiFileName, ".ddi"); + } + + this->addPoolNinjaVariable("JOB_POOL_COMPILE", this->GetGeneratorTarget(), + 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"] = ddModmapFile; + scanningFiles.ModuleMapFile = std::move(ddModmapFile); + } + + if (!scanningFiles.IsEmpty()) { + this->Configs[config].ScanningInfo[language].emplace_back(scanningFiles); + } + } + + this->EnsureParentDirectoryExists(bmiFileName); + + vars["OBJECT_DIR"] = this->GetLocalGenerator()->ConvertToOutputFormat( + bmiDir, cmOutputConverter::SHELL); + vars["OBJECT_FILE_DIR"] = this->GetLocalGenerator()->ConvertToOutputFormat( + bmiFileDir, cmOutputConverter::SHELL); + + this->addPoolNinjaVariable("JOB_POOL_COMPILE", this->GetGeneratorTarget(), + vars); + + this->SetMsvcTargetPdbVariable(vars, config); + + bmiBuild.RspFile = cmStrCat(bmiFileName, ".rsp"); + + this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig), + bmiBuild, commandLineLengthLimit); +} + void cmNinjaTargetGenerator::WriteTargetDependInfo(std::string const& lang, const std::string& config) { @@ -1605,6 +1808,9 @@ void cmNinjaTargetGenerator::WriteTargetDependInfo(std::string const& lang, 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, diff --git a/Source/cmNinjaTargetGenerator.h b/Source/cmNinjaTargetGenerator.h index 8c38499..49e7018 100644 --- a/Source/cmNinjaTargetGenerator.h +++ b/Source/cmNinjaTargetGenerator.h @@ -15,6 +15,7 @@ #include "cmCommonTargetGenerator.h" #include "cmGlobalNinjaGenerator.h" +#include "cmImportedCxxModuleInfo.h" #include "cmNinjaTypes.h" #include "cmOSXBundleGenerator.h" @@ -91,7 +92,8 @@ protected: */ std::string ComputeFlagsForObject(cmSourceFile const* source, const std::string& language, - const std::string& config); + const std::string& config, + const std::string& objectFileName); void AddIncludeFlags(std::string& flags, std::string const& lang, const std::string& config) override; @@ -129,6 +131,8 @@ protected: /// @return the object file path for the given @a source. std::string GetObjectFilePath(cmSourceFile const* source, const std::string& config) const; + std::string GetBmiFilePath(cmSourceFile const* source, + const std::string& config) const; /// @return the preprocessed source file path for the given @a source. std::string GetPreprocessedFilePath(cmSourceFile const* source, @@ -163,6 +167,10 @@ protected: void WriteObjectBuildStatements(const std::string& config, const std::string& fileConfig, bool firstForConfig); + void WriteCxxModuleBmiBuildStatement(cmSourceFile const* source, + const std::string& config, + const std::string& fileConfig, + bool firstForConfig); void WriteObjectBuildStatement(cmSourceFile const* source, const std::string& config, const std::string& fileConfig, @@ -239,6 +247,8 @@ private: cmNinjaDeps Objects; // Dyndep Support std::map<std::string, std::vector<ScanningFiles>> ScanningInfo; + // Imported C++ module info. + mutable ImportedCxxModuleLookup ImportedCxxModules; // Swift Support Json::Value SwiftOutputMap; std::vector<cmCustomCommand const*> CustomCommands; |