[GN] Implement tracking build dependency files for Items.

This CL implements tracking build dependency files for targets,
configs, toolchains and pools and write corresponding unit tests
to verify the behaviors.

Bug: 795913
Change-Id: I3f7a9302db0725ef8167160838448e7eb39c4eee
Reviewed-on: https://chromium-review.googlesource.com/838220
Commit-Queue: Yuke Liao <liaoyuke@chromium.org>
Reviewed-by: Dirk Pranke <dpranke@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#526037}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: 3cf32744d21a6f64e491c167cb995e72fe0d67bc
diff --git a/tools/gn/config.cc b/tools/gn/config.cc
index 953e677..e021fe4 100644
--- a/tools/gn/config.cc
+++ b/tools/gn/config.cc
@@ -8,8 +8,10 @@
 #include "tools/gn/input_file_manager.h"
 #include "tools/gn/scheduler.h"
 
-Config::Config(const Settings* settings, const Label& label)
-    : Item(settings, label), resolved_(false) {}
+Config::Config(const Settings* settings,
+               const Label& label,
+               const std::set<SourceFile>& build_dependency_files)
+    : Item(settings, label, build_dependency_files), resolved_(false) {}
 
 Config::~Config() = default;
 
diff --git a/tools/gn/config.h b/tools/gn/config.h
index 20cfe7e..ad04973 100644
--- a/tools/gn/config.h
+++ b/tools/gn/config.h
@@ -5,6 +5,8 @@
 #ifndef TOOLS_GN_CONFIG_H_
 #define TOOLS_GN_CONFIG_H_
 
+#include <set>
+
 #include "base/logging.h"
 #include "base/macros.h"
 #include "tools/gn/config_values.h"
@@ -21,7 +23,11 @@
 // flags.
 class Config : public Item {
  public:
-  Config(const Settings* settings, const Label& label);
+  // We track the set of build files that may affect this config, please refer
+  // to Scope for how this is determined.
+  Config(const Settings* settings,
+         const Label& label,
+         const std::set<SourceFile>& build_dependency_files = {});
   ~Config() override;
 
   // Item implementation.
diff --git a/tools/gn/function_toolchain.cc b/tools/gn/function_toolchain.cc
index 9fcf1f6..230f77f 100644
--- a/tools/gn/function_toolchain.cc
+++ b/tools/gn/function_toolchain.cc
@@ -454,8 +454,8 @@
 
   // This object will actually be copied into the one owned by the toolchain
   // manager, but that has to be done in the lock.
-  std::unique_ptr<Toolchain> toolchain =
-      std::make_unique<Toolchain>(scope->settings(), label);
+  std::unique_ptr<Toolchain> toolchain = std::make_unique<Toolchain>(
+      scope->settings(), label, scope->build_dependency_files());
   toolchain->set_defined_from(function);
   toolchain->visibility().SetPublic();
 
diff --git a/tools/gn/functions.cc b/tools/gn/functions.cc
index 60a6eb7..38b3020 100644
--- a/tools/gn/functions.cc
+++ b/tools/gn/functions.cc
@@ -338,8 +338,8 @@
     g_scheduler->Log("Defining config", label.GetUserVisibleName(true));
 
   // Create the new config.
-  std::unique_ptr<Config> config =
-      std::make_unique<Config>(scope->settings(), label);
+  std::unique_ptr<Config> config = std::make_unique<Config>(
+      scope->settings(), label, scope->build_dependency_files());
   config->set_defined_from(function);
   if (!Visibility::FillItemVisibility(config.get(), scope, err))
     return Value();
@@ -633,6 +633,7 @@
   SourceFile import_file =
       input_dir.ResolveRelativeFile(args[0], err,
           scope->settings()->build_settings()->root_path_utf8());
+  scope->AddBuildDependencyFile(import_file);
   if (!err->has_error()) {
     scope->settings()->import_manager().DoImport(import_file, function,
                                                  scope, err);
@@ -910,7 +911,8 @@
   }
 
   // Create the new pool.
-  std::unique_ptr<Pool> pool = std::make_unique<Pool>(scope->settings(), label);
+  std::unique_ptr<Pool> pool = std::make_unique<Pool>(
+      scope->settings(), label, scope->build_dependency_files());
   pool->set_depth(depth->int_value());
 
   // Save the generated item.
diff --git a/tools/gn/item.cc b/tools/gn/item.cc
index 31abcdb..f36ff02 100644
--- a/tools/gn/item.cc
+++ b/tools/gn/item.cc
@@ -7,8 +7,13 @@
 #include "base/logging.h"
 #include "tools/gn/settings.h"
 
-Item::Item(const Settings* settings, const Label& label)
-    : settings_(settings), label_(label), defined_from_(nullptr) {}
+Item::Item(const Settings* settings,
+           const Label& label,
+           const std::set<SourceFile>& build_dependency_files)
+    : settings_(settings),
+      label_(label),
+      build_dependency_files_(build_dependency_files),
+      defined_from_(nullptr) {}
 
 Item::~Item() = default;
 
diff --git a/tools/gn/item.h b/tools/gn/item.h
index 3ec482a..9195b5e 100644
--- a/tools/gn/item.h
+++ b/tools/gn/item.h
@@ -5,15 +5,18 @@
 #ifndef TOOLS_GN_ITEM_H_
 #define TOOLS_GN_ITEM_H_
 
+#include <set>
 #include <string>
 
 #include "tools/gn/label.h"
+#include "tools/gn/source_file.h"
 #include "tools/gn/visibility.h"
 
 class Config;
 class ParseNode;
 class Pool;
 class Settings;
+class SourceFile;
 class Target;
 class Toolchain;
 
@@ -21,7 +24,9 @@
 // graph.
 class Item {
  public:
-  Item(const Settings* settings, const Label& label);
+  Item(const Settings* settings,
+       const Label& label,
+       const std::set<SourceFile>& build_dependency_files = {});
   virtual ~Item();
 
   const Settings* settings() const { return settings_; }
@@ -50,6 +55,12 @@
   // be used in logging and error messages.
   std::string GetItemTypeName() const;
 
+  // Returns the set of build files that may affect this item, please refer to
+  // Scope for how this is determined.
+  const std::set<SourceFile>& build_dependency_files() const {
+    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.
@@ -58,6 +69,7 @@
  private:
   const Settings* settings_;
   Label label_;
+  const std::set<SourceFile> build_dependency_files_;
   const ParseNode* defined_from_;
 
   Visibility visibility_;
diff --git a/tools/gn/loader.cc b/tools/gn/loader.cc
index 616a224..2e10c9a 100644
--- a/tools/gn/loader.cc
+++ b/tools/gn/loader.cc
@@ -24,9 +24,7 @@
 
 struct SourceFileAndOrigin {
   SourceFileAndOrigin(const SourceFile& f, const LocationRange& o)
-      : file(f),
-        origin(o) {
-  }
+      : file(f), origin(o) {}
 
   SourceFile file;
   LocationRange origin;
@@ -39,9 +37,7 @@
 struct LoaderImpl::LoadID {
   LoadID() = default;
   LoadID(const SourceFile& f, const Label& tc_name)
-      : file(f),
-        toolchain_name(tc_name) {
-  }
+      : file(f), toolchain_name(tc_name) {}
 
   bool operator<(const LoadID& other) const {
     if (file.value() == other.file.value())
@@ -61,9 +57,10 @@
   ToolchainRecord(const BuildSettings* build_settings,
                   const Label& toolchain_label,
                   const Label& default_toolchain_label)
-      : settings(build_settings,
-                 GetOutputSubdirName(toolchain_label,
-                     toolchain_label == default_toolchain_label)),
+      : settings(
+            build_settings,
+            GetOutputSubdirName(toolchain_label,
+                                toolchain_label == default_toolchain_label)),
         is_toolchain_loaded(false),
         is_config_loaded(false) {
     settings.set_default_toolchain_label(default_toolchain_label);
@@ -111,7 +108,8 @@
                       const LocationRange& origin,
                       const Label& in_toolchain_name) {
   const Label& toolchain_name = in_toolchain_name.is_null()
-      ? default_toolchain_label_ : in_toolchain_name;
+                                    ? default_toolchain_label_
+                                    : in_toolchain_name;
   LoadID load_id(file, toolchain_name);
   if (!invocations_.insert(load_id).second)
     return;  // Already in set, so this file was already loaded or schedulerd.
@@ -253,6 +251,7 @@
   Scope our_scope(settings->base_config());
   ScopePerFileProvider per_file_provider(&our_scope, true);
   our_scope.set_source_dir(file_name.GetDir());
+  our_scope.AddBuildDependencyFile(file_name);
 
   // Targets, etc. generated as part of running this file will end up here.
   Scope::ItemVector collected_items;
@@ -294,9 +293,11 @@
 
   Scope* base_config = settings->base_config();
   base_config->set_source_dir(SourceDir("//"));
+  base_config->AddBuildDependencyFile(
+      settings->build_settings()->build_config_file());
 
-  settings->build_settings()->build_args().SetupRootScope(
-      base_config, toolchain_overrides);
+  settings->build_settings()->build_args().SetupRootScope(base_config,
+                                                          toolchain_overrides);
 
   base_config->SetProcessingBuildConfig();
 
@@ -306,7 +307,7 @@
     base_config->SetProperty(kDefaultToolchainKey, &default_toolchain_label);
 
   ScopedTrace trace(TraceItem::TRACE_FILE_EXECUTE,
-      settings->build_settings()->build_config_file().value());
+                    settings->build_settings()->build_config_file().value());
   trace.SetToolchain(settings->toolchain_label());
 
   Err err;
@@ -327,7 +328,8 @@
     // The default toolchain must have been set in the default build config
     // file.
     if (default_toolchain_label.is_null()) {
-      g_scheduler->FailWithError(Err(Location(),
+      g_scheduler->FailWithError(Err(
+          Location(),
           "The default build config file did not call set_default_toolchain()",
           "If you don't call this, I can't figure out what toolchain to use\n"
           "for all of this code."));
@@ -423,6 +425,5 @@
     return g_scheduler->input_file_manager()->AsyncLoadFile(
         origin, build_settings, file_name, callback, err);
   }
-  return async_load_file_.Run(
-      origin, build_settings, file_name, callback, err);
+  return async_load_file_.Run(origin, build_settings, file_name, callback, err);
 }
diff --git a/tools/gn/loader_unittest.cc b/tools/gn/loader_unittest.cc
index 8f51a42..4b240c3 100644
--- a/tools/gn/loader_unittest.cc
+++ b/tools/gn/loader_unittest.cc
@@ -20,6 +20,35 @@
 
 namespace {
 
+bool ItemContainsBuildDependencyFile(const Item* item,
+                                     const SourceFile& source_file) {
+  const auto& build_dependency_files = item->build_dependency_files();
+  return build_dependency_files.end() !=
+         build_dependency_files.find(source_file);
+}
+
+class MockBuilder {
+ public:
+  void OnItemDefined(std::unique_ptr<Item> item);
+  std::vector<const Item*> GetAllItems() const;
+
+ private:
+  std::vector<std::unique_ptr<Item>> items_;
+};
+
+void MockBuilder::OnItemDefined(std::unique_ptr<Item> item) {
+  items_.push_back(std::move(item));
+}
+
+std::vector<const Item*> MockBuilder::GetAllItems() const {
+  std::vector<const Item*> result;
+  for (const auto& item : items_) {
+    result.push_back(item.get());
+  }
+
+  return result;
+}
+
 class MockInputFileManager {
  public:
   typedef base::Callback<void(const ParseNode*)> Callback;
@@ -119,6 +148,7 @@
  protected:
   Scheduler scheduler_;
   BuildSettings build_settings_;
+  MockBuilder mock_builder_;
   MockInputFileManager mock_ifm_;
 };
 
@@ -184,3 +214,51 @@
 
   EXPECT_FALSE(scheduler_.is_failed());
 }
+
+TEST_F(LoaderTest, BuildDependencyFilesAreCollected) {
+  SourceFile build_config("//build/config/BUILDCONFIG.gn");
+  SourceFile root_build("//BUILD.gn");
+  build_settings_.set_build_config_file(build_config);
+  build_settings_.set_item_defined_callback(base::Bind(
+      &MockBuilder::OnItemDefined, base::Unretained(&mock_builder_)));
+
+  scoped_refptr<LoaderImpl> loader(new LoaderImpl(&build_settings_));
+  mock_ifm_.AddCannedResponse(build_config,
+                              "set_default_toolchain(\"//tc:tc\")");
+  mock_ifm_.AddCannedResponse(SourceFile("//test.gni"), "concurrent_jobs = 1");
+  std::string root_build_content =
+      "executable(\"a\") { sources = [ \"a.cc\" ] }\n"
+      "config(\"b\") { configs = [\"//t:t\"] }\n"
+      "toolchain(\"c\") {}\n"
+      "pool(\"d\") { depth = 1 }";
+  mock_ifm_.AddCannedResponse(root_build, root_build_content);
+
+  loader->set_async_load_file(mock_ifm_.GetCallback());
+
+  // Request the root build file be loaded. This should kick off the default
+  // build config loading.
+  loader->Load(root_build, LocationRange(), Label());
+  EXPECT_TRUE(mock_ifm_.HasOnePending(build_config));
+
+  // Completing the build config load should kick off the root build file load.
+  mock_ifm_.IssueAllPending();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(mock_ifm_.HasOnePending(root_build));
+
+  // Completing the root build file should define a target which must have
+  // set of source files hashes.
+  mock_ifm_.IssueAllPending();
+  base::RunLoop().RunUntilIdle();
+
+  std::vector<const Item*> items = mock_builder_.GetAllItems();
+  EXPECT_TRUE(items[0]->AsTarget());
+  EXPECT_TRUE(ItemContainsBuildDependencyFile(items[0], root_build));
+  EXPECT_TRUE(items[1]->AsConfig());
+  EXPECT_TRUE(ItemContainsBuildDependencyFile(items[1], root_build));
+  EXPECT_TRUE(items[2]->AsToolchain());
+  EXPECT_TRUE(ItemContainsBuildDependencyFile(items[2], root_build));
+  EXPECT_TRUE(items[3]->AsPool());
+  EXPECT_TRUE(ItemContainsBuildDependencyFile(items[3], root_build));
+
+  EXPECT_FALSE(scheduler_.is_failed());
+}
diff --git a/tools/gn/scope.cc b/tools/gn/scope.cc
index 444ce53..145d6ad 100644
--- a/tools/gn/scope.cc
+++ b/tools/gn/scope.cc
@@ -8,6 +8,7 @@
 
 #include "base/logging.h"
 #include "tools/gn/parse_tree.h"
+#include "tools/gn/source_file.h"
 #include "tools/gn/template.h"
 
 namespace {
@@ -30,8 +31,7 @@
 Scope::MergeOptions::MergeOptions()
     : clobber_existing(false),
       skip_private_vars(false),
-      mark_dest_used(false) {
-}
+      mark_dest_used(false) {}
 
 Scope::MergeOptions::~MergeOptions() = default;
 
@@ -51,14 +51,16 @@
       mutable_containing_(parent),
       settings_(parent->settings()),
       mode_flags_(0),
-      item_collector_(nullptr) {}
+      item_collector_(nullptr),
+      build_dependency_files_(parent->build_dependency_files_) {}
 
 Scope::Scope(const Scope* parent)
     : const_containing_(parent),
       mutable_containing_(nullptr),
       settings_(parent->settings()),
       mode_flags_(0),
-      item_collector_(nullptr) {}
+      item_collector_(nullptr),
+      build_dependency_files_(parent->build_dependency_files_) {}
 
 Scope::~Scope() = default;
 
@@ -121,8 +123,8 @@
 
   // Search in the parent mutable scope if requested, but not const one.
   if (search_mode == SEARCH_NESTED && mutable_containing_) {
-    return mutable_containing_->GetMutableValue(
-        ident, Scope::SEARCH_NESTED, counts_as_used);
+    return mutable_containing_->GetMutableValue(ident, Scope::SEARCH_NESTED,
+                                                counts_as_used);
   }
   return nullptr;
 }
@@ -247,7 +249,8 @@
 bool Scope::CheckForUnusedVars(Err* err) const {
   for (const auto& pair : values_) {
     if (!pair.second.used) {
-      std::string help = "You set the variable \"" + pair.first.as_string() +
+      std::string help =
+          "You set the variable \"" + pair.first.as_string() +
           "\" here and it was unused before it went\nout of scope.";
 
       const BinaryOpNode* binary = pair.second.value.origin()->AsBinaryOp();
@@ -294,13 +297,16 @@
         // Value present in both the source and the dest.
         std::string desc_string(desc_for_err);
         *err = Err(node_for_err, "Value collision.",
-            "This " + desc_string + " contains \"" + current_name.as_string() +
-            "\"");
-        err->AppendSubErr(Err(pair.second.value, "defined here.",
-            "Which would clobber the one in your current scope"));
-        err->AppendSubErr(Err(*existing_value, "defined here.",
-            "Executing " + desc_string + " should not conflict with anything "
-            "in the current\nscope unless the values are identical."));
+                   "This " + desc_string + " contains \"" +
+                       current_name.as_string() + "\"");
+        err->AppendSubErr(
+            Err(pair.second.value, "defined here.",
+                "Which would clobber the one in your current scope"));
+        err->AppendSubErr(
+            Err(*existing_value, "defined here.",
+                "Executing " + desc_string +
+                    " should not conflict with anything "
+                    "in the current\nscope unless the values are identical."));
         return false;
       }
     }
@@ -333,12 +339,18 @@
           // target defaults.
           std::string desc_string(desc_for_err);
           *err = Err(node_for_err, "Target defaults collision.",
-              "This " + desc_string + " contains target defaults for\n"
-              "\"" + current_name + "\" which would clobber one for the\n"
-              "same target type in your current scope. It's unfortunate that "
-              "I'm too stupid\nto tell you the location of where the target "
-              "defaults were set. Usually\nthis happens in the BUILDCONFIG.gn "
-              "file or in a related .gni file.\n");
+                     "This " + desc_string +
+                         " contains target defaults for\n"
+                         "\"" +
+                         current_name +
+                         "\" which would clobber one for the\n"
+                         "same target type in your current scope. It's "
+                         "unfortunate that "
+                         "I'm too stupid\nto tell you the location of where "
+                         "the target "
+                         "defaults were set. Usually\nthis happens in the "
+                         "BUILDCONFIG.gn "
+                         "file or in a related .gni file.\n");
           return false;
         }
       }
@@ -357,8 +369,9 @@
         // Sources assignment filter present in both the source and the dest.
         std::string desc_string(desc_for_err);
         *err = Err(node_for_err, "Assignment filter collision.",
-            "The " + desc_string + " contains a sources_assignment_filter "
-            "which\nwould clobber the one in your current scope.");
+                   "The " + desc_string +
+                       " contains a sources_assignment_filter "
+                       "which\nwould clobber the one in your current scope.");
         return false;
       }
     }
@@ -386,15 +399,16 @@
         // same one.
         std::string desc_string(desc_for_err);
         *err = Err(node_for_err, "Template collision.",
-            "This " + desc_string + " contains a template \"" +
-            current_name + "\"");
-        err->AppendSubErr(Err(pair.second->GetDefinitionRange(),
-            "defined here.",
-            "Which would clobber the one in your current scope"));
+                   "This " + desc_string + " contains a template \"" +
+                       current_name + "\"");
+        err->AppendSubErr(
+            Err(pair.second->GetDefinitionRange(), "defined here.",
+                "Which would clobber the one in your current scope"));
         err->AppendSubErr(Err(existing_template->GetDefinitionRange(),
-            "defined here.",
-            "Executing " + desc_string + " should not conflict with anything "
-            "in the current\nscope."));
+                              "defined here.",
+                              "Executing " + desc_string +
+                                  " should not conflict with anything "
+                                  "in the current\nscope."));
         return false;
       }
     }
@@ -403,6 +417,10 @@
     dest->templates_[current_name] = pair.second;
   }
 
+  // Propogate build dependency files,
+  dest->build_dependency_files_.insert(build_dependency_files_.begin(),
+                                       build_dependency_files_.end());
+
   return true;
 }
 
@@ -501,6 +519,10 @@
   return source_dir_;
 }
 
+void Scope::AddBuildDependencyFile(const SourceFile& build_dependency_file) {
+  build_dependency_files_.insert(build_dependency_file);
+}
+
 Scope::ItemVector* Scope::GetItemCollector() {
   if (item_collector_)
     return item_collector_;
diff --git a/tools/gn/scope.h b/tools/gn/scope.h
index 8ad14ed..9e683d2 100644
--- a/tools/gn/scope.h
+++ b/tools/gn/scope.h
@@ -22,6 +22,7 @@
 class Item;
 class ParseNode;
 class Settings;
+class SourceFile;
 class Template;
 
 // Scope for the script execution.
@@ -284,6 +285,15 @@
   const SourceDir& GetSourceDir() const;
   void set_source_dir(const SourceDir& d) { source_dir_ = d; }
 
+  // Set of files that may affect the execution of this scope. Note that this
+  // set is constructed conservatively, meanining that every file that can
+  // potentially affect this scope is included, but not necessarily every change
+  // to these files will affect this scope.
+  const std::set<SourceFile>& build_dependency_files() const {
+    return build_dependency_files_;
+  }
+  void AddBuildDependencyFile(const SourceFile& build_dependency_file);
+
   // The item collector is where Items (Targets, Configs, etc.) go that have
   // been defined. If a scope can generate items, this non-owning pointer will
   // point to the storage for such items. The creator of this scope will be
@@ -379,6 +389,8 @@
 
   SourceDir source_dir_;
 
+  std::set<SourceFile> build_dependency_files_;
+
   DISALLOW_COPY_AND_ASSIGN(Scope);
 };
 
diff --git a/tools/gn/scope_unittest.cc b/tools/gn/scope_unittest.cc
index a90d725..ce3fe0b 100644
--- a/tools/gn/scope_unittest.cc
+++ b/tools/gn/scope_unittest.cc
@@ -2,10 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "tools/gn/scope.h"
+
 #include "testing/gtest/include/gtest/gtest.h"
 #include "tools/gn/input_file.h"
 #include "tools/gn/parse_tree.h"
-#include "tools/gn/scope.h"
+#include "tools/gn/source_file.h"
 #include "tools/gn/template.h"
 #include "tools/gn/test_with_scope.h"
 
@@ -22,8 +24,25 @@
   return value->string_value() == expected_value;
 }
 
+bool ContainsBuildDependencyFile(const Scope* scope,
+                                 const SourceFile& source_file) {
+  const auto& build_dependency_files = scope->build_dependency_files();
+  return build_dependency_files.end() !=
+         build_dependency_files.find(source_file);
+}
+
 }  // namespace
 
+TEST(Scope, InheritBuildDependencyFilesFromParent) {
+  TestWithScope setup;
+  SourceFile source_file = SourceFile("//a/BUILD.gn");
+  setup.scope()->AddBuildDependencyFile(source_file);
+
+  Scope new_scope(setup.scope());
+  EXPECT_EQ(1U, new_scope.build_dependency_files().size());
+  EXPECT_TRUE(ContainsBuildDependencyFile(&new_scope, source_file));
+}
+
 TEST(Scope, NonRecursiveMergeTo) {
   TestWithScope setup;
 
@@ -192,6 +211,24 @@
     EXPECT_TRUE(new_scope.CheckForUnusedVars(&err));
     EXPECT_FALSE(err.has_error());
   }
+
+  // Build dependency files are merged.
+  {
+    Scope from_scope(setup.settings());
+    SourceFile source_file = SourceFile("//a/BUILD.gn");
+    from_scope.AddBuildDependencyFile(source_file);
+
+    Scope to_scope(setup.settings());
+    EXPECT_FALSE(ContainsBuildDependencyFile(&to_scope, source_file));
+
+    Scope::MergeOptions options;
+    Err err;
+    EXPECT_TRUE(from_scope.NonRecursiveMergeTo(&to_scope, options, &assignment,
+                                               "error", &err));
+    EXPECT_FALSE(err.has_error());
+    EXPECT_EQ(1U, to_scope.build_dependency_files().size());
+    EXPECT_TRUE(ContainsBuildDependencyFile(&to_scope, source_file));
+  }
 }
 
 TEST(Scope, MakeClosure) {
diff --git a/tools/gn/setup.cc b/tools/gn/setup.cc
index dea3eae..9bb0b71 100644
--- a/tools/gn/setup.cc
+++ b/tools/gn/setup.cc
@@ -690,6 +690,7 @@
     return false;
   }
 
+  dotfile_scope_.AddBuildDependencyFile(SourceFile("//.gn"));
   dotfile_root_->Execute(&dotfile_scope_, &err);
   if (err.has_error()) {
     err.PrintToStdout();
diff --git a/tools/gn/target.cc b/tools/gn/target.cc
index d65e265..5c5ff5c 100644
--- a/tools/gn/target.cc
+++ b/tools/gn/target.cc
@@ -273,8 +273,10 @@
   future, do not rely on this behavior.
 )";
 
-Target::Target(const Settings* settings, const Label& label)
-    : Item(settings, label),
+Target::Target(const Settings* settings,
+               const Label& label,
+               const std::set<SourceFile>& build_dependency_files)
+    : Item(settings, label, build_dependency_files),
       output_type_(UNKNOWN),
       output_prefix_override_(false),
       output_extension_set_(false),
diff --git a/tools/gn/target.h b/tools/gn/target.h
index 1890a53..2c9dc35 100644
--- a/tools/gn/target.h
+++ b/tools/gn/target.h
@@ -55,7 +55,11 @@
   typedef std::vector<SourceFile> FileList;
   typedef std::vector<std::string> StringVector;
 
-  Target(const Settings* settings, const Label& label);
+  // We track the set of build files that may affect this target, please refer
+  // to Scope for how this is determined.
+  Target(const Settings* settings,
+         const Label& label,
+         const std::set<SourceFile>& build_dependency_files = {});
   ~Target() override;
 
   // Returns a string naming the output type.
diff --git a/tools/gn/target_generator.cc b/tools/gn/target_generator.cc
index 114bca7..c441f04 100644
--- a/tools/gn/target_generator.cc
+++ b/tools/gn/target_generator.cc
@@ -90,8 +90,8 @@
   if (g_scheduler->verbose_logging())
     g_scheduler->Log("Defining target", label.GetUserVisibleName(true));
 
-  std::unique_ptr<Target> target =
-      std::make_unique<Target>(scope->settings(), label);
+  std::unique_ptr<Target> target = std::make_unique<Target>(
+      scope->settings(), label, scope->build_dependency_files());
   target->set_defined_from(function_call);
 
   // Create and call out to the proper generator.
diff --git a/tools/gn/toolchain.cc b/tools/gn/toolchain.cc
index ec02b3e..879acd9 100644
--- a/tools/gn/toolchain.cc
+++ b/tools/gn/toolchain.cc
@@ -28,8 +28,10 @@
 const char* Toolchain::kToolCompileXCAssets = "compile_xcassets";
 const char* Toolchain::kToolAction = "action";
 
-Toolchain::Toolchain(const Settings* settings, const Label& label)
-    : Item(settings, label), setup_complete_(false) {}
+Toolchain::Toolchain(const Settings* settings,
+                     const Label& label,
+                     const std::set<SourceFile>& build_dependency_files)
+    : Item(settings, label, build_dependency_files), setup_complete_(false) {}
 
 Toolchain::~Toolchain() = default;
 
diff --git a/tools/gn/toolchain.h b/tools/gn/toolchain.h
index 368c2db..1e9bc2e 100644
--- a/tools/gn/toolchain.h
+++ b/tools/gn/toolchain.h
@@ -77,7 +77,12 @@
   // Loader::GetToolchainSettings(). Many toolchain objects may be created in a
   // given build, but only a few might be used, and the Loader is in charge of
   // this process.
-  Toolchain(const Settings* settings, const Label& label);
+  //
+  // We also track the set of build files that may affect this target, please
+  // refer to Scope for how this is determined.
+  Toolchain(const Settings* settings,
+            const Label& label,
+            const std::set<SourceFile>& build_dependency_files = {});
   ~Toolchain() override;
 
   // Item overrides.