[metadata] Add metadata walk to target Adds metadata walk logic to targets. Change-Id: Icef7cc8d2b53413f4e8b4357a6f02cac3a52c874 Reviewed-on: https://gn-review.googlesource.com/c/3380 Commit-Queue: Julie Hockett <juliehockett@google.com> Reviewed-by: Brett Wilson <brettw@chromium.org>
diff --git a/tools/gn/source_dir.h b/tools/gn/source_dir.h index 5c1621b..bfc260d 100644 --- a/tools/gn/source_dir.h +++ b/tools/gn/source_dir.h
@@ -119,6 +119,16 @@ return base::StringPiece(&value_[1], value_.size() - 1); } + // Returns a path that does not end with a slash. + // + // This function simply returns the reference to the value if the path is a + // root, e.g. "/" or "//". + base::StringPiece SourceWithNoTrailingSlash() const { + if (value_.size() > 2) + return base::StringPiece(&value_[0], value_.size() - 1); + return base::StringPiece(value_); + } + void SwapValue(std::string* v); bool operator==(const SourceDir& other) const {
diff --git a/tools/gn/source_dir_unittest.cc b/tools/gn/source_dir_unittest.cc index adf865b..d5ada23 100644 --- a/tools/gn/source_dir_unittest.cc +++ b/tools/gn/source_dir_unittest.cc
@@ -185,3 +185,24 @@ EXPECT_FALSE(err.has_error()); #endif } + +TEST(SourceDir, SourceWithNoTrailingSlash) { + Err err; + SourceDir base("//base/"); + SourceDir base_no_slash("//base/"); + EXPECT_EQ(base.SourceWithNoTrailingSlash(), "//base"); + EXPECT_EQ(base_no_slash.SourceWithNoTrailingSlash(), "//base"); + + SourceDir relative_root("//"); + EXPECT_EQ(relative_root.SourceWithNoTrailingSlash(), "//"); + +#if defined(OS_WIN) + SourceDir root("C:/"); + SourceDir root_no_slash("C:"); + EXPECT_EQ(root.SourceWithNoTrailingSlash(), "C:"); + EXPECT_EQ(root_no_slash.SourceWithNoTrailingSlash(), "C:"); +#else + SourceDir root("/"); + EXPECT_EQ(root.SourceWithNoTrailingSlash(), "/"); +#endif +}
diff --git a/tools/gn/target.cc b/tools/gn/target.cc index fbbfd50..bcddf8b 100644 --- a/tools/gn/target.cc +++ b/tools/gn/target.cc
@@ -873,3 +873,73 @@ g_scheduler->AddUnknownGeneratedInput(this, source); } } + +bool Target::GetMetadata(const std::vector<std::string>& keys_to_extract, + const std::vector<std::string>& keys_to_walk, + bool rebase_files, + std::vector<Value>* result, + std::set<const Target*>* targets_walked, + Err* err) const { + std::vector<Value> next_walk_keys; + if (!metadata_.WalkStep(settings()->build_settings(), keys_to_extract, + keys_to_walk, rebase_files, &next_walk_keys, result, + err)) + return false; + + // Gather walk keys and find the appropriate target. Targets identified in + // the walk key set must be deps or data_deps of the declaring target. + const DepsIteratorRange& all_deps = GetDeps(Target::DEPS_ALL); + for (const auto& next : next_walk_keys) { + DCHECK(next.type() == Value::STRING); + + // If we hit an empty string in this list, add all deps and data_deps. The + // ordering in the resulting list of values as a result will be the data + // from each explicitly listed dep prior to this, followed by all data in + // walk order of the remaining deps. + if (next.string_value().empty()) { + for (const auto& dep : all_deps) { + // If we haven't walked this dep yet, go down into it. + auto pair = targets_walked->insert(dep.ptr); + if (pair.second) { + if (!dep.ptr->GetMetadata(keys_to_extract, keys_to_walk, rebase_files, + result, targets_walked, err)) + return false; + } + } + + // Any other walk keys are superfluous, as they can only be a subset of + // all deps. + break; + } + + // Otherwise, look through the target's deps for the specified one. + bool found_next = false; + for (const auto& dep : all_deps) { + // Match against the label with the toolchain. + if (dep.label.GetUserVisibleName(true) == next.string_value()) { + // If we haven't walked this dep yet, go down into it. + auto pair = targets_walked->insert(dep.ptr); + if (pair.second) { + if (!dep.ptr->GetMetadata(keys_to_extract, keys_to_walk, rebase_files, + result, targets_walked, err)) + return false; + } + // We found it, so we can exit this search now. + found_next = true; + break; + } + } + // If we didn't find the specified dep in the target, that's an error. + // Propagate it back to the user. + if (!found_next) { + *err = Err(next.origin(), + std::string("I was expecting ") + next.string_value() + + std::string(" to be a dependency of ") + + label().GetUserVisibleName(true) + + ". Make sure it's included in the deps or data_deps, and " + "that you've specified the appropriate toolchain."); + return false; + } + } + return true; +}
diff --git a/tools/gn/target.h b/tools/gn/target.h index 5421cca..474f9c9 100644 --- a/tools/gn/target.h +++ b/tools/gn/target.h
@@ -149,6 +149,15 @@ const Metadata& metadata() const { return metadata_; } Metadata& metadata() { return metadata_; } + // Collect metadata from this target and its dependencies. This is intended to + // be called after the target is resolved. + bool GetMetadata(const std::vector<std::string>& keys_to_extract, + const std::vector<std::string>& keys_to_walk, + bool rebase_files, + std::vector<Value>* result, + std::set<const Target*>* targets_walked, + Err* err) const; + bool testonly() const { return testonly_; } void set_testonly(bool value) { testonly_ = value; }
diff --git a/tools/gn/target_unittest.cc b/tools/gn/target_unittest.cc index 62dad52..b4fa5f9 100644 --- a/tools/gn/target_unittest.cc +++ b/tools/gn/target_unittest.cc
@@ -1092,3 +1092,159 @@ ASSERT_TRUE(e.bundle_data().assets_catalog_sources().empty()); ASSERT_EQ(e.bundle_data().bundle_deps().size(), 2u); } + +TEST(TargetTest, CollectMetadataNoRecurse) { + TestWithScope setup; + + TestTarget one(setup, "//foo:one", Target::SOURCE_SET); + Value a_expected(nullptr, Value::LIST); + a_expected.list_value().push_back(Value(nullptr, "foo")); + one.metadata().contents().insert( + std::pair<base::StringPiece, Value>("a", a_expected)); + + Value b_expected(nullptr, Value::LIST); + b_expected.list_value().push_back(Value(nullptr, true)); + one.metadata().contents().insert( + std::pair<base::StringPiece, Value>("b", b_expected)); + + one.metadata().set_source_dir(SourceDir("/usr/home/files/")); + + std::vector<std::string> data_keys; + data_keys.push_back("a"); + data_keys.push_back("b"); + + std::vector<std::string> walk_keys; + + Err err; + std::vector<Value> result; + std::set<const Target*> targets; + one.GetMetadata(data_keys, walk_keys, false, &result, &targets, &err); + EXPECT_FALSE(err.has_error()); + + std::vector<Value> expected; + expected.push_back(Value(nullptr, "foo")); + expected.push_back(Value(nullptr, true)); + EXPECT_EQ(result, expected); +} + +TEST(TargetTest, CollectMetadataWithRecurse) { + TestWithScope setup; + + TestTarget one(setup, "//foo:one", Target::SOURCE_SET); + Value a_expected(nullptr, Value::LIST); + a_expected.list_value().push_back(Value(nullptr, "foo")); + one.metadata().contents().insert( + std::pair<base::StringPiece, Value>("a", a_expected)); + + Value b_expected(nullptr, Value::LIST); + b_expected.list_value().push_back(Value(nullptr, true)); + one.metadata().contents().insert( + std::pair<base::StringPiece, Value>("b", b_expected)); + + TestTarget two(setup, "//foo:two", Target::SOURCE_SET); + Value a_2_expected(nullptr, Value::LIST); + a_2_expected.list_value().push_back(Value(nullptr, "bar")); + two.metadata().contents().insert( + std::pair<base::StringPiece, Value>("a", a_2_expected)); + + one.public_deps().push_back(LabelTargetPair(&two)); + + std::vector<std::string> data_keys; + data_keys.push_back("a"); + data_keys.push_back("b"); + + std::vector<std::string> walk_keys; + + Err err; + std::vector<Value> result; + std::set<const Target*> targets; + one.GetMetadata(data_keys, walk_keys, false, &result, &targets, &err); + EXPECT_FALSE(err.has_error()); + + std::vector<Value> expected; + expected.push_back(Value(nullptr, "foo")); + expected.push_back(Value(nullptr, true)); + expected.push_back(Value(nullptr, "bar")); + EXPECT_EQ(result, expected); +} + +TEST(TargetTest, CollectMetadataWithBarrier) { + TestWithScope setup; + + TestTarget one(setup, "//foo:one", Target::SOURCE_SET); + Value a_expected(nullptr, Value::LIST); + a_expected.list_value().push_back(Value(nullptr, "foo")); + one.metadata().contents().insert( + std::pair<base::StringPiece, Value>("a", a_expected)); + + Value walk_expected(nullptr, Value::LIST); + walk_expected.list_value().push_back( + Value(nullptr, "//foo:two(//toolchain:default)")); + one.metadata().contents().insert( + std::pair<base::StringPiece, Value>("walk", walk_expected)); + + TestTarget two(setup, "//foo:two", Target::SOURCE_SET); + Value a_2_expected(nullptr, Value::LIST); + a_2_expected.list_value().push_back(Value(nullptr, "bar")); + two.metadata().contents().insert( + std::pair<base::StringPiece, Value>("a", a_2_expected)); + + TestTarget three(setup, "//foo:three", Target::SOURCE_SET); + Value a_3_expected(nullptr, Value::LIST); + a_3_expected.list_value().push_back(Value(nullptr, "baz")); + three.metadata().contents().insert( + std::pair<base::StringPiece, Value>("a", a_3_expected)); + + one.public_deps().push_back(LabelTargetPair(&two)); + one.public_deps().push_back(LabelTargetPair(&three)); + + std::vector<std::string> data_keys; + data_keys.push_back("a"); + + std::vector<std::string> walk_keys; + walk_keys.push_back("walk"); + + Err err; + std::vector<Value> result; + std::set<const Target*> targets; + one.GetMetadata(data_keys, walk_keys, false, &result, &targets, &err); + EXPECT_FALSE(err.has_error()) << err.message(); + + std::vector<Value> expected; + expected.push_back(Value(nullptr, "foo")); + expected.push_back(Value(nullptr, "bar")); + EXPECT_EQ(result, expected) << result.size(); +} + +TEST(TargetTest, CollectMetadataWithError) { + TestWithScope setup; + + TestTarget one(setup, "//foo:one", Target::SOURCE_SET); + Value a_expected(nullptr, Value::LIST); + a_expected.list_value().push_back(Value(nullptr, "foo")); + one.metadata().contents().insert( + std::pair<base::StringPiece, Value>("a", a_expected)); + + Value walk_expected(nullptr, Value::LIST); + walk_expected.list_value().push_back(Value(nullptr, "//foo:missing")); + one.metadata().contents().insert( + std::pair<base::StringPiece, Value>("walk", walk_expected)); + + std::vector<std::string> data_keys; + data_keys.push_back("a"); + + std::vector<std::string> walk_keys; + walk_keys.push_back("walk"); + + Err err; + std::vector<Value> result; + std::set<const Target*> targets; + one.GetMetadata(data_keys, walk_keys, false, &result, &targets, &err); + EXPECT_TRUE(err.has_error()); + EXPECT_EQ(err.message(), + "I was expecting //foo:missing to be a dependency of " + "//foo:one(//toolchain:default). " + "Make sure it's included in the deps or data_deps, and that you've " + "specified the appropriate toolchain.") + << err.message(); +}