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

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
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
================

View File

@@ -9115,39 +9115,83 @@ cmGeneratorTarget::Cxx20SupportLevel cmGeneratorTarget::HaveCxxModuleSupport(
void cmGeneratorTarget::CheckCxxModuleStatus(std::string const& config) const
{
bool haveScannableSources = false;
// Check for `CXX_MODULE*` file sets and a lack of support.
if (this->HaveCxx20ModuleSources()) {
switch (this->HaveCxxModuleSupport(config)) {
case cmGeneratorTarget::Cxx20SupportLevel::MissingCxx:
this->Makefile->IssueMessage(
MessageType::FATAL_ERROR,
cmStrCat("The target named \"", this->GetName(),
"\" has C++ sources that export 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 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;
haveScannableSources = true;
}
if (!haveScannableSources) {
// Check to see if there are regular sources that have requested scanning.
auto sources = cmGeneratorTarget::GetSourceFiles(config);
for (auto const& source : sources) {
auto const* sf = source.Value;
auto const& lang = sf->GetLanguage();
if (lang != "CXX"_s) {
continue;
}
// Ignore sources which do not need dyndep.
if (this->NeedDyndepForSource(lang, config, sf)) {
haveScannableSources = true;
}
}
}
// 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,
@@ -9185,14 +9229,30 @@ bool cmGeneratorTarget::NeedDyndepForSource(std::string const& lang,
std::string const& config,
cmSourceFile const* sf) const
{
bool const needDyndep = this->NeedDyndep(lang, config);
if (!needDyndep) {
// Fortran always needs to be scanned.
if (lang == "Fortran"_s) {
return true;
}
// Only C++ code needs scanned otherwise.
if (lang != "CXX"_s) {
return false;
}
// Any file in `CXX_MODULES` file sets need scanned (it being `CXX` is
// enforced elsewhere).
auto const* fs = this->GetFileSetForSource(config, sf);
if (fs && fs->GetType() == "CXX_MODULES"_s) {
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");
if (sfProp.IsSet()) {
return sfProp.IsOn();

View File

@@ -435,14 +435,6 @@ void cmGlobalVisualStudio7Generator::WriteTargetsToSolution(
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
cmValue expath = target->GetProperty("EXTERNAL_MSPROJECT");
if (expath) {

View File

@@ -1384,14 +1384,6 @@ bool cmGlobalXCodeGenerator::CreateXCodeTarget(
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 const& deps = this->GetTargetDirectDepends(gtgt);
for (auto const& d : deps) {

View File

@@ -204,14 +204,6 @@ void cmMakefileTargetGenerator::WriteTargetBuildRules()
{
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
// Evaluates generator expressions and expands prop_value

View File

@@ -362,15 +362,6 @@ void cmVisualStudio10TargetGenerator::Generate()
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->Managed = this->ProjectType == VsProjectType::csproj;
const std::string ProjectFileExtension =

View File

@@ -1,13 +1,8 @@
CMake Error in CMakeLists.txt:
The target named "nocxx" has C\+\+ sources that export modules but the "CXX"
(CMake Error in CMakeLists.txt:
( The target named "nocxx" has C\+\+ sources that use modules but the "CXX"
language has not been enabled
(
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
| The target named "nocxx" contains C\+\+ sources that use modules which is not
supported by the generator
| Target "nocxx" has source file
.*/Tests/RunCMake/CXXModules/sources/module.cxx

View File

@@ -1,15 +1,9 @@
CMake Error in CMakeLists.txt:
The target named "nocxx20" has C\+\+ sources that export modules but does not
(CMake Error in CMakeLists.txt:
( 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
"cxx_std_17"
(
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
| The target named "nocxx20" contains C\+\+ sources that use modules which is
not supported by the generator
)
)*
CMake Generate step failed. Build files cannot be regenerated correctly.

View File

@@ -6,13 +6,13 @@
due to lack of required features. Ninja 1.11 or higher is required.
|CMake Error in CMakeLists.txt:
The target named "nodyndep" contains C\+\+ sources that export modules which
is not supported by the generator
The target named "nodyndep" contains C\+\+ sources that use modules which is
not supported by the generator
(
CMake Error in CMakeLists.txt:
The target named "nodyndep" contains C\+\+ sources that export modules which
is not supported by the generator
The target named "nodyndep" contains C\+\+ sources that use modules which is
not supported by the generator
)*)
CMake Generate step failed. Build files cannot be regenerated correctly.

View File

@@ -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.

View File

@@ -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)

View File

@@ -0,0 +1 @@
1

View File

@@ -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.

View File

@@ -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)

View File

@@ -14,6 +14,9 @@ if ("cxx_std_20" IN_LIST CMAKE_CXX_COMPILE_FEATURES)
if (NOT forced_cxx_standard)
run_cmake(NoCXX20)
endif ()
run_cmake(NoScanningSourceFileProperty)
run_cmake(NoScanningTargetProperty)
endif ()
if (RunCMake_GENERATOR MATCHES "Ninja")