|  | // Copyright 2024 The Bazel Authors. All rights reserved. | 
|  | // | 
|  | // Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | // you may not use this file except in compliance with the License. | 
|  | // You may obtain a copy of the License at | 
|  | // | 
|  | //    http://www.apache.org/licenses/LICENSE-2.0 | 
|  | // | 
|  | // Unless required by applicable law or agreed to in writing, software | 
|  | // distributed under the License is distributed on an "AS IS" BASIS, | 
|  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | // See the License for the specific language governing permissions and | 
|  | // limitations under the License. | 
|  |  | 
|  | #include "tools/cpp/modules_tools/generate-modmap/generate-modmap.h" | 
|  |  | 
|  | #include <iostream> | 
|  | #include <queue> | 
|  | #include <sstream> | 
|  | #include <string> | 
|  | #include <unordered_map> | 
|  | #include <vector> | 
|  |  | 
|  | // This function writes parameters about the required modules. | 
|  | // | 
|  | // Format of the modmap file | 
|  | // Clang: -fmodule-file=<module-name>=<path/to/bmi> | 
|  | // GCC:   <module-name> <path/to/bmi> | 
|  | // MSVC:  /reference <module-name>=<path/to/bmi> | 
|  | // | 
|  | // NOTE: For the GCC compiler, additional `$root .` is added | 
|  | // | 
|  | //       Another special consideration for GCC is that it cannot specify | 
|  | //       the output module name when compiling a Module Interface. | 
|  | //       Therefore, the generated module name and BMI file path is added | 
|  | // | 
|  | // see also https://gcc.gnu.org/onlinedocs/gcc/C_002b_002b-Module-Mapper.html | 
|  | void write_modmap(std::ostream &modmap_file_stream, | 
|  | std::ostream &modmap_file_dot_input_stream, | 
|  | const std::unordered_set<ModmapItem> &modmap, | 
|  | const std::string &compiler, | 
|  | const std::optional<ModmapItem> &generated) { | 
|  | if (compiler == "gcc") { | 
|  | modmap_file_stream << "$root ." << "\n"; | 
|  | if (generated.has_value()) { | 
|  | modmap_file_stream << generated.value().name << " " | 
|  | << generated.value().path << "\n"; | 
|  | } | 
|  | } | 
|  | for (const auto &item : modmap) { | 
|  | if (compiler == "clang") { | 
|  | modmap_file_stream << "-fmodule-file=" << item.name << "=" << item.path | 
|  | << "\n"; | 
|  | } else if (compiler == "gcc") { | 
|  | modmap_file_stream << item.name << " " << item.path << "\n"; | 
|  | } else if (compiler == "msvc-cl") { | 
|  | modmap_file_stream << "/reference " << item.name << "=" << item.path | 
|  | << "\n"; | 
|  | } else { | 
|  | std::cerr << "bad compiler: " << compiler << std::endl; | 
|  | std::exit(1); | 
|  | } | 
|  | modmap_file_dot_input_stream << item.path << "\n"; | 
|  | } | 
|  | } | 
|  |  | 
|  | std::unordered_set<ModmapItem> process(const ModuleDep &dep, | 
|  | const Cpp20ModulesInfo &info) { | 
|  | std::queue<std::string> q; | 
|  | for (const auto &item : dep.require_list) { | 
|  | q.push(item); | 
|  | } | 
|  | // Get all dependencies | 
|  | std::unordered_set<std::string> s; | 
|  | while (!q.empty()) { | 
|  | std::string name = q.front(); | 
|  | q.pop(); | 
|  | s.insert(name); | 
|  | auto it = info.usages.find(name); | 
|  | if (it == info.usages.end()) { | 
|  | continue; | 
|  | } | 
|  | auto deps = it->second; | 
|  | for (const auto &dep : deps) { | 
|  | if (s.count(dep)) { | 
|  | continue; | 
|  | } | 
|  | q.push(dep); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Construct modmap | 
|  | std::unordered_set<ModmapItem> modmap; | 
|  | for (const auto &name : s) { | 
|  | auto it = info.modules.find(name); | 
|  | if (it == info.modules.end()) { | 
|  | std::cerr << "ERROR: Module not found: " << name << std::endl; | 
|  | std::exit(1); | 
|  | } | 
|  | modmap.insert(ModmapItem{name, it->second}); | 
|  | } | 
|  | return modmap; | 
|  | } |