From b5a6648c4b4973c4747aa6a325938ca1e7d977d0 Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Mon, 18 Jul 2022 16:47:15 -0400 Subject: RunCMake/CXXModules: fix partition importing syntax Apparently GCC accepts the ill-formed prior syntax. --- Tests/RunCMake/CXXModules/examples/internal-partitions/importable.cxx | 2 +- Tests/RunCMake/CXXModules/examples/partitions/importable.cxx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/RunCMake/CXXModules/examples/internal-partitions/importable.cxx b/Tests/RunCMake/CXXModules/examples/internal-partitions/importable.cxx index 8be521b..b872ae9 100644 --- a/Tests/RunCMake/CXXModules/examples/internal-partitions/importable.cxx +++ b/Tests/RunCMake/CXXModules/examples/internal-partitions/importable.cxx @@ -1,5 +1,5 @@ export module importable; -import importable : internal_partition; +import : internal_partition; #include "internal-partitions_export.h" diff --git a/Tests/RunCMake/CXXModules/examples/partitions/importable.cxx b/Tests/RunCMake/CXXModules/examples/partitions/importable.cxx index 1e81aaf..d0ac2f4 100644 --- a/Tests/RunCMake/CXXModules/examples/partitions/importable.cxx +++ b/Tests/RunCMake/CXXModules/examples/partitions/importable.cxx @@ -1,5 +1,5 @@ export module importable; -export import importable : partition; +export import : partition; #include "partitions_export.h" -- cgit v0.12 From d7f5064ff79380d03e11c7fed01b6e225ea78337 Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Mon, 18 Jul 2022 12:05:58 -0400 Subject: cmScanDepFormat: support P1689R5 This adds the `is-interface` key on provides fields. --- Help/dev/experimental.rst | 4 ++-- Help/release/dev/p1689r5.rst | 6 ++++++ Source/cmScanDepFormat.cxx | 15 +++++++++++++++ Source/cmScanDepFormat.h | 5 +++++ 4 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 Help/release/dev/p1689r5.rst diff --git a/Help/dev/experimental.rst b/Help/dev/experimental.rst index b8d681c..adfa36f 100644 --- a/Help/dev/experimental.rst +++ b/Help/dev/experimental.rst @@ -58,7 +58,7 @@ dependencies to the file specified by the ```` placeholder. The for scandep rules which use ``msvc``-style dependency reporting. The module dependencies should be written in the format described -by the `P1689r4`_ paper. +by the `P1689r5`_ paper. Compiler writers may try out their scanning functionality using the `cxx-modules-sandbox`_ test project, modified to set variables @@ -85,5 +85,5 @@ the GCC documentation, but the relevant section for the purposes of CMake is: -- GCC module mapper documentation .. _`D1483r1`: https://mathstuf.fedorapeople.org/fortran-modules/fortran-modules.html -.. _`P1689r4`: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p1689r4.html +.. _`P1689r5`: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p1689r5.html .. _`cxx-modules-sandbox`: https://github.com/mathstuf/cxx-modules-sandbox diff --git a/Help/release/dev/p1689r5.rst b/Help/release/dev/p1689r5.rst new file mode 100644 index 0000000..a630dc4 --- /dev/null +++ b/Help/release/dev/p1689r5.rst @@ -0,0 +1,6 @@ +p1689r5 +------- + +* C++ module scanning now supports the latest revision, `P1689R5`_. + +.. _`P1689r5`: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p1689r5.html diff --git a/Source/cmScanDepFormat.cxx b/Source/cmScanDepFormat.cxx index 82a374a..81ef3da 100644 --- a/Source/cmScanDepFormat.cxx +++ b/Source/cmScanDepFormat.cxx @@ -188,6 +188,19 @@ bool cmScanDepFormat_P1689_Parse(std::string const& arg_pp, return false; } + if (provide.isMember("is-interface")) { + Json::Value const& is_interface = provide["is-interface"]; + if (!is_interface.isBool()) { + cmSystemTools::Error( + cmStrCat("-E cmake_ninja_dyndep failed to parse ", arg_pp, + ": is-interface is not a boolean")); + return false; + } + provide_info.IsInterface = is_interface.asBool(); + } else { + provide_info.IsInterface = true; + } + info->Provides.push_back(provide_info); } } @@ -308,6 +321,8 @@ bool cmScanDepFormat_P1689_Write(std::string const& path, provide_obj["source-path"] = EncodeFilename(provide.SourcePath); } + provide_obj["is-interface"] = provide.IsInterface; + provides.append(provide_obj); } diff --git a/Source/cmScanDepFormat.h b/Source/cmScanDepFormat.h index dae28d9..dc55bf1 100644 --- a/Source/cmScanDepFormat.h +++ b/Source/cmScanDepFormat.h @@ -18,6 +18,11 @@ struct cmSourceReqInfo std::string SourcePath; std::string CompiledModulePath; bool UseSourcePath = false; + + // Provides-only fields. + bool IsInterface = true; + + // Requires-only fields. LookupMethod Method = LookupMethod::ByName; }; -- cgit v0.12 From a84c186a7d1116226827bb6722f628db98c3a278 Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Mon, 1 Aug 2022 09:59:15 -0400 Subject: cmScanDepFormat: support the MSVC 17.3 toolchain temporarily At least until 17.4 is out. --- Source/cmScanDepFormat.cxx | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/Source/cmScanDepFormat.cxx b/Source/cmScanDepFormat.cxx index 81ef3da..ec53af5 100644 --- a/Source/cmScanDepFormat.cxx +++ b/Source/cmScanDepFormat.cxx @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -280,6 +281,27 @@ bool cmScanDepFormat_P1689_Parse(std::string const& arg_pp, info->Requires.push_back(require_info); } } + + // MSVC 17.3 toolchain bug. Remove when 17.4 is available. + if (rule.isMember("is-interface")) { + std::cerr + << "warning: acknowledging an VS 17.3 toolchain bug; accepting " + "until a new release which fixes it is available" + << std::endl; + + Json::Value const& is_interface_json = rule["is-interface"]; + if (!is_interface_json.isBool()) { + cmSystemTools::Error( + cmStrCat("-E cmake_ninja_dyndep failed to parse ", arg_pp, + ": is-interface is not a boolean")); + return false; + } + bool is_interface = is_interface_json.asBool(); + + for (auto& provide : info->Provides) { + provide.IsInterface = is_interface; + } + } } } -- cgit v0.12 From b90de0b4921bb4b23c116e540819cbeb0cc79c99 Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Mon, 18 Jul 2022 14:32:57 -0400 Subject: RunCMake/CXXModules: support MSVC extensions --- Tests/RunCMake/CXXModules/examples/cxx-modules-find-bmi.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/RunCMake/CXXModules/examples/cxx-modules-find-bmi.cmake b/Tests/RunCMake/CXXModules/examples/cxx-modules-find-bmi.cmake index 8efe508..91f3995 100644 --- a/Tests/RunCMake/CXXModules/examples/cxx-modules-find-bmi.cmake +++ b/Tests/RunCMake/CXXModules/examples/cxx-modules-find-bmi.cmake @@ -1,6 +1,6 @@ function (check_for_bmi prefix destination name) set(found 0) - foreach (ext IN ITEMS gcm) + foreach (ext IN ITEMS gcm ifc) if (EXISTS "${prefix}/${destination}/${name}.${ext}") set(found 1) break () -- cgit v0.12 From a43713d615bccbb109b5481e941b7d2ea37baf0f Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Tue, 26 Jul 2022 12:25:08 -0400 Subject: CTestCustom: ignore `cm::optional` uninitialized memory false positive --- CTestCustom.cmake.in | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CTestCustom.cmake.in b/CTestCustom.cmake.in index 49026a3..cb6b485 100644 --- a/CTestCustom.cmake.in +++ b/CTestCustom.cmake.in @@ -83,6 +83,11 @@ list(APPEND CTEST_CUSTOM_WARNING_EXCEPTION "compilation completed with warnings" # PGI "[0-9]+ Warning\\(s\\) detected" # SunPro + # Ignore false positive on `cm::optional` usage from GCC + "cmGlobalNinjaGenerator.cxx:[0-9]*:[0-9]*: warning: '.*cm::optional::_mem\\)\\)' may be used uninitialized \\[-Wmaybe-uninitialized\\]" + "cmGlobalNinjaGenerator.cxx:[0-9]*:[0-9]*: note: '.*cm::optional::_mem\\)\\)' was declared here" + "cmGlobalNinjaGenerator.cxx:[0-9]*:[0-9]*: warning: '\\*\\(\\(void\\*\\)& modmap_fmt \\+4\\)' may be used uninitialized in this function \\[-Wmaybe-uninitialized\\]" + # clang-analyzer exceptions "cmListFileLexer.c:[0-9]+:[0-9]+: warning: Array subscript is undefined" "jsoncpp/src/.*:[0-9]+:[0-9]+: warning: Value stored to .* is never read" -- cgit v0.12 From b3c2880cb2d5750c8a4508fa41e6552808da2bd3 Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Tue, 19 Jul 2022 16:00:58 -0400 Subject: cmCxxModuleMapper: track transitive modules for MSVC MSVC needs the transitive closure of module usage to compile. --- Source/cmCxxModuleMapper.cxx | 151 ++++++++++++++++++++++++++++++++++++++ Source/cmCxxModuleMapper.h | 37 +++++++++- Source/cmGlobalNinjaGenerator.cxx | 96 ++++++++++++++++++++++-- 3 files changed, 278 insertions(+), 6 deletions(-) diff --git a/Source/cmCxxModuleMapper.cxx b/Source/cmCxxModuleMapper.cxx index 94ad721..b68e28c 100644 --- a/Source/cmCxxModuleMapper.cxx +++ b/Source/cmCxxModuleMapper.cxx @@ -3,10 +3,17 @@ #include "cmCxxModuleMapper.h" #include +#include #include +#include #include +#include +#include + #include "cmScanDepFormat.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" cm::optional CxxModuleLocations::BmiGeneratorPathForModule( std::string const& logical_name) const @@ -49,6 +56,48 @@ std::string CxxModuleMapContentGcc(CxxModuleLocations const& loc, } } +bool CxxModuleUsage::AddReference(std::string const& logical, + std::string const& loc, LookupMethod method) +{ + auto r = this->Reference.find(logical); + if (r != this->Reference.end()) { + auto& ref = r->second; + + if (ref.Path == loc && ref.Method == method) { + return true; + } + + auto method_name = [](LookupMethod m) -> cm::static_string_view { + switch (m) { + case LookupMethod::ByName: + return "by-name"_s; + case LookupMethod::IncludeAngle: + return "include-angle"_s; + case LookupMethod::IncludeQuote: + return "include-quote"_s; + } + assert(false && "unsupported lookup method"); + return ""_s; + }; + + cmSystemTools::Error(cmStrCat("Disagreement of the location of the '", + logical, + "' module. " + "Location A: '", + ref.Path, "' via ", method_name(ref.Method), + "; " + "Location B: '", + loc, "' via ", method_name(method), ".")); + return false; + } + + auto& ref = this->Reference[logical]; + ref.Path = loc; + ref.Method = method; + + return true; +} + cm::static_string_view CxxModuleMapExtension( cm::optional format) { @@ -62,6 +111,108 @@ cm::static_string_view CxxModuleMapExtension( return ".bmi"_s; } +std::set CxxModuleUsageSeed( + CxxModuleLocations const& loc, std::vector const& objects, + CxxModuleUsage& usages) +{ + // Track inner usages to populate usages from internal bits. + // + // This is a map of modules that required some other module that was not + // found to those that were not found. + std::map> internal_usages; + std::set unresolved; + + for (cmScanDepInfo const& object : objects) { + // Add references for each of the provided modules. + for (auto const& p : object.Provides) { + if (auto bmi_loc = loc.BmiGeneratorPathForModule(p.LogicalName)) { + // XXX(cxx-modules): How to support header units? + usages.AddReference(p.LogicalName, loc.PathForGenerator(*bmi_loc), + LookupMethod::ByName); + } + } + + // For each requires, pull in what is required. + for (auto const& r : object.Requires) { + // Find transitive usages. + auto transitive_usages = usages.Usage.find(r.LogicalName); + // Find the required name in the current target. + auto bmi_loc = loc.BmiGeneratorPathForModule(r.LogicalName); + + for (auto const& p : object.Provides) { + auto& this_usages = usages.Usage[p.LogicalName]; + + // Add the direct usage. + this_usages.insert(r.LogicalName); + + // Add the transitive usage. + if (transitive_usages != usages.Usage.end()) { + this_usages.insert(transitive_usages->second.begin(), + transitive_usages->second.end()); + } else if (bmi_loc) { + // Mark that we need to update transitive usages later. + internal_usages[p.LogicalName].insert(r.LogicalName); + } + } + + if (bmi_loc) { + usages.AddReference(r.LogicalName, loc.PathForGenerator(*bmi_loc), + r.Method); + } + } + } + + // While we have internal usages to manage. + while (!internal_usages.empty()) { + size_t starting_size = internal_usages.size(); + + // For each internal usage. + for (auto usage = internal_usages.begin(); usage != internal_usages.end(); + /* see end of loop */) { + auto& this_usages = usages.Usage[usage->first]; + + for (auto use = usage->second.begin(); use != usage->second.end(); + /* see end of loop */) { + // Check if this required module uses other internal modules; defer + // if so. + if (internal_usages.count(*use)) { + // Advance the iterator. + ++use; + continue; + } + + auto transitive_usages = usages.Usage.find(*use); + if (transitive_usages != usages.Usage.end()) { + this_usages.insert(transitive_usages->second.begin(), + transitive_usages->second.end()); + } + + // Remove the entry and advance the iterator. + use = usage->second.erase(use); + } + + // Erase the entry if it doesn't have any remaining usages. + if (usage->second.empty()) { + usage = internal_usages.erase(usage); + } else { + ++usage; + } + } + + // Check that at least one usage was resolved. + if (starting_size == internal_usages.size()) { + // Nothing could be resolved this loop; we have a cycle, so record the + // cycle and exit. + for (auto const& usage : internal_usages) { + unresolved.insert(usage.first); + } + break; + } + } + + return unresolved; +} + std::string CxxModuleMapContent(CxxModuleMapFormat format, CxxModuleLocations const& loc, cmScanDepInfo const& obj) diff --git a/Source/cmCxxModuleMapper.h b/Source/cmCxxModuleMapper.h index 99384c9..6d29fc0 100644 --- a/Source/cmCxxModuleMapper.h +++ b/Source/cmCxxModuleMapper.h @@ -5,12 +5,15 @@ #include "cmConfigure.h" // IWYU pragma: keep #include +#include +#include #include +#include #include #include -struct cmScanDepInfo; +#include "cmScanDepFormat.h" enum class CxxModuleMapFormat { @@ -37,10 +40,42 @@ struct CxxModuleLocations std::string const& logical_name) const; }; +struct CxxModuleReference +{ + // The path to the module file used. + std::string Path; + // How the module was looked up. + LookupMethod Method; +}; + +struct CxxModuleUsage +{ + // The usage requirements for this object. + std::map> Usage; + + // The references for this object. + std::map Reference; + + // Add a reference to a module. + // + // Returns `true` if it matches how it was found previously, `false` if it + // conflicts. + bool AddReference(std::string const& logical, std::string const& loc, + LookupMethod method); +}; + // Return the extension to use for a given modulemap format. cm::static_string_view CxxModuleMapExtension( cm::optional format); +// Fill in module usage information for internal usages. +// +// Returns the set of unresolved module usage requirements (these form an +// import cycle). +std::set CxxModuleUsageSeed( + CxxModuleLocations const& loc, std::vector const& objects, + CxxModuleUsage& usages); + // Return the contents of the module map in the given format for the // object file. std::string CxxModuleMapContent(CxxModuleMapFormat format, diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index b4d5746..f69c95f 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -3,6 +3,7 @@ #include "cmGlobalNinjaGenerator.h" #include +#include #include #include #include @@ -2555,6 +2556,8 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile( objects.push_back(std::move(info)); } + CxxModuleUsage usages; + // Map from module name to module file path, if known. std::map mod_files; @@ -2572,8 +2575,47 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile( return false; } if (ltm.isObject()) { - for (Json::Value::iterator i = ltm.begin(); i != ltm.end(); ++i) { - mod_files[i.key().asString()] = i->asString(); + Json::Value const& target_modules = ltm["modules"]; + if (target_modules.isObject()) { + for (auto i = target_modules.begin(); i != target_modules.end(); ++i) { + mod_files[i.key().asString()] = i->asString(); + } + } + Json::Value const& target_modules_references = ltm["references"]; + if (target_modules_references.isObject()) { + for (auto i = target_modules_references.begin(); + i != target_modules_references.end(); ++i) { + if (i->isObject()) { + Json::Value const& reference_path = (*i)["path"]; + CxxModuleReference module_reference; + if (reference_path.isString()) { + module_reference.Path = reference_path.asString(); + } + Json::Value const& reference_method = (*i)["lookup-method"]; + if (reference_method.isString()) { + std::string reference = reference_method.asString(); + if (reference == "by-name") { + module_reference.Method = LookupMethod::ByName; + } else if (reference == "include-angle") { + module_reference.Method = LookupMethod::IncludeAngle; + } else if (reference == "include-quote") { + module_reference.Method = LookupMethod::IncludeQuote; + } + } + usages.Reference[i.key().asString()] = module_reference; + } + } + } + Json::Value const& target_modules_usage = ltm["usages"]; + if (target_modules_usage.isObject()) { + for (auto i = target_modules_usage.begin(); + i != target_modules_usage.end(); ++i) { + if (i->isArray()) { + for (auto j = i->begin(); j != i->end(); ++j) { + usages.Usage[i.key().asString()].insert(j->asString()); + } + } + } } } } @@ -2595,7 +2637,7 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile( // Extend the module map with those provided by this target. // We do this after loading the modules provided by linked targets // in case we have one of the same name that must be preferred. - Json::Value tm = Json::objectValue; + Json::Value target_modules = Json::objectValue; for (cmScanDepInfo const& object : objects) { for (auto const& p : object.Provides) { std::string mod; @@ -2614,7 +2656,7 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile( mod = cmStrCat(module_dir, safe_logical_name, module_ext); } mod_files[p.LogicalName] = mod; - tm[p.LogicalName] = mod; + target_modules[p.LogicalName] = mod; } } @@ -2636,6 +2678,18 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile( return {}; }; + // Insert information about the current target's modules. + if (modmap_fmt) { + auto cycle_modules = CxxModuleUsageSeed(locs, objects, usages); + if (!cycle_modules.empty()) { + cmSystemTools::Error( + cmStrCat("Circular dependency detected in the C++ module import " + "graph. See modules named: \"", + cmJoin(cycle_modules, R"(", ")"_s), '"')); + return false; + } + } + cmNinjaBuild build("dyndep"); build.Outputs.emplace_back(""); for (cmScanDepInfo const& object : objects) { @@ -2671,12 +2725,44 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile( } } + Json::Value target_module_info = Json::objectValue; + target_module_info["modules"] = target_modules; + + auto& target_usages = target_module_info["usages"] = Json::objectValue; + for (auto const& u : usages.Usage) { + auto& mod_usage = target_usages[u.first] = Json::arrayValue; + for (auto const& v : u.second) { + mod_usage.append(v); + } + } + + auto name_for_method = [](LookupMethod method) -> cm::static_string_view { + switch (method) { + case LookupMethod::ByName: + return "by-name"_s; + case LookupMethod::IncludeAngle: + return "include-angle"_s; + case LookupMethod::IncludeQuote: + return "include-quote"_s; + } + assert(false && "unsupported lookup method"); + return ""_s; + }; + + auto& target_references = target_module_info["references"] = + Json::objectValue; + for (auto const& r : usages.Reference) { + auto& mod_ref = target_references[r.first] = Json::objectValue; + mod_ref["path"] = r.second.Path; + mod_ref["lookup-method"] = std::string(name_for_method(r.second.Method)); + } + // Store the map of modules provided by this target in a file for // use by dependents that reference this target in linked-target-dirs. std::string const target_mods_file = cmStrCat( cmSystemTools::GetFilenamePath(arg_dd), '/', arg_lang, "Modules.json"); cmGeneratedFileStream tmf(target_mods_file); - tmf << tm; + tmf << target_module_info; bool result = true; -- cgit v0.12 From 297e0f4dce189183ff07cc8a79de6ae72c81974b Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Tue, 19 Jul 2022 16:03:32 -0400 Subject: cmCxxModuleMapper: support MSVC module map format THis is a set of flags stored in a response file which informs the compiler about where to place output BMI files as well as find the required BMIs. --- Source/cmCxxModuleMapper.cxx | 83 ++++++++++++++++++++++++++++++++++++++- Source/cmCxxModuleMapper.h | 4 +- Source/cmGlobalNinjaGenerator.cxx | 4 +- 3 files changed, 88 insertions(+), 3 deletions(-) diff --git a/Source/cmCxxModuleMapper.cxx b/Source/cmCxxModuleMapper.cxx index b68e28c..84691c9 100644 --- a/Source/cmCxxModuleMapper.cxx +++ b/Source/cmCxxModuleMapper.cxx @@ -4,7 +4,9 @@ #include #include +#include #include +#include #include #include @@ -54,6 +56,80 @@ std::string CxxModuleMapContentGcc(CxxModuleLocations const& loc, return mm.str(); } + +std::string CxxModuleMapContentMsvc(CxxModuleLocations const& loc, + cmScanDepInfo const& obj, + CxxModuleUsage const& usages) +{ + std::stringstream mm; + + // A response file of `-reference NAME=PATH` arguments. + + // MSVC's command line only supports a single output. If more than one is + // expected, we cannot make a useful module map file. + if (obj.Provides.size() > 1) { + return {}; + } + + auto flag_for_method = [](LookupMethod method) -> cm::static_string_view { + switch (method) { + case LookupMethod::ByName: + return "-reference"_s; + case LookupMethod::IncludeAngle: + return "-headerUnit:angle"_s; + case LookupMethod::IncludeQuote: + return "-headerUnit:quote"_s; + } + assert(false && "unsupported lookup method"); + return ""_s; + }; + + for (auto const& p : obj.Provides) { + if (p.IsInterface) { + mm << "-interface\n"; + } else { + mm << "-internalPartition\n"; + } + + if (auto bmi_loc = loc.BmiGeneratorPathForModule(p.LogicalName)) { + mm << "-ifcOutput " << *bmi_loc << '\n'; + } + } + + std::set transitive_usage_directs; + std::set transitive_usage_names; + + for (auto const& r : obj.Requires) { + if (auto bmi_loc = loc.BmiGeneratorPathForModule(r.LogicalName)) { + auto flag = flag_for_method(r.Method); + + mm << flag << ' ' << r.LogicalName << '=' << *bmi_loc << "\n"; + transitive_usage_directs.insert(r.LogicalName); + + // Insert transitive usages. + auto transitive_usages = usages.Usage.find(r.LogicalName); + if (transitive_usages != usages.Usage.end()) { + transitive_usage_names.insert(transitive_usages->second.begin(), + transitive_usages->second.end()); + } + } + } + + for (auto const& transitive_name : transitive_usage_names) { + if (transitive_usage_directs.count(transitive_name)) { + continue; + } + + auto module_ref = usages.Reference.find(transitive_name); + if (module_ref != usages.Reference.end()) { + auto flag = flag_for_method(module_ref->second.Method); + mm << flag << ' ' << transitive_name << '=' << module_ref->second.Path + << "\n"; + } + } + + return mm.str(); +} } bool CxxModuleUsage::AddReference(std::string const& logical, @@ -105,6 +181,8 @@ cm::static_string_view CxxModuleMapExtension( switch (*format) { case CxxModuleMapFormat::Gcc: return ".gcm"_s; + case CxxModuleMapFormat::Msvc: + return ".ifc"_s; } } @@ -215,11 +293,14 @@ std::set CxxModuleUsageSeed( std::string CxxModuleMapContent(CxxModuleMapFormat format, CxxModuleLocations const& loc, - cmScanDepInfo const& obj) + cmScanDepInfo const& obj, + CxxModuleUsage const& usages) { switch (format) { case CxxModuleMapFormat::Gcc: return CxxModuleMapContentGcc(loc, obj); + case CxxModuleMapFormat::Msvc: + return CxxModuleMapContentMsvc(loc, obj, usages); } assert(false); diff --git a/Source/cmCxxModuleMapper.h b/Source/cmCxxModuleMapper.h index 6d29fc0..8526a07 100644 --- a/Source/cmCxxModuleMapper.h +++ b/Source/cmCxxModuleMapper.h @@ -18,6 +18,7 @@ enum class CxxModuleMapFormat { Gcc, + Msvc, }; struct CxxModuleLocations @@ -80,4 +81,5 @@ std::set CxxModuleUsageSeed( // object file. std::string CxxModuleMapContent(CxxModuleMapFormat format, CxxModuleLocations const& loc, - cmScanDepInfo const& obj); + cmScanDepInfo const& obj, + CxxModuleUsage const& usages); diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index f69c95f..c326ca6 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -2625,6 +2625,8 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile( // nothing to do. } else if (arg_modmapfmt == "gcc") { modmap_fmt = CxxModuleMapFormat::Gcc; + } else if (arg_modmapfmt == "msvc") { + modmap_fmt = CxxModuleMapFormat::Msvc; } else { cmSystemTools::Error( cmStrCat("-E cmake_ninja_dyndep does not understand the ", arg_modmapfmt, @@ -2712,7 +2714,7 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile( } if (modmap_fmt) { - auto mm = CxxModuleMapContent(*modmap_fmt, locs, object); + auto mm = CxxModuleMapContent(*modmap_fmt, locs, object, usages); // XXX(modmap): If changing this path construction, change // `cmNinjaTargetGenerator::WriteObjectBuildStatements` to generate the -- cgit v0.12 From c49d5f137be2c068c5ca1359726fdca0b8c4f8f5 Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Tue, 19 Jul 2022 16:10:20 -0400 Subject: RunCMake/CXXModules: add a "deep-chain" test This tests that transitive usages are propogated properly across long import chains between targets. --- Tests/RunCMake/CXXModules/RunCMakeTest.cmake | 1 + .../CXXModules/examples/deep-chain-stderr.txt | 9 +++ .../CXXModules/examples/deep-chain/CMakeLists.txt | 66 ++++++++++++++++++++++ .../RunCMake/CXXModules/examples/deep-chain/a.cxx | 6 ++ .../RunCMake/CXXModules/examples/deep-chain/b.cxx | 7 +++ .../RunCMake/CXXModules/examples/deep-chain/c.cxx | 7 +++ .../RunCMake/CXXModules/examples/deep-chain/d.cxx | 7 +++ .../RunCMake/CXXModules/examples/deep-chain/e.cxx | 7 +++ .../CXXModules/examples/deep-chain/main.cxx | 6 ++ 9 files changed, 116 insertions(+) create mode 100644 Tests/RunCMake/CXXModules/examples/deep-chain-stderr.txt create mode 100644 Tests/RunCMake/CXXModules/examples/deep-chain/CMakeLists.txt create mode 100644 Tests/RunCMake/CXXModules/examples/deep-chain/a.cxx create mode 100644 Tests/RunCMake/CXXModules/examples/deep-chain/b.cxx create mode 100644 Tests/RunCMake/CXXModules/examples/deep-chain/c.cxx create mode 100644 Tests/RunCMake/CXXModules/examples/deep-chain/d.cxx create mode 100644 Tests/RunCMake/CXXModules/examples/deep-chain/e.cxx create mode 100644 Tests/RunCMake/CXXModules/examples/deep-chain/main.cxx diff --git a/Tests/RunCMake/CXXModules/RunCMakeTest.cmake b/Tests/RunCMake/CXXModules/RunCMakeTest.cmake index ca1bc81..3f17c1f 100644 --- a/Tests/RunCMake/CXXModules/RunCMakeTest.cmake +++ b/Tests/RunCMake/CXXModules/RunCMakeTest.cmake @@ -131,6 +131,7 @@ if ("named" IN_LIST CMake_TEST_MODULE_COMPILATION) run_cxx_module_test(library library-static -DBUILD_SHARED_LIBS=OFF) run_cxx_module_test(generated) run_cxx_module_test(public-req-private) + run_cxx_module_test(deep-chain) endif () # Tests which use named modules in shared libraries. diff --git a/Tests/RunCMake/CXXModules/examples/deep-chain-stderr.txt b/Tests/RunCMake/CXXModules/examples/deep-chain-stderr.txt new file mode 100644 index 0000000..5e4392a --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/deep-chain-stderr.txt @@ -0,0 +1,9 @@ +CMake Warning \(dev\) at CMakeLists.txt:7 \(target_sources\): + CMake's C\+\+ module support is experimental. It is meant only for + experimentation and feedback to CMake developers. +This warning is for project developers. Use -Wno-dev to suppress it. + +CMake Warning \(dev\): + C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is + experimental. It is meant only for compiler developers to try. +This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/deep-chain/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/deep-chain/CMakeLists.txt new file mode 100644 index 0000000..515b240 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/deep-chain/CMakeLists.txt @@ -0,0 +1,66 @@ +cmake_minimum_required(VERSION 3.24) +project(cxx_modules_deep_chain CXX) + +include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake") + +add_library(a STATIC) +target_sources(a + PUBLIC + FILE_SET CXX_MODULES + BASE_DIRS + "${CMAKE_CURRENT_SOURCE_DIR}" + FILES + a.cxx) +target_compile_features(a PUBLIC cxx_std_20) + +add_library(b STATIC) +target_sources(b + PUBLIC + FILE_SET CXX_MODULES + BASE_DIRS + "${CMAKE_CURRENT_SOURCE_DIR}" + FILES + b.cxx) +target_compile_features(b PUBLIC cxx_std_20) +target_link_libraries(b PUBLIC a) + +add_library(c STATIC) +target_sources(c + PUBLIC + FILE_SET CXX_MODULES + BASE_DIRS + "${CMAKE_CURRENT_SOURCE_DIR}" + FILES + c.cxx) +target_compile_features(c PUBLIC cxx_std_20) +target_link_libraries(c PUBLIC b) + +add_library(d STATIC) +target_sources(d + PUBLIC + FILE_SET CXX_MODULES + BASE_DIRS + "${CMAKE_CURRENT_SOURCE_DIR}" + FILES + d.cxx) +target_compile_features(d PUBLIC cxx_std_20) +target_link_libraries(d PUBLIC c) + +add_library(e STATIC) +target_sources(e + PUBLIC + FILE_SET CXX_MODULES + BASE_DIRS + "${CMAKE_CURRENT_SOURCE_DIR}" + FILES + e.cxx) +target_compile_features(e PUBLIC cxx_std_20) +target_link_libraries(e PUBLIC d) + +add_executable(exe) +target_link_libraries(exe PRIVATE e) +target_sources(exe + PRIVATE + main.cxx) + +add_test(NAME exe COMMAND exe) diff --git a/Tests/RunCMake/CXXModules/examples/deep-chain/a.cxx b/Tests/RunCMake/CXXModules/examples/deep-chain/a.cxx new file mode 100644 index 0000000..9edaec9 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/deep-chain/a.cxx @@ -0,0 +1,6 @@ +export module a; + +export int a() +{ + return 0; +} diff --git a/Tests/RunCMake/CXXModules/examples/deep-chain/b.cxx b/Tests/RunCMake/CXXModules/examples/deep-chain/b.cxx new file mode 100644 index 0000000..38ab0c2 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/deep-chain/b.cxx @@ -0,0 +1,7 @@ +export module b; +import a; + +export int b() +{ + return a(); +} diff --git a/Tests/RunCMake/CXXModules/examples/deep-chain/c.cxx b/Tests/RunCMake/CXXModules/examples/deep-chain/c.cxx new file mode 100644 index 0000000..580a458 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/deep-chain/c.cxx @@ -0,0 +1,7 @@ +export module c; +import b; + +export int c() +{ + return b(); +} diff --git a/Tests/RunCMake/CXXModules/examples/deep-chain/d.cxx b/Tests/RunCMake/CXXModules/examples/deep-chain/d.cxx new file mode 100644 index 0000000..78bc5ba --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/deep-chain/d.cxx @@ -0,0 +1,7 @@ +export module d; +import c; + +export int d() +{ + return c(); +} diff --git a/Tests/RunCMake/CXXModules/examples/deep-chain/e.cxx b/Tests/RunCMake/CXXModules/examples/deep-chain/e.cxx new file mode 100644 index 0000000..e019440 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/deep-chain/e.cxx @@ -0,0 +1,7 @@ +export module e; +import d; + +export int e() +{ + return d(); +} diff --git a/Tests/RunCMake/CXXModules/examples/deep-chain/main.cxx b/Tests/RunCMake/CXXModules/examples/deep-chain/main.cxx new file mode 100644 index 0000000..0b7c15d --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/deep-chain/main.cxx @@ -0,0 +1,6 @@ +import e; + +int main(int argc, char* argv[]) +{ + return e(); +} -- cgit v0.12 From 4f95e6b284205c7c01ba1923e6d7f2f5a6dc849a Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Mon, 18 Jul 2022 12:39:25 -0400 Subject: ci: test BMI exporting and installation with GCC in CI --- .gitlab/ci/configure_linux_gcc_cxx_modules_ninja.cmake | 2 +- .gitlab/ci/configure_linux_gcc_cxx_modules_ninja_multi.cmake | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab/ci/configure_linux_gcc_cxx_modules_ninja.cmake b/.gitlab/ci/configure_linux_gcc_cxx_modules_ninja.cmake index bf990c8..2b04e89 100644 --- a/.gitlab/ci/configure_linux_gcc_cxx_modules_ninja.cmake +++ b/.gitlab/ci/configure_linux_gcc_cxx_modules_ninja.cmake @@ -1,4 +1,4 @@ -set(CMake_TEST_MODULE_COMPILATION "named,partitions,internal_partitions" CACHE STRING "") +set(CMake_TEST_MODULE_COMPILATION "named,partitions,internal_partitions,export_bmi,install_bmi" CACHE STRING "") set(CMake_TEST_MODULE_COMPILATION_RULES "${CMAKE_CURRENT_LIST_DIR}/cxx_modules_rules_gcc.cmake" CACHE STRING "") include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake") diff --git a/.gitlab/ci/configure_linux_gcc_cxx_modules_ninja_multi.cmake b/.gitlab/ci/configure_linux_gcc_cxx_modules_ninja_multi.cmake index bf990c8..2b04e89 100644 --- a/.gitlab/ci/configure_linux_gcc_cxx_modules_ninja_multi.cmake +++ b/.gitlab/ci/configure_linux_gcc_cxx_modules_ninja_multi.cmake @@ -1,4 +1,4 @@ -set(CMake_TEST_MODULE_COMPILATION "named,partitions,internal_partitions" CACHE STRING "") +set(CMake_TEST_MODULE_COMPILATION "named,partitions,internal_partitions,export_bmi,install_bmi" CACHE STRING "") set(CMake_TEST_MODULE_COMPILATION_RULES "${CMAKE_CURRENT_LIST_DIR}/cxx_modules_rules_gcc.cmake" CACHE STRING "") include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake") -- cgit v0.12