Commit 05f4248e authored by Brad King's avatar Brad King Committed by Kitware Robot
Browse files

Merge topic 'cpp-modules'

39cbbb59 ninja: add experimental infrastructure to generate gcc-format modmap files
791b4d26 ninja: add experimental infrastructure to generate modmap files with dyndep
4b233591 ninja: Add experimental infrastructure for C++20 module dependency scanning
f814d3b3 cmNinjaTargetGenerator: use $OBJ_FILE for the object
b0fc2993 Treat the '.mpp' file extension as C++ code
988f9971 cmScanDepFormat: Fix name of our internal tool in parse errors
dacd93a2 ninja: De-duplicate version numbers required for ninja features
533386ca

 cmStandardLevelResolver: Factor out helper to capture stoi exceptions
Acked-by: Kitware Robot's avatarKitware Robot <kwrobot@kitware.com>
Acked-by: Ben Boeckel's avatarBen Boeckel <ben.boeckel@kitware.com>
Acked-by: default avatarRobert Maynard <robert.maynard@kitware.com>
Acked-by: Shannon Booth's avatarShannon Booth <shannon.ml.booth@gmail.com>
Merge-request: !5562
parents 520df288 39cbbb59
Pipeline #208003 canceled with stages
in 40 minutes and 55 seconds
......@@ -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
......@@ -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)
......
......@@ -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;
......
......@@ -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();
......
......@@ -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)) {
......
......@@ -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();
......
......@@ -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);
......
......@@ -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;
......
......@@ -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;
......
......@@ -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 ",