cmDyndepCollation: factor out metadata writing for dyndep

This commit is contained in:
Ben Boeckel
2022-11-30 09:39:02 -05:00
parent 2f93a272d0
commit f4a17b29d3
3 changed files with 355 additions and 313 deletions

View File

@@ -5,6 +5,8 @@
#include <algorithm>
#include <map>
#include <ostream>
#include <set>
#include <utility>
#include <vector>
@@ -17,6 +19,7 @@
#include "cmExportBuildFileGenerator.h"
#include "cmExportSet.h"
#include "cmFileSet.h"
#include "cmGeneratedFileStream.h"
#include "cmGeneratorExpression.h" // IWYU pragma: keep
#include "cmGeneratorTarget.h"
#include "cmGlobalGenerator.h"
@@ -26,6 +29,8 @@
#include "cmInstallGenerator.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmOutputConverter.h"
#include "cmScanDepFormat.h"
#include "cmSourceFile.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
@@ -261,10 +266,56 @@ void cmDyndepCollation::AddCollationInformation(
tdi["config"] = config;
}
std::unique_ptr<cmCxxModuleExportInfo> cmDyndepCollation::ParseExportInfo(
Json::Value const& tdi)
struct CxxModuleFileSet
{
auto export_info = cm::make_unique<cmCxxModuleExportInfo>();
std::string Name;
std::string RelativeDirectory;
std::string SourcePath;
std::string Type;
cmFileSetVisibility Visibility;
cm::optional<std::string> Destination;
};
struct CxxModuleBmiInstall
{
std::string Component;
std::string Destination;
bool ExcludeFromAll;
bool Optional;
std::string Permissions;
std::string MessageLevel;
std::string ScriptLocation;
};
struct CxxModuleExport
{
std::string Name;
std::string Destination;
std::string Prefix;
std::string CxxModuleInfoDir;
std::string Namespace;
bool Install;
};
struct cmCxxModuleExportInfo
{
std::map<std::string, CxxModuleFileSet> ObjectToFileSet;
cm::optional<CxxModuleBmiInstall> BmiInstallation;
std::vector<CxxModuleExport> Exports;
std::string Config;
};
void cmCxxModuleExportInfoDeleter::operator()(cmCxxModuleExportInfo* ei) const
{
delete ei;
}
std::unique_ptr<cmCxxModuleExportInfo, cmCxxModuleExportInfoDeleter>
cmDyndepCollation::ParseExportInfo(Json::Value const& tdi)
{
auto export_info =
std::unique_ptr<cmCxxModuleExportInfo, cmCxxModuleExportInfoDeleter>(
new cmCxxModuleExportInfo);
export_info->Config = tdi["config"].asString();
if (export_info->Config.empty()) {
@@ -320,3 +371,282 @@ std::unique_ptr<cmCxxModuleExportInfo> cmDyndepCollation::ParseExportInfo(
return export_info;
}
bool cmDyndepCollation::WriteDyndepMetadata(
std::string const& lang, std::vector<cmScanDepInfo> const& objects,
cmCxxModuleExportInfo const& export_info,
cmDyndepMetadataCallbacks const& cb)
{
// Only C++ supports any of the file-set or BMI installation considered
// below.
if (lang != "CXX"_s) {
return true;
}
bool result = true;
// Prepare the export information blocks.
std::string const config_upper =
cmSystemTools::UpperCase(export_info.Config);
std::vector<
std::pair<std::unique_ptr<cmGeneratedFileStream>, CxxModuleExport const*>>
exports;
for (auto const& exp : export_info.Exports) {
std::unique_ptr<cmGeneratedFileStream> properties;
std::string const export_dir =
cmStrCat(exp.Prefix, '/', exp.CxxModuleInfoDir, '/');
std::string const property_file_path = cmStrCat(
export_dir, "target-", exp.Name, '-', export_info.Config, ".cmake");
properties = cm::make_unique<cmGeneratedFileStream>(property_file_path);
// Set up the preamble.
*properties << "set_property(TARGET \"" << exp.Namespace << exp.Name
<< "\"\n"
<< " PROPERTY IMPORTED_CXX_MODULES_" << config_upper << '\n';
exports.emplace_back(std::move(properties), &exp);
}
std::unique_ptr<cmGeneratedFileStream> bmi_install_script;
if (export_info.BmiInstallation) {
bmi_install_script = cm::make_unique<cmGeneratedFileStream>(
export_info.BmiInstallation->ScriptLocation);
}
auto cmEscape = [](cm::string_view str) {
return cmOutputConverter::EscapeForCMake(
str, cmOutputConverter::WrapQuotes::NoWrap);
};
auto install_destination =
[&cmEscape](std::string const& dest) -> std::pair<bool, std::string> {
if (cmSystemTools::FileIsFullPath(dest)) {
return std::make_pair(true, cmEscape(dest));
}
return std::make_pair(false,
cmStrCat("${_IMPORT_PREFIX}/", cmEscape(dest)));
};
// public/private requirement tracking.
std::set<std::string> private_modules;
std::map<std::string, std::set<std::string>> public_source_requires;
for (cmScanDepInfo const& object : objects) {
// Convert to forward slashes.
auto output_path = object.PrimaryOutput;
#ifdef _WIN32
cmSystemTools::ConvertToUnixSlashes(output_path);
#endif
// Find the fileset for this object.
auto fileset_info_itr = export_info.ObjectToFileSet.find(output_path);
bool const has_provides = !object.Provides.empty();
if (fileset_info_itr == export_info.ObjectToFileSet.end()) {
// If it provides anything, it should have a `CXX_MODULES` or
// `CXX_MODULE_INTERNAL_PARTITIONS` type and be present.
if (has_provides) {
// Take the first module provided to provide context.
auto const& provides = object.Provides[0];
char const* ok_types = "`CXX_MODULES`";
if (provides.LogicalName.find(':') != std::string::npos) {
ok_types = "`CXX_MODULES` (or `CXX_MODULE_INTERNAL_PARTITIONS` if "
"it is not `export`ed)";
}
cmSystemTools::Error(cmStrCat(
"Output ", object.PrimaryOutput, " provides the `",
provides.LogicalName,
"` module but it is not found in a `FILE_SET` of type ", ok_types));
result = false;
}
// This object file does not provide anything, so nothing more needs to
// be done.
continue;
}
auto const& file_set = fileset_info_itr->second;
// Verify the fileset type for the object.
if (file_set.Type == "CXX_MODULES"_s) {
if (!has_provides) {
cmSystemTools::Error(
cmStrCat("Output ", object.PrimaryOutput,
" is of type `CXX_MODULES` but does not provide a module"));
result = false;
continue;
}
} else if (file_set.Type == "CXX_MODULE_INTERNAL_PARTITIONS"_s) {
if (!has_provides) {
cmSystemTools::Error(
cmStrCat("Source ", file_set.SourcePath,
" is of type `CXX_MODULE_INTERNAL_PARTITIONS` but does not "
"provide a module"));
result = false;
continue;
}
auto const& provides = object.Provides[0];
if (provides.LogicalName.find(':') == std::string::npos) {
cmSystemTools::Error(
cmStrCat("Source ", file_set.SourcePath,
" is of type `CXX_MODULE_INTERNAL_PARTITIONS` but does not "
"provide a module partition"));
result = false;
continue;
}
} else if (file_set.Type == "CXX_MODULE_HEADERS"_s) {
// TODO.
} else {
if (has_provides) {
auto const& provides = object.Provides[0];
char const* ok_types = "`CXX_MODULES`";
if (provides.LogicalName.find(':') != std::string::npos) {
ok_types = "`CXX_MODULES` (or `CXX_MODULE_INTERNAL_PARTITIONS` if "
"it is not `export`ed)";
}
cmSystemTools::Error(
cmStrCat("Source ", file_set.SourcePath, " provides the `",
provides.LogicalName, "` C++ module but is of type `",
file_set.Type, "` module but must be of type ", ok_types));
result = false;
}
// Not a C++ module; ignore.
continue;
}
if (!cmFileSetVisibilityIsForInterface(file_set.Visibility)) {
// Nothing needs to be conveyed about non-`PUBLIC` modules.
for (auto const& p : object.Provides) {
private_modules.insert(p.LogicalName);
}
continue;
}
// The module is public. Record what it directly requires.
{
auto& reqs = public_source_requires[file_set.SourcePath];
for (auto const& r : object.Requires) {
reqs.insert(r.LogicalName);
}
}
// Write out properties and install rules for any exports.
for (auto const& p : object.Provides) {
bool bmi_dest_is_abs = false;
std::string bmi_destination;
if (export_info.BmiInstallation) {
auto dest =
install_destination(export_info.BmiInstallation->Destination);
bmi_dest_is_abs = dest.first;
bmi_destination = cmStrCat(dest.second, '/');
}
std::string install_bmi_path;
std::string build_bmi_path;
auto m = cb.ModuleFile(p.LogicalName);
if (m) {
install_bmi_path = cmStrCat(
bmi_destination, cmEscape(cmSystemTools::GetFilenameName(*m)));
build_bmi_path = cmEscape(*m);
}
for (auto const& exp : exports) {
std::string iface_source;
if (exp.second->Install && file_set.Destination) {
auto dest = install_destination(*file_set.Destination);
iface_source = cmStrCat(
dest.second, '/', cmEscape(file_set.RelativeDirectory),
cmEscape(cmSystemTools::GetFilenameName(file_set.SourcePath)));
} else {
iface_source = cmEscape(file_set.SourcePath);
}
std::string bmi_path;
if (exp.second->Install && export_info.BmiInstallation) {
bmi_path = install_bmi_path;
} else if (!exp.second->Install) {
bmi_path = build_bmi_path;
}
if (iface_source.empty()) {
// No destination for the C++ module source; ignore this property
// value.
continue;
}
*exp.first << " \"" << cmEscape(p.LogicalName) << '='
<< iface_source;
if (!bmi_path.empty()) {
*exp.first << ',' << bmi_path;
}
*exp.first << "\"\n";
}
if (bmi_install_script) {
auto const& bmi_install = *export_info.BmiInstallation;
*bmi_install_script << "if (CMAKE_INSTALL_COMPONENT STREQUAL \""
<< cmEscape(bmi_install.Component) << '\"';
if (!bmi_install.ExcludeFromAll) {
*bmi_install_script << " OR NOT CMAKE_INSTALL_COMPONENT";
}
*bmi_install_script << ")\n";
*bmi_install_script << " file(INSTALL\n"
" DESTINATION \"";
if (!bmi_dest_is_abs) {
*bmi_install_script << "${CMAKE_INSTALL_PREFIX}/";
}
*bmi_install_script << cmEscape(bmi_install.Destination)
<< "\"\n"
" TYPE FILE\n";
if (bmi_install.Optional) {
*bmi_install_script << " OPTIONAL\n";
}
if (!bmi_install.MessageLevel.empty()) {
*bmi_install_script << " " << bmi_install.MessageLevel << "\n";
}
if (!bmi_install.Permissions.empty()) {
*bmi_install_script << " PERMISSIONS" << bmi_install.Permissions
<< "\n";
}
*bmi_install_script << " FILES \"" << *m << "\")\n";
if (bmi_dest_is_abs) {
*bmi_install_script
<< " list(APPEND CMAKE_ABSOLUTE_DESTINATION_FILES\n"
" \""
<< cmEscape(cmSystemTools::GetFilenameName(*m))
<< "\")\n"
" if (CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION)\n"
" message(WARNING\n"
" \"ABSOLUTE path INSTALL DESTINATION : "
"${CMAKE_ABSOLUTE_DESTINATION_FILES}\")\n"
" endif ()\n"
" if (CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION)\n"
" message(FATAL_ERROR\n"
" \"ABSOLUTE path INSTALL DESTINATION forbidden (by "
"caller): ${CMAKE_ABSOLUTE_DESTINATION_FILES}\")\n"
" endif ()\n";
}
*bmi_install_script << "endif ()\n";
}
}
}
// Add trailing parenthesis for the `set_property` call.
for (auto const& exp : exports) {
*exp.first << ")\n";
}
// Check that public sources only require public modules.
for (auto const& pub_reqs : public_source_requires) {
for (auto const& req : pub_reqs.second) {
if (private_modules.count(req)) {
cmSystemTools::Error(cmStrCat(
"Public C++ module source `", pub_reqs.first, "` requires the `",
req, "` C++ module which is provided by a private source"));
result = false;
}
}
}
return result;
}

View File

@@ -5,16 +5,14 @@
#include "cmConfigure.h" // IWYU pragma: keep
#include <functional>
#include <map>
#include <memory>
#include <string>
#include <vector>
#include <cm/optional>
#include "cmFileSet.h"
class cmGeneratorTarget;
struct cmScanDepInfo;
class cmSourceFile;
namespace Json {
@@ -27,43 +25,15 @@ struct cmDyndepGeneratorCallbacks
ObjectFilePath;
};
struct CxxModuleFileSet
struct cmDyndepMetadataCallbacks
{
std::string Name;
std::string RelativeDirectory;
std::string SourcePath;
std::string Type;
cmFileSetVisibility Visibility;
cm::optional<std::string> Destination;
std::function<cm::optional<std::string>(std::string const& name)> ModuleFile;
};
struct CxxModuleBmiInstall
struct cmCxxModuleExportInfo;
struct cmCxxModuleExportInfoDeleter
{
std::string Component;
std::string Destination;
bool ExcludeFromAll;
bool Optional;
std::string Permissions;
std::string MessageLevel;
std::string ScriptLocation;
};
struct CxxModuleExport
{
std::string Name;
std::string Destination;
std::string Prefix;
std::string CxxModuleInfoDir;
std::string Namespace;
bool Install;
};
struct cmCxxModuleExportInfo
{
std::map<std::string, CxxModuleFileSet> ObjectToFileSet;
cm::optional<CxxModuleBmiInstall> BmiInstallation;
std::vector<CxxModuleExport> Exports;
std::string Config;
void operator()(cmCxxModuleExportInfo* ei) const;
};
struct cmDyndepCollation
@@ -73,6 +43,10 @@ struct cmDyndepCollation
std::string const& config,
cmDyndepGeneratorCallbacks const& cb);
static std::unique_ptr<cmCxxModuleExportInfo> ParseExportInfo(
Json::Value const& tdi);
static std::unique_ptr<cmCxxModuleExportInfo, cmCxxModuleExportInfoDeleter>
ParseExportInfo(Json::Value const& tdi);
static bool WriteDyndepMetadata(std::string const& lang,
std::vector<cmScanDepInfo> const& objects,
cmCxxModuleExportInfo const& export_info,
cmDyndepMetadataCallbacks const& cb);
};

View File

@@ -26,7 +26,6 @@
#include "cmCxxModuleMapper.h"
#include "cmDyndepCollation.h"
#include "cmFileSet.h"
#include "cmFortranParser.h"
#include "cmGeneratedFileStream.h"
#include "cmGeneratorExpressionEvaluationFile.h"
@@ -2711,279 +2710,18 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile(
cmGeneratedFileStream tmf(target_mods_file);
tmf << target_module_info;
bool result = true;
// Fortran doesn't support any of the file-set or BMI installation considered
// below.
if (arg_lang != "Fortran"_s) {
// Prepare the export information blocks.
std::string const config_upper =
cmSystemTools::UpperCase(export_info.Config);
std::vector<std::pair<std::unique_ptr<cmGeneratedFileStream>,
CxxModuleExport const*>>
exports;
for (auto const& exp : export_info.Exports) {
std::unique_ptr<cmGeneratedFileStream> properties;
std::string const export_dir =
cmStrCat(exp.Prefix, '/', exp.CxxModuleInfoDir, '/');
std::string const property_file_path = cmStrCat(
export_dir, "target-", exp.Name, '-', export_info.Config, ".cmake");
properties = cm::make_unique<cmGeneratedFileStream>(property_file_path);
// Set up the preamble.
*properties << "set_property(TARGET \"" << exp.Namespace << exp.Name
<< "\"\n"
<< " PROPERTY IMPORTED_CXX_MODULES_" << config_upper
<< '\n';
exports.emplace_back(std::move(properties), &exp);
cmDyndepMetadataCallbacks cb;
cb.ModuleFile =
[mod_files](std::string const& name) -> cm::optional<std::string> {
auto m = mod_files.find(name);
if (m != mod_files.end()) {
return m->second;
}
return {};
};
std::unique_ptr<cmGeneratedFileStream> bmi_install_script;
if (export_info.BmiInstallation) {
bmi_install_script = cm::make_unique<cmGeneratedFileStream>(
export_info.BmiInstallation->ScriptLocation);
}
auto cmEscape = [](cm::string_view str) {
return cmOutputConverter::EscapeForCMake(
str, cmOutputConverter::WrapQuotes::NoWrap);
};
auto install_destination =
[&cmEscape](std::string const& dest) -> std::pair<bool, std::string> {
if (cmSystemTools::FileIsFullPath(dest)) {
return std::make_pair(true, cmEscape(dest));
}
return std::make_pair(false,
cmStrCat("${_IMPORT_PREFIX}/", cmEscape(dest)));
};
// public/private requirement tracking.
std::set<std::string> private_modules;
std::map<std::string, std::set<std::string>> public_source_requires;
for (cmScanDepInfo const& object : objects) {
// Convert to forward slashes.
auto output_path = object.PrimaryOutput;
# ifdef _WIN32
cmSystemTools::ConvertToUnixSlashes(output_path);
# endif
// Find the fileset for this object.
auto fileset_info_itr = export_info.ObjectToFileSet.find(output_path);
bool const has_provides = !object.Provides.empty();
if (fileset_info_itr == export_info.ObjectToFileSet.end()) {
// If it provides anything, it should have a `CXX_MODULES` or
// `CXX_MODULE_INTERNAL_PARTITIONS` type and be present.
if (has_provides) {
// Take the first module provided to provide context.
auto const& provides = object.Provides[0];
char const* ok_types = "`CXX_MODULES`";
if (provides.LogicalName.find(':') != std::string::npos) {
ok_types = "`CXX_MODULES` (or `CXX_MODULE_INTERNAL_PARTITIONS` if "
"it is not `export`ed)";
}
cmSystemTools::Error(
cmStrCat("Output ", object.PrimaryOutput, " provides the `",
provides.LogicalName,
"` module but it is not found in a `FILE_SET` of type ",
ok_types));
result = false;
}
// This object file does not provide anything, so nothing more needs to
// be done.
continue;
}
auto const& file_set = fileset_info_itr->second;
// Verify the fileset type for the object.
if (file_set.Type == "CXX_MODULES"_s) {
if (!has_provides) {
cmSystemTools::Error(cmStrCat(
"Output ", object.PrimaryOutput,
" is of type `CXX_MODULES` but does not provide a module"));
result = false;
continue;
}
} else if (file_set.Type == "CXX_MODULE_INTERNAL_PARTITIONS"_s) {
if (!has_provides) {
cmSystemTools::Error(cmStrCat(
"Source ", file_set.SourcePath,
" is of type `CXX_MODULE_INTERNAL_PARTITIONS` but does not "
"provide a module"));
result = false;
continue;
}
auto const& provides = object.Provides[0];
if (provides.LogicalName.find(':') == std::string::npos) {
cmSystemTools::Error(cmStrCat(
"Source ", file_set.SourcePath,
" is of type `CXX_MODULE_INTERNAL_PARTITIONS` but does not "
"provide a module partition"));
result = false;
continue;
}
} else if (file_set.Type == "CXX_MODULE_HEADERS"_s) {
// TODO.
} else {
if (has_provides) {
auto const& provides = object.Provides[0];
char const* ok_types = "`CXX_MODULES`";
if (provides.LogicalName.find(':') != std::string::npos) {
ok_types = "`CXX_MODULES` (or `CXX_MODULE_INTERNAL_PARTITIONS` if "
"it is not `export`ed)";
}
cmSystemTools::Error(cmStrCat(
"Source ", file_set.SourcePath, " provides the `",
provides.LogicalName, "` C++ module but is of type `",
file_set.Type, "` module but must be of type ", ok_types));
result = false;
}
// Not a C++ module; ignore.
continue;
}
if (!cmFileSetVisibilityIsForInterface(file_set.Visibility)) {
// Nothing needs to be conveyed about non-`PUBLIC` modules.
for (auto const& p : object.Provides) {
private_modules.insert(p.LogicalName);
}
continue;
}
// The module is public. Record what it directly requires.
{
auto& reqs = public_source_requires[file_set.SourcePath];
for (auto const& r : object.Requires) {
reqs.insert(r.LogicalName);
}
}
// Write out properties and install rules for any exports.
for (auto const& p : object.Provides) {
bool bmi_dest_is_abs = false;
std::string bmi_destination;
if (export_info.BmiInstallation) {
auto dest =
install_destination(export_info.BmiInstallation->Destination);
bmi_dest_is_abs = dest.first;
bmi_destination = cmStrCat(dest.second, '/');
}
std::string install_bmi_path;
std::string build_bmi_path;
auto m = mod_files.find(p.LogicalName);
if (m != mod_files.end()) {
install_bmi_path =
cmStrCat(bmi_destination,
cmEscape(cmSystemTools::GetFilenameName(m->second)));
build_bmi_path = cmEscape(m->second);
}
for (auto const& exp : exports) {
std::string iface_source;
if (exp.second->Install && file_set.Destination) {
auto dest = install_destination(*file_set.Destination);
iface_source = cmStrCat(
dest.second, '/', cmEscape(file_set.RelativeDirectory),
cmEscape(cmSystemTools::GetFilenameName(file_set.SourcePath)));
} else {
iface_source = cmEscape(file_set.SourcePath);
}
std::string bmi_path;
if (exp.second->Install && export_info.BmiInstallation) {
bmi_path = install_bmi_path;
} else if (!exp.second->Install) {
bmi_path = build_bmi_path;
}
if (iface_source.empty()) {
// No destination for the C++ module source; ignore this property
// value.
continue;
}
*exp.first << " \"" << cmEscape(p.LogicalName) << '='
<< iface_source;
if (!bmi_path.empty()) {
*exp.first << ',' << bmi_path;
}
*exp.first << "\"\n";
}
if (bmi_install_script) {
auto const& bmi_install = *export_info.BmiInstallation;
*bmi_install_script << "if (CMAKE_INSTALL_COMPONENT STREQUAL \""
<< cmEscape(bmi_install.Component) << '\"';
if (!bmi_install.ExcludeFromAll) {
*bmi_install_script << " OR NOT CMAKE_INSTALL_COMPONENT";
}
*bmi_install_script << ")\n";
*bmi_install_script << " file(INSTALL\n"
" DESTINATION \"";
if (!bmi_dest_is_abs) {
*bmi_install_script << "${CMAKE_INSTALL_PREFIX}/";
}
*bmi_install_script << cmEscape(bmi_install.Destination)
<< "\"\n"
" TYPE FILE\n";
if (bmi_install.Optional) {
*bmi_install_script << " OPTIONAL\n";
}
if (!bmi_install.MessageLevel.empty()) {
*bmi_install_script << " " << bmi_install.MessageLevel << "\n";
}
if (!bmi_install.Permissions.empty()) {
*bmi_install_script << " PERMISSIONS" << bmi_install.Permissions
<< "\n";
}
*bmi_install_script << " FILES \"" << m->second << "\")\n";
if (bmi_dest_is_abs) {
*bmi_install_script
<< " list(APPEND CMAKE_ABSOLUTE_DESTINATION_FILES\n"
" \""
<< cmEscape(cmSystemTools::GetFilenameName(m->second))
<< "\")\n"
" if (CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION)\n"
" message(WARNING\n"
" \"ABSOLUTE path INSTALL DESTINATION : "
"${CMAKE_ABSOLUTE_DESTINATION_FILES}\")\n"
" endif ()\n"
" if (CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION)\n"
" message(FATAL_ERROR\n"
" \"ABSOLUTE path INSTALL DESTINATION forbidden (by "
"caller): ${CMAKE_ABSOLUTE_DESTINATION_FILES}\")\n"
" endif ()\n";
}
*bmi_install_script << "endif ()\n";
}
}
}
// Add trailing parenthesis for the `set_property` call.
for (auto const& exp : exports) {
*exp.first << ")\n";
}
// Check that public sources only require public modules.
for (auto const& pub_reqs : public_source_requires) {
for (auto const& req : pub_reqs.second) {
if (private_modules.count(req)) {
cmSystemTools::Error(cmStrCat(
"Public C++ module source `", pub_reqs.first, "` requires the `",
req, "` C++ module which is provided by a private source"));
result = false;
}
}
}
}
return result;
return cmDyndepCollation::WriteDyndepMetadata(arg_lang, objects, export_info,
cb);
}
int cmcmd_cmake_ninja_dyndep(std::vector<std::string>::const_iterator argBeg,