From f4a17b29d3bbdf602052ff6a19f8bda658929fab Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Wed, 30 Nov 2022 09:39:02 -0500 Subject: cmDyndepCollation: factor out metadata writing for dyndep --- Source/cmDyndepCollation.cxx | 336 +++++++++++++++++++++++++++++++++++++- Source/cmDyndepCollation.h | 50 ++---- Source/cmGlobalNinjaGenerator.cxx | 282 ++------------------------------ 3 files changed, 355 insertions(+), 313 deletions(-) diff --git a/Source/cmDyndepCollation.cxx b/Source/cmDyndepCollation.cxx index f933e2a..2827659 100644 --- a/Source/cmDyndepCollation.cxx +++ b/Source/cmDyndepCollation.cxx @@ -5,6 +5,8 @@ #include #include +#include +#include #include #include @@ -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 cmDyndepCollation::ParseExportInfo( - Json::Value const& tdi) +struct CxxModuleFileSet { - auto export_info = cm::make_unique(); + std::string Name; + std::string RelativeDirectory; + std::string SourcePath; + std::string Type; + cmFileSetVisibility Visibility; + cm::optional 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 ObjectToFileSet; + cm::optional BmiInstallation; + std::vector Exports; + std::string Config; +}; + +void cmCxxModuleExportInfoDeleter::operator()(cmCxxModuleExportInfo* ei) const +{ + delete ei; +} + +std::unique_ptr +cmDyndepCollation::ParseExportInfo(Json::Value const& tdi) +{ + auto export_info = + std::unique_ptr( + new cmCxxModuleExportInfo); export_info->Config = tdi["config"].asString(); if (export_info->Config.empty()) { @@ -320,3 +371,282 @@ std::unique_ptr cmDyndepCollation::ParseExportInfo( return export_info; } + +bool cmDyndepCollation::WriteDyndepMetadata( + std::string const& lang, std::vector 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, CxxModuleExport const*>> + exports; + for (auto const& exp : export_info.Exports) { + std::unique_ptr 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(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 bmi_install_script; + if (export_info.BmiInstallation) { + bmi_install_script = cm::make_unique( + 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 { + 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 private_modules; + std::map> 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; +} diff --git a/Source/cmDyndepCollation.h b/Source/cmDyndepCollation.h index 5f78707..e70ac09 100644 --- a/Source/cmDyndepCollation.h +++ b/Source/cmDyndepCollation.h @@ -5,16 +5,14 @@ #include "cmConfigure.h" // IWYU pragma: keep #include -#include #include #include #include #include -#include "cmFileSet.h" - class cmGeneratorTarget; +struct cmScanDepInfo; class cmSourceFile; namespace Json { @@ -27,43 +25,15 @@ struct cmDyndepGeneratorCallbacks ObjectFilePath; }; -struct CxxModuleFileSet -{ - std::string Name; - std::string RelativeDirectory; - std::string SourcePath; - std::string Type; - cmFileSetVisibility Visibility; - cm::optional Destination; -}; - -struct CxxModuleBmiInstall -{ - std::string Component; - std::string Destination; - bool ExcludeFromAll; - bool Optional; - std::string Permissions; - std::string MessageLevel; - std::string ScriptLocation; -}; - -struct CxxModuleExport +struct cmDyndepMetadataCallbacks { - std::string Name; - std::string Destination; - std::string Prefix; - std::string CxxModuleInfoDir; - std::string Namespace; - bool Install; + std::function(std::string const& name)> ModuleFile; }; -struct cmCxxModuleExportInfo +struct cmCxxModuleExportInfo; +struct cmCxxModuleExportInfoDeleter { - std::map ObjectToFileSet; - cm::optional BmiInstallation; - std::vector 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 ParseExportInfo( - Json::Value const& tdi); + static std::unique_ptr + ParseExportInfo(Json::Value const& tdi); + static bool WriteDyndepMetadata(std::string const& lang, + std::vector const& objects, + cmCxxModuleExportInfo const& export_info, + cmDyndepMetadataCallbacks const& cb); }; diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index cd8d9d5..4500f33 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -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, - CxxModuleExport const*>> - exports; - for (auto const& exp : export_info.Exports) { - std::unique_ptr 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(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 bmi_install_script; - if (export_info.BmiInstallation) { - bmi_install_script = cm::make_unique( - export_info.BmiInstallation->ScriptLocation); + cmDyndepMetadataCallbacks cb; + cb.ModuleFile = + [mod_files](std::string const& name) -> cm::optional { + auto m = mod_files.find(name); + if (m != mod_files.end()) { + return m->second; } + return {}; + }; - auto cmEscape = [](cm::string_view str) { - return cmOutputConverter::EscapeForCMake( - str, cmOutputConverter::WrapQuotes::NoWrap); - }; - auto install_destination = - [&cmEscape](std::string const& dest) -> std::pair { - 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 private_modules; - std::map> 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::const_iterator argBeg, -- cgit v0.12