Add TargetPublicPair class. This is conceptually similar to InheritedLibraries, except that this separates the temporary UniqueVector<> used during construction, from the final result, which is a simple vector of (target, is_public) pairs instead, which is smaller to store and faster to parse during target resolution. This will be used in a future CL that moves and caches target resolution computations to a dedicated class. Bug: None Change-Id: I0f7840d105e812a7d43c415ce5e71e43fec1268e Reviewed-on: https://gn-review.googlesource.com/c/gn/+/14880 Commit-Queue: David Turner <digit@google.com> Reviewed-by: Brett Wilson <brettw@chromium.org>
diff --git a/build/gen.py b/build/gen.py index fa23266..eae97f9 100755 --- a/build/gen.py +++ b/build/gen.py
@@ -818,6 +818,7 @@ 'src/gn/string_utils_unittest.cc', 'src/gn/substitution_pattern_unittest.cc', 'src/gn/substitution_writer_unittest.cc', + 'src/gn/target_public_pair_unittest.cc', 'src/gn/target_unittest.cc', 'src/gn/template_unittest.cc', 'src/gn/test_with_scheduler.cc',
diff --git a/src/gn/target_public_pair.h b/src/gn/target_public_pair.h new file mode 100644 index 0000000..f211a81 --- /dev/null +++ b/src/gn/target_public_pair.h
@@ -0,0 +1,131 @@ +// Copyright 2022 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_TARGET_PUBLIC_PAIR_H_ +#define TOOLS_GN_TARGET_PUBLIC_PAIR_H_ + +#include "gn/unique_vector.h" + +class Target; + +// C++ and Rust target resolution requires computing uniquified and +// ordered lists of static/shared libraries that are collected through +// the target's dependency tree. +// +// Maintaining the order is important to ensure the libraries are linked +// in the correct order in the final link command line. +// +// Also each library must only appear once in the final list, even though +// it may appear multiple times during the dependency tree walk, either as +// a "private" or "public" dependency. +// +// The TargetPublicPair class below encodes a (target_ptr, is_public_flag) +// pair, with convenience accessors and utility structs. +// +// The TargetPublicPairListBuilder is a builder-pattern class that generates +// a unique vector of TargetPublicPair values (i.e. the final list described +// above), and supporting the special logic required to build these lists +// (see the comments for its Append() and AppendInherited() methods). +// +// A convenience encoding for a (target_ptr, is_public_flag) pair. +class TargetPublicPair { + public: + TargetPublicPair() = default; + TargetPublicPair(const Target* target, bool is_public) + : target_(target), is_public_(is_public) {} + TargetPublicPair(std::pair<const Target*, bool> pair) + : target_(pair.first), is_public_(pair.second) {} + + const Target* target() const { return target_; } + void set_target(const Target* target) { target_ = target; } + + bool is_public() const { return is_public_; } + void set_is_public(bool is_public) { is_public_ = is_public; } + + // Utility structs that can be used to instantiante containers + // that only use the target for lookups / comparisons. E.g. + // + // std::unordered_set<TargetPublicPair, + // TargetPublicPair::TargetHash, + // TargetPublicPair::TargetEqualTo> + // + // std::set<TargetPublicPair, TargetPublicPair::TargetLess> + // + struct TargetHash { + size_t operator()(TargetPublicPair p) const noexcept { + return std::hash<const Target*>()(p.target()); + } + }; + + struct TargetEqualTo { + bool operator()(TargetPublicPair a, TargetPublicPair b) const noexcept { + return a.target() == b.target(); + } + }; + + struct TargetLess { + bool operator()(TargetPublicPair a, TargetPublicPair b) const noexcept { + return a.target() < b.target(); + } + }; + + private: + const Target* target_ = nullptr; + bool is_public_ = false; +}; + +// A helper type to build a uniquified ordered vector of TargetPublicPair +// instances. Usage is: +// +// 1) Create builder instance. +// +// 2) Call Append() to add a direct dependency, or AppendInherited() to add +// transitive ones, as many times as necessary. +// +// 3) Call Build() to retrieve final list as a vector. +// +class TargetPublicPairListBuilder + : public UniqueVector<TargetPublicPair, + TargetPublicPair::TargetHash, + TargetPublicPair::TargetEqualTo> { + public: + // Add (target, is_public) to the list being constructed. If the target + // was not already in the list, record the |is_public| flag as is, + // otherwise, set the recorded flag to true only if |is_public| is true, or + // don't do anything otherwise. + void Append(const Target* target, bool is_public) { + auto ret = EmplaceBackWithIndex(target, is_public); + if (!ret.first && is_public) { + // UniqueVector<T>::operator[]() always returns a const reference + // because the returned values are lookup keys in its set-like data + // structure (thus modifying them would break its internal consistency). + // However, because TargetHash and TargetEqualTo are being used to + // instantiate this template, only the target() part of the value must + // remain constant, and it is possible to modify the is_public() part + // in-place safely. + auto* pair = const_cast<TargetPublicPair*>(&(*this)[ret.second]); + pair->set_is_public(true); + } + } + + // Append all pairs from any container with begin() and end() iterators + // that dereference to values that convert to a TargetPublicPair value. + // If |is_public| is false, the input pair will be appended with the + // value of the public flag to false. + template < + typename C, + typename = std::void_t< + decltype(static_cast<TargetPublicPair>(*std::declval<C>().begin())), + decltype(static_cast<TargetPublicPair>(*std::declval<C>().end()))>> + void AppendInherited(const C& other, bool is_public) { + for (const auto& pair : other) { + Append(pair.target(), is_public && pair.is_public()); + } + } + + // Build and return the final list to the caller. + std::vector<TargetPublicPair> Build() { return release(); } +}; + +#endif // TOOLS_GN_TARGET_PUBLIC_PAIR_H_
diff --git a/src/gn/target_public_pair_unittest.cc b/src/gn/target_public_pair_unittest.cc new file mode 100644 index 0000000..8b08a0c --- /dev/null +++ b/src/gn/target_public_pair_unittest.cc
@@ -0,0 +1,54 @@ +// Copyright 2022 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/target_public_pair.h" +#include "util/test/test.h" + +TEST(TargetPublicPairTest, ConstructionAndMutation) { + // Fake target pointer values. + const auto* a_target = reinterpret_cast<const Target*>(1000); + const auto* b_target = reinterpret_cast<const Target*>(2000); + + TargetPublicPair a_pair(a_target, true); + EXPECT_EQ(a_target, a_pair.target()); + EXPECT_TRUE(a_pair.is_public()); + + TargetPublicPair b_pair(b_target, false); + EXPECT_EQ(b_target, b_pair.target()); + EXPECT_FALSE(b_pair.is_public()); + + a_pair.set_target(b_target); + EXPECT_EQ(b_target, a_pair.target()); + EXPECT_TRUE(a_pair.is_public()); + + a_pair.set_is_public(false); + EXPECT_EQ(b_target, a_pair.target()); + EXPECT_FALSE(a_pair.is_public()); + + a_pair = TargetPublicPair(a_target, true); + EXPECT_EQ(a_target, a_pair.target()); + EXPECT_TRUE(a_pair.is_public()); + + b_pair = std::move(a_pair); + EXPECT_EQ(a_target, b_pair.target()); + EXPECT_TRUE(b_pair.is_public()); +} + +TEST(TargetPublicPairTest, Builder) { + const auto* a_target = reinterpret_cast<const Target*>(1000); + const auto* b_target = reinterpret_cast<const Target*>(2000); + TargetPublicPairListBuilder builder; + + builder.Append(a_target, false); + builder.Append(b_target, false); + builder.Append(a_target, true); + builder.Append(b_target, false); + + auto list = builder.Build(); + EXPECT_EQ(2u, list.size()); + EXPECT_EQ(a_target, list[0].target()); + EXPECT_EQ(b_target, list[1].target()); + EXPECT_TRUE(list[0].is_public()); + EXPECT_FALSE(list[1].is_public()); +}