[metadata] Walk function

Implements the main call to the walk metadata function, iterating
through specified targets to extract metadata.

Change-Id: If1058d635a5f1d3d2f9d0237826631f289dc0ddd
Reviewed-on: https://gn-review.googlesource.com/c/3360
Commit-Queue: Julie Hockett <juliehockett@google.com>
Reviewed-by: Brett Wilson <brettw@chromium.org>
diff --git a/build/gen.py b/build/gen.py
index f17a648..82c15d1 100755
--- a/build/gen.py
+++ b/build/gen.py
@@ -470,6 +470,7 @@
         'tools/gn/loader.cc',
         'tools/gn/location.cc',
         'tools/gn/metadata.cc',
+        'tools/gn/metadata_walk.cc',
         'tools/gn/ninja_action_target_writer.cc',
         'tools/gn/ninja_binary_target_writer.cc',
         'tools/gn/ninja_build_writer.cc',
@@ -570,6 +571,7 @@
         'tools/gn/label_unittest.cc',
         'tools/gn/loader_unittest.cc',
         'tools/gn/metadata_unittest.cc',
+        'tools/gn/metadata_walk_unittest.cc',
         'tools/gn/ninja_action_target_writer_unittest.cc',
         'tools/gn/ninja_binary_target_writer_unittest.cc',
         'tools/gn/ninja_build_writer_unittest.cc',
diff --git a/tools/gn/metadata_walk.cc b/tools/gn/metadata_walk.cc
new file mode 100644
index 0000000..6ce9b88
--- /dev/null
+++ b/tools/gn/metadata_walk.cc
@@ -0,0 +1,24 @@
+// Copyright 2018 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 "tools/gn/metadata_walk.h"
+
+std::vector<Value> WalkMetadata(
+    const UniqueVector<const Target*>& targets_to_walk,
+    const std::vector<std::string>& keys_to_extract,
+    const std::vector<std::string>& keys_to_walk,
+    bool rebase_files,
+    std::set<const Target*>* targets_walked,
+    Err* err) {
+  std::vector<Value> result;
+  for (const auto* target : targets_to_walk) {
+    auto pair = targets_walked->insert(target);
+    if (pair.second) {
+      if (!target->GetMetadata(keys_to_extract, keys_to_walk, rebase_files,
+                               &result, targets_walked, err))
+        return std::vector<Value>();
+    }
+  }
+  return result;
+}
\ No newline at end of file
diff --git a/tools/gn/metadata_walk.h b/tools/gn/metadata_walk.h
new file mode 100644
index 0000000..4601a3f
--- /dev/null
+++ b/tools/gn/metadata_walk.h
@@ -0,0 +1,26 @@
+// Copyright 2018 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_METADATAWALK_H_
+#define TOOLS_GN_METADATAWALK_H_
+
+#include "tools/gn/build_settings.h"
+#include "tools/gn/target.h"
+#include "tools/gn/unique_vector.h"
+#include "tools/gn/value.h"
+
+// Function to collect metadata from resolved targets listed in targets_walked.
+// Intended to be called after all targets are resolved.
+//
+// This populates targets_walked with all targets touched by this walk, and
+// returns the list of metadata values.
+std::vector<Value> WalkMetadata(
+    const UniqueVector<const Target*>& targets_to_walk,
+    const std::vector<std::string>& keys_to_extract,
+    const std::vector<std::string>& keys_to_walk,
+    bool rebase_files,
+    std::set<const Target*>* targets_walked,
+    Err* err);
+
+#endif  // TOOLS_GN_METADATAWALK_H_
diff --git a/tools/gn/metadata_walk_unittest.cc b/tools/gn/metadata_walk_unittest.cc
new file mode 100644
index 0000000..4855131
--- /dev/null
+++ b/tools/gn/metadata_walk_unittest.cc
@@ -0,0 +1,211 @@
+// Copyright 2018 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 "tools/gn/metadata_walk.h"
+
+#include "tools/gn/metadata.h"
+#include "tools/gn/target.h"
+#include "tools/gn/test_with_scope.h"
+#include "tools/gn/unique_vector.h"
+#include "util/test/test.h"
+
+TEST(MetadataWalkTest, CollectNoRecurse) {
+  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/"));
+
+  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));
+
+  Value b_2_expected(nullptr, Value::LIST);
+  b_2_expected.list_value().push_back(Value(nullptr, false));
+  two.metadata().contents().insert(
+      std::pair<base::StringPiece, Value>("b", b_2_expected));
+
+  two.metadata().set_source_dir(SourceDir("/usr/home/files/inner"));
+
+  UniqueVector<const Target*> targets;
+  targets.push_back(&one);
+  targets.push_back(&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::set<const Target*> targets_walked;
+  std::vector<Value> result =
+      WalkMetadata(targets, data_keys, walk_keys, false, &targets_walked, &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"));
+  expected.push_back(Value(nullptr, false));
+  EXPECT_EQ(result, expected);
+
+  std::set<const Target*> expected_walked_targets;
+  expected_walked_targets.insert(&one);
+  expected_walked_targets.insert(&two);
+  EXPECT_EQ(targets_walked, expected_walked_targets);
+}
+
+TEST(MetadataWalkTest, CollectWithRecurse) {
+  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));
+
+  UniqueVector<const Target*> targets;
+  targets.push_back(&one);
+
+  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::set<const Target*> targets_walked;
+  std::vector<Value> result =
+      WalkMetadata(targets, data_keys, walk_keys, false, &targets_walked, &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);
+
+  std::set<const Target*> expected_walked_targets;
+  expected_walked_targets.insert(&one);
+  expected_walked_targets.insert(&two);
+  EXPECT_EQ(targets_walked, expected_walked_targets);
+}
+
+TEST(MetadataWalkTest, CollectWithBarrier) {
+  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));
+
+  UniqueVector<const Target*> targets;
+  targets.push_back(&one);
+
+  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::set<const Target*> targets_walked;
+  std::vector<Value> result =
+      WalkMetadata(targets, data_keys, walk_keys, false, &targets_walked, &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();
+
+  std::set<const Target*> expected_walked_targets;
+  expected_walked_targets.insert(&one);
+  expected_walked_targets.insert(&two);
+  EXPECT_EQ(targets_walked, expected_walked_targets);
+}
+
+TEST(MetadataWalkTest, CollectWithError) {
+  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));
+
+  UniqueVector<const Target*> targets;
+  targets.push_back(&one);
+
+  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::set<const Target*> targets_walked;
+  std::vector<Value> result =
+      WalkMetadata(targets, data_keys, walk_keys, false, &targets_walked, &err);
+  EXPECT_TRUE(result.empty());
+  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();
+}