summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBen Boeckel <ben.boeckel@kitware.com>2022-11-17 18:50:24 (GMT)
committerBen Boeckel <ben.boeckel@kitware.com>2022-11-18 12:54:31 (GMT)
commita02d792c6e9bf9200ab652db6b6da415d80b578c (patch)
tree6c9d224ad9d3f53ca53cbf0e51b7092834c4308d
parent008c09d6db60f7c24781b47414ffa26edbd0e22d (diff)
downloadCMake-a02d792c6e9bf9200ab652db6b6da415d80b578c.zip
CMake-a02d792c6e9bf9200ab652db6b6da415d80b578c.tar.gz
CMake-a02d792c6e9bf9200ab652db6b6da415d80b578c.tar.bz2
cxxmodules: add properties to control scanning
The `CXX_SCAN_FOR_MODULES` property may be used to control scanning for targets and for source files rather than assuming "C++20 always needs to be scanned".
-rw-r--r--Help/manual/cmake-properties.7.rst2
-rw-r--r--Help/manual/cmake-variables.7.rst1
-rw-r--r--Help/prop_sf/CXX_SCAN_FOR_MODULES.rst19
-rw-r--r--Help/prop_tgt/CXX_SCAN_FOR_MODULES.rst22
-rw-r--r--Help/release/dev/cxx-scanning-properties.rst5
-rw-r--r--Help/variable/CMAKE_CXX_SCAN_FOR_MODULES.rst10
-rw-r--r--Source/cmNinjaTargetGenerator.cxx56
-rw-r--r--Source/cmNinjaTargetGenerator.h13
-rw-r--r--Source/cmTarget.cxx1
-rw-r--r--Tests/RunCMake/CXXModules/RunCMakeTest.cmake1
-rw-r--r--Tests/RunCMake/CXXModules/examples/scan_properties-stderr.txt9
-rw-r--r--Tests/RunCMake/CXXModules/examples/scan_properties/CMakeLists.txt54
-rw-r--r--Tests/RunCMake/CXXModules/examples/scan_properties/always_scan.cxx10
-rw-r--r--Tests/RunCMake/CXXModules/examples/scan_properties/join.cxx17
-rw-r--r--Tests/RunCMake/CXXModules/examples/scan_properties/main.cxx16
-rw-r--r--Tests/RunCMake/CXXModules/examples/scan_properties/module.cxx10
-rw-r--r--Tests/RunCMake/CXXModules/examples/scan_properties/never_scan.cxx8
17 files changed, 245 insertions, 9 deletions
diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst
index b17be82..e9ee681 100644
--- a/Help/manual/cmake-properties.7.rst
+++ b/Help/manual/cmake-properties.7.rst
@@ -195,6 +195,7 @@ Properties on Targets
/prop_tgt/CXX_MODULE_SET
/prop_tgt/CXX_MODULE_SET_NAME
/prop_tgt/CXX_MODULE_SETS
+ /prop_tgt/CXX_SCAN_FOR_MODULES
/prop_tgt/CXX_STANDARD
/prop_tgt/CXX_STANDARD_REQUIRED
/prop_tgt/DEBUG_POSTFIX
@@ -533,6 +534,7 @@ Properties on Source Files
/prop_sf/COMPILE_DEFINITIONS
/prop_sf/COMPILE_FLAGS
/prop_sf/COMPILE_OPTIONS
+ /prop_sf/CXX_SCAN_FOR_MODULES
/prop_sf/EXTERNAL_OBJECT
/prop_sf/Fortran_FORMAT
/prop_sf/Fortran_PREPROCESS
diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst
index cd46615..d66bb2b 100644
--- a/Help/manual/cmake-variables.7.rst
+++ b/Help/manual/cmake-variables.7.rst
@@ -417,6 +417,7 @@ Variables that Control the Build
/variable/CMAKE_CUDA_RESOLVE_DEVICE_SYMBOLS
/variable/CMAKE_CUDA_RUNTIME_LIBRARY
/variable/CMAKE_CUDA_SEPARABLE_COMPILATION
+ /variable/CMAKE_CXX_SCAN_FOR_MODULES
/variable/CMAKE_DEBUG_POSTFIX
/variable/CMAKE_DEFAULT_BUILD_TYPE
/variable/CMAKE_DEFAULT_CONFIGS
diff --git a/Help/prop_sf/CXX_SCAN_FOR_MODULES.rst b/Help/prop_sf/CXX_SCAN_FOR_MODULES.rst
new file mode 100644
index 0000000..9d6c0a5
--- /dev/null
+++ b/Help/prop_sf/CXX_SCAN_FOR_MODULES.rst
@@ -0,0 +1,19 @@
+CXX_SCAN_FOR_MODULES
+--------------------
+
+.. versionadded:: 3.26
+
+``CXX_SCAN_FOR_MODULES`` is a boolean specifying whether CMake will scan the
+source for C++ module dependencies. See also the
+:prop_tgt:`CXX_SCAN_FOR_MODULES` for target-wide settings.
+
+When this property is set ``ON``, CMake will scan the source at build time and
+add module dependency information to the compile line as necessary. When this
+property is set ``OFF``, CMake will not scan the source at build time. When
+this property is unset, the :prop_tgt:`CXX_SCAN_FOR_MODULES` property is
+consulted.
+
+Note that scanning is only performed if C++20 or higher is enabled for the
+target and the source uses the ``CXX`` language. Scanning for modules in
+sources belonging to file sets of type ``CXX_MODULES`` and
+``CXX_MODULES_HEADER_UNITS`` is always performed.
diff --git a/Help/prop_tgt/CXX_SCAN_FOR_MODULES.rst b/Help/prop_tgt/CXX_SCAN_FOR_MODULES.rst
new file mode 100644
index 0000000..5e89ba2
--- /dev/null
+++ b/Help/prop_tgt/CXX_SCAN_FOR_MODULES.rst
@@ -0,0 +1,22 @@
+CXX_SCAN_FOR_MODULES
+--------------------
+
+.. versionadded:: 3.26
+
+``CXX_SCAN_FOR_MODULES`` is a boolean specifying whether CMake will scan C++
+sources in the target for module dependencies. See also the
+:prop_sf:`CXX_SCAN_FOR_MODULES` for per-source settings which, if set,
+overrides the target-wide settings.
+
+This property is initialized by the value of the
+:variable:`CMAKE_CXX_SCAN_FOR_MODULES` variable if it is set when a target is
+created.
+
+When this property is set ``ON`` or unset, CMake will scan the target's
+``CXX`` sources at build time and add module dependency information to the
+compile line as necessary. When this property is set ``OFF``, CMake will not
+scan the target's ``CXX`` sources at build time.
+
+Note that scanning is only performed if C++20 or higher is enabled for the
+target. Scanning for modules in the target's sources belonging to file sets
+of type ``CXX_MODULES`` and ``CXX_MODULES_HEADER_UNITS`` is always performed.
diff --git a/Help/release/dev/cxx-scanning-properties.rst b/Help/release/dev/cxx-scanning-properties.rst
new file mode 100644
index 0000000..b393728
--- /dev/null
+++ b/Help/release/dev/cxx-scanning-properties.rst
@@ -0,0 +1,5 @@
+cxx-scanning-properties
+-----------------------
+
+* The :prop_tgt:`CXX_SCAN_FOR_MODULES` target and source file properties may
+ be used to enable or disable scanning for C++ module dependencies.
diff --git a/Help/variable/CMAKE_CXX_SCAN_FOR_MODULES.rst b/Help/variable/CMAKE_CXX_SCAN_FOR_MODULES.rst
new file mode 100644
index 0000000..0d6c636
--- /dev/null
+++ b/Help/variable/CMAKE_CXX_SCAN_FOR_MODULES.rst
@@ -0,0 +1,10 @@
+CMAKE_CXX_SCAN_FOR_MODULES
+--------------------------
+
+.. versionadded:: 3.26
+
+Whether to scan C++ source files for module dependencies.
+
+This variable is used to initialize the :prop_tgt:`CXX_SCAN_FOR_MODULES`
+property on all the targets. See that target property for additional
+information.
diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx
index ce73c17..d39e155 100644
--- a/Source/cmNinjaTargetGenerator.cxx
+++ b/Source/cmNinjaTargetGenerator.cxx
@@ -109,12 +109,13 @@ cmGlobalNinjaGenerator* cmNinjaTargetGenerator::GetGlobalGenerator() const
}
std::string cmNinjaTargetGenerator::LanguageCompilerRule(
- const std::string& lang, const std::string& config) const
+ const std::string& lang, const std::string& config,
+ WithScanning withScanning) const
{
return cmStrCat(
lang, "_COMPILER__",
cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()),
- '_', config);
+ withScanning == WithScanning::Yes ? "_scanned_" : "_unscanned_", config);
}
std::string cmNinjaTargetGenerator::LanguagePreprocessAndScanRule(
@@ -231,6 +232,32 @@ cmFileSet const* cmNinjaTargetGenerator::GetFileSetForSource(
return fsit->second;
}
+bool cmNinjaTargetGenerator::NeedDyndepForSource(std::string const& lang,
+ std::string const& config,
+ cmSourceFile const* sf)
+{
+ bool const needDyndep = this->NeedDyndep(lang, config);
+ if (!needDyndep) {
+ return false;
+ }
+ auto const* fs = this->GetFileSetForSource(config, sf);
+ if (fs &&
+ (fs->GetType() == "CXX_MODULES"_s ||
+ fs->GetType() == "CXX_MODULE_HEADER_UNITS"_s)) {
+ return true;
+ }
+ auto const sfProp = sf->GetProperty("CXX_SCAN_FOR_MODULES");
+ if (sfProp.IsSet()) {
+ return sfProp.IsOn();
+ }
+ auto const tgtProp =
+ this->GeneratorTarget->GetProperty("CXX_SCAN_FOR_MODULES");
+ if (tgtProp.IsSet()) {
+ return tgtProp.IsOn();
+ }
+ return true;
+}
+
std::string cmNinjaTargetGenerator::OrderDependsTargetForTarget(
const std::string& config)
{
@@ -671,6 +698,19 @@ cmNinjaRule GetScanRule(
void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang,
const std::string& config)
{
+ // For some cases we scan to dynamically discover dependencies.
+ bool const needDyndep = this->NeedDyndep(lang, config);
+
+ if (needDyndep) {
+ this->WriteCompileRule(lang, config, WithScanning::Yes);
+ }
+ this->WriteCompileRule(lang, config, WithScanning::No);
+}
+
+void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang,
+ const std::string& config,
+ WithScanning withScanning)
+{
cmRulePlaceholderExpander::RuleVariables vars;
vars.CMTargetName = this->GetGeneratorTarget()->GetName().c_str();
vars.CMTargetType =
@@ -690,7 +730,6 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang,
cmMakefile* mf = this->GetMakefile();
// For some cases we scan to dynamically discover dependencies.
- bool const needDyndep = this->NeedDyndep(lang, config);
bool const compilationPreprocesses = !this->NeedExplicitPreprocessing(lang);
std::string flags = "$FLAGS";
@@ -728,7 +767,7 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang,
this->GetLocalGenerator()->ConvertToOutputFormat(
cmSystemTools::GetCMakeCommand(), cmLocalGenerator::SHELL);
- if (needDyndep) {
+ if (withScanning == WithScanning::Yes) {
const auto& scanDepType = this->GetMakefile()->GetSafeDefinition(
cmStrCat("CMAKE_EXPERIMENTAL_", lang, "_SCANDEP_DEPFILE_FORMAT"));
@@ -834,7 +873,7 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang,
this->GetGlobalGenerator()->AddRule(rule);
}
- cmNinjaRule rule(this->LanguageCompilerRule(lang, config));
+ cmNinjaRule rule(this->LanguageCompilerRule(lang, config, withScanning));
// If using a response file, move defines, includes, and flags into it.
if (!responseFlag.empty()) {
rule.RspFile = "$RSP_FILE";
@@ -888,7 +927,7 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang,
}
}
- if (needDyndep && !modmapFormat.empty()) {
+ if (withScanning == WithScanning::Yes && !modmapFormat.empty()) {
std::string modmapFlags = mf->GetRequiredDefinition(
cmStrCat("CMAKE_EXPERIMENTAL_", lang, "_MODULE_MAP_FLAG"));
cmSystemTools::ReplaceString(modmapFlags, "<MODULE_MAP_FILE>",
@@ -1348,8 +1387,10 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement(
!(language == "RC" || (language == "CUDA" && !flag));
int const commandLineLengthLimit =
((lang_supports_response && this->ForceResponseFile())) ? -1 : 0;
+ bool const needDyndep = this->NeedDyndepForSource(language, config, source);
- cmNinjaBuild objBuild(this->LanguageCompilerRule(language, config));
+ cmNinjaBuild objBuild(this->LanguageCompilerRule(
+ language, config, needDyndep ? WithScanning::Yes : WithScanning::No));
cmNinjaVars& vars = objBuild.Variables;
vars["FLAGS"] = this->ComputeFlagsForObject(source, language, config);
vars["DEFINES"] = this->ComputeDefines(source, language, config);
@@ -1458,7 +1499,6 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement(
}
// For some cases we scan to dynamically discover dependencies.
- bool const needDyndep = this->NeedDyndep(language, config);
bool const compilationPreprocesses =
!this->NeedExplicitPreprocessing(language);
diff --git a/Source/cmNinjaTargetGenerator.h b/Source/cmNinjaTargetGenerator.h
index bb75fb3..d09f04b 100644
--- a/Source/cmNinjaTargetGenerator.h
+++ b/Source/cmNinjaTargetGenerator.h
@@ -19,6 +19,7 @@
#include "cmOSXBundleGenerator.h"
class cmCustomCommand;
+class cmFileSet;
class cmGeneratedFileStream;
class cmGeneratorTarget;
class cmLocalNinjaGenerator;
@@ -67,8 +68,14 @@ protected:
cmFileSet const* GetFileSetForSource(std::string const& config,
cmSourceFile const* sf);
+ enum class WithScanning
+ {
+ No,
+ Yes,
+ };
std::string LanguageCompilerRule(const std::string& lang,
- const std::string& config) const;
+ const std::string& config,
+ WithScanning withScanning) const;
std::string LanguagePreprocessAndScanRule(std::string const& lang,
const std::string& config) const;
std::string LanguageScanRule(std::string const& lang,
@@ -76,6 +83,8 @@ protected:
std::string LanguageDyndepRule(std::string const& lang,
const std::string& config) const;
bool NeedDyndep(std::string const& lang, std::string const& config) const;
+ bool NeedDyndepForSource(std::string const& lang, std::string const& config,
+ cmSourceFile const* sf);
bool NeedExplicitPreprocessing(std::string const& lang) const;
bool CompileWithDefines(std::string const& lang) const;
bool NeedCxxModuleSupport(std::string const& lang,
@@ -154,6 +163,8 @@ protected:
const std::string& config);
void WriteCompileRule(const std::string& language,
const std::string& config);
+ void WriteCompileRule(const std::string& language, const std::string& config,
+ WithScanning withScanning);
void WriteObjectBuildStatements(const std::string& config,
const std::string& fileConfig,
bool firstForConfig);
diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx
index 2955d7c..70a624d 100644
--- a/Source/cmTarget.cxx
+++ b/Source/cmTarget.cxx
@@ -526,6 +526,7 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type,
initProp("ANDROID_ANT_ADDITIONAL_OPTIONS");
initProp("BUILD_RPATH");
initProp("BUILD_RPATH_USE_ORIGIN");
+ initProp("CXX_SCAN_FOR_MODULES");
initProp("INSTALL_NAME_DIR");
initProp("INSTALL_REMOVE_ENVIRONMENT_RPATH");
initPropValue("INSTALL_RPATH", "");
diff --git a/Tests/RunCMake/CXXModules/RunCMakeTest.cmake b/Tests/RunCMake/CXXModules/RunCMakeTest.cmake
index 921fabd..81a086a 100644
--- a/Tests/RunCMake/CXXModules/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CXXModules/RunCMakeTest.cmake
@@ -138,6 +138,7 @@ if ("named" IN_LIST CMake_TEST_MODULE_COMPILATION)
set(RunCMake_CXXModules_NO_TEST 1)
run_cxx_module_test(circular)
unset(RunCMake_CXXModules_NO_TEST)
+ run_cxx_module_test(scan_properties)
endif ()
# Tests which use named modules in shared libraries.
diff --git a/Tests/RunCMake/CXXModules/examples/scan_properties-stderr.txt b/Tests/RunCMake/CXXModules/examples/scan_properties-stderr.txt
new file mode 100644
index 0000000..7d79bad
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/scan_properties-stderr.txt
@@ -0,0 +1,9 @@
+CMake Warning \(dev\) at CMakeLists.txt:20 \(target_sources\):
+ CMake's C\+\+ module support is experimental. It is meant only for
+ experimentation and feedback to CMake developers.
+This warning is for project developers. Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\):
+ C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is
+ experimental. It is meant only for compiler developers to try.
+This warning is for project developers. Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/examples/scan_properties/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/scan_properties/CMakeLists.txt
new file mode 100644
index 0000000..551c55c
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/scan_properties/CMakeLists.txt
@@ -0,0 +1,54 @@
+cmake_minimum_required(VERSION 3.24)
+project(scan_properties CXX)
+
+include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
+
+# To detect that not-to-be scanned sources are not scanned, add a `-D` to the
+# scan flags so that the files can detect whether scanning happened and error
+# if not.
+string(APPEND CMAKE_EXPERIMENTAL_CXX_MODULE_MAP_FLAG
+ " -DCMAKE_SCANNED_THIS_SOURCE")
+string(APPEND CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE
+ " -DCMAKE_SCANNED_THIS_SOURCE")
+
+set_property(SOURCE always_scan.cxx
+ PROPERTY CXX_SCAN_FOR_MODULES 1)
+set_property(SOURCE never_scan.cxx
+ PROPERTY CXX_SCAN_FOR_MODULES 0)
+
+add_executable(scans_everything)
+target_sources(scans_everything
+ PRIVATE
+ main.cxx
+ join.cxx
+ always_scan.cxx
+ never_scan.cxx
+ PRIVATE
+ FILE_SET CXX_MODULES
+ BASE_DIRS
+ "${CMAKE_CURRENT_SOURCE_DIR}"
+ FILES
+ module.cxx)
+target_compile_features(scans_everything PRIVATE cxx_std_20)
+target_compile_definitions(scans_everything PRIVATE SCAN_AT_TARGET_LEVEL=1)
+
+set(CMAKE_CXX_SCAN_FOR_MODULES 0)
+
+add_executable(no_scan_everything)
+target_sources(no_scan_everything
+ PRIVATE
+ main.cxx
+ join.cxx
+ always_scan.cxx
+ never_scan.cxx
+ PRIVATE
+ FILE_SET CXX_MODULES
+ BASE_DIRS
+ "${CMAKE_CURRENT_SOURCE_DIR}"
+ FILES
+ module.cxx)
+target_compile_features(no_scan_everything PRIVATE cxx_std_20)
+target_compile_definitions(no_scan_everything PRIVATE SCAN_AT_TARGET_LEVEL=0)
+
+add_test(NAME scanned COMMAND scans_everything)
+add_test(NAME unscanned COMMAND no_scan_everything)
diff --git a/Tests/RunCMake/CXXModules/examples/scan_properties/always_scan.cxx b/Tests/RunCMake/CXXModules/examples/scan_properties/always_scan.cxx
new file mode 100644
index 0000000..27087d7
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/scan_properties/always_scan.cxx
@@ -0,0 +1,10 @@
+#ifndef CMAKE_SCANNED_THIS_SOURCE
+# error "This file should have been scanned"
+#endif
+
+import M;
+
+int scanned_file()
+{
+ return from_module();
+}
diff --git a/Tests/RunCMake/CXXModules/examples/scan_properties/join.cxx b/Tests/RunCMake/CXXModules/examples/scan_properties/join.cxx
new file mode 100644
index 0000000..8184a40
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/scan_properties/join.cxx
@@ -0,0 +1,17 @@
+#if SCAN_AT_TARGET_LEVEL
+# ifndef CMAKE_SCANNED_THIS_SOURCE
+# error "This file should have been scanned"
+# endif
+#else
+# ifdef CMAKE_SCANNED_THIS_SOURCE
+# error "This file should not have been scanned"
+# endif
+#endif
+
+int scanned_file();
+int never_scan();
+
+int join()
+{
+ return scanned_file() + never_scan();
+}
diff --git a/Tests/RunCMake/CXXModules/examples/scan_properties/main.cxx b/Tests/RunCMake/CXXModules/examples/scan_properties/main.cxx
new file mode 100644
index 0000000..81e93f4
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/scan_properties/main.cxx
@@ -0,0 +1,16 @@
+#if SCAN_AT_TARGET_LEVEL
+# ifndef CMAKE_SCANNED_THIS_SOURCE
+# error "This file should have been scanned"
+# endif
+#else
+# ifdef CMAKE_SCANNED_THIS_SOURCE
+# error "This file should not have been scanned"
+# endif
+#endif
+
+int join();
+
+int main(int argc, char** argv)
+{
+ return join();
+}
diff --git a/Tests/RunCMake/CXXModules/examples/scan_properties/module.cxx b/Tests/RunCMake/CXXModules/examples/scan_properties/module.cxx
new file mode 100644
index 0000000..ad1e04d
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/scan_properties/module.cxx
@@ -0,0 +1,10 @@
+#ifndef CMAKE_SCANNED_THIS_SOURCE
+# error "This file should have been scanned"
+#endif
+
+export module M;
+
+export int from_module()
+{
+ return 0;
+}
diff --git a/Tests/RunCMake/CXXModules/examples/scan_properties/never_scan.cxx b/Tests/RunCMake/CXXModules/examples/scan_properties/never_scan.cxx
new file mode 100644
index 0000000..8374110
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/scan_properties/never_scan.cxx
@@ -0,0 +1,8 @@
+#ifdef CMAKE_SCANNED_THIS_SOURCE
+# error "This file should not have been scanned"
+#endif
+
+int never_scan()
+{
+ return 0;
+}