[GN] Implements analyzing dependencies between items.

The goal of this CL is to teach GN analyzer to handle build files.

Previous CL:
https://chromium-review.googlesource.com/c/chromium/src/+/838220
implemented tracking build dependency files for each item that
participates in the build dependency tree.

This CL implements analyzing the dependencies between items so as
to recursively decide the set of items/targets that are either
directly or indirectly affected by a list of modified source files
(including build files).

This CL also writes unit tests to verify the new behaviors.

Bug: 795913
Change-Id: I5c123180183246f719248415cdc496a154b5e754
Reviewed-on: https://chromium-review.googlesource.com/851080
Commit-Queue: Yuke Liao <liaoyuke@chromium.org>
Reviewed-by: Dirk Pranke <dpranke@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#527646}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: da71ef3911f81a7453ea8da4297c0eba9c308748
diff --git a/tools/gn/analyzer.cc b/tools/gn/analyzer.cc
index fd01759..36c8d49 100644
--- a/tools/gn/analyzer.cc
+++ b/tools/gn/analyzer.cc
@@ -15,18 +15,16 @@
 #include "base/strings/string_util.h"
 #include "base/values.h"
 #include "tools/gn/builder.h"
+#include "tools/gn/config.h"
 #include "tools/gn/deps_iterator.h"
 #include "tools/gn/err.h"
 #include "tools/gn/filesystem_utils.h"
 #include "tools/gn/loader.h"
 #include "tools/gn/location.h"
+#include "tools/gn/pool.h"
 #include "tools/gn/source_file.h"
 #include "tools/gn/target.h"
 
-using LabelSet = Analyzer::LabelSet;
-using SourceFileSet = Analyzer::SourceFileSet;
-using TargetSet = Analyzer::TargetSet;
-
 namespace {
 
 struct Inputs {
@@ -34,40 +32,30 @@
   std::vector<Label> compile_vec;
   std::vector<Label> test_vec;
   bool compile_included_all = false;
-  SourceFileSet source_files;
-  LabelSet compile_labels;
-  LabelSet test_labels;
+  std::set<const SourceFile*> source_files;
+  std::set<Label> compile_labels;
+  std::set<Label> test_labels;
 };
 
 struct Outputs {
   std::string status;
   std::string error;
   bool compile_includes_all = false;
-  LabelSet compile_labels;
-  LabelSet test_labels;
-  LabelSet invalid_labels;
+  std::set<Label> compile_labels;
+  std::set<Label> test_labels;
+  std::set<Label> invalid_labels;
 };
 
-LabelSet LabelsFor(const TargetSet& targets) {
-  LabelSet labels;
+std::set<Label> LabelsFor(const std::set<const Target*>& targets) {
+  std::set<Label> labels;
   for (auto* target : targets)
     labels.insert(target->label());
   return labels;
 }
 
-bool AnyBuildFilesWereModified(const SourceFileSet& source_files) {
-  for (auto* file : source_files) {
-    if (base::EndsWith(file->value(), ".gn", base::CompareCase::SENSITIVE) ||
-        base::EndsWith(file->value(), ".gni", base::CompareCase::SENSITIVE) ||
-        base::EndsWith(file->value(), "build/vs_toolchain.py",
-                       base::CompareCase::SENSITIVE))
-      return true;
-  }
-  return false;
-}
-
-TargetSet Intersect(const TargetSet& l, const TargetSet& r) {
-  TargetSet result;
+std::set<const Target*> Intersect(const std::set<const Target*>& l,
+                                  const std::set<const Target*>& r) {
+  std::set<const Target*> result;
   std::set_intersection(l.begin(), l.end(), r.begin(), r.end(),
                         std::inserter(result, result.begin()));
   return result;
@@ -109,7 +97,7 @@
 void WriteLabels(const Label& default_toolchain,
                  base::DictionaryValue& dict,
                  const std::string& key,
-                 const LabelSet& labels) {
+                 const std::set<Label>& labels) {
   std::vector<std::string> strings;
   auto value = std::make_unique<base::ListValue>();
   for (const auto l : labels)
@@ -224,17 +212,45 @@
 
 }  // namespace
 
-Analyzer::Analyzer(const Builder& builder)
-    : all_targets_(builder.GetAllResolvedTargets()),
-      default_toolchain_(builder.loader()->GetDefaultToolchain()) {
-  for (const auto* target : all_targets_) {
-    labels_to_targets_[target->label()] = target;
-    for (const auto& dep_pair : target->GetDeps(Target::DEPS_ALL))
-      dep_map_.insert(std::make_pair(dep_pair.ptr, target));
-  }
-  for (const auto* target : all_targets_) {
-    if (dep_map_.find(target) == dep_map_.end())
-      roots_.insert(target);
+Analyzer::Analyzer(const Builder& builder,
+                   const SourceFile& build_config_file,
+                   const SourceFile& dot_file,
+                   const std::set<SourceFile>& build_args_dependency_files)
+    : all_items_(builder.GetAllResolvedItems()),
+      default_toolchain_(builder.loader()->GetDefaultToolchain()),
+      build_config_file_(build_config_file),
+      dot_file_(dot_file),
+      build_args_dependency_files_(build_args_dependency_files) {
+  for (const auto* item : all_items_) {
+    labels_to_items_[item->label()] = item;
+
+    // Fill dep_map_.
+    if (item->AsTarget()) {
+      for (const auto& dep_target_pair :
+           item->AsTarget()->GetDeps(Target::DEPS_ALL))
+        dep_map_.insert(std::make_pair(dep_target_pair.ptr, item));
+
+      for (const auto& dep_config_pair : item->AsTarget()->configs())
+        dep_map_.insert(std::make_pair(dep_config_pair.ptr, item));
+
+      dep_map_.insert(std::make_pair(item->AsTarget()->toolchain(), item));
+
+      if (item->AsTarget()->output_type() == Target::ACTION ||
+          item->AsTarget()->output_type() == Target::ACTION_FOREACH) {
+        const LabelPtrPair<Pool>& pool =
+            item->AsTarget()->action_values().pool();
+        if (pool.ptr)
+          dep_map_.insert(std::make_pair(pool.ptr, item));
+      }
+    } else if (item->AsConfig()) {
+      for (const auto& dep_config_pair : item->AsConfig()->configs())
+        dep_map_.insert(std::make_pair(dep_config_pair.ptr, item));
+    } else if (item->AsToolchain()) {
+      for (const auto& dep_pair : item->AsToolchain()->deps())
+        dep_map_.insert(std::make_pair(dep_pair.ptr, item));
+    } else {
+      DCHECK(item->AsPool());
+    }
   }
 }
 
@@ -250,7 +266,7 @@
     return OutputsToJSON(outputs, default_toolchain_, err);
   }
 
-  LabelSet invalid_labels;
+  std::set<Label> invalid_labels;
   for (const auto& label : InvalidLabels(inputs.compile_labels))
     invalid_labels.insert(label);
   for (const auto& label : InvalidLabels(inputs.test_labels))
@@ -261,12 +277,7 @@
     return OutputsToJSON(outputs, default_toolchain_, err);
   }
 
-  // TODO(crbug.com/555273): We can do smarter things when we detect changes
-  // to build files. For example, if all of the ninja files are unchanged,
-  // we know that we can ignore changes to .gn* files. Also, for most .gn
-  // files, we can treat a change as simply affecting every target, config,
-  // or toolchain defined in that file.
-  if (AnyBuildFilesWereModified(inputs.source_files)) {
+  if (WereMainGNFilesModified(inputs.source_files)) {
     outputs.status = "Found dependency (all)";
     if (inputs.compile_included_all) {
       outputs.compile_includes_all = true;
@@ -280,22 +291,45 @@
     return OutputsToJSON(outputs, default_toolchain_, err);
   }
 
-  TargetSet affected_targets = AllAffectedTargets(inputs.source_files);
+  std::set<const Item*> affected_items =
+      GetAllAffectedItems(inputs.source_files);
+  std::set<const Target*> affected_targets;
+  for (const Item* affected_item : affected_items) {
+    // Only handles targets in the default toolchain.
+    // TODO(crbug.com/667989): Expand analyzer to non-default toolchains when
+    // the bug is fixed.
+    if (affected_item->AsTarget() &&
+        affected_item->label().GetToolchainLabel() == default_toolchain_)
+      affected_targets.insert(affected_item->AsTarget());
+  }
+
   if (affected_targets.empty()) {
     outputs.status = "No dependency";
     return OutputsToJSON(outputs, default_toolchain_, err);
   }
 
-  TargetSet compile_targets = TargetsFor(inputs.compile_labels);
-  if (inputs.compile_included_all) {
-    for (auto* root : roots_)
-      compile_targets.insert(root);
+  std::set<const Target*> root_targets;
+  for (const auto* item : all_items_) {
+    if (item->AsTarget() && dep_map_.find(item) == dep_map_.end())
+      root_targets.insert(item->AsTarget());
   }
-  TargetSet filtered_targets = Filter(compile_targets);
+
+  std::set<const Target*> compile_targets = TargetsFor(inputs.compile_labels);
+  if (inputs.compile_included_all) {
+    for (auto* root_target : root_targets)
+      compile_targets.insert(root_target);
+  }
+  std::set<const Target*> filtered_targets = Filter(compile_targets);
   outputs.compile_labels =
       LabelsFor(Intersect(filtered_targets, affected_targets));
 
-  TargetSet test_targets = TargetsFor(inputs.test_labels);
+  // If every target is affected, simply compile All instead of listing all
+  // the targets to make the output easier to read.
+  if (inputs.compile_included_all &&
+      outputs.compile_labels.size() == filtered_targets.size())
+    outputs.compile_includes_all = true;
+
+  std::set<const Target*> test_targets = TargetsFor(inputs.test_labels);
   outputs.test_labels = LabelsFor(Intersect(test_targets, affected_targets));
 
   if (outputs.compile_labels.empty() && outputs.test_labels.empty())
@@ -305,47 +339,53 @@
   return OutputsToJSON(outputs, default_toolchain_, err);
 }
 
-TargetSet Analyzer::AllAffectedTargets(
-    const SourceFileSet& source_files) const {
-  TargetSet direct_matches;
+std::set<const Item*> Analyzer::GetAllAffectedItems(
+    const std::set<const SourceFile*>& source_files) const {
+  std::set<const Item*> directly_affected_items;
   for (auto* source_file : source_files)
-    AddTargetsDirectlyReferringToFileTo(source_file, &direct_matches);
-  TargetSet all_matches;
-  for (auto* match : direct_matches)
-    AddAllRefsTo(match, &all_matches);
-  return all_matches;
+    AddItemsDirectlyReferringToFile(source_file, &directly_affected_items);
+
+  std::set<const Item*> all_affected_items;
+  for (auto* affected_item : directly_affected_items)
+    AddAllItemsReferringToItem(affected_item, &all_affected_items);
+
+  return all_affected_items;
 }
 
-LabelSet Analyzer::InvalidLabels(const LabelSet& labels) const {
-  LabelSet invalid_labels;
+std::set<Label> Analyzer::InvalidLabels(const std::set<Label>& labels) const {
+  std::set<Label> invalid_labels;
   for (const Label& label : labels) {
-    if (labels_to_targets_.find(label) == labels_to_targets_.end())
+    if (labels_to_items_.find(label) == labels_to_items_.end())
       invalid_labels.insert(label);
   }
   return invalid_labels;
 }
 
-TargetSet Analyzer::TargetsFor(const LabelSet& labels) const {
-  TargetSet targets;
+std::set<const Target*> Analyzer::TargetsFor(
+    const std::set<Label>& labels) const {
+  std::set<const Target*> targets;
   for (const auto& label : labels) {
-    auto it = labels_to_targets_.find(label);
-    if (it != labels_to_targets_.end())
-      targets.insert(it->second);
+    auto it = labels_to_items_.find(label);
+    if (it != labels_to_items_.end()) {
+      DCHECK(it->second->AsTarget());
+      targets.insert(it->second->AsTarget());
+    }
   }
   return targets;
 }
 
-TargetSet Analyzer::Filter(const TargetSet& targets) const {
-  TargetSet seen;
-  TargetSet filtered;
+std::set<const Target*> Analyzer::Filter(
+    const std::set<const Target*>& targets) const {
+  std::set<const Target*> seen;
+  std::set<const Target*> filtered;
   for (const auto* target : targets)
     FilterTarget(target, &seen, &filtered);
   return filtered;
 }
 
 void Analyzer::FilterTarget(const Target* target,
-                            TargetSet* seen,
-                            TargetSet* filtered) const {
+                            std::set<const Target*>* seen,
+                            std::set<const Target*>* filtered) const {
   if (seen->find(target) == seen->end()) {
     seen->insert(target);
     if (target->output_type() != Target::GROUP) {
@@ -357,8 +397,17 @@
   }
 }
 
-bool Analyzer::TargetRefersToFile(const Target* target,
-                                  const SourceFile* file) const {
+bool Analyzer::ItemRefersToFile(const Item* item,
+                                const SourceFile* file) const {
+  for (const auto& cur_file : item->build_dependency_files()) {
+    if (cur_file == *file)
+      return true;
+  }
+
+  if (!item->AsTarget())
+    return false;
+
+  const Target* target = item->AsTarget();
   for (const auto& cur_file : target->sources()) {
     if (cur_file == *file)
       return true;
@@ -391,23 +440,44 @@
   return false;
 }
 
-void Analyzer::AddTargetsDirectlyReferringToFileTo(const SourceFile* file,
-                                                   TargetSet* matches) const {
-  for (auto* target : all_targets_) {
-    // Only handles targets in the default toolchain.
-    if ((target->label().GetToolchainLabel() == default_toolchain_) &&
-        TargetRefersToFile(target, file))
-      matches->insert(target);
+void Analyzer::AddItemsDirectlyReferringToFile(
+    const SourceFile* file,
+    std::set<const Item*>* directly_affected_items) const {
+  for (const auto* item : all_items_) {
+    if (ItemRefersToFile(item, file))
+      directly_affected_items->insert(item);
   }
 }
 
-void Analyzer::AddAllRefsTo(const Target* target, TargetSet* results) const {
-  if (results->find(target) != results->end())
-    return;  // Already found this target.
-  results->insert(target);
+void Analyzer::AddAllItemsReferringToItem(
+    const Item* item,
+    std::set<const Item*>* all_affected_items) const {
+  if (all_affected_items->find(item) != all_affected_items->end())
+    return;  // Already found this item.
 
-  auto dep_begin = dep_map_.lower_bound(target);
-  auto dep_end = dep_map_.upper_bound(target);
-  for (auto cur_dep = dep_begin; cur_dep != dep_end; cur_dep++)
-    AddAllRefsTo(cur_dep->second, results);
+  all_affected_items->insert(item);
+
+  auto dep_begin = dep_map_.lower_bound(item);
+  auto dep_end = dep_map_.upper_bound(item);
+  for (auto cur_dep = dep_begin; cur_dep != dep_end; ++cur_dep)
+    AddAllItemsReferringToItem(cur_dep->second, all_affected_items);
+}
+
+bool Analyzer::WereMainGNFilesModified(
+    const std::set<const SourceFile*>& modified_files) const {
+  for (const auto* file : modified_files) {
+    if (*file == dot_file_)
+      return true;
+
+    if (*file == build_config_file_)
+      return true;
+
+    for (const auto& build_args_dependency_file :
+         build_args_dependency_files_) {
+      if (*file == build_args_dependency_file)
+        return true;
+    }
+  }
+
+  return false;
 }
diff --git a/tools/gn/analyzer.h b/tools/gn/analyzer.h
index 130ebe9..01be0e4 100644
--- a/tools/gn/analyzer.h
+++ b/tools/gn/analyzer.h
@@ -10,9 +10,9 @@
 #include <vector>
 
 #include "tools/gn/builder.h"
+#include "tools/gn/item.h"
 #include "tools/gn/label.h"
 #include "tools/gn/source_file.h"
-#include "tools/gn/target.h"
 
 // An Analyzer can answer questions about a build graph. It is used
 // to answer queries for the `refs` and `analyze` commands, where we
@@ -20,11 +20,10 @@
 // from just a single Target.
 class Analyzer {
  public:
-  using LabelSet = std::set<Label>;
-  using SourceFileSet = std::set<const SourceFile*>;
-  using TargetSet = std::set<const Target*>;
-
-  explicit Analyzer(const Builder& builder);
+  Analyzer(const Builder& builder,
+           const SourceFile& build_config_file,
+           const SourceFile& dot_file,
+           const std::set<SourceFile>& build_args_dependency_files);
   ~Analyzer();
 
   // Figures out from a Buider and a JSON-formatted string containing lists
@@ -35,20 +34,17 @@
   std::string Analyze(const std::string& input, Err* err) const;
 
  private:
-  // Returns the roots of the build graph: the set of targets that
-  // no other target depends on.
-  TargetSet& roots() { return roots_; };
-
-  // Returns the set of all targets that might be affected, directly or
+  // Returns the set of all items that might be affected, directly or
   // indirectly, by modifications to the given source files.
-  TargetSet AllAffectedTargets(const SourceFileSet& source_files) const;
+  std::set<const Item*> GetAllAffectedItems(
+      const std::set<const SourceFile*>& source_files) const;
 
   // Returns the set of labels that do not refer to objects in the graph.
-  LabelSet InvalidLabels(const LabelSet& labels) const;
+  std::set<Label> InvalidLabels(const std::set<Label>& labels) const;
 
   // Returns the set of all targets that have a label in the given set.
   // Invalid (or missing) labels will be ignored.
-  TargetSet TargetsFor(const LabelSet& labels) const;
+  std::set<const Target*> TargetsFor(const std::set<Label>& labels) const;
 
   // Returns a filtered set of the given targets, meaning that for each of the
   // given targets,
@@ -70,26 +66,39 @@
   // ones).
   //
   // This filtering behavior is also known as "pruning" the list of targets.
-  TargetSet Filter(const TargetSet& targets) const;
+  std::set<const Target*> Filter(const std::set<const Target*>& targets) const;
 
   // Filter an individual target and adds the results to filtered
   // (see Filter(), above).
-  void FilterTarget(const Target*, TargetSet* seen, TargetSet* filtered) const;
+  void FilterTarget(const Target*,
+                    std::set<const Target*>* seen,
+                    std::set<const Target*>* filtered) const;
 
-  bool TargetRefersToFile(const Target* target, const SourceFile* file) const;
+  bool ItemRefersToFile(const Item* item, const SourceFile* file) const;
 
-  void AddTargetsDirectlyReferringToFileTo(const SourceFile* file,
-                                           TargetSet* matches) const;
+  void AddItemsDirectlyReferringToFile(
+      const SourceFile* file,
+      std::set<const Item*>* affected_items) const;
 
-  void AddAllRefsTo(const Target* target, TargetSet* matches) const;
+  void AddAllItemsReferringToItem(const Item* item,
+                                  std::set<const Item*>* affected_items) const;
 
-  std::vector<const Target*> all_targets_;
-  std::map<const Label, const Target*> labels_to_targets_;
+  // Main GN files stand for files whose context are used globally to execute
+  // every other build files, this list includes dot file, build config file,
+  // build args files etc.
+  bool WereMainGNFilesModified(
+      const std::set<const SourceFile*>& modified_files) const;
+
+  std::vector<const Item*> all_items_;
+  std::map<Label, const Item*> labels_to_items_;
   Label default_toolchain_;
-  std::set<const Target*> roots_;
 
-  // Maps targets to the list of targets that depend on them.
-  std::multimap<const Target*, const Target*> dep_map_;
+  // Maps items to the list of items that depend on them.
+  std::multimap<const Item*, const Item*> dep_map_;
+
+  const SourceFile build_config_file_;
+  const SourceFile dot_file_;
+  const std::set<SourceFile> build_args_dependency_files_;
 };
 
 #endif  // TOOLS_GN_ANALYZER_H_
diff --git a/tools/gn/analyzer_unittest.cc b/tools/gn/analyzer_unittest.cc
index a15c40e..5c299d1 100644
--- a/tools/gn/analyzer_unittest.cc
+++ b/tools/gn/analyzer_unittest.cc
@@ -6,9 +6,15 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "tools/gn/build_settings.h"
 #include "tools/gn/builder.h"
+#include "tools/gn/config.h"
 #include "tools/gn/loader.h"
+#include "tools/gn/pool.h"
 #include "tools/gn/settings.h"
 #include "tools/gn/source_file.h"
+#include "tools/gn/substitution_list.h"
+#include "tools/gn/target.h"
+#include "tools/gn/tool.h"
+#include "tools/gn/toolchain.h"
 
 namespace gn_analyzer_unittest {
 
@@ -44,60 +50,38 @@
     tc_name_ = settings_.toolchain_label().name();
   }
 
-  Target* MakeTarget(const std::string dir,
-                     const std::string name,
-                     Target::OutputType type,
-                     const std::vector<std::string>& sources,
-                     const std::vector<Target*>& deps) {
-    Label lbl(SourceDir(dir), name, tc_dir_, tc_name_);
-    Target* target = new Target(&settings_, lbl);
-    target->set_output_type(type);
-    for (const auto& s : sources)
-      target->sources().push_back(SourceFile(s));
-    for (const auto* d : deps)
-      target->public_deps().push_back(LabelTargetPair(d));
-    builder_.ItemDefined(std::unique_ptr<Item>(target));
+  // Ownership of the target will be transfered to the builder, so no leaks.
+  Target* MakeTarget(const std::string& dir, const std::string& name) {
+    Label label(SourceDir(dir), name, tc_dir_, tc_name_);
+    Target* target = new Target(&settings_, label);
+
     return target;
   }
 
-  void AddSource(Target* a, std::string path) {}
+  // Ownership of the config will be transfered to the builder, so no leaks.
+  Config* MakeConfig(const std::string& dir, const std::string& name) {
+    Label label(SourceDir(dir), name, tc_dir_, tc_name_);
+    Config* config = new Config(&settings_, label);
 
-  void AddDep(Target* a, Target* b) {}
-
-  void SetUpABasicBuildGraph() {
-    std::vector<std::string> no_sources;
-    std::vector<Target*> no_deps;
-
-    // All of the targets below are owned by the builder, so none of them
-    // get leaked.
-
-    // Ignore the returned target since nothing depends on it.
-    MakeTarget("//", "a", Target::EXECUTABLE, {"//a.cc"}, no_deps);
-
-    Target* b =
-        MakeTarget("//d", "b", Target::SOURCE_SET, {"//d/b.cc"}, no_deps);
-
-    Target* b_unittests = MakeTarget("//d", "b_unittests", Target::EXECUTABLE,
-                                     {"//d/b_unittest.cc"}, {b});
-
-    Target* c = MakeTarget("//d", "c", Target::EXECUTABLE, {"//d/c.cc"}, {b});
-
-    Target* b_unittests_and_c =
-        MakeTarget("//d", "b_unittests_and_c", Target::GROUP, no_sources,
-                   {b_unittests, c});
-
-    Target* e =
-        MakeTarget("//d", "e", Target::EXECUTABLE, {"//d/e.cc"}, no_deps);
-
-    // Also ignore this returned target since nothing depends on it.
-    MakeTarget("//d", "d", Target::GROUP, no_sources, {b_unittests_and_c, e});
+    return config;
   }
 
-  void RunBasicTest(const std::string& input,
-                    const std::string& expected_output) {
-    SetUpABasicBuildGraph();
+  // Ownership of the pool will be transfered to the builder, so no leaks.
+  Pool* MakePool(const std::string& dir, const std::string& name) {
+    Label label(SourceDir(dir), name, tc_dir_, tc_name_);
+    Pool* pool = new Pool(&settings_, label);
+
+    return pool;
+  }
+
+  void RunAnalyzerTest(const std::string& input,
+                       const std::string& expected_output) {
+    Analyzer analyzer(builder_, SourceFile("//build/config/BUILDCONFIG.gn"),
+                      SourceFile("//.gn"),
+                      {SourceFile("//out/debug/args.gn"),
+                       SourceFile("//build/default_args.gn")});
     Err err;
-    std::string actual_output = Analyzer(builder_).Analyze(input, &err);
+    std::string actual_output = analyzer.Analyze(input, &err);
     EXPECT_EQ(err.has_error(), false);
     EXPECT_EQ(expected_output, actual_output);
   }
@@ -111,40 +95,387 @@
   std::string tc_name_;
 };
 
-TEST_F(AnalyzerTest, AllWasPruned) {
-  RunBasicTest(
+// Tests that a target is marked as affected if its sources are modified.
+TEST_F(AnalyzerTest, TargetRefersToSources) {
+  Target* t = MakeTarget("//dir", "target_name");
+  builder_.ItemDefined(std::unique_ptr<Item>(t));
+  RunAnalyzerTest(
       R"({
-        "files": [ "//d/b.cc" ],
-        "additional_compile_targets": [ "all" ],
-        "test_targets": [ ]
-      })",
+       "files": [ "//dir/file_name.cc" ],
+       "additional_compile_targets": [ "all" ],
+       "test_targets": [ "//dir:target_name" ]
+       })",
       "{"
-      R"("compile_targets":["//d:b_unittests","//d:c"],)"
-      R"("status":"Found dependency",)"
+      R"("compile_targets":[],)"
+      R"/("status":"No dependency",)/"
+      R"("test_targets":[])"
+      "}");
+
+  t->sources().push_back(SourceFile("//dir/file_name.cc"));
+  RunAnalyzerTest(
+      R"({
+       "files": [ "//dir/file_name.cc" ],
+       "additional_compile_targets": [ "all" ],
+       "test_targets": [ "//dir:target_name" ]
+       })",
+      "{"
+      R"("compile_targets":["all"],)"
+      R"/("status":"Found dependency",)/"
+      R"("test_targets":["//dir:target_name"])"
+      "}");
+}
+
+// Tests that a target is marked as affected if its public headers are modified.
+TEST_F(AnalyzerTest, TargetRefersToPublicHeaders) {
+  Target* t = MakeTarget("//dir", "target_name");
+  builder_.ItemDefined(std::unique_ptr<Item>(t));
+  RunAnalyzerTest(
+      R"({
+       "files": [ "//dir/header_name.h" ],
+       "additional_compile_targets": [ "all" ],
+       "test_targets": [ "//dir:target_name" ]
+       })",
+      "{"
+      R"("compile_targets":[],)"
+      R"/("status":"No dependency",)/"
+      R"("test_targets":[])"
+      "}");
+
+  t->public_headers().push_back(SourceFile("//dir/header_name.h"));
+  RunAnalyzerTest(
+      R"({
+       "files": [ "//dir/header_name.h" ],
+       "additional_compile_targets": [ "all" ],
+       "test_targets": [ "//dir:target_name" ]
+       })",
+      "{"
+      R"("compile_targets":["all"],)"
+      R"/("status":"Found dependency",)/"
+      R"("test_targets":["//dir:target_name"])"
+      "}");
+}
+
+// Tests that a target is marked as affected if its inputs are modified.
+TEST_F(AnalyzerTest, TargetRefersToInputs) {
+  Target* t = MakeTarget("//dir", "target_name");
+  builder_.ItemDefined(std::unique_ptr<Item>(t));
+  RunAnalyzerTest(
+      R"({
+       "files": [ "//dir/extra_input.cc" ],
+       "additional_compile_targets": [ "all" ],
+       "test_targets": [ "//dir:target_name" ]
+       })",
+      "{"
+      R"("compile_targets":[],)"
+      R"/("status":"No dependency",)/"
+      R"("test_targets":[])"
+      "}");
+
+  t->inputs().push_back(SourceFile("//dir/extra_input.cc"));
+  RunAnalyzerTest(
+      R"({
+       "files": [ "//dir/extra_input.cc" ],
+       "additional_compile_targets": [ "all" ],
+       "test_targets": [ "//dir:target_name" ]
+       })",
+      "{"
+      R"("compile_targets":["all"],)"
+      R"/("status":"Found dependency",)/"
+      R"("test_targets":["//dir:target_name"])"
+      "}");
+}
+
+// Tests that a target is marked as affected if its data are modified.
+TEST_F(AnalyzerTest, TargetRefersToData) {
+  Target* t = MakeTarget("//dir", "target_name");
+  builder_.ItemDefined(std::unique_ptr<Item>(t));
+  RunAnalyzerTest(
+      R"({
+       "files": [ "//dir/data.html" ],
+       "additional_compile_targets": [ "all" ],
+       "test_targets": [ "//dir:target_name" ]
+       })",
+      "{"
+      R"("compile_targets":[],)"
+      R"/("status":"No dependency",)/"
+      R"("test_targets":[])"
+      "}");
+
+  t->data().push_back("//dir/data.html");
+  RunAnalyzerTest(
+      R"({
+       "files": [ "//dir/data.html" ],
+       "additional_compile_targets": [ "all" ],
+       "test_targets": [ "//dir:target_name" ]
+       })",
+      "{"
+      R"("compile_targets":["all"],)"
+      R"/("status":"Found dependency",)/"
+      R"("test_targets":["//dir:target_name"])"
+      "}");
+}
+
+// Tests that a target is marked as affected if the target is an action and its
+// action script is modified.
+TEST_F(AnalyzerTest, TargetRefersToActionScript) {
+  Target* t = MakeTarget("//dir", "target_name");
+  builder_.ItemDefined(std::unique_ptr<Item>(t));
+  t->set_output_type(Target::ACTION);
+  RunAnalyzerTest(
+      R"({
+       "files": [ "//dir/script.py" ],
+       "additional_compile_targets": [ "all" ],
+       "test_targets": [ "//dir:target_name" ]
+       })",
+      "{"
+      R"("compile_targets":[],)"
+      R"/("status":"No dependency",)/"
+      R"("test_targets":[])"
+      "}");
+
+  t->action_values().set_script(SourceFile("//dir/script.py"));
+  RunAnalyzerTest(
+      R"({
+       "files": [ "//dir/script.py" ],
+       "additional_compile_targets": [ "all" ],
+       "test_targets": [ "//dir:target_name" ]
+       })",
+      "{"
+      R"("compile_targets":["all"],)"
+      R"/("status":"Found dependency",)/"
+      R"("test_targets":["//dir:target_name"])"
+      "}");
+}
+
+// Tests that a target is marked as affected if its build dependency files are
+// modified.
+TEST_F(AnalyzerTest, TargetRefersToBuildDependencyFiles) {
+  Target* t = MakeTarget("//dir", "target_name");
+  builder_.ItemDefined(std::unique_ptr<Item>(t));
+  RunAnalyzerTest(
+      R"({
+       "files": [ "//dir/BUILD.gn" ],
+       "additional_compile_targets": [ "all" ],
+       "test_targets": [ "//dir:target_name" ]
+       })",
+      "{"
+      R"("compile_targets":[],)"
+      R"/("status":"No dependency",)/"
+      R"("test_targets":[])"
+      "}");
+
+  t->build_dependency_files().insert(SourceFile("//dir/BUILD.gn"));
+  RunAnalyzerTest(
+      R"({
+       "files": [ "//dir/BUILD.gn" ],
+       "additional_compile_targets": [ "all" ],
+       "test_targets": [ "//dir:target_name" ]
+       })",
+      "{"
+      R"("compile_targets":["all"],)"
+      R"/("status":"Found dependency",)/"
+      R"("test_targets":["//dir:target_name"])"
+      "}");
+}
+
+// Tests that if a target is marked as affected, then it propagates to dependent
+// test_targets.
+TEST_F(AnalyzerTest, AffectedTargetpropagatesToDependentTargets) {
+  Target* t1 = MakeTarget("//dir", "target_name1");
+  Target* t2 = MakeTarget("//dir", "target_name2");
+  Target* t3 = MakeTarget("//dir", "target_name3");
+  t1->private_deps().push_back(LabelTargetPair(t2));
+  t2->private_deps().push_back(LabelTargetPair(t3));
+  builder_.ItemDefined(std::unique_ptr<Item>(t1));
+  builder_.ItemDefined(std::unique_ptr<Item>(t2));
+  builder_.ItemDefined(std::unique_ptr<Item>(t3));
+
+  RunAnalyzerTest(
+      R"({
+       "files": [ "//dir/BUILD.gn" ],
+       "additional_compile_targets": [ "all" ],
+       "test_targets": [ "//dir:target_name1", "//dir:target_name2" ]
+       })",
+      "{"
+      R"("compile_targets":[],)"
+      R"/("status":"No dependency",)/"
+      R"("test_targets":[])"
+      "}");
+
+  t3->build_dependency_files().insert(SourceFile("//dir/BUILD.gn"));
+  RunAnalyzerTest(
+      R"({
+       "files": [ "//dir/BUILD.gn" ],
+       "additional_compile_targets": [ "all" ],
+       "test_targets": [ "//dir:target_name1", "//dir:target_name2" ]
+       })",
+      "{"
+      R"("compile_targets":["all"],)"
+      R"/("status":"Found dependency",)/"
+      R"("test_targets":["//dir:target_name1","//dir:target_name2"])"
+      "}");
+}
+
+// Tests that if a config is marked as affected, then it propagates to dependent
+// test_targets.
+TEST_F(AnalyzerTest, AffectedConfigpropagatesToDependentTargets) {
+  Config* c = MakeConfig("//dir", "config_name");
+  Target* t = MakeTarget("//dir", "target_name");
+  t->configs().push_back(LabelConfigPair(c));
+  builder_.ItemDefined(std::unique_ptr<Item>(t));
+  builder_.ItemDefined(std::unique_ptr<Item>(c));
+  RunAnalyzerTest(
+      R"({
+       "files": [ "//dir/BUILD.gn" ],
+       "additional_compile_targets": [ "all" ],
+       "test_targets": [ "//dir:target_name" ]
+       })",
+      "{"
+      R"("compile_targets":[],)"
+      R"/("status":"No dependency",)/"
+      R"("test_targets":[])"
+      "}");
+
+  c->build_dependency_files().insert(SourceFile("//dir/BUILD.gn"));
+  RunAnalyzerTest(
+      R"({
+       "files": [ "//dir/BUILD.gn" ],
+       "additional_compile_targets": [ "all" ],
+       "test_targets": [ "//dir:target_name" ]
+       })",
+      "{"
+      R"("compile_targets":["all"],)"
+      R"/("status":"Found dependency",)/"
+      R"("test_targets":["//dir:target_name"])"
+      "}");
+}
+
+// Tests that if toolchain is marked as affected, then it propagates to
+// dependent test_targets.
+TEST_F(AnalyzerTest, AffectedToolchainpropagatesToDependentTargets) {
+  Target* target = MakeTarget("//dir", "target_name");
+  target->set_output_type(Target::EXECUTABLE);
+  Toolchain* toolchain = new Toolchain(&settings_, settings_.toolchain_label());
+
+  // The tool is not used anywhere, but is required to construct the dependency
+  // between a target and the toolchain.
+  std::unique_ptr<Tool> fake_tool(new Tool());
+  fake_tool->set_outputs(
+      SubstitutionList::MakeForTest("//out/debug/output.txt"));
+  toolchain->SetTool(Toolchain::TYPE_LINK, std::move(fake_tool));
+  builder_.ItemDefined(std::unique_ptr<Item>(target));
+  builder_.ItemDefined(std::unique_ptr<Item>(toolchain));
+
+  RunAnalyzerTest(
+      R"({
+         "files": [ "//tc/BUILD.gn" ],
+         "additional_compile_targets": [ "all" ],
+         "test_targets": [ "//dir:target_name" ]
+         })",
+      "{"
+      R"("compile_targets":[],)"
+      R"/("status":"No dependency",)/"
+      R"("test_targets":[])"
+      "}");
+
+  toolchain->build_dependency_files().insert(SourceFile("//tc/BUILD.gn"));
+  RunAnalyzerTest(
+      R"({
+       "files": [ "//tc/BUILD.gn" ],
+       "additional_compile_targets": [ "all" ],
+       "test_targets": [ "//dir:target_name" ]
+       })",
+      "{"
+      R"("compile_targets":["all"],)"
+      R"/("status":"Found dependency",)/"
+      R"("test_targets":["//dir:target_name"])"
+      "}");
+}
+
+// Tests that if a pool is marked as affected, then it propagates to dependent
+// targets.
+TEST_F(AnalyzerTest, AffectedPoolpropagatesToDependentTargets) {
+  Target* t = MakeTarget("//dir", "target_name");
+  t->set_output_type(Target::ACTION);
+  Pool* p = MakePool("//dir", "pool_name");
+  t->action_values().set_pool(LabelPtrPair<Pool>(p));
+
+  builder_.ItemDefined(std::unique_ptr<Item>(t));
+  builder_.ItemDefined(std::unique_ptr<Item>(p));
+
+  RunAnalyzerTest(
+      R"({
+       "files": [ "//dir/BUILD.gn" ],
+       "additional_compile_targets": [ "all" ],
+       "test_targets": [ "//dir:target_name" ]
+       })",
+      "{"
+      R"("compile_targets":[],)"
+      R"/("status":"No dependency",)/"
+      R"("test_targets":[])"
+      "}");
+
+  p->build_dependency_files().insert(SourceFile("//dir/BUILD.gn"));
+  RunAnalyzerTest(
+      R"({
+       "files": [ "//dir/BUILD.gn" ],
+       "additional_compile_targets": [ "all" ],
+       "test_targets": [ "//dir:target_name" ]
+       })",
+      "{"
+      R"("compile_targets":["all"],)"
+      R"/("status":"Found dependency",)/"
+      R"("test_targets":["//dir:target_name"])"
+      "}");
+}
+
+// Tests that when dependency was found, the "compile_targets" in the output is
+// not "all".
+TEST_F(AnalyzerTest, CompileTargetsAllWasPruned) {
+  Target* t1 = MakeTarget("//dir", "target_name1");
+  Target* t2 = MakeTarget("//dir", "target_name2");
+  builder_.ItemDefined(std::unique_ptr<Item>(t1));
+  builder_.ItemDefined(std::unique_ptr<Item>(t2));
+  t2->build_dependency_files().insert(SourceFile("//dir/BUILD.gn"));
+
+  RunAnalyzerTest(
+      R"({
+       "files": [ "//dir/BUILD.gn" ],
+       "additional_compile_targets": [ "all" ],
+       "test_targets": []
+       })",
+      "{"
+      R"("compile_targets":["//dir:target_name2"],)"
+      R"/("status":"Found dependency",)/"
       R"("test_targets":[])"
       "}");
 }
 
+// Tests that output is "No dependency" when no dependency is found.
 TEST_F(AnalyzerTest, NoDependency) {
-  RunBasicTest(
+  Target* t = MakeTarget("//dir", "target_name");
+  builder_.ItemDefined(std::unique_ptr<Item>(t));
+
+  RunAnalyzerTest(
       R"({
-        "files":[ "//missing.cc" ],
-        "additional_compile_targets": [ "all" ],
-        "test_targets": [ "//:a" ]
-      })",
+       "files": [ "//dir/BUILD.gn" ],
+       "additional_compile_targets": [ "all" ],
+       "test_targets": []
+       })",
       "{"
       R"("compile_targets":[],)"
-      R"("status":"No dependency",)"
+      R"/("status":"No dependency",)/"
       R"("test_targets":[])"
       "}");
 }
 
+// Tests that output is "No dependency" when no files or targets are provided.
 TEST_F(AnalyzerTest, NoFilesNoTargets) {
-  RunBasicTest(
+  RunAnalyzerTest(
       R"({
-        "files": [],
-        "additional_compile_targets": [],
-        "test_targets": []
+       "files": [],
+       "additional_compile_targets": [],
+       "test_targets": []
       })",
       "{"
       R"("compile_targets":[],)"
@@ -153,26 +484,14 @@
       "}");
 }
 
-TEST_F(AnalyzerTest, OneTestTargetModified) {
-  RunBasicTest(
-      R"({
-        "files": [ "//a.cc" ],
-        "additional_compile_targets": [],
-        "test_targets": [ "//:a" ]
-      })",
-      "{"
-      R"("compile_targets":[],)"
-      R"("status":"Found dependency",)"
-      R"("test_targets":["//:a"])"
-      "}");
-}
-
+// Tests that output displays proper error message when given files aren't
+// source-absolute or absolute path.
 TEST_F(AnalyzerTest, FilesArentSourceAbsolute) {
-  RunBasicTest(
+  RunAnalyzerTest(
       R"({
-        "files": [ "a.cc" ],
-        "additional_compile_targets": [],
-        "test_targets": [ "//:a" ]
+       "files": [ "a.cc" ],
+       "additional_compile_targets": [],
+       "test_targets": [ "//dir:target_name" ]
       })",
       "{"
       R"("error":)"
@@ -181,12 +500,13 @@
       "}");
 }
 
+// Tests that output displays proper error message when input is illy-formed.
 TEST_F(AnalyzerTest, WrongInputFields) {
-  RunBasicTest(
+  RunAnalyzerTest(
       R"({
-        "files": [ "//a.cc" ],
-        "compile_targets": [],
-        "test_targets": [ "//:a" ]
+       "files": [ "//a.cc" ],
+       "compile_targets": [],
+       "test_targets": [ "//dir:target_name" ]
       })",
       "{"
       R"("error":)"
@@ -196,37 +516,59 @@
       "}");
 }
 
-TEST_F(AnalyzerTest, BuildFilesWereModified) {
-  // This tests that if a build file is modified, we bail out early with
-  // "Found dependency (all)" error since we can't handle changes to
-  // build files yet (crbug.com/555273).
-  RunBasicTest(
+// Bails out early with "Found dependency (all)" if dot file is modified.
+TEST_F(AnalyzerTest, DotFileWasModified) {
+  Target* t = MakeTarget("//dir", "target_name");
+  builder_.ItemDefined(std::unique_ptr<Item>(t));
+
+  RunAnalyzerTest(
       R"({
-        "files": [ "//a.cc", "//BUILD.gn" ],
-        "additional_compile_targets": [],
-        "test_targets": [ "//:a" ]
+       "files": [ "//.gn" ],
+       "additional_compile_targets": [],
+       "test_targets": [ "//dir:target_name" ]
       })",
       "{"
-      R"("compile_targets":["//:a"],)"
+      R"("compile_targets":["//dir:target_name"],)"
       R"/("status":"Found dependency (all)",)/"
-      R"("test_targets":["//:a"])"
+      R"("test_targets":["//dir:target_name"])"
       "}");
 }
 
-TEST_F(AnalyzerTest, BuildFilesWereModifiedAndCompilingAll) {
-  // This tests that if a build file is modified, we bail out early with
-  // "Found dependency (all)" error since we can't handle changes to
-  // build files yet (crbug.com/555273).
-  RunBasicTest(
+// Bails out early with "Found dependency (all)" if master build config file is
+// modified.
+TEST_F(AnalyzerTest, BuildConfigFileWasModified) {
+  Target* t = MakeTarget("//dir", "target_name");
+  builder_.ItemDefined(std::unique_ptr<Item>(t));
+
+  RunAnalyzerTest(
       R"({
-        "files": [ "//a.cc", "//BUILD.gn" ],
-        "additional_compile_targets": [ "all" ],
-        "test_targets": [ "//:a" ]
+       "files": [ "//build/config/BUILDCONFIG.gn" ],
+       "additional_compile_targets": [],
+       "test_targets": [ "//dir:target_name" ]
       })",
       "{"
-      R"("compile_targets":["all"],)"
+      R"("compile_targets":["//dir:target_name"],)"
       R"/("status":"Found dependency (all)",)/"
-      R"("test_targets":["//:a"])"
+      R"("test_targets":["//dir:target_name"])"
+      "}");
+}
+
+// Bails out early with "Found dependency (all)" if a build args dependency file
+// is modified.
+TEST_F(AnalyzerTest, BuildArgsDependencyFileWasModified) {
+  Target* t = MakeTarget("//dir", "target_name");
+  builder_.ItemDefined(std::unique_ptr<Item>(t));
+
+  RunAnalyzerTest(
+      R"({
+       "files": [ "//build/default_args.gn" ],
+       "additional_compile_targets": [],
+       "test_targets": [ "//dir:target_name" ]
+      })",
+      "{"
+      R"("compile_targets":["//dir:target_name"],)"
+      R"/("status":"Found dependency (all)",)/"
+      R"("test_targets":["//dir:target_name"])"
       "}");
 }
 
diff --git a/tools/gn/args.cc b/tools/gn/args.cc
index 4a0c137..a53eb49 100644
--- a/tools/gn/args.cc
+++ b/tools/gn/args.cc
@@ -6,6 +6,7 @@
 
 #include "base/sys_info.h"
 #include "build/build_config.h"
+#include "tools/gn/source_file.h"
 #include "tools/gn/string_utils.h"
 #include "tools/gn/variables.h"
 
diff --git a/tools/gn/args.h b/tools/gn/args.h
index 2ae4bc1..cbac794 100644
--- a/tools/gn/args.h
+++ b/tools/gn/args.h
@@ -6,6 +6,7 @@
 #define TOOLS_GN_ARGS_H_
 
 #include <map>
+#include <set>
 
 #include "base/containers/hash_tables.h"
 #include "base/macros.h"
@@ -13,6 +14,7 @@
 #include "tools/gn/scope.h"
 
 class Err;
+class SourceFile;
 
 extern const char kBuildArgs_Help[];
 
@@ -82,6 +84,17 @@
   // map instead of a hash map so the arguements are sorted alphabetically.
   ValueWithOverrideMap GetAllArguments() const;
 
+  // Returns the set of build files that may affect the build arguments, please
+  // refer to Scope for how this is determined.
+  const std::set<SourceFile>& build_args_dependency_files() const {
+    return build_args_dependency_files_;
+  }
+
+  void set_build_args_dependency_files(
+      const std::set<SourceFile>& build_args_dependency_files) {
+    build_args_dependency_files_ = build_args_dependency_files;
+  }
+
  private:
   using ArgumentsPerToolchain =
       base::hash_map<const Settings*, Scope::KeyValueMap>;
@@ -126,6 +139,8 @@
   // we see an argument declaration.
   mutable ArgumentsPerToolchain toolchain_overrides_;
 
+  std::set<SourceFile> build_args_dependency_files_;
+
   DISALLOW_ASSIGN(Args);
 };
 
diff --git a/tools/gn/builder.cc b/tools/gn/builder.cc
index c81cc79..4b464af 100644
--- a/tools/gn/builder.cc
+++ b/tools/gn/builder.cc
@@ -138,6 +138,19 @@
   return result;
 }
 
+std::vector<const Item*> Builder::GetAllResolvedItems() const {
+  std::vector<const Item*> result;
+  result.reserve(records_.size());
+  for (const auto& record : records_) {
+    if (record.second->type() != BuilderRecord::ITEM_UNKNOWN &&
+        record.second->should_generate() && record.second->item()) {
+      result.push_back(record.second->item());
+    }
+  }
+
+  return result;
+}
+
 std::vector<const Target*> Builder::GetAllResolvedTargets() const {
   std::vector<const Target*> result;
   result.reserve(records_.size());
diff --git a/tools/gn/builder.h b/tools/gn/builder.h
index d46bbd1..27a1fb3 100644
--- a/tools/gn/builder.h
+++ b/tools/gn/builder.h
@@ -45,6 +45,9 @@
 
   std::vector<const BuilderRecord*> GetAllRecords() const;
 
+  // Returns items which should be generated and which are defined.
+  std::vector<const Item*> GetAllResolvedItems() const;
+
   // Returns targets which should be generated and which are defined.
   std::vector<const Target*> GetAllResolvedTargets() const;
 
diff --git a/tools/gn/command_analyze.cc b/tools/gn/command_analyze.cc
index 6fd5823..9aa698b 100644
--- a/tools/gn/command_analyze.cc
+++ b/tools/gn/command_analyze.cc
@@ -110,10 +110,12 @@
   if (!setup->DoSetup(args[0], false) || !setup->Run())
     return 1;
 
-  Analyzer analyzer(setup->builder());
-
   Err err;
-  std::string output = Analyzer(setup->builder()).Analyze(input, &err);
+  Analyzer analyzer(
+      setup->builder(), setup->build_settings().build_config_file(),
+      setup->GetDotFile(),
+      setup->build_settings().build_args().build_args_dependency_files());
+  std::string output = analyzer.Analyze(input, &err);
   if (err.has_error()) {
     err.PrintToStdout();
     return 1;
diff --git a/tools/gn/item.h b/tools/gn/item.h
index 9195b5e..f2e7c06 100644
--- a/tools/gn/item.h
+++ b/tools/gn/item.h
@@ -61,6 +61,10 @@
     return build_dependency_files_;
   }
 
+  std::set<SourceFile>& build_dependency_files() {
+    return build_dependency_files_;
+  }
+
   // Called when this item is resolved, meaning it and all of its dependents
   // have no unresolved deps. Returns true on success. Sets the error and
   // returns false on failure.
@@ -69,7 +73,7 @@
  private:
   const Settings* settings_;
   Label label_;
-  const std::set<SourceFile> build_dependency_files_;
+  std::set<SourceFile> build_dependency_files_;
   const ParseNode* defined_from_;
 
   Visibility visibility_;
diff --git a/tools/gn/setup.cc b/tools/gn/setup.cc
index 9bb0b71..36b6ec5 100644
--- a/tools/gn/setup.cc
+++ b/tools/gn/setup.cc
@@ -488,6 +488,8 @@
   Scope::KeyValueMap overrides;
   arg_scope.GetCurrentScopeValues(&overrides);
   build_settings_.build_args().AddArgOverrides(overrides);
+  build_settings_.build_args().set_build_args_dependency_files(
+      arg_scope.build_dependency_files());
   return true;
 }
 
diff --git a/tools/gn/setup.h b/tools/gn/setup.h
index 68a8f0b..68f86c7 100644
--- a/tools/gn/setup.h
+++ b/tools/gn/setup.h
@@ -85,6 +85,8 @@
   Builder& builder() { return builder_; }
   LoaderImpl* loader() { return loader_.get(); }
 
+  const SourceFile& GetDotFile() const { return dotfile_input_file_->name(); }
+
   // Name of the file in the root build directory that contains the build
   // arguements.
   static const char kBuildArgFileName[];
diff --git a/tools/gn/source_file.h b/tools/gn/source_file.h
index 87fd590..06bbd11 100644
--- a/tools/gn/source_file.h
+++ b/tools/gn/source_file.h
@@ -27,6 +27,7 @@
 
   // Takes a known absolute source file. Always begins in a slash.
   explicit SourceFile(const base::StringPiece& p);
+  SourceFile(const SourceFile& other) = default;
 
   // Constructs from the given string by swapping in the contents of the given
   // value. The value will be the empty string after this call.