summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrad King <brad.king@kitware.com>2021-01-07 13:26:21 (GMT)
committerKitware Robot <kwrobot@kitware.com>2021-01-07 13:26:28 (GMT)
commit05f4248e3d89bf9d779140cf60f511126756ab02 (patch)
tree89ecb61830144a8885b2d7e79841d3d6784a6e23
parent520df2880b7f172371de5a6adc6d6ada0e45ac8d (diff)
parent39cbbb59a52c63cd90eebaecea64fd42c3fad1d8 (diff)
downloadCMake-05f4248e3d89bf9d779140cf60f511126756ab02.zip
CMake-05f4248e3d89bf9d779140cf60f511126756ab02.tar.gz
CMake-05f4248e3d89bf9d779140cf60f511126756ab02.tar.bz2
Merge topic 'cpp-modules'
39cbbb59a5 ninja: add experimental infrastructure to generate gcc-format modmap files 791b4d26d6 ninja: add experimental infrastructure to generate modmap files with dyndep 4b23359117 ninja: Add experimental infrastructure for C++20 module dependency scanning f814d3b3c6 cmNinjaTargetGenerator: use $OBJ_FILE for the object b0fc2993e1 Treat the '.mpp' file extension as C++ code 988f997100 cmScanDepFormat: Fix name of our internal tool in parse errors dacd93a2db ninja: De-duplicate version numbers required for ninja features 533386ca29 cmStandardLevelResolver: Factor out helper to capture stoi exceptions Acked-by: Kitware Robot <kwrobot@kitware.com> Acked-by: Ben Boeckel <ben.boeckel@kitware.com> Acked-by: Robert Maynard <robert.maynard@kitware.com> Acked-by: Shannon Booth <shannon.ml.booth@gmail.com> Merge-request: !5562
-rw-r--r--Help/dev/experimental.rst62
-rw-r--r--Modules/CMakeCXXCompiler.cmake.in2
-rw-r--r--Source/cmGlobalNinjaGenerator.cxx88
-rw-r--r--Source/cmGlobalNinjaGenerator.h20
-rw-r--r--Source/cmMakefile.cxx7
-rw-r--r--Source/cmNinjaTargetGenerator.cxx133
-rw-r--r--Source/cmNinjaTargetGenerator.h4
-rw-r--r--Source/cmRulePlaceholderExpander.cxx5
-rw-r--r--Source/cmRulePlaceholderExpander.h1
-rw-r--r--Source/cmScanDepFormat.cxx24
-rw-r--r--Source/cmSourceFile.h2
-rw-r--r--Source/cmStandardLevelResolver.cxx31
-rw-r--r--Source/cmake.cxx5
13 files changed, 309 insertions, 75 deletions
diff --git a/Help/dev/experimental.rst b/Help/dev/experimental.rst
index 4d2b076..d019161 100644
--- a/Help/dev/experimental.rst
+++ b/Help/dev/experimental.rst
@@ -7,4 +7,64 @@ See documentation on `CMake Development`_ for more information.
.. _`CMake Development`: README.rst
-No experimental features are under development in this version of CMake.
+C++20 Module Dependencies
+=========================
+
+The Ninja generator has experimental infrastructure supporting C++20 module
+dependency scanning. This is similar to the Fortran modules support, but
+relies on external tools to scan C++20 translation units for module
+dependencies. The approach is described by Kitware's `D1483r1`_ paper.
+
+The ``CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP`` variable can be set to ``1``
+in order to activate this undocumented experimental infrastructure. This
+is **intended to make the functionality available to compiler writers** so
+they can use it to develop and test their dependency scanning tool.
+The ``CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE`` variable must also be set
+to tell CMake how to invoke the C++20 module dependency scanning tool.
+
+For example, add code like the following to a test project:
+
+.. code-block:: cmake
+
+ set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1)
+ string(CONCAT CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE
+ "<CMAKE_CXX_COMPILER> <DEFINES> <INCLUDES> <FLAGS> <SOURCE>"
+ " -MT <DYNDEP_FILE> -MD -MF <DEP_FILE>"
+ " ${flags_to_scan_deps} -fdep-file=<DYNDEP_FILE> -fdep-output=<OBJECT>"
+ )
+
+The tool specified by ``CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE`` is
+expected to process the translation unit, write preprocessor dependencies
+to the file specified by the ``<DEP_FILE>`` placeholder, and write module
+dependencies to the file specified by the ``<DYNDEP_FILE>`` placeholder.
+
+The module dependencies should be written in the format described
+by the `P1689r3`_ paper.
+
+Compiler writers may try out their scanning functionality using
+the `cxx-modules-sandbox`_ test project, modified to set variables
+as above for their compiler.
+
+For compilers that generate module maps, tell CMake as follows:
+
+.. code-block:: cmake
+
+ set(CMAKE_EXPERIMENTAL_CXX_MODULE_MAP_FORMAT "gcc")
+ set(CMAKE_EXPERIMENTAL_CXX_MODULE_MAP_FLAG
+ "${compiler_flags_for_module_map} -fmodule-mapper=<MODULE_MAP_FILE>")
+
+Currently, the only supported format is ``gcc``. The format is described in
+the GCC documentation, but the relevant section for the purposes of CMake is:
+
+ A mapping file consisting of space-separated module-name, filename
+ pairs, one per line. Only the mappings for the direct imports and any
+ module export name need be provided. If other mappings are provided,
+ they override those stored in any imported CMI files. A repository
+ root may be specified in the mapping file by using ``$root`` as the
+ module name in the first active line.
+
+ -- GCC module mapper documentation
+
+.. _`D1483r1`: https://mathstuf.fedorapeople.org/fortran-modules/fortran-modules.html
+.. _`P1689r3`: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p1689r3.html
+.. _`cxx-modules-sandbox`: https://github.com/mathstuf/cxx-modules-sandbox
diff --git a/Modules/CMakeCXXCompiler.cmake.in b/Modules/CMakeCXXCompiler.cmake.in
index 92ae2ab..45acfe7 100644
--- a/Modules/CMakeCXXCompiler.cmake.in
+++ b/Modules/CMakeCXXCompiler.cmake.in
@@ -44,7 +44,7 @@ if(CMAKE_COMPILER_IS_MINGW)
set(MINGW 1)
endif()
set(CMAKE_CXX_COMPILER_ID_RUN 1)
-set(CMAKE_CXX_SOURCE_FILE_EXTENSIONS C;M;c++;cc;cpp;cxx;m;mm;CPP)
+set(CMAKE_CXX_SOURCE_FILE_EXTENSIONS C;M;c++;cc;cpp;cxx;m;mm;mpp;CPP)
set(CMAKE_CXX_IGNORE_EXTENSIONS inl;h;hpp;HPP;H;o;O;obj;OBJ;def;DEF;rc;RC)
foreach (lang C OBJC OBJCXX)
diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx
index a95ab25..4f17408 100644
--- a/Source/cmGlobalNinjaGenerator.cxx
+++ b/Source/cmGlobalNinjaGenerator.cxx
@@ -555,6 +555,7 @@ void cmGlobalNinjaGenerator::Generate()
this->TargetAll = this->NinjaOutputPath("all");
this->CMakeCacheFile = this->NinjaOutputPath("CMakeCache.txt");
this->DisableCleandead = false;
+ this->DiagnosedCxxModuleSupport = false;
this->PolicyCMP0058 =
this->LocalGenerators[0]->GetMakefile()->GetPolicyStatus(
@@ -755,6 +756,37 @@ bool cmGlobalNinjaGenerator::CheckLanguages(
return true;
}
+bool cmGlobalNinjaGenerator::CheckCxxModuleSupport()
+{
+ bool const diagnose = !this->DiagnosedCxxModuleSupport &&
+ !this->CMakeInstance->GetIsInTryCompile();
+ if (diagnose) {
+ this->DiagnosedCxxModuleSupport = true;
+ this->GetCMakeInstance()->IssueMessage(
+ MessageType::AUTHOR_WARNING,
+ "C++20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP "
+ "is experimental. It is meant only for compiler developers to try.");
+ }
+ if (this->NinjaSupportsDyndeps) {
+ return true;
+ }
+ if (diagnose) {
+ std::ostringstream e;
+ /* clang-format off */
+ e <<
+ "The Ninja generator does not support C++20 modules "
+ "using Ninja version \n"
+ " " << this->NinjaVersion << "\n"
+ "due to lack of required features. "
+ "Ninja " << RequiredNinjaVersionForDyndeps() << " or higher is required."
+ ;
+ /* clang-format on */
+ this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, e.str());
+ cmSystemTools::SetFatalErrorOccured();
+ }
+ return false;
+}
+
bool cmGlobalNinjaGenerator::CheckFortran(cmMakefile* mf) const
{
if (this->NinjaSupportsDyndeps) {
@@ -766,7 +798,8 @@ bool cmGlobalNinjaGenerator::CheckFortran(cmMakefile* mf) const
e <<
"The Ninja generator does not support Fortran using Ninja version\n"
" " << this->NinjaVersion << "\n"
- "due to lack of required features. Ninja 1.10 or higher is required."
+ "due to lack of required features. "
+ "Ninja " << RequiredNinjaVersionForDyndeps() << " or higher is required."
;
/* clang-format on */
mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
@@ -785,7 +818,9 @@ bool cmGlobalNinjaGenerator::CheckISPC(cmMakefile* mf) const
e <<
"The Ninja generator does not support ISPC using Ninja version\n"
" " << this->NinjaVersion << "\n"
- "due to lack of required features. Ninja 1.10 or higher is required."
+ "due to lack of required features. "
+ "Ninja " << RequiredNinjaVersionForMultipleOutputs() <<
+ " or higher is required."
;
/* clang-format on */
mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
@@ -2336,7 +2371,7 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile(
std::string const& arg_dd, std::vector<std::string> const& arg_ddis,
std::string const& module_dir,
std::vector<std::string> const& linked_target_dirs,
- std::string const& arg_lang)
+ std::string const& arg_lang, std::string const& arg_modmapfmt)
{
// Setup path conversions.
{
@@ -2423,6 +2458,48 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile(
build.Variables.emplace("restat", "1");
}
+ if (arg_modmapfmt.empty()) {
+ // nothing to do.
+ } else {
+ std::stringstream mm;
+ if (arg_modmapfmt == "gcc") {
+ // Documented in GCC's documentation. The format is a series of lines
+ // with a module name and the associated filename separated by
+ // spaces. The first line may use `$root` as the module name to
+ // specify a "repository root". That is used to anchor any relative
+ // paths present in the file (CMake should never generate any).
+
+ // Write the root directory to use for module paths.
+ mm << "$root .\n";
+
+ for (auto const& l : object.Provides) {
+ auto m = mod_files.find(l.LogicalName);
+ if (m != mod_files.end()) {
+ mm << l.LogicalName << " " << this->ConvertToNinjaPath(m->second)
+ << "\n";
+ }
+ }
+ for (auto const& r : object.Requires) {
+ auto m = mod_files.find(r.LogicalName);
+ if (m != mod_files.end()) {
+ mm << r.LogicalName << " " << this->ConvertToNinjaPath(m->second)
+ << "\n";
+ }
+ }
+ } else {
+ cmSystemTools::Error(
+ cmStrCat("-E cmake_ninja_dyndep does not understand the ",
+ arg_modmapfmt, " module map format"));
+ return false;
+ }
+
+ // XXX(modmap): If changing this path construction, change
+ // `cmNinjaTargetGenerator::WriteObjectBuildStatements` to generate the
+ // corresponding file path.
+ cmGeneratedFileStream mmf(cmStrCat(object.PrimaryOutput, ".modmap"));
+ mmf << mm.str();
+ }
+
this->WriteBuild(ddf, build);
}
}
@@ -2446,6 +2523,7 @@ int cmcmd_cmake_ninja_dyndep(std::vector<std::string>::const_iterator argBeg,
std::string arg_dd;
std::string arg_lang;
std::string arg_tdi;
+ std::string arg_modmapfmt;
std::vector<std::string> arg_ddis;
for (std::string const& arg : arg_full) {
if (cmHasLiteralPrefix(arg, "--tdi=")) {
@@ -2454,6 +2532,8 @@ int cmcmd_cmake_ninja_dyndep(std::vector<std::string>::const_iterator argBeg,
arg_lang = arg.substr(7);
} else if (cmHasLiteralPrefix(arg, "--dd=")) {
arg_dd = arg.substr(5);
+ } else if (cmHasLiteralPrefix(arg, "--modmapfmt=")) {
+ arg_modmapfmt = arg.substr(12);
} else if (!cmHasLiteralPrefix(arg, "--") &&
cmHasLiteralSuffix(arg, ".ddi")) {
arg_ddis.push_back(arg);
@@ -2512,7 +2592,7 @@ int cmcmd_cmake_ninja_dyndep(std::vector<std::string>::const_iterator argBeg,
if (!ggd ||
!cm::static_reference_cast<cmGlobalNinjaGenerator>(ggd).WriteDyndepFile(
dir_top_src, dir_top_bld, dir_cur_src, dir_cur_bld, arg_dd, arg_ddis,
- module_dir, linked_target_dirs, arg_lang)) {
+ module_dir, linked_target_dirs, arg_lang, arg_modmapfmt)) {
return 1;
}
return 0;
diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h
index 0e94678..fd8542f 100644
--- a/Source/cmGlobalNinjaGenerator.h
+++ b/Source/cmGlobalNinjaGenerator.h
@@ -396,15 +396,13 @@ public:
bool HasOutputPathPrefix() const { return !this->OutputPathPrefix.empty(); }
void StripNinjaOutputPathPrefixAsSuffix(std::string& path);
- bool WriteDyndepFile(std::string const& dir_top_src,
- std::string const& dir_top_bld,
- std::string const& dir_cur_src,
- std::string const& dir_cur_bld,
- std::string const& arg_dd,
- std::vector<std::string> const& arg_ddis,
- std::string const& module_dir,
- std::vector<std::string> const& linked_target_dirs,
- std::string const& arg_lang);
+ bool WriteDyndepFile(
+ std::string const& dir_top_src, std::string const& dir_top_bld,
+ std::string const& dir_cur_src, std::string const& dir_cur_bld,
+ std::string const& arg_dd, std::vector<std::string> const& arg_ddis,
+ std::string const& module_dir,
+ std::vector<std::string> const& linked_target_dirs,
+ std::string const& arg_lang, std::string const& arg_modmapfmt);
virtual std::string BuildAlias(const std::string& alias,
const std::string& /*config*/) const
@@ -448,6 +446,8 @@ public:
bool IsSingleConfigUtility(cmGeneratorTarget const* target) const;
+ bool CheckCxxModuleSupport();
+
protected:
void Generate() override;
@@ -568,6 +568,8 @@ private:
bool NinjaSupportsMultipleOutputs = false;
bool NinjaSupportsMetadataOnRegeneration = false;
+ bool DiagnosedCxxModuleSupport = false;
+
private:
void InitOutputPathPrefix();
diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx
index 0faef15..e4c8e4b 100644
--- a/Source/cmMakefile.cxx
+++ b/Source/cmMakefile.cxx
@@ -1216,9 +1216,10 @@ void cmMakefile::AddCustomCommandOldStyle(
};
// Each output must get its own copy of this rule.
- cmsys::RegularExpression sourceFiles("\\.(C|M|c|c\\+\\+|cc|cpp|cxx|cu|m|mm|"
- "rc|def|r|odl|idl|hpj|bat|h|h\\+\\+|"
- "hm|hpp|hxx|in|txx|inl)$");
+ cmsys::RegularExpression sourceFiles(
+ "\\.(C|M|c|c\\+\\+|cc|cpp|cxx|mpp|cu|m|mm|"
+ "rc|def|r|odl|idl|hpj|bat|h|h\\+\\+|"
+ "hm|hpp|hxx|in|txx|inl)$");
// Choose whether to use a main dependency.
if (sourceFiles.find(source)) {
diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx
index f1bd760..672b579 100644
--- a/Source/cmNinjaTargetGenerator.cxx
+++ b/Source/cmNinjaTargetGenerator.cxx
@@ -35,6 +35,7 @@
#include "cmRange.h"
#include "cmRulePlaceholderExpander.h"
#include "cmSourceFile.h"
+#include "cmStandardLevelResolver.h"
#include "cmState.h"
#include "cmStateTypes.h"
#include "cmStringAlgorithms.h"
@@ -146,9 +147,26 @@ std::string cmNinjaTargetGenerator::LanguageDyndepRule(
'_', config);
}
-bool cmNinjaTargetGenerator::NeedDyndep(std::string const& lang) const
+bool cmNinjaTargetGenerator::NeedCxxModuleSupport(
+ std::string const& lang, std::string const& config) const
{
- return lang == "Fortran";
+ if (lang != "CXX") {
+ return false;
+ }
+ if (!this->Makefile->IsOn("CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP")) {
+ return false;
+ }
+ cmGeneratorTarget const* tgt = this->GetGeneratorTarget();
+ cmStandardLevelResolver standardResolver(this->Makefile);
+ bool const uses_cxx20 =
+ standardResolver.HaveStandardAvailable(tgt, "CXX", config, "cxx_std_20");
+ return uses_cxx20 && this->GetGlobalGenerator()->CheckCxxModuleSupport();
+}
+
+bool cmNinjaTargetGenerator::NeedDyndep(std::string const& lang,
+ std::string const& config) const
+{
+ return lang == "Fortran" || this->NeedCxxModuleSupport(lang, config);
}
std::string cmNinjaTargetGenerator::OrderDependsTargetForTarget(
@@ -530,8 +548,9 @@ cmNinjaRule GetScanRule(
scanVars.CMTargetName = vars.CMTargetName;
scanVars.CMTargetType = vars.CMTargetType;
scanVars.Language = vars.Language;
- scanVars.Object = "$out"; // for RULE_LAUNCH_COMPILE
+ scanVars.Object = "$OBJ_FILE";
scanVars.PreprocessedSource = "$out";
+ scanVars.DynDepFile = "$DYNDEP_INTERMEDIATE_FILE";
scanVars.DependencyFile = rule.DepFile.c_str();
scanVars.DependencyTarget = "$out";
@@ -586,7 +605,7 @@ 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);
+ bool const needDyndep = this->NeedDyndep(lang, config);
bool const compilationPreprocesses = !this->NeedExplicitPreprocessing(lang);
std::string flags = "$FLAGS";
@@ -601,6 +620,10 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang,
responseFlag = "@";
}
}
+ std::string const modmapFormatVar =
+ cmStrCat("CMAKE_EXPERIMENTAL_", lang, "_MODULE_MAP_FORMAT");
+ std::string const modmapFormat =
+ this->Makefile->GetSafeDefinition(modmapFormatVar);
std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander(
this->GetLocalGenerator()->CreateRulePlaceholderExpander());
@@ -624,16 +647,26 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang,
// Rule to scan dependencies of sources that need preprocessing.
{
std::vector<std::string> scanCommands;
- std::string const& scanRuleName =
- this->LanguagePreprocessAndScanRule(lang, config);
- std::string const& ppCommmand = mf->GetRequiredDefinition(
- cmStrCat("CMAKE_", lang, "_PREPROCESS_SOURCE"));
- cmExpandList(ppCommmand, scanCommands);
- for (std::string& i : scanCommands) {
- i = cmStrCat(launcher, i);
+ std::string scanRuleName;
+ if (compilationPreprocesses) {
+ scanRuleName = this->LanguageScanRule(lang, config);
+ std::string const& scanCommand = mf->GetRequiredDefinition(
+ cmStrCat("CMAKE_EXPERIMENTAL_", lang, "_SCANDEP_SOURCE"));
+ cmExpandList(scanCommand, scanCommands);
+ for (std::string& i : scanCommands) {
+ i = cmStrCat(launcher, i);
+ }
+ } else {
+ scanRuleName = this->LanguagePreprocessAndScanRule(lang, config);
+ std::string const& ppCommmand = mf->GetRequiredDefinition(
+ cmStrCat("CMAKE_", lang, "_PREPROCESS_SOURCE"));
+ cmExpandList(ppCommmand, scanCommands);
+ for (std::string& i : scanCommands) {
+ i = cmStrCat(launcher, i);
+ }
+ scanCommands.emplace_back(GetScanCommand(cmakeCmd, tdi, lang, "$out",
+ "$DYNDEP_INTERMEDIATE_FILE"));
}
- scanCommands.emplace_back(GetScanCommand(cmakeCmd, tdi, lang, "$out",
- "$DYNDEP_INTERMEDIATE_FILE"));
auto scanRule = GetScanRule(
scanRuleName, vars, responseFlag, flags, rulePlaceholderExpander.get(),
@@ -641,12 +674,18 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang,
scanRule.Comment =
cmStrCat("Rule for generating ", lang, " dependencies.");
- scanRule.Description = cmStrCat("Building ", lang, " preprocessed $out");
+ if (compilationPreprocesses) {
+ scanRule.Description =
+ cmStrCat("Scanning $in for ", lang, " dependencies");
+ } else {
+ scanRule.Description =
+ cmStrCat("Building ", lang, " preprocessed $out");
+ }
this->GetGlobalGenerator()->AddRule(scanRule);
}
- {
+ if (!compilationPreprocesses) {
// Compilation will not preprocess, so it does not need the defines
// unless the compiler wants them for some other purpose.
if (!this->CompileWithDefines(lang)) {
@@ -681,12 +720,16 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang,
// Run CMake dependency scanner on the source file (using the preprocessed
// source if that was performed).
+ std::string ddModmapArg;
+ if (!modmapFormat.empty()) {
+ ddModmapArg += cmStrCat(" --modmapfmt=", modmapFormat);
+ }
{
std::vector<std::string> ddCmds;
{
- std::string ccmd =
- cmStrCat(cmakeCmd, " -E cmake_ninja_dyndep --tdi=", tdi,
- " --lang=", lang, " --dd=$out @", rule.RspFile);
+ std::string ccmd = cmStrCat(
+ cmakeCmd, " -E cmake_ninja_dyndep --tdi=", tdi, " --lang=", lang,
+ ddModmapArg, " --dd=$out @", rule.RspFile);
ddCmds.emplace_back(std::move(ccmd));
}
rule.Command = this->GetLocalGenerator()->BuildCommandLine(ddCmds);
@@ -748,6 +791,14 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang,
}
}
+ if (needDyndep && !modmapFormat.empty()) {
+ std::string modmapFlags = mf->GetRequiredDefinition(
+ cmStrCat("CMAKE_EXPERIMENTAL_", lang, "_MODULE_MAP_FLAG"));
+ cmSystemTools::ReplaceString(modmapFlags, "<MODULE_MAP_FILE>",
+ "$DYNDEP_MODULE_MAP_FILE");
+ flags += cmStrCat(' ', modmapFlags);
+ }
+
vars.Flags = flags.c_str();
vars.DependencyFile = rule.DepFile.c_str();
@@ -1053,6 +1104,7 @@ cmNinjaBuild GetScanBuildStatement(const std::string& ruleName,
const std::string& ppFileName,
bool compilePP, bool compilePPWithDefines,
cmNinjaBuild& objBuild, cmNinjaVars& vars,
+ std::string const& modmapFormat,
const std::string& objectFileName,
cmLocalGenerator* lg)
{
@@ -1123,6 +1175,15 @@ cmNinjaBuild GetScanBuildStatement(const std::string& ruleName,
vars.erase("DEP_FILE");
}
+ if (!modmapFormat.empty()) {
+ // XXX(modmap): If changing this path construction, change
+ // `cmGlobalNinjaGenerator::WriteDyndep` to expect the corresponding
+ // file path.
+ std::string const ddModmapFile = cmStrCat(objectFileName, ".modmap");
+ scanBuild.Variables["DYNDEP_MODULE_MAP_FILE"] = ddModmapFile;
+ scanBuild.ImplicitOuts.push_back(ddModmapFile);
+ }
+
return scanBuild;
}
}
@@ -1262,10 +1323,17 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement(
}
// For some cases we scan to dynamically discover dependencies.
- bool const needDyndep = this->NeedDyndep(language);
+ bool const needDyndep = this->NeedDyndep(language, config);
bool const compilationPreprocesses =
!this->NeedExplicitPreprocessing(language);
+ std::string modmapFormat;
+ if (needDyndep) {
+ std::string const modmapFormatVar =
+ cmStrCat("CMAKE_EXPERIMENTAL_", language, "_MODULE_MAP_FORMAT");
+ modmapFormat = this->Makefile->GetSafeDefinition(modmapFormatVar);
+ }
+
if (needDyndep) {
// If source/target has preprocessing turned off, we still need to
// generate an explicit dependency step
@@ -1295,7 +1363,7 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement(
cmNinjaBuild ppBuild = GetScanBuildStatement(
scanRuleName, ppFileName, compilePP, compilePPWithDefines, objBuild,
- vars, objectFileName, this->LocalGenerator);
+ vars, modmapFormat, objectFileName, this->LocalGenerator);
if (compilePP) {
// In case compilation requires flags that are incompatible with
@@ -1331,6 +1399,12 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement(
std::string const dyndep = this->GetDyndepFilePath(language, config);
objBuild.OrderOnlyDeps.push_back(dyndep);
vars["dyndep"] = dyndep;
+
+ if (!modmapFormat.empty()) {
+ std::string const ddModmapFile = cmStrCat(objectFileName, ".modmap");
+ vars["DYNDEP_MODULE_MAP_FILE"] = ddModmapFile;
+ objBuild.OrderOnlyDeps.push_back(ddModmapFile);
+ }
}
this->EnsureParentDirectoryExists(objectFileName);
@@ -1444,17 +1518,26 @@ void cmNinjaTargetGenerator::WriteTargetDependInfo(std::string const& lang,
tdi["compiler-id"] = this->Makefile->GetSafeDefinition(
cmStrCat("CMAKE_", lang, "_COMPILER_ID"));
+ std::string mod_dir;
if (lang == "Fortran") {
- std::string mod_dir = this->GeneratorTarget->GetFortranModuleDirectory(
+ mod_dir = this->GeneratorTarget->GetFortranModuleDirectory(
this->Makefile->GetHomeOutputDirectory());
- if (mod_dir.empty()) {
- mod_dir = this->Makefile->GetCurrentBinaryDirectory();
- }
- tdi["module-dir"] = mod_dir;
+ } else if (lang == "CXX") {
+ mod_dir =
+ cmSystemTools::CollapseFullPath(this->GeneratorTarget->ObjectDirectory);
+ }
+ if (mod_dir.empty()) {
+ mod_dir = this->Makefile->GetCurrentBinaryDirectory();
+ }
+ tdi["module-dir"] = mod_dir;
+
+ if (lang == "Fortran") {
tdi["submodule-sep"] =
this->Makefile->GetSafeDefinition("CMAKE_Fortran_SUBMODULE_SEP");
tdi["submodule-ext"] =
this->Makefile->GetSafeDefinition("CMAKE_Fortran_SUBMODULE_EXT");
+ } else if (lang == "CXX") {
+ // No extra information necessary.
}
tdi["dir-cur-bld"] = this->Makefile->GetCurrentBinaryDirectory();
diff --git a/Source/cmNinjaTargetGenerator.h b/Source/cmNinjaTargetGenerator.h
index 83a4342..79dc622 100644
--- a/Source/cmNinjaTargetGenerator.h
+++ b/Source/cmNinjaTargetGenerator.h
@@ -71,9 +71,11 @@ protected:
const std::string& config) const;
std::string LanguageDyndepRule(std::string const& lang,
const std::string& config) const;
- bool NeedDyndep(std::string const& lang) const;
+ bool NeedDyndep(std::string const& lang, std::string const& config) const;
bool NeedExplicitPreprocessing(std::string const& lang) const;
bool CompileWithDefines(std::string const& lang) const;
+ bool NeedCxxModuleSupport(std::string const& lang,
+ std::string const& config) const;
std::string OrderDependsTargetForTarget(const std::string& config);
diff --git a/Source/cmRulePlaceholderExpander.cxx b/Source/cmRulePlaceholderExpander.cxx
index 5363fef..d00bbdf 100644
--- a/Source/cmRulePlaceholderExpander.cxx
+++ b/Source/cmRulePlaceholderExpander.cxx
@@ -44,6 +44,11 @@ std::string cmRulePlaceholderExpander::ExpandRuleVariable(
return replaceValues.Source;
}
}
+ if (replaceValues.DynDepFile) {
+ if (variable == "DYNDEP_FILE") {
+ return replaceValues.DynDepFile;
+ }
+ }
if (replaceValues.PreprocessedSource) {
if (variable == "PREPROCESSED_SOURCE") {
return replaceValues.PreprocessedSource;
diff --git a/Source/cmRulePlaceholderExpander.h b/Source/cmRulePlaceholderExpander.h
index 710f8a6..f8dc368 100644
--- a/Source/cmRulePlaceholderExpander.h
+++ b/Source/cmRulePlaceholderExpander.h
@@ -41,6 +41,7 @@ public:
const char* Source = nullptr;
const char* AssemblySource = nullptr;
const char* PreprocessedSource = nullptr;
+ const char* DynDepFile = nullptr;
const char* Output = nullptr;
const char* Object = nullptr;
const char* ObjectDir = nullptr;
diff --git a/Source/cmScanDepFormat.cxx b/Source/cmScanDepFormat.cxx
index 40bf4c9..e046069 100644
--- a/Source/cmScanDepFormat.cxx
+++ b/Source/cmScanDepFormat.cxx
@@ -55,9 +55,8 @@ static Json::Value EncodeFilename(std::string const& path)
#define PARSE_BLOB(val, res) \
do { \
if (!ParseFilename(val, res)) { \
- cmSystemTools::Error( \
- cmStrCat("-E cmake_ninja_depends failed to parse ", arg_pp, \
- ": invalid blob")); \
+ cmSystemTools::Error(cmStrCat("-E cmake_ninja_dyndep failed to parse ", \
+ arg_pp, ": invalid blob")); \
return false; \
} \
} while (0)
@@ -65,9 +64,8 @@ static Json::Value EncodeFilename(std::string const& path)
#define PARSE_FILENAME(val, res) \
do { \
if (!ParseFilename(val, res)) { \
- cmSystemTools::Error( \
- cmStrCat("-E cmake_ninja_depends failed to parse ", arg_pp, \
- ": invalid filename")); \
+ cmSystemTools::Error(cmStrCat("-E cmake_ninja_dyndep failed to parse ", \
+ arg_pp, ": invalid filename")); \
return false; \
} \
\
@@ -84,7 +82,7 @@ bool cmScanDepFormat_P1689_Parse(std::string const& arg_pp, cmSourceInfo* info)
{
Json::Reader reader;
if (!reader.parse(ppf, ppio, false)) {
- cmSystemTools::Error(cmStrCat("-E cmake_ninja_depends failed to parse ",
+ cmSystemTools::Error(cmStrCat("-E cmake_ninja_dyndep failed to parse ",
arg_pp,
reader.getFormattedErrorMessages()));
return false;
@@ -93,7 +91,7 @@ bool cmScanDepFormat_P1689_Parse(std::string const& arg_pp, cmSourceInfo* info)
Json::Value const& version = ppi["version"];
if (version.asUInt() != 0) {
- cmSystemTools::Error(cmStrCat("-E cmake_ninja_depends failed to parse ",
+ cmSystemTools::Error(cmStrCat("-E cmake_ninja_dyndep failed to parse ",
arg_pp, ": version ", version.asString()));
return false;
}
@@ -101,7 +99,7 @@ bool cmScanDepFormat_P1689_Parse(std::string const& arg_pp, cmSourceInfo* info)
Json::Value const& rules = ppi["rules"];
if (rules.isArray()) {
if (rules.size() != 1) {
- cmSystemTools::Error(cmStrCat("-E cmake_ninja_depends failed to parse ",
+ cmSystemTools::Error(cmStrCat("-E cmake_ninja_dyndep failed to parse ",
arg_pp, ": expected 1 source entry"));
return false;
}
@@ -109,9 +107,9 @@ bool cmScanDepFormat_P1689_Parse(std::string const& arg_pp, cmSourceInfo* info)
for (auto const& rule : rules) {
Json::Value const& workdir = rule["work-directory"];
if (!workdir.isString()) {
- cmSystemTools::Error(
- cmStrCat("-E cmake_ninja_depends failed to parse ", arg_pp,
- ": work-directory is not a string"));
+ cmSystemTools::Error(cmStrCat("-E cmake_ninja_dyndep failed to parse ",
+ arg_pp,
+ ": work-directory is not a string"));
return false;
}
std::string work_directory;
@@ -134,7 +132,7 @@ bool cmScanDepFormat_P1689_Parse(std::string const& arg_pp, cmSourceInfo* info)
if (outputs.isArray()) {
if (outputs.empty()) {
cmSystemTools::Error(
- cmStrCat("-E cmake_ninja_depends failed to parse ", arg_pp,
+ cmStrCat("-E cmake_ninja_dyndep failed to parse ", arg_pp,
": expected at least one 1 output"));
return false;
}
diff --git a/Source/cmSourceFile.h b/Source/cmSourceFile.h
index 94b5cc8..76a5ded 100644
--- a/Source/cmSourceFile.h
+++ b/Source/cmSourceFile.h
@@ -175,7 +175,7 @@ private:
#define CM_HEADER_REGEX "\\.(h|hh|h\\+\\+|hm|hpp|hxx|in|txx|inl)$"
#define CM_SOURCE_REGEX \
- "\\.(C|F|M|c|c\\+\\+|cc|cpp|cxx|cu|f|f90|for|fpp|ftn|m|mm|" \
+ "\\.(C|F|M|c|c\\+\\+|cc|cpp|mpp|cxx|cu|f|f90|for|fpp|ftn|m|mm|" \
"rc|def|r|odl|idl|hpj|bat)$"
#define CM_PCH_REGEX "cmake_pch(_[^.]+)?\\.(h|hxx)$"
diff --git a/Source/cmStandardLevelResolver.cxx b/Source/cmStandardLevelResolver.cxx
index 5d8ccf1..bf6925e 100644
--- a/Source/cmStandardLevelResolver.cxx
+++ b/Source/cmStandardLevelResolver.cxx
@@ -44,6 +44,16 @@ struct StandardNeeded
int value;
};
+int ParseStd(std::string const& level)
+{
+ try {
+ return std::stoi(level);
+ } catch (std::invalid_argument&) {
+ // Fall through to use an invalid value.
+ }
+ return -1;
+}
+
struct StanardLevelComputer
{
explicit StanardLevelComputer(std::string lang, std::vector<int> levels,
@@ -113,17 +123,8 @@ struct StanardLevelComputer
standardStr = "03";
}
- int standardValue = -1;
- int defaultValue = -1;
- try {
- standardValue = std::stoi(standardStr);
- defaultValue = std::stoi(*defaultStd);
- } catch (std::invalid_argument&) {
- // fall through as we want an error
- // when we can't find the bad value in the `stds` vector
- }
-
- auto stdIt = std::find(cm::cbegin(stds), cm::cend(stds), standardValue);
+ auto stdIt =
+ std::find(cm::cbegin(stds), cm::cend(stds), ParseStd(standardStr));
if (stdIt == cm::cend(stds)) {
std::string e =
cmStrCat(this->Language, "_STANDARD is set to invalid value '",
@@ -134,7 +135,7 @@ struct StanardLevelComputer
}
auto defaultStdIt =
- std::find(cm::cbegin(stds), cm::cend(stds), defaultValue);
+ std::find(cm::cbegin(stds), cm::cend(stds), ParseStd(*defaultStd));
if (defaultStdIt == cm::cend(stds)) {
std::string e = cmStrCat("CMAKE_", this->Language,
"_STANDARD_DEFAULT is set to invalid value '",
@@ -195,7 +196,7 @@ struct StanardLevelComputer
if (existingStandard) {
existingLevelIter =
std::find(cm::cbegin(this->Levels), cm::cend(this->Levels),
- std::stoi(*existingStandard));
+ ParseStd(*existingStandard));
if (existingLevelIter == cm::cend(this->Levels)) {
const std::string e =
cmStrCat("The ", this->Language, "_STANDARD property on target \"",
@@ -240,7 +241,7 @@ struct StanardLevelComputer
}
// convert defaultStandard to an integer
if (std::find(cm::cbegin(this->Levels), cm::cend(this->Levels),
- std::stoi(*defaultStandard)) == cm::cend(this->Levels)) {
+ ParseStd(*defaultStandard)) == cm::cend(this->Levels)) {
const std::string e = cmStrCat("The CMAKE_", this->Language,
"_STANDARD_DEFAULT variable contains an "
"invalid value: \"",
@@ -257,7 +258,7 @@ struct StanardLevelComputer
auto existingLevelIter =
std::find(cm::cbegin(this->Levels), cm::cend(this->Levels),
- std::stoi(*existingStandard));
+ ParseStd(*existingStandard));
if (existingLevelIter == cm::cend(this->Levels)) {
const std::string e =
cmStrCat("The ", this->Language, "_STANDARD property on target \"",
diff --git a/Source/cmake.cxx b/Source/cmake.cxx
index d6360a7..48848a7 100644
--- a/Source/cmake.cxx
+++ b/Source/cmake.cxx
@@ -208,8 +208,9 @@ cmake::cmake(Role role, cmState::Mode mode)
};
// The "c" extension MUST precede the "C" extension.
- setupExts(this->CLikeSourceFileExtensions,
- { "c", "C", "c++", "cc", "cpp", "cxx", "cu", "m", "M", "mm" });
+ setupExts(
+ this->CLikeSourceFileExtensions,
+ { "c", "C", "c++", "cc", "cpp", "cxx", "cu", "mpp", "m", "M", "mm" });
setupExts(this->HeaderFileExtensions,
{ "h", "hh", "h++", "hm", "hpp", "hxx", "in", "txx" });
setupExts(this->CudaFileExtensions, { "cu" });