List .gn file in build.ninja.d.

This way, gn reruns when the .gn file is touched.

Bug: 24
Change-Id: Iabc21ee5f4b82106c7d8004f8372b3d83471b515
Reviewed-on: https://gn-review.googlesource.com/c/3321
Commit-Queue: Brett Wilson <brettw@chromium.org>
Reviewed-by: Brett Wilson <brettw@chromium.org>
diff --git a/build/gen.py b/build/gen.py
index 9f5eaf2..56a6566 100755
--- a/build/gen.py
+++ b/build/gen.py
@@ -586,6 +586,7 @@
         'tools/gn/runtime_deps_unittest.cc',
         'tools/gn/scope_per_file_provider_unittest.cc',
         'tools/gn/scope_unittest.cc',
+        'tools/gn/setup_unittest.cc',
         'tools/gn/source_dir_unittest.cc',
         'tools/gn/source_file_unittest.cc',
         'tools/gn/string_utils_unittest.cc',
diff --git a/tools/gn/command_analyze.cc b/tools/gn/command_analyze.cc
index 9aa698b..d3b24bf 100644
--- a/tools/gn/command_analyze.cc
+++ b/tools/gn/command_analyze.cc
@@ -106,6 +106,7 @@
     return 1;
   }
 
+  // Deliberately leaked to avoid expensive process teardown.
   Setup* setup = new Setup;
   if (!setup->DoSetup(args[0], false) || !setup->Run())
     return 1;
diff --git a/tools/gn/command_args.cc b/tools/gn/command_args.cc
index 9980333..fac8683 100644
--- a/tools/gn/command_args.cc
+++ b/tools/gn/command_args.cc
@@ -205,6 +205,7 @@
 }
 
 int ListArgs(const std::string& build_dir) {
+  // Deliberately leaked to avoid expensive process teardown.
   Setup* setup = new Setup;
   if (!setup->DoSetup(build_dir, false) || !setup->Run())
     return 1;
diff --git a/tools/gn/command_clean.cc b/tools/gn/command_clean.cc
index b138864..b354039 100644
--- a/tools/gn/command_clean.cc
+++ b/tools/gn/command_clean.cc
@@ -63,6 +63,7 @@
     return 1;
   }
 
+  // Deliberately leaked to avoid expensive process teardown.
   Setup* setup = new Setup;
   if (!setup->DoSetup(args[0], false))
     return 1;
diff --git a/tools/gn/command_ls.cc b/tools/gn/command_ls.cc
index 65ec1a9..2609499 100644
--- a/tools/gn/command_ls.cc
+++ b/tools/gn/command_ls.cc
@@ -68,6 +68,7 @@
     return 1;
   }
 
+  // Deliberately leaked to avoid expensive process teardown.
   Setup* setup = new Setup;
   if (!setup->DoSetup(args[0], false) || !setup->Run())
     return 1;
diff --git a/tools/gn/command_path.cc b/tools/gn/command_path.cc
index 333a78e..503ba6d 100644
--- a/tools/gn/command_path.cc
+++ b/tools/gn/command_path.cc
@@ -319,6 +319,7 @@
     return 1;
   }
 
+  // Deliberately leaked to avoid expensive process teardown.
   Setup* setup = new Setup;
   if (!setup->DoSetup(args[0], false))
     return 1;
diff --git a/tools/gn/command_refs.cc b/tools/gn/command_refs.cc
index 92ecf10..1ca63ec 100644
--- a/tools/gn/command_refs.cc
+++ b/tools/gn/command_refs.cc
@@ -407,6 +407,7 @@
   bool all = cmdline->HasSwitch("all");
   bool all_toolchains = cmdline->HasSwitch(switches::kAllToolchains);
 
+  // Deliberately leaked to avoid expensive process teardown.
   Setup* setup = new Setup;
   if (!setup->DoSetup(args[0], false) || !setup->Run())
     return 1;
diff --git a/tools/gn/exec_process_unittest.cc b/tools/gn/exec_process_unittest.cc
index 323a070..647bf42 100644
--- a/tools/gn/exec_process_unittest.cc
+++ b/tools/gn/exec_process_unittest.cc
@@ -67,7 +67,6 @@
 // byte buffer and, if stdout is non-blocking, python will throw an IOError when
 // a write exceeds the buffer size.
 TEST(ExecProcessTest, TestLargeOutput) {
-  base::ScopedTempDir temp_dir;
   std::string std_out, std_err;
   int exit_code;
 
@@ -78,7 +77,6 @@
 }
 
 TEST(ExecProcessTest, TestStdoutAndStderrOutput) {
-  base::ScopedTempDir temp_dir;
   std::string std_out, std_err;
   int exit_code;
 
diff --git a/tools/gn/ninja_build_writer_unittest.cc b/tools/gn/ninja_build_writer_unittest.cc
index 55ffcc9..50530b1 100644
--- a/tools/gn/ninja_build_writer_unittest.cc
+++ b/tools/gn/ninja_build_writer_unittest.cc
@@ -113,7 +113,8 @@
   target_baz.action_values().outputs() = SubstitutionList::MakeForTest(
       "//out/Debug/out5.out", "//out/Debug/out6.out");
   target_baz.SetToolchain(&other_toolchain);
-  target_baz.action_values().set_pool(LabelPtrPair<Pool>(&another_regular_pool));
+  target_baz.action_values().set_pool(
+      LabelPtrPair<Pool>(&another_regular_pool));
   ASSERT_TRUE(target_baz.OnResolved(&err));
 
   // The console pool must be in the default toolchain.
diff --git a/tools/gn/setup.cc b/tools/gn/setup.cc
index 27c6475..6720f65 100644
--- a/tools/gn/setup.cc
+++ b/tools/gn/setup.cc
@@ -315,20 +315,25 @@
 Setup::~Setup() = default;
 
 bool Setup::DoSetup(const std::string& build_dir, bool force_create) {
-  base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
+  return DoSetup(build_dir, force_create,
+                 *base::CommandLine::ForCurrentProcess());
+}
 
-  scheduler_.set_verbose_logging(cmdline->HasSwitch(switches::kVerbose));
-  if (cmdline->HasSwitch(switches::kTime) ||
-      cmdline->HasSwitch(switches::kTracelog))
+bool Setup::DoSetup(const std::string& build_dir,
+                    bool force_create,
+                    const base::CommandLine& cmdline) {
+  scheduler_.set_verbose_logging(cmdline.HasSwitch(switches::kVerbose));
+  if (cmdline.HasSwitch(switches::kTime) ||
+      cmdline.HasSwitch(switches::kTracelog))
     EnableTracing();
 
   ScopedTrace setup_trace(TraceItem::TRACE_SETUP, "DoSetup");
 
-  if (!FillSourceDir(*cmdline))
+  if (!FillSourceDir(cmdline))
     return false;
   if (!RunConfigFile())
     return false;
-  if (!FillOtherConfig(*cmdline))
+  if (!FillOtherConfig(cmdline))
     return false;
 
   // Must be after FillSourceDir to resolve.
@@ -344,10 +349,10 @@
   }
 
   if (fill_arguments_) {
-    if (!FillArguments(*cmdline))
+    if (!FillArguments(cmdline))
       return false;
   }
-  if (!FillPythonPath(*cmdline))
+  if (!FillPythonPath(cmdline))
     return false;
 
   // Check for unused variables in the .gn file.
@@ -361,10 +366,14 @@
 }
 
 bool Setup::Run() {
+  return Run(*base::CommandLine::ForCurrentProcess());
+}
+
+bool Setup::Run(const base::CommandLine& cmdline) {
   RunPreMessageLoop();
   if (!scheduler_.Run())
     return false;
-  return RunPostMessageLoop();
+  return RunPostMessageLoop(cmdline);
 }
 
 SourceFile Setup::GetBuildArgFile() const {
@@ -379,7 +388,7 @@
   loader_->Load(root_build_file_, LocationRange(), Label());
 }
 
-bool Setup::RunPostMessageLoop() {
+bool Setup::RunPostMessageLoop(const base::CommandLine& cmdline) {
   Err err;
   if (!builder_.CheckForBadItems(&err)) {
     err.PrintToStdout();
@@ -387,8 +396,7 @@
   }
 
   if (!build_settings_.build_args().VerifyAllOverridesUsed(&err)) {
-    if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-            switches::kFailOnUnusedArgs)) {
+    if (cmdline.HasSwitch(switches::kFailOnUnusedArgs)) {
       err.PrintToStdout();
       return false;
     }
@@ -416,11 +424,10 @@
   }
 
   // Write out tracing and timing if requested.
-  const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
-  if (cmdline->HasSwitch(switches::kTime))
+  if (cmdline.HasSwitch(switches::kTime))
     PrintLongHelp(SummarizeTraces());
-  if (cmdline->HasSwitch(switches::kTracelog))
-    SaveTraces(cmdline->GetSwitchValuePath(switches::kTracelog));
+  if (cmdline.HasSwitch(switches::kTracelog))
+    SaveTraces(cmdline.GetSwitchValuePath(switches::kTracelog));
 
   return true;
 }
@@ -715,6 +722,12 @@
     return false;
   }
 
+  // Add a dependency on the build arguments file. If this changes, we want
+  // to re-generate the build. This causes the dotfile to make it into
+  // build.ninja.d.
+  g_scheduler->AddGenDependency(dotfile_name_);
+
+  // Also add a build dependency to the scope, which is used by `gn analyze`.
   dotfile_scope_.AddBuildDependencyFile(SourceFile("//.gn"));
   dotfile_root_->Execute(&dotfile_scope_, &err);
   if (err.has_error()) {
diff --git a/tools/gn/setup.h b/tools/gn/setup.h
index 13d0da5..cca30ab 100644
--- a/tools/gn/setup.h
+++ b/tools/gn/setup.h
@@ -29,7 +29,7 @@
 
 extern const char kDotfile_Help[];
 
-// Helper class to setup the build settings and environment for the various
+// Helper class to set up the build settings and environment for the various
 // commands to run.
 class Setup {
  public:
@@ -49,12 +49,24 @@
   // generation should set this to true to create it, but querying commands
   // should set it to false to prevent creating oddly-named directories in case
   // the user omits the build directory argument (which is easy to do).
+  //
+  // cmdline is the gn invocation command, with flags like --root and --dotfile.
+  // If no explicit cmdline is passed, base::CommandLine::ForCurrentProcess()
+  // is used.
   bool DoSetup(const std::string& build_dir, bool force_create);
+  bool DoSetup(const std::string& build_dir,
+               bool force_create,
+               const base::CommandLine& cmdline);
 
   // Runs the load, returning true on success. On failure, prints the error
   // and returns false. This includes both RunPreMessageLoop() and
   // RunPostMessageLoop().
+  //
+  // cmdline is the gn invocation command, with flags like --root and --dotfile.
+  // If no explicit cmdline is passed, base::CommandLine::ForCurrentProcess()
+  // is used.
   bool Run();
+  bool Run(const base::CommandLine& cmdline);
 
   Scheduler& scheduler() { return scheduler_; }
 
@@ -93,7 +105,7 @@
   // Performs the two sets of operations to run the generation before and after
   // the message loop is run.
   void RunPreMessageLoop();
-  bool RunPostMessageLoop();
+  bool RunPostMessageLoop(const base::CommandLine& cmdline);
 
   // Fills build arguments. Returns true on success.
   bool FillArguments(const base::CommandLine& cmdline);
diff --git a/tools/gn/setup_unittest.cc b/tools/gn/setup_unittest.cc
new file mode 100644
index 0000000..ab94ce1
--- /dev/null
+++ b/tools/gn/setup_unittest.cc
@@ -0,0 +1,44 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "tools/gn/setup.h"
+
+#include "base/command_line.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "tools/gn/filesystem_utils.h"
+#include "tools/gn/switches.h"
+#include "tools/gn/test_with_scheduler.h"
+
+using SetupTest = TestWithScheduler;
+
+static void WriteFile(const base::FilePath& file, const std::string& data) {
+  CHECK(data.size() == base::WriteFile(file, data.data(), data.size()));
+}
+
+TEST_F(SetupTest, DotGNFileIsGenDep) {
+  base::CommandLine cmdline(base::CommandLine::NO_PROGRAM);
+
+  // Create a temp directory containing a .gn file and a BUILDCONFIG.gn file,
+  // pass it as --root.
+  base::ScopedTempDir in_temp_dir;
+  ASSERT_TRUE(in_temp_dir.CreateUniqueTempDir());
+  base::FilePath in_path = in_temp_dir.GetPath();
+  base::FilePath dot_gn_name = in_path.Append(FILE_PATH_LITERAL(".gn"));
+  WriteFile(dot_gn_name, "buildconfig = \"//BUILDCONFIG.gn\"\n");
+  WriteFile(in_path.Append(FILE_PATH_LITERAL("BUILDCONFIG.gn")), "");
+  cmdline.AppendSwitchASCII(switches::kRoot, FilePathToUTF8(in_path));
+
+  // Create another temp dir for writing the generated files to.
+  base::ScopedTempDir build_temp_dir;
+  ASSERT_TRUE(build_temp_dir.CreateUniqueTempDir());
+
+  // Run setup and check that the .gn file is in the scheduler's gen deps.
+  Setup setup;
+  EXPECT_TRUE(
+      setup.DoSetup(FilePathToUTF8(build_temp_dir.GetPath()), true, cmdline));
+  std::vector<base::FilePath> gen_deps = g_scheduler->GetGenDependencies();
+  ASSERT_EQ(1u, gen_deps.size());
+  EXPECT_EQ(gen_deps[0], base::MakeAbsoluteFilePath(dot_gn_name));
+}