| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450 |
- /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
- file Copyright.txt or https://cmake.org/licensing for details. */
- #include "cmCxxModuleMapper.h"
- #include <cassert>
- #include <cstddef>
- #include <set>
- #include <sstream>
- #include <string>
- #include <utility>
- #include <vector>
- #include <cm/string_view>
- #include <cmext/string_view>
- #include "cmScanDepFormat.h"
- #include "cmStringAlgorithms.h"
- #include "cmSystemTools.h"
- CxxBmiLocation::CxxBmiLocation() = default;
- CxxBmiLocation::CxxBmiLocation(std::string path)
- : BmiLocation(std::move(path))
- {
- }
- CxxBmiLocation CxxBmiLocation::Unknown()
- {
- return {};
- }
- CxxBmiLocation CxxBmiLocation::Private()
- {
- return { std::string{} };
- }
- CxxBmiLocation CxxBmiLocation::Known(std::string path)
- {
- return { std::move(path) };
- }
- bool CxxBmiLocation::IsKnown() const
- {
- return this->BmiLocation.has_value();
- }
- bool CxxBmiLocation::IsPrivate() const
- {
- if (auto const& loc = this->BmiLocation) {
- return loc->empty();
- }
- return false;
- }
- std::string const& CxxBmiLocation::Location() const
- {
- if (auto const& loc = this->BmiLocation) {
- return *loc;
- }
- static std::string empty;
- return empty;
- }
- CxxBmiLocation CxxModuleLocations::BmiGeneratorPathForModule(
- std::string const& logical_name) const
- {
- auto bmi_loc = this->BmiLocationForModule(logical_name);
- if (bmi_loc.IsKnown() && !bmi_loc.IsPrivate()) {
- bmi_loc =
- CxxBmiLocation::Known(this->PathForGenerator(bmi_loc.Location()));
- }
- return bmi_loc;
- }
- namespace {
- struct TransitiveUsage
- {
- TransitiveUsage(std::string name, std::string location, LookupMethod method)
- : LogicalName(std::move(name))
- , Location(std::move(location))
- , Method(method)
- {
- }
- std::string LogicalName;
- std::string Location;
- LookupMethod Method;
- };
- std::vector<TransitiveUsage> GetTransitiveUsages(
- CxxModuleLocations const& loc, std::vector<cmSourceReqInfo> const& required,
- CxxModuleUsage const& usages)
- {
- std::set<std::string> transitive_usage_directs;
- std::set<std::string> transitive_usage_names;
- std::vector<TransitiveUsage> all_usages;
- for (auto const& r : required) {
- auto bmi_loc = loc.BmiGeneratorPathForModule(r.LogicalName);
- if (bmi_loc.IsKnown()) {
- all_usages.emplace_back(r.LogicalName, bmi_loc.Location(), r.Method);
- 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()) {
- all_usages.emplace_back(transitive_name, module_ref->second.Path,
- module_ref->second.Method);
- }
- }
- return all_usages;
- }
- std::string CxxModuleMapContentClang(CxxModuleLocations const& loc,
- cmScanDepInfo const& obj,
- CxxModuleUsage const& usages)
- {
- std::stringstream mm;
- // Clang'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 {};
- }
- // A series of flags which tell the compiler where to look for modules.
- for (auto const& p : obj.Provides) {
- auto bmi_loc = loc.BmiGeneratorPathForModule(p.LogicalName);
- if (bmi_loc.IsKnown()) {
- // Force the TU to be considered a C++ module source file regardless of
- // extension.
- mm << "-x c++-module\n";
- mm << "-fmodule-output=" << bmi_loc.Location() << '\n';
- break;
- }
- }
- auto all_usages = GetTransitiveUsages(loc, obj.Requires, usages);
- for (auto const& usage : all_usages) {
- mm << "-fmodule-file=" << usage.LogicalName << '=' << usage.Location
- << '\n';
- }
- return mm.str();
- }
- std::string CxxModuleMapContentGcc(CxxModuleLocations const& loc,
- cmScanDepInfo const& obj)
- {
- std::stringstream mm;
- // 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 " << loc.RootDirectory << '\n';
- for (auto const& p : obj.Provides) {
- auto bmi_loc = loc.BmiGeneratorPathForModule(p.LogicalName);
- if (bmi_loc.IsKnown()) {
- mm << p.LogicalName << ' ' << bmi_loc.Location() << '\n';
- }
- }
- for (auto const& r : obj.Requires) {
- auto bmi_loc = loc.BmiGeneratorPathForModule(r.LogicalName);
- if (bmi_loc.IsKnown()) {
- mm << r.LogicalName << ' ' << bmi_loc.Location() << '\n';
- }
- }
- 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";
- }
- auto bmi_loc = loc.BmiGeneratorPathForModule(p.LogicalName);
- if (bmi_loc.IsKnown()) {
- mm << "-ifcOutput " << bmi_loc.Location() << '\n';
- }
- }
- auto all_usages = GetTransitiveUsages(loc, obj.Requires, usages);
- for (auto const& usage : all_usages) {
- auto flag = flag_for_method(usage.Method);
- mm << flag << ' ' << usage.LogicalName << '=' << usage.Location << '\n';
- }
- return mm.str();
- }
- }
- 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<CxxModuleMapFormat> format)
- {
- if (format) {
- switch (*format) {
- case CxxModuleMapFormat::Clang:
- return ".pcm"_s;
- case CxxModuleMapFormat::Gcc:
- return ".gcm"_s;
- case CxxModuleMapFormat::Msvc:
- return ".ifc"_s;
- }
- }
- return ".bmi"_s;
- }
- std::set<std::string> CxxModuleUsageSeed(
- CxxModuleLocations const& loc, std::vector<cmScanDepInfo> const& objects,
- CxxModuleUsage& usages, bool& private_usage_found)
- {
- // 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<std::string, std::set<std::string>> internal_usages;
- std::set<std::string> unresolved;
- for (cmScanDepInfo const& object : objects) {
- // Add references for each of the provided modules.
- for (auto const& p : object.Provides) {
- auto bmi_loc = loc.BmiGeneratorPathForModule(p.LogicalName);
- if (bmi_loc.IsKnown()) {
- // XXX(cxx-modules): How to support header units?
- usages.AddReference(p.LogicalName, bmi_loc.Location(),
- LookupMethod::ByName);
- }
- }
- // For each requires, pull in what is required.
- for (auto const& r : object.Requires) {
- // Find the required name in the current target.
- auto bmi_loc = loc.BmiGeneratorPathForModule(r.LogicalName);
- if (bmi_loc.IsPrivate()) {
- cmSystemTools::Error(
- cmStrCat("Unable to use module '", r.LogicalName,
- "' as it is 'PRIVATE' and therefore not accessible outside "
- "of its owning target."));
- private_usage_found = true;
- continue;
- }
- // Find transitive usages.
- auto transitive_usages = usages.Usage.find(r.LogicalName);
- for (auto const& p : object.Provides) {
- auto& this_usages = usages.Usage[p.LogicalName];
- // Add the direct usage.
- this_usages.insert(r.LogicalName);
- if (transitive_usages == usages.Usage.end() ||
- internal_usages.find(r.LogicalName) != internal_usages.end()) {
- // Mark that we need to update transitive usages later.
- if (bmi_loc.IsKnown()) {
- internal_usages[p.LogicalName].insert(r.LogicalName);
- }
- } else {
- // Add the transitive usage.
- this_usages.insert(transitive_usages->second.begin(),
- transitive_usages->second.end());
- }
- }
- if (bmi_loc.IsKnown()) {
- usages.AddReference(r.LogicalName, bmi_loc.Location(), 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,
- CxxModuleUsage const& usages)
- {
- switch (format) {
- case CxxModuleMapFormat::Clang:
- return CxxModuleMapContentClang(loc, obj, usages);
- case CxxModuleMapFormat::Gcc:
- return CxxModuleMapContentGcc(loc, obj);
- case CxxModuleMapFormat::Msvc:
- return CxxModuleMapContentMsvc(loc, obj, usages);
- }
- assert(false);
- return {};
- }
- CxxModuleMapMode CxxModuleMapOpenMode(CxxModuleMapFormat format)
- {
- switch (format) {
- case CxxModuleMapFormat::Gcc:
- return CxxModuleMapMode::Binary;
- case CxxModuleMapFormat::Clang:
- case CxxModuleMapFormat::Msvc:
- return CxxModuleMapMode::Default;
- }
- assert(false);
- return CxxModuleMapMode::Default;
- }
|