Refactor C++ module dependency logic into a new utility This change refactors the C++ module dependency logic out of ninja_c_binary_target_writer.cc and into a new utility, ninja_module_writer_util.cc and .h. This consolidation makes the code cleaner and prepares it for reuse in other areas, like the compile_commands_writer in https://gn-review.googlesource.com/c/gn/+/19840 Bug: 443228626 Change-Id: Ide370ddde6baee588aae6e658e0f1cb550fb6e1d Reviewed-on: https://gn-review.googlesource.com/c/gn/+/19880 Commit-Queue: Takuto Ikuta <tikuta@google.com> Reviewed-by: Sylvain Defresne <sdefresne@chromium.org>
diff --git a/build/gen.py b/build/gen.py index d3ecfdc..5d161e4 100755 --- a/build/gen.py +++ b/build/gen.py
@@ -736,6 +736,7 @@ 'src/gn/ninja_create_bundle_target_writer.cc', 'src/gn/ninja_generated_file_target_writer.cc', 'src/gn/ninja_group_target_writer.cc', + 'src/gn/ninja_module_writer_util.cc', 'src/gn/ninja_outputs_writer.cc', 'src/gn/ninja_rust_binary_target_writer.cc', 'src/gn/ninja_target_command_util.cc',
diff --git a/src/gn/ninja_c_binary_target_writer.cc b/src/gn/ninja_c_binary_target_writer.cc index 83a95d0..94580e9 100644 --- a/src/gn/ninja_c_binary_target_writer.cc +++ b/src/gn/ninja_c_binary_target_writer.cc
@@ -19,6 +19,7 @@ #include "gn/escape.h" #include "gn/filesystem_utils.h" #include "gn/general_tool.h" +#include "gn/ninja_module_writer_util.h" #include "gn/ninja_target_command_util.h" #include "gn/ninja_utils.h" #include "gn/pool.h" @@ -28,29 +29,6 @@ #include "gn/substitution_writer.h" #include "gn/target.h" -struct ModuleDep { - ModuleDep(const SourceFile* modulemap, - const std::string& module_name, - const OutputFile& pcm, - bool is_self) - : modulemap(modulemap), - module_name(module_name), - pcm(pcm), - is_self(is_self) {} - - // The input module.modulemap source file. - const SourceFile* modulemap; - - // The internal module name, in GN this is the target's label. - std::string module_name; - - // The compiled version of the module. - OutputFile pcm; - - // Is this the module for the current target. - bool is_self; -}; - namespace { // Returns the proper escape options for writing compiler and linker flags. @@ -75,63 +53,6 @@ return ""; } -const SourceFile* GetModuleMapFromTargetSources(const Target* target) { - for (const SourceFile& sf : target->sources()) { - if (sf.IsModuleMapType()) - return &sf; - } - return nullptr; -} - -std::vector<ModuleDep> GetModuleDepsInformation( - const Target* target, - const ResolvedTargetData& resolved) { - std::vector<ModuleDep> ret; - // Use a set to keep track of added PCM files to ensure uniqueness. - std::set<OutputFile> added_pcms; - - auto add_if_new = [&added_pcms, &ret](const Target* t, bool is_self) { - const SourceFile* modulemap = GetModuleMapFromTargetSources(t); - if (!modulemap) // Not a module or no .modulemap file. - return; - - std::string label; - CHECK(SubstitutionWriter::GetTargetSubstitution( - t, &SubstitutionLabelNoToolchain, &label)); - - const char* tool_type; - std::vector<OutputFile> modulemap_outputs; - CHECK( - t->GetOutputFilesForSource(*modulemap, &tool_type, &modulemap_outputs)); - // Must be only one .pcm from .modulemap. - CHECK(modulemap_outputs.size() == 1u); - const OutputFile& pcm_file = modulemap_outputs[0]; - - if (added_pcms.insert(pcm_file).second) { - ret.emplace_back(modulemap, label, pcm_file, is_self); - } - }; - - if (target->source_types_used().Get(SourceFile::SOURCE_MODULEMAP)) { - add_if_new(target, true); - } - - // Process direct dependencies and their publicly inherited modules. - for (const auto& pairs : resolved.GetModuleDepsInformation(target)) { - const Target* dep = pairs.target(); - if (dep->source_types_used().Get(SourceFile::SOURCE_MODULEMAP)) { - add_if_new(dep, false); - } - } - - // Sort by pcm path for deterministic output. - std::sort(ret.begin(), ret.end(), [](const ModuleDep& a, const ModuleDep& b) { - return a.pcm < b.pcm; - }); - - return ret; -} - } // namespace NinjaCBinaryTargetWriter::NinjaCBinaryTargetWriter(const Target* target, @@ -142,7 +63,7 @@ NinjaCBinaryTargetWriter::~NinjaCBinaryTargetWriter() = default; void NinjaCBinaryTargetWriter::Run() { - std::vector<ModuleDep> module_dep_info = + std::vector<ClangModuleDep> module_dep_info = GetModuleDepsInformation(target_, resolved()); WriteCompilerVars(module_dep_info); @@ -241,7 +162,7 @@ } void NinjaCBinaryTargetWriter::WriteCompilerVars( - const std::vector<ModuleDep>& module_dep_info) { + const std::vector<ClangModuleDep>& module_dep_info) { const SubstitutionBits& subst = target_->toolchain()->substitution_bits(); WriteCCompilerVars(subst, /*indent=*/false, @@ -263,7 +184,7 @@ void NinjaCBinaryTargetWriter::WriteModuleDepsSubstitution( const Substitution* substitution, - const std::vector<ModuleDep>& module_dep_info, + const std::vector<ClangModuleDep>& module_dep_info, bool include_self) { if (target_->toolchain()->substitution_bits().used.count(substitution)) { EscapeOptions options; @@ -442,7 +363,7 @@ const std::vector<OutputFile>& pch_deps, const std::vector<OutputFile>& input_deps, const std::vector<OutputFile>& order_only_deps, - const std::vector<ModuleDep>& module_dep_info, + const std::vector<ClangModuleDep>& module_dep_info, std::vector<OutputFile>* object_files, std::vector<SourceFile>* other_files) { DCHECK(!target_->source_types_used().SwiftSourceUsed());
diff --git a/src/gn/ninja_c_binary_target_writer.h b/src/gn/ninja_c_binary_target_writer.h index fd277d7..8baa434 100644 --- a/src/gn/ninja_c_binary_target_writer.h +++ b/src/gn/ninja_c_binary_target_writer.h
@@ -11,7 +11,7 @@ #include "gn/unique_vector.h" struct EscapeOptions; -struct ModuleDep; +struct ClangModuleDep; // Writes a .ninja file for a binary target type (an executable, a shared // library, or a static library). @@ -26,12 +26,12 @@ using OutputFileSet = std::set<OutputFile>; // Writes all flags for the compiler: includes, defines, cflags, etc. - void WriteCompilerVars(const std::vector<ModuleDep>& module_dep_info); + void WriteCompilerVars(const std::vector<ClangModuleDep>& module_dep_info); // Write module_deps or module_deps_no_self flags for clang modulemaps. void WriteModuleDepsSubstitution( const Substitution* substitution, - const std::vector<ModuleDep>& module_dep_info, + const std::vector<ClangModuleDep>& module_dep_info, bool include_self); // Writes build lines required for precompiled headers. Any generated @@ -77,7 +77,7 @@ void WriteSources(const std::vector<OutputFile>& pch_deps, const std::vector<OutputFile>& input_deps, const std::vector<OutputFile>& order_only_deps, - const std::vector<ModuleDep>& module_dep_info, + const std::vector<ClangModuleDep>& module_dep_info, std::vector<OutputFile>* object_files, std::vector<SourceFile>* other_files); void WriteSwiftSources(const std::vector<OutputFile>& input_deps,
diff --git a/src/gn/ninja_module_writer_util.cc b/src/gn/ninja_module_writer_util.cc new file mode 100644 index 0000000..b08aafd --- /dev/null +++ b/src/gn/ninja_module_writer_util.cc
@@ -0,0 +1,78 @@ +// 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 "gn/resolved_target_data.h" +#include "gn/substitution_writer.h" +#include "gn/target.h" + +namespace { + +// Returns the first source file in the target's sources that is a modulemap +// file. Returns nullptr if no modulemap file is found. +const SourceFile* GetModuleMapFromTargetSources(const Target* target) { + for (const SourceFile& sf : target->sources()) { + if (sf.IsModuleMapType()) + return &sf; + } + return nullptr; +} + +} // namespace + +ClangModuleDep::ClangModuleDep(const SourceFile* modulemap, + const std::string& module_name, + const OutputFile& pcm, + bool is_self) + : modulemap(modulemap), + module_name(module_name), + pcm(pcm), + is_self(is_self) {} + +std::vector<ClangModuleDep> GetModuleDepsInformation( + const Target* target, + const ResolvedTargetData& resolved) { + std::vector<ClangModuleDep> ret; + // Use a set to keep track of added PCM files to ensure uniqueness. + std::set<OutputFile> added_pcms; + + auto add_if_new = [&added_pcms, &ret](const Target* t, bool is_self) { + const SourceFile* modulemap = GetModuleMapFromTargetSources(t); + if (!modulemap) // Not a module or no .modulemap file. + return; + + std::string label; + CHECK(SubstitutionWriter::GetTargetSubstitution( + t, &SubstitutionLabelNoToolchain, &label)); + + const char* tool_type; + std::vector<OutputFile> modulemap_outputs; + CHECK( + t->GetOutputFilesForSource(*modulemap, &tool_type, &modulemap_outputs)); + CHECK(modulemap_outputs.size() == 1u); // Must be only one .pcm. + const OutputFile& pcm_file = modulemap_outputs[0]; + + if (added_pcms.insert(pcm_file).second) { + ret.emplace_back(modulemap, label, pcm_file, is_self); + } + }; + + if (target->source_types_used().Get(SourceFile::SOURCE_MODULEMAP)) + add_if_new(target, true); + + for (const auto& pair : resolved.GetModuleDepsInformation(target)) + add_if_new(pair.target(), false); + + // Sort by pcm path for deterministic output. + std::sort(ret.begin(), ret.end(), + [](const ClangModuleDep& a, const ClangModuleDep& b) { + return a.pcm < b.pcm; + }); + + return ret; +}
diff --git a/src/gn/ninja_module_writer_util.h b/src/gn/ninja_module_writer_util.h new file mode 100644 index 0000000..19a6444 --- /dev/null +++ b/src/gn/ninja_module_writer_util.h
@@ -0,0 +1,41 @@ +// 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. + +#ifndef TOOLS_GN_NINJA_MODULE_WRITER_UTIL_H_ +#define TOOLS_GN_NINJA_MODULE_WRITER_UTIL_H_ + +#include <string> +#include <vector> + +#include "gn/output_file.h" + +class ResolvedTargetData; +class SourceFile; +class Target; + +struct ClangModuleDep { + ClangModuleDep(const SourceFile* modulemap, + const std::string& module_name, + const OutputFile& pcm, + bool is_self); + + // The input module.modulemap source file. + const SourceFile* modulemap; + + // The internal module name, in GN this is the target's label. + std::string module_name; + + // The compiled version of the module. + OutputFile pcm; + + // Is this the module for the current target. + bool is_self; +}; + +// Gathers information about all module dependencies for a given target. +std::vector<ClangModuleDep> GetModuleDepsInformation( + const Target* target, + const ResolvedTargetData& resolved); + +#endif // TOOLS_GN_NINJA_MODULE_WRITER_UTIL_H_