[metadata] Allow specification of rebase dir

Change generated_file's rebase variable to be a source directory,
specifying on which dir to rebase the final paths.

Change-Id: Ic4d04427f66580d3e49f75d5079bedfb117b4d58
Reviewed-on: https://gn-review.googlesource.com/c/3580
Commit-Queue: Julie Hockett <juliehockett@google.com>
Reviewed-by: Brett Wilson <brettw@chromium.org>
diff --git a/tools/gn/command_meta.cc b/tools/gn/command_meta.cc
index f5f96b4..ecf07ef 100644
--- a/tools/gn/command_meta.cc
+++ b/tools/gn/command_meta.cc
@@ -20,11 +20,36 @@
 const char kMeta_HelpShort[] = "meta: List target metadata collection results.";
 const char kMeta_Help[] =
     R"(gn meta <out_dir> <target>* --data=<key>[,<key>*]* [--walk=<key>[,<key>*]*]
-       [--rebase]
+       [--rebase=<dest dir>]
 
   Lists collected metaresults of all given targets for the given data key(s),
   collecting metadata dependencies as specified by the given walk key(s).
 
+  See `gn help generated_file` for more information on the walk.
+
+Arguments
+
+  <target(s)>
+    A list of target labels from which to initiate the walk.
+
+  --data
+    A list of keys from which to extract data. In each target walked, its metadata
+    scope is checked for the presence of these keys. If present, the contents of
+    those variable in the scope are appended to the results list.
+
+  --walk (optional)
+    A list of keys from which to control the walk. In each target walked, its
+    metadata scope is checked for the presence of any of these keys. If present,
+    the contents of those variables is checked to ensure that it is a label of
+    a valid dependency of the target and then added to the set of targets to walk.
+    If the empty string ("") is present in any of these keys, all deps and data_deps
+    are added to the walk set.
+
+  --rebase (optional)
+    A destination directory onto which to rebase any paths found. If set, all
+    collected metadata will be rebased onto this path. This option will throw errors
+    if collected metadata is not a list of strings.
+
 Examples
 
   gn meta out/Debug "//base/foo" --data=files
@@ -39,17 +64,17 @@
       Lists collected metaresults for the `files` key in the //base/foo:foo
       target and all of the dependencies listed in the `stop` key (and so on).
 
-  gn meta out/Debug "//base/foo" --data=files --rebase-files
+  gn meta out/Debug "//base/foo" --data=files --rebase="/"
       Lists collected metaresults for the `files` key in the //base/foo:foo
       target and all of its dependency tree, rebasing the strings in the `files`
-      key onto the source directory of the target's declaration.
+      key onto the source directory of the target's declaration relative to "/".
 )";
 
 int RunMeta(const std::vector<std::string>& args) {
   if (args.size() == 0) {
     Err(Location(), "You're holding it wrong.",
         "Usage: \"gn meta <out_dir> <target>* --data=<key>[,<key>*] "
-        "[--walk=<key>[,<key>*]*] [--rebase-files]\"")
+        "[--walk=<key>[,<key>*]*] [--rebase=<dest dir>]\"")
         .PrintToStdout();
     return 1;
   }
@@ -59,7 +84,8 @@
     return 1;
 
   const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
-  bool rebase_files = cmdline->HasSwitch(switches::kMetaRebaseFiles);
+  std::string rebase_dir =
+      cmdline->GetSwitchValueASCII(switches::kMetaRebaseFiles);
   std::string data_keys_str =
       cmdline->GetSwitchValueASCII(switches::kMetaDataKeys);
   std::string walk_keys_str =
@@ -86,8 +112,9 @@
       walk_keys_str, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
   Err err;
   std::set<const Target*> targets_walked;
-  std::vector<Value> result = WalkMetadata(targets, data_keys, walk_keys,
-                                           rebase_files, &targets_walked, &err);
+  std::vector<Value> result =
+      WalkMetadata(targets, data_keys, walk_keys, SourceDir(rebase_dir),
+                   &targets_walked, &err);
   if (err.has_error()) {
     err.PrintToStdout();
     return 1;
diff --git a/tools/gn/desc_builder.cc b/tools/gn/desc_builder.cc
index 3bc7e86..d4b4941 100644
--- a/tools/gn/desc_builder.cc
+++ b/tools/gn/desc_builder.cc
@@ -476,8 +476,8 @@
         res->SetKey(variables::kDataKeys, std::move(keys));
       }
       if (what(variables::kRebase)) {
-        res->SetKey(variables::kRebase,
-                    std::move(base::Value(target_->rebase())));
+        res->SetWithoutPathExpansion(variables::kRebase,
+                                     RenderValue(target_->rebase()));
       }
       if (what(variables::kWalkKeys)) {
         base::ListValue keys;
diff --git a/tools/gn/generated_file_target_generator.cc b/tools/gn/generated_file_target_generator.cc
index f953f38..1854d5b 100644
--- a/tools/gn/generated_file_target_generator.cc
+++ b/tools/gn/generated_file_target_generator.cc
@@ -105,9 +105,19 @@
     return true;
   if (!IsMetadataCollectionTarget(variables::kRebase, value->origin()))
     return false;
-  if (!value->VerifyTypeIs(Value::BOOLEAN, err_))
+  if (!value->VerifyTypeIs(Value::STRING, err_))
     return false;
-  target_->set_rebase(value->boolean_value());
+
+  if (value->string_value().empty())
+    return true;  // Treat empty string as the default and do nothing.
+
+  const BuildSettings* build_settings = scope_->settings()->build_settings();
+  SourceDir dir = scope_->GetSourceDir().ResolveRelativeDir(
+      *value, err_, build_settings->root_path_utf8());
+  if (err_->has_error())
+    return false;
+
+  target_->set_rebase(dir);
   return true;
 }
 
diff --git a/tools/gn/metadata.cc b/tools/gn/metadata.cc
index 605294b..3250b03 100644
--- a/tools/gn/metadata.cc
+++ b/tools/gn/metadata.cc
@@ -4,10 +4,12 @@
 
 #include "tools/gn/metadata.h"
 
+#include "tools/gn/filesystem_utils.h"
+
 bool Metadata::WalkStep(const BuildSettings* settings,
                         const std::vector<std::string>& keys_to_extract,
                         const std::vector<std::string>& keys_to_walk,
-                        bool rebase_files,
+                        const SourceDir& rebase_dir,
                         std::vector<Value>* next_walk_keys,
                         std::vector<Value>* result,
                         Err* err) const {
@@ -24,22 +26,22 @@
       continue;
     assert(iter->second.type() == Value::LIST);
 
-    if (rebase_files) {
+    if (!rebase_dir.is_null()) {
       for (const auto& val : iter->second.list_value()) {
         if (!val.VerifyTypeIs(Value::STRING, err))
           return false;
-        // TODO(juliehockett): Do we want to consider absolute paths here? In
-        // which case we'd need to propagate the root_path_utf8 from
-        // build_settings as well.
         std::string filename = source_dir_.ResolveRelativeAs(
             /*as_file = */ true, val, err, settings->root_path_utf8());
         if (err->has_error())
           return false;
-        result->emplace_back(val.origin(), std::move(filename));
+
+        result->emplace_back(
+            val.origin(),
+            RebasePath(filename, rebase_dir, settings->root_path_utf8()));
       }
     } else {
       result->insert(result->end(), iter->second.list_value().begin(),
-                    iter->second.list_value().end());
+                     iter->second.list_value().end());
     }
   }
 
diff --git a/tools/gn/metadata.h b/tools/gn/metadata.h
index 235542b..da79fdf 100644
--- a/tools/gn/metadata.h
+++ b/tools/gn/metadata.h
@@ -53,7 +53,7 @@
   bool WalkStep(const BuildSettings* settings,
                 const std::vector<std::string>& keys_to_extract,
                 const std::vector<std::string>& keys_to_walk,
-                bool rebase_files,
+                const SourceDir& rebase_dir,
                 std::vector<Value>* next_walk_keys,
                 std::vector<Value>* result,
                 Err* err) const;
diff --git a/tools/gn/metadata_unittest.cc b/tools/gn/metadata_unittest.cc
index 2fcebfa..b159372 100644
--- a/tools/gn/metadata_unittest.cc
+++ b/tools/gn/metadata_unittest.cc
@@ -58,8 +58,8 @@
 
   Err err;
   EXPECT_TRUE(metadata.WalkStep(setup.settings()->build_settings(), data_keys,
-                                walk_keys, false, &next_walk_keys, &results,
-                                &err));
+                                walk_keys, SourceDir(), &next_walk_keys,
+                                &results, &err));
   EXPECT_FALSE(err.has_error());
   EXPECT_EQ(next_walk_keys, expected_walk_keys);
   EXPECT_EQ(results, expected);
@@ -84,16 +84,16 @@
   std::vector<Value> results;
 
   std::vector<Value> expected;
-  expected.emplace_back(Value(nullptr, "/usr/home/files/foo.cpp"));
-  expected.emplace_back(Value(nullptr, "/usr/home/files/foo/bar.h"));
+  expected.emplace_back(Value(nullptr, "../home/files/foo.cpp"));
+  expected.emplace_back(Value(nullptr, "../home/files/foo/bar.h"));
 
   std::vector<Value> expected_walk_keys;
   expected_walk_keys.emplace_back(nullptr, "");
 
   Err err;
   EXPECT_TRUE(metadata.WalkStep(setup.settings()->build_settings(), data_keys,
-                                walk_keys, true, &next_walk_keys, &results,
-                                &err));
+                                walk_keys, SourceDir("/usr/foo_dir/"),
+                                &next_walk_keys, &results, &err));
   EXPECT_FALSE(err.has_error());
   EXPECT_EQ(next_walk_keys, expected_walk_keys);
   EXPECT_EQ(results, expected);
@@ -119,8 +119,8 @@
 
   Err err;
   EXPECT_FALSE(metadata.WalkStep(setup.settings()->build_settings(), data_keys,
-                                 walk_keys, true, &next_walk_keys, &results,
-                                 &err));
+                                 walk_keys, SourceDir("/foo_dir/"),
+                                 &next_walk_keys, &results, &err));
   EXPECT_TRUE(err.has_error());
 }
 
@@ -146,8 +146,8 @@
 
   Err err;
   EXPECT_TRUE(metadata.WalkStep(setup.settings()->build_settings(), data_keys,
-                                walk_keys, true, &next_walk_keys, &results,
-                                &err));
+                                walk_keys, SourceDir(), &next_walk_keys,
+                                &results, &err));
   EXPECT_FALSE(err.has_error());
   EXPECT_EQ(next_walk_keys, expected_walk_keys);
   EXPECT_TRUE(results.empty());
@@ -168,8 +168,8 @@
 
   Err err;
   EXPECT_TRUE(metadata.WalkStep(setup.settings()->build_settings(), data_keys,
-                                walk_keys, true, &next_walk_keys, &results,
-                                &err));
+                                walk_keys, SourceDir(), &next_walk_keys,
+                                &results, &err));
   EXPECT_FALSE(err.has_error());
   EXPECT_EQ(next_walk_keys, expected_walk_keys);
   EXPECT_TRUE(results.empty());
@@ -196,8 +196,8 @@
 
   Err err;
   EXPECT_TRUE(metadata.WalkStep(setup.settings()->build_settings(), data_keys,
-                                walk_keys, true, &next_walk_keys, &results,
-                                &err));
+                                walk_keys, SourceDir(), &next_walk_keys,
+                                &results, &err));
   EXPECT_FALSE(err.has_error());
   EXPECT_EQ(next_walk_keys, expected_walk_keys);
   EXPECT_TRUE(results.empty());
diff --git a/tools/gn/metadata_walk.cc b/tools/gn/metadata_walk.cc
index 6ce9b88..dabe285 100644
--- a/tools/gn/metadata_walk.cc
+++ b/tools/gn/metadata_walk.cc
@@ -8,14 +8,14 @@
     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,
+    const SourceDir& rebase_dir,
     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,
+      if (!target->GetMetadata(keys_to_extract, keys_to_walk, rebase_dir,
                                &result, targets_walked, err))
         return std::vector<Value>();
     }
diff --git a/tools/gn/metadata_walk.h b/tools/gn/metadata_walk.h
index 4601a3f..9f4f34d 100644
--- a/tools/gn/metadata_walk.h
+++ b/tools/gn/metadata_walk.h
@@ -19,7 +19,7 @@
     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,
+    const SourceDir& rebase_dir,
     std::set<const Target*>* targets_walked,
     Err* err);
 
diff --git a/tools/gn/metadata_walk_unittest.cc b/tools/gn/metadata_walk_unittest.cc
index 4855131..4ceea7b 100644
--- a/tools/gn/metadata_walk_unittest.cc
+++ b/tools/gn/metadata_walk_unittest.cc
@@ -51,8 +51,8 @@
 
   Err err;
   std::set<const Target*> targets_walked;
-  std::vector<Value> result =
-      WalkMetadata(targets, data_keys, walk_keys, false, &targets_walked, &err);
+  std::vector<Value> result = WalkMetadata(targets, data_keys, walk_keys,
+                                           SourceDir(), &targets_walked, &err);
   EXPECT_FALSE(err.has_error());
 
   std::vector<Value> expected;
@@ -101,8 +101,8 @@
 
   Err err;
   std::set<const Target*> targets_walked;
-  std::vector<Value> result =
-      WalkMetadata(targets, data_keys, walk_keys, false, &targets_walked, &err);
+  std::vector<Value> result = WalkMetadata(targets, data_keys, walk_keys,
+                                           SourceDir(), &targets_walked, &err);
   EXPECT_FALSE(err.has_error());
 
   std::vector<Value> expected;
@@ -158,8 +158,8 @@
 
   Err err;
   std::set<const Target*> targets_walked;
-  std::vector<Value> result =
-      WalkMetadata(targets, data_keys, walk_keys, false, &targets_walked, &err);
+  std::vector<Value> result = WalkMetadata(targets, data_keys, walk_keys,
+                                           SourceDir(), &targets_walked, &err);
   EXPECT_FALSE(err.has_error()) << err.message();
 
   std::vector<Value> expected;
@@ -198,8 +198,8 @@
 
   Err err;
   std::set<const Target*> targets_walked;
-  std::vector<Value> result =
-      WalkMetadata(targets, data_keys, walk_keys, false, &targets_walked, &err);
+  std::vector<Value> result = WalkMetadata(targets, data_keys, walk_keys,
+                                           SourceDir(), &targets_walked, &err);
   EXPECT_TRUE(result.empty());
   EXPECT_TRUE(err.has_error());
   EXPECT_EQ(err.message(),
diff --git a/tools/gn/target.cc b/tools/gn/target.cc
index 89810c6..c5eebd3 100644
--- a/tools/gn/target.cc
+++ b/tools/gn/target.cc
@@ -287,8 +287,7 @@
       check_includes_(true),
       complete_static_lib_(false),
       testonly_(false),
-      toolchain_(nullptr),
-      rebase_(false) {}
+      toolchain_(nullptr) {}
 
 Target::~Target() = default;
 
@@ -880,13 +879,13 @@
 
 bool Target::GetMetadata(const std::vector<std::string>& keys_to_extract,
                          const std::vector<std::string>& keys_to_walk,
-                         bool rebase_files,
+                         const SourceDir& rebase_dir,
                          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,
+                          keys_to_walk, rebase_dir, &next_walk_keys, result,
                           err))
     return false;
 
@@ -905,7 +904,7 @@
         // 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,
+          if (!dep.ptr->GetMetadata(keys_to_extract, keys_to_walk, rebase_dir,
                                     result, targets_walked, err))
             return false;
         }
@@ -924,7 +923,7 @@
         // 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,
+          if (!dep.ptr->GetMetadata(keys_to_extract, keys_to_walk, rebase_dir,
                                     result, targets_walked, err))
             return false;
         }
diff --git a/tools/gn/target.h b/tools/gn/target.h
index e75b97a..0087e9e 100644
--- a/tools/gn/target.h
+++ b/tools/gn/target.h
@@ -154,7 +154,7 @@
   // 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,
+                   const SourceDir& rebase_dir,
                    std::vector<Value>* result,
                    std::set<const Target*>* targets_walked,
                    Err* err) const;
@@ -168,8 +168,8 @@
   void set_output_conversion(const Value& value) { output_conversion_ = value; }
 
   // Metadata collection methods for WriteData targets.
-  bool rebase() const { return rebase_; }
-  void set_rebase(bool value) { rebase_ = value; }
+  const SourceDir& rebase() const { return rebase_; }
+  void set_rebase(const SourceDir& value) { rebase_ = value; }
   const std::vector<std::string>& data_keys() const { return data_keys_; }
   std::vector<std::string>& data_keys() { return data_keys_; }
   const std::vector<std::string>& walk_keys() const { return walk_keys_; }
@@ -426,7 +426,7 @@
   Value contents_;  // Value::NONE if metadata collection should occur.
 
   // GeneratedFile as metadata collection values.
-  bool rebase_;
+  SourceDir rebase_;
   std::vector<std::string> data_keys_;
   std::vector<std::string> walk_keys_;
 
diff --git a/tools/gn/target_unittest.cc b/tools/gn/target_unittest.cc
index b4fa5f9..55605c6 100644
--- a/tools/gn/target_unittest.cc
+++ b/tools/gn/target_unittest.cc
@@ -1118,7 +1118,7 @@
   Err err;
   std::vector<Value> result;
   std::set<const Target*> targets;
-  one.GetMetadata(data_keys, walk_keys, false, &result, &targets, &err);
+  one.GetMetadata(data_keys, walk_keys, SourceDir(), &result, &targets, &err);
   EXPECT_FALSE(err.has_error());
 
   std::vector<Value> expected;
@@ -1158,7 +1158,7 @@
   Err err;
   std::vector<Value> result;
   std::set<const Target*> targets;
-  one.GetMetadata(data_keys, walk_keys, false, &result, &targets, &err);
+  one.GetMetadata(data_keys, walk_keys, SourceDir(), &result, &targets, &err);
   EXPECT_FALSE(err.has_error());
 
   std::vector<Value> expected;
@@ -1207,7 +1207,7 @@
   Err err;
   std::vector<Value> result;
   std::set<const Target*> targets;
-  one.GetMetadata(data_keys, walk_keys, false, &result, &targets, &err);
+  one.GetMetadata(data_keys, walk_keys, SourceDir(), &result, &targets, &err);
   EXPECT_FALSE(err.has_error()) << err.message();
 
   std::vector<Value> expected;
@@ -1239,7 +1239,7 @@
   Err err;
   std::vector<Value> result;
   std::set<const Target*> targets;
-  one.GetMetadata(data_keys, walk_keys, false, &result, &targets, &err);
+  one.GetMetadata(data_keys, walk_keys, SourceDir(), &result, &targets, &err);
   EXPECT_TRUE(err.has_error());
   EXPECT_EQ(err.message(),
             "I was expecting //foo:missing to be a dependency of "