| // Copyright 2023 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_RESOLVED_TARGET_DATA_H_ | |
| #define TOOLS_GN_RESOLVED_TARGET_DATA_H_ | |
| #include <atomic> | |
| #include <memory> | |
| #include <mutex> | |
| #include <shared_mutex> | |
| #include <vector> | |
| #include "base/containers/span.h" | |
| #include "gn/lib_file.h" | |
| #include "gn/output_file.h" | |
| #include "gn/resolved_target_deps.h" | |
| #include "gn/source_dir.h" | |
| #include "gn/target.h" | |
| #include "gn/target_public_pair.h" | |
| #include "gn/unique_vector.h" | |
| // A class used to compute target-specific data by collecting information | |
| // from its tree of dependencies. | |
| // | |
| // For example, linkable targets can call GetLinkedLibraries() and | |
| // GetLinkedLibraryDirs() to find the library files and library search | |
| // paths to add to their final linker command string, based on the | |
| // definitions of the `libs` and `lib_dirs` config values of their | |
| // transitive dependencies. | |
| // | |
| // Values are computed on demand, but memorized by the class instance in order | |
| // to speed up multiple queries for targets that share dependencies. | |
| // | |
| // Usage is: | |
| // | |
| // 1) Create instance. | |
| // | |
| // 2) Call any of the methods to retrieve the value of the corresponding | |
| // data. For all methods, the input Target instance passed as argument | |
| // must have been fully resolved (meaning that Target::OnResolved() | |
| // must have been called and completed). Input target pointers are | |
| // const and thus are never modified. This allows using multiple | |
| // ResolvedTargetData instances from the same input graph in multiple | |
| // threads safely. | |
| // | |
| class ResolvedTargetData { | |
| public: | |
| // Return the public/private/data/dependencies of a given target | |
| // as a ResolvedTargetDeps instance. | |
| const ResolvedTargetDeps& GetTargetDeps(const Target* target) const { | |
| return GetTargetInfo(target)->deps; | |
| } | |
| // Return the data dependencies of a given target. | |
| // Convenience shortcut for GetTargetDeps(target).data_deps(). | |
| base::span<const Target*> GetDataDeps(const Target* target) const { | |
| return GetTargetDeps(target).data_deps(); | |
| } | |
| // Return the public and private dependencies of a given target. | |
| // Convenience shortcut for GetTargetDeps(target).linked_deps(). | |
| base::span<const Target*> GetLinkedDeps(const Target* target) const { | |
| return GetTargetDeps(target).linked_deps(); | |
| } | |
| // The list of all library directory search path to add to the final link | |
| // command of linkable binary. For example, if this returns ['dir1', 'dir2'] | |
| // a command for a C++ linker would typically use `-Ldir1 -Ldir2`. | |
| const std::vector<SourceDir>& GetLinkedLibraryDirs( | |
| const Target* target) const { | |
| return GetTargetLibInfo(target)->lib_dirs; | |
| } | |
| // The list of all library files to add to the final link command of linkable | |
| // binaries. For example, if this returns ['foo', '/path/to/bar'], the command | |
| // for a C++ linker would typically use '-lfoo /path/to/bar'. | |
| const std::vector<LibFile>& GetLinkedLibraries(const Target* target) const { | |
| return GetTargetLibInfo(target)->libs; | |
| } | |
| // The list of framework directories search paths to use at link time | |
| // when generating macOS or iOS linkable binaries. | |
| const std::vector<SourceDir>& GetLinkedFrameworkDirs( | |
| const Target* target) const { | |
| return GetTargetFrameworkInfo(target)->framework_dirs; | |
| } | |
| // The list of framework names to use at link time when generating macOS | |
| // or iOS linkable binaries. | |
| const std::vector<std::string>& GetLinkedFrameworks( | |
| const Target* target) const { | |
| return GetTargetFrameworkInfo(target)->frameworks; | |
| } | |
| // The list of weak framework names to use at link time when generating macOS | |
| // or iOS linkable binaries. | |
| const std::vector<std::string>& GetLinkedWeakFrameworks( | |
| const Target* target) const { | |
| return GetTargetFrameworkInfo(target)->weak_frameworks; | |
| } | |
| // The list of weak library files to use at link time when generating macOS | |
| // or iOS linkable binaries. | |
| const std::vector<std::string>& GetLinkedWeakLibraries( | |
| const Target* target) const { | |
| return GetTargetFrameworkInfo(target)->weak_libraries; | |
| } | |
| // Retrieves a set of hard dependencies for this target. | |
| // All hard deps from this target and all dependencies, but not the | |
| // target itself. | |
| const TargetSet& GetHardDeps(const Target* target) const { | |
| return GetTargetHardDeps(target)->hard_deps; | |
| } | |
| // Retrieves an ordered list of (target, is_public) pairs for all link-time | |
| // libraries inherited by this target. | |
| const std::vector<TargetPublicPair>& GetInheritedLibraries( | |
| const Target* target) const { | |
| return GetTargetInheritedLibs(target)->inherited_libs; | |
| } | |
| // Retrieves an ordered list of (target, is_public) pairs for all module | |
| // dependencies inherited by this target. | |
| const std::vector<TargetPublicPair>& GetModuleDepsInformation( | |
| const Target* target) const { | |
| return GetTargetModuleDepsInformation(target)->module_deps_information; | |
| } | |
| // Retrieves an ordered list of (target, is_public) paris for all link-time | |
| // libraries for Rust-specific binary targets. | |
| const std::vector<TargetPublicPair>& GetRustInheritedLibraries( | |
| const Target* target) const { | |
| return GetTargetRustLibs(target)->rust_inherited_libs; | |
| } | |
| // List of dependent target that generate a .swiftmodule. The current target | |
| // is assumed to depend on those modules, and will add them to the module | |
| // search path. | |
| base::span<const Target*> GetSwiftModuleDependencies( | |
| const Target* target) const { | |
| const TargetInfo* info = GetTargetSwiftValues(target); | |
| if (!info->swift_values.get()) | |
| return {}; | |
| return info->swift_values->modules; | |
| } | |
| // Retrieves an ordered list of all order-only dependency outputs for this | |
| // target. | |
| const std::vector<OutputFile>& GetOrderOnlyDeps(const Target* target) const { | |
| return GetTargetOrderOnlyDeps(target)->order_only_deps; | |
| } | |
| // Returns true if this target exports public inputs, either directly or from | |
| // public dependencies which do. | |
| bool ExportsPublicInputs(const Target* target) const { | |
| TargetInfo* info = GetTargetInfo(target); | |
| LazyBool value = | |
| info->does_export_public_inputs.load(std::memory_order_acquire); | |
| if (value == LazyBool::kUnknown) { | |
| value = | |
| ComputeExportsPublicInputs(info) ? LazyBool::kTrue : LazyBool::kFalse; | |
| info->does_export_public_inputs.store(value, std::memory_order_release); | |
| } | |
| return value == LazyBool::kTrue; | |
| } | |
| private: | |
| enum class LazyBool : uint8_t { | |
| kUnknown, | |
| kTrue, | |
| kFalse, | |
| }; | |
| // The information associated with a given Target pointer. | |
| struct TargetInfo { | |
| TargetInfo() = default; | |
| TargetInfo(const Target* target) | |
| : target(target), | |
| deps(target->public_deps(), | |
| target->private_deps(), | |
| target->data_deps()) {} | |
| const Target* target = nullptr; | |
| ResolvedTargetDeps deps; | |
| mutable std::mutex mutex; | |
| std::atomic<bool> has_lib_info = false; | |
| std::atomic<bool> has_framework_info = false; | |
| std::atomic<bool> has_hard_deps = false; | |
| std::atomic<bool> has_inherited_libs = false; | |
| std::atomic<bool> has_module_deps_information = false; | |
| std::atomic<bool> has_rust_libs = false; | |
| std::atomic<bool> has_swift_values = false; | |
| std::atomic<bool> has_order_only_deps = false; | |
| std::atomic<LazyBool> does_export_public_inputs = LazyBool::kUnknown; | |
| // Only valid if |has_lib_info| is true. | |
| std::vector<SourceDir> lib_dirs; | |
| std::vector<LibFile> libs; | |
| // Only valid if |has_framework_info| is true. | |
| std::vector<SourceDir> framework_dirs; | |
| std::vector<std::string> frameworks; | |
| std::vector<std::string> weak_frameworks; | |
| std::vector<std::string> weak_libraries; | |
| // Only valid if |has_hard_deps| is true. | |
| TargetSet hard_deps; | |
| // Only valid if |has_inherited_libs| is true. | |
| std::vector<TargetPublicPair> inherited_libs; | |
| // Only valid if |has_module_deps_information| is true. | |
| std::vector<TargetPublicPair> module_deps_information; | |
| // Only valid if |has_rust_libs| is true. | |
| std::vector<TargetPublicPair> rust_inherited_libs; | |
| std::vector<TargetPublicPair> rust_inheritable_libs; | |
| // Only valid if |has_swift_values| is true. | |
| // Most targets will not have Swift dependencies, so only | |
| // allocate a SwiftValues struct when needed. A null pointer | |
| // indicates empty lists. | |
| struct SwiftValues { | |
| std::vector<const Target*> modules; | |
| std::vector<const Target*> public_modules; | |
| SwiftValues(std::vector<const Target*> modules, | |
| std::vector<const Target*> public_modules) | |
| : modules(std::move(modules)), | |
| public_modules(std::move(public_modules)) {} | |
| }; | |
| std::unique_ptr<SwiftValues> swift_values; | |
| // Only valid if |has_order_only_deps| is true. | |
| std::vector<OutputFile> order_only_deps; | |
| }; | |
| // Retrieve TargetInfo value associated with |target|. Create | |
| // a new empty instance on demand if none is already available. | |
| TargetInfo* GetTargetInfo(const Target* target) const; | |
| const TargetInfo* GetTargetLibInfo(const Target* target) const { | |
| TargetInfo* info = GetTargetInfo(target); | |
| if (!info->has_lib_info.load(std::memory_order_acquire)) { | |
| std::lock_guard<std::mutex> lock(info->mutex); | |
| if (!info->has_lib_info.load(std::memory_order_relaxed)) { | |
| ComputeLibInfo(info); | |
| info->has_lib_info.store(true, std::memory_order_release); | |
| } | |
| } | |
| return info; | |
| } | |
| const TargetInfo* GetTargetFrameworkInfo(const Target* target) const { | |
| TargetInfo* info = GetTargetInfo(target); | |
| if (!info->has_framework_info.load(std::memory_order_acquire)) { | |
| std::lock_guard<std::mutex> lock(info->mutex); | |
| if (!info->has_framework_info.load(std::memory_order_relaxed)) { | |
| ComputeFrameworkInfo(info); | |
| info->has_framework_info.store(true, std::memory_order_release); | |
| } | |
| } | |
| return info; | |
| } | |
| const TargetInfo* GetTargetHardDeps(const Target* target) const { | |
| TargetInfo* info = GetTargetInfo(target); | |
| if (!info->has_hard_deps.load(std::memory_order_acquire)) { | |
| std::lock_guard<std::mutex> lock(info->mutex); | |
| if (!info->has_hard_deps.load(std::memory_order_relaxed)) { | |
| ComputeHardDeps(info); | |
| info->has_hard_deps.store(true, std::memory_order_release); | |
| } | |
| } | |
| return info; | |
| } | |
| const TargetInfo* GetTargetInheritedLibs(const Target* target) const { | |
| TargetInfo* info = GetTargetInfo(target); | |
| if (!info->has_inherited_libs.load(std::memory_order_acquire)) { | |
| std::lock_guard<std::mutex> lock(info->mutex); | |
| if (!info->has_inherited_libs.load(std::memory_order_relaxed)) { | |
| ComputeInheritedLibs(info); | |
| info->has_inherited_libs.store(true, std::memory_order_release); | |
| } | |
| } | |
| return info; | |
| } | |
| const TargetInfo* GetTargetModuleDepsInformation(const Target* target) const { | |
| TargetInfo* info = GetTargetInfo(target); | |
| if (!info->has_module_deps_information.load(std::memory_order_acquire)) { | |
| std::lock_guard<std::mutex> lock(info->mutex); | |
| if (!info->has_module_deps_information.load(std::memory_order_relaxed)) { | |
| ComputeModuleDepsInformation(info); | |
| info->has_module_deps_information.store(true, | |
| std::memory_order_release); | |
| } | |
| } | |
| return info; | |
| } | |
| const TargetInfo* GetTargetRustLibs(const Target* target) const { | |
| TargetInfo* info = GetTargetInfo(target); | |
| if (!info->has_rust_libs.load(std::memory_order_acquire)) { | |
| std::lock_guard<std::mutex> lock(info->mutex); | |
| if (!info->has_rust_libs.load(std::memory_order_relaxed)) { | |
| ComputeRustLibs(info); | |
| info->has_rust_libs.store(true, std::memory_order_release); | |
| } | |
| } | |
| return info; | |
| } | |
| const TargetInfo* GetTargetSwiftValues(const Target* target) const { | |
| TargetInfo* info = GetTargetInfo(target); | |
| if (!info->has_swift_values.load(std::memory_order_acquire)) { | |
| std::lock_guard<std::mutex> lock(info->mutex); | |
| if (!info->has_swift_values.load(std::memory_order_relaxed)) { | |
| ComputeSwiftValues(info); | |
| info->has_swift_values.store(true, std::memory_order_release); | |
| } | |
| } | |
| return info; | |
| } | |
| const TargetInfo* GetTargetOrderOnlyDeps(const Target* target) const { | |
| TargetInfo* info = GetTargetInfo(target); | |
| if (!info->has_order_only_deps.load(std::memory_order_acquire)) { | |
| std::lock_guard<std::mutex> lock(info->mutex); | |
| if (!info->has_order_only_deps.load(std::memory_order_relaxed)) { | |
| ComputeOrderOnlyDeps(info); | |
| info->has_order_only_deps.store(true, std::memory_order_release); | |
| } | |
| } | |
| return info; | |
| } | |
| // Compute the portion of TargetInfo guarded by one of the |has_xxx| | |
| // booleans. This performs recursive and expensive computations and | |
| // should only be called once per TargetInfo instance. | |
| void ComputeLibInfo(TargetInfo* info) const; | |
| void ComputeFrameworkInfo(TargetInfo* info) const; | |
| void ComputeHardDeps(TargetInfo* info) const; | |
| void ComputeInheritedLibs(TargetInfo* info) const; | |
| void ComputeModuleDepsInformation(TargetInfo* info) const; | |
| void ComputeRustLibs(TargetInfo* info) const; | |
| void ComputeSwiftValues(TargetInfo* info) const; | |
| void ComputeOrderOnlyDeps(TargetInfo* info) const; | |
| bool ComputeExportsPublicInputs(const TargetInfo* info) const; | |
| // Helper function used by ComputeInheritedLibs(). | |
| void ComputeInheritedLibsFor( | |
| base::span<const Target*> deps, | |
| bool is_public, | |
| TargetPublicPairListBuilder* inherited_libraries) const; | |
| // Helper function used by ComputeModuleDepsInformation(). | |
| void ComputeModuleDepsInformationFor( | |
| base::span<const Target*> deps, | |
| bool is_public, | |
| TargetPublicPairListBuilder* module_deps_information) const; | |
| // Helper data structure and function used by ComputeRustLibs(). | |
| struct RustLibsBuilder { | |
| TargetPublicPairListBuilder inherited; | |
| TargetPublicPairListBuilder inheritable; | |
| }; | |
| void ComputeRustLibsFor(base::span<const Target*> deps, | |
| bool is_public, | |
| RustLibsBuilder* rust_libs) const; | |
| // A { Target* -> TargetInfo } map that will create entries | |
| // on demand (hence the mutable qualifier). Implemented with a | |
| // UniqueVector<> and a parallel vector of unique TargetInfo | |
| // instances for best performance. | |
| // We shard the TargetInfo map to reduce lock contention under the | |
| // high-concurrency parallel writing phase of 'gn gen'. 128 shards is chosen | |
| // as the sweet spot based on benchmarking, providing optimal scaling for | |
| // high-core workstation counts (up to 128 threads) with negligible memory | |
| // overhead from empty shards. | |
| static constexpr size_t kNumShards = 128; | |
| struct Shard { | |
| mutable std::shared_mutex mutex; | |
| UniqueVector<const Target*> targets; | |
| std::vector<std::unique_ptr<TargetInfo>> infos; | |
| }; | |
| // We use std::hash to distribute targets evenly across shards and avoid | |
| // pointer alignment biases. | |
| size_t GetShardIndex(const Target* target) const { | |
| return std::hash<const Target*>()(target) % kNumShards; | |
| } | |
| mutable Shard shards_[kNumShards]; | |
| }; | |
| #endif // TOOLS_GN_RESOLVED_TARGET_DATA_H_ |