GN: don't write separate files for non-binary targets

Binary targets that compile code need to be in separate ninja files for the various flags variables to be properly scoped. But other targets like groups and actions don't. This change skips making separate files for such targets.

The target writers have changed to return the code they want written to the main ninja file, which is either all of the code (for the new integrated case) or a subninja line to reference a written file. This allowed some amount of cleanup in how the Ninja toolchain files were written.

The changes in the ninja toolchain files required more changes in the NinjaBuildWriter because the lists the NinjaBuildWriter were no longer being generated as a side effect of running. The build writer is now more isolated and can run without such precomputed context. There was a fair bit of refactoring required to make this work in ninja_build_writer.cc. Effectively the complexity in NinjaWriter moved to NinjaBuildWriter::RunAndWriteFiles.

A related cleanup is that I noticed the Builder object is RefCountedThreadsafe because it used to be used across threads. But a while ago I changes this to work on the main thread only. I removed the refcounting and changed most places to pass by reference instead of pointer.

GN runtime stats:
 - Desktop Linux: saves 2656 files, 110ms.
 - Android: saves 8825 files, 231ms.
 - Windows: saves 2203 files, 247ms.

Ninja no-op build stats (measures loading time):
 - Desktop Linux: same
 - Android: regressed 50ms (maybe because there's less parsing parallelism and Linux's filesystem is so fast?)
 - Windows: saves 156ms (because Windows' filesystem is so slow.

Review-Url: https://codereview.chromium.org/2152413002
Cr-Original-Commit-Position: refs/heads/master@{#407908}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: 8293c354da7aee9e9a7130b3b6f67d9ea43a7230
diff --git a/tools/gn/builder.h b/tools/gn/builder.h
index 0c2fe70..d8209bc 100644
--- a/tools/gn/builder.h
+++ b/tools/gn/builder.h
@@ -8,7 +8,6 @@
 #include "base/callback.h"
 #include "base/containers/hash_tables.h"
 #include "base/macros.h"
-#include "base/memory/ref_counted.h"
 #include "tools/gn/builder_record.h"
 #include "tools/gn/label.h"
 #include "tools/gn/label_ptr.h"
@@ -19,11 +18,14 @@
 class Loader;
 class ParseNode;
 
-class Builder : public base::RefCountedThreadSafe<Builder> {
+// The builder assembles the dependency tree. It is not threadsafe and runs on
+// the main thread only. See also BuilderRecord.
+class Builder {
  public:
   typedef base::Callback<void(const BuilderRecord*)> ResolvedCallback;
 
   explicit Builder(Loader* loader);
+  ~Builder();
 
   // The resolved callback is called whenever a target has been resolved. This
   // will be executed only on the main thread.
@@ -53,10 +55,6 @@
   bool CheckForBadItems(Err* err) const;
 
  private:
-  friend class base::RefCountedThreadSafe<Builder>;
-
-  virtual ~Builder();
-
   bool TargetDefined(BuilderRecord* record, Err* err);
   bool ConfigDefined(BuilderRecord* record, Err* err);
   bool ToolchainDefined(BuilderRecord* record, Err* err);
diff --git a/tools/gn/builder_unittest.cc b/tools/gn/builder_unittest.cc
index 96fad7d..616e76a 100644
--- a/tools/gn/builder_unittest.cc
+++ b/tools/gn/builder_unittest.cc
@@ -67,7 +67,7 @@
  public:
   BuilderTest()
       : loader_(new MockLoader),
-        builder_(new Builder(loader_.get())),
+        builder_(loader_.get()),
         settings_(&build_settings_, std::string()),
         scope_(&settings_) {
     build_settings_.SetBuildDir(SourceDir("//out/"));
@@ -78,13 +78,13 @@
   Toolchain* DefineToolchain() {
     Toolchain* tc = new Toolchain(&settings_, settings_.toolchain_label());
     TestWithScope::SetupToolchain(tc);
-    builder_->ItemDefined(std::unique_ptr<Item>(tc));
+    builder_.ItemDefined(std::unique_ptr<Item>(tc));
     return tc;
   }
 
  protected:
   scoped_refptr<MockLoader> loader_;
-  scoped_refptr<Builder> builder_;
+  Builder builder_;
   BuildSettings build_settings_;
   Settings settings_;
   Scope scope_;
@@ -107,7 +107,7 @@
   Target* a = new Target(&settings_, a_label);
   a->public_deps().push_back(LabelTargetPair(b_label));
   a->set_output_type(Target::EXECUTABLE);
-  builder_->ItemDefined(std::unique_ptr<Item>(a));
+  builder_.ItemDefined(std::unique_ptr<Item>(a));
 
   // Should have requested that B and the toolchain is loaded.
   EXPECT_TRUE(loader_->HasLoadedTwo(SourceFile("//tc/BUILD.gn"),
@@ -116,18 +116,18 @@
   // Define the toolchain.
   DefineToolchain();
   BuilderRecord* toolchain_record =
-      builder_->GetRecord(settings_.toolchain_label());
+      builder_.GetRecord(settings_.toolchain_label());
   ASSERT_TRUE(toolchain_record);
   EXPECT_EQ(BuilderRecord::ITEM_TOOLCHAIN, toolchain_record->type());
 
   // A should be unresolved with an item
-  BuilderRecord* a_record = builder_->GetRecord(a_label);
+  BuilderRecord* a_record = builder_.GetRecord(a_label);
   EXPECT_TRUE(a_record->item());
   EXPECT_FALSE(a_record->resolved());
   EXPECT_FALSE(a_record->can_resolve());
 
   // B should be unresolved, have no item, and no deps.
-  BuilderRecord* b_record = builder_->GetRecord(b_label);
+  BuilderRecord* b_record = builder_.GetRecord(b_label);
   EXPECT_FALSE(b_record->item());
   EXPECT_FALSE(b_record->resolved());
   EXPECT_FALSE(b_record->can_resolve());
@@ -152,7 +152,7 @@
   Target* c = new Target(&settings_, c_label);
   c->set_output_type(Target::STATIC_LIBRARY);
   c->visibility().SetPublic();
-  builder_->ItemDefined(std::unique_ptr<Item>(c));
+  builder_.ItemDefined(std::unique_ptr<Item>(c));
 
   // C only depends on the already-loaded toolchain so we shouldn't have
   // requested anything else.
@@ -163,14 +163,14 @@
   a->public_deps().push_back(LabelTargetPair(c_label));
   b->set_output_type(Target::SHARED_LIBRARY);
   b->visibility().SetPublic();
-  builder_->ItemDefined(std::unique_ptr<Item>(b));
+  builder_.ItemDefined(std::unique_ptr<Item>(b));
 
   // B depends only on the already-loaded C and toolchain so we shouldn't have
   // requested anything else.
   EXPECT_TRUE(loader_->HasLoadedNone());
 
   // All targets should now be resolved.
-  BuilderRecord* c_record = builder_->GetRecord(c_label);
+  BuilderRecord* c_record = builder_.GetRecord(c_label);
   EXPECT_TRUE(a_record->resolved());
   EXPECT_TRUE(b_record->resolved());
   EXPECT_TRUE(c_record->resolved());
@@ -194,7 +194,7 @@
   settings2.set_toolchain_label(toolchain_label2);
   Toolchain* tc2 = new Toolchain(&settings2, toolchain_label2);
   TestWithScope::SetupToolchain(tc2);
-  builder_->ItemDefined(std::unique_ptr<Item>(tc2));
+  builder_.ItemDefined(std::unique_ptr<Item>(tc2));
 
   // Construct a dependency chain: A -> B. A is in the default toolchain, B
   // is not.
@@ -207,20 +207,20 @@
   Target* b = new Target(&settings2, b_label);
   b->visibility().SetPublic();
   b->set_output_type(Target::EXECUTABLE);
-  builder_->ItemDefined(std::unique_ptr<Item>(b));
+  builder_.ItemDefined(std::unique_ptr<Item>(b));
 
   // B should not be marked generated by default.
-  BuilderRecord* b_record = builder_->GetRecord(b_label);
+  BuilderRecord* b_record = builder_.GetRecord(b_label);
   EXPECT_FALSE(b_record->should_generate());
 
   // Define A with a dependency on B.
   Target* a = new Target(&settings_, a_label);
   a->public_deps().push_back(LabelTargetPair(b_label));
   a->set_output_type(Target::EXECUTABLE);
-  builder_->ItemDefined(std::unique_ptr<Item>(a));
+  builder_.ItemDefined(std::unique_ptr<Item>(a));
 
   // A should have the generate bit set since it's in the default toolchain.
-  BuilderRecord* a_record = builder_->GetRecord(a_label);
+  BuilderRecord* a_record = builder_.GetRecord(a_label);
   EXPECT_TRUE(a_record->should_generate());
 
   // It should have gotten pushed to B.
@@ -242,7 +242,7 @@
   // The builder will take ownership of the pointers.
   Config* a = new Config(&settings_, a_label);
   a->configs().push_back(LabelConfigPair(b_label));
-  builder_->ItemDefined(std::unique_ptr<Item>(a));
+  builder_.ItemDefined(std::unique_ptr<Item>(a));
 
   // Should have requested that B is loaded.
   EXPECT_TRUE(loader_->HasLoadedOne(SourceFile("//b/BUILD.gn")));
diff --git a/tools/gn/command_check.cc b/tools/gn/command_check.cc
index 40931a6..467644b 100644
--- a/tools/gn/command_check.cc
+++ b/tools/gn/command_check.cc
@@ -184,7 +184,7 @@
     return 1;
 
   std::vector<const Target*> all_targets =
-      setup->builder()->GetAllResolvedTargets();
+      setup->builder().GetAllResolvedTargets();
 
   bool filtered_by_build_config = false;
   std::vector<const Target*> targets_to_check;
diff --git a/tools/gn/command_gen.cc b/tools/gn/command_gen.cc
index 06b99ca..7de9aed 100644
--- a/tools/gn/command_gen.cc
+++ b/tools/gn/command_gen.cc
@@ -46,32 +46,44 @@
 const char kSwitchJsonIdeScript[] = "json-ide-script";
 const char kSwitchJsonIdeScriptArgs[] = "json-ide-script-args";
 
+// Collects Ninja rules for each toolchain. The lock protectes the rules.
+struct TargetWriteInfo {
+  base::Lock lock;
+  NinjaWriter::PerToolchainRules rules;
+};
+
 // Called on worker thread to write the ninja file.
-void BackgroundDoWrite(const Target* target) {
-  NinjaTargetWriter::RunAndWriteFile(target);
+void BackgroundDoWrite(TargetWriteInfo* write_info, const Target* target) {
+  std::string rule = NinjaTargetWriter::RunAndWriteFile(target);
+  DCHECK(!rule.empty());
+
+  {
+    base::AutoLock lock(write_info->lock);
+    write_info->rules[target->toolchain()].emplace_back(
+        target, std::move(rule));
+  }
+
   g_scheduler->DecrementWorkCount();
 }
 
 // Called on the main thread.
-void ItemResolvedCallback(base::subtle::Atomic32* write_counter,
-                          scoped_refptr<Builder> builder,
+void ItemResolvedCallback(TargetWriteInfo* write_info,
                           const BuilderRecord* record) {
-  base::subtle::NoBarrier_AtomicIncrement(write_counter, 1);
-
   const Item* item = record->item();
   const Target* target = item->AsTarget();
   if (target) {
     g_scheduler->IncrementWorkCount();
-    g_scheduler->ScheduleWork(base::Bind(&BackgroundDoWrite, target));
+    g_scheduler->ScheduleWork(base::Bind(&BackgroundDoWrite,
+                                         write_info, target));
   }
 }
 
 // Returns a pointer to the target with the given file as an output, or null
 // if no targets generate the file. This is brute force since this is an
 // error condition and performance shouldn't matter.
-const Target* FindTargetThatGeneratesFile(const Builder* builder,
+const Target* FindTargetThatGeneratesFile(const Builder& builder,
                                           const SourceFile& file) {
-  std::vector<const Target*> targets = builder->GetAllResolvedTargets();
+  std::vector<const Target*> targets = builder.GetAllResolvedTargets();
   if (targets.empty())
     return nullptr;
 
@@ -87,7 +99,7 @@
 
 // Prints an error that the given file was present as a source or input in
 // the given target(s) but was not generated by any of its dependencies.
-void PrintInvalidGeneratedInput(const Builder* builder,
+void PrintInvalidGeneratedInput(const Builder& builder,
                                 const SourceFile& file,
                                 const std::vector<const Target*>& targets) {
   std::string err;
@@ -167,7 +179,7 @@
 
 bool RunIdeWriter(const std::string& ide,
                   const BuildSettings* build_settings,
-                  Builder* builder,
+                  const Builder& builder,
                   Err* err) {
   const base::CommandLine* command_line =
       base::CommandLine::ForCurrentProcess();
@@ -373,27 +385,36 @@
   if (command_line->HasSwitch(kSwitchCheck))
     setup->set_check_public_headers(true);
 
-  // Cause the load to also generate the ninja files for each target. We wrap
-  // the writing to maintain a counter.
-  base::subtle::Atomic32 write_counter = 0;
-  setup->builder()->set_resolved_callback(
-      base::Bind(&ItemResolvedCallback, &write_counter,
-                 scoped_refptr<Builder>(setup->builder())));
+  // Cause the load to also generate the ninja files for each target.
+  TargetWriteInfo write_info;
+  setup->builder().set_resolved_callback(
+      base::Bind(&ItemResolvedCallback, &write_info));
 
   // Do the actual load. This will also write out the target ninja files.
   if (!setup->Run())
     return 1;
 
+  // Sort the targets in each toolchain according to their label. This makes
+  // the ninja files have deterministic content.
+  for (auto& cur_toolchain : write_info.rules) {
+    std::sort(cur_toolchain.second.begin(), cur_toolchain.second.end(),
+              [](const NinjaWriter::TargetRulePair& a,
+                 const NinjaWriter::TargetRulePair& b) {
+                return a.first->label() < b.first->label();
+              });
+  }
+
   Err err;
   // Write the root ninja files.
   if (!NinjaWriter::RunAndWriteFiles(&setup->build_settings(),
                                      setup->builder(),
+                                     write_info.rules,
                                      &err)) {
     err.PrintToStdout();
     return 1;
   }
 
-  if (!WriteRuntimeDepsFilesIfNecessary(*setup->builder(), &err)) {
+  if (!WriteRuntimeDepsFilesIfNecessary(setup->builder(), &err)) {
     err.PrintToStdout();
     return 1;
   }
@@ -413,8 +434,11 @@
   if (!command_line->HasSwitch(switches::kQuiet)) {
     OutputString("Done. ", DECORATION_GREEN);
 
-    std::string stats = "Wrote " +
-        base::IntToString(static_cast<int>(write_counter)) +
+    size_t targets_collected = 0;
+    for (const auto& rules : write_info.rules)
+      targets_collected += rules.second.size();
+
+    std::string stats = "Made " + base::SizeTToString(targets_collected) +
         " targets from " +
         base::IntToString(
             setup->scheduler().input_file_manager()->GetInputFileCount()) +
diff --git a/tools/gn/command_ls.cc b/tools/gn/command_ls.cc
index f53a727..4d14e0f 100644
--- a/tools/gn/command_ls.cc
+++ b/tools/gn/command_ls.cc
@@ -97,10 +97,10 @@
                    target_matches.begin(), target_matches.end());
   } else if (all_toolchains) {
     // List all resolved targets.
-    matches = setup->builder()->GetAllResolvedTargets();
+    matches = setup->builder().GetAllResolvedTargets();
   } else {
     // List all resolved targets in the default toolchain.
-    for (auto* target : setup->builder()->GetAllResolvedTargets()) {
+    for (auto* target : setup->builder().GetAllResolvedTargets()) {
       if (target->settings()->is_default())
         matches.push_back(target);
     }
diff --git a/tools/gn/command_refs.cc b/tools/gn/command_refs.cc
index 64969b9..184f901 100644
--- a/tools/gn/command_refs.cc
+++ b/tools/gn/command_refs.cc
@@ -33,7 +33,7 @@
 
 // Populates the reverse dependency map for the targets in the Setup.
 void FillDepMap(Setup* setup, DepMap* dep_map) {
-  for (auto* target : setup->builder()->GetAllResolvedTargets()) {
+  for (auto* target : setup->builder().GetAllResolvedTargets()) {
     for (const auto& dep_pair : target->GetDeps(Target::DEPS_ALL))
       dep_map->insert(std::make_pair(dep_pair.ptr, target));
   }
@@ -443,7 +443,7 @@
   // the output, while for normal targets you don't want to see the inputs,
   // only what refers to them.
   std::vector<const Target*> all_targets =
-      setup->builder()->GetAllResolvedTargets();
+      setup->builder().GetAllResolvedTargets();
   UniqueVector<const Target*> explicit_target_matches;
   for (const auto& file : file_matches) {
     GetTargetsContainingFile(setup, all_targets, file, all_toolchains,
diff --git a/tools/gn/commands.cc b/tools/gn/commands.cc
index 492b74f..bf89c95 100644
--- a/tools/gn/commands.cc
+++ b/tools/gn/commands.cc
@@ -56,7 +56,7 @@
 
   std::vector<LabelPattern> pattern_vector;
   pattern_vector.push_back(pattern);
-  FilterTargetsByPatterns(setup->builder()->GetAllResolvedTargets(),
+  FilterTargetsByPatterns(setup->builder().GetAllResolvedTargets(),
                           pattern_vector, matches);
   return true;
 }
@@ -102,7 +102,7 @@
     return true;
   }
 
-  const Item* item = setup->builder()->GetItem(label);
+  const Item* item = setup->builder().GetItem(label);
   if (item) {
     if (const Config* as_config = item->AsConfig())
       config_matches->push_back(as_config);
@@ -391,7 +391,7 @@
     return nullptr;
   }
 
-  const Item* item = setup->builder()->GetItem(label);
+  const Item* item = setup->builder().GetItem(label);
   if (!item) {
     Err(Location(), "Label not found.",
         label.GetUserVisibleName(false) + " not found.").PrintToStdout();
diff --git a/tools/gn/eclipse_writer.cc b/tools/gn/eclipse_writer.cc
index ee91f61..dbc6eb9 100644
--- a/tools/gn/eclipse_writer.cc
+++ b/tools/gn/eclipse_writer.cc
@@ -36,7 +36,7 @@
 }  // namespace
 
 EclipseWriter::EclipseWriter(const BuildSettings* build_settings,
-                             const Builder* builder,
+                             const Builder& builder,
                              std::ostream& out)
     : build_settings_(build_settings), builder_(builder), out_(out) {
   languages_.push_back("C++ Source File");
@@ -52,7 +52,7 @@
 // static
 bool EclipseWriter::RunAndWriteFile(
     const BuildSettings* build_settings,
-    const Builder* builder,
+    const Builder& builder,
     Err* err) {
   base::FilePath file = build_settings->GetFullPath(build_settings->build_dir())
                             .AppendASCII("eclipse-cdt-settings.xml");
@@ -77,7 +77,7 @@
 }
 
 void EclipseWriter::GetAllIncludeDirs() {
-  std::vector<const Target*> targets = builder_->GetAllResolvedTargets();
+  std::vector<const Target*> targets = builder_.GetAllResolvedTargets();
   for (const Target* target : targets) {
     if (!UsesDefaultToolchain(target))
       continue;
@@ -92,7 +92,7 @@
 }
 
 void EclipseWriter::GetAllDefines() {
-  std::vector<const Target*> targets = builder_->GetAllResolvedTargets();
+  std::vector<const Target*> targets = builder_.GetAllResolvedTargets();
   for (const Target* target : targets) {
     if (!UsesDefaultToolchain(target))
       continue;
@@ -116,7 +116,7 @@
 
 bool EclipseWriter::UsesDefaultToolchain(const Target* target) const {
   return target->toolchain()->label() ==
-         builder_->loader()->GetDefaultToolchain();
+         builder_.loader()->GetDefaultToolchain();
 }
 
 void EclipseWriter::WriteCDTSettings() {
diff --git a/tools/gn/eclipse_writer.h b/tools/gn/eclipse_writer.h
index 560b872..e15254f 100644
--- a/tools/gn/eclipse_writer.h
+++ b/tools/gn/eclipse_writer.h
@@ -21,12 +21,12 @@
 class EclipseWriter {
  public:
   static bool RunAndWriteFile(const BuildSettings* build_settings,
-                              const Builder* builder,
+                              const Builder& builder,
                               Err* err);
 
  private:
   EclipseWriter(const BuildSettings* build_settings,
-                const Builder* builder,
+                const Builder& builder,
                 std::ostream& out);
   ~EclipseWriter();
 
@@ -47,7 +47,7 @@
   void WriteCDTSettings();
 
   const BuildSettings* build_settings_;
-  const Builder* builder_;
+  const Builder& builder_;
 
   // The output stream for the settings file.
   std::ostream& out_;
diff --git a/tools/gn/json_project_writer.cc b/tools/gn/json_project_writer.cc
index 272a288..dc375a6 100644
--- a/tools/gn/json_project_writer.cc
+++ b/tools/gn/json_project_writer.cc
@@ -82,7 +82,7 @@
 }
 
 std::string RenderJSON(const BuildSettings* build_settings,
-                       const Builder* builder,
+                       const Builder& builder,
                        std::vector<const Target*>& all_targets) {
   Label default_toolchain_label;
 
@@ -167,7 +167,7 @@
 
 bool JSONProjectWriter::RunAndWriteFiles(
     const BuildSettings* build_settings,
-    const Builder* builder,
+    const Builder& builder,
     const std::string& file_name,
     const std::string& exec_script,
     const std::string& exec_script_extra_args,
@@ -182,7 +182,7 @@
 
   base::FilePath output_path = build_settings->GetFullPath(output_file);
 
-  std::vector<const Target*> all_targets = builder->GetAllResolvedTargets();
+  std::vector<const Target*> all_targets = builder.GetAllResolvedTargets();
   std::vector<const Target*> targets;
   if (!FilterTargets(build_settings, all_targets, &targets, dir_filter_string,
                      err)) {
diff --git a/tools/gn/json_project_writer.h b/tools/gn/json_project_writer.h
index e69dc3a..8c293bf 100644
--- a/tools/gn/json_project_writer.h
+++ b/tools/gn/json_project_writer.h
@@ -14,7 +14,7 @@
 class JSONProjectWriter {
  public:
   static bool RunAndWriteFiles(const BuildSettings* build_setting,
-                               const Builder* builder,
+                               const Builder& builder,
                                const std::string& file_name,
                                const std::string& exec_script,
                                const std::string& exec_script_extra_args,
diff --git a/tools/gn/ninja_build_writer.cc b/tools/gn/ninja_build_writer.cc
index 8ee90b0..a7f56f9 100644
--- a/tools/gn/ninja_build_writer.cc
+++ b/tools/gn/ninja_build_writer.cc
@@ -18,10 +18,12 @@
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 #include "tools/gn/build_settings.h"
+#include "tools/gn/builder.h"
 #include "tools/gn/err.h"
 #include "tools/gn/escape.h"
 #include "tools/gn/filesystem_utils.h"
 #include "tools/gn/input_file_manager.h"
+#include "tools/gn/loader.h"
 #include "tools/gn/ninja_utils.h"
 #include "tools/gn/pool.h"
 #include "tools/gn/scheduler.h"
@@ -127,17 +129,15 @@
 
 NinjaBuildWriter::NinjaBuildWriter(
     const BuildSettings* build_settings,
-    const std::vector<const Settings*>& all_settings,
+    const std::map<const Settings*, const Toolchain*>& used_toolchains,
     const Toolchain* default_toolchain,
     const std::vector<const Target*>& default_toolchain_targets,
-    const std::vector<const Pool*>& all_pools,
     std::ostream& out,
     std::ostream& dep_out)
     : build_settings_(build_settings),
-      all_settings_(all_settings),
+      used_toolchains_(used_toolchains),
       default_toolchain_(default_toolchain),
       default_toolchain_targets_(default_toolchain_targets),
-      all_pools_(all_pools),
       out_(out),
       dep_out_(dep_out),
       path_output_(build_settings->build_dir(),
@@ -157,17 +157,42 @@
 // static
 bool NinjaBuildWriter::RunAndWriteFile(
     const BuildSettings* build_settings,
-    const std::vector<const Settings*>& all_settings,
-    const Toolchain* default_toolchain,
-    const std::vector<const Target*>& default_toolchain_targets,
-    const std::vector<const Pool*>& all_pools,
+    const Builder& builder,
     Err* err) {
   ScopedTrace trace(TraceItem::TRACE_FILE_WRITE, "build.ninja");
 
+  std::vector<const Target*> all_targets = builder.GetAllResolvedTargets();
+  std::map<const Settings*, const Toolchain*> used_toolchains;
+
+  // Find the default toolchain info.
+  Label default_toolchain_label = builder.loader()->GetDefaultToolchain();
+  const Settings* default_toolchain_settings =
+      builder.loader()->GetToolchainSettings(default_toolchain_label);
+  const Toolchain* default_toolchain =
+      builder.GetToolchain(default_toolchain_label);
+
+  // Most targets will be in the default toolchain. Add it at the beginning and
+  // skip adding it to the list every time in the loop.
+  used_toolchains[default_toolchain_settings] = default_toolchain;
+
+  std::vector<const Target*> default_toolchain_targets;
+  default_toolchain_targets.reserve(all_targets.size());
+  for (const Target* target : all_targets) {
+    if (target->settings() == default_toolchain_settings) {
+      default_toolchain_targets.push_back(target);
+      // The default toolchain will already have been added to the used
+      // settings array.
+    } else if (used_toolchains.find(target->settings()) ==
+               used_toolchains.end()) {
+      used_toolchains[target->settings()] =
+          builder.GetToolchain(target->settings()->toolchain_label());
+    }
+  }
+
   std::stringstream file;
   std::stringstream depfile;
-  NinjaBuildWriter gen(build_settings, all_settings, default_toolchain,
-                       default_toolchain_targets, all_pools, file, depfile);
+  NinjaBuildWriter gen(build_settings, used_toolchains, default_toolchain,
+                       default_toolchain_targets, file, depfile);
   if (!gen.Run(err))
     return false;
 
@@ -233,7 +258,18 @@
        << "  depth = " << default_toolchain_->concurrent_links() << std::endl
        << std::endl;
 
-  for (const Pool* pool : all_pools_) {
+  // Compute the pools referenced by all tools of all used toolchains.
+  std::set<const Pool*> used_pools;
+  for (const auto& pair : used_toolchains_) {
+    for (int j = Toolchain::TYPE_NONE + 1; j < Toolchain::TYPE_NUMTYPES; j++) {
+      Toolchain::ToolType tool_type = static_cast<Toolchain::ToolType>(j);
+      const Tool* tool = pair.second->GetTool(tool_type);
+      if (tool && tool->pool().ptr)
+        used_pools.insert(tool->pool().ptr);
+    }
+  }
+
+  for (const Pool* pool : used_pools) {
     std::string pool_name = pool->GetNinjaName(default_toolchain_->label());
     out_ << "pool " << pool_name << std::endl
          << "  depth = " << pool->depth() << std::endl
@@ -242,9 +278,9 @@
 }
 
 void NinjaBuildWriter::WriteSubninjas() {
-  for (auto* elem : all_settings_) {
+  for (const auto& pair : used_toolchains_) {
     out_ << "subninja ";
-    path_output_.WriteFile(out_, GetNinjaFileForToolchain(elem));
+    path_output_.WriteFile(out_, GetNinjaFileForToolchain(pair.first));
     out_ << std::endl;
   }
   out_ << std::endl;
diff --git a/tools/gn/ninja_build_writer.h b/tools/gn/ninja_build_writer.h
index 358ecc6..cf72852 100644
--- a/tools/gn/ninja_build_writer.h
+++ b/tools/gn/ninja_build_writer.h
@@ -6,12 +6,13 @@
 #define TOOLS_GN_NINJA_BUILD_WRITER_H_
 
 #include <iosfwd>
-#include <set>
+#include <map>
 #include <vector>
 
 #include "base/macros.h"
 #include "tools/gn/path_output.h"
 
+class Builder;
 class BuildSettings;
 class Err;
 class Pool;
@@ -24,38 +25,42 @@
 // build itself.
 class NinjaBuildWriter {
  public:
-  static bool RunAndWriteFile(
+  NinjaBuildWriter(
       const BuildSettings* settings,
-      const std::vector<const Settings*>& all_settings,
+      const std::map<const Settings*, const Toolchain*>& used_toolchains,
       const Toolchain* default_toolchain,
       const std::vector<const Target*>& default_toolchain_targets,
-      const std::vector<const Pool*>& all_pools,
-      Err* err);
-
-  NinjaBuildWriter(const BuildSettings* settings,
-                   const std::vector<const Settings*>& all_settings,
-                   const Toolchain* default_toolchain,
-                   const std::vector<const Target*>& default_toolchain_targets,
-                   const std::vector<const Pool*>& all_pools,
-                   std::ostream& out,
-                   std::ostream& dep_out);
+      std::ostream& out,
+      std::ostream& dep_out);
   ~NinjaBuildWriter();
 
+  // The design of this class is that this static factory function takes the
+  // Builder, extracts the relevant information, and passes it to the class
+  // constructor. The class itself doesn't depend on the Builder at all which
+  // makes testing much easier (tests integrating various functions along with
+  // the Builder get very complicated).
+  static bool RunAndWriteFile(
+      const BuildSettings* settings,
+      const Builder& builder,
+      Err* err);
+
   bool Run(Err* err);
 
  private:
   void WriteNinjaRules();
   void WriteAllPools();
   void WriteSubninjas();
-  bool WritePhonyAndAllRules(Err* err);
+  bool WritePhonyAndAllRules(
+      Err* err);
 
   void WritePhonyRule(const Target* target, const std::string& phony_name);
 
   const BuildSettings* build_settings_;
-  std::vector<const Settings*> all_settings_;
+
+  const std::map<const Settings*, const Toolchain*>& used_toolchains_;
   const Toolchain* default_toolchain_;
-  std::vector<const Target*> default_toolchain_targets_;
-  std::vector<const Pool*> all_pools_;
+  const std::vector<const Target*>& default_toolchain_targets_;
+
   std::ostream& out_;
   std::ostream& dep_out_;
   PathOutput path_output_;
diff --git a/tools/gn/ninja_build_writer_unittest.cc b/tools/gn/ninja_build_writer_unittest.cc
index 955eb1c..abb5475 100644
--- a/tools/gn/ninja_build_writer_unittest.cc
+++ b/tools/gn/ninja_build_writer_unittest.cc
@@ -32,47 +32,57 @@
   target_bar.SetToolchain(setup.toolchain());
   ASSERT_TRUE(target_bar.OnResolved(&err));
 
-  Pool swiming_pool(setup.settings(),
-                    Label(SourceDir("//swiming/"), "pool",
-                          SourceDir("//other/"), "toolchain"));
-  swiming_pool.set_depth(42);
+  // Make a secondary toolchain that references a pool.
+  Label other_toolchain_label(SourceDir("//other/"), "toolchain");
+  Pool other_pool(setup.settings(),
+                    Label(SourceDir("//other/"), "pool",
+                          other_toolchain_label.dir(),
+                          other_toolchain_label.name()));
+  other_pool.set_depth(42);
+  Toolchain other_toolchain(setup.settings(), other_toolchain_label);
+  TestWithScope::SetupToolchain(&other_toolchain);
+  other_toolchain.GetTool(Toolchain::TYPE_LINK)->set_pool(
+      LabelPtrPair<Pool>(&other_pool));
+
+  // Settings to go with the other toolchain.
+  Settings other_settings(setup.build_settings(), "toolchain/");
+  other_settings.set_toolchain_label(other_toolchain_label);
+
+  std::map<const Settings*, const Toolchain*> used_toolchains;
+  used_toolchains[setup.settings()] = setup.toolchain();
+  used_toolchains[&other_settings] = &other_toolchain;
+
+  std::vector<const Target*> targets = { &target_foo, &target_bar };
 
   std::ostringstream ninja_out;
   std::ostringstream depfile_out;
-  std::vector<const Settings*> all_settings = {setup.settings()};
-  std::vector<const Target*> targets = {&target_foo, &target_bar};
-  std::vector<const Pool*> all_pools = {&swiming_pool};
-  NinjaBuildWriter writer(setup.build_settings(), all_settings,
-                          setup.toolchain(), targets, all_pools, ninja_out,
-                          depfile_out);
+
+  NinjaBuildWriter writer(setup.build_settings(), used_toolchains,
+                          setup.toolchain(), targets, ninja_out, depfile_out);
   ASSERT_TRUE(writer.Run(&err));
 
   const char expected_rule_gn[] = "rule gn\n";
   const char expected_build_ninja[] =
       "build build.ninja: gn\n"
       "  generator = 1\n"
-      "  depfile = build.ninja.d\n"
-      "\n";
+      "  depfile = build.ninja.d\n";
   const char expected_link_pool[] =
       "pool link_pool\n"
-      "  depth = 0\n"
-      "\n"
-      "pool other_toolchain_swiming_pool\n"
-      "  depth = 42\n"
-      "\n";
+      "  depth = 0\n";
+  const char expected_other_pool[] =
+      "pool other_toolchain_other_pool\n"
+      "  depth = 42\n";
   const char expected_toolchain[] =
-      "subninja toolchain.ninja\n"
-      "\n";
+      "subninja toolchain.ninja\n";
   const char expected_targets[] =
       "build bar: phony obj/bar/bar.stamp\n"
       "build foo$:bar: phony obj/foo/bar.stamp\n"
-      "build bar$:bar: phony obj/bar/bar.stamp\n"
-      "\n";
+      "build bar$:bar: phony obj/bar/bar.stamp\n";
   const char expected_root_target[] =
       "build all: phony $\n"
       "    obj/foo/bar.stamp $\n"
-      "    obj/bar/bar.stamp\n"
-      "\n"
+      "    obj/bar/bar.stamp\n";
+  const char expected_default[] =
       "default all\n";
   std::string out_str = ninja_out.str();
 #define EXPECT_SNIPPET(expected) \
@@ -82,9 +92,11 @@
   EXPECT_SNIPPET(expected_rule_gn);
   EXPECT_SNIPPET(expected_build_ninja);
   EXPECT_SNIPPET(expected_link_pool);
+  EXPECT_SNIPPET(expected_other_pool);
   EXPECT_SNIPPET(expected_toolchain);
   EXPECT_SNIPPET(expected_targets);
   EXPECT_SNIPPET(expected_root_target);
+  EXPECT_SNIPPET(expected_default);
 #undef EXPECT_SNIPPET
 }
 
@@ -109,14 +121,13 @@
   target_bar.SetToolchain(setup.toolchain());
   ASSERT_TRUE(target_bar.OnResolved(&err));
 
+  std::map<const Settings*, const Toolchain*> used_toolchains;
+  used_toolchains[setup.settings()] = setup.toolchain();
+  std::vector<const Target*> targets = { &target_foo, &target_bar };
   std::ostringstream ninja_out;
   std::ostringstream depfile_out;
-  std::vector<const Settings*> all_settings = { setup.settings() };
-  std::vector<const Target*> targets = { &target_foo, &target_bar };
-  std::vector<const Pool*> all_pools;
-  NinjaBuildWriter writer(setup.build_settings(), all_settings,
-                          setup.toolchain(), targets, all_pools, ninja_out,
-                          depfile_out);
+  NinjaBuildWriter writer(setup.build_settings(), used_toolchains,
+                          setup.toolchain(), targets, ninja_out, depfile_out);
   ASSERT_FALSE(writer.Run(&err));
 
   const char expected_help_test[] =
diff --git a/tools/gn/ninja_target_writer.cc b/tools/gn/ninja_target_writer.cc
index 69e9d90..ccb3eb7 100644
--- a/tools/gn/ninja_target_writer.cc
+++ b/tools/gn/ninja_target_writer.cc
@@ -39,50 +39,82 @@
 }
 
 // static
-void NinjaTargetWriter::RunAndWriteFile(const Target* target) {
+std::string NinjaTargetWriter::RunAndWriteFile(const Target* target) {
   const Settings* settings = target->settings();
 
   ScopedTrace trace(TraceItem::TRACE_FILE_WRITE,
                     target->label().GetUserVisibleName(false));
   trace.SetToolchain(settings->toolchain_label());
 
-  base::FilePath ninja_file(settings->build_settings()->GetFullPath(
-      GetNinjaFileForTarget(target)));
-
   if (g_scheduler->verbose_logging())
-    g_scheduler->Log("Writing", FilePathToUTF8(ninja_file));
-
-  base::CreateDirectory(ninja_file.DirName());
+    g_scheduler->Log("Computing", target->label().GetUserVisibleName(true));
 
   // It's ridiculously faster to write to a string and then write that to
   // disk in one operation than to use an fstream here.
-  std::stringstream file;
+  std::stringstream rules;
 
-  // Call out to the correct sub-type of writer.
+  // Call out to the correct sub-type of writer. Binary targets need to be
+  // written to separate files for compiler flag scoping, but other target
+  // types can have their rules coalesced.
+  //
+  // In ninja, if a rule uses a variable (like $include_dirs) it will use
+  // the value set by indenting it under the build line or it takes the value
+  // from the end of the invoking scope (otherwise the current file). It does
+  // not copy the value from what it was when the build line was encountered.
+  // To avoid writing lots of duplicate rules for defines and cflags, etc. on
+  // each source file build line, we use separate .ninja files with the shared
+  // variables set at the top.
+  //
+  // Groups and actions don't use this type of flag, they make unique rules
+  // or write variables scoped under each build line. As a result, they don't
+  // need the separate files.
+  bool needs_file_write = false;
   if (target->output_type() == Target::BUNDLE_DATA) {
-    NinjaBundleDataTargetWriter writer(target, file);
+    NinjaBundleDataTargetWriter writer(target, rules);
     writer.Run();
   } else if (target->output_type() == Target::CREATE_BUNDLE) {
-    NinjaCreateBundleTargetWriter writer(target, file);
+    NinjaCreateBundleTargetWriter writer(target, rules);
     writer.Run();
   } else if (target->output_type() == Target::COPY_FILES) {
-    NinjaCopyTargetWriter writer(target, file);
+    NinjaCopyTargetWriter writer(target, rules);
     writer.Run();
   } else if (target->output_type() == Target::ACTION ||
              target->output_type() == Target::ACTION_FOREACH) {
-    NinjaActionTargetWriter writer(target, file);
+    NinjaActionTargetWriter writer(target, rules);
     writer.Run();
   } else if (target->output_type() == Target::GROUP) {
-    NinjaGroupTargetWriter writer(target, file);
+    NinjaGroupTargetWriter writer(target, rules);
     writer.Run();
   } else if (target->IsBinary()) {
-    NinjaBinaryTargetWriter writer(target, file);
+    needs_file_write = true;
+    NinjaBinaryTargetWriter writer(target, rules);
     writer.Run();
   } else {
     CHECK(0) << "Output type of target not handled.";
   }
 
-  WriteFileIfChanged(ninja_file, file.str(), nullptr);
+  if (needs_file_write) {
+    // Write the ninja file.
+    SourceFile ninja_file = GetNinjaFileForTarget(target);
+    base::FilePath full_ninja_file =
+        settings->build_settings()->GetFullPath(ninja_file);
+    base::CreateDirectory(full_ninja_file.DirName());
+    WriteFileIfChanged(full_ninja_file, rules.str(), nullptr);
+
+    EscapeOptions options;
+    options.mode = ESCAPE_NINJA;
+
+    // Return the subninja command to load the rules file.
+    std::string result = "subninja ";
+    result.append(EscapeString(
+        OutputFile(target->settings()->build_settings(), ninja_file).value(),
+                   options, nullptr));
+    result.push_back('\n');
+    return result;
+  }
+
+  // No separate file required, just return the rules.
+  return rules.str();
 }
 
 void NinjaTargetWriter::WriteEscapedSubstitution(SubstitutionType type) {
diff --git a/tools/gn/ninja_target_writer.h b/tools/gn/ninja_target_writer.h
index 0248b07..72ecbca 100644
--- a/tools/gn/ninja_target_writer.h
+++ b/tools/gn/ninja_target_writer.h
@@ -24,7 +24,14 @@
   NinjaTargetWriter(const Target* target, std::ostream& out);
   virtual ~NinjaTargetWriter();
 
-  static void RunAndWriteFile(const Target* target);
+  // Returns the build line to be written to the toolchain build file.
+  //
+  // Some targets have their rules written to separate files, and some can have
+  // their rules coalesced in the main build file. For the coalesced case, this
+  // function will return the rules as a string. For the separate file case,
+  // the separate ninja file will be written and the return string will be the
+  // subninja command to load that file.
+  static std::string RunAndWriteFile(const Target* target);
 
   virtual void Run() = 0;
 
diff --git a/tools/gn/ninja_toolchain_writer.cc b/tools/gn/ninja_toolchain_writer.cc
index 9b699b8..40653a8 100644
--- a/tools/gn/ninja_toolchain_writer.cc
+++ b/tools/gn/ninja_toolchain_writer.cc
@@ -27,11 +27,9 @@
 NinjaToolchainWriter::NinjaToolchainWriter(
     const Settings* settings,
     const Toolchain* toolchain,
-    const std::vector<const Target*>& targets,
     std::ostream& out)
     : settings_(settings),
       toolchain_(toolchain),
-      targets_(targets),
       out_(out),
       path_output_(settings_->build_settings()->build_dir(),
                    settings_->build_settings()->root_path_utf8(),
@@ -41,16 +39,27 @@
 NinjaToolchainWriter::~NinjaToolchainWriter() {
 }
 
-void NinjaToolchainWriter::Run() {
-  WriteRules();
-  WriteSubninjas();
+void NinjaToolchainWriter::Run(
+    const std::vector<NinjaWriter::TargetRulePair>& rules) {
+  std::string rule_prefix = GetNinjaRulePrefixForToolchain(settings_);
+
+  for (int i = Toolchain::TYPE_NONE + 1; i < Toolchain::TYPE_NUMTYPES; i++) {
+    Toolchain::ToolType tool_type = static_cast<Toolchain::ToolType>(i);
+    const Tool* tool = toolchain_->GetTool(tool_type);
+    if (tool)
+      WriteToolRule(tool_type, tool, rule_prefix);
+  }
+  out_ << std::endl;
+
+  for (const auto& pair : rules)
+    out_ << pair.second;
 }
 
 // static
 bool NinjaToolchainWriter::RunAndWriteFile(
     const Settings* settings,
     const Toolchain* toolchain,
-    const std::vector<const Target*>& targets) {
+    const std::vector<NinjaWriter::TargetRulePair>& rules) {
   base::FilePath ninja_file(settings->build_settings()->GetFullPath(
       GetNinjaFileForToolchain(settings)));
   ScopedTrace trace(TraceItem::TRACE_FILE_WRITE, FilePathToUTF8(ninja_file));
@@ -63,23 +72,11 @@
   if (file.fail())
     return false;
 
-  NinjaToolchainWriter gen(settings, toolchain, targets, file);
-  gen.Run();
+  NinjaToolchainWriter gen(settings, toolchain, file);
+  gen.Run(rules);
   return true;
 }
 
-void NinjaToolchainWriter::WriteRules() {
-  std::string rule_prefix = GetNinjaRulePrefixForToolchain(settings_);
-
-  for (int i = Toolchain::TYPE_NONE + 1; i < Toolchain::TYPE_NUMTYPES; i++) {
-    Toolchain::ToolType tool_type = static_cast<Toolchain::ToolType>(i);
-    const Tool* tool = toolchain_->GetTool(tool_type);
-    if (tool)
-      WriteToolRule(tool_type, tool, rule_prefix);
-  }
-  out_ << std::endl;
-}
-
 void NinjaToolchainWriter::WriteToolRule(const Toolchain::ToolType type,
                                          const Tool* tool,
                                          const std::string& rule_prefix) {
@@ -133,15 +130,3 @@
   SubstitutionWriter::WriteWithNinjaVariables(pattern, options, out_);
   out_ << std::endl;
 }
-
-void NinjaToolchainWriter::WriteSubninjas() {
-  // Write subninja commands for each generated target.
-  for (auto* target : targets_) {
-    OutputFile ninja_file(target->settings()->build_settings(),
-                          GetNinjaFileForTarget(target));
-    out_ << "subninja ";
-    path_output_.WriteFile(out_, ninja_file);
-    out_ << std::endl;
-  }
-  out_ << std::endl;
-}
diff --git a/tools/gn/ninja_toolchain_writer.h b/tools/gn/ninja_toolchain_writer.h
index a1e39c3..9ddde84 100644
--- a/tools/gn/ninja_toolchain_writer.h
+++ b/tools/gn/ninja_toolchain_writer.h
@@ -12,6 +12,7 @@
 
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
+#include "tools/gn/ninja_writer.h"
 #include "tools/gn/path_output.h"
 #include "tools/gn/toolchain.h"
 
@@ -25,20 +26,20 @@
  public:
   // Takes the settings for the toolchain, as well as the list of all targets
   // associated with the toolchain.
-  static bool RunAndWriteFile(const Settings* settings,
-                              const Toolchain* toolchain,
-                              const std::vector<const Target*>& targets);
+  static bool RunAndWriteFile(
+      const Settings* settings,
+      const Toolchain* toolchain,
+      const std::vector<NinjaWriter::TargetRulePair>& rules);
 
  private:
   FRIEND_TEST_ALL_PREFIXES(NinjaToolchainWriter, WriteToolRule);
 
   NinjaToolchainWriter(const Settings* settings,
                        const Toolchain* toolchain,
-                       const std::vector<const Target*>& targets,
                        std::ostream& out);
   ~NinjaToolchainWriter();
 
-  void Run();
+  void Run(const std::vector<NinjaWriter::TargetRulePair>& extra_rules);
 
   void WriteRules();
   void WriteToolRule(Toolchain::ToolType type,
@@ -47,11 +48,9 @@
   void WriteRulePattern(const char* name,
                         const SubstitutionPattern& pattern,
                         const EscapeOptions& options);
-  void WriteSubninjas();
 
   const Settings* settings_;
   const Toolchain* toolchain_;
-  std::vector<const Target*> targets_;
   std::ostream& out_;
   PathOutput path_output_;
 
diff --git a/tools/gn/ninja_toolchain_writer_unittest.cc b/tools/gn/ninja_toolchain_writer_unittest.cc
index fc52f7d..4e9d767 100644
--- a/tools/gn/ninja_toolchain_writer_unittest.cc
+++ b/tools/gn/ninja_toolchain_writer_unittest.cc
@@ -11,14 +11,8 @@
 TEST(NinjaToolchainWriter, WriteToolRule) {
   TestWithScope setup;
 
-  //Target target(setup.settings(), Label(SourceDir("//foo/"), "target"));
-  //target.set_output_type(Target::EXECUTABLE);
-  //target.SetToolchain(setup.toolchain());
-
   std::ostringstream stream;
-
-  NinjaToolchainWriter writer(setup.settings(), setup.toolchain(),
-                              std::vector<const Target*>(), stream);
+  NinjaToolchainWriter writer(setup.settings(), setup.toolchain(), stream);
   writer.WriteToolRule(Toolchain::TYPE_CC,
                        setup.toolchain()->GetTool(Toolchain::TYPE_CC),
                        std::string("prefix_"));
diff --git a/tools/gn/ninja_writer.cc b/tools/gn/ninja_writer.cc
index 22a97ec..4fd5b22 100644
--- a/tools/gn/ninja_writer.cc
+++ b/tools/gn/ninja_writer.cc
@@ -12,118 +12,45 @@
 #include "tools/gn/settings.h"
 #include "tools/gn/target.h"
 
-NinjaWriter::NinjaWriter(const BuildSettings* build_settings,
-                         Builder* builder)
-    : build_settings_(build_settings),
-      builder_(builder) {
+NinjaWriter::NinjaWriter(const Builder& builder)
+    : builder_(builder) {
 }
 
 NinjaWriter::~NinjaWriter() {
 }
 
 // static
-bool NinjaWriter::RunAndWriteFiles(const BuildSettings* build_settings,
-                                   Builder* builder,
-                                   Err* err) {
-  NinjaWriter writer(build_settings, builder);
-
-  std::vector<const Settings*> all_settings;
-  std::vector<const Target*> default_targets;
-  std::vector<const Pool*> all_pools;
-  if (!writer.WriteToolchains(&all_settings, &default_targets, &all_pools, err))
-    return false;
-  return writer.WriteRootBuildfiles(all_settings, default_targets, all_pools,
-                                    err);
-}
-
-// static
-bool NinjaWriter::RunAndWriteToolchainFiles(
+bool NinjaWriter::RunAndWriteFiles(
     const BuildSettings* build_settings,
-    Builder* builder,
-    std::vector<const Settings*>* all_settings,
+    const Builder& builder,
+    const PerToolchainRules& per_toolchain_rules,
     Err* err) {
-  NinjaWriter writer(build_settings, builder);
-  std::vector<const Target*> default_targets;
-  std::vector<const Pool*> all_pools;
-  return writer.WriteToolchains(all_settings, &default_targets, &all_pools,
-                                err);
+  NinjaWriter writer(builder);
+
+  if (!writer.WriteToolchains(per_toolchain_rules, err))
+    return false;
+  return NinjaBuildWriter::RunAndWriteFile(build_settings, builder, err);
 }
 
-bool NinjaWriter::WriteToolchains(std::vector<const Settings*>* all_settings,
-                                  std::vector<const Target*>* default_targets,
-                                  std::vector<const Pool*>* all_pools,
+bool NinjaWriter::WriteToolchains(const PerToolchainRules& per_toolchain_rules,
                                   Err* err) {
-  // Categorize all targets by toolchain.
-  typedef std::map<Label, std::vector<const Target*> > CategorizedMap;
-  CategorizedMap categorized;
-
-  std::vector<const BuilderRecord*> all_records = builder_->GetAllRecords();
-  for (auto* all_record : all_records) {
-    if (all_record->type() == BuilderRecord::ITEM_TARGET &&
-        all_record->should_generate()) {
-      categorized[all_record->label().GetToolchainLabel()].push_back(
-          all_record->item()->AsTarget());
-      }
-  }
-  if (categorized.empty()) {
+  if (per_toolchain_rules.empty()) {
     Err(Location(), "No targets.",
         "I could not find any targets to write, so I'm doing nothing.")
         .PrintToStdout();
     return false;
   }
 
-  for (auto& i : categorized) {
-    // Sort targets so that they are in a deterministic order.
-    std::sort(i.second.begin(), i.second.end(),
-              [](const Target* a, const Target* b) {
-                return a->label() < b->label();
-              });
-  }
-
-  Label default_label = builder_->loader()->GetDefaultToolchain();
-
-  // Write out the toolchain buildfiles, and also accumulate the set of
-  // all settings and find the list of targets in the default toolchain.
-  UniqueVector<const Pool*> pools;
-  for (const auto& i : categorized) {
+  for (const auto& i : per_toolchain_rules) {
+    const Toolchain* toolchain = i.first;
     const Settings* settings =
-        builder_->loader()->GetToolchainSettings(i.first);
-    const Toolchain* toolchain = builder_->GetToolchain(i.first);
-
-    all_settings->push_back(settings);
-
+        builder_.loader()->GetToolchainSettings(toolchain->label());
     if (!NinjaToolchainWriter::RunAndWriteFile(settings, toolchain, i.second)) {
       Err(Location(),
           "Couldn't open toolchain buildfile(s) for writing").PrintToStdout();
       return false;
     }
-
-    for (int j = Toolchain::TYPE_NONE + 1; j < Toolchain::TYPE_NUMTYPES; j++) {
-      Toolchain::ToolType tool_type = static_cast<Toolchain::ToolType>(j);
-      const Tool* tool = toolchain->GetTool(tool_type);
-      if (tool && tool->pool().ptr)
-        pools.push_back(tool->pool().ptr);
-    }
   }
 
-  *all_pools = pools.vector();
-  *default_targets = categorized[default_label];
   return true;
 }
-
-bool NinjaWriter::WriteRootBuildfiles(
-    const std::vector<const Settings*>& all_settings,
-    const std::vector<const Target*>& default_targets,
-    const std::vector<const Pool*>& all_pools,
-    Err* err) {
-  // All Settings objects should have the same default toolchain, and there
-  // should always be at least one settings object in the build.
-  CHECK(!all_settings.empty());
-  const Toolchain* default_toolchain =
-      builder_->GetToolchain(all_settings[0]->default_toolchain_label());
-
-  // Write the root buildfile.
-  return NinjaBuildWriter::RunAndWriteFile(build_settings_, all_settings,
-                                           default_toolchain, default_targets,
-                                           all_pools, err);
-}
diff --git a/tools/gn/ninja_writer.h b/tools/gn/ninja_writer.h
index ea8fc47..4bcbf36 100644
--- a/tools/gn/ninja_writer.h
+++ b/tools/gn/ninja_writer.h
@@ -5,6 +5,7 @@
 #ifndef TOOLS_GN_NINJA_WRITER_H_
 #define TOOLS_GN_NINJA_WRITER_H_
 
+#include <map>
 #include <set>
 #include <string>
 #include <vector>
@@ -17,37 +18,34 @@
 class Pool;
 class Settings;
 class Target;
+class Toolchain;
 
 class NinjaWriter {
  public:
-  // On failure will populate |err| and will return false.
-  static bool RunAndWriteFiles(const BuildSettings* build_settings,
-                               Builder* builder,
-                               Err* err);
+  // Combines a target and the computed build rule for it.
+  using TargetRulePair = std::pair<const Target*, std::string>;
 
-  // Writes only the toolchain.ninja files, skipping the root buildfile. The
-  // settings for the files written will be added to the vector.
-  static bool RunAndWriteToolchainFiles(
+  // Associates the build rules with each toolchain.
+  using PerToolchainRules =
+      std::map<const Toolchain*, std::vector<TargetRulePair>>;
+
+  // On failure will populate |err| and will return false.  The map contains
+  // the per-toolchain set of rules collected to write to the toolchain build
+  // files.
+  static bool RunAndWriteFiles(
       const BuildSettings* build_settings,
-      Builder* builder,
-      std::vector<const Settings*>* all_settings,
+      const Builder& builder,
+      const PerToolchainRules& per_toolchain_rules,
       Err* err);
 
  private:
-  NinjaWriter(const BuildSettings* build_settings, Builder* builder);
+  NinjaWriter(const Builder& builder);
   ~NinjaWriter();
 
-  bool WriteToolchains(std::vector<const Settings*>* all_settings,
-                       std::vector<const Target*>* default_targets,
-                       std::vector<const Pool*>* all_pools,
+  bool WriteToolchains(const PerToolchainRules& per_toolchain_rules,
                        Err* err);
-  bool WriteRootBuildfiles(const std::vector<const Settings*>& all_settings,
-                           const std::vector<const Target*>& default_targets,
-                           const std::vector<const Pool*>& all_pools,
-                           Err* err);
 
-  const BuildSettings* build_settings_;
-  Builder* builder_;
+  const Builder& builder_;
 
   DISALLOW_COPY_AND_ASSIGN(NinjaWriter);
 };
diff --git a/tools/gn/qt_creator_writer.cc b/tools/gn/qt_creator_writer.cc
index 96f247f..db0c9b8 100644
--- a/tools/gn/qt_creator_writer.cc
+++ b/tools/gn/qt_creator_writer.cc
@@ -32,7 +32,7 @@
 
 // static
 bool QtCreatorWriter::RunAndWriteFile(const BuildSettings* build_settings,
-                                      const Builder* builder,
+                                      const Builder& builder,
                                       Err* err,
                                       const std::string& root_target) {
   base::FilePath project_dir =
@@ -60,7 +60,7 @@
 }
 
 QtCreatorWriter::QtCreatorWriter(const BuildSettings* build_settings,
-                                 const Builder* builder,
+                                 const Builder& builder,
                                  const base::FilePath& project_prefix,
                                  const std::string& root_target_name)
     : build_settings_(build_settings),
@@ -81,7 +81,7 @@
 }
 
 bool QtCreatorWriter::DiscoverTargets() {
-  auto all_targets = builder_->GetAllResolvedTargets();
+  auto all_targets = builder_.GetAllResolvedTargets();
 
   if (root_target_name_.empty()) {
     targets_ = std::set<const Target*>(all_targets.begin(), all_targets.end());
@@ -160,7 +160,7 @@
 
   for (const Target* target : targets_) {
     if (target->toolchain()->label() !=
-        builder_->loader()->GetDefaultToolchain())
+        builder_.loader()->GetDefaultToolchain())
       continue;
     HandleTarget(target);
   }
diff --git a/tools/gn/qt_creator_writer.h b/tools/gn/qt_creator_writer.h
index cea75fd..cb37404 100644
--- a/tools/gn/qt_creator_writer.h
+++ b/tools/gn/qt_creator_writer.h
@@ -19,13 +19,13 @@
 class QtCreatorWriter {
  public:
   static bool RunAndWriteFile(const BuildSettings* build_settings,
-                              const Builder* builder,
+                              const Builder& builder,
                               Err* err,
                               const std::string& root_target);
 
  private:
   QtCreatorWriter(const BuildSettings* build_settings,
-                  const Builder* builder,
+                  const Builder& builder,
                   const base::FilePath& project_prefix,
                   const std::string& root_target_name);
   ~QtCreatorWriter();
@@ -41,7 +41,7 @@
                     const std::set<std::string>& items);
 
   const BuildSettings* build_settings_;
-  const Builder* builder_;
+  const Builder& builder_;
   base::FilePath project_prefix_;
   std::string root_target_name_;
   std::set<const Target*> targets_;
diff --git a/tools/gn/settings.h b/tools/gn/settings.h
index 9192551..91f6410 100644
--- a/tools/gn/settings.h
+++ b/tools/gn/settings.h
@@ -39,6 +39,10 @@
 
   const BuildSettings* build_settings() const { return build_settings_; }
 
+  // The actual Toolchain object pointer is not available on the settings
+  // object because it might not be resolved yet. Code running after the
+  // load is complete can ask the Builder for the Toolchain corresponding to
+  // this label.
   const Label& toolchain_label() const { return toolchain_label_; }
   void set_toolchain_label(const Label& l) { toolchain_label_ = l; }
 
diff --git a/tools/gn/setup.cc b/tools/gn/setup.cc
index 60aeaa8..490204d 100644
--- a/tools/gn/setup.cc
+++ b/tools/gn/setup.cc
@@ -142,11 +142,14 @@
 // Called on any thread. Post the item to the builder on the main thread.
 void ItemDefinedCallback(
     scoped_refptr<base::SingleThreadTaskRunner> task_runner,
-    scoped_refptr<Builder> builder,
+    Builder* builder_call_on_main_thread_only,
     std::unique_ptr<Item> item) {
   DCHECK(item);
-  task_runner->PostTask(FROM_HERE, base::Bind(&Builder::ItemDefined, builder,
-                                              base::Passed(&item)));
+  task_runner->PostTask(
+      FROM_HERE,
+      base::Bind(&Builder::ItemDefined,
+                 base::Unretained(builder_call_on_main_thread_only),
+                 base::Passed(&item)));
 }
 
 void DecrementWorkCount() {
@@ -251,7 +254,7 @@
 Setup::Setup()
     : build_settings_(),
       loader_(new LoaderImpl(&build_settings_)),
-      builder_(new Builder(loader_.get())),
+      builder_(loader_.get()),
       root_build_file_("//BUILD.gn"),
       check_public_headers_(false),
       dotfile_settings_(&build_settings_, std::string()),
@@ -259,7 +262,7 @@
       fill_arguments_(true) {
   dotfile_settings_.set_toolchain_label(Label());
   build_settings_.set_item_defined_callback(
-      base::Bind(&ItemDefinedCallback, scheduler_.task_runner(), builder_));
+      base::Bind(&ItemDefinedCallback, scheduler_.task_runner(), &builder_));
 
   loader_->set_complete_callback(base::Bind(&DecrementWorkCount));
   // The scheduler's task runner wasn't created when the Loader was created, so
@@ -329,7 +332,7 @@
 bool Setup::RunPostMessageLoop() {
   Err err;
   if (build_settings_.check_for_bad_items()) {
-    if (!builder_->CheckForBadItems(&err)) {
+    if (!builder_.CheckForBadItems(&err)) {
       err.PrintToStdout();
       return false;
     }
@@ -347,7 +350,7 @@
   }
 
   if (check_public_headers_) {
-    std::vector<const Target*> all_targets = builder_->GetAllResolvedTargets();
+    std::vector<const Target*> all_targets = builder_.GetAllResolvedTargets();
     std::vector<const Target*> to_check;
     if (check_patterns()) {
       commands::FilterTargetsByPatterns(all_targets, *check_patterns(),
diff --git a/tools/gn/setup.h b/tools/gn/setup.h
index 22e6557..45335bf 100644
--- a/tools/gn/setup.h
+++ b/tools/gn/setup.h
@@ -82,7 +82,7 @@
   }
 
   BuildSettings& build_settings() { return build_settings_; }
-  Builder* builder() { return builder_.get(); }
+  Builder& builder() { return builder_; }
   LoaderImpl* loader() { return loader_.get(); }
 
   // Name of the file in the root build directory that contains the build
@@ -128,7 +128,7 @@
 
   BuildSettings build_settings_;
   scoped_refptr<LoaderImpl> loader_;
-  scoped_refptr<Builder> builder_;
+  Builder builder_;
 
   SourceFile root_build_file_;
 
diff --git a/tools/gn/toolchain.h b/tools/gn/toolchain.h
index 76858de..4ccfdc8 100644
--- a/tools/gn/toolchain.h
+++ b/tools/gn/toolchain.h
@@ -66,6 +66,15 @@
   static const char* kToolCopyBundleData;
   static const char* kToolCompileXCAssets;
 
+  // The Settings of an Item is always the context in which the Item was
+  // defined. For a toolchain this is confusing because this is NOT the
+  // settings object that applies to the things in the toolchain.
+  //
+  // To get the Settings object corresponding to objects loaded in the context
+  // of this toolchain (probably what you want instead), see
+  // 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);
   ~Toolchain() override;
 
diff --git a/tools/gn/visual_studio_writer.cc b/tools/gn/visual_studio_writer.cc
index 401782f..fb6d923 100644
--- a/tools/gn/visual_studio_writer.cc
+++ b/tools/gn/visual_studio_writer.cc
@@ -166,12 +166,12 @@
 }
 
 bool FilterTargets(const BuildSettings* build_settings,
-                   Builder* builder,
+                   const Builder& builder,
                    const std::string& dir_filters,
                    std::vector<const Target*>* targets,
                    Err* err) {
   if (dir_filters.empty()) {
-    *targets = builder->GetAllResolvedTargets();
+    *targets = builder.GetAllResolvedTargets();
     return true;
   }
 
@@ -180,7 +180,7 @@
                                           err))
     return false;
 
-  commands::FilterTargetsByPatterns(builder->GetAllResolvedTargets(), filters,
+  commands::FilterTargetsByPatterns(builder.GetAllResolvedTargets(), filters,
                                     targets);
 
   std::set<Label> labels;
@@ -269,7 +269,7 @@
 
 // static
 bool VisualStudioWriter::RunAndWriteFiles(const BuildSettings* build_settings,
-                                          Builder* builder,
+                                          const Builder& builder,
                                           Version version,
                                           const std::string& sln_name,
                                           const std::string& dir_filters,
diff --git a/tools/gn/visual_studio_writer.h b/tools/gn/visual_studio_writer.h
index 99c5370..0a8fcb4 100644
--- a/tools/gn/visual_studio_writer.h
+++ b/tools/gn/visual_studio_writer.h
@@ -37,7 +37,7 @@
   // of generated projects. Only matching targets will be included to the
   // solution. On failure will populate |err| and will return false.
   static bool RunAndWriteFiles(const BuildSettings* build_settings,
-                               Builder* builder,
+                               const Builder& builder,
                                Version version,
                                const std::string& sln_name,
                                const std::string& dir_filters,
diff --git a/tools/gn/xcode_writer.cc b/tools/gn/xcode_writer.cc
index d60f4f7..e6b66d5 100644
--- a/tools/gn/xcode_writer.cc
+++ b/tools/gn/xcode_writer.cc
@@ -145,7 +145,7 @@
                                    const std::string& ninja_extra_args,
                                    const std::string& dir_filters_string,
                                    const BuildSettings* build_settings,
-                                   Builder* builder,
+                                   const Builder& builder,
                                    Err* err) {
   const XcodeWriter::TargetOsType target_os =
       GetTargetOs(build_settings->build_args());
@@ -180,7 +180,7 @@
     config_name = config_name.substr(0, separator);
 
   std::vector<const Target*> targets;
-  std::vector<const Target*> all_targets = builder->GetAllResolvedTargets();
+  std::vector<const Target*> all_targets = builder.GetAllResolvedTargets();
   if (!XcodeWriter::FilterTargets(build_settings, all_targets,
                                   dir_filters_string, &targets, err)) {
     return false;
diff --git a/tools/gn/xcode_writer.h b/tools/gn/xcode_writer.h
index 8c4e304..5148788 100644
--- a/tools/gn/xcode_writer.h
+++ b/tools/gn/xcode_writer.h
@@ -48,7 +48,7 @@
                                const std::string& ninja_extra_args,
                                const std::string& dir_filters_string,
                                const BuildSettings* build_settings,
-                               Builder* builder,
+                               const Builder& builder,
                                Err* err);
 
  private: