| // Copyright 2025 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "gn/ninja_module_writer_util.h" |
| |
| #include <algorithm> |
| #include <set> |
| #include <utility> |
| |
| #include "base/strings/stringprintf.h" |
| #include "gn/resolved_target_data.h" |
| #include "gn/substitution_writer.h" |
| #include "gn/target.h" |
| |
| ClangModuleDep::ClangModuleDep(const SourceFile* modulemap, |
| const std::string& module_name, |
| std::optional<OutputFile> pcm, |
| bool is_self) |
| : modulemap(modulemap), |
| module_name(module_name), |
| pcm(std::move(pcm)), |
| is_self(is_self) {} |
| |
| std::strong_ordering ClangModuleDep::operator<=>( |
| const ClangModuleDep& other) const { |
| // Sort by (module name, modulemap path, module file path) |
| if (auto cmp = module_name <=> other.module_name; cmp != 0) |
| return cmp; |
| if (modulemap && other.modulemap) { |
| if (auto cmp = *modulemap <=> *other.modulemap; cmp != 0) |
| return cmp; |
| } else { |
| if (auto cmp = modulemap <=> other.modulemap; cmp != 0) |
| return cmp; |
| } |
| // std::optional doesn't support <=> on older versions of mac. |
| if (pcm.has_value() && other.pcm.has_value()) { |
| return *pcm <=> *other.pcm; |
| } else { |
| return pcm.has_value() <=> other.pcm.has_value(); |
| } |
| } |
| |
| std::set<ClangModuleDep> GetModuleDepsInformation( |
| const Target* target, |
| const ResolvedTargetData& resolved) { |
| std::set<ClangModuleDep> ret; |
| |
| auto add_if_new = [&ret](const Target* t, bool is_self, |
| bool has_generated_modulemap) { |
| if (!t->module_type().test(Target::HAS_MODULEMAP)) |
| return; |
| |
| std::optional<OutputFile> pcm = std::nullopt; |
| auto modulemap = t->modulemap_file(); |
| CHECK(modulemap != nullptr); |
| if (!t->module_type().test(Target::MODULEMAP_IS_TEXTUAL)) { |
| const char* tool_type; |
| std::vector<OutputFile> modulemap_outputs; |
| CHECK(t->GetOutputFilesForSource(*modulemap, &tool_type, |
| &modulemap_outputs)); |
| CHECK(modulemap_outputs.size() == 1u); |
| pcm = std::move(modulemap_outputs[0]); |
| } |
| |
| ret.emplace( |
| // If we have a generated modulemap, the modulemap should contain |
| // "extern module" declarations, so we don't need to declare |
| // -fmodule-map-file for the dependencies. |
| has_generated_modulemap ? nullptr : modulemap, t->module_name(), pcm, |
| is_self); |
| }; |
| |
| // Generated modulemaps always generate private modulemaps as well. |
| bool has_generated_modulemap = |
| target->module_type().test(Target::MODULEMAP_IS_GENERATED); |
| if (has_generated_modulemap) { |
| // Add the private modulemap as a dependency. |
| ret.emplace(target->private_modulemap_file(), |
| base::StringPrintf("%s_Private", target->module_name().c_str()), |
| std::nullopt, true); |
| } else { |
| add_if_new(target, true, has_generated_modulemap); |
| } |
| |
| for (const auto& pair : resolved.GetModuleDepsInformation(target)) |
| add_if_new(pair.target(), false, has_generated_modulemap); |
| |
| return ret; |
| } |
| |
| void ClangModuleDep::Write(std::ostream& out, |
| const PathOutput& path_output, |
| bool include_self) const { |
| if (modulemap) { |
| out << " -fmodule-map-file="; |
| path_output.WriteFile(out, *modulemap); |
| } |
| if (pcm && (include_self || !is_self)) { |
| out << " -fmodule-file=" << module_name << "="; |
| path_output.WriteFile(out, *pcm); |
| } |
| } |