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());
+}