cxxmodules: rework control logic for scanning regular C++ sources

Now that scanning support is no longer experimental, the logic for
whether or not to scan C++ 20 sources is now important because all
projects are now exposted to the logic. Make the scanning rules explicit
in the documentation and rework the queries to localize all of the
associated logic.

A policy to handle the ultimate fallback logic will be implemented in a
following commit.
This commit is contained in:
Ben Boeckel
2023-09-27 20:31:20 -04:00
parent 5eb7bd641a
commit 197a6bf171
16 changed files with 166 additions and 87 deletions
+17
View File
@@ -12,6 +12,23 @@ to scan source files for module dependencies during the build, collates
scanning results to infer ordering constraints, and tells the build tool scanning results to infer ordering constraints, and tells the build tool
how to dynamically update the build graph. how to dynamically update the build graph.
Scanning Control
================
Whether or not sources get scanned for C++ module usage is dependent on the
following queries. The first query that provides a yes/no answer is used.
- If the source file belongs to a file set of type ``CXX_MODULES``, it will
be scanned.
- If the target does not use at least C++ 20, it will not be scanned.
- If the source file is not the language ``CXX``, it will not be scanned.
- If the :prop_sf:`CXX_SCAN_FOR_MODULES` source file property is set, its
value will be used.
- If the :prop_tgt:`CXX_SCAN_FOR_MODULES` target property is set, its value
will be used. Set the :variable:`CMAKE_CXX_SCAN_FOR_MODULES` variable
to initialize this property on all targets as they are created.
- Otherwise the source will be scanned.
Compiler Support Compiler Support
================ ================
+91 -31
View File
@@ -9115,39 +9115,83 @@ cmGeneratorTarget::Cxx20SupportLevel cmGeneratorTarget::HaveCxxModuleSupport(
void cmGeneratorTarget::CheckCxxModuleStatus(std::string const& config) const void cmGeneratorTarget::CheckCxxModuleStatus(std::string const& config) const
{ {
bool haveScannableSources = false;
// Check for `CXX_MODULE*` file sets and a lack of support. // Check for `CXX_MODULE*` file sets and a lack of support.
if (this->HaveCxx20ModuleSources()) { if (this->HaveCxx20ModuleSources()) {
switch (this->HaveCxxModuleSupport(config)) { haveScannableSources = true;
case cmGeneratorTarget::Cxx20SupportLevel::MissingCxx: }
this->Makefile->IssueMessage(
MessageType::FATAL_ERROR, if (!haveScannableSources) {
cmStrCat("The target named \"", this->GetName(), // Check to see if there are regular sources that have requested scanning.
"\" has C++ sources that export modules but the \"CXX\" " auto sources = cmGeneratorTarget::GetSourceFiles(config);
"language has not been enabled")); for (auto const& source : sources) {
break; auto const* sf = source.Value;
case cmGeneratorTarget::Cxx20SupportLevel::NoCxx20: { auto const& lang = sf->GetLanguage();
cmStandardLevelResolver standardResolver(this->Makefile); if (lang != "CXX"_s) {
auto effStandard = continue;
standardResolver.GetEffectiveStandard(this, "CXX", config); }
if (effStandard.empty()) { // Ignore sources which do not need dyndep.
effStandard = "; no C++ standard found"; if (this->NeedDyndepForSource(lang, config, sf)) {
} else { haveScannableSources = true;
effStandard = cmStrCat("; found \"cxx_std_", effStandard, '"'); }
}
this->Makefile->IssueMessage(
MessageType::FATAL_ERROR,
cmStrCat(
"The target named \"", this->GetName(),
"\" has C++ sources that export modules but does not include "
"\"cxx_std_20\" (or newer) among its `target_compile_features`",
effStandard));
} break;
case cmGeneratorTarget::Cxx20SupportLevel::MissingRule:
case cmGeneratorTarget::Cxx20SupportLevel::Supported:
// All is well.
break;
} }
} }
// If there isn't anything scannable, ignore it.
if (!haveScannableSources) {
return;
}
// If the generator doesn't support modules at all, error that we have
// sources that require the support.
if (!this->GetGlobalGenerator()->CheckCxxModuleSupport()) {
this->Makefile->IssueMessage(
MessageType::FATAL_ERROR,
cmStrCat(
"The target named \"", this->GetName(),
"\" contains C++ "
"sources that use modules which is not supported by the generator"));
return;
}
switch (this->HaveCxxModuleSupport(config)) {
case cmGeneratorTarget::Cxx20SupportLevel::MissingCxx:
this->Makefile->IssueMessage(
MessageType::FATAL_ERROR,
cmStrCat("The target named \"", this->GetName(),
"\" has C++ sources that use modules but the \"CXX\" "
"language has not been enabled"));
break;
case cmGeneratorTarget::Cxx20SupportLevel::NoCxx20: {
cmStandardLevelResolver standardResolver(this->Makefile);
auto effStandard =
standardResolver.GetEffectiveStandard(this, "CXX", config);
if (effStandard.empty()) {
effStandard = "; no C++ standard found";
} else {
effStandard = cmStrCat("; found \"cxx_std_", effStandard, '"');
}
this->Makefile->IssueMessage(
MessageType::FATAL_ERROR,
cmStrCat(
"The target named \"", this->GetName(),
"\" has C++ sources that use modules but does not include "
"\"cxx_std_20\" (or newer) among its `target_compile_features`",
effStandard));
} break;
case cmGeneratorTarget::Cxx20SupportLevel::MissingRule: {
this->Makefile->IssueMessage(
MessageType::FATAL_ERROR,
cmStrCat("The target named \"", this->GetName(),
"\" has C++ sources that use modules but the compiler does "
"not provide a way to discover the import graph "
"dependencies"));
} break;
case cmGeneratorTarget::Cxx20SupportLevel::Supported:
// All is well.
break;
}
} }
bool cmGeneratorTarget::NeedCxxModuleSupport(std::string const& lang, bool cmGeneratorTarget::NeedCxxModuleSupport(std::string const& lang,
@@ -9185,14 +9229,30 @@ bool cmGeneratorTarget::NeedDyndepForSource(std::string const& lang,
std::string const& config, std::string const& config,
cmSourceFile const* sf) const cmSourceFile const* sf) const
{ {
bool const needDyndep = this->NeedDyndep(lang, config); // Fortran always needs to be scanned.
if (!needDyndep) { if (lang == "Fortran"_s) {
return true;
}
// Only C++ code needs scanned otherwise.
if (lang != "CXX"_s) {
return false; return false;
} }
// Any file in `CXX_MODULES` file sets need scanned (it being `CXX` is
// enforced elsewhere).
auto const* fs = this->GetFileSetForSource(config, sf); auto const* fs = this->GetFileSetForSource(config, sf);
if (fs && fs->GetType() == "CXX_MODULES"_s) { if (fs && fs->GetType() == "CXX_MODULES"_s) {
return true; return true;
} }
switch (this->HaveCxxModuleSupport(config)) {
case Cxx20SupportLevel::MissingCxx:
case Cxx20SupportLevel::NoCxx20:
return false;
case Cxx20SupportLevel::MissingRule:
case Cxx20SupportLevel::Supported:
break;
}
auto const sfProp = sf->GetProperty("CXX_SCAN_FOR_MODULES"); auto const sfProp = sf->GetProperty("CXX_SCAN_FOR_MODULES");
if (sfProp.IsSet()) { if (sfProp.IsSet()) {
return sfProp.IsOn(); return sfProp.IsOn();
@@ -435,14 +435,6 @@ void cmGlobalVisualStudio7Generator::WriteTargetsToSolution(
target->CheckCxxModuleStatus(c); target->CheckCxxModuleStatus(c);
} }
if (target->HaveCxx20ModuleSources() && !this->SupportsCxxModuleDyndep()) {
root->GetMakefile()->IssueMessage(
MessageType::FATAL_ERROR,
cmStrCat("The target named \"", target->GetName(),
"\" contains C++ sources that export modules which is not "
"supported by the generator"));
}
// handle external vc project files // handle external vc project files
cmValue expath = target->GetProperty("EXTERNAL_MSPROJECT"); cmValue expath = target->GetProperty("EXTERNAL_MSPROJECT");
if (expath) { if (expath) {
-8
View File
@@ -1384,14 +1384,6 @@ bool cmGlobalXCodeGenerator::CreateXCodeTarget(
gtgt->CheckCxxModuleStatus(configName); gtgt->CheckCxxModuleStatus(configName);
} }
if (gtgt->HaveCxx20ModuleSources()) {
gtgt->Makefile->IssueMessage(
MessageType::FATAL_ERROR,
cmStrCat("The target named \"", gtgt->GetName(),
"\" contains C++ sources that export modules which is not "
"supported by the generator"));
}
auto& gtgt_visited = this->CommandsVisited[gtgt]; auto& gtgt_visited = this->CommandsVisited[gtgt];
auto const& deps = this->GetTargetDirectDepends(gtgt); auto const& deps = this->GetTargetDirectDepends(gtgt);
for (auto const& d : deps) { for (auto const& d : deps) {
-8
View File
@@ -204,14 +204,6 @@ void cmMakefileTargetGenerator::WriteTargetBuildRules()
{ {
this->GeneratorTarget->CheckCxxModuleStatus(this->GetConfigName()); this->GeneratorTarget->CheckCxxModuleStatus(this->GetConfigName());
if (this->GeneratorTarget->HaveCxx20ModuleSources()) {
this->Makefile->IssueMessage(
MessageType::FATAL_ERROR,
cmStrCat("The target named \"", this->GeneratorTarget->GetName(),
"\" contains C++ sources that export modules which is not "
"supported by the generator"));
}
// -- Write the custom commands for this target // -- Write the custom commands for this target
// Evaluates generator expressions and expands prop_value // Evaluates generator expressions and expands prop_value
@@ -362,15 +362,6 @@ void cmVisualStudio10TargetGenerator::Generate()
this->GeneratorTarget->CheckCxxModuleStatus(config); this->GeneratorTarget->CheckCxxModuleStatus(config);
} }
if (this->GeneratorTarget->HaveCxx20ModuleSources() &&
!this->GlobalGenerator->SupportsCxxModuleDyndep()) {
this->Makefile->IssueMessage(
MessageType::FATAL_ERROR,
cmStrCat("The target named \"", this->GeneratorTarget->GetName(),
"\" contains C++ sources that export modules which is not "
"supported by the generator"));
}
this->ProjectType = computeProjectType(this->GeneratorTarget); this->ProjectType = computeProjectType(this->GeneratorTarget);
this->Managed = this->ProjectType == VsProjectType::csproj; this->Managed = this->ProjectType == VsProjectType::csproj;
const std::string ProjectFileExtension = const std::string ProjectFileExtension =
+4 -9
View File
@@ -1,13 +1,8 @@
CMake Error in CMakeLists.txt: (CMake Error in CMakeLists.txt:
The target named "nocxx" has C\+\+ sources that export modules but the "CXX" ( The target named "nocxx" has C\+\+ sources that use modules but the "CXX"
language has not been enabled language has not been enabled
| The target named "nocxx" contains C\+\+ sources that use modules which is not
( supported by the generator
CMake Error in CMakeLists.txt:
( The target named "nocxx" has C\+\+ sources that export modules but the "CXX"
language has not been enabled
| The target named "nocxx" contains C\+\+ sources that export modules which is
not supported by the generator
| Target "nocxx" has source file | Target "nocxx" has source file
.*/Tests/RunCMake/CXXModules/sources/module.cxx .*/Tests/RunCMake/CXXModules/sources/module.cxx
+4 -10
View File
@@ -1,15 +1,9 @@
CMake Error in CMakeLists.txt: (CMake Error in CMakeLists.txt:
The target named "nocxx20" has C\+\+ sources that export modules but does not ( The target named "nocxx20" has C\+\+ sources that use modules but does not
include "cxx_std_20" \(or newer\) among its `target_compile_features`; found include "cxx_std_20" \(or newer\) among its `target_compile_features`; found
"cxx_std_17" "cxx_std_17"
| The target named "nocxx20" contains C\+\+ sources that use modules which is
( not supported by the generator
CMake Error in CMakeLists.txt:
( The target named "nocxx20" has C\+\+ sources that export modules but does not
include "cxx_std_20" \(or newer\) among its `target_compile_features`; found
"cxx_std_17"
| The target named "nocxx20" contains C\+\+ sources that export modules which
is not supported by the generator
) )
)* )*
CMake Generate step failed. Build files cannot be regenerated correctly. CMake Generate step failed. Build files cannot be regenerated correctly.
@@ -6,13 +6,13 @@
due to lack of required features. Ninja 1.11 or higher is required. due to lack of required features. Ninja 1.11 or higher is required.
|CMake Error in CMakeLists.txt: |CMake Error in CMakeLists.txt:
The target named "nodyndep" contains C\+\+ sources that export modules which The target named "nodyndep" contains C\+\+ sources that use modules which is
is not supported by the generator not supported by the generator
( (
CMake Error in CMakeLists.txt: CMake Error in CMakeLists.txt:
The target named "nodyndep" contains C\+\+ sources that export modules which The target named "nodyndep" contains C\+\+ sources that use modules which is
is not supported by the generator not supported by the generator
)*) )*)
CMake Generate step failed. Build files cannot be regenerated correctly. CMake Generate step failed. Build files cannot be regenerated correctly.
@@ -0,0 +1,9 @@
(CMake Error in CMakeLists.txt:
( The target named "noscanning-sf-property" has C\+\+ sources that use modules
but the compiler does not provide a way to discover the import graph
dependencies
| The target named "noscanning-sf-property" contains C\+\+ sources that use modules which
is not supported by the generator
)
)*
CMake Generate step failed. Build files cannot be regenerated correctly.
@@ -0,0 +1,13 @@
enable_language(CXX)
unset(CMAKE_CXX_SCANDEP_SOURCE)
add_executable(noscanning-sf-property
sources/module-use.cxx)
set_target_properties(noscanning-sf-property
PROPERTIES
CXX_STANDARD 20
CXX_STANDARD_REQUIRED ON
CXX_SCAN_FOR_MODULES 0)
set_source_files_properties(sources/module-use.cxx
PROPERTIES
CXX_SCAN_FOR_MODULES 1)
@@ -0,0 +1 @@
1
@@ -0,0 +1,9 @@
(CMake Error in CMakeLists.txt:
( The target named "noscanning-target-property" has C\+\+ sources that use
modules but the compiler does not provide a way to discover the import
graph dependencies
| The target named "noscanning-target-property" contains C\+\+ sources that use modules which
is not supported by the generator
)
)*
CMake Generate step failed. Build files cannot be regenerated correctly.
@@ -0,0 +1,10 @@
enable_language(CXX)
unset(CMAKE_CXX_SCANDEP_SOURCE)
add_executable(noscanning-target-property
sources/module-use.cxx)
set_target_properties(noscanning-target-property
PROPERTIES
CXX_STANDARD 20
CXX_STANDARD_REQUIRED ON
CXX_SCAN_FOR_MODULES 1)
@@ -14,6 +14,9 @@ if ("cxx_std_20" IN_LIST CMAKE_CXX_COMPILE_FEATURES)
if (NOT forced_cxx_standard) if (NOT forced_cxx_standard)
run_cmake(NoCXX20) run_cmake(NoCXX20)
endif () endif ()
run_cmake(NoScanningSourceFileProperty)
run_cmake(NoScanningTargetProperty)
endif () endif ()
if (RunCMake_GENERATOR MATCHES "Ninja") if (RunCMake_GENERATOR MATCHES "Ninja")