From 4a4986d28af3db8566137cb54f65cabdb17f0d15 Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Wed, 25 Mar 2026 22:39:32 -0400 Subject: [PATCH] cxxmodules: support command templates for BMI compilation Some compilers (Clang) warn when using their BMI-only flag with `-c`. Support a complete template for BMI compilation rather than an additional flag to support such toolchains. Fixes: #27600 --- Help/manual/cmake-cxxmodules.7.rst | 9 ++++- Modules/Compiler/Clang-CXX.cmake | 3 +- Source/cmGeneratorTarget.cxx | 22 +++++++---- Source/cmNinjaTargetGenerator.cxx | 39 +++++++++++++------ Source/cmNinjaTargetGenerator.h | 1 + .../expect/exp-builddb-imped-all-multi.json | 4 +- .../expect/exp-builddb-imped-all.json | 2 +- .../expect/exp-builddb-imped-config.json | 2 +- .../expect/exp-builddb-imped-cxx-config.json | 2 +- .../expect/exp-builddb-imped-cxx-multi.json | 4 +- .../expect/exp-builddb-imped-cxx.json | 2 +- .../export-build-database-setup.cmake | 4 ++ 12 files changed, 64 insertions(+), 30 deletions(-) diff --git a/Help/manual/cmake-cxxmodules.7.rst b/Help/manual/cmake-cxxmodules.7.rst index 069fddf89c..9dc14d2353 100644 --- a/Help/manual/cmake-cxxmodules.7.rst +++ b/Help/manual/cmake-cxxmodules.7.rst @@ -470,13 +470,18 @@ Additionally, toolchains should set the following variables: * ``CMAKE_CXX_MODULE_MAP_FLAG``: The arguments used to inform the compiler of the :term:`module map` file. It should use the ```` placeholder. +* ``CMAKE_CXX_COMPILE_BMI``: The command template to compile a :term:`BMI` + file from a :term:`module interface unit`. Used when + ``CMAKE_CXX_MODULE_BMI_ONLY_FLAG`` is not completely additive to an + object compilation template. * ``CMAKE_CXX_MODULE_BMI_ONLY_FLAG``: The arguments used to compile only a :term:`BMI` file from a :term:`module interface unit`. This is used when consuming modules from external projects to compile :term:`BMI` files for use within the current build. -If a toolchain does not provide the ``CMAKE_CXX_MODULE_BMI_ONLY_FLAG``, it -will not be able to consume modules provided by ``IMPORTED`` targets. +If a toolchain does not provide the ``CMAKE_CXX_COMPILE_BMI`` or +``CMAKE_CXX_MODULE_BMI_ONLY_FLAG`` variables, it will not be able to consume +modules provided by ``IMPORTED`` targets. Configure ^^^^^^^^^ diff --git a/Modules/Compiler/Clang-CXX.cmake b/Modules/Compiler/Clang-CXX.cmake index a6412e9abf..205c5f7e80 100644 --- a/Modules/Compiler/Clang-CXX.cmake +++ b/Modules/Compiler/Clang-CXX.cmake @@ -56,6 +56,7 @@ if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 16.0) unset(_clang_scan_deps_mv) set(CMAKE_CXX_MODULE_MAP_FORMAT "clang") set(CMAKE_CXX_MODULE_MAP_FLAG "@") - set(CMAKE_CXX_MODULE_BMI_ONLY_FLAG "--precompile") + set(CMAKE_CXX_COMPILE_BMI + " -o --precompile ") endif() endif() diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index e1cb914171..0cf0c35b06 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/Source/cmGeneratorTarget.cxx @@ -2335,6 +2335,7 @@ cmGeneratorTarget::GetClassifiedFlagsForSource(cmSourceFile const* sf, } // C++ module flags. + bool useBmiTemplate = false; if (lang == "CXX"_s) { FlagClassification cls = FlagClassification::LocationFlag; FlagKind kind = FlagKind::BuildSystem; @@ -2353,13 +2354,17 @@ cmGeneratorTarget::GetClassifiedFlagsForSource(cmSourceFile const* sf, } if (!this->Target->IsNormal()) { - auto flag = mf->GetSafeDefinition("CMAKE_CXX_MODULE_BMI_ONLY_FLAG"); - cmRulePlaceholderExpander::RuleVariables compileObjectVars; - compileObjectVars.Object = sfVars.ObjectFileDir.c_str(); - auto rulePlaceholderExpander = lg->CreateRulePlaceholderExpander(); - rulePlaceholderExpander->ExpandRuleVariables(lg, flag, - compileObjectVars); - lg->AppendCompileOptions(bmiFlags, flag); + if (!mf->GetDefinition("CMAKE_CXX_COMPILE_BMI").IsEmpty()) { + useBmiTemplate = true; + } else { + auto flag = mf->GetSafeDefinition("CMAKE_CXX_MODULE_BMI_ONLY_FLAG"); + cmRulePlaceholderExpander::RuleVariables compileObjectVars; + compileObjectVars.Object = sfVars.ObjectFileDir.c_str(); + auto rulePlaceholderExpander = lg->CreateRulePlaceholderExpander(); + rulePlaceholderExpander->ExpandRuleVariables(lg, flag, + compileObjectVars); + lg->AppendCompileOptions(bmiFlags, flag); + } } } @@ -2382,7 +2387,8 @@ cmGeneratorTarget::GetClassifiedFlagsForSource(cmSourceFile const* sf, FlagClassification cls = FlagClassification::ExecutionFlag; FlagKind kind = FlagKind::NotAFlag; - std::string const cmdVar = cmStrCat("CMAKE_", lang, "_COMPILE_OBJECT"); + std::string const cmdVar = + cmStrCat("CMAKE_", lang, "_COMPILE_", useBmiTemplate ? "BMI" : "OBJECT"); std::string const& compileCmd = mf->GetRequiredDefinition(cmdVar); cmList compileCmds(compileCmd); // FIXME: which command to use? std::string& cmd = compileCmds[0]; diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index 20f6265b6e..b2308b5d64 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -265,15 +265,19 @@ std::string cmNinjaTargetGenerator::ComputeFlagsForObject( } if (!this->GeneratorTarget->Target->IsNormal()) { - 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); + 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); + } } } @@ -652,6 +656,19 @@ void cmNinjaTargetGenerator::WriteCompileRule(std::string const& lang, 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) @@ -928,7 +945,7 @@ void cmNinjaTargetGenerator::WriteCompileRule(std::string const& lang, } // Rule for compiling object file. - std::string const cmdVar = cmStrCat("CMAKE_", lang, "_COMPILE_OBJECT"); + std::string const cmdVar = this->GetCompileTemplateVar(lang); std::string const& compileCmd = mf->GetRequiredDefinition(cmdVar); cmList compileCmds(compileCmd); @@ -2313,7 +2330,7 @@ void cmNinjaTargetGenerator::ExportObjectCompileCommand( compileObjectVars.CudaCompileMode = cudaCompileMode.c_str(); } - std::string const cmdVar = cmStrCat("CMAKE_", language, "_COMPILE_OBJECT"); + std::string const cmdVar = this->GetCompileTemplateVar(language); std::string const& compileCmd = this->Makefile->GetRequiredDefinition(cmdVar); cmList compileCmds(compileCmd); diff --git a/Source/cmNinjaTargetGenerator.h b/Source/cmNinjaTargetGenerator.h index 81f945acd1..39d3026f31 100644 --- a/Source/cmNinjaTargetGenerator.h +++ b/Source/cmNinjaTargetGenerator.h @@ -168,6 +168,7 @@ protected: void WriteLanguageRules(std::string const& language, std::string const& config); + std::string GetCompileTemplateVar(std::string const& lang) const; void WriteCompileRule(std::string const& language, std::string const& config); void WriteCompileRule(std::string const& language, std::string const& config, diff --git a/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-all-multi.json b/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-all-multi.json index cbb5d486fd..eee91050af 100644 --- a/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-all-multi.json +++ b/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-all-multi.json @@ -143,7 +143,7 @@ "REGEX:PATH:-Ddepflag=\"CMakeFiles/CXXModules__export_build_database@synth_.dir/.d\"", "REGEX:", "REGEX:PATH:CMakeFiles/CXXModules__export_build_database@synth_.dir/", - "-c", + "REGEX:-[cv]", "PATH:/exp-builddb/importable.cxx" ], "baseline-arguments": [ @@ -229,7 +229,7 @@ "REGEX:PATH:-Ddepflag=\"CMakeFiles/CXXModules__export_build_database@synth_.dir/.d\"", "REGEX:", "REGEX:PATH:CMakeFiles/CXXModules__export_build_database@synth_.dir/", - "-c", + "REGEX:-[cv]", "PATH:/exp-builddb/importable.cxx" ], "baseline-arguments": [ diff --git a/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-all.json b/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-all.json index edcb25a44e..4d1a3df0ff 100644 --- a/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-all.json +++ b/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-all.json @@ -87,7 +87,7 @@ "REGEX:PATH:-Ddepflag=\"CMakeFiles/CXXModules__export_build_database@synth_.dir/.d\"", "REGEX:", "REGEX:PATH:CMakeFiles/CXXModules__export_build_database@synth_.dir/", - "-c", + "REGEX:-[cv]", "PATH:/exp-builddb/importable.cxx" ], "baseline-arguments": [ diff --git a/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-config.json b/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-config.json index edcb25a44e..4d1a3df0ff 100644 --- a/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-config.json +++ b/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-config.json @@ -87,7 +87,7 @@ "REGEX:PATH:-Ddepflag=\"CMakeFiles/CXXModules__export_build_database@synth_.dir/.d\"", "REGEX:", "REGEX:PATH:CMakeFiles/CXXModules__export_build_database@synth_.dir/", - "-c", + "REGEX:-[cv]", "PATH:/exp-builddb/importable.cxx" ], "baseline-arguments": [ diff --git a/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-cxx-config.json b/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-cxx-config.json index edcb25a44e..4d1a3df0ff 100644 --- a/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-cxx-config.json +++ b/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-cxx-config.json @@ -87,7 +87,7 @@ "REGEX:PATH:-Ddepflag=\"CMakeFiles/CXXModules__export_build_database@synth_.dir/.d\"", "REGEX:", "REGEX:PATH:CMakeFiles/CXXModules__export_build_database@synth_.dir/", - "-c", + "REGEX:-[cv]", "PATH:/exp-builddb/importable.cxx" ], "baseline-arguments": [ diff --git a/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-cxx-multi.json b/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-cxx-multi.json index cbb5d486fd..eee91050af 100644 --- a/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-cxx-multi.json +++ b/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-cxx-multi.json @@ -143,7 +143,7 @@ "REGEX:PATH:-Ddepflag=\"CMakeFiles/CXXModules__export_build_database@synth_.dir/.d\"", "REGEX:", "REGEX:PATH:CMakeFiles/CXXModules__export_build_database@synth_.dir/", - "-c", + "REGEX:-[cv]", "PATH:/exp-builddb/importable.cxx" ], "baseline-arguments": [ @@ -229,7 +229,7 @@ "REGEX:PATH:-Ddepflag=\"CMakeFiles/CXXModules__export_build_database@synth_.dir/.d\"", "REGEX:", "REGEX:PATH:CMakeFiles/CXXModules__export_build_database@synth_.dir/", - "-c", + "REGEX:-[cv]", "PATH:/exp-builddb/importable.cxx" ], "baseline-arguments": [ diff --git a/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-cxx.json b/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-cxx.json index edcb25a44e..4d1a3df0ff 100644 --- a/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-cxx.json +++ b/Tests/RunCMake/CXXModulesCompile/expect/exp-builddb-imped-cxx.json @@ -87,7 +87,7 @@ "REGEX:PATH:-Ddepflag=\"CMakeFiles/CXXModules__export_build_database@synth_.dir/.d\"", "REGEX:", "REGEX:PATH:CMakeFiles/CXXModules__export_build_database@synth_.dir/", - "-c", + "REGEX:-[cv]", "PATH:/exp-builddb/importable.cxx" ], "baseline-arguments": [ diff --git a/Tests/RunCMake/CXXModulesCompile/export-build-database-setup.cmake b/Tests/RunCMake/CXXModulesCompile/export-build-database-setup.cmake index abe7d3b917..4b85cfa5b9 100644 --- a/Tests/RunCMake/CXXModulesCompile/export-build-database-setup.cmake +++ b/Tests/RunCMake/CXXModulesCompile/export-build-database-setup.cmake @@ -36,6 +36,10 @@ if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC") else () set(output_flag "-o") endif () +if (CMAKE_CXX_COMPILE_BMI) # Only `clang` does this today. + set(CMAKE_CXX_COMPILE_BMI + " --precompile ${output_flag} -v ") +endif () set(CMAKE_CXX_COMPILE_OBJECT " ${output_flag} -c ")