diff options
author | Ben Boeckel <ben.boeckel@kitware.com> | 2022-11-17 18:50:24 (GMT) |
---|---|---|
committer | Ben Boeckel <ben.boeckel@kitware.com> | 2022-11-18 12:54:31 (GMT) |
commit | a02d792c6e9bf9200ab652db6b6da415d80b578c (patch) | |
tree | 6c9d224ad9d3f53ca53cbf0e51b7092834c4308d | |
parent | 008c09d6db60f7c24781b47414ffa26edbd0e22d (diff) | |
download | CMake-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".
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; +} |