[metadata] Adding metadata variable to target

This adds the `metadata` variable to targets.

Change-Id: Iddb8adada4323f6e44011bc315fa20674dcbcaff
Reviewed-on: https://gn-review.googlesource.com/c/3300
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 56a6566..e32a609 100755
--- a/build/gen.py
+++ b/build/gen.py
@@ -568,6 +568,7 @@
         'tools/gn/label_pattern_unittest.cc',
         'tools/gn/label_unittest.cc',
         'tools/gn/loader_unittest.cc',
+        'tools/gn/metadata_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/functions_target.cc b/tools/gn/functions_target.cc
index 2339e17..008b47e 100644
--- a/tools/gn/functions_target.cc
+++ b/tools/gn/functions_target.cc
@@ -16,9 +16,10 @@
 #define DEPENDENT_CONFIG_VARS \
   "  Dependent configs: all_dependent_configs, public_configs\n"
 #define DEPS_VARS "  Deps: data_deps, deps, public_deps\n"
-#define GENERAL_TARGET_VARS                                                  \
-  "  General: check_includes, configs, data, friend, inputs, output_name,\n" \
-  "           output_extension, public, sources, testonly, visibility\n"
+#define GENERAL_TARGET_VARS                                                \
+  "  General: check_includes, configs, data, friend, inputs, metadata,\n"  \
+  "           output_name, output_extension, public, sources, testonly,\n" \
+  "           visibility\n"
 
 namespace functions {
 
@@ -138,7 +139,7 @@
     R"(
 Variables
 
-  args, data, data_deps, depfile, deps, inputs, outputs*, pool,
+  args, data, data_deps, depfile, deps, inputs, metadata, outputs*, pool,
   response_file_contents, script*, sources
   * = required
 
@@ -208,7 +209,7 @@
     R"(
 Variables
 
-  args, data, data_deps, depfile, deps, inputs, outputs*, pool,
+  args, data, data_deps, depfile, deps, inputs, metadata, outputs*, pool,
   response_file_contents, script*, sources*
   * = required
 
@@ -272,7 +273,7 @@
 
 Variables
 
-  sources*, outputs*, deps, data_deps, public_deps, visibility
+  sources*, outputs*, deps, data_deps, metadata, public_deps, visibility
   * = required
 
 Examples
@@ -361,7 +362,8 @@
   bundle_executable_dir*, bundle_plugins_dir*, bundle_deps_filter, deps,
   data_deps, public_deps, visibility, product_type, code_signing_args,
   code_signing_script, code_signing_sources, code_signing_outputs,
-  xcode_extra_attributes, xcode_test_application_name, partial_info_plist
+  xcode_extra_attributes, xcode_test_application_name, partial_info_plist,
+  metadata
   * = required
 
 Example
diff --git a/tools/gn/metadata.h b/tools/gn/metadata.h
new file mode 100644
index 0000000..fe66ff6
--- /dev/null
+++ b/tools/gn/metadata.h
@@ -0,0 +1,55 @@
+// 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_METADATA_H_
+#define TOOLS_GN_METADATA_H_
+
+#include <memory>
+
+#include "tools/gn/scope.h"
+#include "tools/gn/source_dir.h"
+
+// Metadata about a particular target.
+//
+// Metadata is a collection of keys and values relating to a particular target.
+// Generally, these keys will include three categories of strings: ordinary
+// strings, filenames intended to be rebased according to their particular
+// source directory, and target labels intended to be used as barriers to the
+// walk. Verfication of these categories occurs at walk time, not creation
+// time (since it is not clear until the walk which values are intended for
+// which purpose).
+//
+// Represented as a scope in the expression language, here it is reduced to just
+// the KeyValueMap (since it doesn't need the logical overhead of a full scope).
+// Values must be lists of strings, as the walking collection logic contatenates
+// their values across targets.
+class Metadata {
+ public:
+  using Contents = Scope::KeyValueMap;
+
+  // Members must be set explicitly.
+  Metadata() = default;
+
+  const ParseNode* origin() const { return origin_; }
+  void set_origin(const ParseNode* origin) { origin_ = origin; }
+
+  // The contents of this metadata varaiable.
+  const Contents& contents() const { return contents_; }
+  Contents& contents() { return contents_; }
+  void set_contents(Contents&& contents) { contents_.swap(contents); }
+
+  // The relative source directory to use when rebasing.
+  const SourceDir& source_dir() const { return source_dir_; }
+  SourceDir& source_dir() { return source_dir_; }
+  void set_source_dir(const SourceDir& d) { source_dir_ = d; }
+
+ private:
+  const ParseNode* origin_;
+  Contents contents_;
+  SourceDir source_dir_;
+
+  DISALLOW_COPY_AND_ASSIGN(Metadata);
+};
+
+#endif  // TOOLS_GN_METADATA_H_
diff --git a/tools/gn/metadata_unittest.cc b/tools/gn/metadata_unittest.cc
new file mode 100644
index 0000000..cb94911
--- /dev/null
+++ b/tools/gn/metadata_unittest.cc
@@ -0,0 +1,32 @@
+// 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.h"
+#include "tools/gn/test_with_scope.h"
+#include "util/test/test.h"
+
+TEST(MetadataTest, SetContents) {
+  Metadata metadata;
+
+  ASSERT_TRUE(metadata.contents().empty());
+
+  Value a_expected(nullptr, Value::LIST);
+  a_expected.list_value().push_back(Value(nullptr, "foo"));
+  Value b_expected(nullptr, Value::LIST);
+  b_expected.list_value().push_back(Value(nullptr, true));
+
+  Metadata::Contents contents;
+  contents.insert(std::pair<base::StringPiece, Value>("a", a_expected));
+  contents.insert(std::pair<base::StringPiece, Value>("b", b_expected));
+
+  metadata.set_contents(std::move(contents));
+
+  ASSERT_EQ(metadata.contents().size(), 2);
+  auto a_actual = metadata.contents().find("a");
+  auto b_actual = metadata.contents().find("b");
+  ASSERT_FALSE(a_actual == metadata.contents().end());
+  ASSERT_FALSE(b_actual == metadata.contents().end());
+  ASSERT_EQ(a_actual->second, a_expected);
+  ASSERT_EQ(b_actual->second, b_expected);
+}
diff --git a/tools/gn/target.h b/tools/gn/target.h
index 8754b61..5421cca 100644
--- a/tools/gn/target.h
+++ b/tools/gn/target.h
@@ -20,6 +20,7 @@
 #include "tools/gn/label_pattern.h"
 #include "tools/gn/label_ptr.h"
 #include "tools/gn/lib_file.h"
+#include "tools/gn/metadata.h"
 #include "tools/gn/ordered_set.h"
 #include "tools/gn/output_file.h"
 #include "tools/gn/source_file.h"
@@ -144,6 +145,10 @@
     complete_static_lib_ = complete;
   }
 
+  // Metadata. Target takes ownership of the resulting scope.
+  const Metadata& metadata() const { return metadata_; }
+  Metadata& metadata() { return metadata_; }
+
   bool testonly() const { return testonly_; }
   void set_testonly(bool value) { testonly_ = value; }
 
@@ -388,6 +393,8 @@
   OutputFile dependency_output_file_;
   std::vector<OutputFile> runtime_outputs_;
 
+  Metadata metadata_;
+
   DISALLOW_COPY_AND_ASSIGN(Target);
 };
 
diff --git a/tools/gn/target_generator.cc b/tools/gn/target_generator.cc
index 4bc693c..c7a9f08 100644
--- a/tools/gn/target_generator.cc
+++ b/tools/gn/target_generator.cc
@@ -20,6 +20,7 @@
 #include "tools/gn/filesystem_utils.h"
 #include "tools/gn/functions.h"
 #include "tools/gn/group_target_generator.h"
+#include "tools/gn/metadata.h"
 #include "tools/gn/parse_tree.h"
 #include "tools/gn/scheduler.h"
 #include "tools/gn/scope.h"
@@ -50,6 +51,9 @@
   if (!FillDependencies())
     return;
 
+  if (!FillMetadata())
+    return;
+
   if (!FillTestonly())
     return;
 
@@ -254,6 +258,36 @@
   return true;
 }
 
+bool TargetGenerator::FillMetadata() {
+  // Need to get a mutable value to mark all values in the scope as used. This
+  // cannot be done on a const Scope.
+  Value* value = scope_->GetMutableValue(variables::kMetadata,
+                                         Scope::SEARCH_CURRENT, true);
+
+  if (!value)
+    return true;
+
+  if (!value->VerifyTypeIs(Value::SCOPE, err_))
+    return false;
+
+  Scope* scope_value = value->scope_value();
+
+  scope_value->GetCurrentScopeValues(&target_->metadata().contents());
+  scope_value->MarkAllUsed();
+
+  // Metadata values should always hold lists of Values, such that they can be
+  // collected and concatenated. Any additional specific type verification is
+  // done at walk time.
+  for (const auto& iter : target_->metadata().contents()) {
+    if (!iter.second.VerifyTypeIs(Value::LIST, err_))
+      return false;
+  }
+
+  target_->metadata().set_source_dir(scope_->GetSourceDir());
+  target_->metadata().set_origin(value->origin());
+  return true;
+}
+
 bool TargetGenerator::FillTestonly() {
   const Value* value = scope_->GetValue(variables::kTestonly, true);
   if (value) {
diff --git a/tools/gn/target_generator.h b/tools/gn/target_generator.h
index 549f2cd..627505d 100644
--- a/tools/gn/target_generator.h
+++ b/tools/gn/target_generator.h
@@ -68,6 +68,7 @@
   bool FillDependentConfigs();  // Includes all types of dependent configs.
   bool FillData();
   bool FillDependencies();  // Includes data dependencies.
+  bool FillMetadata();
   bool FillTestonly();
   bool FillAssertNoDeps();
   bool FillWriteRuntimeDeps();
diff --git a/tools/gn/variables.cc b/tools/gn/variables.cc
index a405d9d..16de86f 100644
--- a/tools/gn/variables.cc
+++ b/tools/gn/variables.cc
@@ -1337,6 +1337,31 @@
     libs = [ "ld" ]
 )";
 
+const char kMetadata[] = "metadata";
+const char kMetadata_HelpShort[] = "metadata: [scope] Metadata of this target.";
+const char kMetadata_Help[] =
+    R"(metadata: Metadata of this target.
+
+  Metadata is a collection of keys and values relating to a particular target.
+  Generally, these keys will include three categories of strings: ordinary
+  strings, filenames intended to be rebased according to their particular
+  source directory, and target labels intended to be used as barriers to the
+  walk. Verfication of these categories occurs at walk time, not creation
+  time (since it is not clear until the walk which values are intended for
+  which purpose).
+
+  Example
+
+  group("doom_melon") {
+    metadata = {
+      # These keys are not built in to GN but are interpreted when consuming
+      # metadata.
+      my_barrier = []
+      my_files = [ "a.txt", "b.txt" ]
+    }
+  }
+)";
+
 const char kOutputExtension[] = "output_extension";
 const char kOutputExtension_HelpShort[] =
     "output_extension: [string] Value to use for the output's file extension.";
@@ -2092,6 +2117,7 @@
     INSERT_VARIABLE(Ldflags)
     INSERT_VARIABLE(Libs)
     INSERT_VARIABLE(LibDirs)
+    INSERT_VARIABLE(Metadata)
     INSERT_VARIABLE(OutputDir)
     INSERT_VARIABLE(OutputExtension)
     INSERT_VARIABLE(OutputName)
diff --git a/tools/gn/variables.h b/tools/gn/variables.h
index 080989b..a90cb00 100644
--- a/tools/gn/variables.h
+++ b/tools/gn/variables.h
@@ -219,6 +219,10 @@
 extern const char kLibs_HelpShort[];
 extern const char kLibs_Help[];
 
+extern const char kMetadata[];
+extern const char kMetadata_HelpShort[];
+extern const char kMetadata_Help[];
+
 extern const char kOutputDir[];
 extern const char kOutputDir_HelpShort[];
 extern const char kOutputDir_Help[];