Add initial prototype for the GN meta-buildsystem.

This is currently not hooked into the build. To build, add a reference to the
gn.gyp file to build/all.gyp

R=darin@chromium.org, scottmg@chromium.org

Review URL: https://codereview.chromium.org/21114002

Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: c88bd8f2c08838c6730b946dc4a50d4386ef43f9
diff --git a/tools/gn/BUILD.gn b/tools/gn/BUILD.gn
new file mode 100644
index 0000000..ccb5411
--- /dev/null
+++ b/tools/gn/BUILD.gn
@@ -0,0 +1,153 @@
+static_library("gn_lib") {

+  sources = [

+    "build_settings.cc",

+    "build_settings.h",

+    "command_desc.cc",

+    "command_desc.h",

+    "command_gen.cc",

+    "command_gen.h",

+    "command.cc",

+    "config.cc",

+    "config.h",

+    "config_values.h",

+    "config_values_extractors.cc",

+    "config_values_extractors.h",

+    "config_values_generator.cc",

+    "config_values_generator.h",

+    "err.cc",

+    "err.h",

+    "escape.cc",

+    "escape.h",

+    "file_template.cc",

+    "file_template.h",

+    "filesystem_utils.cc",

+    "filesystem_utils.h",

+    "functions.cc",

+    "functions.h",

+    "functions_target.cc",

+    "function_exec_script.cc",

+    "function_process_file_template.cc",

+    "function_read_file.cc",

+    "function_set_default_toolchain.cc",

+    "function_template.cc",

+    "function_toolchain.cc",

+    "function_write_file.cc",

+    "import_manager.cc",

+    "import_manager.h",

+    "input_conversion.cc",

+    "input_conversion.h",

+    "input_file.cc",

+    "input_file.h",

+    "input_file_manager.cc",

+    "input_file_manager.h",

+    "item.cc",

+    "item.h",

+    "item_node.cc",

+    "item_node.h",

+    "item_tree.cc",

+    "item_tree.h",

+    "label.cc",

+    "label.h",

+    "location.h",

+    "ninja_build_writer.cc",

+    "ninja_build_writer.h",

+    "ninja_helper.cc",

+    "ninja_helper.h",

+    "ninja_target_writer.cc",

+    "ninja_target_writer.h",

+    "ninja_toolchain_writer.cc",

+    "ninja_toolchain_writer.h",

+    "ninja_writer.cc",

+    "ninja_writer.h",

+    "operators.cc",

+    "operators.h",

+    "output_file.h",

+    "parse_tree.cc",

+    "parse_tree.h",

+    "parser.cc",

+    "parser.h",

+    "path_output.cc",

+    "path_output.h",

+    "pattern.cc",

+    "pattern.h",

+    "scheduler.cc",

+    "scheduler.h",

+    "scope.cc",

+    "scope.h",

+    "scope_per_file_provider.cc",

+    "scope_per_file_provider.h",

+    "settings.cc",

+    "settings.h",

+    "setup.cc",

+    "setup.h",

+    "source_dir.cc",

+    "source_dir.h",

+    "source_file.cc",

+    "source_file.h",

+    "standard_out.cc",

+    "standard_out.h",

+    "string_utils.cc",

+    "string_utils.h",

+    "target.cc",

+    "target.h",

+    "target_generator.cc",

+    "target_generator.h",

+    "target_manager.cc",

+    "target_manager.h",

+    "token.cc",

+    "token.h",

+    "tokenizer.cc",

+    "tokenizer.h",

+    "toolchain.cc",

+    "toolchain.h",

+    "toolchain_manager.cc",

+    "toolchain_manager.h",

+    "value.cc",

+    "value.h",

+    "value_extractors.cc",

+    "value_extractors.h",

+  ]

+  deps = [

+    "//base",

+    "//base/third_party/dynamic_annotations",

+  ]

+}

+

+executable("gn") {

+  sources = [

+    "gn_main.cc",

+  ]

+  deps = [

+    ":gn_lib",

+  ]

+}

+

+test("gn_unittests") {

+  sources = [

+    "escape_unittest.cc",

+    "file_template_unittest.cc",

+    "filesystem_utils_unittest.cc",

+    "input_conversion_unittest.cc",

+    "label_unittest.cc",

+    "ninja_helper_unittest.cc",

+    "parser_unittest.cc",

+    "path_output_unittest.cc",

+    "pattern_unittest.cc",

+    "source_dir_unittest.cc",

+    "string_utils_unittest.cc",

+    "target_generator_unittest.cc",

+    "target_manager_unittest.cc",

+    "tokenizer_unittest.cc",

+  ]

+  deps = [

+    ":gn_lib",

+    "//base:run_all_unittests",

+    "//base:test_support_base",

+    "//testing:gtest",

+  ]

+}

+

+executable("generate_test_gn_data") {

+  sources = [ "generate_test_gn_data.cc" ]

+  deps = [ "//base" ]

+}

diff --git a/tools/gn/OWNERS b/tools/gn/OWNERS
new file mode 100644
index 0000000..06fefbf
--- /dev/null
+++ b/tools/gn/OWNERS
@@ -0,0 +1 @@
+brettw@chromium.org
diff --git a/tools/gn/README.txt b/tools/gn/README.txt
new file mode 100644
index 0000000..0a637bf
--- /dev/null
+++ b/tools/gn/README.txt
@@ -0,0 +1,7 @@
+GN "Generate Ninja"
+
+This tool is an experimental metabuildsystem. It is not currently in a state
+where it is ready for public consumption.
+
+It is not currently used in the build and there are currently no plans to
+replace GYP.
diff --git a/tools/gn/build_settings.cc b/tools/gn/build_settings.cc
new file mode 100644
index 0000000..09b4c99
--- /dev/null
+++ b/tools/gn/build_settings.cc
@@ -0,0 +1,44 @@
+// Copyright (c) 2013 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/build_settings.h"
+
+#include "tools/gn/filesystem_utils.h"
+
+BuildSettings::BuildSettings()
+    : item_tree_(),
+      target_manager_(this),
+      toolchain_manager_(this) {
+}
+
+BuildSettings::~BuildSettings() {
+}
+
+void BuildSettings::SetSecondarySourcePath(const SourceDir& d) {
+  secondary_source_path_ = GetFullPath(d);
+}
+
+void BuildSettings::SetBuildDir(const SourceDir& d) {
+  build_dir_ = d;
+  build_to_source_dir_string_ = InvertDir(d);
+}
+
+base::FilePath BuildSettings::GetFullPath(const SourceFile& file) const {
+  return file.Resolve(root_path_);
+}
+
+base::FilePath BuildSettings::GetFullPath(const SourceDir& dir) const {
+  return dir.Resolve(root_path_);
+}
+
+base::FilePath BuildSettings::GetFullPathSecondary(
+    const SourceFile& file) const {
+  return file.Resolve(secondary_source_path_);
+}
+
+base::FilePath BuildSettings::GetFullPathSecondary(
+    const SourceDir& dir) const {
+  return dir.Resolve(secondary_source_path_);
+}
+
diff --git a/tools/gn/build_settings.h b/tools/gn/build_settings.h
new file mode 100644
index 0000000..fe04266
--- /dev/null
+++ b/tools/gn/build_settings.h
@@ -0,0 +1,110 @@
+// Copyright (c) 2013 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.
+
+#ifndef TOOLS_GN_BUILD_SETTINGS_H_
+#define TOOLS_GN_BUILD_SETTINGS_H_
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "tools/gn/item_tree.h"
+#include "tools/gn/scope.h"
+#include "tools/gn/source_dir.h"
+#include "tools/gn/source_file.h"
+#include "tools/gn/target_manager.h"
+#include "tools/gn/toolchain_manager.h"
+
+// Settings for one build, which is one toplevel output directory. There
+// may be multiple Settings objects that refer to this, one for each toolchain.
+class BuildSettings {
+ public:
+  typedef base::Callback<void(const Target*)> TargetResolvedCallback;
+
+  BuildSettings();
+  ~BuildSettings();
+
+  // Absolute path of the source root on the local system. Everything is
+  // relative to this.
+  const base::FilePath& root_path() const { return root_path_; }
+  void set_root_path(const base::FilePath& r) { root_path_ = r; }
+
+  // When nonempty, specifies a parallel directory higherarchy in which to
+  // search for buildfiles if they're not found in the root higherarchy. This
+  // allows us to keep buildfiles in a separate tree during development.
+  const base::FilePath& secondary_source_path() const {
+    return secondary_source_path_;
+  }
+  void SetSecondarySourcePath(const SourceDir& d);
+
+  // Path of the python executable to run scripts with.
+  base::FilePath python_path() const { return python_path_; }
+  void set_python_path(const base::FilePath& p) { python_path_ = p; }
+
+  const SourceFile& build_config_file() const { return build_config_file_; }
+  void set_build_config_file(const SourceFile& f) { build_config_file_ = f; }
+
+  // The build directory is the root of all output files. The default toolchain
+  // files go into here, and non-default toolchains will have separate
+  // toolchain-specific root directories inside this.
+  const SourceDir& build_dir() const { return build_dir_; }
+  void SetBuildDir(const SourceDir& dir);
+
+  // The inverse of relative_build_dir, ending with a separator.
+  // Example: relative_build_dir_ = "out/Debug/" this will be "../../"
+  const std::string& build_to_source_dir_string() const {
+    return build_to_source_dir_string_;
+  }
+
+  // These accessors do not return const objects since the resulting objects
+  // are threadsafe. In this setting, we use constness primarily to ensure
+  // that the Settings object is used in a threadsafe manner. Although this
+  // violates the concept of logical constness, that's less important in our
+  // application, and actually implementing this in a way that preserves
+  // logical constness is cumbersome.
+  ItemTree& item_tree() const { return item_tree_; }
+  TargetManager& target_manager() const { return target_manager_; }
+  ToolchainManager& toolchain_manager() const { return toolchain_manager_; }
+
+  // Returns the full absolute OS path cooresponding to the given file in the
+  // root source tree.
+  base::FilePath GetFullPath(const SourceFile& file) const;
+  base::FilePath GetFullPath(const SourceDir& dir) const;
+
+  // Returns the absolute OS path inside the secondary source path. Will return
+  // an empty FilePath if the secondary source path is empty. When loading a
+  // buildfile, the GetFullPath should always be consulted first.
+  base::FilePath GetFullPathSecondary(const SourceFile& file) const;
+  base::FilePath GetFullPathSecondary(const SourceDir& dir) const;
+
+  // This is the callback to execute when a target is marked resolved. If we
+  // don't need to do anything, this will be null. When a target is resolved,
+  // this callback should be posted to the scheduler pool so the work is
+  // distributed properly.
+  const TargetResolvedCallback& target_resolved_callback() const {
+    return target_resolved_callback_;
+  }
+  void set_target_resolved_callback(const TargetResolvedCallback& cb) {
+    target_resolved_callback_ = cb;
+  }
+
+ private:
+  base::FilePath root_path_;
+  base::FilePath secondary_source_path_;
+  base::FilePath python_path_;
+
+  SourceFile build_config_file_;
+  SourceDir build_dir_;
+  std::string build_to_source_dir_string_;
+
+  TargetResolvedCallback target_resolved_callback_;
+
+  // See getters above.
+  mutable ItemTree item_tree_;
+  mutable TargetManager target_manager_;
+  mutable ToolchainManager toolchain_manager_;
+
+  DISALLOW_COPY_AND_ASSIGN(BuildSettings);
+};
+
+#endif  // TOOLS_GN_BUILD_SETTINGS_H_
diff --git a/tools/gn/command_desc.cc b/tools/gn/command_desc.cc
new file mode 100644
index 0000000..bf91776
--- /dev/null
+++ b/tools/gn/command_desc.cc
@@ -0,0 +1,201 @@
+// Copyright (c) 2013 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 <algorithm>
+#include <set>
+
+#include "tools/gn/commands.h"
+#include "tools/gn/config.h"
+#include "tools/gn/item.h"
+#include "tools/gn/item_node.h"
+#include "tools/gn/label.h"
+#include "tools/gn/setup.h"
+#include "tools/gn/standard_out.h"
+#include "tools/gn/target.h"
+
+namespace {
+
+struct CompareTargetLabel {
+  bool operator()(const Target* a, const Target* b) const {
+    return a->label() < b->label();
+  }
+};
+
+const Target* GetTargetForDesc(const std::vector<std::string>& args) {
+  // Deliberately leaked to avoid expensive process teardown.
+  Setup* setup = new Setup;
+  if (!setup->DoSetup())
+    return NULL;
+
+  // FIXME(brettw): set the output dir to be a sandbox one to avoid polluting
+  // the real output dir with files written by the build scripts.
+
+  // Do the actual load. This will also write out the target ninja files.
+  if (!setup->Run())
+    return NULL;
+
+  // Need to resolve the label after we know the default toolchain.
+  // TODO(brettw) find the current directory and resolve the input label
+  // relative to that.
+  Label default_toolchain = setup->build_settings().toolchain_manager()
+      .GetDefaultToolchainUnlocked();
+  Value arg_value(NULL, args[0]);
+  Err err;
+  Label label = Label::Resolve(SourceDir(), default_toolchain, arg_value, &err);
+  if (err.has_error()) {
+    err.PrintToStdout();
+    return NULL;
+  }
+
+  ItemNode* node;
+  {
+    base::AutoLock lock(setup->build_settings().item_tree().lock());
+    node = setup->build_settings().item_tree().GetExistingNodeLocked(label);
+  }
+  if (!node) {
+    Err(Location(), "",
+        "I don't know about this \"" + label.GetUserVisibleName(false) +
+        "\"").PrintToStdout();
+    return NULL;
+  }
+
+  const Target* target = node->item()->AsTarget();
+  if (!target) {
+    Err(Location(), "Not a target.",
+        "The \"" + label.GetUserVisibleName(false) + "\" thing\n"
+        "is not a target. Somebody should probably implement this command for "
+        "other\nitem types.");
+    return NULL;
+  }
+
+  return target;
+}
+
+void RecursiveCollectDeps(const Target* target, std::set<Label>* result) {
+  if (result->find(target->label()) != result->end())
+    return;  // Already did this target.
+  result->insert(target->label());
+
+  const std::vector<const Target*>& deps = target->deps();
+  for (size_t i = 0; i < deps.size(); i++)
+    RecursiveCollectDeps(deps[i], result);
+}
+
+// Prints dependencies of the given target (not the target itself).
+void RecursivePrintDeps(const Target* target,
+                        const Label& default_toolchain,
+                        int indent_level) {
+  std::vector<const Target*> sorted_deps = target->deps();
+  std::sort(sorted_deps.begin(), sorted_deps.end(), CompareTargetLabel());
+
+  std::string indent(indent_level * 2, ' ');
+  for (size_t i = 0; i < sorted_deps.size(); i++) {
+    OutputString(indent +
+        sorted_deps[i]->label().GetUserVisibleName(default_toolchain) + "\n");
+    RecursivePrintDeps(sorted_deps[i], default_toolchain, indent_level + 1);
+  }
+}
+
+}  // namespace
+
+int RunDescCommand(const std::vector<std::string>& args) {
+  if (args.size() != 1) {
+    Err(Location(), "You're holding it wrong.",
+        "Usage: \"gn desc <target_name>\"").PrintToStdout();
+    return NULL;
+  }
+
+  const Target* target = GetTargetForDesc(args);
+  if (!target)
+    return 1;
+
+  // Generally we only want to display toolchains on labels when the toolchain
+  // is different than the default one for this target (which we always print
+  // in the header).
+  Label target_toolchain = target->label().GetToolchainLabel();
+
+  // Header.
+  std::string title_target =
+      "Target: " + target->label().GetUserVisibleName(false);
+  std::string title_toolchain =
+      "Toolchain: " + target_toolchain.GetUserVisibleName(false);
+  OutputString(title_target + "\n", DECORATION_YELLOW);
+  OutputString(title_toolchain + "\n", DECORATION_YELLOW);
+  OutputString(std::string(
+      std::max(title_target.size(), title_toolchain.size()), '=') + "\n");
+
+  OutputString("Sources:\n");
+  const Target::FileList& sources = target->sources();
+  for (size_t i = 0; i < sources.size(); i++)
+    OutputString("  " + sources[i].value() + "\n");
+
+  // Configs (don't sort since the order determines how things are processed).
+  OutputString("\nConfigs:\n");
+  const std::vector<const Config*>& configs = target->configs();
+  for (size_t i = 0; i < configs.size(); i++) {
+    OutputString("  " +
+        configs[i]->label().GetUserVisibleName(target_toolchain) + "\n");
+  }
+
+  // Deps. Sorted for convenience. Sort the labels rather than the strings so
+  // that "//foo:bar" comes before "//foo/third_party:bar".
+  OutputString("\nDirect dependencies:\n"
+               "(Use \"gn deps\" or \"gn tree\" to display recursive deps.)\n");
+  const std::vector<const Target*>& deps = target->deps();
+  std::vector<Label> sorted_deps;
+  for (size_t i = 0; i < deps.size(); i++)
+    sorted_deps.push_back(deps[i]->label());
+  std::sort(sorted_deps.begin(), sorted_deps.end());
+  for (size_t i = 0; i < sorted_deps.size(); i++) {
+    OutputString("  " + sorted_deps[i].GetUserVisibleName(target_toolchain) +
+                 "\n");
+  }
+  return 0;
+}
+
+int RunDepsCommand(const std::vector<std::string>& args) {
+  if (args.size() != 1) {
+    Err(Location(), "You're holding it wrong.",
+        "Usage: \"gn deps <target_name>\"").PrintToStdout();
+    return NULL;
+  }
+
+  const Target* target = GetTargetForDesc(args);
+  if (!target)
+    return 1;
+
+  // Generally we only want to display toolchains on labels when the toolchain
+  // is different than the default one for this target (which we always print
+  // in the header).
+  Label target_toolchain = target->label().GetToolchainLabel();
+
+  std::set<Label> all_deps;
+  RecursiveCollectDeps(target, &all_deps);
+
+  OutputString("Recursive dependencies of " +
+               target->label().GetUserVisibleName(true) + "\n",
+               DECORATION_YELLOW);
+
+  for (std::set<Label>::iterator i = all_deps.begin();
+       i != all_deps.end(); ++i)
+    OutputString("  " + i->GetUserVisibleName(target_toolchain) + "\n");
+  return 0;
+}
+
+int RunTreeCommand(const std::vector<std::string>& args) {
+  if (args.size() != 1) {
+    Err(Location(), "You're holding it wrong.",
+        "Usage: \"gn tree <target_name>\"").PrintToStdout();
+    return NULL;
+  }
+
+  const Target* target = GetTargetForDesc(args);
+  if (!target)
+    return 1;
+
+  OutputString(target->label().GetUserVisibleName(false) + "\n");
+  RecursivePrintDeps(target, target->label().GetToolchainLabel(), 1);
+
+  return 0;
+}
diff --git a/tools/gn/command_gen.cc b/tools/gn/command_gen.cc
new file mode 100644
index 0000000..ab64a8d
--- /dev/null
+++ b/tools/gn/command_gen.cc
@@ -0,0 +1,66 @@
+// Copyright (c) 2013 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 "base/bind.h"
+#include "base/command_line.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/time/time.h"
+#include "tools/gn/build_settings.h"
+#include "tools/gn/commands.h"
+#include "tools/gn/ninja_target_writer.h"
+#include "tools/gn/ninja_writer.h"
+#include "tools/gn/scheduler.h"
+#include "tools/gn/setup.h"
+#include "tools/gn/standard_out.h"
+
+namespace {
+
+// Suppress output on success.
+const char kSwitchQuiet[] = "q";
+
+}  // namespace
+
+int RunGenCommand(const std::vector<std::string>& args) {
+  base::TimeTicks begin_time = base::TimeTicks::Now();
+
+  // Deliberately leaked to avoid expensive process teardown.
+  Setup* setup = new Setup;
+  if (!setup->DoSetup())
+    return 1;
+
+  // Cause the load to also generate the ninja files for each target.
+  setup->build_settings().set_target_resolved_callback(
+      base::Bind(&NinjaTargetWriter::RunAndWriteFile));
+
+  // Do the actual load. This will also write out the target ninja files.
+  if (!setup->Run())
+    return 1;
+
+  // Write the root ninja files.
+  if (!NinjaWriter::RunAndWriteFiles(&setup->build_settings())) {
+    Err(Location(),
+        "Couldn't open root buildfile(s) for writing").PrintToStdout();
+    return 1;
+  }
+
+  base::TimeTicks end_time = base::TimeTicks::Now();
+
+  if (!CommandLine::ForCurrentProcess()->HasSwitch(kSwitchQuiet)) {
+    OutputString("Done. ", DECORATION_GREEN);
+
+    // TODO(brettw) get the number of targets without getting the entire list.
+    std::vector<const Target*> all_targets;
+    setup->build_settings().target_manager().GetAllTargets(&all_targets);
+    std::string stats = "Generated " +
+        base::IntToString(static_cast<int>(all_targets.size())) +
+        " targets from " +
+        base::IntToString(
+            setup->scheduler().input_file_manager()->GetInputFileCount()) +
+        " files in " +
+        base::IntToString((end_time - begin_time).InMilliseconds()) + "ms\n";
+    OutputString(stats);
+  }
+
+  return 0;
+}
diff --git a/tools/gn/commands.h b/tools/gn/commands.h
new file mode 100644
index 0000000..1d4449c
--- /dev/null
+++ b/tools/gn/commands.h
@@ -0,0 +1,18 @@
+// Copyright (c) 2013 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.
+
+#ifndef TOOLS_GN_COMMANDS_H_
+#define TOOLS_GN_COMMANDS_H_
+
+#include <string>
+#include <vector>
+
+// The different commands we have, returns the value we should return from
+// main().
+int RunDepsCommand(const std::vector<std::string>& args);
+int RunDescCommand(const std::vector<std::string>& args);
+int RunGenCommand(const std::vector<std::string>& args);
+int RunTreeCommand(const std::vector<std::string>& args);
+
+#endif  // TOOLS_GN_COMMANDS_H_
diff --git a/tools/gn/config.cc b/tools/gn/config.cc
new file mode 100644
index 0000000..1175d7f
--- /dev/null
+++ b/tools/gn/config.cc
@@ -0,0 +1,78 @@
+// Copyright (c) 2013 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/config.h"
+
+#include "tools/gn/err.h"
+#include "tools/gn/input_file_manager.h"
+#include "tools/gn/item_node.h"
+#include "tools/gn/item_tree.h"
+#include "tools/gn/scheduler.h"
+
+Config::Config(const Label& label) : Item(label) {
+}
+
+Config::~Config() {
+}
+
+Config* Config::AsConfig() {
+  return this;
+}
+
+const Config* Config::AsConfig() const {
+  return this;
+}
+
+// static
+Config* Config::GetConfig(const Settings* settings,
+                          const LocationRange& specified_from_here,
+                          const Label& label,
+                          Item* dep_from,
+                          Err* err) {
+  DCHECK(!label.is_null());
+
+  ItemTree* tree = &settings->build_settings()->item_tree();
+  base::AutoLock lock(tree->lock());
+
+  ItemNode* node = tree->GetExistingNodeLocked(label);
+  Config* config = NULL;
+  if (!node) {
+    config = new Config(label);
+    node = new ItemNode(config);
+    tree->AddNodeLocked(node);
+
+    // Only schedule loading the given target if somebody is depending on it
+    // (and we optimize by not re-asking it to run the current file).
+    // Otherwise, we're probably generating it right now.
+    if (dep_from && dep_from->label().dir() != label.dir()) {
+      settings->build_settings()->toolchain_manager().ScheduleInvocationLocked(
+          specified_from_here, label.GetToolchainLabel(), label.dir(),
+          err);
+    }
+  } else if ((config = node->item()->AsConfig())) {
+    // Previously saw this item as a config.
+
+    // If we have no dep_from, we're generating it. In this case, it had better
+    // not already be generated.
+    if (!dep_from && node->state() != ItemNode::REFERENCED) {
+      *err = Err(specified_from_here, "Duplicate config definition.",
+          "You already told me about a config with this name.");
+      return NULL;
+    }
+  } else {
+    // Previously saw this thing as a non-config.
+    *err = Err(specified_from_here,
+               "Config name already used.",
+               "Previously you specified a " +
+               node->item()->GetItemTypeName() + " with this name instead.");
+    return NULL;
+  }
+
+  // Keep a record of the guy asking us for this dependency. We know if
+  // somebody is adding a dependency, that guy it himself not resolved.
+  if (dep_from && node->state() != ItemNode::RESOLVED)
+    tree->GetExistingNodeLocked(dep_from->label())->AddDependency(node);
+
+  return config;
+}
diff --git a/tools/gn/config.h b/tools/gn/config.h
new file mode 100644
index 0000000..0ee9b7b
--- /dev/null
+++ b/tools/gn/config.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2013 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.
+
+#ifndef TOOLS_GN_CONFIG_H_
+#define TOOLS_GN_CONFIG_H_
+
+#include "base/compiler_specific.h"
+#include "tools/gn/config_values.h"
+#include "tools/gn/item.h"
+
+class Err;
+class ItemTree;
+class LocationRange;
+class Settings;
+
+// Represents a named config in the dependency graph.
+class Config : public Item {
+ public:
+  Config(const Label& label);
+  virtual ~Config();
+
+  virtual Config* AsConfig() OVERRIDE;
+  virtual const Config* AsConfig() const OVERRIDE;
+
+  ConfigValues& config_values() { return config_values_; }
+  const ConfigValues& config_values() const { return config_values_; }
+
+  // Gets or creates a config.
+  //
+  // This is like the TargetManager is for Targets, but Configs are so much
+  // simpler that this one function is all we need.
+  static Config* GetConfig(const Settings* settings,
+                           const LocationRange& specified_from_here,
+                           const Label& label,
+                           Item* dep_from,
+                           Err* err);
+
+ private:
+  ConfigValues config_values_;
+
+  DISALLOW_COPY_AND_ASSIGN(Config);
+};
+
+#endif  // TOOLS_GN_CONFIG_H_
diff --git a/tools/gn/config_values.cc b/tools/gn/config_values.cc
new file mode 100644
index 0000000..53466df
--- /dev/null
+++ b/tools/gn/config_values.cc
@@ -0,0 +1,11 @@
+// Copyright (c) 2013 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/config_values.h"
+
+ConfigValues::ConfigValues() {
+}
+
+ConfigValues::~ConfigValues() {
+}
diff --git a/tools/gn/config_values.h b/tools/gn/config_values.h
new file mode 100644
index 0000000..e7c1712
--- /dev/null
+++ b/tools/gn/config_values.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2013 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.
+
+#ifndef TOOLS_GN_CONFIG_VALUES_H_
+#define TOOLS_GN_CONFIG_VALUES_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "tools/gn/source_dir.h"
+
+// Holds the values (includes, defines, compiler flags, etc.) for a given
+// config or target.
+class ConfigValues {
+ public:
+  ConfigValues();
+  ~ConfigValues();
+
+  const std::vector<SourceDir>& includes() const { return includes_; }
+  void swap_in_includes(std::vector<SourceDir>* lo) { includes_.swap(*lo); }
+
+  const std::vector<std::string>& defines() const { return defines_; }
+  void swap_in_defines(std::vector<std::string>* d) { defines_.swap(*d); }
+
+  const std::vector<std::string>& cflags() const { return cflags_; }
+  void swap_in_cflags(std::vector<std::string>* lo) { cflags_.swap(*lo); }
+
+  const std::vector<std::string>& cflags_c() const { return cflags_c_; }
+  void swap_in_cflags_c(std::vector<std::string>* lo) { cflags_c_.swap(*lo); }
+
+  const std::vector<std::string>& cflags_cc() const { return cflags_cc_; }
+  void swap_in_cflags_cc(std::vector<std::string>* lo) { cflags_cc_.swap(*lo); }
+
+  const std::vector<std::string>& ldflags() const { return ldflags_; }
+  void swap_in_ldflags(std::vector<std::string>* lo) { ldflags_.swap(*lo); }
+
+ private:
+  std::vector<SourceDir> includes_;
+  std::vector<std::string> defines_;
+  std::vector<std::string> cflags_;
+  std::vector<std::string> cflags_c_;
+  std::vector<std::string> cflags_cc_;
+  std::vector<std::string> ldflags_;
+
+  DISALLOW_COPY_AND_ASSIGN(ConfigValues);
+};
+
+#endif  // TOOLS_GN_CONFIG_VALUES_H_
diff --git a/tools/gn/config_values_extractors.cc b/tools/gn/config_values_extractors.cc
new file mode 100644
index 0000000..96b514a
--- /dev/null
+++ b/tools/gn/config_values_extractors.cc
@@ -0,0 +1,22 @@
+// Copyright (c) 2013 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/config_values_extractors.h"
+
+namespace {
+
+struct StringWriter {
+  void operator()(const std::string& s, std::ostream& out) const {
+    out << " " << s;
+  }
+};
+
+}  // namespace
+
+void RecursiveTargetConfigStringsToStream(
+    const Target* target,
+    const std::vector<std::string>& (ConfigValues::* getter)() const,
+    std::ostream& out) {
+  RecursiveTargetConfigToStream(target, getter, StringWriter(), out);
+}
diff --git a/tools/gn/config_values_extractors.h b/tools/gn/config_values_extractors.h
new file mode 100644
index 0000000..0eaa2c9
--- /dev/null
+++ b/tools/gn/config_values_extractors.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2013 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.
+
+#ifndef TOOLS_GN_CONFIG_VALUES_EXTRACTORS_H_
+#define TOOLS_GN_CONFIG_VALUES_EXTRACTORS_H_
+
+#include <ostream>
+#include <string>
+#include <vector>
+
+#include "tools/gn/config.h"
+#include "tools/gn/config_values.h"
+#include "tools/gn/target.h"
+
+template<typename T, class Writer>
+inline void ConfigValuesToStream(
+    const ConfigValues& values,
+    const std::vector<T>& (ConfigValues::* getter)() const,
+    const Writer& writer,
+    std::ostream& out) {
+  const std::vector<T>& v = (values.*getter)();
+  for (size_t i = 0; i < v.size(); i++)
+    writer(v[i], out);
+};
+
+template<typename T, class Writer>
+inline void RecursiveTargetConfigToStream(
+    const Target* target,
+    const std::vector<T>& (ConfigValues::* getter)() const,
+    const Writer& writer,
+    std::ostream& out) {
+  // Write all configs in reverse order (to get oldest first, which will look
+  // more normal in the output).
+  for (int i = static_cast<int>(target->configs().size() - 1); i >= 0; i--) {
+    ConfigValuesToStream(target->configs()[i]->config_values(), getter,
+                         writer, out);
+  }
+
+  // Last write from the config from the Target itself, if any.
+  ConfigValuesToStream(target->config_values(), getter, writer, out);
+}
+
+// Writes the values out as strings with no transformation.
+void RecursiveTargetConfigStringsToStream(
+    const Target* target,
+    const std::vector<std::string>& (ConfigValues::* getter)() const,
+    std::ostream& out);
+
+#endif  // TOOLS_GN_CONFIG_VALUES_EXTRACTORS_H_
diff --git a/tools/gn/config_values_generator.cc b/tools/gn/config_values_generator.cc
new file mode 100644
index 0000000..34c3f67
--- /dev/null
+++ b/tools/gn/config_values_generator.cc
@@ -0,0 +1,89 @@
+// Copyright (c) 2013 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/config_values_generator.h"
+
+#include "tools/gn/config_values.h"
+#include "tools/gn/scope.h"
+#include "tools/gn/value.h"
+#include "tools/gn/value_extractors.h"
+
+namespace {
+
+void GetStringList(
+    const Scope* scope,
+    const char* var_name,
+    ConfigValues* config_values,
+    void (ConfigValues::* swapper_inner)(std::vector<std::string>*),
+    Err* err) {
+  const Value* value = scope->GetValue(var_name);
+  if (!value)
+    return;  // No value, empty input and succeed.
+
+  std::vector<std::string> result;
+  ExtractListOfStringValues(*value, &result, err);
+  (config_values->*swapper_inner)(&result);
+}
+
+}  // namespace
+
+ConfigValuesGenerator::ConfigValuesGenerator(ConfigValues* dest_values,
+                                             const Scope* scope,
+                                             const Token& function_token,
+                                             const SourceDir& input_dir,
+                                             Err* err)
+    : config_values_(dest_values),
+      scope_(scope),
+      function_token_(function_token),
+      input_dir_(input_dir),
+      err_(err) {
+}
+
+ConfigValuesGenerator::~ConfigValuesGenerator() {
+}
+
+void ConfigValuesGenerator::Run() {
+  FillDefines();
+  FillIncludes();
+  FillCflags();
+  FillCflags_C();
+  FillCflags_CC();
+  FillLdflags();
+}
+
+void ConfigValuesGenerator::FillDefines() {
+  GetStringList(scope_, "defines", config_values_,
+                &ConfigValues::swap_in_defines, err_);
+}
+
+void ConfigValuesGenerator::FillIncludes() {
+  const Value* value = scope_->GetValue("includes");
+  if (!value)
+    return;  // No value, empty input and succeed.
+
+  std::vector<SourceDir> includes;
+  if (!ExtractListOfRelativeDirs(*value, input_dir_, &includes, err_))
+    return;
+  config_values_->swap_in_includes(&includes);
+}
+
+void ConfigValuesGenerator::FillCflags() {
+  GetStringList(scope_, "cflags", config_values_,
+                &ConfigValues::swap_in_cflags, err_);
+}
+
+void ConfigValuesGenerator::FillCflags_C() {
+  GetStringList(scope_, "cflags_c", config_values_,
+                &ConfigValues::swap_in_cflags_c, err_);
+}
+
+void ConfigValuesGenerator::FillCflags_CC() {
+  GetStringList(scope_, "cflags_cc", config_values_,
+                &ConfigValues::swap_in_cflags_cc, err_);
+}
+
+void ConfigValuesGenerator::FillLdflags() {
+  GetStringList(scope_, "ldflags", config_values_,
+                &ConfigValues::swap_in_ldflags, err_);
+}
diff --git a/tools/gn/config_values_generator.h b/tools/gn/config_values_generator.h
new file mode 100644
index 0000000..79d9067
--- /dev/null
+++ b/tools/gn/config_values_generator.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2013 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.
+
+#ifndef TOOLS_GN_CONFIG_VALUES_GENERATOR_H_
+#define TOOLS_GN_CONFIG_VALUES_GENERATOR_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "tools/gn/source_dir.h"
+
+class ConfigValues;
+class Err;
+class Scope;
+class Token;
+
+class ConfigValuesGenerator {
+ public:
+  ConfigValuesGenerator(ConfigValues* dest_values,
+                        const Scope* scope,
+                        const Token& function_token,
+                        const SourceDir& input_dir,
+                        Err* err);
+  ~ConfigValuesGenerator();
+
+  // Sets the error passed to the constructor on failure.
+  void Run();
+
+ private:
+  void FillDefines();
+  void FillIncludes();
+  void FillCflags();
+  void FillCflags_C();
+  void FillCflags_CC();
+  void FillLdflags();
+
+  ConfigValues* config_values_;
+  const Scope* scope_;
+  const Token& function_token_;
+  const SourceDir input_dir_;
+  Err* err_;
+
+  DISALLOW_COPY_AND_ASSIGN(ConfigValuesGenerator);
+};
+
+#endif  // TOOLS_GN_CONFIG_VALUES_GENERATOR_H_
diff --git a/tools/gn/err.cc b/tools/gn/err.cc
new file mode 100644
index 0000000..068ea6d
--- /dev/null
+++ b/tools/gn/err.cc
@@ -0,0 +1,196 @@
+// Copyright (c) 2013 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/err.h"
+
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "tools/gn/filesystem_utils.h"
+#include "tools/gn/input_file.h"
+#include "tools/gn/parse_tree.h"
+#include "tools/gn/standard_out.h"
+#include "tools/gn/tokenizer.h"
+#include "tools/gn/value.h"
+
+namespace {
+
+std::string GetNthLine(const base::StringPiece& data, int n) {
+  size_t line_off = Tokenizer::ByteOffsetOfNthLine(data, n);
+  size_t end = line_off + 1;
+  while (end < data.size() && !Tokenizer::IsNewline(data, end))
+    end++;
+  return data.substr(line_off, end - line_off).as_string();
+}
+
+void FillRangeOnLine(const LocationRange& range, int line_number,
+                     std::string* line) {
+  // Only bother if the range's begin or end overlaps the line. If the entire
+  // line is highlighted as a result of this range, it's not very helpful.
+  if (range.begin().line_number() != line_number &&
+      range.end().line_number() != line_number)
+    return;
+
+  // Watch out, the char offsets in the location are 1-based, so we have to
+  // subtract 1.
+  int begin_char;
+  if (range.begin().line_number() < line_number)
+    begin_char = 0;
+  else
+    begin_char = range.begin().char_offset() - 1;
+
+  int end_char;
+  if (range.end().line_number() > line_number)
+    end_char = line->size();  // Ending is non-inclusive.
+  else
+    end_char = range.end().char_offset() - 1;
+
+  CHECK(end_char >= begin_char);
+  CHECK(begin_char >= 0 && begin_char <= static_cast<int>(line->size()));
+  CHECK(end_char >= 0 && end_char <= static_cast<int>(line->size()));
+  for (int i = begin_char; i < end_char; i++)
+    line->at(i) = '-';
+}
+
+// The line length is used to clip the maximum length of the markers we'll
+// make if the error spans more than one line (like unterminated literals).
+void OutputHighlighedPosition(const Location& location,
+                              const Err::RangeList& ranges,
+                              size_t line_length) {
+  // Make a buffer of the line in spaces.
+  std::string highlight;
+  highlight.resize(line_length);
+  for (size_t i = 0; i < line_length; i++)
+    highlight[i] = ' ';
+
+  // Highlight all the ranges on the line.
+  for (size_t i = 0; i < ranges.size(); i++)
+    FillRangeOnLine(ranges[i], location.line_number(), &highlight);
+
+  // Allow the marker to be one past the end of the line for marking the end.
+  highlight.push_back(' ');
+  CHECK(location.char_offset() - 1 >= 0 &&
+        location.char_offset() - 1 < static_cast<int>(highlight.size()));
+  highlight[location.char_offset() - 1] = '^';
+
+  // Trim unused spaces from end of line.
+  while (!highlight.empty() && highlight[highlight.size() - 1] == ' ')
+    highlight.resize(highlight.size() - 1);
+
+  highlight += "\n";
+  OutputString(highlight, DECORATION_BLUE);
+}
+
+}  // namespace
+
+Err::Err() : has_error_(false) {
+}
+
+Err::Err(const Location& location,
+         const std::string& msg,
+         const std::string& help)
+    : has_error_(true),
+      location_(location),
+      message_(msg),
+      help_text_(help) {
+}
+
+Err::Err(const LocationRange& range,
+         const std::string& msg,
+         const std::string& help)
+    : has_error_(true),
+      location_(range.begin()),
+      message_(msg),
+      help_text_(help) {
+  ranges_.push_back(range);
+}
+
+Err::Err(const Token& token,
+         const std::string& msg,
+         const std::string& help)
+    : has_error_(true),
+      location_(token.location()),
+      message_(msg),
+      help_text_(help) {
+  ranges_.push_back(token.range());
+}
+
+Err::Err(const ParseNode* node,
+         const std::string& msg,
+         const std::string& help_text)
+    : has_error_(true),
+      message_(msg),
+      help_text_(help_text) {
+  // Node will be null in certain tests.
+  if (node) {
+    LocationRange range = node->GetRange();
+    location_ = range.begin();
+    ranges_.push_back(range);
+  }
+}
+
+Err::Err(const Value& value,
+         const std::string msg,
+         const std::string& help_text)
+    : has_error_(true),
+      message_(msg),
+      help_text_(help_text) {
+  if (value.origin()) {
+    LocationRange range = value.origin()->GetRange();
+    location_ = range.begin();
+    ranges_.push_back(range);
+  }
+}
+
+Err::~Err() {
+}
+
+void Err::PrintToStdout() const {
+  InternalPrintToStdout(false);
+}
+
+void Err::AppendSubErr(const Err& err) {
+  sub_errs_.push_back(err);
+}
+
+void Err::InternalPrintToStdout(bool is_sub_err) const {
+  DCHECK(has_error_);
+
+  if (!is_sub_err)
+    OutputString("ERROR ", DECORATION_RED);
+
+  // File name and location.
+  const InputFile* input_file = location_.file();
+  std::string loc_str;
+  if (input_file) {
+    std::string path8;
+    path8.assign(input_file->name().value());
+
+    if (is_sub_err)
+      loc_str = "See ";
+    else
+      loc_str = "at ";
+    loc_str += path8 + ": " +
+        base::IntToString(location_.line_number()) + ":" +
+        base::IntToString(location_.char_offset()) + ": ";
+  }
+  OutputString(loc_str + message_ + "\n");
+
+  // Quoted line.
+  if (input_file) {
+    std::string line = GetNthLine(input_file->contents(),
+                                  location_.line_number());
+    if (!ContainsOnlyWhitespaceASCII(line)) {
+      OutputString(line + "\n", DECORATION_BOLD);
+      OutputHighlighedPosition(location_, ranges_, line.size());
+    }
+  }
+
+  // Optional help text.
+  if (!help_text_.empty())
+    OutputString(help_text_ + "\n");
+
+  // Sub errors.
+  for (size_t i = 0; i < sub_errs_.size(); i++)
+    sub_errs_[i].InternalPrintToStdout(true);
+}
diff --git a/tools/gn/err.h b/tools/gn/err.h
new file mode 100644
index 0000000..3e077e9
--- /dev/null
+++ b/tools/gn/err.h
@@ -0,0 +1,85 @@
+// Copyright (c) 2013 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.
+
+#ifndef TOOLS_GN_ERR_H_
+#define TOOLS_GN_ERR_H_
+
+#include <string>
+#include <vector>
+
+#include "tools/gn/location.h"
+#include "tools/gn/token.h"
+
+class ParseNode;
+class Value;
+
+// Result of doing some operation. Check has_error() to see if an error
+// occurred.
+//
+// An error has a location and a message. Below that, is some optional help
+// text to go with the annotation of the location.
+//
+// An error can also have sub-errors which are additionally printed out
+// below. They can provide additional context.
+class Err {
+ public:
+  typedef std::vector<LocationRange> RangeList;
+
+  // Indicates no error.
+  Err();
+
+  // Error at a single point.
+  Err(const Location& location,
+      const std::string& msg,
+      const std::string& help = std::string());
+
+  // Error at a given range.
+  Err(const LocationRange& range,
+      const std::string& msg,
+      const std::string& help = std::string());
+
+  // Error at a given token.
+  Err(const Token& token,
+      const std::string& msg,
+      const std::string& help_text = std::string());
+
+  // Error at a given node.
+  Err(const ParseNode* node,
+      const std::string& msg,
+      const std::string& help_text = std::string());
+
+  // Error at a given value.
+  Err(const Value& value,
+      const std::string msg,
+      const std::string& help_text = std::string());
+
+  ~Err();
+
+  bool has_error() const { return has_error_; }
+  const Location& location() const { return location_; }
+  const std::string& message() const { return message_; }
+  const std::string& help_text() const { return help_text_; }
+
+  void AppendRange(const LocationRange& range) { ranges_.push_back(range); }
+  const RangeList& ranges() const { return ranges_; }
+
+  void AppendSubErr(const Err& err);
+
+  void PrintToStdout() const;
+
+ private:
+  void InternalPrintToStdout(bool is_sub_err) const;
+
+  bool has_error_;
+  Location location_;
+
+  std::vector<LocationRange> ranges_;
+
+  std::string message_;
+  std::string help_text_;
+
+  std::vector<Err> sub_errs_;
+};
+
+#endif  // TOOLS_GN_ERR_H_
diff --git a/tools/gn/escape.cc b/tools/gn/escape.cc
new file mode 100644
index 0000000..5fc7b6f
--- /dev/null
+++ b/tools/gn/escape.cc
@@ -0,0 +1,77 @@
+// Copyright (c) 2013 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/escape.h"
+
+#include "base/containers/stack_container.h"
+
+namespace {
+
+template<typename DestString>
+void EscapeStringToString(const base::StringPiece& str,
+                          const EscapeOptions& options,
+                          DestString* dest) {
+  bool used_quotes = false;
+
+  for (size_t i = 0; i < str.size(); i++) {
+    if (str[i] == '$' && options.mode == ESCAPE_NINJA) {
+      // Escape dollars signs since ninja treats these specially.
+      dest->push_back('$');
+      dest->push_back('$');
+    } else if (str[i] == '"' && options.mode == ESCAPE_SHELL) {
+      // Escape quotes with backslashes for the command-line (Ninja doesn't
+      // care).
+      dest->push_back('\\');
+      dest->push_back('"');
+    } else if (str[i] == ' ') {
+      if (options.mode == ESCAPE_NINJA) {
+        // For ninja just escape spaces with $.
+        dest->push_back('$');
+      } else if (options.mode == ESCAPE_SHELL && !options.inhibit_quoting) {
+        // For the shell, quote the whole string.
+        if (!used_quotes) {
+          used_quotes = true;
+          dest->insert(dest->begin(), '"');
+        }
+      }
+      dest->push_back(' ');
+#if defined(OS_WIN)
+    } else if (str[i] == '/' && options.convert_slashes) {
+      // Convert slashes on Windows if requested.
+      dest->push_back('\\');
+#else
+    } else if (str[i] == '\\' && options.mode == ESCAPE_SHELL) {
+      // For non-Windows shell, escape backslashes.
+      dest->push_back('\\');
+      dest->push_back('\\');
+#endif
+    } else {
+      dest->push_back(str[i]);
+    }
+  }
+
+  if (used_quotes)
+    dest->push_back('"');
+}
+
+}  // namespace
+
+std::string EscapeString(const base::StringPiece& str,
+                         const EscapeOptions& options) {
+  std::string result;
+  result.reserve(str.size() + 4);  // Guess we'll add a couple of extra chars.
+  EscapeStringToString(str, options, &result);
+  return result;
+}
+
+void EscapeStringToStream(std::ostream& out,
+                          const base::StringPiece& str,
+                          const EscapeOptions& options) {
+  // Escape to a stack buffer and then write out to the stream.
+  base::StackVector<char, 256> result;
+  result->reserve(str.size() + 4);  // Guess we'll add a couple of extra chars.
+  EscapeStringToString(str, options, &result.container());
+  if (!result->empty())
+    out.write(result->data(), result->size());
+}
diff --git a/tools/gn/escape.h b/tools/gn/escape.h
new file mode 100644
index 0000000..1791161
--- /dev/null
+++ b/tools/gn/escape.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2013 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.
+
+#ifndef TOOLS_GN_ESCAPE_H_
+#define TOOLS_GN_ESCAPE_H_
+
+#include <iosfwd>
+
+#include "base/strings/string_piece.h"
+
+// TODO(brettw) we may need to make this a bitfield. If we want to write a
+// shell command in a ninja file, we need the shell characters to be escaped,
+// and THEN the ninja characters. Or maybe we require the caller to do two
+// passes.
+enum EscapingMode {
+  ESCAPE_NONE,   // No escaping.
+  ESCAPE_NINJA,  // Ninja string escaping.
+  ESCAPE_SHELL,  // Shell string escaping.
+};
+
+struct EscapeOptions {
+  EscapeOptions()
+      : mode(ESCAPE_NONE),
+        convert_slashes(false),
+        inhibit_quoting(false) {
+  }
+
+  EscapingMode mode;
+
+  // When set, converts forward-slashes to system-specific path separators.
+  bool convert_slashes;
+
+  // When the escaping mode is ESCAPE_SHELL, the escaper will normally put
+  // quotes around things with spaces. If this value is set to true, we'll
+  // disable the quoting feature and just add the spaces.
+  //
+  // This mode is for when quoting is done at some higher-level. Defaults to
+  // false.
+  bool inhibit_quoting;
+};
+
+// Escapes the given input, returnining the result.
+std::string EscapeString(const base::StringPiece& str,
+                         const EscapeOptions& options);
+
+// Same as EscapeString but writes the results to the given stream, saving a
+// copy.
+void EscapeStringToStream(std::ostream& out,
+                          const base::StringPiece& str,
+                          const EscapeOptions& options);
+
+#endif  // TOOLS_GN_ESCAPE_H_
diff --git a/tools/gn/escape_unittest.cc b/tools/gn/escape_unittest.cc
new file mode 100644
index 0000000..a7c19b3
--- /dev/null
+++ b/tools/gn/escape_unittest.cc
@@ -0,0 +1,4 @@
+// Copyright (c) 2013 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.
+
diff --git a/tools/gn/file_template.cc b/tools/gn/file_template.cc
new file mode 100644
index 0000000..8b5d09f
--- /dev/null
+++ b/tools/gn/file_template.cc
@@ -0,0 +1,125 @@
+// Copyright (c) 2013 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/file_template.h"
+
+#include "tools/gn/filesystem_utils.h"
+
+const char FileTemplate::kSource[] = "{{source}}";
+const char FileTemplate::kSourceNamePart[] = "{{source_name_part}}";
+
+FileTemplate::FileTemplate(const Value& t, Err* err) {
+  ParseInput(t, err);
+}
+
+FileTemplate::FileTemplate(const std::vector<std::string>& t) {
+  for (size_t i = 0; i < t.size(); i++)
+    ParseOneTemplateString(t[i]);
+}
+
+FileTemplate::~FileTemplate() {
+}
+
+void FileTemplate::Apply(const Value& sources,
+                         const ParseNode* origin,
+                         std::vector<Value>* dest,
+                         Err* err) const {
+  if (!sources.VerifyTypeIs(Value::LIST, err))
+    return;
+  dest->reserve(sources.list_value().size() * templates_.container().size());
+
+  // Temporary holding place, allocate outside to re-use- buffer.
+  std::vector<std::string> string_output;
+
+  const std::vector<Value>& sources_list = sources.list_value();
+  for (size_t i = 0; i < sources_list.size(); i++) {
+    if (!sources_list[i].VerifyTypeIs(Value::STRING, err))
+      return;
+
+    ApplyString(sources_list[i].string_value(), &string_output);
+    for (size_t out_i = 0; out_i < string_output.size(); out_i++)
+      dest->push_back(Value(origin, string_output[i]));
+  }
+}
+
+void FileTemplate::ApplyString(const std::string& str,
+                               std::vector<std::string>* output) const {
+  // Compute all substitutions needed so we can just do substitutions below.
+  // We skip the LITERAL one since that varies each time.
+  std::string subst[Subrange::NUM_TYPES];
+  if (types_required_[Subrange::SOURCE])
+    subst[Subrange::SOURCE] = str;
+  if (types_required_[Subrange::NAME_PART])
+    subst[Subrange::NAME_PART] = FindFilenameNoExtension(&str).as_string();
+
+  output->resize(templates_.container().size());
+  for (size_t template_i = 0;
+       template_i < templates_.container().size(); template_i++) {
+    const Template& t = templates_[template_i];
+    (*output)[template_i].clear();
+    for (size_t subrange_i = 0; subrange_i < t.container().size();
+         subrange_i++) {
+      if (t[subrange_i].type == Subrange::LITERAL)
+        (*output)[template_i].append(t[subrange_i].literal);
+      else
+        (*output)[template_i].append(subst[t[subrange_i].type]);
+    }
+  }
+}
+
+void FileTemplate::ParseInput(const Value& value, Err* err) {
+  switch (value.type()) {
+    case Value::STRING:
+      ParseOneTemplateString(value.string_value());
+      break;
+    case Value::LIST:
+      for (size_t i = 0; i < value.list_value().size(); i++) {
+        if (!value.list_value()[i].VerifyTypeIs(Value::STRING, err))
+          return;
+        ParseOneTemplateString(value.list_value()[i].string_value());
+      }
+      break;
+    default:
+      *err = Err(value, "File template must be a string or list.",
+                 "A sarcastic comment about your skills goes here.");
+  }
+}
+
+void FileTemplate::ParseOneTemplateString(const std::string& str) {
+  templates_.container().resize(templates_.container().size() + 1);
+  Template& t = templates_[templates_.container().size() - 1];
+
+  size_t cur = 0;
+  while (true) {
+    size_t next = str.find("{{", cur);
+
+    // Pick up everything from the previous spot to here as a literal.
+    if (next == std::string::npos) {
+      if (cur != str.size())
+        t.container().push_back(Subrange(Subrange::LITERAL, str.substr(cur)));
+      break;
+    } else if (next > cur) {
+      t.container().push_back(
+          Subrange(Subrange::LITERAL, str.substr(cur, next - cur)));
+    }
+
+    // Decode the template param.
+    if (str.compare(next, arraysize(kSource) - 1, kSource) == 0) {
+      t.container().push_back(Subrange(Subrange::SOURCE));
+      types_required_[Subrange::SOURCE] = true;
+      cur = next + arraysize(kSource) - 1;
+    } else if (str.compare(next, arraysize(kSourceNamePart) - 1,
+                           kSourceNamePart) == 0) {
+      t.container().push_back(Subrange(Subrange::NAME_PART));
+      types_required_[Subrange::NAME_PART] = true;
+      cur = next + arraysize(kSourceNamePart) - 1;
+    } else {
+      // If it's not a match, treat it like a one-char literal (this will be
+      // rare, so it's not worth the bother to add to the previous literal) so
+      // we can keep going.
+      t.container().push_back(Subrange(Subrange::LITERAL, "{"));
+      cur = next + 1;
+    }
+  }
+}
diff --git a/tools/gn/file_template.h b/tools/gn/file_template.h
new file mode 100644
index 0000000..25d2251
--- /dev/null
+++ b/tools/gn/file_template.h
@@ -0,0 +1,74 @@
+// Copyright (c) 2013 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.
+
+#ifndef TOOLS_GN_FILE_TEMPLATE_H_
+#define TOOLS_GN_FILE_TEMPLATE_H_
+
+#include "base/basictypes.h"
+#include "base/containers/stack_container.h"
+#include "tools/gn/err.h"
+#include "tools/gn/value.h"
+
+class ParseNode;
+
+class FileTemplate {
+ public:
+  struct Subrange {
+    enum Type {
+      LITERAL = 0,
+      SOURCE,
+      NAME_PART,
+      NUM_TYPES  // Must be last
+    };
+    Subrange(Type t, const std::string& l = std::string())
+        : type(t),
+          literal(l) {
+    }
+
+    Type type;
+
+    // When type_ == LITERAL, this specifies the literal.
+    std::string literal;
+  };
+
+  // Constructs a template from the given value. On error, the err will be
+  // set. In this case you should not use this object.
+  FileTemplate(const Value& t, Err* err);
+  FileTemplate(const std::vector<std::string>& t);
+  ~FileTemplate();
+
+  // Applies this template to the given list of sources, appending all
+  // results to the given dest list. The sources must be a list for the
+  // one that takes a value as an input, otherwise the given error will be set.
+  void Apply(const Value& sources,
+             const ParseNode* origin,
+             std::vector<Value>* dest,
+             Err* err) const;
+  void ApplyString(const std::string& input,
+                   std::vector<std::string>* output) const;
+
+  // Known template types.
+  static const char kSource[];
+  static const char kSourceNamePart[];
+
+ private:
+  typedef base::StackVector<Subrange, 8> Template;
+  typedef base::StackVector<Template, 8> TemplateVector;
+
+  void ParseInput(const Value& value, Err* err);
+
+  // Parses a template string and adds it to the templates_ list.
+  void ParseOneTemplateString(const std::string& str);
+
+  TemplateVector templates_;
+
+  // The corresponding value is set to true if the given subrange type is
+  // required. This allows us to precompute these types whem applying them
+  // to a given source file.
+  bool types_required_[Subrange::NUM_TYPES];
+
+  DISALLOW_COPY_AND_ASSIGN(FileTemplate);
+};
+
+#endif  // TOOLS_GN_FILE_TEMPLATE_H_
diff --git a/tools/gn/file_template_unittest.cc b/tools/gn/file_template_unittest.cc
new file mode 100644
index 0000000..47c84f0
--- /dev/null
+++ b/tools/gn/file_template_unittest.cc
@@ -0,0 +1,45 @@
+// Copyright (c) 2013 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 "testing/gtest/include/gtest/gtest.h"
+#include "tools/gn/file_template.h"
+
+TEST(FileTemplate, Static) {
+  std::vector<std::string> templates;
+  templates.push_back("something_static");
+  FileTemplate t(templates);
+
+  std::vector<std::string> result;
+  t.ApplyString("", &result);
+  ASSERT_EQ(1u, result.size());
+  EXPECT_EQ("something_static", result[0]);
+
+  t.ApplyString("lalala", &result);
+  ASSERT_EQ(1u, result.size());
+  EXPECT_EQ("something_static", result[0]);
+}
+
+TEST(FileTemplate, Typical) {
+  std::vector<std::string> templates;
+  templates.push_back("foo/{{source_name_part}}.cc");
+  templates.push_back("foo/{{source_name_part}}.h");
+  FileTemplate t(templates);
+
+  std::vector<std::string> result;
+  t.ApplyString("sources/ha.idl", &result);
+  ASSERT_EQ(2u, result.size());
+  EXPECT_EQ("foo/ha.cc", result[0]);
+  EXPECT_EQ("foo/ha.h", result[1]);
+}
+
+TEST(FileTemplate, Weird) {
+  std::vector<std::string> templates;
+  templates.push_back("{{{source}}{{source}}{{");
+  FileTemplate t(templates);
+
+  std::vector<std::string> result;
+  t.ApplyString("foo/lalala.c", &result);
+  ASSERT_EQ(1u, result.size());
+  EXPECT_EQ("{foo/lalala.cfoo/lalala.c{{", result[0]);
+}
diff --git a/tools/gn/filesystem_utils.cc b/tools/gn/filesystem_utils.cc
new file mode 100644
index 0000000..625a358
--- /dev/null
+++ b/tools/gn/filesystem_utils.cc
@@ -0,0 +1,350 @@
+// Copyright (c) 2013 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/filesystem_utils.h"
+
+#include "base/logging.h"
+#include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
+#include "tools/gn/location.h"
+#include "tools/gn/source_dir.h"
+
+namespace {
+
+enum DotDisposition {
+  // The given dot is just part of a filename and is not special.
+  NOT_A_DIRECTORY,
+
+  // The given dot is the current directory.
+  DIRECTORY_CUR,
+
+  // The given dot is the first of a double dot that should take us up one.
+  DIRECTORY_UP
+};
+
+// When we find a dot, this function is called with the character following
+// that dot to see what it is. The return value indicates what type this dot is
+// (see above). This code handles the case where the dot is at the end of the
+// input.
+//
+// |*consumed_len| will contain the number of characters in the input that
+// express what we found.
+DotDisposition ClassifyAfterDot(const std::string& path,
+                                size_t after_dot,
+                                size_t* consumed_len) {
+  if (after_dot == path.size()) {
+    // Single dot at the end.
+    *consumed_len = 1;
+    return DIRECTORY_CUR;
+  }
+  if (path[after_dot] == '/') {
+    // Single dot followed by a slash.
+    *consumed_len = 2;  // Consume the slash
+    return DIRECTORY_CUR;
+  }
+
+  if (path[after_dot] == '.') {
+    // Two dots.
+    if (after_dot + 1 == path.size()) {
+      // Double dot at the end.
+      *consumed_len = 2;
+      return DIRECTORY_UP;
+    }
+    if (path[after_dot + 1] == '/') {
+      // Double dot folowed by a slash.
+      *consumed_len = 3;
+      return DIRECTORY_UP;
+    }
+  }
+
+  // The dots are followed by something else, not a directory.
+  *consumed_len = 1;
+  return NOT_A_DIRECTORY;
+}
+
+}  // namesapce
+
+SourceFileType GetSourceFileType(const SourceFile& file,
+                                 Settings::TargetOS os) {
+  base::StringPiece extension = FindExtension(&file.value());
+  if (extension == "cc" || extension == "cpp" || extension == "cxx")
+    return SOURCE_CC;
+  if (extension == "h")
+    return SOURCE_H;
+  if (extension == "c")
+    return SOURCE_C;
+
+  switch (os) {
+    case Settings::MAC:
+      if (extension == "m")
+        return SOURCE_M;
+      if (extension == "mm")
+        return SOURCE_MM;
+      break;
+
+    case Settings::WIN:
+      if (extension == "rc")
+        return SOURCE_RC;
+      break;
+
+    default:
+      break;
+  }
+
+  // TODO(brettw) asm files.
+  // TODO(brettw) weird thing with .S on non-Windows platforms.
+  return SOURCE_UNKNOWN;
+}
+
+const char* GetExtensionForOutputType(Target::OutputType type,
+                                      Settings::TargetOS os) {
+  switch (os) {
+    case Settings::WIN:
+      switch (type) {
+        case Target::NONE:
+          NOTREACHED();
+          return "";
+        case Target::EXECUTABLE:
+          return "exe";
+        case Target::SHARED_LIBRARY:
+          return "dll.lib";  // Extension of import library.
+        case Target::STATIC_LIBRARY:
+          return "lib";
+        case Target::LOADABLE_MODULE:
+          return "dll";  // TODO(brettw) what's this?
+        default:
+          NOTREACHED();
+      }
+      break;
+
+    default:
+      NOTREACHED();
+  }
+  return "";
+}
+
+std::string FilePathToUTF8(const base::FilePath& path) {
+#if defined(OS_WIN)
+  return WideToUTF8(path.value());
+#else
+  return path.value();
+#endif
+}
+
+base::FilePath UTF8ToFilePath(const base::StringPiece& sp) {
+#if defined(OS_WIN)
+  return base::FilePath(UTF8ToWide(sp));
+#else
+  return base::FilePath(sp.as_string());
+#endif
+}
+
+size_t FindExtensionOffset(const std::string& path) {
+  for (int i = static_cast<int>(path.size()); i >= 0; i--) {
+    if (path[i] == '/')
+      break;
+    if (path[i] == '.')
+      return i + 1;
+  }
+  return std::string::npos;
+}
+
+base::StringPiece FindExtension(const std::string* path) {
+  size_t extension_offset = FindExtensionOffset(*path);
+  if (extension_offset == std::string::npos)
+    return base::StringPiece();
+  return base::StringPiece(&path->data()[extension_offset],
+                           path->size() - extension_offset);
+}
+
+size_t FindFilenameOffset(const std::string& path) {
+  for (int i = static_cast<int>(path.size()) - 1; i >= 0; i--) {
+    if (path[i] == '/')
+      return i + 1;
+  }
+  return 0;  // No filename found means everything was the filename.
+}
+
+base::StringPiece FindFilename(const std::string* path) {
+  size_t filename_offset = FindFilenameOffset(*path);
+  if (filename_offset == 0)
+    return base::StringPiece(*path);  // Everything is the file name.
+  return base::StringPiece(&(*path).data()[filename_offset],
+                           path->size() - filename_offset);
+}
+
+base::StringPiece FindFilenameNoExtension(const std::string* path) {
+  if (path->empty())
+    return base::StringPiece();
+  size_t filename_offset = FindFilenameOffset(*path);
+  size_t extension_offset = FindExtensionOffset(*path);
+
+  size_t name_len;
+  if (extension_offset == std::string::npos)
+    name_len = path->size() - filename_offset;
+  else
+    name_len = extension_offset - filename_offset - 1;
+
+  return base::StringPiece(&(*path).data()[filename_offset], name_len);
+}
+
+void RemoveFilename(std::string* path) {
+  path->resize(FindFilenameOffset(*path));
+}
+
+bool EndsWithSlash(const std::string& s) {
+  return !s.empty() && s[s.size() - 1] == '/';
+}
+
+base::StringPiece FindDir(const std::string* path) {
+  size_t filename_offset = FindFilenameOffset(*path);
+  if (filename_offset == 0u)
+    return base::StringPiece();
+  return base::StringPiece(path->data(), filename_offset);
+}
+
+bool EnsureStringIsInOutputDir(const SourceDir& dir,
+                               const std::string& str,
+                               const Value& originating,
+                               Err* err) {
+  // The last char of the dir will be a slash. We don't care if the input ends
+  // in a slash or not, so just compare up until there.
+  //
+  // This check will be wrong for all proper prefixes "e.g. "/output" will
+  // match "/out" but we don't really care since this is just a sanity check.
+  const std::string& dir_str = dir.value();
+  if (str.compare(0, dir_str.length() - 1, dir_str, 0, dir_str.length() - 1)
+      != 0) {
+    *err = Err(originating, "File not inside output directory.",
+        "The given file should be in the output directory. Normally you would "
+        "specify\n\"$target_output_dir/foo\" or "
+        "\"$target_gen_dir/foo\". I interpreted this as\n\""
+        + str + "\".");
+    return false;
+  }
+  return true;
+}
+
+std::string InvertDir(const SourceDir& path) {
+  const std::string value = path.value();
+  if (value.empty())
+    return std::string();
+
+  DCHECK(value[0] == '/');
+  size_t begin_index = 1;
+
+  // If the input begins with two slashes, skip over both (this is a
+  // source-relative dir).
+  if (value.size() > 1 && value[1] == '/')
+    begin_index = 2;
+
+  std::string ret;
+  for (size_t i = begin_index; i < value.size(); i++) {
+    if (value[i] == '/')
+      ret.append("../");
+  }
+  return ret;
+}
+
+void NormalizePath(std::string* path) {
+  char* pathbuf = path->empty() ? NULL : &(*path)[0];
+
+  // top_index is the first character we can modify in the path. Anything
+  // before this indicates where the path is relative to.
+  size_t top_index = 0;
+  bool is_relative = true;
+  if (!path->empty() && pathbuf[0] == '/') {
+    is_relative = false;
+
+    if (path->size() > 1 && pathbuf[1] == '/') {
+      // Two leading slashes, this is a path into the source dir.
+      top_index = 2;
+    } else {
+      // One leading slash, this is a system-absolute path.
+      top_index = 1;
+    }
+  }
+
+  size_t dest_i = top_index;
+  for (size_t src_i = top_index; src_i < path->size(); /* nothing */) {
+    if (pathbuf[src_i] == '.') {
+      if (src_i == 0 || pathbuf[src_i - 1] == '/') {
+        // Slash followed by a dot, see if it's something special.
+        size_t consumed_len;
+        switch (ClassifyAfterDot(*path, src_i + 1, &consumed_len)) {
+          case NOT_A_DIRECTORY:
+            // Copy the dot to the output, it means nothing special.
+            pathbuf[dest_i++] = pathbuf[src_i++];
+            break;
+          case DIRECTORY_CUR:
+            // Current directory, just skip the input.
+            src_i += consumed_len;
+            break;
+          case DIRECTORY_UP:
+            // Back up over previous directory component. If we're already
+            // at the top, preserve the "..".
+            if (dest_i > top_index) {
+              // The previous char was a slash, remove it.
+              dest_i--;
+            }
+
+            if (dest_i == top_index) {
+              if (is_relative) {
+                // We're already at the beginning of a relative input, copy the
+                // ".." and continue. We need the trailing slash if there was
+                // one before (otherwise we're at the end of the input).
+                pathbuf[dest_i++] = '.';
+                pathbuf[dest_i++] = '.';
+                if (consumed_len == 3)
+                  pathbuf[dest_i++] = '/';
+
+                // This also makes a new "root" that we can't delete by going
+                // up more levels.  Otherwise "../.." would collapse to
+                // nothing.
+                top_index = dest_i;
+              }
+              // Otherwise we're at the beginning of an absolute path. Don't
+              // allow ".." to go up another level and just eat it.
+            } else {
+              // Just find the previous slash or the beginning of input.
+              while (dest_i > 0 && pathbuf[dest_i - 1] != '/')
+                dest_i--;
+            }
+            src_i += consumed_len;
+        }
+      } else {
+        // Dot not preceeded by a slash, copy it literally.
+        pathbuf[dest_i++] = pathbuf[src_i++];
+      }
+    } else if (pathbuf[src_i] == '/') {
+      if (src_i > 0 && pathbuf[src_i - 1] == '/') {
+        // Two slashes in a row, skip over it.
+        src_i++;
+      } else {
+        // Just one slash, copy it.
+        pathbuf[dest_i++] = pathbuf[src_i++];
+      }
+    } else {
+      // Input nothing special, just copy it.
+      pathbuf[dest_i++] = pathbuf[src_i++];
+    }
+  }
+  path->resize(dest_i);
+}
+
+void ConvertPathToSystem(std::string* path) {
+#if defined(OS_WIN)
+  for (size_t i = 0; i < path->size(); i++) {
+    if ((*path)[i] == '/')
+      (*path)[i] = '\\';
+  }
+#endif
+}
+
+std::string PathToSystem(const std::string& path) {
+  std::string ret(path);
+  ConvertPathToSystem(&ret);
+  return ret;
+}
+
diff --git a/tools/gn/filesystem_utils.h b/tools/gn/filesystem_utils.h
new file mode 100644
index 0000000..19139f8
--- /dev/null
+++ b/tools/gn/filesystem_utils.h
@@ -0,0 +1,115 @@
+// Copyright (c) 2013 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.
+
+#ifndef TOOLS_GN_FILESYSTEM_UTILS_H_
+#define TOOLS_GN_FILESYSTEM_UTILS_H_
+
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/strings/string_piece.h"
+#include "tools/gn/settings.h"
+#include "tools/gn/target.h"
+
+class Err;
+class Location;
+class Value;
+
+enum SourceFileType {
+  SOURCE_UNKNOWN,
+  SOURCE_ASM,
+  SOURCE_C,
+  SOURCE_CC,
+  SOURCE_H,
+  SOURCE_M,
+  SOURCE_MM,
+  //SOURCE_S,  // TODO(brettw) what is this?
+  SOURCE_RC,
+};
+
+SourceFileType GetSourceFileType(const SourceFile& file,
+                                 Settings::TargetOS os);
+
+// Returns the extension, not including the dot, for the given file type on the
+// given system.
+//
+// Some targets make multiple files (like a .dll and an import library). This
+// function returns the name of the file other targets should depend on and
+// link to (so in this example, the import library).
+const char* GetExtensionForOutputType(Target::OutputType type,
+                                      Settings::TargetOS os);
+
+std::string FilePathToUTF8(const base::FilePath& path);
+base::FilePath UTF8ToFilePath(const base::StringPiece& sp);
+
+// Extensions -----------------------------------------------------------------
+
+// Returns the index of the extension (character after the last dot not after a
+// slash). Returns std::string::npos if not found. Returns path.size() if the
+// file ends with a dot.
+size_t FindExtensionOffset(const std::string& path);
+
+// Returns a string piece pointing into the input string identifying the
+// extension. Note that the input pointer must outlive the output.
+base::StringPiece FindExtension(const std::string* path);
+
+// Filename parts -------------------------------------------------------------
+
+// Returns the offset of the character following the last slash, or
+// 0 if no slash was found. Returns path.size() if the path ends with a slash.
+// Note that the input pointer must outlive the output.
+size_t FindFilenameOffset(const std::string& path);
+
+// Returns a string piece pointing into the input string identifying the
+// file name (following the last slash, including the extension). Note that the
+// input pointer must outlive the output.
+base::StringPiece FindFilename(const std::string* path);
+
+// Like FindFilename but does not include the extension.
+base::StringPiece FindFilenameNoExtension(const std::string* path);
+
+// Removes everything after the last slash. The last slash, if any, will be
+// preserved.
+void RemoveFilename(std::string* path);
+
+// Returns true if the given path ends with a slash.
+bool EndsWithSlash(const std::string& s);
+
+// Path parts -----------------------------------------------------------------
+
+// Returns a string piece pointing into the input string identifying the
+// directory name of the given path, including the last slash. Note that the
+// input pointer must outlive the output.
+base::StringPiece FindDir(const std::string* path);
+
+// Verifies that the given string references a file inside of the given
+// directory. This is pretty stupid and doesn't handle "." and "..", etc.,
+// it is designed for a sanity check to keep people from writing output files
+// to the source directory accidentally.
+//
+// The originating value will be blamed in the error.
+//
+// If the file isn't in the dir, returns false and sets the error. Otherwise
+// returns true and leaves the error untouched.
+bool EnsureStringIsInOutputDir(const SourceDir& dir,
+                               const std::string& str,
+                               const Value& originating,
+                               Err* err);
+
+// ----------------------------------------------------------------------------
+
+// Converts a directory to its inverse (e.g. "/foo/bar/" -> "../../").
+// This will be the empty string for the root directories ("/" and "//"), and
+// in all other cases, this is guaranteed to end in a slash.
+std::string InvertDir(const SourceDir& dir);
+
+// Collapses "." and sequential "/"s and evaluates "..".
+void NormalizePath(std::string* path);
+
+// Converts slashes to backslashes for Windows. Keeps the string unchanged
+// for other systems.
+void ConvertPathToSystem(std::string* path);
+std::string PathToSystem(const std::string& path);
+
+#endif  // TOOLS_GN_FILESYSTEM_UTILS_H_
diff --git a/tools/gn/filesystem_utils_unittest.cc b/tools/gn/filesystem_utils_unittest.cc
new file mode 100644
index 0000000..75bf7cd
--- /dev/null
+++ b/tools/gn/filesystem_utils_unittest.cc
@@ -0,0 +1,146 @@
+// Copyright (c) 2013 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 "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "tools/gn/filesystem_utils.h"
+
+TEST(FilesystemUtils, FileExtensionOffset) {
+  EXPECT_EQ(std::string::npos, FindExtensionOffset(""));
+  EXPECT_EQ(std::string::npos, FindExtensionOffset("foo/bar/baz"));
+  EXPECT_EQ(4u, FindExtensionOffset("foo."));
+  EXPECT_EQ(4u, FindExtensionOffset("f.o.bar"));
+  EXPECT_EQ(std::string::npos, FindExtensionOffset("foo.bar/"));
+  EXPECT_EQ(std::string::npos, FindExtensionOffset("foo.bar/baz"));
+}
+
+TEST(FilesystemUtils, FindExtension) {
+  std::string input;
+  EXPECT_EQ("", FindExtension(&input).as_string());
+  input = "foo/bar/baz";
+  EXPECT_EQ("", FindExtension(&input).as_string());
+  input = "foo.";
+  EXPECT_EQ("", FindExtension(&input).as_string());
+  input = "f.o.bar";
+  EXPECT_EQ("bar", FindExtension(&input).as_string());
+  input = "foo.bar/";
+  EXPECT_EQ("", FindExtension(&input).as_string());
+  input = "foo.bar/baz";
+  EXPECT_EQ("", FindExtension(&input).as_string());
+}
+
+TEST(FilesystemUtils, FindFilenameOffset) {
+  EXPECT_EQ(0u, FindFilenameOffset(""));
+  EXPECT_EQ(0u, FindFilenameOffset("foo"));
+  EXPECT_EQ(4u, FindFilenameOffset("foo/"));
+  EXPECT_EQ(4u, FindFilenameOffset("foo/bar"));
+}
+
+TEST(FilesystemUtils, RemoveFilename) {
+  std::string s;
+
+  RemoveFilename(&s);
+  EXPECT_STREQ("", s.c_str());
+
+  s = "foo";
+  RemoveFilename(&s);
+  EXPECT_STREQ("", s.c_str());
+
+  s = "/";
+  RemoveFilename(&s);
+  EXPECT_STREQ("/", s.c_str());
+
+  s = "foo/bar";
+  RemoveFilename(&s);
+  EXPECT_STREQ("foo/", s.c_str());
+
+  s = "foo/bar/baz.cc";
+  RemoveFilename(&s);
+  EXPECT_STREQ("foo/bar/", s.c_str());
+}
+
+TEST(FilesystemUtils, FindDir) {
+  std::string input;
+  EXPECT_EQ("", FindDir(&input));
+  input = "/";
+  EXPECT_EQ("/", FindDir(&input));
+  input = "foo/";
+  EXPECT_EQ("foo/", FindDir(&input));
+  input = "foo/bar/baz";
+  EXPECT_EQ("foo/bar/", FindDir(&input));
+}
+
+TEST(FilesystemUtils, InvertDir) {
+  EXPECT_TRUE(InvertDir(SourceDir()) == "");
+  EXPECT_TRUE(InvertDir(SourceDir("/")) == "");
+  EXPECT_TRUE(InvertDir(SourceDir("//")) == "");
+
+  EXPECT_TRUE(InvertDir(SourceDir("//foo/bar")) == "../../");
+  EXPECT_TRUE(InvertDir(SourceDir("/foo/bar/")) == "../../");
+}
+
+TEST(FilesystemUtils, NormalizePath) {
+  std::string input;
+
+  NormalizePath(&input);
+  EXPECT_EQ("", input);
+
+  input = "foo/bar.txt";
+  NormalizePath(&input);
+  EXPECT_EQ("foo/bar.txt", input);
+
+  input = ".";
+  NormalizePath(&input);
+  EXPECT_EQ("", input);
+
+  input = "..";
+  NormalizePath(&input);
+  EXPECT_EQ("..", input);
+
+  input = "foo//bar";
+  NormalizePath(&input);
+  EXPECT_EQ("foo/bar", input);
+
+  input = "//foo";
+  NormalizePath(&input);
+  EXPECT_EQ("//foo", input);
+
+  input = "foo/..//bar";
+  NormalizePath(&input);
+  EXPECT_EQ("bar", input);
+
+  input = "foo/../../bar";
+  NormalizePath(&input);
+  EXPECT_EQ("../bar", input);
+
+  input = "/../foo";  // Don't go aboe the root dir.
+  NormalizePath(&input);
+  EXPECT_EQ("/foo", input);
+
+  input = "//../foo";  // Don't go aboe the root dir.
+  NormalizePath(&input);
+  EXPECT_EQ("//foo", input);
+
+  input = "../foo";
+  NormalizePath(&input);
+  EXPECT_EQ("../foo", input);
+
+  input = "..";
+  NormalizePath(&input);
+  EXPECT_EQ("..", input);
+
+  input = "./././.";
+  NormalizePath(&input);
+  EXPECT_EQ("", input);
+
+  input = "../../..";
+  NormalizePath(&input);
+  EXPECT_EQ("../../..", input);
+
+  input = "../";
+  NormalizePath(&input);
+  EXPECT_EQ("../", input);
+}
diff --git a/tools/gn/function_define_rule.cc b/tools/gn/function_define_rule.cc
new file mode 100644
index 0000000..afbed9e
--- /dev/null
+++ b/tools/gn/function_define_rule.cc
@@ -0,0 +1,37 @@
+// Copyright (c) 2013 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/functions.h"
+
+#include "tools/gn/parse_tree.h"
+#include "tools/gn/scope.h"
+#include "tools/gn/value.h"
+
+Value ExecuteDefineRule(Scope* scope,
+                        const FunctionCallNode* function,
+                        const std::vector<Value>& args,
+                        BlockNode* block,
+                        Err* err) {
+  // TODO(brettw) determine if the function is built-in and throw an error if
+  // it is.
+  if (args.size() != 1) {
+    *err = Err(function->function(),
+               "Need exactly one string arg to define_rule.");
+    return Value();
+  }
+  if (!args[0].VerifyTypeIs(Value::STRING, err))
+    return Value();
+  std::string rule_name = args[0].string_value();
+
+  const FunctionCallNode* existing_rule = scope->GetRule(rule_name);
+  if (existing_rule) {
+    *err = Err(function, "Duplicate rule definition.",
+               "A rule with this name was already defined.");
+    err->AppendSubErr(Err(existing_rule->function(), "Previous definition."));
+    return Value();
+  }
+
+  scope->AddRule(rule_name, function);
+  return Value();
+}
diff --git a/tools/gn/function_exec_script.cc b/tools/gn/function_exec_script.cc
new file mode 100644
index 0000000..a950909
--- /dev/null
+++ b/tools/gn/function_exec_script.cc
@@ -0,0 +1,254 @@
+// Copyright (c) 2013 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 "base/command_line.h"
+#include "base/file_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "build/build_config.h"
+#include "tools/gn/err.h"
+#include "tools/gn/filesystem_utils.h"
+#include "tools/gn/functions.h"
+#include "tools/gn/input_conversion.h"
+#include "tools/gn/input_file.h"
+#include "tools/gn/parse_tree.h"
+#include "tools/gn/scheduler.h"
+#include "tools/gn/value.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+
+#include "base/win/scoped_handle.h"
+#include "base/win/scoped_process_information.h"
+#endif
+
+/*
+exec_script: Synchronously run a script and return the output.
+
+  exec_script(filename, arguments, input_conversion, [file_dependencies])
+
+  Runs the given script, returning the stdout of the script. The build
+  generation will fail if the script does not exist or returns a nonzero
+  exit code.
+
+Arguments:
+
+  filename:
+      File name of python script to execute, relative to the build file.
+
+  arguments:
+      A list of strings to be passed to the scripe as arguments.
+
+  input_conversion:
+      Controls how the file is read and parsed. See "help input_conversion".
+
+  dependencies:
+      (Optional) A list of files that this script reads or otherwise depends
+      on. These dependencies will be added to the build result such that if
+      any of them change, the build will be regenerated and the script will
+      be re-run.
+
+      The script itself will be an implicit dependency so you do not need to
+      list it.
+
+Example:
+
+  all_lines = exec_script("myscript.py", [some_input], "list lines",
+                          ["data_file.txt"])
+*/
+
+namespace {
+
+#if defined(OS_WIN)
+bool ExecProcess(const CommandLine& cmdline,
+                 const base::FilePath& startup_dir,
+                 std::string* std_out,
+                 std::string* std_err,
+                 int* exit_code) {
+  SECURITY_ATTRIBUTES sa_attr;
+  // Set the bInheritHandle flag so pipe handles are inherited.
+  sa_attr.nLength = sizeof(SECURITY_ATTRIBUTES);
+  sa_attr.bInheritHandle = TRUE;
+  sa_attr.lpSecurityDescriptor = NULL;
+
+  // Create the pipe for the child process's STDOUT.
+  HANDLE out_read = NULL;
+  HANDLE out_write = NULL;
+  if (!CreatePipe(&out_read, &out_write, &sa_attr, 0)) {
+    NOTREACHED() << "Failed to create pipe";
+    return false;
+  }
+  base::win::ScopedHandle scoped_out_read(out_read);
+  base::win::ScopedHandle scoped_out_write(out_write);
+
+  // Create the pipe for the child process's STDERR.
+  HANDLE err_read = NULL;
+  HANDLE err_write = NULL;
+  if (!CreatePipe(&err_read, &err_write, &sa_attr, 0)) {
+    NOTREACHED() << "Failed to create pipe";
+    return false;
+  }
+  base::win::ScopedHandle scoped_err_read(err_read);
+  base::win::ScopedHandle scoped_err_write(err_write);
+
+  // Ensure the read handle to the pipe for STDOUT/STDERR is not inherited.
+  if (!SetHandleInformation(out_read, HANDLE_FLAG_INHERIT, 0)) {
+    NOTREACHED() << "Failed to disabled pipe inheritance";
+    return false;
+  }
+  if (!SetHandleInformation(err_read, HANDLE_FLAG_INHERIT, 0)) {
+    NOTREACHED() << "Failed to disabled pipe inheritance";
+    return false;
+  }
+
+  base::FilePath::StringType cmdline_str(cmdline.GetCommandLineString());
+
+  base::win::ScopedProcessInformation proc_info;
+  STARTUPINFO start_info = { 0 };
+
+  start_info.cb = sizeof(STARTUPINFO);
+  start_info.hStdOutput = out_write;
+  // Keep the normal stdin.
+  start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+  // FIXME(brettw) set stderr here when we actually read it below.
+  //start_info.hStdError = err_write;
+  start_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
+  start_info.dwFlags |= STARTF_USESTDHANDLES;
+
+  // Create the child process.
+  if (!CreateProcess(NULL,
+                     &cmdline_str[0],
+                     NULL, NULL,
+                     TRUE,  // Handles are inherited.
+                     0, NULL,
+                     startup_dir.value().c_str(),
+                     &start_info, proc_info.Receive())) {
+    return false;
+  }
+
+  // Close our writing end of pipes now. Otherwise later read would not be able
+  // to detect end of child's output.
+  scoped_out_write.Close();
+  scoped_err_write.Close();
+
+  // Read output from the child process's pipe for STDOUT
+  const int kBufferSize = 1024;
+  char buffer[kBufferSize];
+
+  // FIXME(brettw) read from stderr here! This is complicated because we want
+  // to read both of them at the same time, probably need overlapped I/O.
+  // Also uncomment start_info code above.
+  for (;;) {
+    DWORD bytes_read = 0;
+    BOOL success = ReadFile(out_read, buffer, kBufferSize, &bytes_read, NULL);
+    if (!success || bytes_read == 0)
+      break;
+    std_out->append(buffer, bytes_read);
+  }
+
+  // Let's wait for the process to finish.
+  WaitForSingleObject(proc_info.process_handle(), INFINITE);
+
+  DWORD dw_exit_code;
+  GetExitCodeProcess(proc_info.process_handle(), &dw_exit_code);
+  *exit_code = static_cast<int>(dw_exit_code);
+
+  return true;
+}
+#else
+bool ExecProcess(const CommandLine& cmdline,
+                 const base::FilePath& startup_dir,
+                 std::string* std_out,
+                 std::string* std_err,
+                 int* exit_code) {
+  //NOTREACHED() << "Implement me.";
+  return false;
+}
+#endif
+
+}  // namespace
+
+Value ExecuteExecScript(Scope* scope,
+                        const FunctionCallNode* function,
+                        const std::vector<Value>& args,
+                        Err* err) {
+  if (args.size() != 3 && args.size() != 4) {
+    *err = Err(function->function(), "Wrong number of args to write_file",
+               "I expected three or four arguments.");
+    return Value();
+  }
+
+  const Settings* settings = scope->settings();
+  const BuildSettings* build_settings = settings->build_settings();
+  const SourceDir& cur_dir = SourceDirForFunctionCall(function);
+
+  // Find the python script to run.
+  if (!args[0].VerifyTypeIs(Value::STRING, err))
+    return Value();
+  SourceFile script_source =
+      cur_dir.ResolveRelativeFile(args[0].string_value());
+  base::FilePath script_path = build_settings->GetFullPath(script_source);
+  if (!build_settings->secondary_source_path().empty() &&
+      !base::PathExists(script_path)) {
+    // Fall back to secondary source root when the file doesn't exist.
+    script_path = build_settings->GetFullPathSecondary(script_source);
+  }
+
+  // Add all dependencies of this script, including the script itself, to the
+  // build deps.
+  g_scheduler->AddGenDependency(script_source);
+  if (args.size() == 4) {
+    const Value& deps_value = args[3];
+    if (!deps_value.VerifyTypeIs(Value::LIST, err))
+      return Value();
+
+    for (size_t i = 0; i < deps_value.list_value().size(); i++) {
+      if (!deps_value.list_value()[0].VerifyTypeIs(Value::STRING, err))
+        return Value();
+      g_scheduler->AddGenDependency(cur_dir.ResolveRelativeFile(
+          deps_value.list_value()[0].string_value()));
+    }
+  }
+
+  // Make the command line.
+  const base::FilePath& python_path = build_settings->python_path();
+  CommandLine cmdline(python_path);
+  cmdline.AppendArgPath(script_path);
+
+  const Value& script_args = args[1];
+  if (!script_args.VerifyTypeIs(Value::LIST, err))
+    return Value();
+  for (size_t i = 0; i < script_args.list_value().size(); i++) {
+    if (!script_args.list_value()[i].VerifyTypeIs(Value::STRING, err))
+      return Value();
+    cmdline.AppendArg(script_args.list_value()[i].string_value());
+  }
+
+  // Execute the process.
+  // TODO(brettw) set the environment block.
+  std::string output;
+  std::string stderr_output;  // TODO(brettw) not hooked up, see above.
+  int exit_code = 0;
+  if (!ExecProcess(cmdline, build_settings->GetFullPath(cur_dir),
+                   &output, &stderr_output, &exit_code)) {
+    *err = Err(function->function(), "Could not execute python.",
+        "I was trying to execute \"" + FilePathToUTF8(python_path) + "\".");
+    return Value();
+  }
+
+  // TODO(brettw) maybe we need stderr also for reasonable stack dumps.
+  if (exit_code != 0) {
+    std::string msg =
+        std::string("I was running \"") + FilePathToUTF8(script_path) + "\"\n"
+        "and it returned " + base::IntToString(exit_code);
+    if (!output.empty())
+      msg += " and printed out:\n\n" + output;
+    else
+      msg += ".";
+    *err = Err(function->function(), "Script returned non-zero exit code.",
+               msg);
+    return Value();
+  }
+
+  return ConvertInputToValue(output, function, args[2], err);
+}
diff --git a/tools/gn/function_process_file_template.cc b/tools/gn/function_process_file_template.cc
new file mode 100644
index 0000000..18e1425
--- /dev/null
+++ b/tools/gn/function_process_file_template.cc
@@ -0,0 +1,65 @@
+// Copyright (c) 2013 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/file_template.h"
+#include "tools/gn/functions.h"
+#include "tools/gn/parse_tree.h"
+
+/*
+process_file_template: Do template expansion over a list of files.
+
+  process_file_template(source_list, template)
+
+  process_file_template applies a template list to a source file list,
+  returning the result of applying each template to each source. This is
+  typically used for computing output file names from input files.
+
+Arguments:
+
+  The source_list is a list of file names.
+
+  The template can be a string or a list. If it is a list, multiple output
+  strings are generated for each input.
+
+  The following template substrings are used in the template arguments
+  and are replaced with the corresponding part of the input file name:
+
+    "{{source}}": The entire source name.
+
+    "{{source_name_part}}": The source name with no path or extension.
+
+Example:
+
+  sources = [
+    "foo.idl",
+    "bar.idl",
+  ]
+  myoutputs = process_file_template(
+      sources,
+      [ "$target_gen_dir/{{source_name_part}}.cc",
+        "$target_gen_dir/{{source_name_part}}.h" ])
+
+ The result in this case will be:
+    [ "/out/Debug/foo.cc"
+      "/out/Debug/foo.h"
+      "/out/Debug/bar.cc"
+      "/out/Debug/bar.h" ]
+*/
+Value ExecuteProcessFileTemplate(Scope* scope,
+                                 const FunctionCallNode* function,
+                                 const std::vector<Value>& args,
+                                 Err* err) {
+  if (args.size() != 2) {
+    *err = Err(function->function(), "Expected two arguments");
+    return Value();
+  }
+
+  FileTemplate file_template(args[1], err);
+  if (err->has_error())
+    return Value();
+
+  Value ret(function, Value::LIST);
+  file_template.Apply(args[0], function, &ret.list_value(), err);
+  return ret;
+}
diff --git a/tools/gn/function_read_file.cc b/tools/gn/function_read_file.cc
new file mode 100644
index 0000000..db981ad
--- /dev/null
+++ b/tools/gn/function_read_file.cc
@@ -0,0 +1,67 @@
+// Copyright (c) 2013 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 "base/file_util.h"
+#include "tools/gn/err.h"
+#include "tools/gn/filesystem_utils.h"
+#include "tools/gn/functions.h"
+#include "tools/gn/input_conversion.h"
+#include "tools/gn/input_file.h"
+#include "tools/gn/scheduler.h"
+
+// TODO(brettw) consider removing this. I originally wrote it for making the
+// WebKit bindings but misundersood what was required, and didn't need to
+// use this. This seems to have a high potential for misuse.
+
+/*
+read_file: Read a file into a variable.
+
+  read_file(filename, how_to_read)
+
+  Whitespace will be trimmed from the end of the file. Throws an error if the
+  file can not be opened.
+
+Arguments:
+
+  filename:
+      Filename to read, relative to the build file.
+
+  input_conversion:
+      Controls how the file is read and parsed. See "help input_conversion".
+
+Example:
+
+  lines = read_file("foo.txt", "list lines")
+*/
+Value ExecuteReadFile(Scope* scope,
+                      const FunctionCallNode* function,
+                      const std::vector<Value>& args,
+                      Err* err) {
+  if (args.size() != 2) {
+    *err = Err(function->function(), "Wrong number of args to read_file",
+               "I expected two arguments.");
+    return Value();
+  }
+  if (!args[0].VerifyTypeIs(Value::STRING, err))
+    return Value();
+
+  // Compute the file name.
+  const SourceDir& cur_dir = SourceDirForFunctionCall(function);
+  SourceFile source_file = cur_dir.ResolveRelativeFile(args[0].string_value());
+  base::FilePath file_path =
+      scope->settings()->build_settings()->GetFullPath(source_file);
+
+  // Ensure that everything is recomputed if the read file changes.
+  g_scheduler->AddGenDependency(source_file);
+
+  // Read contents.
+  std::string file_contents;
+  if (!file_util::ReadFileToString(file_path, &file_contents)) {
+    *err = Err(args[0], "Could not read file.",
+               "I resolved this to \"" + FilePathToUTF8(file_path) + "\".");
+    return Value();
+  }
+
+  return ConvertInputToValue(file_contents, function, args[1], err);
+}
diff --git a/tools/gn/function_set_default_toolchain.cc b/tools/gn/function_set_default_toolchain.cc
new file mode 100644
index 0000000..4939ac8
--- /dev/null
+++ b/tools/gn/function_set_default_toolchain.cc
@@ -0,0 +1,70 @@
+// Copyright (c) 2013 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/build_settings.h"
+#include "tools/gn/functions.h"
+#include "tools/gn/parse_tree.h"
+#include "tools/gn/scope.h"
+#include "tools/gn/settings.h"
+#include "tools/gn/toolchain_manager.h"
+
+/*
+set_default_toolchain: Sets the default toolchain name.
+
+  set_default_toolchain(toolchain_label)
+
+  The given label should identify a toolchain definition (see "toolchain").
+  This toolchain will be used for all targets unless otherwise specified.
+
+  This function is only valid to call during the processing of the build
+  configuration file. Since the build configuration file is processed
+  separately for each toolchain, this function will be a no-op when called
+  under any non-default toolchains.
+
+  For example, the default toolchain should be appropriate for the current
+  environment. If the current environment is 32-bit and somebody references
+  a target with a 64-bit toolchain, we wouldn't want processing of the build
+  config file for the 64-bit toolchain to reset the default toolchain to
+  64-bit, we want to keep it 32-bits.
+
+Argument:
+
+  toolchain_label:
+      Toolchain name.
+
+Example:
+
+  set_default_toolchain("//build/config/win:vs32")
+*/
+
+Value ExecuteSetDefaultToolchain(Scope* scope,
+                                 const FunctionCallNode* function,
+                                 const std::vector<Value>& args,
+                                 Err* err) {
+  if (!scope->IsProcessingBuildConfig()) {
+    *err = Err(function->function(), "Must be called from build config.",
+        "set_default_toolchain can only be called from the build configuration "
+        "file.");
+    return Value();
+  }
+
+  // Ignore non-default-build-config invocations.
+  if (!scope->IsProcessingDefaultBuildConfig())
+    return Value();
+
+  const SourceDir& current_dir = SourceDirForFunctionCall(function);
+  const Label& default_toolchain = ToolchainLabelForScope(scope);
+
+  if (!EnsureSingleStringArg(function, args, err))
+    return Value();
+  Label toolchain_label(
+      Label::Resolve(current_dir, default_toolchain, args[0], err));
+  if (toolchain_label.is_null())
+    return Value();
+
+  ToolchainManager& mgr =
+      scope->settings()->build_settings()->toolchain_manager();
+  mgr.SetDefaultToolchainUnlocked(toolchain_label, function->GetRange(), err);
+  return Value();
+}
diff --git a/tools/gn/function_template.cc b/tools/gn/function_template.cc
new file mode 100644
index 0000000..a85a40f
--- /dev/null
+++ b/tools/gn/function_template.cc
@@ -0,0 +1,38 @@
+// Copyright (c) 2013 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/functions.h"
+
+#include "tools/gn/parse_tree.h"
+#include "tools/gn/scope.h"
+#include "tools/gn/value.h"
+
+Value ExecuteTemplate(Scope* scope,
+                      const FunctionCallNode* function,
+                      const std::vector<Value>& args,
+                      BlockNode* block,
+                      Err* err) {
+  // TODO(brettw) determine if the function is built-in and throw an error if
+  // it is.
+  if (args.size() != 1) {
+    *err = Err(function->function(),
+               "Need exactly one string arg to template.");
+    return Value();
+  }
+  if (!args[0].VerifyTypeIs(Value::STRING, err))
+    return Value();
+  std::string template_name = args[0].string_value();
+
+  const FunctionCallNode* existing_template = scope->GetTemplate(template_name);
+  if (existing_template) {
+    *err = Err(function, "Duplicate template definition.",
+               "A template with this name was already defined.");
+    err->AppendSubErr(Err(existing_template->function(),
+                          "Previous definition."));
+    return Value();
+  }
+
+  scope->AddTemplate(template_name, function);
+  return Value();
+}
diff --git a/tools/gn/function_toolchain.cc b/tools/gn/function_toolchain.cc
new file mode 100644
index 0000000..b78c639
--- /dev/null
+++ b/tools/gn/function_toolchain.cc
@@ -0,0 +1,126 @@
+// Copyright (c) 2013 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/err.h"
+#include "tools/gn/functions.h"
+#include "tools/gn/parse_tree.h"
+#include "tools/gn/scheduler.h"
+#include "tools/gn/scope.h"
+#include "tools/gn/settings.h"
+#include "tools/gn/toolchain.h"
+
+namespace {
+
+// This is jsut a unique value to take the address of to use as the key for
+// the toolchain property on a scope.
+const int kToolchainPropertyKey = 0;
+
+// Reads the given string from the scope (if present) and puts the result into
+// dest. If the value is not a string, sets the error and returns false.
+bool ReadString(Scope& scope, const char* var, std::string* dest, Err* err) {
+  const Value* v = scope.GetValue(var, true);
+  if (!v)
+    return true;  // Not present is fine.
+
+  if (!v->VerifyTypeIs(Value::STRING, err))
+    return false;
+  *dest = v->string_value();
+  return true;
+}
+
+}  // namespace
+
+Value ExecuteToolchain(Scope* scope,
+                       const FunctionCallNode* function,
+                       const std::vector<Value>& args,
+                       BlockNode* block,
+                       Err* err) {
+  if (!EnsureNotProcessingImport(function, scope, err) ||
+      !EnsureNotProcessingBuildConfig(function, scope, err))
+    return Value();
+
+  // Note that we don't want to use MakeLabelForScope since that will include
+  // the toolchain name in the label, and toolchain labels don't themselves
+  // have toolchain names.
+  const SourceDir& input_dir = SourceDirForFunctionCall(function);
+  Label label(input_dir, args[0].string_value(), SourceDir(), std::string());
+  if (g_scheduler->verbose_logging())
+    g_scheduler->Log("Generating toolchain", label.GetUserVisibleName(true));
+
+  // This object will actually be copied into the one owned by the toolchain
+  // manager, but that has to be done in the lock.
+  Toolchain toolchain(label);
+
+  Scope block_scope(scope);
+  block_scope.SetProperty(&kToolchainPropertyKey, &toolchain);
+  block->ExecuteBlockInScope(&block_scope, err);
+  block_scope.SetProperty(&kToolchainPropertyKey, NULL);
+  if (err->has_error())
+    return Value();
+  if (!block_scope.CheckForUnusedVars(err))
+    return Value();
+
+  const BuildSettings* build_settings = scope->settings()->build_settings();
+  {
+    // Save the toolchain definition in the toolchain manager and mark the
+    // corresponding item in the dependency tree resolved so that targets
+    // that depend on this toolchain know it's ready.
+    base::AutoLock lock(build_settings->item_tree().lock());
+    build_settings->toolchain_manager().SetToolchainDefinitionLocked(
+        toolchain, function->GetRange(), err);
+    build_settings->item_tree().MarkItemGeneratedLocked(label);
+  }
+  return Value();
+}
+
+Value ExecuteTool(Scope* scope,
+                  const FunctionCallNode* function,
+                  const std::vector<Value>& args,
+                  BlockNode* block,
+                  Err* err) {
+  // Find the toolchain definition we're executing inside of. The toolchain
+  // function will set a property pointing to it that we'll pick up.
+  Toolchain* toolchain = reinterpret_cast<Toolchain*>(
+      scope->GetProperty(&kToolchainPropertyKey, NULL));
+  if (!toolchain) {
+    *err = Err(function->function(), "tool() called outside of toolchain().",
+        "The tool() function can only be used inside a toolchain() "
+        "definition.");
+    return Value();
+  }
+
+  if (!EnsureSingleStringArg(function, args, err))
+    return Value();
+  const std::string& tool_name = args[0].string_value();
+  Toolchain::ToolType tool_type = Toolchain::ToolNameToType(tool_name);
+  if (tool_type == Toolchain::TYPE_NONE) {
+    *err = Err(args[0], "Unknown tool type");
+    return Value();
+  }
+
+  // Run the tool block.
+  Scope block_scope(scope);
+  block->ExecuteBlockInScope(&block_scope, err);
+  if (err->has_error())
+    return Value();
+
+  // Extract the stuff we need.
+  Toolchain::Tool t;
+  if (!ReadString(block_scope, "command", &t.command, err) ||
+      !ReadString(block_scope, "depfile", &t.depfile, err) ||
+      !ReadString(block_scope, "deps", &t.deps, err) ||
+      !ReadString(block_scope, "description", &t.description, err) ||
+      !ReadString(block_scope, "pool", &t.pool, err) ||
+      !ReadString(block_scope, "restat", &t.restat, err) ||
+      !ReadString(block_scope, "rspfile", &t.rspfile, err) ||
+      !ReadString(block_scope, "rspfile_content", &t.rspfile_content, err))
+    return Value();
+
+  // Make sure there weren't any vars set in this tool that were unused.
+  if (!block_scope.CheckForUnusedVars(err))
+    return Value();
+
+  toolchain->SetTool(tool_type, t);
+  return Value();
+}
diff --git a/tools/gn/function_write_file.cc b/tools/gn/function_write_file.cc
new file mode 100644
index 0000000..9356feb
--- /dev/null
+++ b/tools/gn/function_write_file.cc
@@ -0,0 +1,84 @@
+// Copyright (c) 2013 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 <iostream>
+#include <sstream>
+
+#include "base/file_util.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "tools/gn/err.h"
+#include "tools/gn/filesystem_utils.h"
+#include "tools/gn/functions.h"
+#include "tools/gn/input_file.h"
+#include "tools/gn/parse_tree.h"
+#include "tools/gn/scheduler.h"
+
+/*
+write_file: Read a file into a variable.
+
+  write_file(filename, data)
+
+  If data is a list, the list will be written one-item-per-line with no
+  quoting or brackets.
+
+  TODO(brettw) we probably need an optional third argument to control list
+  formatting.
+
+Arguments:
+
+  filename:
+      Filename to write. This must be within the output directory.
+
+  data:
+      The list or string to write.
+*/
+Value ExecuteWriteFile(Scope* scope,
+                       const FunctionCallNode* function,
+                       const std::vector<Value>& args,
+                       Err* err) {
+  if (args.size() != 2) {
+    *err = Err(function->function(), "Wrong number of args to write_file",
+               "I expected two arguments.");
+    return Value();
+  }
+
+  // Compute the file name and make sure it's in the output dir.
+  if (!args[0].VerifyTypeIs(Value::STRING, err))
+    return Value();
+  const SourceDir& cur_dir = SourceDirForFunctionCall(function);
+  SourceFile source_file = cur_dir.ResolveRelativeFile(args[0].string_value());
+  if (!EnsureStringIsInOutputDir(
+          scope->settings()->build_settings()->build_dir(),
+          source_file.value(), args[0], err))
+    return Value();
+
+  // Compute output.
+  std::ostringstream contents;
+  if (args[1].type() == Value::LIST) {
+    const std::vector<Value>& list = args[1].list_value();
+    for (size_t i = 0; i < list.size(); i++)
+      contents << list[i].ToString() << std::endl;
+  } else {
+    contents << args[1].ToString();
+  }
+
+  // Write file, creating the directory if necessary.
+  base::FilePath file_path =
+      scope->settings()->build_settings()->GetFullPath(source_file);
+  const std::string& contents_string = contents.str();
+  if (!file_util::CreateDirectory(file_path.DirName())) {
+    *err = Err(function->function(), "Unable to create directory.",
+               "I was using \"" + FilePathToUTF8(file_path.DirName()) + "\".");
+    return Value();
+  }
+  if (file_util::WriteFile(file_path,
+                           contents_string.c_str(), contents_string.size())
+      != static_cast<int>(contents_string.size())) {
+    *err = Err(function->function(), "Unable to write file.",
+               "I was writing \"" + FilePathToUTF8(file_path) + "\".");
+    return Value();
+  }
+  return Value();
+}
diff --git a/tools/gn/functions.cc b/tools/gn/functions.cc
new file mode 100644
index 0000000..7f32db2
--- /dev/null
+++ b/tools/gn/functions.cc
@@ -0,0 +1,443 @@
+// Copyright (c) 2013 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/functions.h"
+
+#include <iostream>
+
+#include "base/strings/string_util.h"
+#include "tools/gn/config.h"
+#include "tools/gn/config_values_generator.h"
+#include "tools/gn/err.h"
+#include "tools/gn/input_file.h"
+#include "tools/gn/item_tree.h"
+#include "tools/gn/parse_tree.h"
+#include "tools/gn/scheduler.h"
+#include "tools/gn/scope.h"
+#include "tools/gn/settings.h"
+#include "tools/gn/target_manager.h"
+#include "tools/gn/token.h"
+#include "tools/gn/value.h"
+
+namespace {
+
+void FillNeedsBlockError(const FunctionCallNode* function, Err* err) {
+  *err = Err(function->function(), "This function call requires a block.",
+      "The block's \"{\" must be on the same line as the function "
+      "call's \")\".");
+}
+
+Value ExecuteAssert(const FunctionCallNode* function,
+                    const std::vector<Value>& args,
+                    Err* err) {
+  if (args.size() != 1) {
+    *err = Err(function->function(), "Wrong number of arguments.",
+               "assert() takes one argument, "
+               "were you expecting somethig else?");
+  } else if (args[0].InterpretAsInt() == 0) {
+    *err = Err(function->function(), "Assertion failed.");
+    if (args[0].origin()) {
+      // If you do "assert(foo)" we'd ideally like to show you where foo was
+      // set, and in this case the origin of the args will tell us that.
+      // However, if you do "assert(foo && bar)" the source of the value will
+      // be the assert like, which isn't so helpful.
+      //
+      // So we try to see if the args are from the same line or not. This will
+      // break if you do "assert(\nfoo && bar)" and we may show the second line
+      // as the source, oh well. The way around this is to check to see if the
+      // origin node is inside our function call block.
+      Location origin_location = args[0].origin()->GetRange().begin();
+      if (origin_location.file() != function->function().location().file() ||
+          origin_location.line_number() !=
+              function->function().location().line_number()) {
+        err->AppendSubErr(Err(args[0].origin()->GetRange(), "",
+                              "This is where it was set."));
+      }
+    }
+  }
+  return Value();
+}
+
+Value ExecuteConfig(Scope* scope,
+                    const FunctionCallNode* function,
+                    const std::vector<Value>& args,
+                    Err* err) {
+  if (!EnsureSingleStringArg(function, args, err) ||
+      !EnsureNotProcessingImport(function, scope, err))
+    return Value();
+
+  Label label(MakeLabelForScope(scope, function, args[0].string_value()));
+
+  if (g_scheduler->verbose_logging())
+    g_scheduler->Log("Generating config", label.GetUserVisibleName(true));
+
+  // Create the empty config object.
+  ItemTree* tree = &scope->settings()->build_settings()->item_tree();
+  Config* config = Config::GetConfig(scope->settings(), function->GetRange(),
+                                     label, NULL, err);
+  if (err->has_error())
+    return Value();
+
+  // Fill it.
+  const SourceDir input_dir = SourceDirForFunctionCall(function);
+  ConfigValuesGenerator gen(&config->config_values(), scope,
+                            function->function(), input_dir, err);
+  gen.Run();
+  if (err->has_error())
+    return Value();
+
+  // Mark as complete.
+  {
+    base::AutoLock lock(tree->lock());
+    tree->MarkItemGeneratedLocked(label);
+  }
+  return Value();
+}
+
+Value ExecuteDeclareArgs(Scope* scope,
+                         const FunctionCallNode* function,
+                         const std::vector<Value>& args,
+                         Err* err) {
+  // Only allow this to be called once. We use a variable in the current scope
+  // with a name the parser will reject if the user tried to type it.
+  const char did_declare_args_var[] = "@@declared_args";
+  if (scope->GetValue(did_declare_args_var)) {
+    *err = Err(function->function(), "Duplicate call to declared_args.");
+    err->AppendSubErr(
+        Err(scope->GetValue(did_declare_args_var)->origin()->GetRange(),
+                            "See the original call."));
+    return Value();
+  }
+
+  // Find the root scope where the values will be set.
+  Scope* root = scope->mutable_containing();
+  if (!root || root->containing() || !scope->IsProcessingBuildConfig()) {
+    *err = Err(function->function(), "declare_args called incorrectly."
+        "It must be called only from the build config script and in the "
+        "root scope.");
+    return Value();
+  }
+
+  // Take all variables set in the current scope as default values and put
+  // them in the parent scope. The values in the current scope are the defaults,
+  // then we apply the external args to this list.
+  Scope::KeyValueVector values;
+  scope->GetCurrentScopeValues(&values);
+  for (size_t i = 0; i < values.size(); i++) {
+    // TODO(brettw) actually import the arguments from the command line rather
+    // than only using the defaults.
+    root->SetValue(values[i].first, values[i].second,
+                   values[i].second.origin());
+  }
+
+  scope->SetValue(did_declare_args_var, Value(function, 1), NULL);
+  return Value();
+}
+
+Value ExecuteImport(Scope* scope,
+                    const FunctionCallNode* function,
+                    const std::vector<Value>& args,
+                    Err* err) {
+  if (!EnsureSingleStringArg(function, args, err) ||
+      !EnsureNotProcessingImport(function, scope, err))
+    return Value();
+
+  const SourceDir input_dir = SourceDirForFunctionCall(function);
+  SourceFile import_file =
+      input_dir.ResolveRelativeFile(args[0].string_value());
+  scope->settings()->import_manager().DoImport(import_file, function,
+                                               scope, err);
+  return Value();
+}
+
+Value ExecuteTemplate(Scope* scope,
+                      const FunctionCallNode* invocation,
+                      const std::vector<Value>& args,
+                      BlockNode* block,
+                      const FunctionCallNode* rule,
+                      Err* err) {
+  if (!EnsureNotProcessingImport(invocation, scope, err))
+    return Value();
+  Scope block_scope(scope);
+  if (!FillTargetBlockScope(scope, invocation,
+                            invocation->function().value().data(),
+                            block, args, &block_scope, err))
+    return Value();
+
+  // Run the block for the rule invocation.
+  block->ExecuteBlockInScope(&block_scope, err);
+  if (err->has_error())
+    return Value();
+
+  // Now run the rule itself with that block as the current scope.
+  rule->block()->ExecuteBlockInScope(&block_scope, err);
+  if (err->has_error())
+    return Value();
+
+  return Value();
+}
+
+Value ExecuteSetDefaults(Scope* scope,
+                         const FunctionCallNode* function,
+                         const std::vector<Value>& args,
+                         BlockNode* block,
+                         Err* err) {
+  if (!EnsureSingleStringArg(function, args, err))
+    return Value();
+  const std::string& target_type(args[0].string_value());
+
+  // Ensure there aren't defaults already set.
+  if (scope->GetTargetDefaults(target_type)) {
+    *err = Err(function->function(),
+               "This target type defaults were already set.");
+    return Value();
+  }
+
+  // Execute the block in a new scope that has a parent of the containing
+  // scope.
+  Scope block_scope(scope);
+  if (!FillTargetBlockScope(scope, function,
+                            function->function().value().data(),
+                            block, args, &block_scope, err))
+    return Value();
+
+  // Run the block for the rule invocation.
+  block->ExecuteBlockInScope(&block_scope, err);
+  if (err->has_error())
+    return Value();
+
+  // Now copy the values set on the scope we made into the free-floating one
+  // (with no containing scope) used to hold the target defaults.
+  Scope* dest = scope->MakeTargetDefaults(target_type);
+  block_scope.NonRecursiveMergeTo(dest, function, "<SHOULD NOT FAIL>", err);
+  return Value();
+}
+
+Value ExecuteSetSourcesAssignmentFilter(Scope* scope,
+                                        const FunctionCallNode* function,
+                                        const std::vector<Value>& args,
+                                        Err* err) {
+  if (args.size() != 1) {
+    *err = Err(function, "set_sources_assignment_filter takes one argument.");
+  } else {
+    scoped_ptr<PatternList> f(new PatternList);
+    f->SetFromValue(args[0], err);
+    if (!err->has_error())
+      scope->set_sources_assignment_filter(f.Pass());
+  }
+  return Value();
+}
+
+// void print(...)
+// prints all arguments to the console separated by spaces.
+Value ExecutePrint(const std::vector<Value>& args, Err* err) {
+  for (size_t i = 0; i < args.size(); i++) {
+    if (i != 0)
+      std::cout << " ";
+    std::cout << args[i].ToString();
+  }
+  std::cout << std::endl;
+  return Value();
+}
+
+}  // namespace
+
+// ----------------------------------------------------------------------------
+
+namespace functions {
+
+const char kAssert[] = "assert";
+const char kComponent[] = "component";
+const char kConfig[] = "config";
+const char kCopy[] = "copy";
+const char kCustom[] = "custom";
+const char kDeclareArgs[] = "declare_args";
+const char kExecScript[] = "exec_script";
+const char kExecutable[] = "executable";
+const char kGroup[] = "group";
+const char kImport[] = "import";
+const char kPrint[] = "print";
+const char kProcessFileTemplate[] = "process_file_template";
+const char kReadFile[] = "read_file";
+const char kSetDefaults[] = "set_defaults";
+const char kSetDefaultToolchain[] = "set_default_toolchain";
+const char kSetSourcesAssignmentFilter[] = "set_sources_assignment_filter";
+const char kSharedLibrary[] = "shared_library";
+const char kStaticLibrary[] = "static_library";
+const char kTemplate[] = "template";
+const char kTool[] = "tool";
+const char kToolchain[] = "toolchain";
+const char kTest[] = "test";
+const char kWriteFile[] = "write_file";
+
+}  // namespace functions
+
+// ----------------------------------------------------------------------------
+
+bool EnsureNotProcessingImport(const ParseNode* node,
+                               const Scope* scope,
+                               Err* err) {
+  if (scope->IsProcessingImport()) {
+    *err = Err(node, "Not valid from an import.",
+        "We need to talk about this thing you are doing here. Doing this\n"
+        "kind of thing from an imported file makes me feel like you are\n"
+        "abusing me. Imports are for defining defaults, variables, and rules.\n"
+        "The appropriate place for this kind of thing is really in a normal\n"
+        "BUILD file.");
+    return false;
+  }
+  return true;
+}
+
+bool EnsureNotProcessingBuildConfig(const ParseNode* node,
+                                    const Scope* scope,
+                                    Err* err) {
+  if (scope->IsProcessingBuildConfig()) {
+    *err = Err(node, "Not valid from the build config.",
+        "You can't do this kind of thing from the build config script, "
+        "silly!\nPut it in a regular BUILD file.");
+    return false;
+  }
+  return true;
+}
+
+bool FillTargetBlockScope(const Scope* scope,
+                          const FunctionCallNode* function,
+                          const char* target_type,
+                          const BlockNode* block,
+                          const std::vector<Value>& args,
+                          Scope* block_scope,
+                          Err* err) {
+  if (!block) {
+    FillNeedsBlockError(function, err);
+    return false;
+  }
+
+  // Copy the target defaults, if any, into the scope we're going to execute
+  // the block in.
+  const Scope* default_scope = scope->GetTargetDefaults(target_type);
+  if (default_scope) {
+    if (!default_scope->NonRecursiveMergeTo(block_scope, function,
+                                            "target defaults", err))
+      return false;
+  }
+
+  // The name is the single argument to the target function.
+  if (!EnsureSingleStringArg(function, args, err))
+    return false;
+
+  // Set the target name variable to the current target, and mark it used
+  // because we don't want to issue an error if the script ignores it.
+  const base::StringPiece target_name("target_name");
+  block_scope->SetValue(target_name, Value(function, args[0].string_value()),
+                        function);
+  block_scope->MarkUsed(target_name);
+  return true;
+}
+
+bool EnsureSingleStringArg(const FunctionCallNode* function,
+                           const std::vector<Value>& args,
+                           Err* err) {
+  if (args.size() != 1) {
+    *err = Err(function->function(), "Incorrect arguments.",
+               "This function requires a single string argument.");
+    return false;
+  }
+  return args[0].VerifyTypeIs(Value::STRING, err);
+}
+
+const SourceDir& SourceDirForFunctionCall(const FunctionCallNode* function) {
+  return function->function().location().file()->dir();
+}
+
+const Label& ToolchainLabelForScope(const Scope* scope) {
+  return scope->settings()->toolchain()->label();
+}
+
+Label MakeLabelForScope(const Scope* scope,
+                        const FunctionCallNode* function,
+                        const std::string& name) {
+  const SourceDir& input_dir = SourceDirForFunctionCall(function);
+  const Label& toolchain_label = ToolchainLabelForScope(scope);
+  return Label(input_dir, name, toolchain_label.dir(), toolchain_label.name());
+}
+
+Value ExecuteFunction(Scope* scope,
+                      const FunctionCallNode* function,
+                      const std::vector<Value>& args,
+                      BlockNode* block,
+                      Err* err) {
+  const Token& name = function->function();
+  if (block) {
+    // These target generators need to execute the block themselves.
+    if (name.IsIdentifierEqualTo(functions::kComponent))
+      return ExecuteComponent(scope, function, args, block, err);
+    if (name.IsIdentifierEqualTo(functions::kCustom))
+      return ExecuteCustom(scope, function, args, block, err);
+    if (name.IsIdentifierEqualTo(functions::kExecutable))
+      return ExecuteExecutable(scope, function, args, block, err);
+    if (name.IsIdentifierEqualTo(functions::kSetDefaults))
+      return ExecuteSetDefaults(scope, function, args, block, err);
+    if (name.IsIdentifierEqualTo(functions::kSharedLibrary))
+      return ExecuteSharedLibrary(scope, function, args, block, err);
+    if (name.IsIdentifierEqualTo(functions::kStaticLibrary))
+      return ExecuteStaticLibrary(scope, function, args, block, err);
+    if (name.IsIdentifierEqualTo(functions::kGroup))
+      return ExecuteGroup(scope, function, args, block, err);
+    if (name.IsIdentifierEqualTo(functions::kTest))
+      return ExecuteExecutable(scope, function, args, block, err);
+    if (name.IsIdentifierEqualTo(functions::kTemplate))
+      return ExecuteTemplate(scope, function, args, block, err);
+    if (name.IsIdentifierEqualTo(functions::kTool))
+      return ExecuteTool(scope, function, args, block, err);
+    if (name.IsIdentifierEqualTo(functions::kToolchain))
+      return ExecuteToolchain(scope, function, args, block, err);
+
+    const FunctionCallNode* rule =
+        scope->GetTemplate(function->function().value().as_string());
+    if (rule)
+      return ExecuteTemplate(scope, function, args, block, rule, err);
+
+    // FIXME(brettw) This is not right, what if you specify a function that
+    // doesn't take a block but specify one?!?!?
+
+    // The rest of the functions can take a pre-executed block for simplicity.
+    Scope block_scope(scope);
+    block->ExecuteBlockInScope(&block_scope, err);
+    if (err->has_error())
+      return Value();
+
+    if (name.IsIdentifierEqualTo(functions::kConfig))
+      return ExecuteConfig(&block_scope, function, args, err);
+    if (name.IsIdentifierEqualTo(functions::kCopy))
+      return ExecuteCopy(&block_scope, function, args, err);
+    if (name.IsIdentifierEqualTo(functions::kDeclareArgs))
+      return ExecuteDeclareArgs(&block_scope, function, args, err);
+
+    *err = Err(name, "Unknown function.");
+    return Value();
+  }
+
+  if (name.IsIdentifierEqualTo(functions::kAssert))
+    return ExecuteAssert(function, args, err);
+  if (name.IsIdentifierEqualTo(functions::kExecScript))
+    return ExecuteExecScript(scope, function, args, err);
+  if (name.IsIdentifierEqualTo(functions::kImport))
+    return ExecuteImport(scope, function, args, err);
+  if (name.IsIdentifierEqualTo(functions::kPrint))
+    return ExecutePrint(args, err);
+  if (name.IsIdentifierEqualTo(functions::kProcessFileTemplate))
+    return ExecuteProcessFileTemplate(scope, function, args, err);
+  if (name.IsIdentifierEqualTo(functions::kReadFile))
+    return ExecuteReadFile(scope, function, args, err);
+  if (name.IsIdentifierEqualTo(functions::kSetDefaultToolchain))
+    return ExecuteSetDefaultToolchain(scope, function, args, err);
+  if (name.IsIdentifierEqualTo(functions::kSetSourcesAssignmentFilter))
+    return ExecuteSetSourcesAssignmentFilter(scope, function, args, err);
+  if (name.IsIdentifierEqualTo(functions::kWriteFile))
+    return ExecuteWriteFile(scope, function, args, err);
+
+  *err = Err(function, "Unknown function.");
+  return Value();
+}
diff --git a/tools/gn/functions.h b/tools/gn/functions.h
new file mode 100644
index 0000000..806de1d
--- /dev/null
+++ b/tools/gn/functions.h
@@ -0,0 +1,182 @@
+// Copyright (c) 2013 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.
+
+#ifndef TOOLS_GN_FUNCTIONS_H_
+#define TOOLS_GN_FUNCTIONS_H_
+
+#include <string>
+#include <vector>
+
+class Err;
+class BlockNode;
+class FunctionCallNode;
+class Label;
+class ListNode;
+class ParseNode;
+class Scope;
+class SourceDir;
+class Token;
+class Value;
+
+Value ExecuteFunction(Scope* scope,
+                      const FunctionCallNode* function,
+                      const std::vector<Value>& args,
+                      BlockNode* block,  // Optional.
+                      Err* err);
+
+// Function executing functions -----------------------------------------------
+
+Value ExecuteTemplate(Scope* scope,
+                      const FunctionCallNode* function,
+                      const std::vector<Value>& args,
+                      BlockNode* block,
+                      Err* err);
+Value ExecuteExecScript(Scope* scope,
+                        const FunctionCallNode* function,
+                        const std::vector<Value>& args,
+                        Err* err);
+Value ExecuteProcessFileTemplate(Scope* scope,
+                                 const FunctionCallNode* function,
+                                 const std::vector<Value>& args,
+                                 Err* err);
+Value ExecuteReadFile(Scope* scope,
+                      const FunctionCallNode* function,
+                      const std::vector<Value>& args,
+                      Err* err);
+Value ExecuteSetDefaultToolchain(Scope* scope,
+                                 const FunctionCallNode* function,
+                                 const std::vector<Value>& args,
+                                 Err* err);
+Value ExecuteTool(Scope* scope,
+                  const FunctionCallNode* function,
+                  const std::vector<Value>& args,
+                  BlockNode* block,
+                  Err* err);
+Value ExecuteToolchain(Scope* scope,
+                       const FunctionCallNode* function,
+                       const std::vector<Value>& args,
+                       BlockNode* block,
+                       Err* err);
+Value ExecuteWriteFile(Scope* scope,
+                       const FunctionCallNode* function,
+                       const std::vector<Value>& args,
+                       Err* err);
+
+// Target-generating functions.
+Value ExecuteComponent(Scope* scope,
+                       const FunctionCallNode* function,
+                       const std::vector<Value>& args,
+                       BlockNode* block,
+                       Err* err);
+Value ExecuteCopy(Scope* scope,
+                  const FunctionCallNode* function,
+                  const std::vector<Value>& args,
+                  Err* err);
+Value ExecuteCustom(Scope* scope,
+                    const FunctionCallNode* function,
+                    const std::vector<Value>& args,
+                    BlockNode* block,
+                    Err* err);
+Value ExecuteExecutable(Scope* scope,
+                        const FunctionCallNode* function,
+                        const std::vector<Value>& args,
+                        BlockNode* block,
+                        Err* err);
+Value ExecuteSharedLibrary(Scope* scope,
+                           const FunctionCallNode* function,
+                           const std::vector<Value>& args,
+                           BlockNode* block,
+                           Err* err);
+Value ExecuteStaticLibrary(Scope* scope,
+                           const FunctionCallNode* function,
+                           const std::vector<Value>& args,
+                           BlockNode* block,
+                           Err* err);
+Value ExecuteGroup(Scope* scope,
+                   const FunctionCallNode* function,
+                   const std::vector<Value>& args,
+                   BlockNode* block,
+                   Err* err);
+
+// Helper functions -----------------------------------------------------------
+
+// Verifies that the current scope is not processing an import. If it is, it
+// will set the error, blame the given parse node for it, and return false.
+bool EnsureNotProcessingImport(const ParseNode* node,
+                               const Scope* scope,
+                               Err* err);
+
+// Like EnsureNotProcessingImport but checks for running the build config.
+bool EnsureNotProcessingBuildConfig(const ParseNode* node,
+                                    const Scope* scope,
+                                    Err* err);
+
+// Sets up the |block_scope| for executing a target (or something like it).
+// The |scope| is the containing scope. It should have been already set as the
+// parent for the |block_scope| when the |block_scope| was created.
+//
+// This will set up the target defaults and set the |target_name| variable in
+// the block scope to the current target name, which is assumed to be the first
+// argument to the function.
+//
+// On success, returns true. On failure, sets the error and returns false.
+bool FillTargetBlockScope(const Scope* scope,
+                          const FunctionCallNode* function,
+                          const char* target_type,
+                          const BlockNode* block,
+                          const std::vector<Value>& args,
+                          Scope* block_scope,
+                          Err* err);
+
+// Validates that the given function call has one string argument. This is
+// the most common function signature, so it saves space to have this helper.
+// Returns false and sets the error on failure.
+bool EnsureSingleStringArg(const FunctionCallNode* function,
+                           const std::vector<Value>& args,
+                           Err* err);
+
+// Returns the source directory for the file comtaining the given function
+// invocation.
+const SourceDir& SourceDirForFunctionCall(const FunctionCallNode* function);
+
+// Returns the name of the toolchain for the given scope.
+const Label& ToolchainLabelForScope(const Scope* scope);
+
+// Generates a label for the given scope, using the current directory and
+// toolchain, and the given name.
+Label MakeLabelForScope(const Scope* scope,
+                        const FunctionCallNode* function,
+                        const std::string& name);
+
+// Function name constants ----------------------------------------------------
+
+namespace functions {
+
+extern const char kAssert[];
+extern const char kComponent[];
+extern const char kConfig[];
+extern const char kCopy[];
+extern const char kCustom[];
+extern const char kDeclareArgs[];
+extern const char kExecScript[];
+extern const char kExecutable[];
+extern const char kGroup[];
+extern const char kImport[];
+extern const char kPrint[];
+extern const char kProcessFileTemplate[];
+extern const char kReadFile[];
+extern const char kSetDefaults[];
+extern const char kSetDefaultToolchain[];
+extern const char kSetSourcesAssignmentFilter[];
+extern const char kSharedLibrary[];
+extern const char kStaticLibrary[];
+extern const char kTemplate[];
+extern const char kTest[];
+extern const char kTool[];
+extern const char kToolchain[];
+extern const char kWriteFile[];
+
+}  // namespace functions
+
+#endif  // TOOLS_GN_FUNCTIONS_H_
diff --git a/tools/gn/functions_target.cc b/tools/gn/functions_target.cc
new file mode 100644
index 0000000..a5991e7
--- /dev/null
+++ b/tools/gn/functions_target.cc
@@ -0,0 +1,221 @@
+// Copyright (c) 2013 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/functions.h"
+
+#include "tools/gn/err.h"
+#include "tools/gn/parse_tree.h"
+#include "tools/gn/scope.h"
+#include "tools/gn/target_generator.h"
+#include "tools/gn/value.h"
+
+namespace {
+
+Value ExecuteGenericTarget(const char* target_type,
+                           Scope* scope,
+                           const FunctionCallNode* function,
+                           const std::vector<Value>& args,
+                           BlockNode* block,
+                           Err* err) {
+  if (!EnsureNotProcessingImport(function, scope, err) ||
+      !EnsureNotProcessingBuildConfig(function, scope, err))
+    return Value();
+  Scope block_scope(scope);
+  if (!FillTargetBlockScope(scope, function, target_type, block,
+                            args, &block_scope, err))
+    return Value();
+
+  block->ExecuteBlockInScope(&block_scope, err);
+  if (err->has_error())
+    return Value();
+
+  TargetGenerator::GenerateTarget(&block_scope, function->function(), args,
+                                  target_type, err);
+
+  block_scope.CheckForUnusedVars(err);
+  return Value();
+}
+
+}  // namespace
+
+Value ExecuteComponent(Scope* scope,
+                       const FunctionCallNode* function,
+                       const std::vector<Value>& args,
+                       BlockNode* block,
+                       Err* err) {
+  // A component is either a shared or static library, depending on the value
+  // of |component_mode|.
+  const Value* component_mode_value = scope->GetValue("component_mode");
+
+  static const char helptext[] =
+      "You're declaring a component here but have not defined "
+      "\"component_mode\" to\neither \"shared_library\" or \"static_library\".";
+  if (!component_mode_value) {
+    *err = Err(function->function(), "No component mode set.", helptext);
+    return Value();
+  }
+  if (component_mode_value->type() != Value::STRING ||
+      (component_mode_value->string_value() != functions::kSharedLibrary &&
+       component_mode_value->string_value() != functions::kStaticLibrary)) {
+    *err = Err(function->function(), "Invalid component mode set.", helptext);
+    return Value();
+  }
+  const std::string& component_mode = component_mode_value->string_value();
+
+  if (!EnsureNotProcessingImport(function, scope, err))
+    return Value();
+  Scope block_scope(scope);
+  if (!FillTargetBlockScope(scope, function, component_mode.c_str(), block,
+                            args, &block_scope, err))
+    return Value();
+
+  block->ExecuteBlockInScope(&block_scope, err);
+  if (err->has_error())
+    return Value();
+
+  TargetGenerator::GenerateTarget(&block_scope, function->function(), args,
+                                  component_mode, err);
+  return Value();
+}
+
+Value ExecuteCopy(Scope* scope,
+                  const FunctionCallNode* function,
+                  const std::vector<Value>& args,
+                  Err* err) {
+  if (!EnsureNotProcessingImport(function, scope, err) ||
+      !EnsureNotProcessingBuildConfig(function, scope, err))
+    return Value();
+  TargetGenerator::GenerateTarget(scope, function->function(), args,
+                                  functions::kCopy, err);
+  return Value();
+}
+
+/*
+custom: Declare a script-generated target.
+
+  This target type allows you to run a script over a set of sources files and
+  generate a set of output files.
+
+  The script will be executed with the given arguments with the current
+  directory being that of the current BUILD file.
+
+  There are two modes. The first mode is the "per-file" mode where you
+  specify a list of sources and the script is run once for each one as a build
+  rule. In this case, each file specified in the |outputs| variable must be
+  unique when applied to each source file (normally you would reference
+  "{{source_name_part}}" from within each one) or the build system will get
+  confused about how to build those files. You should use the |data| variable
+  to list all additional dependencies of your script: these will be added
+  as dependencies for each build step.
+
+  The second mode is when you just want to run a script once rather than as a
+  general rule over a set of files. In this case you don't list any sources.
+  Dependencies of your script are specified only in the |data| variable and
+  your |outputs| variable should just list all outputs.
+
+Variables:
+
+  args, data, deps, outputs, script*, sources
+  * = required
+
+  There are some special substrings that will be searched for when processing
+  some variables:
+
+    "{{source}}"
+        Expanded in |args|, this is the name of the source file relative to the
+        current directory when running the script. This is how you specify
+        the current input file to your script.
+
+    "{{source_name_part}}"
+        Expanded in |args| and |outputs|, this is just the filename part of the
+        current source file with no directory or extension. This is how you
+        specify a name transoformation to the output. Normally you would
+        write an output as "$target_output_dir/{{source_name_part}}.o".
+
+  All |outputs| files must be inside the output directory of the build. You
+  would generally use "$target_output_dir" or "$target_gen_dir" to reference
+  the output or generated intermediate file directories, respectively.
+
+Examples:
+
+  custom("general_rule") {
+    script = "do_processing.py"
+    sources = [ "foo.idl" ]
+    data = [ "my_configuration.txt" ]
+    outputs = [ "$target_gen_dir/{{source_name_part}}.h" ]
+    args = [ "{{source}}",
+             "-o", "$relative_target_gen_dir/{{source_name_part}}.h" ]
+  }
+
+  custom("just_run_this_guy_once") {
+    script = "doprocessing.py"
+    data = [ "my_configuration.txt" ]
+    outputs = [ "$target_gen_dir/insightful_output.txt" ]
+    args = [ "--output_dir", $target_gen_dir ]
+  }
+*/
+Value ExecuteCustom(Scope* scope,
+                    const FunctionCallNode* function,
+                    const std::vector<Value>& args,
+                    BlockNode* block,
+                    Err* err) {
+  return ExecuteGenericTarget(functions::kCustom, scope, function, args,
+                              block, err);
+}
+
+Value ExecuteExecutable(Scope* scope,
+                        const FunctionCallNode* function,
+                        const std::vector<Value>& args,
+                        BlockNode* block,
+                        Err* err) {
+  return ExecuteGenericTarget(functions::kExecutable, scope, function, args,
+                              block, err);
+}
+
+Value ExecuteSharedLibrary(Scope* scope,
+                           const FunctionCallNode* function,
+                           const std::vector<Value>& args,
+                           BlockNode* block,
+                           Err* err) {
+  return ExecuteGenericTarget(functions::kSharedLibrary, scope, function, args,
+                              block, err);
+}
+
+Value ExecuteStaticLibrary(Scope* scope,
+                           const FunctionCallNode* function,
+                           const std::vector<Value>& args,
+                           BlockNode* block,
+                           Err* err) {
+  return ExecuteGenericTarget(functions::kStaticLibrary, scope, function, args,
+                              block, err);
+}
+
+/*
+group: Declare a group of targets.
+
+  This target type allows you to create meta-targets that just collect a set
+  of dependencies into one named target.
+
+Variables:
+
+  deps
+
+Example:
+
+  group("all") {
+    deps = [
+      "//project:runner",
+      "//project:unit_tests",
+    ]
+  }
+*/
+Value ExecuteGroup(Scope* scope,
+                   const FunctionCallNode* function,
+                   const std::vector<Value>& args,
+                   BlockNode* block,
+                   Err* err) {
+  return ExecuteGenericTarget(functions::kGroup, scope, function, args,
+                              block, err);
+}
+
diff --git a/tools/gn/generate_test_gn_data.cc b/tools/gn/generate_test_gn_data.cc
new file mode 100644
index 0000000..017c4c4
--- /dev/null
+++ b/tools/gn/generate_test_gn_data.cc
@@ -0,0 +1,129 @@
+// Copyright (c) 2013 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 <fstream>
+#include <iostream>
+
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
+
+// Usage: just run in the directory where you want your test source root to be.
+
+int files_written = 0;
+int targets_written = 0;
+
+base::FilePath UTF8ToFilePath(const std::string& s) {
+#if defined(OS_WIN)
+  return base::FilePath(UTF8ToWide(s));
+#else
+  return base::FilePath(s);
+#endif
+}
+
+std::string FilePathToUTF8(const base::FilePath& path) {
+#if defined(OS_WIN)
+  return WideToUTF8(path.value());
+#else
+  return path.value();
+#endif
+}
+
+base::FilePath RepoPathToPathName(const std::vector<int>& repo_path) {
+  base::FilePath ret;
+  for (size_t i = 0; i < repo_path.size(); i++) {
+    ret = ret.Append(UTF8ToFilePath(base::IntToString(repo_path[i])));
+  }
+  return ret;
+}
+
+std::string TargetIndexToLetter(int target_index) {
+  char ret[2];
+  ret[0] = 'a' + target_index;
+  ret[1] = 0;
+  return ret;
+}
+
+std::string RepoPathToTargetName(const std::vector<int>& repo_path,
+                                 int target_index) {
+  std::string ret;
+  for (size_t i = 0; i < repo_path.size(); i++) {
+    if (i != 0)
+      ret.push_back('_');
+    ret.append(base::IntToString(repo_path[i]));
+  }
+  ret += TargetIndexToLetter(target_index);
+  return ret;
+}
+
+std::string RepoPathToFullTargetName(const std::vector<int>& repo_path,
+                                 int target_index) {
+  std::string ret;
+  for (size_t i = 0; i < repo_path.size(); i++) {
+    ret.push_back('/');
+    ret.append(base::IntToString(repo_path[i]));
+  }
+
+  ret += ":" + RepoPathToTargetName(repo_path, target_index);
+  return ret;
+}
+
+void WriteLevel(const std::vector<int>& repo_path,
+                int spread,
+                int max_depth,
+                int targets_per_level,
+                int files_per_target) {
+  base::FilePath dirname = RepoPathToPathName(repo_path);
+  base::FilePath filename = dirname.AppendASCII("BUILD.gn");
+  std::cout << "Writing " << FilePathToUTF8(filename) << "\n";
+
+  // Don't keep the file open while recursing.
+  {
+    file_util::CreateDirectory(dirname);
+
+    std::ofstream file;
+    file.open(FilePathToUTF8(filename).c_str(),
+              std::ios_base::out | std::ios_base::binary);
+    files_written++;
+
+    for (int i = 0; i < targets_per_level; i++) {
+      targets_written++;
+      file << "executable(\"" << RepoPathToTargetName(repo_path, i)
+           << "\") {\n";
+      file << "  sources = [\n";
+      for (int f = 0; f < files_per_target; f++)
+        file << "    \"" << base::IntToString(f) << ".cc\",\n";
+
+      if (repo_path.size() < (size_t)max_depth) {
+        file << "  ]\n";
+        file << "  deps = [\n";
+        for (int d = 0; d < spread; d++) {
+          std::vector<int> cur = repo_path;
+          cur.push_back(d);
+          for (int t = 0; t < targets_per_level; t++)
+            file << "    \"" << RepoPathToFullTargetName(cur, t) << "\",\n";
+        }
+      }
+      file << "  ]\n}\n\n";
+    }
+  }
+  if (repo_path.size() < (size_t)max_depth) {
+    // Recursively generate subdirs.
+    for (int i = 0; i < spread; i++) {
+      std::vector<int> cur = repo_path;
+      cur.push_back(i);
+      WriteLevel(cur, spread, max_depth, targets_per_level, files_per_target);
+    }
+  }
+}
+
+int main() {
+  WriteLevel(std::vector<int>(), 5, 4, 3, 50);  // 781 files, 2343 targets
+  //WriteLevel(std::vector<int>(), 6, 4, 2, 50);
+  std::cout << "Wrote " << files_written << " files and "
+            << targets_written << " targets.\n";
+  return 0;
+}
diff --git a/tools/gn/gn.gyp b/tools/gn/gn.gyp
new file mode 100644
index 0000000..4904e3d
--- /dev/null
+++ b/tools/gn/gn.gyp
@@ -0,0 +1,171 @@
+{

+  'variables': {

+    'chromium_code': 1,

+  },

+  'targets': [

+    {

+      'target_name': 'gn_lib',

+      'type': 'static_library',

+      'dependencies': [

+        '../../base/base.gyp:base',

+      ],

+      'sources': [

+        'build_settings.cc',

+        'build_settings.h',

+        'command_desc.cc',

+        'command_desc.h',

+        'command_gen.cc',

+        'command_gen.h',

+        'commands.h',

+        'config.cc',

+        'config.h',

+        'config_values.cc',

+        'config_values.h',

+        'config_values_extractors.cc',

+        'config_values_extractors.h',

+        'config_values_generator.cc',

+        'config_values_generator.h',

+        'err.cc',

+        'err.h',

+        'escape.cc',

+        'escape.h',

+        'file_template.cc',

+        'file_template.h',

+        'filesystem_utils.cc',

+        'filesystem_utils.h',

+        'functions_target.cc',

+        'functions.cc',

+        'functions.h',

+        'function_exec_script.cc',

+        'function_process_file_template.cc',

+        'function_read_file.cc',

+        'function_set_default_toolchain.cc',

+        'function_template.cc',

+        'function_toolchain.cc',

+        'function_write_file.cc',

+        'import_manager.cc',

+        'import_manager.h',

+        'input_conversion.cc',

+        'input_conversion.h',

+        'input_file.cc',

+        'input_file.h',

+        'input_file_manager.cc',

+        'input_file_manager.h',

+        'item.cc',

+        'item.h',

+        'item_node.cc',

+        'item_node.h',

+        'item_tree.cc',

+        'item_tree.h',

+        'label.cc',

+        'label.h',

+        'location.h',

+        'ninja_build_writer.cc',

+        'ninja_build_writer.h',

+        'ninja_helper.cc',

+        'ninja_helper.h',

+        'ninja_target_writer.cc',

+        'ninja_target_writer.h',

+        'ninja_toolchain_writer.cc',

+        'ninja_toolchain_writer.h',

+        'ninja_writer.cc',

+        'ninja_writer.h',

+        'operators.cc',

+        'operators.h',

+        'output_file.h',

+        'parse_tree.cc',

+        'parse_tree.h',

+        'parser.cc',

+        'parser.h',

+        'path_output.cc',

+        'path_output.h',

+        'pattern.cc',

+        'pattern.h',

+        'scheduler.cc',

+        'scheduler.h',

+        'scope.cc',

+        'scope.h',

+        'scope_per_file_provider.cc',

+        'scope_per_file_provider.h',

+        'settings.cc',

+        'settings.h',

+        'setup.cc',

+        'setup.h',

+        'source_dir.cc',

+        'source_dir.h',

+        'source_file.cc',

+        'source_file.h',

+        'standard_out.cc',

+        'standard_out.h',

+        'string_utils.cc',

+        'string_utils.h',

+        'target.cc',

+        'target.h',

+        'target_generator.cc',

+        'target_generator.h',

+        'target_manager.cc',

+        'target_manager.h',

+        'token.cc',

+        'token.h',

+        'tokenizer.cc',

+        'tokenizer.h',

+        'toolchain.cc',

+        'toolchain.h',

+        'toolchain_manager.cc',

+        'toolchain_manager.h',

+        'value.cc',

+        'value.h',

+        'value_extractors.cc',

+        'value_extractors.h',

+      ],

+    },

+    {

+      'target_name': 'gn',

+      'type': 'executable',

+      'sources': [

+        'gn_main.cc',

+      ],

+      'dependencies': [

+        'gn_lib',

+        '../../base/base.gyp:base',

+        '../../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',

+      ],

+    },

+    {

+      'target_name': 'gn_unittests',

+      'type': '<(gtest_target_type)',

+      'sources': [

+        'escape_unittest.cc',

+        'file_template_unittest.cc',

+        'filesystem_utils_unittest.cc',

+        'input_conversion_unittest.cc',

+        'label_unittest.cc',

+        'ninja_helper_unittest.cc',

+        'parser_unittest.cc',

+        'path_output_unittest.cc',

+        'pattern_unittest.cc',

+        'source_dir_unittest.cc',

+        'string_utils_unittest.cc',

+        'target_generator_unittest.cc',

+        'target_manager_unittest.cc',

+        'tokenizer_unittest.cc',

+      ],

+      'dependencies': [

+        'gn_lib',

+        '../../base/base.gyp:run_all_unittests',

+        '../../base/base.gyp:test_support_base',

+        '../../testing/gtest.gyp:gtest',

+      ],

+    },

+    {

+      'target_name': 'generate_test_gn_data',

+      'type': 'executable',

+      'sources': [

+        'generate_test_gn_data.cc',

+      ],

+      'dependencies': [

+        '../../base/base.gyp:base',

+      ],

+    }

+  ],

+}

diff --git a/tools/gn/gn_main.cc b/tools/gn/gn_main.cc
new file mode 100644
index 0000000..6f5b31e
--- /dev/null
+++ b/tools/gn/gn_main.cc
@@ -0,0 +1,59 @@
+// Copyright (c) 2013 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 "base/at_exit.h"
+#include "base/command_line.h"
+#include "base/strings/utf_string_conversions.h"
+#include "tools/gn/commands.h"
+#include "tools/gn/err.h"
+#include "tools/gn/location.h"
+
+namespace {
+
+std::vector<std::string> GetArgs(const CommandLine& cmdline) {
+  CommandLine::StringVector in_args = cmdline.GetArgs();
+#if defined(OS_WIN)
+  std::vector<std::string> out_args;
+  for (size_t i = 0; i < in_args.size(); i++)
+    out_args.push_back(base::WideToUTF8(in_args[i]));
+  return out_args;
+#else
+  return in_args;
+#endif
+}
+
+}  // namespace
+
+int main(int argc, char** argv) {
+  base::AtExitManager at_exit;
+  CommandLine::Init(argc, argv);
+
+  std::vector<std::string> args = GetArgs(*CommandLine::ForCurrentProcess());
+
+  std::string command;
+  if (args.empty()) {
+    command = "gen";
+  } else {
+    command = args[0];
+    args.erase(args.begin());
+  }
+
+  int retval = 0;
+  if (command == "gen") {
+    retval = RunGenCommand(args);
+  } else if (command == "desc" || command == "wtf") {
+    retval = RunDescCommand(args);
+  } else if (command == "deps") {
+    retval = RunDepsCommand(args);
+  } else if (command == "tree") {
+    retval = RunTreeCommand(args);
+  } else {
+    Err(Location(),
+        "Command \"" + command + "\" unknown.").PrintToStdout();
+    retval = 1;
+  }
+
+  exit(retval);  // Don't free memory, it can be really slow!
+  return retval;
+}
diff --git a/tools/gn/import_manager.cc b/tools/gn/import_manager.cc
new file mode 100644
index 0000000..774f866
--- /dev/null
+++ b/tools/gn/import_manager.cc
@@ -0,0 +1,83 @@
+// Copyright (c) 2013 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/import_manager.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "base/stl_util.h"
+#include "tools/gn/parse_tree.h"
+#include "tools/gn/scheduler.h"
+
+namespace {
+
+// Returns a newly-allocated scope on success, null on failure.
+Scope* UncachedImport(const Settings* settings,
+                      const SourceFile& file,
+                      const ParseNode* node_for_err,
+                      Err* err) {
+  const ParseNode* node = g_scheduler->input_file_manager()->SyncLoadFile(
+      node_for_err->GetRange(), settings->build_settings(), file, err);
+  if (!node)
+    return NULL;
+  const BlockNode* block = node->AsBlock();
+  CHECK(block);
+
+  scoped_ptr<Scope> scope(new Scope(settings->base_config()));
+  scope->SetProcessingImport();
+  block->ExecuteBlockInScope(scope.get(), err);
+  if (err->has_error())
+    return NULL;
+  scope->ClearProcessingImport();
+
+  return scope.release();
+}
+
+}  // namesapce
+
+ImportManager::ImportManager() {
+}
+
+ImportManager::~ImportManager() {
+  STLDeleteContainerPairSecondPointers(imports_.begin(), imports_.end());
+}
+
+bool ImportManager::DoImport(const SourceFile& file,
+                             const ParseNode* node_for_err,
+                             Scope* scope,
+                             Err* err) {
+  // See if we have a cached import, but be careful to actually do the scope
+  // copying outside of the lock.
+  const Scope* imported_scope = NULL;
+  {
+    base::AutoLock lock(lock_);
+    ImportMap::const_iterator found = imports_.find(file);
+    if (found != imports_.end())
+      imported_scope = found->second;
+  }
+
+  if (!imported_scope) {
+    // Do a new import of the file.
+    imported_scope = UncachedImport(scope->settings(), file,
+                                    node_for_err, err);
+    if (!imported_scope)
+      return false;
+
+    // We loaded the file outside the lock. This means that there could be a
+    // race and the file was already loaded on a background thread. Recover
+    // from this and use the existing one if that happens.
+    {
+      base::AutoLock lock(lock_);
+      ImportMap::const_iterator found = imports_.find(file);
+      if (found != imports_.end()) {
+        delete imported_scope;
+        imported_scope = found->second;
+      } else {
+        imports_[file] = imported_scope;
+      }
+    }
+  }
+
+  return imported_scope->NonRecursiveMergeTo(scope, node_for_err,
+                                             "import", err);
+}
diff --git a/tools/gn/import_manager.h b/tools/gn/import_manager.h
new file mode 100644
index 0000000..725a5b2
--- /dev/null
+++ b/tools/gn/import_manager.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2013 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.
+
+#ifndef TOOLS_GN_IMPORT_MANAGER_H_
+#define TOOLS_GN_IMPORT_MANAGER_H_
+
+#include <map>
+
+#include "base/synchronization/lock.h"
+
+class Err;
+class ParseNode;
+class Scope;
+class SourceFile;
+
+// Provides a cache of the results of importing scopes so the results can
+// be re-used rather than running the imported files multiple times.
+class ImportManager {
+ public:
+  ImportManager();
+  ~ImportManager();
+
+  // Does an import of the given file into the given scope. On error, sets the
+  // error and returns false.
+  bool DoImport(const SourceFile& file,
+                const ParseNode* node_for_err,
+                Scope* scope,
+                Err* err);
+
+ private:
+  base::Lock lock_;
+
+  // Owning pointers to the scopes.
+  typedef std::map<SourceFile, const Scope*> ImportMap;
+  ImportMap imports_;
+
+  DISALLOW_COPY_AND_ASSIGN(ImportManager);
+};
+
+#endif  // TOOLS_GN_IMPORT_MANAGER_H_
diff --git a/tools/gn/input_conversion.cc b/tools/gn/input_conversion.cc
new file mode 100644
index 0000000..c0a0685
--- /dev/null
+++ b/tools/gn/input_conversion.cc
@@ -0,0 +1,205 @@
+// Copyright (c) 2013 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 "input_conversion.h"
+
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "tools/gn/build_settings.h"
+#include "tools/gn/err.h"
+#include "tools/gn/input_file.h"
+#include "tools/gn/label.h"
+#include "tools/gn/parse_tree.h"
+#include "tools/gn/parser.h"
+#include "tools/gn/scope.h"
+#include "tools/gn/settings.h"
+#include "tools/gn/tokenizer.h"
+#include "tools/gn/value.h"
+
+namespace {
+
+// Returns the "first bit" of some script output for writing to error messages.
+std::string GetExampleOfBadInput(const std::string& input) {
+  std::string result(input);
+
+  // Maybe the result starts with a blank line or something, which we don't
+  // want.
+  TrimWhitespaceASCII(result, TRIM_ALL, &result);
+
+  // Now take the first line, or the first set of chars, whichever is shorter.
+  bool trimmed = false;
+  size_t newline_offset = result.find('\n');
+  if (newline_offset != std::string::npos) {
+    trimmed = true;
+    result.resize(newline_offset);
+  }
+  TrimWhitespaceASCII(result, TRIM_ALL, &result);
+
+  const int kMaxSize = 50;
+  if (result.size() > kMaxSize) {
+    trimmed = true;
+    result.resize(kMaxSize);
+  }
+
+  if (trimmed)
+    result.append("...");
+  return result;
+}
+
+// When parsing the result as a value, we may get various types of errors.
+// This creates an error message for this case with an optional nested error
+// message to reference. If there is no nested err, pass Err().
+//
+// This code also takes care to rewrite the original error which will reference
+// the temporary InputFile which won't exist when the error is propogated
+// out to a higher level.
+Err MakeParseErr(const std::string& input,
+                 const ParseNode* origin,
+                 const Err& nested) {
+  std::string help_text =
+      "When parsing a result as a \"value\" it should look like a list:\n"
+      "  [ \"a\", \"b\", 5 ]\n"
+      "or a single literal:\n"
+      "  \"my result\"\n"
+      "but instead I got this, which I find very confusing:\n";
+  help_text.append(input);
+  if (nested.has_error())
+    help_text.append("\nThe exact error was:");
+
+  Err result(origin, "Script result wasn't a valid value.", help_text);
+  if (nested.has_error()) {
+    result.AppendSubErr(Err(LocationRange(), nested.message(),
+                            nested.help_text()));
+  }
+  return result;
+}
+
+// Sets the origin of the value and any nested values with the given node.
+void RecursivelySetOrigin(Value* value, const ParseNode* origin) {
+  value->set_origin(origin);
+  if (value->type() == Value::LIST) {
+    std::vector<Value>& list_value = value->list_value();
+    for (size_t i = 0; i < list_value.size(); i++)
+      RecursivelySetOrigin(&list_value[i], origin);
+  }
+}
+
+Value ParseString(const std::string& input,
+                  const ParseNode* origin,
+                  Err* err) {
+  SourceFile empty_source_for_most_vexing_parse;
+  InputFile input_file(empty_source_for_most_vexing_parse);
+  input_file.SetContents(input);
+
+  std::vector<Token> tokens = Tokenizer::Tokenize(&input_file, err);
+  if (err->has_error()) {
+    *err = MakeParseErr(input, origin, *err);
+    return Value();
+  }
+
+  scoped_ptr<ParseNode> expression = Parser::ParseExpression(tokens, err);
+  if (err->has_error()) {
+    *err = MakeParseErr(input, origin, *err);
+    return Value();
+  }
+
+  // It's valid for the result to be a null pointer, this just means that the
+  // script returned nothing.
+  if (!expression)
+    return Value();
+
+  // The result should either be a list or a literal, anything else is
+  // invalid.
+  if (!expression->AsList() && !expression->AsLiteral()) {
+    *err = MakeParseErr(input, origin, Err());
+    return Value();
+  }
+
+  BuildSettings build_settings;
+  Label empty_label;
+  Toolchain toolchain(empty_label);
+  Settings settings(&build_settings, &toolchain, std::string());
+  Scope scope(&settings);
+
+  Err nested_err;
+  Value result = expression->Execute(&scope, &nested_err);
+  if (nested_err.has_error()) {
+    *err = MakeParseErr(input, origin, nested_err);
+    return Value();
+  }
+
+  // The returned value will have references to the temporary parse nodes we
+  // made on the stack. If the values are used in an error message in the
+  // future, this will crash. Reset the origin of all values to be our
+  // containing origin.
+  RecursivelySetOrigin(&result, origin);
+  return result;
+}
+
+Value ParseList(const std::string& input,
+                const ParseNode* origin,
+                Err* err) {
+  Value ret(origin, Value::LIST);
+  std::vector<std::string> as_lines;
+  base::SplitString(input, '\n', &as_lines);
+
+  // Trim empty lines from the end.
+  // Do we want to make this configurable?
+  while (!as_lines.empty() && as_lines[as_lines.size() - 1].empty())
+    as_lines.resize(as_lines.size() - 1);
+
+  ret.list_value().reserve(as_lines.size());
+  for (size_t i = 0; i < as_lines.size(); i++)
+    ret.list_value().push_back(Value(origin, as_lines[i]));
+  return ret;
+}
+
+}  // namespace
+
+/*
+input_conversion: Specifies how to transform input to a variable.
+
+  input_conversion is an argument to read_file and exec_script that specifies
+  how the result of the read operation should be converted into a variable.
+
+  "list lines":
+      Return the file contents as a list, with a string for each line. The
+      newlines will not be present in the result. Empty newlines will be
+      trimmed from the trailing end of the returned list.
+
+  "value":
+      Parse the input as if it was a literal rvalue in a buildfile.
+      Examples of typical program output using this mode:
+        [ "foo", "bar" ]     (result will be a list)
+      or
+        "foo bar"            (result will be a string)
+      or
+        5                    (result will be an integer)
+
+      Note that if the input is empty, the result will be a null value which
+      will produce an error if assigned to a variable.
+
+  "string":
+      Return the file contents into a single string.
+*/
+
+Value ConvertInputToValue(const std::string& input,
+                          const ParseNode* origin,
+                          const Value& input_conversion_value,
+                          Err* err) {
+  if (!input_conversion_value.VerifyTypeIs(Value::STRING, err))
+    return Value();
+  const std::string& input_conversion = input_conversion_value.string_value();
+
+  if (input_conversion == "value")
+    return ParseString(input, origin, err);
+  if (input_conversion == "string")
+    return Value(origin, input);
+  if (input_conversion == "list lines")
+    return ParseList(input, origin, err);
+
+  *err = Err(input_conversion_value, "Not a valid read file mode.",
+             "Have you considered a career in retail?");
+  return Value();
+}
diff --git a/tools/gn/input_conversion.h b/tools/gn/input_conversion.h
new file mode 100644
index 0000000..d15e513
--- /dev/null
+++ b/tools/gn/input_conversion.h
@@ -0,0 +1,26 @@
+// Copyright (c) 2013 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.
+
+#ifndef TOOLS_GN_INPUT_CONVERSION_H_
+#define TOOLS_GN_INPUT_CONVERSION_H_
+
+#include <string>
+
+class Err;
+class ParseNode;
+class Value;
+
+// Converts the given input string (is read from a file or output from a
+// script) to a Value. Conversions as specified in the input_conversion string
+// will be performed. The given origin will be used for constructing the
+// resulting Value.
+//
+// If the conversion string is invalid, the error will be set and an empty
+// value will be returned.
+Value ConvertInputToValue(const std::string& input,
+                          const ParseNode* origin,
+                          const Value& input_conversion_value,
+                          Err* err);
+
+#endif  // TOOLS_GN_INPUT_CONVERSION_H_
diff --git a/tools/gn/input_conversion_unittest.cc b/tools/gn/input_conversion_unittest.cc
new file mode 100644
index 0000000..59c0123
--- /dev/null
+++ b/tools/gn/input_conversion_unittest.cc
@@ -0,0 +1,81 @@
+// Copyright (c) 2013 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 "testing/gtest/include/gtest/gtest.h"
+#include "tools/gn/err.h"
+#include "tools/gn/input_conversion.h"
+#include "tools/gn/value.h"
+
+TEST(InputConversion, String) {
+  Err err;
+  std::string input("\nfoo bar  \n");
+  Value result = ConvertInputToValue(input, NULL, Value(NULL, "string"), &err);
+  EXPECT_FALSE(err.has_error());
+  EXPECT_EQ(Value::STRING, result.type());
+  EXPECT_EQ(input, result.string_value());
+}
+
+TEST(InputConversion, ListLines) {
+  Err err;
+  std::string input("\nfoo\nbar  \n");
+  Value result = ConvertInputToValue(input, NULL, Value(NULL, "list lines"),
+                                     &err);
+  EXPECT_FALSE(err.has_error());
+  EXPECT_EQ(Value::LIST, result.type());
+  ASSERT_EQ(3u, result.list_value().size());
+  EXPECT_EQ("",    result.list_value()[0].string_value());
+  EXPECT_EQ("foo", result.list_value()[1].string_value());
+  EXPECT_EQ("bar", result.list_value()[2].string_value());
+}
+
+TEST(InputConversion, ValueString) {
+  Err err;
+  std::string input("\"str\"");
+  Value result = ConvertInputToValue(input, NULL, Value(NULL, "value"), &err);
+  EXPECT_FALSE(err.has_error());
+  EXPECT_EQ(Value::STRING, result.type());
+  EXPECT_EQ("str", result.string_value());
+}
+
+TEST(InputConversion, ValueInt) {
+  Err err;
+  std::string input("\n\n  6 \n ");
+  Value result = ConvertInputToValue(input, NULL, Value(NULL, "value"), &err);
+  EXPECT_FALSE(err.has_error());
+  EXPECT_EQ(Value::INTEGER, result.type());
+  EXPECT_EQ(6, result.int_value());
+}
+
+TEST(InputConversion, ValueList) {
+  Err err;
+  std::string input("\n [ \"a\", 5]");
+  Value result = ConvertInputToValue(input, NULL, Value(NULL, "value"), &err);
+  EXPECT_FALSE(err.has_error());
+  ASSERT_EQ(Value::LIST, result.type());
+  ASSERT_EQ(2u, result.list_value().size());
+  EXPECT_EQ("a", result.list_value()[0].string_value());
+  EXPECT_EQ(5,   result.list_value()[1].int_value());
+}
+
+TEST(InputConversion, ValueEmpty) {
+  Err err;
+  ConvertInputToValue("", NULL, Value(NULL, "value"), &err);
+}
+
+TEST(InputConversion, ValueError) {
+  Err err;
+  std::string input("\n [ \"a\", 5\nfoo bar");
+  Value result = ConvertInputToValue(input, NULL, Value(NULL, "value"), &err);
+  EXPECT_TRUE(err.has_error());
+
+  // Blocks not allowed.
+  input = "{ foo = 5 }";
+  result = ConvertInputToValue(input, NULL, Value(NULL, "value"), &err);
+  EXPECT_TRUE(err.has_error());
+
+  // Function calls not allowed.
+  input = "print(5)";
+  result = ConvertInputToValue(input, NULL, Value(NULL, "value"), &err);
+  EXPECT_TRUE(err.has_error());
+}
diff --git a/tools/gn/input_file.cc b/tools/gn/input_file.cc
new file mode 100644
index 0000000..b2c3c0b
--- /dev/null
+++ b/tools/gn/input_file.cc
@@ -0,0 +1,30 @@
+// Copyright (c) 2013 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/input_file.h"
+
+#include "base/file_util.h"
+
+InputFile::InputFile(const SourceFile& name)
+    : name_(name),
+      dir_(name_.GetDir()),
+      contents_loaded_(false) {
+}
+
+InputFile::~InputFile() {
+}
+
+void InputFile::SetContents(const std::string& c) {
+  contents_loaded_ = true;
+  contents_ = c;
+}
+
+bool InputFile::Load(const base::FilePath& system_path) {
+  if (file_util::ReadFileToString(system_path, &contents_)) {
+    contents_loaded_ = true;
+    return true;
+  }
+  return false;
+}
+
diff --git a/tools/gn/input_file.h b/tools/gn/input_file.h
new file mode 100644
index 0000000..66cf55c
--- /dev/null
+++ b/tools/gn/input_file.h
@@ -0,0 +1,52 @@
+// Copyright (c) 2013 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.
+
+#ifndef TOOLS_GN_INPUT_FILE_H_
+#define TOOLS_GN_INPUT_FILE_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "tools/gn/source_dir.h"
+#include "tools/gn/source_file.h"
+
+class InputFile {
+ public:
+  InputFile(const SourceFile& name);
+
+  // Constructor for testing. Uses an empty file path and a given contents.
+  //InputFile(const char* contents);
+  ~InputFile();
+
+  const SourceFile& name() const { return name_; }
+
+  // The directory is just a cached version of name_->GetDir() but we get this
+  // a lot so computing it once up front saves a bunch of work.
+  const SourceDir& dir() const { return dir_; }
+
+  const std::string& contents() const {
+    DCHECK(contents_loaded_);
+    return contents_;
+  }
+
+  // For testing and in cases where this input doesn't actually refer to
+  // "a file".
+  void SetContents(const std::string& c);
+
+  // Loads the given file synchronously, returning true on success. This
+  bool Load(const base::FilePath& system_path);
+
+ private:
+  SourceFile name_;
+  SourceDir dir_;
+
+  bool contents_loaded_;
+  std::string contents_;
+
+  DISALLOW_COPY_AND_ASSIGN(InputFile);
+};
+
+#endif  // TOOLS_GN_INPUT_FILE_H_
diff --git a/tools/gn/input_file_manager.cc b/tools/gn/input_file_manager.cc
new file mode 100644
index 0000000..e840ba0
--- /dev/null
+++ b/tools/gn/input_file_manager.cc
@@ -0,0 +1,254 @@
+// Copyright (c) 2013 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/input_file_manager.h"
+
+#include "base/bind.h"
+#include "base/stl_util.h"
+#include "tools/gn/filesystem_utils.h"
+#include "tools/gn/parser.h"
+#include "tools/gn/scheduler.h"
+#include "tools/gn/scope_per_file_provider.h"
+#include "tools/gn/tokenizer.h"
+
+namespace {
+
+void InvokeFileLoadCallback(const InputFileManager::FileLoadCallback& cb,
+                            const ParseNode* node) {
+  cb.Run(node);
+}
+
+}  // namespace
+
+InputFileManager::InputFileData::InputFileData(const SourceFile& file_name)
+    : file(file_name),
+      loaded(false),
+      sync_invocation(false) {
+}
+
+InputFileManager::InputFileData::~InputFileData() {
+}
+
+InputFileManager::InputFileManager() {
+}
+
+InputFileManager::~InputFileManager() {
+  // Should be single-threaded by now.
+  STLDeleteContainerPairSecondPointers(input_files_.begin(),
+                                       input_files_.end());
+}
+
+bool InputFileManager::AsyncLoadFile(const LocationRange& origin,
+                                     const BuildSettings* build_settings,
+                                     const SourceFile& file_name,
+                                     const FileLoadCallback& callback,
+                                     Err* err) {
+  // Try not to schedule callbacks while holding the lock. All cases that don't
+  // want to schedule should return early. Otherwise, this will be scheduled
+  // after we leave the lock.
+  base::Closure schedule_this;
+  {
+    base::AutoLock lock(lock_);
+
+    InputFileMap::const_iterator found = input_files_.find(file_name);
+    if (found == input_files_.end()) {
+      // New file, schedule load.
+      InputFileData* data = new InputFileData(file_name);
+      data->scheduled_callbacks.push_back(callback);
+      input_files_[file_name] = data;
+
+      schedule_this = base::Bind(&InputFileManager::BackgroundLoadFile,
+                                 this,
+                                 origin,
+                                 build_settings,
+                                 file_name,
+                                 &data->file);
+    } else {
+      InputFileData* data = found->second;
+
+      // Prevent mixing async and sync loads. See SyncLoadFile for discussion.
+      if (data->sync_invocation) {
+        g_scheduler->FailWithError(Err(
+            origin, "Load type mismatch.",
+            "The file \"" + file_name.value() + "\" was previously loaded\n"
+            "synchronously (via an import) and now you're trying to load it "
+            "asynchronously\n(via a deps rule). This is a class 2 misdemeanor: "
+            "a single input file must\nbe loaded the same way each time to "
+            "avoid blowing my tiny, tiny mind."));
+        return false;
+      }
+
+      if (data->loaded) {
+        // Can just directly issue the callback on the background thread.
+        schedule_this = base::Bind(&InvokeFileLoadCallback, callback,
+                                   data->parsed_root.get());
+      } else {
+        // Load is pending on this file, schedule the invoke.
+        data->scheduled_callbacks.push_back(callback);
+        return true;
+      }
+    }
+  }
+  g_scheduler->pool()->PostWorkerTaskWithShutdownBehavior(
+      FROM_HERE, schedule_this,
+      base::SequencedWorkerPool::BLOCK_SHUTDOWN);
+  return true;
+}
+
+const ParseNode* InputFileManager::SyncLoadFile(
+    const LocationRange& origin,
+    const BuildSettings* build_settings,
+    const SourceFile& file_name,
+    Err* err) {
+  base::AutoLock lock(lock_);
+
+  InputFileData* data = NULL;
+  InputFileMap::iterator found = input_files_.find(file_name);
+  if (found == input_files_.end()) {
+    base::AutoUnlock unlock(lock_);
+
+    // Haven't seen this file yet, start loading right now.
+    data = new InputFileData(file_name);
+    data->sync_invocation = true;
+    input_files_[file_name] = data;
+
+    if (!LoadFile(origin, build_settings, file_name, &data->file, err))
+      return NULL;
+  } else {
+    // This file has either been loaded or is pending loading.
+    data = found->second;
+
+    if (!data->sync_invocation) {
+      // Don't allow mixing of sync and async loads. If an async load is
+      // scheduled and then a bunch of threads need to load it synchronously
+      // and block on it loading, it could deadlock or at least cause a lot
+      // of wasted CPU while those threads wait for the load to complete (which
+      // may be far back in the input queue).
+      //
+      // We could work around this by promoting the load to a sync load. This
+      // requires a bunch of extra code to either check flags and likely do
+      // extra locking (bad) or to just do both types of load on the file and
+      // deal with the race condition.
+      //
+      // I have no practical way to test this, and generally we should have
+      // all include files processed synchronously and all build files
+      // processed asynchronously, so it doesn't happen in practice.
+      *err = Err(
+          origin, "Load type mismatch.",
+          "The file \"" + file_name.value() + "\" was previously loaded\n"
+          "asynchronously (via a deps rule) and now you're trying to load it "
+          "synchronously.\nThis is a class 2 misdemeanor: a single input file "
+          "must be loaded the same way\neach time to avoid blowing my tiny, "
+          "tiny mind.");
+      return NULL;
+    }
+
+    if (!data->loaded) {
+      // Wait for the already-pending sync load to complete.
+      if (!data->completion_event)
+        data->completion_event.reset(new base::WaitableEvent(false, false));
+      {
+        base::AutoUnlock unlock(lock_);
+        data->completion_event->Wait();
+      }
+    }
+  }
+
+  // The other load could have failed. In this case that error will be printed
+  // to the console, but we need to return something here, so make up a
+  // dummy error.
+  if (!data->parsed_root)
+    *err = Err(origin, "File parse failed");
+  return data->parsed_root.get();
+}
+
+int InputFileManager::GetInputFileCount() const {
+  base::AutoLock lock(lock_);
+  return input_files_.size();
+}
+
+void InputFileManager::GetAllInputFileNames(
+    std::vector<SourceFile>* result) const {
+  base::AutoLock lock(lock_);
+  result->reserve(input_files_.size());
+  for (InputFileMap::const_iterator i = input_files_.begin();
+       i != input_files_.end(); ++i) {
+    result->push_back(i->second->file.name());
+  }
+}
+
+void InputFileManager::BackgroundLoadFile(const LocationRange& origin,
+                                          const BuildSettings* build_settings,
+                                          const SourceFile& name,
+                                          InputFile* file) {
+  Err err;
+  if (!LoadFile(origin, build_settings, name, file, &err))
+    g_scheduler->FailWithError(err);
+}
+
+bool InputFileManager::LoadFile(const LocationRange& origin,
+                                const BuildSettings* build_settings,
+                                const SourceFile& name,
+                                InputFile* file,
+                                Err* err) {
+  // Do all of this stuff outside the lock. We should not give out file
+  // pointers until the read is complete.
+  if (g_scheduler->verbose_logging())
+    g_scheduler->Log("Loading", name.value());
+
+  // Read.
+  base::FilePath primary_path = build_settings->GetFullPath(name);
+  if (!file->Load(primary_path)) {
+    if (!build_settings->secondary_source_path().empty()) {
+      // Fall back to secondary source tree.
+      base::FilePath secondary_path =
+          build_settings->GetFullPathSecondary(name);
+      if (!file->Load(secondary_path)) {
+        *err = Err(origin, "Can't load input file.",
+                   "Unable to load either \n" +
+                   FilePathToUTF8(primary_path) + " or \n" +
+                   FilePathToUTF8(secondary_path));
+        return false;
+      }
+    } else {
+      *err = Err(origin,
+                 "Unable to load \"" + FilePathToUTF8(primary_path) + "\".");
+      return false;
+    }
+  }
+
+  if (g_scheduler->verbose_logging())
+    g_scheduler->Log("Parsing", name.value());
+
+  // Tokenize.
+  std::vector<Token> tokens = Tokenizer::Tokenize(file, err);
+  if (err->has_error())
+    return false;
+
+  // Parse.
+  scoped_ptr<ParseNode> root = Parser::Parse(tokens, err);
+  if (err->has_error())
+    return false;
+  ParseNode* unowned_root = root.get();
+
+  std::vector<FileLoadCallback> callbacks;
+  {
+    base::AutoLock lock(lock_);
+    DCHECK(input_files_.find(name) != input_files_.end());
+
+    InputFileData* data = input_files_[name];
+    data->loaded = true;
+    data->tokens.swap(tokens);
+    data->parsed_root = root.Pass();
+
+    callbacks.swap(data->scheduled_callbacks);
+  }
+
+  // Run pending invocations. Theoretically we could schedule each of these
+  // separately to get some parallelism. But normally there will only be one
+  // item in the list, so that's extra overhead and complexity for no gain.
+  for (size_t i = 0; i < callbacks.size(); i++)
+    callbacks[i].Run(unowned_root);
+  return true;
+}
diff --git a/tools/gn/input_file_manager.h b/tools/gn/input_file_manager.h
new file mode 100644
index 0000000..0f708d5
--- /dev/null
+++ b/tools/gn/input_file_manager.h
@@ -0,0 +1,123 @@
+// Copyright (c) 2013 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.
+
+#ifndef TOOLS_GN_INPUT_FILE_MANAGER_H_
+#define TOOLS_GN_INPUT_FILE_MANAGER_H_
+
+#include <set>
+#include <utility>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/containers/hash_tables.h"
+#include "base/files/file_path.h"
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
+#include "base/synchronization/waitable_event.h"
+#include "tools/gn/build_settings.h"
+#include "tools/gn/input_file.h"
+#include "tools/gn/parse_tree.h"
+#include "tools/gn/settings.h"
+
+class Err;
+class LocationRange;
+class ParseNode;
+class Token;
+
+// Manages loading and parsing files from disk. This doesn't actually have
+// any context for executing the results, so potentially multiple configs
+// could use the same input file (saving parsing).
+//
+// This class is threadsafe.
+//
+// InputFile objects must never be deleted while the program is running since
+// various state points into them.
+class InputFileManager : public base::RefCountedThreadSafe<InputFileManager> {
+ public:
+  // Callback issued when a file is laoded. On auccess, the parse node will
+  // refer to the root block of the file. On failure, this will be NULL.
+  typedef base::Callback<void(const ParseNode*)> FileLoadCallback;
+
+  InputFileManager();
+
+  // Loads the given file and executes the callback on the worker pool.
+  //
+  // There are two types of errors. For errors known synchronously, the error
+  // will be set, it will return false, and no work will be scheduled.
+  //
+  // For parse errors and such that happen in the future, the error will be
+  // logged to the scheduler and the callback will be invoked with a null
+  // ParseNode pointer. The given |origin| will be blamed for the invocation.
+  bool AsyncLoadFile(const LocationRange& origin,
+                     const BuildSettings* build_settings,
+                     const SourceFile& file_name,
+                     const FileLoadCallback& callback,
+                     Err* err);
+
+  // Loads and parses the given file synchronously, returning the root block
+  // corresponding to the parsed result. On error, return NULL and the given
+  // Err is set.
+  const ParseNode* SyncLoadFile(const LocationRange& origin,
+                                const BuildSettings* build_settings,
+                                const SourceFile& file_name,
+                                Err* err);
+
+  int GetInputFileCount() const;
+
+  void GetAllInputFileNames(std::vector<SourceFile>* result) const;
+
+ private:
+  friend class base::RefCountedThreadSafe<InputFileManager>;
+
+  struct InputFileData {
+    InputFileData(const SourceFile& file_name);
+    ~InputFileData();
+
+    // Don't touch this outside the lock until it's marked loaded.
+    InputFile file;
+
+    bool loaded;
+
+    bool sync_invocation;
+
+    // Lists all invocations that need to be executed when the file completes
+    // loading.
+    std::vector<FileLoadCallback> scheduled_callbacks;
+
+    // Event to signal when the load is complete (or fails). This is lazily
+    // created only when a thread is synchronously waiting for this load (which
+    // only happens for imports).
+    scoped_ptr<base::WaitableEvent> completion_event;
+
+    std::vector<Token> tokens;
+
+    // Null before the file is loaded or if loading failed.
+    scoped_ptr<ParseNode> parsed_root;
+  };
+
+  virtual ~InputFileManager();
+
+  void BackgroundLoadFile(const LocationRange& origin,
+                          const BuildSettings* build_settings,
+                          const SourceFile& name,
+                          InputFile* file);
+
+  // Loads the given file. On error, sets the Err and return false.
+  bool LoadFile(const LocationRange& origin,
+                const BuildSettings* build_settings,
+                const SourceFile& name,
+                InputFile* file,
+                Err* err);
+
+  mutable base::Lock lock_;
+
+  // Maps repo-relative filenames to the corresponding owned pointer.
+  typedef base::hash_map<SourceFile, InputFileData*> InputFileMap;
+  InputFileMap input_files_;
+
+  DISALLOW_COPY_AND_ASSIGN(InputFileManager);
+};
+
+#endif  // TOOLS_GN_INPUT_FILE_MANAGER_H_
diff --git a/tools/gn/item.cc b/tools/gn/item.cc
new file mode 100644
index 0000000..747183c
--- /dev/null
+++ b/tools/gn/item.cc
@@ -0,0 +1,31 @@
+// Copyright (c) 2013 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/item.h"
+
+#include "base/logging.h"
+
+Item::Item(const Label& label) : label_(label) {
+}
+
+Item::~Item() {
+}
+
+Config* Item::AsConfig() { return NULL; }
+const Config* Item::AsConfig() const { return NULL; }
+Target* Item::AsTarget() { return NULL; }
+const Target* Item::AsTarget() const { return NULL; }
+Toolchain* Item::AsToolchain() { return NULL; }
+const Toolchain* Item::AsToolchain() const { return NULL; }
+
+std::string Item::GetItemTypeName() const {
+  if (AsConfig())
+    return "config";
+  if (AsTarget())
+    return "target";
+  if (AsToolchain())
+    return "toolchain";
+  NOTREACHED();
+  return "this thing that I have no idea what it is";
+}
diff --git a/tools/gn/item.h b/tools/gn/item.h
new file mode 100644
index 0000000..aa71544
--- /dev/null
+++ b/tools/gn/item.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2013 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.
+
+#ifndef TOOLS_GN_ITEM_H_
+#define TOOLS_GN_ITEM_H_
+
+#include <string>
+
+#include "tools/gn/label.h"
+
+class Config;
+class Target;
+class Toolchain;
+
+// A named item (target, config, etc.) that participates in the dependency
+// graph.
+class Item {
+ public:
+  Item(const Label& label);
+  virtual ~Item();
+
+  const Label& label() const { return label_; }
+
+  // Manual RTTI.
+  virtual Config* AsConfig();
+  virtual const Config* AsConfig() const;
+  virtual Target* AsTarget();
+  virtual const Target* AsTarget() const;
+  virtual Toolchain* AsToolchain();
+  virtual const Toolchain* AsToolchain() const;
+
+  // Returns a name like "target" or "config" for the type of item this is, to
+  // be used in logging and error messages.
+  std::string GetItemTypeName() const;
+
+  // Called when this item is resolved, meaning it and all of its dependents
+  // have no unresolved deps.
+  virtual void OnResolved() {}
+
+ private:
+  Label label_;
+};
+
+#endif  // TOOLS_GN_ITEM_H_
diff --git a/tools/gn/item_node.cc b/tools/gn/item_node.cc
new file mode 100644
index 0000000..776a126
--- /dev/null
+++ b/tools/gn/item_node.cc
@@ -0,0 +1,51 @@
+// Copyright (c) 2013 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/item_node.h"
+
+#include <algorithm>
+
+#include "base/callback.h"
+#include "base/logging.h"
+#include "tools/gn/item.h"
+
+ItemNode::ItemNode(Item* i)
+    : state_(REFERENCED),
+      item_(i) {
+}
+
+ItemNode::~ItemNode() {
+}
+
+void ItemNode::AddDependency(ItemNode* node) {
+  if (direct_dependencies_.find(node) != direct_dependencies_.end())
+    return;  // Already have this dep.
+  direct_dependencies_.insert(node);
+
+  if (node->state() != RESOLVED) {
+    // Wire up the pending resolution info.
+    unresolved_dependencies_.insert(node);
+    node->waiting_on_resolution_.insert(this);
+  }
+}
+
+void ItemNode::MarkDirectDependencyResolved(ItemNode* node) {
+  DCHECK(unresolved_dependencies_.find(node) != unresolved_dependencies_.end());
+  unresolved_dependencies_.erase(node);
+}
+
+void ItemNode::SwapOutWaitingDependencySet(ItemNodeSet* out_set) {
+  waiting_on_resolution_.swap(*out_set);
+}
+
+void ItemNode::SetGenerated() {
+  state_ = GENERATED;
+}
+
+void ItemNode::SetResolved() {
+  state_ = RESOLVED;
+
+  if (!resolved_closure_.is_null())
+    resolved_closure_.Run();
+}
diff --git a/tools/gn/item_node.h b/tools/gn/item_node.h
new file mode 100644
index 0000000..297010f
--- /dev/null
+++ b/tools/gn/item_node.h
@@ -0,0 +1,119 @@
+// Copyright (c) 2013 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.
+
+#ifndef TOOLS_GN_ITEM_NODE_H_
+#define TOOLS_GN_ITEM_NODE_H_
+
+#include <set>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
+#include "tools/gn/location.h"
+
+class Item;
+
+// Represents a node in the depdency tree. It references an Item which is
+// the actual thing.
+//
+// The items and nodes are split apart so that the ItemTree can manipulate
+// the dependencies one one thread while the Item itself is been modified on
+// another.
+class ItemNode {
+ public:
+  enum State {
+    // Another item has referenced this one by name, but we have not yet
+    // encountered this item to know what it is.
+    REFERENCED,
+
+    // This item has been defined but some of the dependencies it references
+    // have not been.
+    GENERATED,
+
+    // All of this item's transitive dependencies have been found and
+    // resolved.
+    RESOLVED,
+  };
+
+  typedef std::set<ItemNode*> ItemNodeSet;
+
+  // Takes ownership of the pointer.
+  // Initial state will be REFERENCED.
+  ItemNode(Item* i);
+  ~ItemNode();
+
+  State state() const { return state_; }
+
+  // This closure will be executed when the item is resolved.
+  void set_resolved_closure(const base::Closure& closure) {
+    resolved_closure_ = closure;
+  }
+
+  const Item* item() const { return item_.get(); }
+  Item* item() { return item_.get(); }
+
+  // Where this was created from, which might be when it was generated or
+  // when it was first referenced from another target.
+  const LocationRange& originally_referenced_from_here() const {
+    return originally_referenced_from_here_;
+  }
+  void set_originally_referenced_from_here(const LocationRange& r) {
+    originally_referenced_from_here_ = r;
+  }
+
+  // Where this was generated from. This will be empty for items that have
+  // been referenced but not generated. Note that this has to be one the
+  // ItemNode because it can be changing from multiple threads and we need
+  // to be sure that access is serialized.
+  const LocationRange& generated_from_here() const {
+    return generated_from_here_;
+  }
+  void set_generated_from_here(const LocationRange& r) {
+    generated_from_here_ = r;
+  }
+
+  const ItemNodeSet& direct_dependencies() const {
+    return direct_dependencies_;
+  }
+  const ItemNodeSet& unresolved_dependencies() const {
+    return unresolved_dependencies_;
+  }
+
+  void AddDependency(ItemNode* node);
+
+  // Removes the given dependency from the unresolved list. Does not do
+  // anything else to update waiters.
+  void MarkDirectDependencyResolved(ItemNode* node);
+
+  // Destructively retrieve the set of waiting nodes.
+  void SwapOutWaitingDependencySet(ItemNodeSet* out_set);
+
+  void SetGenerated();
+  void SetResolved();
+
+ private:
+  State state_;
+  scoped_ptr<Item> item_;
+
+  LocationRange originally_referenced_from_here_;
+  LocationRange generated_from_here_;
+
+  // What to run when this item is resolved.
+  base::Closure resolved_closure_;
+
+  // Everything this item directly depends on.
+  ItemNodeSet direct_dependencies_;
+
+  // Unresolved things this item directly depends on.
+  ItemNodeSet unresolved_dependencies_;
+
+  // These items are waiting on us to be resolved before they can be
+  // resolved. This is the backpointer for unresolved_dependencies_.
+  ItemNodeSet waiting_on_resolution_;
+
+  DISALLOW_COPY_AND_ASSIGN(ItemNode);
+};
+
+#endif  // TOOLS_GN_ITEM_NODE_H_
diff --git a/tools/gn/item_tree.cc b/tools/gn/item_tree.cc
new file mode 100644
index 0000000..a4d1181
--- /dev/null
+++ b/tools/gn/item_tree.cc
@@ -0,0 +1,193 @@
+// Copyright (c) 2013 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/item_tree.h"
+
+#include <algorithm>
+
+#include "base/stl_util.h"
+#include "tools/gn/err.h"
+#include "tools/gn/item.h"
+#include "tools/gn/item_node.h"
+
+namespace {
+
+// Recursively looks in the tree for a given node, returning true if it
+// was found in the dependecy graph. This is used to see if a given node
+// participates in a cycle.
+//
+// Note that "look_for" and "search_in" will be the same node when starting the
+// search, so we don't want to return true in that case.
+//
+// If a cycle is found, the return value will be true and the cycle vector will
+// be filled with the path (in reverse order).
+bool RecursiveFindCycle(const ItemNode* look_for,
+                        const ItemNode* search_in,
+                        std::vector<const ItemNode*>* cycle) {
+  const ItemNode::ItemNodeSet& unresolved =
+      search_in->unresolved_dependencies();
+  for (ItemNode::ItemNodeSet::const_iterator i = unresolved.begin();
+       i != unresolved.end(); ++i) {
+    if (*i == look_for) {
+      cycle->push_back(*i);
+      return true;
+    }
+
+    if (RecursiveFindCycle(look_for, *i, cycle)) {
+      // Found a cycle inside this one, record our path and return.
+      cycle->push_back(*i);
+      return true;
+    }
+  }
+  return false;
+}
+
+}  // namespace
+
+ItemTree::ItemTree() {
+}
+
+ItemTree::~ItemTree() {
+  STLDeleteContainerPairSecondPointers(items_.begin(), items_.end());
+}
+
+ItemNode* ItemTree::GetExistingNodeLocked(const Label& label) {
+  lock_.AssertAcquired();
+  StringToNodeHash::iterator found = items_.find(label);
+  if (found == items_.end())
+    return NULL;
+  return found->second;
+}
+
+void ItemTree::AddNodeLocked(ItemNode* node) {
+  lock_.AssertAcquired();
+  DCHECK(items_.find(node->item()->label()) == items_.end());
+  items_[node->item()->label()] = node;
+}
+
+Err ItemTree::MarkItemGeneratedLocked(const Label& label) {
+  lock_.AssertAcquired();
+  DCHECK(items_.find(label) != items_.end());
+
+  ItemNode* node = items_[label];
+
+  if (!node->unresolved_dependencies().empty()) {
+    // Still some pending dependencies, wait for those to be resolved.
+    node->SetGenerated();
+    return Err();
+  }
+  return MarkItemResolvedLocked(node);
+}
+
+void ItemTree::GetAllItemsLocked(std::vector<const Item*>* dest) const {
+  lock_.AssertAcquired();
+  dest->reserve(items_.size());
+  for (StringToNodeHash::const_iterator i = items_.begin();
+       i != items_.end(); ++i) {
+    dest->push_back(i->second->item());
+  }
+}
+
+Err ItemTree::CheckForBadItems() const {
+  base::AutoLock lock(lock_);
+
+  // Look for errors where we find a GENERATED node that refers to a REFERENCED
+  // one. There may be other nodes depending on the GENERATED one, but listing
+  // all of those isn't helpful, we want to find the broken link.
+  //
+  // This finds normal "missing dependency" errors but does not find circular
+  // dependencies because in this case all items in the cycle will be GENERATED
+  // but none will be resolved. If this happens, we'll check explicitly for
+  // that below.
+  std::vector<const ItemNode*> bad_nodes;
+  std::string depstring;
+  for (StringToNodeHash::const_iterator i = items_.begin();
+       i != items_.end(); ++i) {
+    const ItemNode* src = i->second;
+
+    if (src->state() == ItemNode::GENERATED) {
+      bad_nodes.push_back(src);
+
+      // Check dependencies.
+      for (ItemNode::ItemNodeSet::const_iterator dest =
+               src->unresolved_dependencies().begin();
+          dest != src->unresolved_dependencies().end();
+          ++dest) {
+        if ((*dest)->state() == ItemNode::REFERENCED) {
+          depstring += "\"" + src->item()->label().GetUserVisibleName(false) +
+              "\" needs " + (*dest)->item()->GetItemTypeName() +
+              " \"" + (*dest)->item()->label().GetUserVisibleName(false) +
+              "\"\n";
+        }
+      }
+    }
+  }
+
+  if (!bad_nodes.empty() && depstring.empty()) {
+    // Our logic above found a bad node but didn't identify the problem. This
+    // normally means a circular dependency.
+    depstring = CheckForCircularDependenciesLocked(bad_nodes);
+    if (depstring.empty()) {
+      // Something's very wrong, just dump out the bad nodes.
+      depstring = "I have no idea what went wrong, but these are unresolved, "
+          "possible due to an\ninternal error:";
+      for (size_t i = 0; i < bad_nodes.size(); i++) {
+        depstring += "\n\"" +
+            bad_nodes[i]->item()->label().GetUserVisibleName(false) + "\"";
+      }
+    }
+  }
+
+  if (depstring.empty())
+    return Err();
+  return Err(Location(), "Unresolved dependencies.", depstring);
+}
+
+Err ItemTree::MarkItemResolvedLocked(ItemNode* node) {
+  node->SetResolved();
+  node->item()->OnResolved();
+
+  // Now update our waiters, pushing the "resolved" bit.
+  ItemNode::ItemNodeSet waiting;
+  node->SwapOutWaitingDependencySet(&waiting);
+  for (ItemNode::ItemNodeSet::iterator i = waiting.begin();
+       i != waiting.end(); ++i) {
+    ItemNode* waiter = *i;
+
+    // Our node should be unresolved in the waiter.
+    DCHECK(waiter->unresolved_dependencies().find(node) !=
+           waiter->unresolved_dependencies().end());
+    waiter->MarkDirectDependencyResolved(node);
+
+    // Recursively mark nodes as resolved.
+    if (waiter->state() == ItemNode::GENERATED &&
+        waiter->unresolved_dependencies().empty()) {
+      Err err = MarkItemResolvedLocked(waiter);
+      if (err.has_error())
+        return err;
+    }
+  }
+
+  return Err();
+}
+
+std::string ItemTree::CheckForCircularDependenciesLocked(
+    const std::vector<const ItemNode*>& bad_nodes) const {
+  std::vector<const ItemNode*> cycle;
+  if (!RecursiveFindCycle(bad_nodes[0], bad_nodes[0], &cycle))
+    return std::string();  // Didn't find a cycle, something else is wrong.
+
+  cycle.push_back(bad_nodes[0]);
+  std::string ret = "There is a dependency cycle:";
+
+  // Walk backwards since the dependency arrows point in the reverse direction.
+  for (int i = static_cast<int>(cycle.size()) - 1; i >= 0; i--) {
+    ret += "\n  \"" + cycle[i]->item()->label().GetUserVisibleName(false) +
+        "\"";
+    if (i != 0)
+      ret += " ->";
+  }
+
+  return ret;
+}
diff --git a/tools/gn/item_tree.h b/tools/gn/item_tree.h
new file mode 100644
index 0000000..f2ac4ae
--- /dev/null
+++ b/tools/gn/item_tree.h
@@ -0,0 +1,69 @@
+// Copyright (c) 2013 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.
+
+#ifndef TOOLS_GN_ITEM_TREE_H_
+#define TOOLS_GN_ITEM_TREE_H_
+
+#include "base/containers/hash_tables.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/synchronization/lock.h"
+#include "tools/gn/label.h"
+
+class Err;
+class Item;
+class ItemNode;
+
+// Represents the full dependency tree if labeled items in the system.
+// Generally you will interact with this through the target manager, etc.
+class ItemTree {
+ public:
+  ItemTree();
+  ~ItemTree();
+
+  // This lock must be held when calling the "Locked" functions below.
+  base::Lock& lock() { return lock_; }
+
+  // Returns NULL if the item is not found.
+  //
+  // The lock must be held.
+  ItemNode* GetExistingNodeLocked(const Label& label);
+
+  // There must not be an item with this label in the tree already. Takes
+  // ownership of the pointer.
+  //
+  // The lock must be held.
+  void AddNodeLocked(ItemNode* node);
+
+  // Mark the given item as being generated. If it has no unresolved
+  // dependencies, it will be marked resolved, and the resolved state will be
+  // recursively pushed into the dependency tree. Returns an error if there was
+  // an error.
+  Err MarkItemGeneratedLocked(const Label& label);
+
+  // Fills the given vector with all known items.
+  void GetAllItemsLocked(std::vector<const Item*>* dest) const;
+
+  // Returns an error if there are unresolved dependencies, or no error if
+  // there aren't.
+  //
+  // The lock should not be held.
+  Err CheckForBadItems() const;
+
+ private:
+  Err MarkItemResolvedLocked(ItemNode* node);
+
+  // Given a set of unresolved nodes, looks for cycles and returns the error
+  // message describing any cycles it found.
+  std::string CheckForCircularDependenciesLocked(
+      const std::vector<const ItemNode*>& bad_nodes) const;
+
+  mutable base::Lock lock_;
+
+  typedef base::hash_map<Label, ItemNode*> StringToNodeHash;
+  StringToNodeHash items_;  // Owning pointer.
+
+  DISALLOW_COPY_AND_ASSIGN(ItemTree);
+};
+
+#endif  // TOOLS_GN_ITEM_TREE_H_
diff --git a/tools/gn/label.cc b/tools/gn/label.cc
new file mode 100644
index 0000000..f9b48da
--- /dev/null
+++ b/tools/gn/label.cc
@@ -0,0 +1,263 @@
+// Copyright (c) 2013 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/label.h"
+
+#include "base/logging.h"
+#include "tools/gn/err.h"
+#include "tools/gn/parse_tree.h"
+#include "tools/gn/value.h"
+
+namespace {
+
+// We print user visible label names with no trailing slash after the
+// directory name.
+std::string DirWithNoTrailingSlash(const SourceDir& dir) {
+  // Be careful not to trim if the input is just "/" or "//".
+  if (dir.value().size() > 2)
+    return dir.value().substr(0, dir.value().size() - 1);
+  return dir.value();
+}
+
+// Given the separate-out input (everything before the colon) in the dep rule,
+// computes the final build rule. Sets err on failure. On success,
+// |*used_implicit| will be set to whether the implicit current directory was
+// used. The value is used only for generating error messages.
+bool ComputeBuildLocationFromDep(const Value& input_value,
+                                 const SourceDir& current_dir,
+                                 const base::StringPiece& input,
+                                 SourceDir* result,
+                                 Err* err) {
+  // No rule, use the current locaton.
+  if (input.empty()) {
+    *result = current_dir;
+    return true;
+  }
+
+  // Don't allow directories to start with a single slash. All labels must be
+  // in the source root.
+  if (input[0] == '/' && (input.size() == 1 || input[1] != '/')) {
+    *err = Err(input_value, "Label can't start with a single slash",
+        "Labels must be either relative (no slash at the beginning) or be "
+        "absolute\ninside the source root (two slashes at the beginning).");
+    return false;
+  }
+
+  *result = current_dir.ResolveRelativeDir(input);
+  return true;
+}
+
+// Given the separated-out target name (after the colon) computes the final
+// name, using the implicit name from the previously-generated
+// computed_location if necessary. The input_value is used only for generating
+// error messages.
+bool ComputeTargetNameFromDep(const Value& input_value,
+                              const SourceDir& computed_location,
+                              const base::StringPiece& input,
+                              std::string* result,
+                              Err* err) {
+  if (!input.empty()) {
+    // Easy case: input is specified, just use it.
+    result->assign(input.data(), input.size());
+    return true;
+  }
+
+  const std::string& loc = computed_location.value();
+
+  // Use implicit name. The path will be "//", "//base/", "//base/i18n/", etc.
+  if (loc.size() <= 1) {
+    *err = Err(input_value, "This dependency name is empty");
+    return false;
+  }
+
+  size_t next_to_last_slash = loc.rfind('/', loc.size() - 2);
+  DCHECK(next_to_last_slash != std::string::npos);
+  result->assign(&loc[next_to_last_slash + 1],
+                 loc.size() - next_to_last_slash - 2);
+  return true;
+}
+
+// The original value is used only for error reporting, use the |input| as the
+// input to this function (which may be a substring of the original value when
+// we're parsing toolchains.
+//
+// If the output toolchain vars are NULL, then we'll report an error if we
+// find a toolchain specified (this is used when recursively parsing toolchain
+// labels which themselves can't have toolchain specs).
+//
+// We assume that the output variables are initialized to empty so we don't
+// write them unless we need them to contain something.
+//
+// Returns true on success. On failure, the out* variables might be written to
+// but shouldn't be used.
+bool Resolve(const SourceDir& current_dir,
+             const Label& current_toolchain,
+             const Value& original_value,
+             const base::StringPiece& input,
+             SourceDir* out_dir,
+             std::string* out_name,
+             SourceDir* out_toolchain_dir,
+             std::string* out_toolchain_name,
+             Err* err) {
+  // To workaround the problem that StringPiece operator[] doesn't return a ref.
+  const char* input_str = input.data();
+
+  size_t path_separator = input.find_first_of(":(");
+  base::StringPiece location_piece;
+  base::StringPiece name_piece;
+  base::StringPiece toolchain_piece;
+  if (path_separator == std::string::npos) {
+    location_piece = input;
+    // Leave name & toolchain piece null.
+  } else {
+    location_piece = base::StringPiece(&input_str[0], path_separator);
+
+    size_t toolchain_separator = input.find('(', path_separator);
+    if (toolchain_separator == std::string::npos) {
+      name_piece = base::StringPiece(&input_str[path_separator + 1],
+                                     input.size() - path_separator - 1);
+      // Leave location piece null.
+    } else if (!out_toolchain_dir) {
+      // Toolchain specified but not allows in this context.
+      *err = Err(original_value, "Toolchain has a toolchain.",
+          "Your toolchain definition (inside the parens) seems to itself "
+          "have a\ntoolchain. Don't do this.");
+      return false;
+    } else {
+      // Name piece is everything between the two separators. Note that the
+      // separators may be the same (e.g. "//foo(bar)" which means empty name.
+      if (toolchain_separator > path_separator) {
+        name_piece = base::StringPiece(
+            &input_str[path_separator + 1],
+            toolchain_separator - path_separator - 1);
+      }
+
+      // Toolchain name should end in a ) and this should be the end of the
+      // string.
+      if (input[input.size() - 1] != ')') {
+        *err = Err(original_value, "Bad toolchain name.",
+            "Toolchain name must end in a \")\" at the end of the label.");
+        return false;
+      }
+
+      // Subtract off the two parens to just get the toolchain name.
+      toolchain_piece = base::StringPiece(
+          &input_str[toolchain_separator + 1],
+          input.size() - toolchain_separator - 2);
+    }
+  }
+
+  // Everything before the separator is the filename.
+  // We allow three cases:
+  //   Absolute:                "//foo:bar" -> /foo:bar
+  //   Target in current file:  ":foo"     -> <currentdir>:foo
+  //   Path with implicit name: "/foo"     -> /foo:foo
+  if (location_piece.empty() && name_piece.empty()) {
+    // Can't use both implicit filename and name (":").
+    *err = Err(original_value, "This doesn't specify a dependency.");
+    return false;
+  }
+
+  if (!ComputeBuildLocationFromDep(original_value, current_dir, location_piece,
+                                   out_dir, err))
+    return false;
+
+  if (!ComputeTargetNameFromDep(original_value, *out_dir, name_piece,
+                                out_name, err))
+    return false;
+
+  // Last, do the toolchains.
+  if (out_toolchain_dir) {
+    // Handle empty toolchain strings. We don't allow normal labels to be
+    // empty so we can't allow the recursive call of this function to do this
+    // check.
+    if (toolchain_piece.empty()) {
+      *out_toolchain_dir = current_toolchain.dir();
+      *out_toolchain_name = current_toolchain.name();
+      return true;
+    } else {
+      return Resolve(current_dir, current_toolchain,
+                     original_value, toolchain_piece,
+                     out_toolchain_dir, out_toolchain_name, NULL, NULL, err);
+    }
+  }
+  return true;
+}
+
+}  // namespace
+
+Label::Label() {
+}
+
+Label::Label(const SourceDir& dir,
+             const base::StringPiece& name,
+             const SourceDir& toolchain_dir,
+             const base::StringPiece& toolchain_name)
+    : dir_(dir),
+      toolchain_dir_(toolchain_dir) {
+  name_.assign(name.data(), name.size());
+  toolchain_name_.assign(toolchain_name.data(), toolchain_name.size());
+}
+
+Label::~Label() {
+}
+
+// static
+Label Label::Resolve(const SourceDir& current_dir,
+                     const Label& current_toolchain,
+                     const Value& input,
+                     Err* err) {
+  Label ret;
+  if (input.type() != Value::STRING) {
+    *err = Err(input, "Dependency is not a string.");
+    return ret;
+  }
+  const std::string& input_string = input.string_value();
+  if (input_string.empty()) {
+    *err = Err(input, "Dependency string is empty.");
+    return ret;
+  }
+
+  if (!::Resolve(current_dir, current_toolchain, input, input_string,
+                 &ret.dir_, &ret.name_,
+                 &ret.toolchain_dir_, &ret.toolchain_name_,
+                 err))
+    return Label();
+  return ret;
+}
+
+Label Label::GetToolchainLabel() const {
+  return Label(toolchain_dir_, toolchain_name_,
+               SourceDir(), base::StringPiece());
+}
+
+std::string Label::GetUserVisibleName(bool include_toolchain) const {
+  std::string ret;
+  ret.reserve(dir_.value().size() + name_.size() + 1);
+
+  if (dir_.is_null())
+    return ret;
+
+  ret = DirWithNoTrailingSlash(dir_);
+  ret.push_back(':');
+  ret.append(name_);
+
+  if (include_toolchain) {
+    ret.push_back('(');
+    if (!toolchain_dir_.is_null() && !toolchain_name_.empty()) {
+      ret.append(DirWithNoTrailingSlash(toolchain_dir_));
+      ret.push_back(':');
+      ret.append(toolchain_name_);
+    }
+    ret.push_back(')');
+  }
+  return ret;
+}
+
+std::string Label::GetUserVisibleName(const Label& default_toolchain) const {
+  bool include_toolchain =
+      default_toolchain.dir() != toolchain_dir_ ||
+      default_toolchain.name() != toolchain_name_;
+  return GetUserVisibleName(include_toolchain);
+}
diff --git a/tools/gn/label.h b/tools/gn/label.h
new file mode 100644
index 0000000..b31117a
--- /dev/null
+++ b/tools/gn/label.h
@@ -0,0 +1,116 @@
+// Copyright (c) 2013 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.
+
+#ifndef TOOLS_GN_LABEL_H_
+#define TOOLS_GN_LABEL_H_
+
+#include "base/containers/hash_tables.h"
+#include "build/build_config.h"
+#include "tools/gn/source_dir.h"
+
+class Err;
+class Value;
+
+// A label represents the name of a target or some other named thing in
+// the source path. The label is always absolute and always includes a name
+// part, so it starts with a slash, and has one colon.
+class Label {
+ public:
+  Label();
+
+  // Makes a label given an already-separate out path and name.
+  // See also Resolve().
+  Label(const SourceDir& dir,
+        const base::StringPiece& name,
+        const SourceDir& toolchain_dir,
+        const base::StringPiece& toolchain_name);
+  ~Label();
+
+  // Resolives a string from a build file that may be relative to the
+  // current directory into a fully qualified label. On failure returns an
+  // is_null() label and sets the error.
+  static Label Resolve(const SourceDir& current_dir,
+                       const Label& current_toolchain,
+                       const Value& input,
+                       Err* err);
+
+  bool is_null() const { return dir_.is_null(); }
+
+  const SourceDir& dir() const { return dir_; }
+  const std::string& name() const { return name_; }
+
+  const SourceDir& toolchain_dir() const { return toolchain_dir_; }
+  const std::string& toolchain_name() const { return toolchain_name_; }
+  Label GetToolchainLabel() const;
+
+  // Formats this label in a way that we can present to the user or expose to
+  // other parts of the system. SourceDirs end in slashes, but the user
+  // expects names like "//chrome/renderer:renderer_config" when printed. The
+  // toolchain is optionally included.
+  std::string GetUserVisibleName(bool include_toolchain) const;
+
+  // Like the above version, but automatically includes the toolchain if it's
+  // not the default one. Normally the user only cares about the toolchain for
+  // non-default ones, so this can make certain output more clear.
+  std::string GetUserVisibleName(const Label& default_toolchain) const;
+
+  bool operator==(const Label& other) const {
+    return name_ == other.name_ && dir_ == other.dir_ &&
+           toolchain_dir_ == other.toolchain_dir_ &&
+           toolchain_name_ == other.toolchain_name_;
+  }
+  bool operator!=(const Label& other) const {
+    return !operator==(other);
+  }
+  bool operator<(const Label& other) const {
+    // TODO(brettw) could be optimized to avoid an extra full string check
+    // (one for operator==, one for <).
+    if (dir_ != other.dir_)
+      return dir_ < other.dir_;
+    if (name_ != other.name_)
+      return name_ < other.name_;
+    if (toolchain_dir_ != other.toolchain_dir_)
+      return toolchain_dir_ < other.toolchain_dir_;
+    return toolchain_name_ < other.toolchain_name_;
+  }
+
+  // Returns true if the toolchain dir/name of this object matches some
+  // other object.
+  bool ToolchainsEqual(const Label& other) const {
+    return toolchain_dir_ == other.toolchain_dir_ &&
+           toolchain_name_ == other.toolchain_name_;
+  }
+
+ private:
+  SourceDir dir_;
+  std::string name_;
+
+  SourceDir toolchain_dir_;
+  std::string toolchain_name_;
+};
+
+namespace BASE_HASH_NAMESPACE {
+
+#if defined(COMPILER_GCC)
+template<> struct hash<Label> {
+  std::size_t operator()(const Label& v) const {
+    hash<std::string> stringhash;
+    return ((stringhash(v.dir().value()) * 131 +
+             stringhash(v.name())) * 131 +
+            stringhash(v.toolchain_dir().value())) * 131 +
+           stringhash(v.toolchain_name());
+  }
+};
+#elif defined(COMPILER_MSVC)
+inline size_t hash_value(const Label& v) {
+  return ((hash_value(v.dir().value()) * 131 +
+           hash_value(v.name())) * 131 +
+          hash_value(v.toolchain_dir().value())) * 131 +
+         hash_value(v.toolchain_name());
+}
+#endif  // COMPILER...
+
+}  // namespace BASE_HASH_NAMESPACE
+
+#endif  // TOOLS_GN_LABEL_H_
diff --git a/tools/gn/label_unittest.cc b/tools/gn/label_unittest.cc
new file mode 100644
index 0000000..74eb3da
--- /dev/null
+++ b/tools/gn/label_unittest.cc
@@ -0,0 +1,88 @@
+// Copyright (c) 2013 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 "testing/gtest/include/gtest/gtest.h"
+#include "tools/gn/err.h"
+#include "tools/gn/label.h"
+#include "tools/gn/value.h"
+
+namespace {
+
+struct ParseDepStringCase {
+  const char* cur_dir;
+  const char* str;
+  bool success;
+  const char* expected_dir;
+  const char* expected_name;
+  const char* expected_toolchain_dir;
+  const char* expected_toolchain_name;
+};
+
+}  // namespace
+
+TEST(Label, Resolve) {
+  ParseDepStringCase cases[] = {
+      // cur         input                        succ   expected dir          name    tc dir    tc name
+      { "//chrome/", "",                          false, "",                   "",     "",       "" },
+      { "//chrome/", "/",                         false, "",                   "",     "",       "" },
+      { "//chrome/", ":",                         false, "",                   "",     "",       "" },
+      { "//chrome/", "/:",                        false, "",                   "",     "",       "" },
+      { "//chrome/", "blah",                      true,  "//chrome/blah/",     "blah", "//t/",   "d" },
+      { "//chrome/", "blah:bar",                  true,  "//chrome/blah/",     "bar",  "//t/",   "d" },
+      // No single-leading slash.
+      { "//chrome/", "/chrome:bar",               false, "",                   "",     "",       "" },
+      // No trailing slash.
+      { "//chrome/", "/chrome/:bar",              false, "",                   "",     "",       "" },
+      // Refers to root dir.
+      { "//chrome/", "//:bar",                    true,  "//",                 "bar",  "//t/",   "d" },
+      // Implicit directory
+      { "//chrome/", ":bar",                      true,  "//chrome/",          "bar",  "//t/",   "d" },
+      { "//chrome/renderer/", ":bar",             true,  "//chrome/renderer/", "bar",  "//t/",   "d" },
+      // Implicit names.
+      { "//chrome/", "//base",                    true,  "//base/",            "base", "//t/",   "d" },
+      { "//chrome/", "//base/i18n",               true,  "//base/i18n/",       "i18n", "//t/",   "d" },
+      { "//chrome/", "//base/i18n:foo",           true,  "//base/i18n/",       "foo",  "//t/",   "d" },
+      // Toolchain parsing.
+      { "//chrome/", "//chrome:bar(//t:n)",       true,  "//chrome/",          "bar",  "//t/",   "n" },
+      { "//chrome/", "//chrome:bar(//t)",         true,  "//chrome/",          "bar",  "//t/",   "t" },
+      { "//chrome/", "//chrome:bar(//t:)",        true,  "//chrome/",          "bar",  "//t/",   "t" },
+      { "//chrome/", "//chrome:bar()",            true,  "//chrome/",          "bar",  "//t/",   "d" },
+      { "//chrome/", "//chrome:bar(foo)",         true,  "//chrome/",          "bar",  "//chrome/foo/", "foo" },
+      { "//chrome/", "//chrome:bar(:foo)",        true,  "//chrome/",          "bar",  "//chrome/",     "foo" },
+      // TODO(brettw) it might be nice to make this an error:
+      //{ "//chrome/", "//chrome:bar())",           false, "",                   "",     "",       "" },
+      { "//chrome/", "//chrome:bar(//t:bar(tc))", false, "",                   "",     "",       "" },
+      { "//chrome/", "//chrome:bar(()",           false, "",                   "",     "",       "" },
+      { "//chrome/", "(t:b)",                     false, "",                   "",     "",       "" },
+      { "//chrome/", ":bar(//t/b)",               true,  "//chrome/",          "bar",  "//t/b/", "b" },
+      { "//chrome/", ":bar(/t/b)",                false, "",                   "",     "",       "" },
+      { "//chrome/", ":bar(t/b)",                 true,  "//chrome/",          "bar",  "//chrome/t/b/", "b" },
+  };
+
+  Label default_toolchain(SourceDir("//t/"), "d",
+                          SourceDir(), std::string());
+
+  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
+    const ParseDepStringCase& cur = cases[i];
+
+    std::string location, name;
+    Err err;
+    Value v(NULL, Value::STRING);
+    v.string_value() = cur.str;
+    Label result =
+        Label::Resolve(SourceDir(cur.cur_dir), default_toolchain, v, &err);
+    EXPECT_EQ(cur.success, !err.has_error()) << i << " " << cur.str;
+    if (!err.has_error() && cur.success) {
+      EXPECT_EQ(cur.expected_dir, result.dir().value())
+          << i << " " << cur.str;
+      EXPECT_EQ(cur.expected_name, result.name())
+          << i << " " << cur.str;
+      EXPECT_EQ(cur.expected_toolchain_dir,
+                result.toolchain_dir().value())
+          << i << " " << cur.str;
+      EXPECT_EQ(cur.expected_toolchain_name, result.toolchain_name())
+          << i << " " << cur.str;
+    }
+  }
+}
diff --git a/tools/gn/location.h b/tools/gn/location.h
new file mode 100644
index 0000000..2055125
--- /dev/null
+++ b/tools/gn/location.h
@@ -0,0 +1,77 @@
+// Copyright (c) 2013 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.
+
+#ifndef TOOLS_GN_LOCATION_H_
+#define TOOLS_GN_LOCATION_H_
+
+#include <algorithm>
+
+#include "base/logging.h"
+
+class InputFile;
+
+// Represents a place in a source file. Used for error reporting.
+class Location {
+ public:
+  Location()
+      : file_(NULL),
+        line_number_(-1),
+        char_offset_(-1) {
+  }
+  Location(const InputFile* file, int line_number, int char_offset)
+      : file_(file),
+        line_number_(line_number),
+        char_offset_(char_offset) {
+  }
+
+  const InputFile* file() const { return file_; }
+  int line_number() const { return line_number_; }
+  int char_offset() const { return char_offset_; }
+
+  bool operator==(const Location& other) const {
+    return other.file_ == file_ &&
+           other.line_number_ == line_number_ &&
+           other.char_offset_ == char_offset_;
+  }
+
+  bool operator<(const Location& other) const {
+    DCHECK(file_ == other.file_);
+    if (line_number_ != other.line_number_)
+      return line_number_ < other.line_number_;
+    return char_offset_ < other.char_offset_;
+  }
+
+ private:
+  const InputFile* file_;  // Null when unset.
+  int line_number_;  // -1 when unset.
+  int char_offset_;  // -1 when unset.
+};
+
+// Represents a range in a source file. Used for error reporting.
+// The end is exclusive i.e. [begin, end)
+class LocationRange {
+ public:
+  LocationRange() {}
+  LocationRange(const Location& begin, const Location& end)
+      : begin_(begin),
+        end_(end) {
+    DCHECK(begin_.file() == end_.file());
+  }
+
+  const Location& begin() const { return begin_; }
+  const Location& end() const { return end_; }
+
+  LocationRange Union(const LocationRange& other) const {
+    DCHECK(begin_.file() == other.begin_.file());
+    return LocationRange(
+        begin_ < other.begin_ ? begin_ : other.begin_,
+        end_ < other.end_ ? other.end_ : end_);
+  }
+
+ private:
+  Location begin_;
+  Location end_;
+};
+
+#endif  // TOOLS_GN_LOCATION_H_
diff --git a/tools/gn/ninja_build_writer.cc b/tools/gn/ninja_build_writer.cc
new file mode 100644
index 0000000..a63765c
--- /dev/null
+++ b/tools/gn/ninja_build_writer.cc
@@ -0,0 +1,165 @@
+// Copyright (c) 2013 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/ninja_build_writer.h"
+
+#include <fstream>
+
+#include "base/command_line.h"
+#include "base/file_util.h"
+#include "base/process/process_handle.h"
+#include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
+#include "tools/gn/build_settings.h"
+#include "tools/gn/filesystem_utils.h"
+#include "tools/gn/input_file_manager.h"
+#include "tools/gn/scheduler.h"
+#include "tools/gn/target.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
+namespace {
+
+std::string GetSelfInvocationCommand(const BuildSettings* build_settings) {
+#if defined(OS_WIN)
+  wchar_t module[MAX_PATH];
+  GetModuleFileName(NULL, module, MAX_PATH);
+  //result = "\"" + WideToUTF8(module) + "\"";
+  base::FilePath executable(module);
+#elif defined(OS_MACOSX)
+  // FIXME(brettw) write this on Mac!
+  base::FilePath executable("gn");
+#else
+  base::FilePath executable =
+      base::GetProcessExecutablePath(base::GetCurrentProcessHandle());
+#endif
+
+/*
+  // Append the root path.
+  CommandLine* cmdline = CommandLine::ForCurrentProcess();
+  result += " --root=\"" + FilePathToUTF8(settings->root_path()) + "\"";
+*/
+
+  CommandLine cmdline(executable);
+  cmdline.AppendSwitchPath("--root", build_settings->root_path());
+
+  // TODO(brettw) append other parameters.
+
+#if defined(OS_WIN)
+  return WideToUTF8(cmdline.GetCommandLineString());
+#else
+  return cmdline.GetCommandLineString();
+#endif
+}
+
+}  // namespace
+
+NinjaBuildWriter::NinjaBuildWriter(
+    const BuildSettings* build_settings,
+    const std::vector<const Settings*>& all_settings,
+    const std::vector<const Target*>& default_toolchain_targets,
+    std::ostream& out)
+    : build_settings_(build_settings),
+      all_settings_(all_settings),
+      default_toolchain_targets_(default_toolchain_targets),
+      out_(out),
+      path_output_(build_settings->build_dir(), ESCAPE_NINJA, true),
+      helper_(build_settings) {
+}
+
+NinjaBuildWriter::~NinjaBuildWriter() {
+}
+
+void NinjaBuildWriter::Run() {
+  WriteNinjaRules();
+  WriteSubninjas();
+  WritePhonyAndAllRules();
+}
+
+// static
+bool NinjaBuildWriter::RunAndWriteFile(
+    const BuildSettings* build_settings,
+    const std::vector<const Settings*>& all_settings,
+    const std::vector<const Target*>& default_toolchain_targets) {
+  base::FilePath ninja_file(build_settings->GetFullPath(
+      SourceFile(build_settings->build_dir().value() + "build.ninja")));
+  file_util::CreateDirectory(ninja_file.DirName());
+
+  std::ofstream file;
+  file.open(FilePathToUTF8(ninja_file).c_str(),
+            std::ios_base::out | std::ios_base::binary);
+  if (file.fail())
+    return false;
+
+  NinjaBuildWriter gen(build_settings, all_settings,
+                       default_toolchain_targets, file);
+  gen.Run();
+  return true;
+}
+
+void NinjaBuildWriter::WriteNinjaRules() {
+  out_ << "rule gn\n";
+  out_ << "  command = " << GetSelfInvocationCommand(build_settings_) << "\n";
+  out_ << "  description = GN the world\n\n";
+
+  out_ << "build build.ninja: gn";
+
+  // Input build files.
+  std::vector<SourceFile> input_files;
+  g_scheduler->input_file_manager()->GetAllInputFileNames(&input_files);
+  for (size_t i = 0; i < input_files.size(); i++) {
+    out_ << " ";
+    path_output_.WriteFile(out_, input_files[i]);
+  }
+
+  // Other files read by the build.
+  std::vector<SourceFile> other_files = g_scheduler->GetGenDependencies();
+  for (size_t i = 0; i < other_files.size(); i++) {
+    out_ << " ";
+    path_output_.WriteFile(out_, other_files[i]);
+  }
+
+  out_ << std::endl << std::endl;
+}
+
+void NinjaBuildWriter::WriteSubninjas() {
+  for (size_t i = 0; i < all_settings_.size(); i++) {
+    out_ << "subninja ";
+    path_output_.WriteFile(out_,
+                           helper_.GetNinjaFileForToolchain(all_settings_[i]));
+    out_ << std::endl;
+  }
+  out_ << std::endl;
+}
+
+void NinjaBuildWriter::WritePhonyAndAllRules() {
+  std::string all_rules;
+
+  // Write phony rules for the default toolchain (don't do other toolchains or
+  // we'll get naming conflicts).
+  for (size_t i = 0; i < default_toolchain_targets_.size(); i++) {
+    const Target* target = default_toolchain_targets_[i];
+    if (target->output_type() == Target::NONE)
+      continue;  // Nothing to generate.
+
+    OutputFile target_file = helper_.GetTargetOutputFile(target);
+    if (target_file.value() != target->label().name()) {
+      out_ << "build " << target->label().name() << ": phony ";
+      path_output_.WriteFile(out_, target_file);
+      out_ << std::endl;
+    }
+
+    if (!all_rules.empty())
+      all_rules.append(" $\n    ");
+    all_rules.append(target_file.value());
+  }
+
+  if (!all_rules.empty()) {
+    out_ << "\nbuild all: phony " << all_rules << std::endl;
+    out_ << "default all" << std::endl;
+  }
+}
+
diff --git a/tools/gn/ninja_build_writer.h b/tools/gn/ninja_build_writer.h
new file mode 100644
index 0000000..85548df
--- /dev/null
+++ b/tools/gn/ninja_build_writer.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2013 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.
+
+#ifndef TOOLS_GN_NINJA_BUILD_WRITER_H_
+#define TOOLS_GN_NINJA_BUILD_WRITER_H_
+
+#include <iosfwd>
+#include <vector>
+
+#include "tools/gn/ninja_helper.h"
+#include "tools/gn/path_output.h"
+
+class BuildSettings;
+class Settings;
+class Target;
+
+// Generates the toplevel "build.ninja" file. This references the individual
+// toolchain files and lists all input .gn files as dependencies of the
+// build itself.
+class NinjaBuildWriter {
+ public:
+  static bool RunAndWriteFile(
+      const BuildSettings* settings,
+      const std::vector<const Settings*>& all_settings,
+      const std::vector<const Target*>& default_toolchain_targets);
+
+ private:
+  NinjaBuildWriter(const BuildSettings* settings,
+                   const std::vector<const Settings*>& all_settings,
+                   const std::vector<const Target*>& default_toolchain_targets,
+                   std::ostream& out);
+  ~NinjaBuildWriter();
+
+  void Run();
+
+  void WriteNinjaRules();
+  void WriteSubninjas();
+  void WritePhonyAndAllRules();
+
+  const BuildSettings* build_settings_;
+  std::vector<const Settings*> all_settings_;
+  std::vector<const Target*> default_toolchain_targets_;
+  std::ostream& out_;
+  PathOutput path_output_;
+
+  NinjaHelper helper_;
+
+  DISALLOW_COPY_AND_ASSIGN(NinjaBuildWriter);
+};
+
+#endif  // TOOLS_GN_NINJA_BUILD_GENERATOR_H_
+
diff --git a/tools/gn/ninja_helper.cc b/tools/gn/ninja_helper.cc
new file mode 100644
index 0000000..c27c0e4
--- /dev/null
+++ b/tools/gn/ninja_helper.cc
@@ -0,0 +1,165 @@
+// Copyright (c) 2013 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/ninja_helper.h"
+
+#include "base/logging.h"
+#include "tools/gn/filesystem_utils.h"
+#include "tools/gn/string_utils.h"
+#include "tools/gn/target.h"
+
+namespace {
+
+const char kLibDirWithSlash[] = "lib";
+const char kObjectDirNoSlash[] = "obj";
+
+}  // namespace
+
+NinjaHelper::NinjaHelper(const BuildSettings* build_settings)
+    : build_settings_(build_settings) {
+  build_to_src_no_last_slash_ = build_settings->build_to_source_dir_string();
+  if (!build_to_src_no_last_slash_.empty() &&
+      build_to_src_no_last_slash_[build_to_src_no_last_slash_.size() - 1] ==
+          '/')
+    build_to_src_no_last_slash_.resize(build_to_src_no_last_slash_.size() - 1);
+
+  build_to_src_system_no_last_slash_ = build_to_src_no_last_slash_;
+  ConvertPathToSystem(&build_to_src_system_no_last_slash_);
+}
+
+NinjaHelper::~NinjaHelper() {
+}
+
+std::string NinjaHelper::GetTopleveOutputDir() const {
+  return kObjectDirNoSlash;
+}
+
+std::string NinjaHelper::GetTargetOutputDir(const Target* target) const {
+  return kObjectDirNoSlash + target->label().dir().SourceAbsoluteWithOneSlash();
+}
+
+OutputFile NinjaHelper::GetNinjaFileForTarget(const Target* target) const {
+  OutputFile ret(target->settings()->toolchain_output_subdir());
+  ret.value().append(kObjectDirNoSlash);
+  AppendStringPiece(&ret.value(),
+                    target->label().dir().SourceAbsoluteWithOneSlash());
+  ret.value().append(target->label().name());
+  ret.value().append(".ninja");
+  return ret;
+}
+
+OutputFile NinjaHelper::GetNinjaFileForToolchain(
+    const Settings* settings) const {
+  OutputFile ret;
+  ret.value().append(settings->toolchain_output_subdir().value());
+  ret.value().append("toolchain.ninja");
+  return ret;
+}
+
+// In Python, GypPathToUniqueOutput does the qualification. The only case where
+// the Python version doesn't qualify the name is for target outputs, which we
+// handle in a separate function.
+OutputFile NinjaHelper::GetOutputFileForSource(
+    const Target* target,
+    const SourceFile& source,
+    SourceFileType type) const {
+  // Extract the filename and remove the extension (keep the dot).
+  base::StringPiece filename = FindFilename(&source.value());
+  std::string name(filename.data(), filename.size());
+  size_t extension_offset = FindExtensionOffset(name);
+  CHECK(extension_offset != std::string::npos);
+  name.resize(extension_offset);
+
+  // Append the new extension.
+  switch (type) {
+    case SOURCE_ASM:
+    case SOURCE_C:
+    case SOURCE_CC:
+    case SOURCE_M:
+    case SOURCE_MM:
+      name.append(target->settings()->IsWin() ? "obj" : "o");
+      break;
+
+    case SOURCE_RC:
+      name.append("res");
+      break;
+
+    case SOURCE_H:
+    case SOURCE_UNKNOWN:
+      NOTREACHED();
+      return OutputFile();
+  }
+
+  // Use the scheme <path>/<target>.<name>.<extension> so that all output
+  // names are unique to different targets.
+  OutputFile ret(kObjectDirNoSlash);
+
+  // Find the directory, assume it starts with two slashes, and trim to one.
+  base::StringPiece dir = FindDir(&source.value());
+  CHECK(dir.size() >= 2 && dir[0] == '/' && dir[1] == '/')
+      << "Source file isn't in the source repo: " << dir;
+  AppendStringPiece(&ret.value(), dir.substr(1));
+
+  ret.value().append(target->label().name());
+  ret.value().append(".");
+  ret.value().append(name);
+  return ret;
+}
+
+OutputFile NinjaHelper::GetTargetOutputFile(const Target* target) const {
+  OutputFile ret;
+  if (target->output_type() == Target::NONE) {
+    NOTREACHED();
+    return ret;
+  }
+
+  const char* extension;
+  if (target->output_type() == Target::NONE ||
+      target->output_type() == Target::COPY_FILES ||
+      target->output_type() == Target::CUSTOM) {
+    extension = "stamp";
+  } else {
+    extension = GetExtensionForOutputType(target->output_type(),
+                                          target->settings()->target_os());
+  }
+
+  // Everything goes into the toolchain directory (which will be empty for the
+  // default toolchain, and will end in a slash otherwise).
+  ret.value().append(target->settings()->toolchain_output_subdir().value());
+
+  // Binaries and loadable libraries go into the toolchain root.
+  if (target->output_type() == Target::EXECUTABLE ||
+      target->output_type() == Target::LOADABLE_MODULE ||
+      (target->settings()->IsMac() &&
+          (target->output_type() == Target::SHARED_LIBRARY ||
+           target->output_type() == Target::STATIC_LIBRARY)) ||
+      (target->settings()->IsWin() &&
+       target->output_type() == Target::SHARED_LIBRARY)) {
+    // Generate a name like "<toolchain>/<name>.<extension>".
+    ret.value().append(target->label().name());
+    ret.value().push_back('.');
+    ret.value().append(extension);
+    return ret;
+  }
+
+  // Libraries go into the library subdirectory like
+  // "<toolchain>/lib/<name>.<extension>".
+  if (target->output_type() == Target::SHARED_LIBRARY) {
+    ret.value().append(kLibDirWithSlash);
+    ret.value().append(target->label().name());
+    ret.value().push_back('.');
+    ret.value().append(extension);
+    return ret;
+  }
+
+  // Everything else goes next to the target's .ninja file like
+  // "<toolchain>/obj/<path>/<name>.<extension>".
+  ret.value().append(kObjectDirNoSlash);
+  AppendStringPiece(&ret.value(),
+                    target->label().dir().SourceAbsoluteWithOneSlash());
+  ret.value().append(target->label().name());
+  ret.value().push_back('.');
+  ret.value().append(extension);
+  return ret;
+}
diff --git a/tools/gn/ninja_helper.h b/tools/gn/ninja_helper.h
new file mode 100644
index 0000000..5bea29e
--- /dev/null
+++ b/tools/gn/ninja_helper.h
@@ -0,0 +1,71 @@
+// Copyright (c) 2013 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.
+
+#ifndef TOOLS_GN_NINJA_HELPER_H_
+#define TOOLS_GN_NINJA_HELPER_H_
+
+#include <iosfwd>
+#include <string>
+
+#include "tools/gn/filesystem_utils.h"
+#include "tools/gn/output_file.h"
+
+class BuildSettings;
+class SourceDir;
+class SourceFile;
+class Target;
+
+// NinjaHelper -----------------------------------------------------------------
+
+class NinjaHelper {
+ public:
+  NinjaHelper(const BuildSettings* build_settings);
+  ~NinjaHelper();
+
+  // Ends in a slash.
+  std::string GetTopleveOutputDir() const;
+
+  // Ends in a slash.
+  std::string GetTargetOutputDir(const Target* target) const;
+
+  // Example: "base/base.ninja". The string version will not be escaped, and
+  // will always have slashes for path separators.
+  OutputFile GetNinjaFileForTarget(const Target* target) const;
+
+  // Returns the name of the root .ninja file for the given toolchain.
+  OutputFile GetNinjaFileForToolchain(const Settings* settings) const;
+
+  // Given a source file relative to the source root, returns the output
+  // filename.
+  OutputFile GetOutputFileForSource(const Target* target,
+                                   const SourceFile& source,
+                                   SourceFileType type) const;
+
+  // Returns the filename produced by the given output.
+  //
+  // Some targets make multiple files (like a .dll and an import library). This
+  // function returns the name of the file other targets should depend on and
+  // link to (so in this example, the import library).
+  OutputFile GetTargetOutputFile(const Target* target) const;
+
+  // Returns the relative directory in either slashes or the system separator
+  // from the ninja directory (e.g. "out/Debug") to the source root (e.g.
+  // "../.."). It has no terminating slash.
+  const std::string& build_to_src_no_last_slash() const {
+    return build_to_src_no_last_slash_;
+  }
+  const std::string& build_to_src_system_no_last_slash() const {
+    return build_to_src_system_no_last_slash_;
+  }
+
+ private:
+  const BuildSettings* build_settings_;
+
+  std::string build_to_src_no_last_slash_;
+  std::string build_to_src_system_no_last_slash_;
+
+  DISALLOW_COPY_AND_ASSIGN(NinjaHelper);
+};
+
+#endif  // TOOLS_GN_NINJA_HELPER_H_
diff --git a/tools/gn/ninja_helper_unittest.cc b/tools/gn/ninja_helper_unittest.cc
new file mode 100644
index 0000000..4aec032
--- /dev/null
+++ b/tools/gn/ninja_helper_unittest.cc
@@ -0,0 +1,73 @@
+// Copyright (c) 2013 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 "testing/gtest/include/gtest/gtest.h"
+
+#include "tools/gn/filesystem_utils.h"
+#include "tools/gn/ninja_helper.h"
+#include "tools/gn/settings.h"
+#include "tools/gn/target.h"
+#include "tools/gn/toolchain.h"
+
+namespace {
+
+class HelperSetterUpper {
+ public:
+  HelperSetterUpper()
+      : build_settings(),
+        toolchain(Label(SourceDir("//"), "tc", SourceDir(), std::string())),
+        settings(&build_settings, &toolchain, std::string()),
+        target(&settings,
+               Label(SourceDir("//tools/gn/"), "name",
+                     SourceDir(), std::string())) {
+    settings.set_target_os(Settings::WIN);
+
+    // Output going to "out/Debug".
+    build_settings.SetBuildDir(SourceDir("/out/Debug/"));
+
+    // Our source target is in "tools/gn".
+    target.set_output_type(Target::EXECUTABLE);
+  }
+
+  BuildSettings build_settings;
+  Toolchain toolchain;
+  Settings settings;
+  Target target;
+};
+
+}  // namespace
+
+TEST(NinjaHelper, GetNinjaFileForTarget) {
+  HelperSetterUpper setup;
+  NinjaHelper helper(&setup.build_settings);
+
+  // Default toolchain.
+  EXPECT_EQ(OutputFile("obj/tools/gn/name.ninja").value(),
+            helper.GetNinjaFileForTarget(&setup.target).value());
+}
+
+TEST(NinjaHelper, GetOutputFileForSource) {
+  HelperSetterUpper setup;
+  NinjaHelper helper(&setup.build_settings);
+
+  // On Windows, expect ".obj"
+  EXPECT_EQ(OutputFile("obj/tools/gn/name.foo.obj").value(),
+            helper.GetOutputFileForSource(&setup.target,
+                                          SourceFile("//tools/gn/foo.cc"),
+                                          SOURCE_CC).value());
+}
+
+TEST(NinjaHelper, GetTargetOutputFile) {
+  HelperSetterUpper setup;
+  NinjaHelper helper(&setup.build_settings);
+  EXPECT_EQ(OutputFile("name.exe"),
+            helper.GetTargetOutputFile(&setup.target));
+
+  // Static library on Windows goes alongside the object files.
+  setup.target.set_output_type(Target::STATIC_LIBRARY);
+  EXPECT_EQ(OutputFile("obj/tools/gn/name.lib"),
+            helper.GetTargetOutputFile(&setup.target));
+
+  // TODO(brettw) test output to library and other OS types.
+}
diff --git a/tools/gn/ninja_target_writer.cc b/tools/gn/ninja_target_writer.cc
new file mode 100644
index 0000000..ed2d09d
--- /dev/null
+++ b/tools/gn/ninja_target_writer.cc
@@ -0,0 +1,550 @@
+// Copyright (c) 2013 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/ninja_target_writer.h"
+
+#include <fstream>
+#include <sstream>
+
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/strings/string_util.h"
+#include "tools/gn/config_values_extractors.h"
+#include "tools/gn/err.h"
+#include "tools/gn/escape.h"
+#include "tools/gn/file_template.h"
+#include "tools/gn/location.h"
+#include "tools/gn/path_output.h"
+#include "tools/gn/scheduler.h"
+#include "tools/gn/string_utils.h"
+#include "tools/gn/target.h"
+
+namespace {
+
+static const char kCustomTargetSourceKey[] = "{{source}}";
+static const char kCustomTargetSourceNamePartKey[] = "{{source_name_part}}";
+
+struct DefineWriter {
+  void operator()(const std::string& s, std::ostream& out) const {
+    out << " -D" << s;
+  }
+};
+
+struct IncludeWriter {
+  IncludeWriter(PathOutput& path_output,
+                const NinjaHelper& h)
+      : helper(h),
+        path_output_(path_output),
+        old_inhibit_quoting_(path_output.inhibit_quoting()) {
+    // Inhibit quoting since we'll put quotes around the whole thing ourselves.
+    // Since we're writing in NINJA escaping mode, this won't actually do
+    // anything, but I think we may need to change to shell-and-then-ninja
+    // escaping for this in the future.
+    path_output_.set_inhibit_quoting(true);
+  }
+  ~IncludeWriter() {
+    path_output_.set_inhibit_quoting(old_inhibit_quoting_);
+  }
+
+  void operator()(const SourceDir& d, std::ostream& out) const {
+    out << " \"-I";
+    // It's important not to include the trailing slash on directories or on
+    // Windows it will be a backslash and the compiler might think we're
+    // escaping the quote!
+    path_output_.WriteDir(out, d, PathOutput::DIR_NO_LAST_SLASH);
+    out << "\"";
+  }
+
+  const NinjaHelper& helper;
+  PathOutput& path_output_;
+  bool old_inhibit_quoting_;  // So we can put the PathOutput back.
+};
+
+}  // namespace
+
+NinjaTargetWriter::NinjaTargetWriter(const Target* target, std::ostream& out)
+    : settings_(target->settings()),
+      target_(target),
+      out_(out),
+      path_output_(settings_->build_settings()->build_dir(),
+                   ESCAPE_NINJA, true),
+      helper_(settings_->build_settings()) {
+}
+
+NinjaTargetWriter::~NinjaTargetWriter() {
+}
+
+void NinjaTargetWriter::Run() {
+  out_ << "arch = environment.x86\n";
+
+  if (target_->output_type() == Target::COPY_FILES) {
+    WriteCopyRules();
+  } else if (target_->output_type() == Target::CUSTOM) {
+    WriteCustomRules();
+  } else {
+    WriteCompilerVars();
+
+    std::vector<OutputFile> obj_files;
+    WriteSources(&obj_files);
+
+    WriteLinkerStuff(obj_files);
+  }
+}
+
+// static
+void NinjaTargetWriter::RunAndWriteFile(const Target* target) {
+  if (target->output_type() == Target::NONE)
+    return;
+
+  const Settings* settings = target->settings();
+  NinjaHelper helper(settings->build_settings());
+
+  base::FilePath ninja_file(settings->build_settings()->GetFullPath(
+      helper.GetNinjaFileForTarget(target).GetSourceFile(
+          settings->build_settings())));
+
+  file_util::CreateDirectory(ninja_file.DirName());
+
+  // It's rediculously faster to write to a string and then write that to
+  // disk in one operation than to use an fstream here.
+  std::stringstream file;
+  if (file.fail()) {
+    g_scheduler->FailWithError(
+        Err(Location(), "Error writing ninja file.",
+            "Unable to open \"" + FilePathToUTF8(ninja_file) + "\"\n"
+            "for writing."));
+    return;
+  }
+
+  NinjaTargetWriter gen(target, file);
+  gen.Run();
+
+  std::string contents = file.str();
+  file_util::WriteFile(ninja_file, contents.c_str(), contents.size());
+}
+
+void NinjaTargetWriter::WriteCopyRules() {
+  // The dest dir should be inside the output dir so we can just remove the
+  // prefix and get ninja-relative paths.
+  const std::string& output_dir =
+      settings_->build_settings()->build_dir().value();
+  const std::string& dest_dir = target_->destdir().value();
+  DCHECK(StartsWithASCII(dest_dir, output_dir, true));
+  std::string relative_dest_dir(&dest_dir[output_dir.size()],
+                                dest_dir.size() - output_dir.size());
+
+  const Target::FileList& sources = target_->sources();
+  std::vector<OutputFile> dest_files;
+  dest_files.reserve(sources.size());
+
+  // Write out rules for each file copied.
+  for (size_t i = 0; i < sources.size(); i++) {
+    const SourceFile& input_file = sources[i];
+
+    // The files should have the same name but in the dest dir.
+    base::StringPiece name_part = FindFilename(&input_file.value());
+    OutputFile dest_file(relative_dest_dir);
+    AppendStringPiece(&dest_file.value(), name_part);
+
+    dest_files.push_back(dest_file);
+
+    out_ << "build ";
+    path_output_.WriteFile(out_, dest_file);
+    out_ << ": copy ";
+    path_output_.WriteFile(out_, input_file);
+    out_ << std::endl;
+  }
+
+  // Write out the rule for the target to copy all of them.
+  out_ << std::endl << "build ";
+  path_output_.WriteFile(out_, helper_.GetTargetOutputFile(target_));
+  out_ << ": stamp";
+  for (size_t i = 0; i < dest_files.size(); i++) {
+    out_ << " ";
+    path_output_.WriteFile(out_, dest_files[i]);
+  }
+  out_ << std::endl;
+
+  // TODO(brettw) need some kind of stamp file for depending on this, as well
+  // as order_only=prebuild.
+}
+
+void NinjaTargetWriter::WriteCustomRules() {
+  // Make a unique name for this rule.
+  std::string target_label = target_->label().GetUserVisibleName(true);
+  std::string custom_rule_name(target_label);
+  ReplaceChars(custom_rule_name, ":/()", "_", &custom_rule_name);
+  custom_rule_name.append("_rule");
+
+  // Run the script from the dir of the BUILD file. This has no trailing
+  // slash.
+  const SourceDir& script_cd = target_->label().dir();
+  std::string script_cd_to_root = InvertDir(script_cd);
+  if (script_cd_to_root.empty()) {
+    script_cd_to_root = ".";
+  } else {
+    // Remove trailing slash
+    DCHECK(script_cd_to_root[script_cd_to_root.size() - 1] == '/');
+    script_cd_to_root.resize(script_cd_to_root.size() - 1);
+  }
+
+  std::string script_relative_to_cd =
+      script_cd_to_root + target_->script().value();
+
+  bool no_sources = target_->sources().empty();
+
+  // Use a unique name for the response file when there are multiple build
+  // steps so that they don't stomp on each other.
+  std::string rspfile = custom_rule_name;
+  if (!no_sources)
+    rspfile += ".$unique_name";
+  rspfile += ".rsp";
+
+  // First write the custom rule.
+  out_ << "rule " << custom_rule_name << std::endl;
+  out_ << "  command = $pythonpath gyp-win-tool action-wrapper $arch "
+       << rspfile << " ";
+  path_output_.WriteDir(out_, script_cd, PathOutput::DIR_NO_LAST_SLASH);
+  out_ << std::endl;
+  out_ << "  description = CUSTOM " << target_label << std::endl;
+  out_ << "  restat = 1" << std::endl;
+  out_ << "  rspfile = " << rspfile << std::endl;
+
+  // The build command goes in the rsp file.
+  out_ << "  rspfile_content = $pythonpath " << script_relative_to_cd;
+  for (size_t i = 0; i < target_->script_args().size(); i++) {
+    const std::string& arg = target_->script_args()[i];
+    out_ << " ";
+    WriteCustomArg(arg);
+  }
+  out_ << std::endl << std::endl;
+
+  // Precompute the common dependencies for each step. This includes the
+  // script itself (changing the script should force a rebuild) and any data
+  // files.
+  std::ostringstream common_deps_stream;
+  path_output_.WriteFile(common_deps_stream, target_->script());
+  const Target::FileList& datas = target_->data();
+  for (size_t i = 0; i < datas.size(); i++) {
+    common_deps_stream << " ";
+    path_output_.WriteFile(common_deps_stream, datas[i]);
+  }
+  const std::string& common_deps = common_deps_stream.str();
+
+  // Collects all output files for writing below.
+  std::vector<OutputFile> output_files;
+
+  if (no_sources) {
+    // No sources, write a rule that invokes the script once with the
+    // outputs as outputs, and the data as inputs.
+    out_ << "build";
+    const Target::FileList& outputs = target_->outputs();
+    for (size_t i = 0; i < outputs.size(); i++) {
+      OutputFile output_path(
+          RemovePrefix(outputs[i].value(),
+                       settings_->build_settings()->build_dir().value()));
+      output_files.push_back(output_path);
+      out_ << " ";
+      path_output_.WriteFile(out_, output_path);
+    }
+    out_ << ": " << custom_rule_name << " " << common_deps << std::endl;
+  } else {
+    // Write separate rules for each input source file.
+    WriteCustomSourceRules(custom_rule_name, common_deps, script_cd,
+                           script_cd_to_root, &output_files);
+  }
+  out_ << std::endl;
+
+  // Last write a stamp rule to collect all outputs.
+  out_ << "build ";
+  path_output_.WriteFile(out_, helper_.GetTargetOutputFile(target_));
+  out_ << ": stamp";
+  for (size_t i = 0; i < output_files.size(); i++) {
+    out_ << " ";
+    path_output_.WriteFile(out_, output_files[i]);
+  }
+  out_ << std::endl;
+}
+
+void NinjaTargetWriter::WriteCustomArg(const std::string& arg) {
+  // This can be optimized if it's called a lot.
+  EscapeOptions options;
+  options.mode = ESCAPE_NINJA;
+  std::string output_str = EscapeString(arg, options);
+
+  // Do this substitution after escaping our our $ will be escaped (which we
+  // don't want).
+  ReplaceSubstringsAfterOffset(&output_str, 0, FileTemplate::kSource,
+                               "${source}");
+  ReplaceSubstringsAfterOffset(&output_str, 0, FileTemplate::kSourceNamePart,
+                               "${source_name_part}");
+  out_ << output_str;
+}
+
+void NinjaTargetWriter::WriteCustomSourceRules(
+    const std::string& custom_rule_name,
+    const std::string& common_deps,
+    const SourceDir& script_cd,
+    const std::string& script_cd_to_root,
+    std::vector<OutputFile>* output_files) {
+  // Construct the template for generating the output files from each source.
+  const Target::FileList& outputs = target_->outputs();
+  std::vector<std::string> output_template_args;
+  for (size_t i = 0; i < outputs.size(); i++) {
+    // All outputs should be in the output dir.
+    output_template_args.push_back(
+        RemovePrefix(outputs[i].value(),
+                     settings_->build_settings()->build_dir().value()));
+  }
+  FileTemplate output_template(output_template_args);
+
+  // Prevent re-allocating each time by initializing outside the loop.
+  std::vector<std::string> output_template_result;
+
+  // Path output formatter for wrigin source paths passed to the script.
+  PathOutput script_source_path_output(script_cd, ESCAPE_SHELL, true);
+
+  const Target::FileList& sources = target_->sources();
+  for (size_t i = 0; i < sources.size(); i++) {
+    // Write outputs for this source file computed by the template.
+    out_ << "build";
+    output_template.ApplyString(sources[i].value(), &output_template_result);
+    for (size_t out_i = 0; out_i < output_template_result.size(); out_i++) {
+      OutputFile output_path(output_template_result[out_i]);
+      output_files->push_back(output_path);
+      out_ << " ";
+      path_output_.WriteFile(out_, output_path);
+    }
+
+    out_ << ": " << custom_rule_name
+         << " " << common_deps
+         << " ";
+    path_output_.WriteFile(out_, sources[i]);
+    out_ << std::endl;
+
+    out_ << "  unique_name = " << i << std::endl;
+
+    // The source file here should be relative to the script directory since
+    // this is the variable passed to the script. Here we slightly abuse the
+    // OutputFile object by putting a non-output-relative path in it to signal
+    // that the PathWriter should not prepend directories.
+    out_ << "  source = ";
+    script_source_path_output.WriteFile(out_, sources[i]);
+    out_ << std::endl;
+
+    out_ << "  source_name_part = "
+         << FindFilenameNoExtension(&sources[i].value()).as_string()
+         << std::endl;
+  }
+}
+
+void NinjaTargetWriter::WriteCompilerVars() {
+  // Defines.
+  out_ << "defines =";
+  RecursiveTargetConfigToStream(target_, &ConfigValues::defines,
+                                DefineWriter(), out_);
+  out_ << std::endl;
+
+  // Includes.
+  out_ << "includes =";
+  RecursiveTargetConfigToStream(target_, &ConfigValues::includes,
+                                IncludeWriter(path_output_, helper_), out_);
+
+  out_ << std::endl;
+
+  // C flags and friends.
+  out_ << "cflags =";
+  RecursiveTargetConfigStringsToStream(target_, &ConfigValues::cflags, out_);
+  out_ << std::endl;
+  out_ << "cflags_c =";
+  RecursiveTargetConfigStringsToStream(target_, &ConfigValues::cflags_c, out_);
+  out_ << std::endl;
+  out_ << "cflags_cc =";
+  RecursiveTargetConfigStringsToStream(target_, &ConfigValues::cflags_cc, out_);
+  out_ << std::endl;
+
+  out_ << std::endl;
+}
+
+void NinjaTargetWriter::WriteSources(
+    std::vector<OutputFile>* object_files) {
+  const Target::FileList& sources = target_->sources();
+  object_files->reserve(sources.size());
+
+  for (size_t i = 0; i < sources.size(); i++) {
+    const SourceFile& input_file = sources[i];
+
+    SourceFileType input_file_type = GetSourceFileType(input_file,
+                                                       settings_->target_os());
+    if (input_file_type == SOURCE_UNKNOWN)
+      continue;  // Skip unknown file types.
+    const char* command = GetCommandForSourceType(input_file_type);
+    if (!command)
+      continue;  // Skip files not needing compilation.
+
+    OutputFile output_file = helper_.GetOutputFileForSource(
+        target_, input_file, input_file_type);
+    object_files->push_back(output_file);
+
+    out_ << "build ";
+    path_output_.WriteFile(out_, output_file);
+    out_ << ": " << command << " ";
+    path_output_.WriteFile(out_, input_file);
+    out_ << std::endl;
+  }
+  out_ << std::endl;
+}
+
+void NinjaTargetWriter::WriteLinkerStuff(
+    const std::vector<OutputFile>& object_files) {
+  // Manifest file on Windows.
+  // TODO(brettw) this seems not to be necessary for static libs, skip in
+  // that case?
+  OutputFile windows_manifest;
+  if (settings_->IsWin()) {
+    windows_manifest.value().assign(helper_.GetTargetOutputDir(target_));
+    windows_manifest.value().append(target_->label().name());
+    windows_manifest.value().append(".intermediate.manifest");
+    out_ << "manifests = ";
+    path_output_.WriteFile(out_, windows_manifest);
+    out_ << std::endl;
+  }
+
+  // Linker flags, append manifest flag on Windows to reference our file.
+  out_ << "ldflags =";
+  RecursiveTargetConfigStringsToStream(target_, &ConfigValues::ldflags, out_);
+  if (settings_->IsWin())
+    out_ << " /MANIFEST /ManifestFile:";
+    path_output_.WriteFile(out_, windows_manifest);
+  {  // HACK ERASEME BRETTW FIXME
+    out_ << " /DEBUG /MACHINE:X86 /LIBPATH:\"C:\\Program Files (x86)\\Windows Kits\\8.0\\Lib\\win8\\um\\x86\" /DELAYLOAD:dbghelp.dll /DELAYLOAD:dwmapi.dll /DELAYLOAD:shell32.dll /DELAYLOAD:uxtheme.dll /safeseh /dynamicbase /ignore:4199 /ignore:4221 /nxcompat /SUBSYSTEM:CONSOLE /INCREMENTAL /FIXED:NO /DYNAMICBASE:NO wininet.lib dnsapi.lib version.lib msimg32.lib ws2_32.lib usp10.lib psapi.lib dbghelp.lib winmm.lib shlwapi.lib kernel32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib user32.lib uuid.lib odbc32.lib odbccp32.lib delayimp.lib /NXCOMPAT";
+  }
+  out_ << std::endl;
+
+  // Libraries to link.
+  out_ << "libs =" << std::endl;
+
+  // The external output file is the one that other libs depend on.
+  OutputFile external_output_file = helper_.GetTargetOutputFile(target_);
+
+  // The internal output file is the "main thing" we think we're making. In
+  // the case of shared libraries, this is the shared library and the external
+  // output file is the import library. In other cases, the internal one and
+  // the external one are the same.
+  OutputFile internal_output_file;
+  if (target_->output_type() == Target::SHARED_LIBRARY) {
+    if (settings_->IsWin()) {
+      internal_output_file = OutputFile(target_->label().name() + ".dll");
+    } else {
+      NOTREACHED();  // TODO(brettw) write this.
+    }
+  } else {
+    internal_output_file = external_output_file;
+  }
+
+  // TODO(brettw) should we append data files to this?
+
+  // In Python see "self.ninja.build(output, command, input,"
+  out_ << "build ";
+  path_output_.WriteFile(out_, internal_output_file);
+  if (external_output_file != internal_output_file) {
+    out_ << " ";
+    path_output_.WriteFile(out_, external_output_file);
+  }
+  out_ << ": " << GetCommandForTargetType();
+  for (size_t i = 0; i < object_files.size(); i++) {
+    out_ << " ";
+    path_output_.WriteFile(out_, object_files[i]);
+  }
+
+  if (target_->output_type() == Target::EXECUTABLE ||
+      target_->output_type() == Target::SHARED_LIBRARY ||
+      target_->output_type() == Target::LOADABLE_MODULE) {
+    const std::vector<const Target*>& deps = target_->deps();
+    const std::set<const Target*>& inherited = target_->inherited_libraries();
+
+    // Now append linkable libraries to the linker command.
+    for (size_t i = 0; i < deps.size(); i++) {
+      if (deps[i]->IsLinkable() &&
+          inherited.find(deps[i]) == inherited.end()) {
+        out_ << " ";
+        path_output_.WriteFile(out_,
+                               helper_.GetTargetOutputFile(target_->deps()[i]));
+      }
+    }
+    for (std::set<const Target*>::const_iterator i = inherited.begin();
+         i != inherited.end(); ++i) {
+      out_ << " ";
+      path_output_.WriteFile(out_, helper_.GetTargetOutputFile(*i));
+    }
+  }
+  out_ << std::endl;
+
+  if (target_->output_type() == Target::SHARED_LIBRARY) {
+    out_ << "  soname = ";
+    path_output_.WriteFile(out_, internal_output_file);
+    out_ << std::endl;
+
+    out_ << "  lib = ";
+    path_output_.WriteFile(out_, internal_output_file);
+    out_ << std::endl;
+
+    out_ << "  dll = ";
+    path_output_.WriteFile(out_, internal_output_file);
+    out_ << std::endl;
+
+    if (settings_->IsWin()) {
+      out_ << "  implibflag = /IMPLIB:";
+      path_output_.WriteFile(out_, external_output_file);
+      out_ << std::endl;
+    }
+  }
+
+  // TODO(brettw) postbuild steps here.
+
+  out_ << std::endl;
+}
+
+const char* NinjaTargetWriter::GetCommandForSourceType(
+    SourceFileType type) const {
+  if (type == SOURCE_C)
+    return "cc";
+  if (type == SOURCE_CC)
+    return "cxx";
+
+  // TODO(brettw) asm files.
+
+  if (settings_->IsMac()) {
+    if (type == SOURCE_M)
+      return "objc";
+    if (type == SOURCE_MM)
+      return "objcxx";
+  }
+
+  if (settings_->IsWin()) {
+    if (type == SOURCE_RC)
+      return "rc";
+  }
+
+  // TODO(brettw) stuff about "S" files on non-Windows.
+  return NULL;
+}
+
+const char* NinjaTargetWriter::GetCommandForTargetType() const {
+  if (target_->output_type() == Target::NONE) {
+    NOTREACHED();
+    return "";
+  }
+
+  if (target_->output_type() == Target::STATIC_LIBRARY) {
+    // TODO(brettw) stuff about standalong static libraryes on Unix in
+    // WriteTarget in the Python one, and lots of postbuild steps.
+    return "alink";
+  }
+
+  if (target_->output_type() == Target::SHARED_LIBRARY)
+    return "solink";
+
+  return "link";
+}
diff --git a/tools/gn/ninja_target_writer.h b/tools/gn/ninja_target_writer.h
new file mode 100644
index 0000000..5e6827d
--- /dev/null
+++ b/tools/gn/ninja_target_writer.h
@@ -0,0 +1,67 @@
+// Copyright (c) 2013 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.
+
+#ifndef TOOLS_GN_NINJA_TARGET_WRITER_H_
+#define TOOLS_GN_NINJA_TARGET_WRITER_H_
+
+#include <iosfwd>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/files/file_path.h"
+#include "tools/gn/filesystem_utils.h"
+#include "tools/gn/ninja_helper.h"
+#include "tools/gn/path_output.h"
+#include "tools/gn/settings.h"
+
+class Target;
+
+// Generates one target's ".ninja" file. The toplevel "build.ninja" file is
+// generated by the NinjaBuildGenerator.
+class NinjaTargetWriter {
+ public:
+  NinjaTargetWriter(const Target* target, std::ostream& out);
+  ~NinjaTargetWriter();
+
+  void Run();
+
+  static void RunAndWriteFile(const Target* target);
+
+ private:
+  void WriteCopyRules();
+
+  void WriteCustomRules();
+  void WriteCustomArg(const std::string& arg);
+
+  // Writs the rules for compiling each source, writing all output files
+  // to the given vector.
+  //
+  // common_deps is a precomputed string of all ninja files that are common
+  // to each build step. This is added to each one.
+  void WriteCustomSourceRules(const std::string& custom_rule_name,
+                              const std::string& common_deps,
+                              const SourceDir& script_cd,
+                              const std::string& script_cd_to_root,
+                              std::vector<OutputFile>* output_files);
+
+  void WriteCompilerVars();
+  void WriteSources(std::vector<OutputFile>* object_files);
+  void WriteLinkerStuff(const std::vector<OutputFile>& object_files);
+
+  // Returns NULL if the source type should not be compiled on this target.
+  const char* GetCommandForSourceType(SourceFileType type) const;
+
+  const char* GetCommandForTargetType() const;
+
+  const Settings* settings_;  // Non-owning.
+  const Target* target_;  // Non-owning.
+  std::ostream& out_;
+  PathOutput path_output_;
+
+  NinjaHelper helper_;
+
+  DISALLOW_COPY_AND_ASSIGN(NinjaTargetWriter);
+};
+
+#endif  // TOOLS_GN_NINJA_TARGET_WRITER_H_
diff --git a/tools/gn/ninja_toolchain_writer.cc b/tools/gn/ninja_toolchain_writer.cc
new file mode 100644
index 0000000..0e38b8c
--- /dev/null
+++ b/tools/gn/ninja_toolchain_writer.cc
@@ -0,0 +1,94 @@
+// Copyright (c) 2013 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/ninja_toolchain_writer.h"
+
+#include <fstream>
+
+#include "base/file_util.h"
+#include "base/strings/stringize_macros.h"
+#include "tools/gn/build_settings.h"
+#include "tools/gn/settings.h"
+#include "tools/gn/target.h"
+#include "tools/gn/toolchain.h"
+
+NinjaToolchainWriter::NinjaToolchainWriter(
+    const Settings* settings,
+    const std::vector<const Target*>& targets,
+    std::ostream& out)
+    : settings_(settings),
+      targets_(targets),
+      out_(out),
+      path_output_(settings_->build_settings()->build_dir(),
+                   ESCAPE_NINJA, true),
+      helper_(settings->build_settings()) {
+}
+
+NinjaToolchainWriter::~NinjaToolchainWriter() {
+}
+
+void NinjaToolchainWriter::Run() {
+  WriteRules();
+  WriteSubninjas();
+}
+
+// static
+bool NinjaToolchainWriter::RunAndWriteFile(
+    const Settings* settings,
+    const std::vector<const Target*>& targets) {
+  NinjaHelper helper(settings->build_settings());
+  base::FilePath ninja_file(settings->build_settings()->GetFullPath(
+      helper.GetNinjaFileForToolchain(settings).GetSourceFile(
+          settings->build_settings())));
+  file_util::CreateDirectory(ninja_file.DirName());
+
+  std::ofstream file;
+  file.open(FilePathToUTF8(ninja_file).c_str(),
+            std::ios_base::out | std::ios_base::binary);
+  if (file.fail())
+    return false;
+
+  NinjaToolchainWriter gen(settings, targets, file);
+  gen.Run();
+  return true;
+}
+
+void NinjaToolchainWriter::WriteRules() {
+  const Toolchain* tc = settings_->toolchain();
+  std::string indent("  ");
+
+  for (int i = Toolchain::TYPE_NONE + 1; i < Toolchain::TYPE_NUMTYPES; i++) {
+    Toolchain::ToolType tool_type = static_cast<Toolchain::ToolType>(i);
+    const Toolchain::Tool& tool = tc->GetTool(tool_type);
+    if (tool.empty())
+      continue;
+
+    out_ << "rule " << Toolchain::ToolTypeToName(tool_type) << std::endl;
+
+    #define WRITE_ARG(name) \
+      if (!tool.name.empty()) \
+        out_ << indent << "  " STRINGIZE(name) " = " << tool.name << std::endl;
+    WRITE_ARG(command);
+    WRITE_ARG(depfile);
+    WRITE_ARG(deps);
+    WRITE_ARG(description);
+    WRITE_ARG(pool);
+    WRITE_ARG(restat);
+    WRITE_ARG(rspfile);
+    WRITE_ARG(rspfile_content);
+    #undef WRITE_ARG
+  }
+  out_ << std::endl;
+}
+
+void NinjaToolchainWriter::WriteSubninjas() {
+  for (size_t i = 0; i < targets_.size(); i++) {
+    if (targets_[i]->output_type() != Target::NONE) {
+      out_ << "subninja ";
+      path_output_.WriteFile(out_, helper_.GetNinjaFileForTarget(targets_[i]));
+      out_ << std::endl;
+    }
+  }
+  out_ << std::endl;
+}
diff --git a/tools/gn/ninja_toolchain_writer.h b/tools/gn/ninja_toolchain_writer.h
new file mode 100644
index 0000000..71759ef
--- /dev/null
+++ b/tools/gn/ninja_toolchain_writer.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2013 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.
+
+#ifndef TOOLS_GN_NINJA_TOOLCHAIN_WRITER_H_
+#define TOOLS_GN_NINJA_TOOLCHAIN_WRITER_H_
+
+#include <iosfwd>
+#include <vector>
+
+#include "tools/gn/ninja_helper.h"
+#include "tools/gn/path_output.h"
+
+class BuildSettings;
+class Settings;
+class Target;
+
+class NinjaToolchainWriter {
+ public:
+  // Takes the settings for the toolchain, as well as the list of all targets
+  // assicoated with the toolchain.
+  static bool RunAndWriteFile(const Settings* settings,
+                              const std::vector<const Target*>& targets);
+
+ private:
+  NinjaToolchainWriter(const Settings* settings,
+                       const std::vector<const Target*>& targets,
+                       std::ostream& out);
+  ~NinjaToolchainWriter();
+
+  void Run();
+
+  void WriteRules();
+  void WriteSubninjas();
+
+  const Settings* settings_;
+  std::vector<const Target*> targets_;
+  std::ostream& out_;
+  PathOutput path_output_;
+
+  NinjaHelper helper_;
+
+  DISALLOW_COPY_AND_ASSIGN(NinjaToolchainWriter);
+};
+
+#endif  // TOOLS_GN_NINJA_TOOLCHAIN_WRITER_H_
diff --git a/tools/gn/ninja_writer.cc b/tools/gn/ninja_writer.cc
new file mode 100644
index 0000000..8b69a3f
--- /dev/null
+++ b/tools/gn/ninja_writer.cc
@@ -0,0 +1,64 @@
+// Copyright (c) 2013 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/ninja_writer.h"
+
+#include "tools/gn/location.h"
+#include "tools/gn/ninja_build_writer.h"
+#include "tools/gn/ninja_toolchain_writer.h"
+
+
+NinjaWriter::NinjaWriter(const BuildSettings* build_settings)
+    : build_settings_(build_settings) {
+}
+
+NinjaWriter::~NinjaWriter() {
+}
+
+// static
+bool NinjaWriter::RunAndWriteFiles(const BuildSettings* build_settings) {
+  NinjaWriter writer(build_settings);
+  return writer.WriteRootBuildfiles();
+}
+
+bool NinjaWriter::WriteRootBuildfiles() {
+  // Categorize all targets by toolchain.
+  typedef std::map<Label, std::vector<const Target*> > CategorizedMap;
+  CategorizedMap categorized;
+
+  std::vector<const Target*> all_targets;
+  build_settings_->target_manager().GetAllTargets(&all_targets);
+  for (size_t i = 0; i < all_targets.size(); i++) {
+    categorized[all_targets[i]->label().GetToolchainLabel()].push_back(
+        all_targets[i]);
+  }
+
+  Label default_label =
+      build_settings_->toolchain_manager().GetDefaultToolchainUnlocked();
+
+  // Write out the toolchain buildfiles, and also accumulate the set of
+  // all settings and find the list of targets in the default toolchain.
+  std::vector<const Settings*> all_settings;
+  const std::vector<const Target*>* default_targets = NULL;
+  for (CategorizedMap::const_iterator i = categorized.begin();
+       i != categorized.end(); ++i) {
+    const Settings* settings;
+    {
+      base::AutoLock lock(build_settings_->item_tree().lock());
+      Err ignored;
+      settings =
+          build_settings_->toolchain_manager().GetSettingsForToolchainLocked(
+              LocationRange(), i->first, &ignored);
+    }
+    if (i->first == default_label)
+      default_targets = &i->second;
+    all_settings.push_back(settings);
+    if (!NinjaToolchainWriter::RunAndWriteFile(settings, i->second))
+      return false;
+  }
+
+  // Write the root buildfile.
+  return NinjaBuildWriter::RunAndWriteFile(build_settings_, all_settings,
+                                           *default_targets);
+}
diff --git a/tools/gn/ninja_writer.h b/tools/gn/ninja_writer.h
new file mode 100644
index 0000000..9b6d2c1
--- /dev/null
+++ b/tools/gn/ninja_writer.h
@@ -0,0 +1,27 @@
+// Copyright (c) 2013 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.
+
+#ifndef TOOLS_GN_NINJA_WRITER_H_
+#define TOOLS_GN_NINJA_WRITER_H_
+
+#include "base/basictypes.h"
+
+class BuildSettings;
+
+class NinjaWriter {
+ public:
+  static bool RunAndWriteFiles(const BuildSettings* build_settings);
+
+ private:
+  NinjaWriter(const BuildSettings* build_settings);
+  ~NinjaWriter();
+
+  bool WriteRootBuildfiles();
+
+  const BuildSettings* build_settings_;
+
+  DISALLOW_COPY_AND_ASSIGN(NinjaWriter);
+};
+
+#endif  // TOOLS_GN_NINJA_WRITER_H_
diff --git a/tools/gn/operators.cc b/tools/gn/operators.cc
new file mode 100644
index 0000000..afded9f
--- /dev/null
+++ b/tools/gn/operators.cc
@@ -0,0 +1,573 @@
+// Copyright (c) 2013 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/operators.h"
+
+#include "base/strings/string_number_conversions.h"
+#include "tools/gn/err.h"
+#include "tools/gn/parse_tree.h"
+#include "tools/gn/scope.h"
+#include "tools/gn/token.h"
+#include "tools/gn/value.h"
+
+namespace {
+
+const char kSourcesName[] = "sources";
+
+// Applies the sources assignment filter from the given scope to each element
+// of source (can be a list or a string), appending it to dest if it doesn't
+// match.
+void AppendFilteredSourcesToValue(const Scope* scope,
+                                  const Value& source,
+                                  Value* dest) {
+  const PatternList* filter = scope->GetSourcesAssignmentFilter();
+
+  const std::vector<Value>& source_list = source.list_value();
+
+  if (source.type() == Value::STRING) {
+    if (!filter || filter->is_empty() ||
+        !filter->MatchesValue(source))
+      dest->list_value().push_back(source);
+    return;
+  }
+
+  // Otherwise source is a list.
+  DCHECK(source.type() == Value::LIST);
+  if (!filter || filter->is_empty()) {
+    // No filter, append everything.
+    for (size_t i = 0; i < source_list.size(); i++)
+      dest->list_value().push_back(source_list[i]);
+    return;
+  }
+
+  // Note: don't reserve() the dest vector here since that actually hurts
+  // the allocation pattern when the build script is doing multiple small
+  // additions.
+  for (size_t i = 0; i < source_list.size(); i++) {
+    if (!filter->MatchesValue(source_list[i]))
+      dest->list_value().push_back(source_list[i]);
+  }
+}
+
+void RemoveMatchesFromList(const BinaryOpNode* op_node,
+                           Value* list,
+                           const Value& to_remove,
+                           Err* err) {
+  std::vector<Value>& v = list->list_value();
+  switch (to_remove.type()) {
+    case Value::INTEGER:  // Filter out the individual int/string.
+    case Value::STRING: {
+      bool found_match = false;
+      for (size_t i = 0; i < v.size(); /* nothing */) {
+        if (v[i] == to_remove) {
+          found_match = true;
+          v.erase(v.begin() + i);
+        } else {
+          i++;
+        }
+      }
+      if (!found_match) {
+        *err = Err(to_remove.origin()->GetRange(), "Item not found",
+            "You were trying to remove \"" + to_remove.ToString() +
+            "\"\nfrom the list but it wasn't there.");
+      }
+      break;
+    }
+
+    case Value::LIST:  // Filter out each individual thing.
+      for (size_t i = 0; i < to_remove.list_value().size(); i++) {
+        // TODO(brettw) if the nested item is a list, we may want to search
+        // for the literal list rather than remote the items in it.
+        RemoveMatchesFromList(op_node, list, to_remove.list_value()[i], err);
+        if (err->has_error())
+          return;
+      }
+      break;
+
+    default:
+      break;
+  }
+}
+
+// Assignment -----------------------------------------------------------------
+
+Value ExecuteEquals(Scope* scope,
+                    const BinaryOpNode* op_node,
+                    const Token& left,
+                    const Value& right,
+                    Err* err) {
+  const Value* old_value = scope->GetValue(left.value(), false);
+  if (old_value) {
+    if (scope->IsSetButUnused(left.value())) {
+      // Throw an error for re-assigning without using the value first. The
+      // exception is that you can overwrite an empty list with another list
+      // since this is the way to get around the "can't overwrite a nonempty
+      // list with another nonempty list" restriction.
+      if (old_value->type() != Value::LIST ||
+          !old_value->list_value().empty()) {
+        *err = Err(op_node->left()->GetRange(), "Overwriting unused variable.",
+            "This overwrites a previous assignment to \"" +
+            left.value().as_string() + "\" that had no effect.");
+        err->AppendSubErr(Err(*scope->GetValue(left.value()),
+                              "Previously set here.",
+                              "Maybe you wanted \"+=\" to append instead?"));
+        return Value();
+      }
+    } else {
+      // Throw an error when overwriting a nonempty list with another nonempty
+      // list item. This is to detect the case where you write
+      //   defines = ["FOO"]
+      // and you overwrote inherited ones, when instead you mean to append:
+      //   defines += ["FOO"]
+      if (old_value->type() == Value::LIST &&
+          !old_value->list_value().empty() &&
+          right.type() == Value::LIST &&
+          !right.list_value().empty()) {
+        *err = Err(op_node->left()->GetRange(), "Replacing nonempty list.",
+            std::string("This overwrites a previously-defined nonempty list ") +
+            "(length " + base::IntToString(old_value->list_value().size()) +
+            ").");
+        err->AppendSubErr(Err(*old_value, "for previous definition",
+            "with another one (length " +
+            base::IntToString(right.list_value().size()) + "). Did you mean " +
+            "\"+=\" to append instead? If you\nreally want to do this, do\n  " +
+            left.value().as_string() + " = []\nbefore reassigning."));
+        return Value();
+      }
+    }
+  }
+  if (err->has_error())
+    return Value();
+
+  if (right.type() == Value::LIST && left.value() == kSourcesName) {
+    // Assigning to sources, filter the list. Here we do the filtering and
+    // copying in one step to save an extra list copy (the lists may be
+    // long).
+    Value* set_value = scope->SetValue(left.value(),
+                                       Value(op_node, Value::LIST), op_node);
+    set_value->list_value().reserve(right.list_value().size());
+    AppendFilteredSourcesToValue(scope, right, set_value);
+  } else {
+    // Normal value set, just copy it.
+    scope->SetValue(left.value(), right, op_node->right());
+  }
+  return Value();
+}
+
+// allow_type_conversion indicates if we're allowed to change the type of the
+// left value. This is set to true when doing +, and false when doing +=.
+void ValuePlusEquals(const Scope* scope,
+                     const BinaryOpNode* op_node,
+                     const Token& left_token,
+                     Value* left,
+                     const Value& right,
+                     bool allow_type_conversion,
+                     Err* err) {
+  switch (left->type()) {
+    // Left-hand-side int.
+    case Value::INTEGER:
+      switch (right.type()) {
+        case Value::INTEGER:  // int + int -> addition.
+          left->int_value() += right.int_value();
+          return;
+
+        case Value::STRING:  // int + string -> string concat.
+          if (allow_type_conversion) {
+            *left = Value(op_node,
+                base::Int64ToString(left->int_value()) + right.string_value());
+            return;
+          }
+          break;
+
+        default:
+          break;
+      }
+      break;
+
+    // Left-hand-side string.
+    case Value::STRING:
+      switch (right.type()) {
+        case Value::INTEGER:  // string + int -> string concat.
+          left->string_value().append(base::Int64ToString(right.int_value()));
+          return;
+
+        case Value::STRING:  // string + string -> string contat.
+          left->string_value().append(right.string_value());
+          return;
+
+        default:
+          break;
+      }
+      break;
+
+    // Left-hand-side list.
+    case Value::LIST:
+      switch (right.type()) {
+        case Value::INTEGER:  // list + integer -> list append.
+        case Value::STRING:  // list + string -> list append.
+          if (left_token.value() == kSourcesName)
+            AppendFilteredSourcesToValue(scope, right, left);
+          else
+            left->list_value().push_back(right);
+          return;
+
+        case Value::LIST:  // list + list -> list concat.
+          if (left_token.value() == kSourcesName) {
+            // Filter additions through the assignment filter.
+            AppendFilteredSourcesToValue(scope, right, left);
+          } else {
+            // Normal list concat.
+            for (size_t i = 0; i < right.list_value().size(); i++)
+              left->list_value().push_back(right.list_value()[i]);
+          }
+          return;
+
+        default:
+          break;
+      }
+
+    default:
+      break;
+  }
+
+  *err = Err(op_node->op(), "Incompatible types to add.",
+      std::string("I see a ") + Value::DescribeType(left->type()) + " and a " +
+      Value::DescribeType(right.type()) + ".");
+}
+
+Value ExecutePlusEquals(Scope* scope,
+                        const BinaryOpNode* op_node,
+                        const Token& left,
+                        const Value& right,
+                        Err* err) {
+  // We modify in-place rather than doing read-modify-write to avoid
+  // copying large lists.
+  Value* left_value =
+      scope->GetValueForcedToCurrentScope(left.value(), op_node);
+  if (!left_value) {
+    *err = Err(left, "Undefined variable for +=.",
+        "I don't have something with this name in scope now.");
+    return Value();
+  }
+  ValuePlusEquals(scope, op_node, left, left_value, right, false, err);
+  left_value->set_origin(op_node);
+  scope->MarkUnused(left.value());
+  return Value();
+}
+
+void ValueMinusEquals(const BinaryOpNode* op_node,
+                      Value* left,
+                      const Value& right,
+                      bool allow_type_conversion,
+                      Err* err) {
+  switch (left->type()) {
+    // Left-hand-side int.
+    case Value::INTEGER:
+      switch (right.type()) {
+        case Value::INTEGER:  // int - int -> subtraction.
+          left->int_value() -= right.int_value();
+          return;
+
+        default:
+          break;
+      }
+      break;
+
+    // Left-hand-side string.
+    case Value::STRING:
+      break;  // All are errors.
+
+    // Left-hand-side list.
+    case Value::LIST:
+      RemoveMatchesFromList(op_node, left, right, err);
+      return;
+
+    default:
+      break;
+  }
+
+  *err = Err(op_node->op(), "Incompatible types to add.",
+      std::string("I see a ") + Value::DescribeType(left->type()) + " and a " +
+      Value::DescribeType(right.type()) + ".");
+}
+
+Value ExecuteMinusEquals(Scope* scope,
+                         const BinaryOpNode* op_node,
+                         const Token& left,
+                         const Value& right,
+                         Err* err) {
+  Value* left_value =
+      scope->GetValueForcedToCurrentScope(left.value(), op_node);
+  if (!left_value) {
+    *err = Err(left, "Undefined variable for -=.",
+        "I don't have something with this name in scope now.");
+    return Value();
+  }
+  ValueMinusEquals(op_node, left_value, right, false, err);
+  left_value->set_origin(op_node);
+  scope->MarkUnused(left.value());
+  return Value();
+}
+
+// Plus/Minus -----------------------------------------------------------------
+
+Value ExecutePlus(Scope* scope,
+                  const BinaryOpNode* op_node,
+                  const Value& left,
+                  const Value& right,
+                  Err* err) {
+  Value ret = left;
+  ValuePlusEquals(scope, op_node, Token(), &ret, right, true, err);
+  ret.set_origin(op_node);
+  return ret;
+}
+
+Value ExecuteMinus(Scope* scope,
+                   const BinaryOpNode* op_node,
+                   const Value& left,
+                   const Value& right,
+                   Err* err) {
+  Value ret = left;
+  ValueMinusEquals(op_node, &ret, right, true, err);
+  ret.set_origin(op_node);
+  return ret;
+}
+
+// Comparison -----------------------------------------------------------------
+
+Value ExecuteEqualsEquals(Scope* scope,
+                          const BinaryOpNode* op_node,
+                          const Value& left,
+                          const Value& right,
+                          Err* err) {
+  if (left == right)
+    return Value(op_node, 1);
+  return Value(op_node, 0);
+}
+
+Value ExecuteNotEquals(Scope* scope,
+                       const BinaryOpNode* op_node,
+                       const Value& left,
+                       const Value& right,
+                       Err* err) {
+  // Evaluate in terms of ==.
+  Value result = ExecuteEqualsEquals(scope, op_node, left, right, err);
+  result.int_value() = static_cast<int64>(!result.int_value());
+  return result;
+}
+
+Value FillNeedsToIntegersError(const BinaryOpNode* op_node,
+                               const Value& left,
+                               const Value& right,
+                               Err* err) {
+  *err = Err(op_node, "Comparison requires two integers.",
+             "This operator can only compare two integers.");
+  err->AppendRange(left.origin()->GetRange());
+  err->AppendRange(right.origin()->GetRange());
+  return Value();
+}
+
+Value ExecuteLessEquals(Scope* scope,
+                        const BinaryOpNode* op_node,
+                        const Value& left,
+                        const Value& right,
+                        Err* err) {
+  if (left.type() != Value::INTEGER || right.type() != Value::INTEGER)
+    return FillNeedsToIntegersError(op_node, left, right, err);
+  return Value(op_node, left.int_value() <= right.int_value());
+}
+
+Value ExecuteGreaterEquals(Scope* scope,
+                           const BinaryOpNode* op_node,
+                           const Value& left,
+                           const Value& right,
+                           Err* err) {
+  if (left.type() != Value::INTEGER || right.type() != Value::INTEGER)
+    return FillNeedsToIntegersError(op_node, left, right, err);
+  return Value(op_node, left.int_value() >= right.int_value());
+}
+
+Value ExecuteGreater(Scope* scope,
+                     const BinaryOpNode* op_node,
+                     const Value& left,
+                     const Value& right,
+                     Err* err) {
+  if (left.type() != Value::INTEGER || right.type() != Value::INTEGER)
+    return FillNeedsToIntegersError(op_node, left, right, err);
+  return Value(op_node, left.int_value() > right.int_value());
+}
+
+Value ExecuteLess(Scope* scope,
+                  const BinaryOpNode* op_node,
+                  const Value& left,
+                  const Value& right,
+                  Err* err) {
+  if (left.type() != Value::INTEGER || right.type() != Value::INTEGER)
+    return FillNeedsToIntegersError(op_node, left, right, err);
+  return Value(op_node, left.int_value() < right.int_value());
+}
+
+// Binary ----------------------------------------------------------------------
+
+Value ExecuteOr(Scope* scope,
+                const BinaryOpNode* op_node,
+                const Value& left,
+                const Value& right,
+                Err* err) {
+  return Value(op_node,
+      static_cast<int64>(left.InterpretAsInt() || right.InterpretAsInt()));
+}
+
+Value ExecuteAnd(Scope* scope,
+                 const BinaryOpNode* op_node,
+                 const Value& left,
+                 const Value& right,
+                 Err* err) {
+  return Value(op_node,
+      static_cast<int64>(left.InterpretAsInt() && right.InterpretAsInt()));
+}
+
+}  // namespace
+
+// ----------------------------------------------------------------------------
+
+bool IsUnaryOperator(const Token& token) {
+  if (token.type() != Token::OPERATOR)
+    return false;
+  return token.value() == "!";
+}
+
+bool IsBinaryOperator(const Token& token) {
+  if (token.type() != Token::OPERATOR)
+    return false;
+  return token.value() == "=" ||
+         token.value() == "+=" ||
+         token.value() == "-=" ||
+         token.value() == "+" ||
+         token.value() == "-" ||
+         token.value() == "==" ||
+         token.value() == "!=" ||
+         token.value() == "<=" ||
+         token.value() == ">=" ||
+         token.value() == "<" ||
+         token.value() == ">" ||
+         token.value() == "&&" ||
+         token.value() == "||";
+}
+
+bool IsFunctionCallArgBeginScoper(const Token& token) {
+  return token.IsScoperEqualTo("(");
+}
+
+bool IsFunctionCallArgEndScoper(const Token& token) {
+  return token.IsScoperEqualTo(")");
+}
+
+bool IsScopeBeginScoper(const Token& token) {
+  return token.IsScoperEqualTo("{");
+}
+
+bool IsScopeEndScoper(const Token& token) {
+  return token.IsScoperEqualTo("}");
+}
+
+Value ExecuteUnaryOperator(Scope* scope,
+                           const UnaryOpNode* op_node,
+                           const Value& expr,
+                           Err* err) {
+  DCHECK(op_node->op().IsOperatorEqualTo("!"));
+  return Value(op_node, !expr.InterpretAsInt());
+}
+
+Value ExecuteBinaryOperator(Scope* scope,
+                            const BinaryOpNode* op_node,
+                            const ParseNode* left,
+                            const ParseNode* right,
+                            Err* err) {
+  const Token& op = op_node->op();
+
+  // First handle the ones that take an lvalue.
+  if (op.IsOperatorEqualTo("=") ||
+      op.IsOperatorEqualTo("+=") ||
+      op.IsOperatorEqualTo("-=")) {
+    const IdentifierNode* left_id = left->AsIdentifier();
+    if (!left_id) {
+      *err = Err(op, "Operator requires an lvalue.",
+                 "This thing on the left is not an idenfitier.");
+      err->AppendRange(left->GetRange());
+      return Value();
+    }
+    const Token& dest = left_id->value();
+
+    Value right_value = right->Execute(scope, err);
+    if (err->has_error())
+      return Value();
+    if (right_value.type() == Value::NONE) {
+      *err = Err(op, "Operator requires an rvalue.",
+                 "This thing on the right does not evaluate to a value.");
+      err->AppendRange(right->GetRange());
+      return Value();
+    }
+
+    if (op.IsOperatorEqualTo("="))
+      return ExecuteEquals(scope, op_node, dest, right_value, err);
+    if (op.IsOperatorEqualTo("+="))
+      return ExecutePlusEquals(scope, op_node, dest, right_value, err);
+    if (op.IsOperatorEqualTo("-="))
+      return ExecuteMinusEquals(scope, op_node, dest, right_value, err);
+    NOTREACHED();
+    return Value();
+  }
+
+  // Left value.
+  Value left_value = left->Execute(scope, err);
+  if (err->has_error())
+    return Value();
+  if (left_value.type() == Value::NONE) {
+    *err = Err(op, "Operator requires an value.",
+               "This thing on the left does not evaluate to a value.");
+    err->AppendRange(left->GetRange());
+    return Value();
+  }
+
+  // Right value. Note: don't move this above to share code with the lvalue
+  // version since in this case we want to execute the left side first.
+  Value right_value = right->Execute(scope, err);
+  if (err->has_error())
+    return Value();
+  if (right_value.type() == Value::NONE) {
+    *err = Err(op, "Operator requires an value.",
+               "This thing on the right does not evaluate to a value.");
+    err->AppendRange(right->GetRange());
+    return Value();
+  }
+
+  // +, -.
+  if (op.IsOperatorEqualTo("-"))
+    return ExecuteMinus(scope, op_node, left_value, right_value, err);
+  if (op.IsOperatorEqualTo("+"))
+    return ExecutePlus(scope, op_node, left_value, right_value, err);
+
+  // Comparisons.
+  if (op.IsOperatorEqualTo("=="))
+    return ExecuteEqualsEquals(scope, op_node, left_value, right_value, err);
+  if (op.IsOperatorEqualTo("!="))
+    return ExecuteNotEquals(scope, op_node, left_value, right_value, err);
+  if (op.IsOperatorEqualTo(">="))
+    return ExecuteGreaterEquals(scope, op_node, left_value, right_value, err);
+  if (op.IsOperatorEqualTo("<="))
+    return ExecuteLessEquals(scope, op_node, left_value, right_value, err);
+  if (op.IsOperatorEqualTo(">"))
+    return ExecuteGreater(scope, op_node, left_value, right_value, err);
+  if (op.IsOperatorEqualTo("<"))
+    return ExecuteLess(scope, op_node, left_value, right_value, err);
+
+  // ||, &&.
+  if (op.IsOperatorEqualTo("||"))
+    return ExecuteOr(scope, op_node, left_value, right_value, err);
+  if (op.IsOperatorEqualTo("&&"))
+    return ExecuteAnd(scope, op_node, left_value, right_value, err);
+
+  return Value();
+}
diff --git a/tools/gn/operators.h b/tools/gn/operators.h
new file mode 100644
index 0000000..3401c22
--- /dev/null
+++ b/tools/gn/operators.h
@@ -0,0 +1,35 @@
+// Copyright (c) 2013 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.
+
+#ifndef TOOLS_GN_OPERATORS_H_
+#define TOOLS_GN_OPERATORS_H_
+
+class BinaryOpNode;
+class Err;
+class ParseNode;
+class Scope;
+class Token;
+class UnaryOpNode;
+class Value;
+
+bool IsUnaryOperator(const Token& token);
+bool IsBinaryOperator(const Token& token);
+
+bool IsFunctionCallArgBeginScoper(const Token& token);  // "("
+bool IsFunctionCallArgEndScoper(const Token& token);  // ")"
+
+bool IsScopeBeginScoper(const Token& token);  // "{"
+bool IsScopeEndScoper(const Token& token);  // "}"
+
+Value ExecuteUnaryOperator(Scope* scope,
+                           const UnaryOpNode* op_node,
+                           const Value& value,
+                           Err* err);
+Value ExecuteBinaryOperator(Scope* scope,
+                            const BinaryOpNode* op_node,
+                            const ParseNode* left,
+                            const ParseNode* right,
+                            Err* err);
+
+#endif  // TOOLS_GN_OPERATORS_H_
diff --git a/tools/gn/output_file.h b/tools/gn/output_file.h
new file mode 100644
index 0000000..9c5e4b2
--- /dev/null
+++ b/tools/gn/output_file.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2013 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.
+
+#ifndef TOOLS_GN_OUTPUT_FILE_H_
+#define TOOLS_GN_OUTPUT_FILE_H_
+
+#include <string>
+
+#include "tools/gn/build_settings.h"
+#include "tools/gn/source_file.h"
+
+// A simple wrapper around a string that indicates the string is a path
+// relative to the output directory.
+class OutputFile {
+ public:
+  OutputFile() : value_() {}
+  explicit OutputFile(const base::StringPiece& str)
+      : value_(str.data(), str.size()) {
+  }
+
+  std::string& value() { return value_; }
+  const std::string& value() const { return value_; }
+
+  // Converts to a SourceFile by prepending the build directory to the file.
+  SourceFile GetSourceFile(const BuildSettings* build_settings) const {
+    return SourceFile(build_settings->build_dir().value() + value_);
+  }
+
+  bool operator==(const OutputFile& other) const {
+    return value_ == other.value_;
+  }
+  bool operator!=(const OutputFile& other) const {
+    return value_ != other.value_;
+  }
+
+ private:
+  std::string value_;
+};
+
+#endif
diff --git a/tools/gn/output_stream.h b/tools/gn/output_stream.h
new file mode 100644
index 0000000..6f39c86
--- /dev/null
+++ b/tools/gn/output_stream.h
@@ -0,0 +1,42 @@
+// Copyright (c) 2013 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.
+
+#ifndef TOOLS_GN_OUTPUT_STREAM_H_
+#define TOOLS_GN_OUTPUT_STREAM_H_
+
+class OutputStream {
+ public:
+
+
+
+  OutputStream& WriteBuffer(const char* buf, size_t len);
+  OutputStream& WriteInt(int i);
+
+  // Write a literal.
+  // This template expansion prevents having to look for nulls.
+  template<size_t size> OutputStream& Write(const char (&buf)[size]) {
+    return WriteBuffer(buf, size);
+  }
+
+  // Write a literal string.
+  OutputStream& Write(const std::string& str) {
+    return WriteBuffer(str.c_str(), str.size());
+  }
+
+  // Quotes if necessary, and does necessary escaping. If more than one
+  // input is provided, the results will be concatenated together (useful
+  // for constructing paths without a temporary buffer).
+  OutputStream& WritePath(const std::string& s);
+  OutputStream& WritePath(const std::string& s0,
+                          const std::string& s1);
+  OutputStream& WritePath(const std::string& s0,
+                          const std::string& s1,
+                          const std::string& s2);
+
+  OutputStream& EndLine() {
+    return WriteBuffer("\n", 1);
+  }
+};
+
+#endif  // TOOLS_GN_OUTPUT_STREAM_H_
diff --git a/tools/gn/parse_tree.cc b/tools/gn/parse_tree.cc
new file mode 100644
index 0000000..0f84970
--- /dev/null
+++ b/tools/gn/parse_tree.cc
@@ -0,0 +1,472 @@
+// Copyright (c) 2013 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/parse_tree.h"
+
+#include <string>
+
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "tools/gn/functions.h"
+#include "tools/gn/operators.h"
+#include "tools/gn/scope.h"
+#include "tools/gn/string_utils.h"
+
+namespace {
+
+std::string IndentFor(int value) {
+  std::string ret;
+  for (int i = 0; i < value; i++)
+    ret.append(" ");
+  return ret;
+}
+
+}  // namespace
+
+ParseNode::ParseNode() {
+}
+
+ParseNode::~ParseNode() {
+}
+
+const AccessorNode* ParseNode::AsAccessor() const { return NULL; }
+const BinaryOpNode* ParseNode::AsBinaryOp() const { return NULL; }
+const BlockNode* ParseNode::AsBlock() const { return NULL; }
+const ConditionNode* ParseNode::AsConditionNode() const { return NULL; }
+const FunctionCallNode* ParseNode::AsFunctionCall() const { return NULL; }
+const IdentifierNode* ParseNode::AsIdentifier() const { return NULL; }
+const ListNode* ParseNode::AsList() const { return NULL; }
+const LiteralNode* ParseNode::AsLiteral() const { return NULL; }
+const UnaryOpNode* ParseNode::AsUnaryOp() const { return NULL; }
+
+// AccessorNode ---------------------------------------------------------------
+
+AccessorNode::AccessorNode() {
+}
+
+AccessorNode::~AccessorNode() {
+}
+
+const AccessorNode* AccessorNode::AsAccessor() const {
+  return this;
+}
+
+Value AccessorNode::Execute(Scope* scope, Err* err) const {
+  Value index_value = index_->Execute(scope, err);
+  if (err->has_error())
+    return Value();
+  if (!index_value.VerifyTypeIs(Value::INTEGER, err))
+    return Value();
+
+  const Value* base_value = scope->GetValue(base_.value(), true);
+  if (!base_value) {
+    *err = MakeErrorDescribing("Undefined identifier.");
+    return Value();
+  }
+  if (!base_value->VerifyTypeIs(Value::LIST, err))
+    return Value();
+
+  int64 index_int = index_value.int_value();
+  if (index_int < 0) {
+    *err = Err(index_->GetRange(), "Negative array subscript.",
+        "You gave me " + base::Int64ToString(index_int) + ".");
+    return Value();
+  }
+  size_t index_sizet = static_cast<size_t>(index_int);
+  if (index_sizet >= base_value->list_value().size()) {
+    *err = Err(index_->GetRange(), "Array subscript out of range.",
+        "You gave me " + base::Int64ToString(index_int) +
+        " but I was expecting something from 0 to " +
+        base::Int64ToString(
+            static_cast<int64>(base_value->list_value().size()) - 1) +
+        ", inclusive.");
+    return Value();
+  }
+
+  // Doing this assumes that there's no way in the language to do anything
+  // between the time the reference is created and the time that the reference
+  // is used. If there is, this will crash! Currently, this is just used for
+  // array accesses where this "shouldn't" happen.
+  return base_value->list_value()[index_sizet];
+}
+
+LocationRange AccessorNode::GetRange() const {
+  return LocationRange(base_.location(), index_->GetRange().end());
+}
+
+Err AccessorNode::MakeErrorDescribing(const std::string& msg,
+                                      const std::string& help) const {
+  return Err(GetRange(), msg, help);
+}
+
+void AccessorNode::Print(std::ostream& out, int indent) const {
+  out << IndentFor(indent) << "ACCESSOR\n";
+  out << IndentFor(indent + 1) << base_.value() << "\n";
+  index_->Print(out, indent + 1);
+}
+
+// BinaryOpNode ---------------------------------------------------------------
+
+BinaryOpNode::BinaryOpNode() {
+}
+
+BinaryOpNode::~BinaryOpNode() {
+}
+
+const BinaryOpNode* BinaryOpNode::AsBinaryOp() const {
+  return this;
+}
+
+Value BinaryOpNode::Execute(Scope* scope, Err* err) const {
+  return ExecuteBinaryOperator(scope, this, left_.get(), right_.get(), err);
+}
+
+LocationRange BinaryOpNode::GetRange() const {
+  return left_->GetRange().Union(right_->GetRange());
+}
+
+Err BinaryOpNode::MakeErrorDescribing(const std::string& msg,
+                                      const std::string& help) const {
+  return Err(op_, msg, help);
+}
+
+void BinaryOpNode::Print(std::ostream& out, int indent) const {
+  out << IndentFor(indent) << "BINARY(" << op_.value() << ")\n";
+  left_->Print(out, indent + 1);
+  right_->Print(out, indent + 1);
+}
+
+// BlockNode ------------------------------------------------------------------
+
+BlockNode::BlockNode(bool has_scope)
+    : has_scope_(has_scope),
+      begin_token_(NULL),
+      end_token_(NULL) {
+}
+
+BlockNode::~BlockNode() {
+  STLDeleteContainerPointers(statements_.begin(), statements_.end());
+}
+
+const BlockNode* BlockNode::AsBlock() const {
+  return this;
+}
+
+Value BlockNode::Execute(Scope* containing_scope, Err* err) const {
+  if (has_scope_) {
+    Scope our_scope(containing_scope);
+    Value ret = ExecuteBlockInScope(&our_scope, err);
+    if (err->has_error())
+      return Value();
+
+    // Check for unused vars in the scope.
+    //our_scope.CheckForUnusedVars(err);
+    return ret;
+  }
+  return ExecuteBlockInScope(containing_scope, err);
+}
+
+LocationRange BlockNode::GetRange() const {
+  if (begin_token_ && end_token_) {
+    return begin_token_->range().Union(end_token_->range());
+  }
+  return LocationRange();  // TODO(brettw) indicate the entire file somehow.
+}
+
+Err BlockNode::MakeErrorDescribing(const std::string& msg,
+                                   const std::string& help) const {
+  if (begin_token_)
+    return Err(*begin_token_, msg, help);
+  // TODO(brettw) this should have the beginning of the file in it or something.
+  return Err(Location(NULL, 1, 1), msg, help);
+}
+
+void BlockNode::Print(std::ostream& out, int indent) const {
+  out << IndentFor(indent) << "BLOCK\n";
+  for (size_t i = 0; i < statements_.size(); i++)
+    statements_[i]->Print(out, indent + 1);
+}
+
+Value BlockNode::ExecuteBlockInScope(Scope* our_scope, Err* err) const {
+  for (size_t i = 0; i < statements_.size() && !err->has_error(); i++) {
+    // Check for trying to execute things with no side effects in a block.
+    const ParseNode* cur = statements_[i];
+    if (cur->AsList() || cur->AsLiteral() || cur->AsUnaryOp() ||
+        cur->AsIdentifier()) {
+      *err = cur->MakeErrorDescribing(
+          "This statment has no effect.",
+          "Either delete it or do something with the result.");
+      return Value();
+    }
+    cur->Execute(our_scope, err);
+  }
+  return Value();
+}
+
+// ConditionNode --------------------------------------------------------------
+
+ConditionNode::ConditionNode() {
+}
+
+ConditionNode::~ConditionNode() {
+}
+
+const ConditionNode* ConditionNode::AsConditionNode() const {
+  return this;
+}
+
+Value ConditionNode::Execute(Scope* scope, Err* err) const {
+  Value condition_result = condition_->Execute(scope, err);
+  if (err->has_error())
+    return Value();
+  if (condition_result.type() == Value::NONE) {
+    *err = condition_->MakeErrorDescribing(
+        "This does not evaluate to a value.",
+        "Please give me something to work with for the if statement.");
+    err->AppendRange(if_token_.range());
+    return Value();
+  }
+
+  if (condition_result.InterpretAsInt()) {
+    if_true_->ExecuteBlockInScope(scope, err);
+  } else if (if_false_) {
+    // The else block is optional. It's either another condition (for an
+    // "else if" and we can just Execute it and the condition will handle
+    // the scoping) or it's a block indicating an "else" in which ase we
+    // need to be sure it inherits our scope.
+    const BlockNode* if_false_block = if_false_->AsBlock();
+    if (if_false_block)
+      if_false_block->ExecuteBlockInScope(scope, err);
+    else
+      if_false_->Execute(scope, err);
+  }
+
+  return Value();
+}
+
+LocationRange ConditionNode::GetRange() const {
+  if (if_false_)
+    return if_token_.range().Union(if_false_->GetRange());
+  return if_token_.range().Union(if_true_->GetRange());
+}
+
+Err ConditionNode::MakeErrorDescribing(const std::string& msg,
+                                       const std::string& help) const {
+  return Err(if_token_, msg, help);
+}
+
+void ConditionNode::Print(std::ostream& out, int indent) const {
+  out << IndentFor(indent) << "CONDITION\n";
+  condition_->Print(out, indent + 1);
+  if_true_->Print(out, indent + 1);
+  if (if_false_)
+    if_false_->Print(out, indent + 1);
+}
+
+// FunctionCallNode -----------------------------------------------------------
+
+FunctionCallNode::FunctionCallNode() {
+}
+
+FunctionCallNode::~FunctionCallNode() {
+}
+
+const FunctionCallNode* FunctionCallNode::AsFunctionCall() const {
+  return this;
+}
+
+Value FunctionCallNode::Execute(Scope* scope, Err* err) const {
+  Value args = args_->Execute(scope, err);
+  if (err->has_error())
+    return Value();
+  return ExecuteFunction(scope, this, args.list_value(), block_.get(), err);
+}
+
+LocationRange FunctionCallNode::GetRange() const {
+  if (block_)
+    return function_.range().Union(block_->GetRange());
+  return function_.range().Union(args_->GetRange());
+}
+
+Err FunctionCallNode::MakeErrorDescribing(const std::string& msg,
+                                          const std::string& help) const {
+  return Err(function_, msg, help);
+}
+
+void FunctionCallNode::Print(std::ostream& out, int indent) const {
+  out << IndentFor(indent) << "FUNCTION(" << function_.value() << ")\n";
+  args_->Print(out, indent + 1);
+  if (block_)
+    block_->Print(out, indent + 1);
+}
+
+// IdentifierNode --------------------------------------------------------------
+
+IdentifierNode::IdentifierNode() {
+}
+
+IdentifierNode::IdentifierNode(const Token& token) : value_(token) {
+}
+
+IdentifierNode::~IdentifierNode() {
+}
+
+const IdentifierNode* IdentifierNode::AsIdentifier() const {
+  return this;
+}
+
+Value IdentifierNode::Execute(Scope* scope, Err* err) const {
+  const Value* result = scope->GetValue(value_.value(), true);
+  if (!result) {
+    *err = MakeErrorDescribing("Undefined identifier");
+    return Value();
+  }
+  return *result;
+}
+
+LocationRange IdentifierNode::GetRange() const {
+  return value_.range();
+}
+
+Err IdentifierNode::MakeErrorDescribing(const std::string& msg,
+                                        const std::string& help) const {
+  return Err(value_, msg, help);
+}
+
+void IdentifierNode::Print(std::ostream& out, int indent) const {
+  out << IndentFor(indent) << "IDENTIFIER(" << value_.value() << ")\n";
+}
+
+// ListNode -------------------------------------------------------------------
+
+ListNode::ListNode() {
+}
+
+ListNode::~ListNode() {
+  STLDeleteContainerPointers(contents_.begin(), contents_.end());
+}
+
+const ListNode* ListNode::AsList() const {
+  return this;
+}
+
+Value ListNode::Execute(Scope* scope, Err* err) const {
+  Value result_value(this, Value::LIST);
+  std::vector<Value>& results = result_value.list_value();
+  results.resize(contents_.size());
+
+  for (size_t i = 0; i < contents_.size(); i++) {
+    const ParseNode* cur = contents_[i];
+    results[i] = cur->Execute(scope, err);
+    if (err->has_error())
+      return Value();
+    if (results[i].type() == Value::NONE) {
+      *err = cur->MakeErrorDescribing(
+          "This does not evaluate to a value.",
+          "I can't do something with nothing.");
+      return Value();
+    }
+  }
+  return result_value;
+}
+
+LocationRange ListNode::GetRange() const {
+  return LocationRange(begin_token_.location(), end_token_.location());
+}
+
+Err ListNode::MakeErrorDescribing(const std::string& msg,
+                                  const std::string& help) const {
+  return Err(begin_token_, msg, help);
+}
+
+void ListNode::Print(std::ostream& out, int indent) const {
+  out << IndentFor(indent) << "LIST\n";
+  for (size_t i = 0; i < contents_.size(); i++)
+    contents_[i]->Print(out, indent + 1);
+}
+
+// LiteralNode -----------------------------------------------------------------
+
+LiteralNode::LiteralNode() {
+}
+
+LiteralNode::LiteralNode(const Token& token) : value_(token) {
+}
+
+LiteralNode::~LiteralNode() {
+}
+
+const LiteralNode* LiteralNode::AsLiteral() const {
+  return this;
+}
+
+Value LiteralNode::Execute(Scope* scope, Err* err) const {
+  switch (value_.type()) {
+    case Token::INTEGER: {
+      int64 result_int;
+      if (!base::StringToInt64(value_.value(), &result_int)) {
+        *err = MakeErrorDescribing("This does not look like an integer");
+        return Value();
+      }
+      return Value(this, result_int);
+    }
+    case Token::STRING: {
+      // TODO(brettw) Unescaping probably needs to be moved & improved.
+      // The input value includes the quotes around the string, strip those
+      // off and unescape.
+      Value v(this, Value::STRING);
+      ExpandStringLiteral(scope, value_, &v, err);
+      return v;
+    }
+    default:
+      NOTREACHED();
+      return Value();
+  }
+}
+
+LocationRange LiteralNode::GetRange() const {
+  return value_.range();
+}
+
+Err LiteralNode::MakeErrorDescribing(const std::string& msg,
+                                     const std::string& help) const {
+  return Err(value_, msg, help);
+}
+
+void LiteralNode::Print(std::ostream& out, int indent) const {
+  out << IndentFor(indent) << "LITERAL(" << value_.value() << ")\n";
+}
+
+// UnaryOpNode ----------------------------------------------------------------
+
+UnaryOpNode::UnaryOpNode() {
+}
+
+UnaryOpNode::~UnaryOpNode() {
+}
+
+const UnaryOpNode* UnaryOpNode::AsUnaryOp() const {
+  return this;
+}
+
+Value UnaryOpNode::Execute(Scope* scope, Err* err) const {
+  Value operand_value = operand_->Execute(scope, err);
+  if (err->has_error())
+    return Value();
+  return ExecuteUnaryOperator(scope, this, operand_value, err);
+}
+
+LocationRange UnaryOpNode::GetRange() const {
+  return op_.range().Union(operand_->GetRange());
+}
+
+Err UnaryOpNode::MakeErrorDescribing(const std::string& msg,
+                                     const std::string& help) const {
+  return Err(op_, msg, help);
+}
+
+void UnaryOpNode::Print(std::ostream& out, int indent) const {
+  out << IndentFor(indent) << "UNARY(" << op_.value() << ")\n";
+  operand_->Print(out, indent + 1);
+}
diff --git a/tools/gn/parse_tree.h b/tools/gn/parse_tree.h
new file mode 100644
index 0000000..09646e5
--- /dev/null
+++ b/tools/gn/parse_tree.h
@@ -0,0 +1,366 @@
+// Copyright (c) 2013 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.
+
+#ifndef TOOLS_GN_PARSE_TREE_H_
+#define TOOLS_GN_PARSE_TREE_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "tools/gn/err.h"
+#include "tools/gn/token.h"
+#include "tools/gn/value.h"
+
+class AccessorNode;
+class BinaryOpNode;
+class BlockNode;
+class ConditionNode;
+class FunctionCallNode;
+class IdentifierNode;
+class ListNode;
+class LiteralNode;
+class Scope;
+class UnaryOpNode;
+
+// ParseNode -------------------------------------------------------------------
+
+// A node in the AST.
+class ParseNode {
+ public:
+  ParseNode();
+  virtual ~ParseNode();
+
+  virtual const AccessorNode* AsAccessor() const;
+  virtual const BinaryOpNode* AsBinaryOp() const;
+  virtual const BlockNode* AsBlock() const;
+  virtual const ConditionNode* AsConditionNode() const;
+  virtual const FunctionCallNode* AsFunctionCall() const;
+  virtual const IdentifierNode* AsIdentifier() const;
+  virtual const ListNode* AsList() const;
+  virtual const LiteralNode* AsLiteral() const;
+  virtual const UnaryOpNode* AsUnaryOp() const;
+
+  virtual Value Execute(Scope* scope, Err* err) const = 0;
+
+  virtual LocationRange GetRange() const = 0;
+
+  // Returns an error with the given messages and the range set to something
+  // that indicates this node.
+  virtual Err MakeErrorDescribing(
+      const std::string& msg,
+      const std::string& help = std::string()) const = 0;
+
+  // Prints a representation of this node to the given string, indenting
+  // by the given number of spaces.
+  virtual void Print(std::ostream& out, int indent) const = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ParseNode);
+};
+
+// AccessorNode ----------------------------------------------------------------
+
+// Access an array element.
+//
+// If we need to add support for member variables like "variable.len" I was
+// thinking this would also handle that case.
+class AccessorNode : public ParseNode {
+ public:
+  AccessorNode();
+  virtual ~AccessorNode();
+
+  virtual const AccessorNode* AsAccessor() const OVERRIDE;
+  virtual Value Execute(Scope* scope, Err* err) const OVERRIDE;
+  virtual LocationRange GetRange() const OVERRIDE;
+  virtual Err MakeErrorDescribing(
+      const std::string& msg,
+      const std::string& help = std::string()) const OVERRIDE;
+  virtual void Print(std::ostream& out, int indent) const OVERRIDE;
+
+  // Base is the thing on the left of the [], currently always required to be
+  // an identifier token.
+  const Token& base() const { return base_; }
+  void set_base(const Token& b) { base_ = b; }
+
+  // Index is the expression inside the [].
+  const ParseNode* index() const { return index_.get(); }
+  void set_index(scoped_ptr<ParseNode> i) { index_ = i.Pass(); }
+
+ private:
+  Token base_;
+  scoped_ptr<ParseNode> index_;
+
+  DISALLOW_COPY_AND_ASSIGN(AccessorNode);
+};
+
+// BinaryOpNode ----------------------------------------------------------------
+
+class BinaryOpNode : public ParseNode {
+ public:
+  BinaryOpNode();
+  virtual ~BinaryOpNode();
+
+  virtual const BinaryOpNode* AsBinaryOp() const OVERRIDE;
+  virtual Value Execute(Scope* scope, Err* err) const OVERRIDE;
+  virtual LocationRange GetRange() const OVERRIDE;
+  virtual Err MakeErrorDescribing(
+      const std::string& msg,
+      const std::string& help = std::string()) const OVERRIDE;
+  virtual void Print(std::ostream& out, int indent) const OVERRIDE;
+
+  const Token& op() const { return op_; }
+  void set_op(const Token& t) { op_ = t; }
+
+  const ParseNode* left() const { return left_.get(); }
+  void set_left(scoped_ptr<ParseNode> left) {
+    left_ = left.Pass();
+  }
+
+  const ParseNode* right() const { return right_.get(); }
+  void set_right(scoped_ptr<ParseNode> right) {
+    right_ = right.Pass();
+  }
+
+ private:
+  scoped_ptr<ParseNode> left_;
+  Token op_;
+  scoped_ptr<ParseNode> right_;
+
+  DISALLOW_COPY_AND_ASSIGN(BinaryOpNode);
+};
+
+// BlockNode -------------------------------------------------------------------
+
+class BlockNode : public ParseNode {
+ public:
+  // Set has_scope if this block introduces a nested scope.
+  BlockNode(bool has_scope);
+  virtual ~BlockNode();
+
+  virtual const BlockNode* AsBlock() const OVERRIDE;
+  virtual Value Execute(Scope* scope, Err* err) const OVERRIDE;
+  virtual LocationRange GetRange() const OVERRIDE;
+  virtual Err MakeErrorDescribing(
+      const std::string& msg,
+      const std::string& help = std::string()) const OVERRIDE;
+  virtual void Print(std::ostream& out, int indent) const OVERRIDE;
+
+  void set_begin_token(const Token* t) { begin_token_ = t; }
+  void set_end_token(const Token* t) { end_token_ = t; }
+
+  const std::vector<ParseNode*>& statements() const { return statements_; }
+  void append_statement(scoped_ptr<ParseNode> s) {
+    statements_.push_back(s.release());
+  }
+
+  // Doesn't create a nested scope.
+  Value ExecuteBlockInScope(Scope* our_scope, Err* err) const;
+
+ private:
+  bool has_scope_;
+
+  // Tokens corresponding to { and }, if any (may be NULL).
+  const Token* begin_token_;
+  const Token* end_token_;
+
+  // Owning pointers, use unique_ptr when we can use C++11.
+  std::vector<ParseNode*> statements_;
+
+  DISALLOW_COPY_AND_ASSIGN(BlockNode);
+};
+
+// ConditionNode ---------------------------------------------------------------
+
+class ConditionNode : public ParseNode {
+ public:
+  ConditionNode();
+  virtual ~ConditionNode();
+
+  virtual const ConditionNode* AsConditionNode() const OVERRIDE;
+  virtual Value Execute(Scope* scope, Err* err) const OVERRIDE;
+  virtual LocationRange GetRange() const OVERRIDE;
+  virtual Err MakeErrorDescribing(
+      const std::string& msg,
+      const std::string& help = std::string()) const OVERRIDE;
+  virtual void Print(std::ostream& out, int indent) const OVERRIDE;
+
+  void set_if_token(const Token& token) { if_token_ = token; }
+
+  const ParseNode* condition() const { return condition_.get(); }
+  void set_condition(scoped_ptr<ParseNode> c) {
+    condition_ = c.Pass();
+  }
+
+  const BlockNode* if_true() const { return if_true_.get(); }
+  void set_if_true(scoped_ptr<BlockNode> t) {
+    if_true_ = t.Pass();
+  }
+
+  // This is either empty, a block (for the else clause), or another
+  // condition.
+  const ParseNode* if_false() const { return if_false_.get(); }
+  void set_if_false(scoped_ptr<ParseNode> f) {
+    if_false_ = f.Pass();
+  }
+
+ private:
+  // Token corresponding to the "if" string.
+  Token if_token_;
+
+  scoped_ptr<ParseNode> condition_;  // Always non-null.
+  scoped_ptr<BlockNode> if_true_;  // Always non-null.
+  scoped_ptr<ParseNode> if_false_;  // May be null.
+
+  DISALLOW_COPY_AND_ASSIGN(ConditionNode);
+};
+
+// FunctionCallNode ------------------------------------------------------------
+
+class FunctionCallNode : public ParseNode {
+ public:
+  FunctionCallNode();
+  virtual ~FunctionCallNode();
+
+  virtual const FunctionCallNode* AsFunctionCall() const OVERRIDE;
+  virtual Value Execute(Scope* scope, Err* err) const OVERRIDE;
+  virtual LocationRange GetRange() const OVERRIDE;
+  virtual Err MakeErrorDescribing(
+      const std::string& msg,
+      const std::string& help = std::string()) const OVERRIDE;
+  virtual void Print(std::ostream& out, int indent) const OVERRIDE;
+
+  const Token& function() const { return function_; }
+  void set_function(Token t) { function_ = t; }
+
+  const ListNode* args() const { return args_.get(); }
+  void set_args(scoped_ptr<ListNode> a) { args_ = a.Pass(); }
+
+  const BlockNode* block() const { return block_.get(); }
+  void set_block(scoped_ptr<BlockNode> b) { block_ = b.Pass(); }
+
+ private:
+  Token function_;
+  scoped_ptr<ListNode> args_;
+  scoped_ptr<BlockNode> block_;  // May be null.
+
+  DISALLOW_COPY_AND_ASSIGN(FunctionCallNode);
+};
+
+// IdentifierNode --------------------------------------------------------------
+
+class IdentifierNode : public ParseNode {
+ public:
+  IdentifierNode();
+  IdentifierNode(const Token& token);
+  virtual ~IdentifierNode();
+
+  virtual const IdentifierNode* AsIdentifier() const OVERRIDE;
+  virtual Value Execute(Scope* scope, Err* err) const OVERRIDE;
+  virtual LocationRange GetRange() const OVERRIDE;
+  virtual Err MakeErrorDescribing(
+      const std::string& msg,
+      const std::string& help = std::string()) const OVERRIDE;
+  virtual void Print(std::ostream& out, int indent) const OVERRIDE;
+
+  const Token& value() const { return value_; }
+  void set_value(const Token& t) { value_ = t; }
+
+ private:
+  Token value_;
+
+  DISALLOW_COPY_AND_ASSIGN(IdentifierNode);
+};
+
+// ListNode --------------------------------------------------------------------
+
+class ListNode : public ParseNode {
+ public:
+  ListNode();
+  virtual ~ListNode();
+
+  virtual const ListNode* AsList() const OVERRIDE;
+  virtual Value Execute(Scope* scope, Err* err) const OVERRIDE;
+  virtual LocationRange GetRange() const OVERRIDE;
+  virtual Err MakeErrorDescribing(
+      const std::string& msg,
+      const std::string& help = std::string()) const OVERRIDE;
+  virtual void Print(std::ostream& out, int indent) const OVERRIDE;
+
+  void set_begin_token(const Token& t) { begin_token_ = t; }
+  void set_end_token(const Token& t) { end_token_ = t; }
+
+  void append_item(scoped_ptr<ParseNode> s) {
+    contents_.push_back(s.release());
+  }
+  const std::vector<ParseNode*>& contents() const { return contents_; }
+
+ private:
+  // Tokens corresponding to the [ and ].
+  Token begin_token_;
+  Token end_token_;
+
+  // Owning pointers, use unique_ptr when we can use C++11.
+  std::vector<ParseNode*> contents_;
+
+  DISALLOW_COPY_AND_ASSIGN(ListNode);
+};
+
+// LiteralNode -----------------------------------------------------------------
+
+class LiteralNode : public ParseNode {
+ public:
+  LiteralNode();
+  LiteralNode(const Token& token);
+  virtual ~LiteralNode();
+
+  virtual const LiteralNode* AsLiteral() const OVERRIDE;
+  virtual Value Execute(Scope* scope, Err* err) const OVERRIDE;
+  virtual LocationRange GetRange() const OVERRIDE;
+  virtual Err MakeErrorDescribing(
+      const std::string& msg,
+      const std::string& help = std::string()) const OVERRIDE;
+  virtual void Print(std::ostream& out, int indent) const OVERRIDE;
+
+  const Token& value() const { return value_; }
+  void set_value(const Token& t) { value_ = t; }
+
+ private:
+  Token value_;
+
+  DISALLOW_COPY_AND_ASSIGN(LiteralNode);
+};
+
+// UnaryOpNode -----------------------------------------------------------------
+
+class UnaryOpNode : public ParseNode {
+ public:
+  UnaryOpNode();
+  virtual ~UnaryOpNode();
+
+  virtual const UnaryOpNode* AsUnaryOp() const OVERRIDE;
+  virtual Value Execute(Scope* scope, Err* err) const OVERRIDE;
+  virtual LocationRange GetRange() const OVERRIDE;
+  virtual Err MakeErrorDescribing(
+      const std::string& msg,
+      const std::string& help = std::string()) const OVERRIDE;
+  virtual void Print(std::ostream& out, int indent) const OVERRIDE;
+
+  const Token& op() const { return op_; }
+  void set_op(const Token& t) { op_ = t; }
+
+  const ParseNode* operand() const { return operand_.get(); }
+  void set_operand(scoped_ptr<ParseNode> operand) {
+    operand_ = operand.Pass();
+  }
+
+ private:
+  Token op_;
+  scoped_ptr<ParseNode> operand_;
+
+  DISALLOW_COPY_AND_ASSIGN(UnaryOpNode);
+};
+
+#endif  // TOOLS_GN_PARSE_TREE_H_
diff --git a/tools/gn/parser.cc b/tools/gn/parser.cc
new file mode 100644
index 0000000..385aa34
--- /dev/null
+++ b/tools/gn/parser.cc
@@ -0,0 +1,470 @@
+// Copyright (c) 2013 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/parser.h"
+
+#include "base/logging.h"
+#include "tools/gn/functions.h"
+#include "tools/gn/operators.h"
+#include "tools/gn/token.h"
+
+namespace {
+
+// Returns true if the two tokens are on the same line. We assume they're in
+// the same file.
+bool IsSameLine(const Token& a, const Token& b) {
+  DCHECK(a.location().file() == b.location().file());
+  return a.location().line_number() == b.location().line_number();
+}
+
+}  // namespace
+
+Parser::Parser(const std::vector<Token>& tokens, Err* err)
+    : tokens_(tokens),
+      err_(err),
+      cur_(0) {
+}
+
+Parser::~Parser() {
+}
+
+// static
+scoped_ptr<ParseNode> Parser::Parse(const std::vector<Token>& tokens,
+                                    Err* err) {
+  Parser p(tokens, err);
+  return p.ParseBlock(false).PassAs<ParseNode>();
+}
+
+// static
+scoped_ptr<ParseNode> Parser::ParseExpression(const std::vector<Token>& tokens,
+                                              Err* err) {
+  Parser p(tokens, err);
+  return p.ParseExpression().Pass();
+}
+
+bool Parser::IsToken(Token::Type type, char* str) const {
+  if (at_end())
+    return false;
+  return cur_token().type() == type || cur_token().value() == str;
+}
+
+scoped_ptr<AccessorNode> Parser::ParseAccessor() {
+  scoped_ptr<AccessorNode> accessor(new AccessorNode);
+
+  DCHECK(cur_token().type() == Token::IDENTIFIER);
+  accessor->set_base(cur_token());
+  cur_++;  // Skip identifier.
+  cur_++;  // Skip "[" (we know this exists because the existance of this
+           // token is how the caller knows it's an accessor.
+
+  if (at_end()) {
+    *err_ = MakeEOFError("Got EOF when looking for list index.");
+    return scoped_ptr<AccessorNode>();
+  }
+
+  // Get the expression.
+  scoped_ptr<ParseNode> expr = ParseExpression().Pass();
+  if (has_error())
+    return scoped_ptr<AccessorNode>();
+  if (at_end()) {
+    *err_ = MakeEOFError("Got EOF when looking for list accessor ]");
+    return scoped_ptr<AccessorNode>();
+  }
+  accessor->set_index(expr.Pass());
+
+  // Skip over "]"
+  if (!cur_token().IsScoperEqualTo("]")) {
+    *err_ = Err(cur_token(), "Expecting ]",
+               "You started a list access but didn't terminate it, and instead "
+               "I fould this\nstupid thing.");
+    return scoped_ptr<AccessorNode>();
+  }
+  cur_++;
+
+  return accessor.Pass();
+}
+
+// Blocks at the file scope don't need {} so we have the option to ignore
+// them. When need_braces is set, we'll expect a begin an end brace.
+//
+// block := "{" block_contents "}"
+// block_contents := (expression | conditional | block)*
+scoped_ptr<BlockNode> Parser::ParseBlock(bool need_braces) {
+  scoped_ptr<BlockNode> block(new BlockNode(true));
+
+  // Eat initial { if necessary.
+  const Token* opening_curly_brace;
+  if (need_braces) {
+    if (at_end()) {
+      *err_ = MakeEOFError("Got EOF when looking for { for block.",
+                           "It should have been after here.");
+      return scoped_ptr<BlockNode>();
+    } else if(!IsScopeBeginScoper(cur_token())) {
+      *err_ = Err(cur_token(), "Expecting { instead of this thing.",
+                  "THOU SHALT USE CURLY BRACES FOR ALL BLOCKS.");
+      return scoped_ptr<BlockNode>();
+    }
+    opening_curly_brace = &cur_token();
+    block->set_begin_token(opening_curly_brace);
+    cur_++;
+  }
+
+  // Loop until EOF or end brace found.
+  while (!at_end() && !IsScopeEndScoper(cur_token())) {
+    if (cur_token().IsIdentifierEqualTo("if")) {
+      // Conditional.
+      block->append_statement(ParseCondition().PassAs<ParseNode>());
+    } else if (IsScopeBeginScoper(cur_token())) {
+      // Nested block.
+      block->append_statement(ParseBlock(true).PassAs<ParseNode>());
+    } else {
+      // Everything else is an expression.
+      block->append_statement(ParseExpression().PassAs<ParseNode>());
+    }
+    if (has_error())
+      return scoped_ptr<BlockNode>();
+  }
+
+  // Eat the ending "}" if necessary.
+  if (need_braces) {
+    if (at_end() || !IsScopeEndScoper(cur_token())) {
+      *err_ = Err(*opening_curly_brace, "Expecting }",
+                  "I ran headlong into the end of the file looking for the "
+                  "closing brace\ncorresponding to this one.");
+      return scoped_ptr<BlockNode>();
+    }
+    block->set_end_token(&cur_token());
+    cur_++;  // Skip past "}".
+  }
+
+  return block.Pass();
+}
+
+// conditional := "if (" expression ")" block [else_conditional]
+// else_conditional := ("else" block) | ("else" conditional)
+scoped_ptr<ConditionNode> Parser::ParseCondition() {
+  scoped_ptr<ConditionNode> cond(new ConditionNode);
+
+  // Skip past "if".
+  const Token& if_token = cur_token();
+  cond->set_if_token(if_token);
+  DCHECK(if_token.IsIdentifierEqualTo("if"));
+  cur_++;
+
+  if (at_end() || !IsFunctionCallArgBeginScoper(cur_token())) {
+    *err_ = Err(if_token, "Expecting \"(\" after \"if\"",
+                "Did you think this was Python or something?");
+    return scoped_ptr<ConditionNode>();
+  }
+
+  // Skip over (.
+  const Token& open_paren_token = cur_token();
+  cur_++;
+  if (at_end()) {
+    *err_ = Err(if_token, "Unexpected EOF inside if condition");
+    return scoped_ptr<ConditionNode>();
+  }
+
+  // Condition inside ().
+  cond->set_condition(ParseExpression().Pass());
+  if (has_error())
+    return scoped_ptr<ConditionNode>();
+
+  if (at_end() || !IsFunctionCallArgEndScoper(cur_token())) {
+    *err_ = Err(open_paren_token, "Expecting \")\" for \"if\" condition",
+                "You didn't finish the thought you started here.");
+    return scoped_ptr<ConditionNode>();
+  }
+  cur_++;  // Skip over )
+
+  // Contents of {}.
+  cond->set_if_true(ParseBlock(true).Pass());
+  if (has_error())
+    return scoped_ptr<ConditionNode>();
+
+  // Optional "else" at the end.
+  if (!at_end() && cur_token().IsIdentifierEqualTo("else")) {
+    cur_++;
+
+    // The else may be followed by an if or a block.
+    if (at_end()) {
+      *err_ = MakeEOFError("Ran into end of file after \"else\".",
+                           "else, WHAT?!?!?");
+      return scoped_ptr<ConditionNode>();
+    }
+    if (cur_token().IsIdentifierEqualTo("if")) {
+      // "else if() {"
+      cond->set_if_false(ParseCondition().PassAs<ParseNode>());
+    } else if (IsScopeBeginScoper(cur_token())) {
+      // "else {"
+      cond->set_if_false(ParseBlock(true).PassAs<ParseNode>());
+    } else {
+      // else <anything else>
+      *err_ = Err(cur_token(), "Expected \"if\" or \"{\" after \"else\".",
+                  "This is neither of those things.");
+      return scoped_ptr<ConditionNode>();
+    }
+  }
+
+  if (has_error())
+    return scoped_ptr<ConditionNode>();
+  return cond.Pass();
+}
+
+// expression := paren_expression | accessor | identifier | literal |
+//               funccall | unary_expression | binary_expression
+//
+// accessor := identifier <non-newline-whitespace>* "[" expression "]"
+//
+// The "non-newline-whitespace is used to differentiate between this case:
+//   a[1]
+// and this one:
+//   a
+//   [1]
+// The second one is kind of stupid (since it does nothing with the values)
+// but is still legal.
+scoped_ptr<ParseNode> Parser::ParseExpression() {
+  scoped_ptr<ParseNode> expr = ParseExpressionExceptBinaryOperators();
+  if (has_error())
+    return scoped_ptr<ParseNode>();
+
+  // That may have hit EOF, in which case we can't have any binary operators.
+  if (at_end())
+    return expr.Pass();
+
+  // TODO(brettw) handle operator precidence!
+  // Gobble up all subsequent expressions as long as there are binary
+  // operators.
+
+  if (IsBinaryOperator(cur_token())) {
+    scoped_ptr<BinaryOpNode> binary_op(new BinaryOpNode);
+    binary_op->set_left(expr.Pass());
+    const Token& operator_token = cur_token();
+    binary_op->set_op(operator_token);
+    cur_++;
+    if (at_end()) {
+      *err_ = Err(operator_token, "Unexpected EOF in expression.",
+                  "I was looking for the right-hand-side of this operator.");
+      return scoped_ptr<ParseNode>();
+    }
+    binary_op->set_right(ParseExpression().Pass());
+    if (has_error())
+      return scoped_ptr<ParseNode>();
+    return binary_op.PassAs<ParseNode>();
+  }
+
+  return expr.Pass();
+}
+
+
+// This internal one does not handle binary operators, since it requires
+// looking at the "next" thing. The regular ParseExpression above handles it.
+scoped_ptr<ParseNode> Parser::ParseExpressionExceptBinaryOperators() {
+  if (at_end())
+    return scoped_ptr<ParseNode>();
+
+  const Token& token = cur_token();
+
+  // Unary expression.
+  if (IsUnaryOperator(token))
+    return ParseUnaryOp().PassAs<ParseNode>();
+
+  // Parenthesized expressions.
+  if (token.IsScoperEqualTo("("))
+    return ParseParenExpression();
+
+  // Function calls.
+  if (token.type() == Token::IDENTIFIER) {
+    if (has_next_token() && IsFunctionCallArgBeginScoper(next_token()))
+      return ParseFunctionCall().PassAs<ParseNode>();
+  }
+
+  // Lists.
+  if (token.IsScoperEqualTo("[")) {
+    return ParseList(Token(Location(), Token::SCOPER, "["),
+                     Token(Location(), Token::SCOPER, "]")).PassAs<ParseNode>();
+  }
+
+  // Literals.
+  if (token.type() == Token::STRING || token.type() == Token::INTEGER) {
+    cur_++;
+    return scoped_ptr<ParseNode>(new LiteralNode(token));
+  }
+
+  // Accessors.
+  if (token.type() == Token::IDENTIFIER &&
+      has_next_token() && next_token().IsScoperEqualTo("[") &&
+      IsSameLine(token, next_token())) {
+    return ParseAccessor().PassAs<ParseNode>();
+  }
+
+  // Identifiers.
+  if (token.type() == Token::IDENTIFIER) {
+    cur_++;
+    return scoped_ptr<ParseNode>(new IdentifierNode(token));
+  }
+
+  // Handle errors.
+  if (token.type() == Token::SEPARATOR) {
+    *err_ = Err(token, "Unexpected comma.",
+                "You can't put a comma here, it must be in list separating "
+                "complete\nthoughts.");
+  } else if (IsScopeBeginScoper(token)) {
+    *err_ = Err(token, "Unexpected token.",
+                "You can't put a \"{\" scope here, it must be in a block.");
+  } else {
+    *err_ = Err(token, "Unexpected token.",
+                "I was really hoping for something else here and you let me down.");
+  }
+  return scoped_ptr<ParseNode>();
+}
+
+// function_call := identifier "(" list_contents ")"
+//                  [<non-newline-whitespace>* block]
+scoped_ptr<FunctionCallNode> Parser::ParseFunctionCall() {
+  scoped_ptr<FunctionCallNode> func(new FunctionCallNode);
+
+  const Token& function_token = cur_token();
+  func->set_function(function_token);
+
+  // This function should only get called when we know we have a function,
+  // which only happens when there is a paren following the name. Skip past it.
+  DCHECK(has_next_token());
+  cur_++;  // Skip past function name to (.
+  const Token& open_paren_token = cur_token();
+  DCHECK(IsFunctionCallArgBeginScoper(open_paren_token));
+
+  if (at_end()) {
+    *err_ = Err(open_paren_token, "Unexpected EOF for function call.",
+                "You didn't finish the thought you started here.");
+    return scoped_ptr<FunctionCallNode>();
+  }
+
+  // Arguments.
+  func->set_args(ParseList(Token(Location(), Token::SCOPER, "("),
+                           Token(Location(), Token::SCOPER, ")")));
+  if (has_error())
+    return scoped_ptr<FunctionCallNode>();
+
+  // Optional {} after function call for certain functions. The "{" must be on
+  // the same line as the ")" to disambiguate the case of a function followed
+  // by a random block just used for scoping purposes.
+  if (!at_end() && IsScopeBeginScoper(cur_token())) {
+    const Token& args_end_token = tokens_[cur_ - 1];
+    DCHECK(args_end_token.IsScoperEqualTo(")"));
+    if (IsSameLine(args_end_token, cur_token()))
+      func->set_block(ParseBlock(true).Pass());
+  }
+
+  if (has_error())
+    return scoped_ptr<FunctionCallNode>();
+  return func.Pass();
+}
+
+// list := "[" expression* "]"
+// list_contents := [(expression ",")* expression [","]]
+//
+// The list_contents is also used in function calls surrounded by parens, so
+// this function takes the tokens that are expected to surround the list.
+scoped_ptr<ListNode> Parser::ParseList(const Token& expected_begin,
+                                       const Token& expected_end) {
+  scoped_ptr<ListNode> list(new ListNode);
+
+  const Token& open_bracket_token = cur_token();
+  list->set_begin_token(open_bracket_token);
+  cur_++;  // Skip "[" or "(".
+
+  bool need_separator = false;
+  while(true) {
+    if (at_end()) {
+      *err_ = Err(open_bracket_token, "EOF found when parsing list.",
+                  "I expected a \"" + expected_end.value().as_string() +
+                  "\" corresponding to this one.");
+      return scoped_ptr<ListNode>();
+    }
+    if (cur_token().type() == expected_end.type() &&
+        cur_token().value() == expected_end.value()) {
+      list->set_end_token(cur_token());
+      cur_++;
+      break;
+    }
+
+    if (need_separator) {
+      DCHECK(!list->contents().empty());
+      LocationRange prev_item_range =
+          list->contents().at(list->contents().size() - 1)->GetRange();
+      *err_ = Err(prev_item_range.end(),
+                  "Need comma separating items in list.",
+                  "You probably need a comma after this thingy.");
+      err_->AppendRange(prev_item_range);
+      return scoped_ptr<ListNode>();
+    }
+    scoped_ptr<ParseNode> expr = ParseExpression().Pass();
+    if (has_error())
+      return scoped_ptr<ListNode>();
+    list->append_item(expr.Pass());
+
+    need_separator = true;
+    if (!at_end()) {
+      // Skip over the separator, marking that we found it.
+      if (cur_token().type() == Token::SEPARATOR) {
+        cur_++;
+        need_separator = false;
+      }
+    }
+  }
+  return list.Pass();
+}
+
+// paren_expression := "(" expression ")"
+scoped_ptr<ParseNode> Parser::ParseParenExpression() {
+  const Token& open_paren_token = cur_token();
+  cur_++;  // Skip over (
+
+  scoped_ptr<ParseNode> ret = ParseExpression();
+  if (has_error())
+    return scoped_ptr<ParseNode>();
+
+  if (at_end()) {
+    *err_ = Err(open_paren_token, "EOF found when parsing expression.",
+                "I was looking for a \")\" corresponding to this one.");
+    return scoped_ptr<ParseNode>();
+  }
+  if (!cur_token().IsScoperEqualTo(")")) {
+    *err_ = Err(open_paren_token, "Expected \")\" for expression",
+                "I was looking for a \")\" corresponding to this one.");
+    return scoped_ptr<ParseNode>();
+  }
+  cur_++;  // Skip over )
+  return ret.Pass();
+}
+
+// unary_expression := "!" expression
+scoped_ptr<UnaryOpNode> Parser::ParseUnaryOp() {
+  scoped_ptr<UnaryOpNode> unary(new UnaryOpNode);
+
+  DCHECK(!at_end() && IsUnaryOperator(cur_token()));
+  const Token& op_token = cur_token();
+  unary->set_op(op_token);
+  cur_++;
+
+  if (at_end()) {
+    *err_ = Err(op_token, "Expected expression.",
+                "This operator needs something to operate on.");
+    return scoped_ptr<UnaryOpNode>();
+  }
+  unary->set_operand(ParseExpression().Pass());
+  if (has_error())
+    return scoped_ptr<UnaryOpNode>();
+  return unary.Pass();
+}
+
+Err Parser::MakeEOFError(const std::string& message,
+                         const std::string& help) const {
+  if (tokens_.empty())
+    return Err(Location(NULL, 1, 1), message, help);
+
+  const Token& last = tokens_[tokens_.size() - 1];
+  return Err(last, message, help);
+}
diff --git a/tools/gn/parser.h b/tools/gn/parser.h
new file mode 100644
index 0000000..252b801
--- /dev/null
+++ b/tools/gn/parser.h
@@ -0,0 +1,81 @@
+// Copyright (c) 2013 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.
+
+#ifndef TOOLS_GN_PARSER_H_
+#define TOOLS_GN_PARSER_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "tools/gn/err.h"
+#include "tools/gn/parse_tree.h"
+
+// Parses a series of tokens. The resulting AST will refer to the tokens passed
+// to the input, so the tokens an the file data they refer to must outlive your
+// use of the ParseNode.
+class Parser {
+ public:
+  // Will return a null pointer and set the err on error.
+  static scoped_ptr<ParseNode> Parse(const std::vector<Token>& tokens,
+                                     Err* err);
+
+  // Alternative to parsing that assumes the input is an expression.
+  static scoped_ptr<ParseNode> ParseExpression(const std::vector<Token>& tokens,
+                                               Err* err);
+
+ private:
+  // Vector must be valid for lifetime of call.
+  Parser(const std::vector<Token>& tokens, Err* err);
+  ~Parser();
+
+  scoped_ptr<AccessorNode> ParseAccessor();
+  scoped_ptr<BlockNode> ParseBlock(bool need_braces);
+  scoped_ptr<ConditionNode> ParseCondition();
+  scoped_ptr<ParseNode> ParseExpression();
+  scoped_ptr<ParseNode> ParseExpressionExceptBinaryOperators();
+  scoped_ptr<FunctionCallNode> ParseFunctionCall();
+  scoped_ptr<ListNode> ParseList(const Token& expected_begin,
+                                 const Token& expected_end);
+  scoped_ptr<ParseNode> ParseParenExpression();
+  scoped_ptr<UnaryOpNode> ParseUnaryOp();
+
+  bool IsToken(Token::Type type, char* str) const;
+
+  // Gets an error corresponding to the last token. When we hit an EOF
+  // usually we've already gone beyond the end (or maybe there are no tokens)
+  // so there is some tricky logic to report this.
+  Err MakeEOFError(const std::string& message,
+                   const std::string& help = std::string()) const;
+
+  const Token& cur_token() const { return tokens_[cur_]; }
+
+  bool done() const { return at_end() || has_error(); }
+  bool at_end() const { return cur_ >= tokens_.size(); }
+  bool has_error() const { return err_->has_error(); }
+
+  const Token& next_token() const { return tokens_[cur_ + 1]; }
+  bool has_next_token() const { return cur_ + 1 < tokens_.size(); }
+
+  const std::vector<Token>& tokens_;
+
+  Err* err_;
+
+  // Current index into the tokens.
+  size_t cur_;
+
+  FRIEND_TEST_ALL_PREFIXES(Parser, BinaryOp);
+  FRIEND_TEST_ALL_PREFIXES(Parser, Block);
+  FRIEND_TEST_ALL_PREFIXES(Parser, Condition);
+  FRIEND_TEST_ALL_PREFIXES(Parser, Expression);
+  FRIEND_TEST_ALL_PREFIXES(Parser, FunctionCall);
+  FRIEND_TEST_ALL_PREFIXES(Parser, List);
+  FRIEND_TEST_ALL_PREFIXES(Parser, ParenExpression);
+  FRIEND_TEST_ALL_PREFIXES(Parser, UnaryOp);
+
+  DISALLOW_COPY_AND_ASSIGN(Parser);
+};
+
+#endif  // TOOLS_GN_PARSER_H_
diff --git a/tools/gn/parser_unittest.cc b/tools/gn/parser_unittest.cc
new file mode 100644
index 0000000..3fd8ebe
--- /dev/null
+++ b/tools/gn/parser_unittest.cc
@@ -0,0 +1,329 @@
+// Copyright (c) 2013 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 <iostream>
+#include <sstream>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "tools/gn/input_file.h"
+#include "tools/gn/parser.h"
+#include "tools/gn/tokenizer.h"
+
+namespace {
+
+bool GetTokens(const InputFile* input, std::vector<Token>* result) {
+  result->clear();
+  Err err;
+  *result = Tokenizer::Tokenize(input, &err);
+  return !err.has_error();
+}
+
+bool IsIdentifierEqual(const ParseNode* node, const char* val) {
+  if (!node)
+    return false;
+  const IdentifierNode* ident = node->AsIdentifier();
+  if (!ident)
+    return false;
+  return ident->value().value() == val;
+}
+
+bool IsLiteralEqual(const ParseNode* node, const char* val) {
+  if (!node)
+    return false;
+  const LiteralNode* lit = node->AsLiteral();
+  if (!lit)
+    return false;
+  return lit->value().value() == val;
+}
+
+// Returns true if the given node as a simple assignment to a given value.
+bool IsAssignment(const ParseNode* node, const char* ident, const char* value) {
+  if (!node)
+    return false;
+  const BinaryOpNode* binary = node->AsBinaryOp();
+  if (!binary)
+    return false;
+  return binary->op().IsOperatorEqualTo("=") &&
+         IsIdentifierEqual(binary->left(), ident) &&
+         IsLiteralEqual(binary->right(), value);
+}
+
+// Returns true if the given node is a block with one assignment statement.
+bool IsBlockWithAssignment(const ParseNode* node,
+                           const char* ident, const char* value) {
+  if (!node)
+    return false;
+  const BlockNode* block = node->AsBlock();
+  if (!block)
+    return false;
+  if (block->statements().size() != 1)
+    return false;
+  return IsAssignment(block->statements()[0], ident, value);
+}
+
+void DoParserPrintTest(const char* input, const char* expected) {
+  std::vector<Token> tokens;
+  InputFile input_file(SourceFile("/test"));
+  input_file.SetContents(input);
+  ASSERT_TRUE(GetTokens(&input_file, &tokens));
+
+  Err err;
+  scoped_ptr<ParseNode> result = Parser::Parse(tokens, &err);
+  ASSERT_TRUE(result);
+
+  std::ostringstream collector;
+  result->Print(collector, 0);
+
+  EXPECT_EQ(expected, collector.str());
+}
+
+// Expects the tokenizer or parser to identify an error at the given line and
+// character.
+void DoParserErrorTest(const char* input, int err_line, int err_char) {
+  InputFile input_file(SourceFile("/test"));
+  input_file.SetContents(input);
+
+  Err err;
+  std::vector<Token> tokens = Tokenizer::Tokenize(&input_file, &err);
+  if (!err.has_error()) {
+    scoped_ptr<ParseNode> result = Parser::Parse(tokens, &err);
+    ASSERT_FALSE(result);
+    ASSERT_TRUE(err.has_error());
+  }
+
+  EXPECT_EQ(err_line, err.location().line_number());
+  EXPECT_EQ(err_char, err.location().char_offset());
+}
+
+}  // namespace
+
+TEST(Parser, BinaryOp) {
+  std::vector<Token> tokens;
+
+  // Simple set expression.
+  InputFile expr_input(SourceFile("/test"));
+  expr_input.SetContents("a=2");
+  ASSERT_TRUE(GetTokens(&expr_input, &tokens));
+  Err err;
+  Parser set(tokens, &err);
+  scoped_ptr<ParseNode> expr = set.ParseExpression();
+  ASSERT_TRUE(expr);
+
+  const BinaryOpNode* binary_op = expr->AsBinaryOp();
+  ASSERT_TRUE(binary_op);
+
+  EXPECT_TRUE(binary_op->left()->AsIdentifier());
+
+  EXPECT_TRUE(binary_op->op().type() == Token::OPERATOR);
+  EXPECT_TRUE(binary_op->op().value() == "=");
+
+  EXPECT_TRUE(binary_op->right()->AsLiteral());
+}
+
+TEST(Parser, Condition) {
+  std::vector<Token> tokens;
+
+  InputFile cond_input(SourceFile("/test"));
+  cond_input.SetContents("if(1) { a = 2 }");
+  ASSERT_TRUE(GetTokens(&cond_input, &tokens));
+  Err err;
+  Parser simple_if(tokens, &err);
+  scoped_ptr<ConditionNode> cond = simple_if.ParseCondition();
+  ASSERT_TRUE(cond);
+
+  EXPECT_TRUE(IsLiteralEqual(cond->condition(), "1"));
+  EXPECT_FALSE(cond->if_false());  // No else block.
+  EXPECT_TRUE(IsBlockWithAssignment(cond->if_true(), "a", "2"));
+
+  // Now try a complicated if/else if/else one.
+  InputFile complex_if_input(SourceFile("/test"));
+  complex_if_input.SetContents(
+      "if(1) { a = 2 } else if (0) { a = 3 } else { a = 4 }");
+  ASSERT_TRUE(GetTokens(&complex_if_input, &tokens));
+  Parser complex_if(tokens, &err);
+  cond = complex_if.ParseCondition();
+  ASSERT_TRUE(cond);
+
+  EXPECT_TRUE(IsLiteralEqual(cond->condition(), "1"));
+  EXPECT_TRUE(IsBlockWithAssignment(cond->if_true(), "a", "2"));
+
+  ASSERT_TRUE(cond->if_false());
+  const ConditionNode* nested_cond = cond->if_false()->AsConditionNode();
+  ASSERT_TRUE(nested_cond);
+  EXPECT_TRUE(IsLiteralEqual(nested_cond->condition(), "0"));
+  EXPECT_TRUE(IsBlockWithAssignment(nested_cond->if_true(), "a", "3"));
+  EXPECT_TRUE(IsBlockWithAssignment(nested_cond->if_false(), "a", "4"));
+}
+
+TEST(Parser, FunctionCall) {
+  const char* input = "foo(a, 1, 2,) bar()";
+  const char* expected =
+      "BLOCK\n"
+      " FUNCTION(foo)\n"
+      "  LIST\n"
+      "   IDENTIFIER(a)\n"
+      "   LITERAL(1)\n"
+      "   LITERAL(2)\n"
+      " FUNCTION(bar)\n"
+      "  LIST\n";
+  DoParserPrintTest(input, expected);
+}
+
+TEST(Parser, ParenExpression) {
+  const char* input = "(foo(1)) + (a + b)";
+  const char* expected =
+      "BLOCK\n"
+      " BINARY(+)\n"
+      "  FUNCTION(foo)\n"
+      "   LIST\n"
+      "    LITERAL(1)\n"
+      "  BINARY(+)\n"
+      "   IDENTIFIER(a)\n"
+      "   IDENTIFIER(b)\n";
+  DoParserPrintTest(input, expected);
+  DoParserErrorTest("(a +", 1, 4);
+}
+
+TEST(Parser, UnaryOp) {
+  std::vector<Token> tokens;
+
+  InputFile ident_input(SourceFile("/test"));
+  ident_input.SetContents("!foo");
+  ASSERT_TRUE(GetTokens(&ident_input, &tokens));
+  Err err;
+  Parser ident(tokens, &err);
+  scoped_ptr<UnaryOpNode> op = ident.ParseUnaryOp();
+
+  ASSERT_TRUE(op);
+  EXPECT_TRUE(op->op().type() == Token::OPERATOR);
+  EXPECT_TRUE(op->op().value() == "!");
+}
+
+TEST(Parser, CompleteFunction) {
+  const char* input =
+      "cc_test(\"foo\") {\n"
+      "  sources = [\n"
+      "    \"foo.cc\",\n"
+      "    \"foo.h\"\n"
+      "  ]\n"
+      "  dependencies = [\n"
+      "    \"base\"\n"
+      "  ]\n"
+      "}\n";
+  const char* expected =
+      "BLOCK\n"
+      " FUNCTION(cc_test)\n"
+      "  LIST\n"
+      "   LITERAL(\"foo\")\n"
+      "  BLOCK\n"
+      "   BINARY(=)\n"
+      "    IDENTIFIER(sources)\n"
+      "    LIST\n"
+      "     LITERAL(\"foo.cc\")\n"
+      "     LITERAL(\"foo.h\")\n"
+      "   BINARY(=)\n"
+      "    IDENTIFIER(dependencies)\n"
+      "    LIST\n"
+      "     LITERAL(\"base\")\n";
+  DoParserPrintTest(input, expected);
+}
+
+TEST(Parser, FunctionWithConditional) {
+  const char* input =
+      "cc_test(\"foo\") {\n"
+      "  sources = [\"foo.cc\"]\n"
+      "  if (OS == \"mac\") {\n"
+      "    sources += \"bar.cc\"\n"
+      "  } else if (OS == \"win\") {\n"
+      "    sources -= [\"asd.cc\", \"foo.cc\"]\n"
+      "  } else {\n"
+      "    dependencies += [\"bar.cc\"]\n"
+      "  }\n"
+      "}\n";
+  const char* expected =
+      "BLOCK\n"
+      " FUNCTION(cc_test)\n"
+      "  LIST\n"
+      "   LITERAL(\"foo\")\n"
+      "  BLOCK\n"
+      "   BINARY(=)\n"
+      "    IDENTIFIER(sources)\n"
+      "    LIST\n"
+      "     LITERAL(\"foo.cc\")\n"
+      "   CONDITION\n"
+      "    BINARY(==)\n"
+      "     IDENTIFIER(OS)\n"
+      "     LITERAL(\"mac\")\n"
+      "    BLOCK\n"
+      "     BINARY(+=)\n"
+      "      IDENTIFIER(sources)\n"
+      "      LITERAL(\"bar.cc\")\n"
+      "    CONDITION\n"
+      "     BINARY(==)\n"
+      "      IDENTIFIER(OS)\n"
+      "      LITERAL(\"win\")\n"
+      "     BLOCK\n"
+      "      BINARY(-=)\n"
+      "       IDENTIFIER(sources)\n"
+      "       LIST\n"
+      "        LITERAL(\"asd.cc\")\n"
+      "        LITERAL(\"foo.cc\")\n"
+      "     BLOCK\n"
+      "      BINARY(+=)\n"
+      "       IDENTIFIER(dependencies)\n"
+      "       LIST\n"
+      "        LITERAL(\"bar.cc\")\n";
+  DoParserPrintTest(input, expected);
+}
+
+TEST(Parser, NestedBlocks) {
+  const char* input = "{cc_test(\"foo\") {{foo=1}{}}}";
+  const char* expected =
+      "BLOCK\n"
+      " BLOCK\n"
+      "  FUNCTION(cc_test)\n"
+      "   LIST\n"
+      "    LITERAL(\"foo\")\n"
+      "   BLOCK\n"
+      "    BLOCK\n"
+      "     BINARY(=)\n"
+      "      IDENTIFIER(foo)\n"
+      "      LITERAL(1)\n"
+      "    BLOCK\n";
+  DoParserPrintTest(input, expected);
+}
+
+TEST(Parser, List) {
+  const char* input = "[] a = [1,asd,] b = [1, 2+3 - foo]";
+  const char* expected =
+      "BLOCK\n"
+      " LIST\n"
+      " BINARY(=)\n"
+      "  IDENTIFIER(a)\n"
+      "  LIST\n"
+      "   LITERAL(1)\n"
+      "   IDENTIFIER(asd)\n"
+      " BINARY(=)\n"
+      "  IDENTIFIER(b)\n"
+      "  LIST\n"
+      "   LITERAL(1)\n"
+      "   BINARY(+)\n"
+      "    LITERAL(2)\n"
+      "    BINARY(-)\n"
+      "     LITERAL(3)\n"
+      "     IDENTIFIER(foo)\n";
+  DoParserPrintTest(input, expected);
+
+  DoParserErrorTest("[a, 2+,]", 1, 7);
+  DoParserErrorTest("[,]", 1, 2);
+  DoParserErrorTest("[a,,]", 1, 4);
+}
+
+TEST(Parser, UnterminatedBlock) {
+  DoParserErrorTest("hello {", 1, 7);
+}
+
+TEST(Parser, BadlyTerminatedNumber) {
+  DoParserErrorTest("1234z", 1, 5);
+}
diff --git a/tools/gn/path_output.cc b/tools/gn/path_output.cc
new file mode 100644
index 0000000..f67b3d4
--- /dev/null
+++ b/tools/gn/path_output.cc
@@ -0,0 +1,116 @@
+// Copyright (c) 2013 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/path_output.h"
+
+#include "build/build_config.h"
+#include "tools/gn/filesystem_utils.h"
+#include "tools/gn/output_file.h"
+#include "tools/gn/string_utils.h"
+
+PathOutput::PathOutput(const SourceDir& current_dir,
+                       EscapingMode escaping,
+                       bool convert_slashes)
+    : current_dir_(current_dir) {
+  inverse_current_dir_ = InvertDir(current_dir_);
+
+  options_.mode = escaping;
+  options_.convert_slashes = convert_slashes;
+  options_.inhibit_quoting = false;
+
+  if (convert_slashes)
+    ConvertPathToSystem(&inverse_current_dir_);
+}
+
+PathOutput::~PathOutput() {
+}
+
+void PathOutput::WriteFile(std::ostream& out, const SourceFile& file) const {
+  WritePathStr(out, file.value());
+}
+
+void PathOutput::WriteDir(std::ostream& out,
+                          const SourceDir& dir,
+                          DirSlashEnding slash_ending) const {
+  if (dir.value() == "/") {
+    // Writing system root is always a slash (this will normally only come up
+    // on Posix systems).
+    out << "/";
+  } else if (dir.value() == "//") {
+    // Writing out the source root.
+    if (slash_ending == DIR_NO_LAST_SLASH) {
+      // The inverse_current_dir_ will contain a [back]slash at the end, so we
+      // can't just write it out.
+      if (inverse_current_dir_.empty()) {
+        out << ".";
+      } else {
+        out.write(inverse_current_dir_.c_str(),
+                  inverse_current_dir_.size() - 1);
+      }
+    } else {
+      if (inverse_current_dir_.empty())
+        out << "./";
+      else
+        out << inverse_current_dir_;
+    }
+  } else if (slash_ending == DIR_INCLUDE_LAST_SLASH) {
+    WritePathStr(out, dir.value());
+  } else {
+    // DIR_NO_LAST_SLASH mode, just trim the last char.
+    WritePathStr(out, base::StringPiece(dir.value().data(),
+                                        dir.value().size() - 1));
+  }
+}
+
+void PathOutput::WriteFile(std::ostream& out, const OutputFile& file) const {
+  // Here we assume that the path is already preprocessed.
+  EscapeStringToStream(out, file.value(), options_);
+}
+
+void PathOutput::WriteSourceRelativeString(
+    std::ostream& out,
+    const base::StringPiece& str) const {
+  // Input begins with two slashes, is relative to source root. Strip off
+  // the two slashes when cat-ing it.
+  if (options_.mode == ESCAPE_SHELL) {
+    // Shell escaping needs an intermediate string since it may end up
+    // quoting the whole thing. On Windows, the slashes may already be
+    // converted to backslashes in inverse_current_dir_, but we assume that on
+    // Windows the escaper won't try to then escape the preconverted
+    // backslashes and will just pass them, so this is fine.
+    std::string intermediate;
+    intermediate.reserve(inverse_current_dir_.size() + str.size());
+    intermediate.assign(inverse_current_dir_.c_str(),
+                        inverse_current_dir_.size());
+    intermediate.append(str.data(), str.size());
+
+    EscapeStringToStream(out,
+        base::StringPiece(intermediate.c_str(), intermediate.size()),
+        options_);
+  } else {
+    // Ninja (and none) escaping can avoid the intermediate string and
+    // reprocessing of the inverse_current_dir_.
+    out << inverse_current_dir_;
+    EscapeStringToStream(out, str, options_);
+  }
+}
+
+void PathOutput::WritePathStr(std::ostream& out,
+                              const base::StringPiece& str) const {
+  DCHECK(str.size() > 0 && str[0] == '/');
+
+  if (str.size() >= 2 && str[1] == '/') {
+    WriteSourceRelativeString(out, str.substr(2));
+  } else {
+    // Input begins with one slash, don't write the current directory since
+    // it's system-absolute.
+#if defined(OS_WIN)
+    // On Windows, trim the leading slash, since the input for absolute
+    // paths will look like "/C:/foo/bar.txt".
+    EscapeStringToStream(out, str.substr(1), options_);
+#else
+    EscapeStringToStream(out, str, options_);
+#endif
+  }
+}
diff --git a/tools/gn/path_output.h b/tools/gn/path_output.h
new file mode 100644
index 0000000..00bdbee
--- /dev/null
+++ b/tools/gn/path_output.h
@@ -0,0 +1,80 @@
+// Copyright (c) 2013 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.
+
+#ifndef TOOLS_GN_PATH_OUTPUT_H_
+#define TOOLS_GN_PATH_OUTPUT_H_
+
+#include <iosfwd>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/strings/string_piece.h"
+#include "tools/gn/escape.h"
+#include "tools/gn/source_dir.h"
+
+class OutputFile;
+class SourceFile;
+
+// Writes file names to streams assuming a certain input directory and
+// escaping rules. This gives us a central place for managing this state.
+class PathOutput {
+ public:
+  // Controls whether writing directory names include the trailing slash.
+  // Often we don't want the trailing slash when writing out to a command line,
+  // especially on Windows where it's a backslash and might be interpreted as
+  // escaping the thing following it.
+  enum DirSlashEnding {
+    DIR_INCLUDE_LAST_SLASH,
+    DIR_NO_LAST_SLASH,
+  };
+
+  PathOutput(const SourceDir& current_dir,
+             EscapingMode escaping,
+             bool convert_slashes);
+  ~PathOutput();
+
+  // Read-only since inverse_current_dir_ is computed depending on this.
+  EscapingMode escaping_mode() const { return options_.mode; }
+
+  // When true, converts slashes to the system-type path separators (on
+  // Windows, this is a backslash, this is a NOP otherwise).
+  //
+  // Read-only since inverse_current_dir_ is computed depending on this.
+  bool convert_slashes_to_system() const { return options_.convert_slashes; }
+
+  // When the output escaping is ESCAPE_SHELL, the escaper will normally put
+  // quotes around suspect things. If this value is set to true, we'll disable
+  // the quoting feature. This means that in ESCAPE_SHELL mode, strings with
+  // spaces in them qon't be quoted. This mode is for when quoting is done at
+  // some higher-level. Defaults to false.
+  bool inhibit_quoting() const { return options_.inhibit_quoting; }
+  void set_inhibit_quoting(bool iq) { options_.inhibit_quoting = iq; }
+
+  void WriteFile(std::ostream& out, const SourceFile& file) const;
+  void WriteFile(std::ostream& out, const OutputFile& file) const;
+  void WriteDir(std::ostream& out,
+                const SourceDir& dir,
+                DirSlashEnding slash_ending) const;
+
+  // Backend for WriteFile and WriteDir. This appends the given file or
+  // directory string to the file.
+  void WritePathStr(std::ostream& out, const base::StringPiece& str) const;
+
+ private:
+  // Takes the given string and writes it out, appending to the inverse
+  // current dir. This assumes leading slashes have been trimmed.
+  void WriteSourceRelativeString(std::ostream& out,
+                                 const base::StringPiece& str) const;
+
+  SourceDir current_dir_;
+
+  // Uses system slashes if convert_slashes_to_system_.
+  std::string inverse_current_dir_;
+
+  // Since the inverse_current_dir_ depends on some of these, we don't expose
+  // this directly to modification.
+  EscapeOptions options_;
+};
+
+#endif  // TOOLS_GN_PATH_OUTPUT_H_
diff --git a/tools/gn/path_output_unittest.cc b/tools/gn/path_output_unittest.cc
new file mode 100644
index 0000000..5133b79
--- /dev/null
+++ b/tools/gn/path_output_unittest.cc
@@ -0,0 +1,193 @@
+// Copyright (c) 2013 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 <sstream>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "tools/gn/path_output.h"
+#include "tools/gn/source_dir.h"
+#include "tools/gn/source_file.h"
+
+TEST(PathOutput, Basic) {
+  SourceDir build_dir("//out/Debug/");
+  PathOutput writer(build_dir, ESCAPE_NONE, false);
+  {
+    // Normal source-root path.
+    std::ostringstream out;
+    writer.WriteFile(out, SourceFile("//foo/bar.cc"));
+    EXPECT_EQ("../../foo/bar.cc", out.str());
+  }
+  {
+    // File in the root dir.
+    std::ostringstream out;
+    writer.WriteFile(out, SourceFile("//foo.cc"));
+    EXPECT_EQ("../../foo.cc", out.str());
+  }
+#if defined(OS_WIN)
+  {
+    // System-absolute path.
+    std::ostringstream out;
+    writer.WriteFile(out, SourceFile("/C:/foo/bar.cc"));
+    EXPECT_EQ("C:/foo/bar.cc", out.str());
+  }
+#else
+  {
+    // System-absolute path.
+    std::ostringstream out;
+    writer.WriteFile(out, SourceFile("/foo/bar.cc"));
+    EXPECT_EQ("/foo/bar.cc", out.str());
+  }
+#endif
+}
+
+// Same as basic but the output dir is the root.
+TEST(PathOutput, BasicInRoot) {
+  SourceDir build_dir("//");
+  PathOutput writer(build_dir, ESCAPE_NONE, false);
+  {
+    // Normal source-root path.
+    std::ostringstream out;
+    writer.WriteFile(out, SourceFile("//foo/bar.cc"));
+    EXPECT_EQ("foo/bar.cc", out.str());
+  }
+  {
+    // File in the root dir.
+    std::ostringstream out;
+    writer.WriteFile(out, SourceFile("//foo.cc"));
+    EXPECT_EQ("foo.cc", out.str());
+  }
+}
+
+TEST(PathOutput, NinjaEscaping) {
+  SourceDir build_dir("//out/Debug/");
+  PathOutput writer(build_dir, ESCAPE_NINJA, false);
+  {
+    // Spaces and $ in filenames.
+    std::ostringstream out;
+    writer.WriteFile(out, SourceFile("//foo/foo bar$.cc"));
+    EXPECT_EQ("../../foo/foo$ bar$$.cc", out.str());
+  }
+  {
+    // Not other weird stuff
+    std::ostringstream out;
+    writer.WriteFile(out, SourceFile("//foo/\"foo\\bar\".cc"));
+    EXPECT_EQ("../../foo/\"foo\\bar\".cc", out.str());
+  }
+}
+
+TEST(PathOutput, ShellEscaping) {
+  SourceDir build_dir("//out/Debug/");
+  PathOutput writer(build_dir, ESCAPE_SHELL, false);
+  {
+    // Spaces in filenames should get quoted.
+    std::ostringstream out;
+    writer.WriteFile(out, SourceFile("//foo/foo bar.cc"));
+    EXPECT_EQ("\"../../foo/foo bar.cc\"", out.str());
+  }
+  {
+    // Quotes should get blackslash-escaped.
+    std::ostringstream out;
+    writer.WriteFile(out, SourceFile("//foo/\"foobar\".cc"));
+    EXPECT_EQ("../../foo/\\\"foobar\\\".cc", out.str());
+  }
+  {
+    // Backslashes should get escaped on non-Windows and preserved on Windows.
+    std::ostringstream out;
+    writer.WriteFile(out, SourceFile("//foo\\bar.cc"));
+#if defined(OS_WIN)
+    EXPECT_EQ("../../foo\\bar.cc", out.str());
+#else
+    EXPECT_EQ("../../foo\\\\bar.cc", out.str());
+#endif
+  }
+}
+
+TEST(PathOutput, SlashConversion) {
+  SourceDir build_dir("//out/Debug/");
+  PathOutput writer(build_dir, ESCAPE_NINJA, true);
+  {
+    std::ostringstream out;
+    writer.WriteFile(out, SourceFile("//foo/bar.cc"));
+#if defined(OS_WIN)
+    EXPECT_EQ("..\\..\\foo\\bar.cc", out.str());
+#else
+    EXPECT_EQ("../../foo/bar.cc", out.str());
+#endif
+  }
+}
+
+TEST(PathOutput, InhibitQuoting) {
+  SourceDir build_dir("//out/Debug/");
+  PathOutput writer(build_dir, ESCAPE_SHELL, false);
+  writer.set_inhibit_quoting(true);
+  {
+    // We should get unescaped spaces in the output with no quotes.
+    std::ostringstream out;
+    writer.WriteFile(out, SourceFile("//foo/foo bar.cc"));
+    EXPECT_EQ("../../foo/foo bar.cc", out.str());
+  }
+}
+
+TEST(PathOutput, WriteDir) {
+  {
+    SourceDir build_dir("//out/Debug/");
+    PathOutput writer(build_dir, ESCAPE_NINJA, false);
+    {
+      std::ostringstream out;
+      writer.WriteDir(out, SourceDir("//foo/bar/"),
+                      PathOutput::DIR_INCLUDE_LAST_SLASH);
+      EXPECT_EQ("../../foo/bar/", out.str());
+    }
+    {
+      std::ostringstream out;
+      writer.WriteDir(out, SourceDir("//foo/bar/"),
+                      PathOutput::DIR_NO_LAST_SLASH);
+      EXPECT_EQ("../../foo/bar", out.str());
+    }
+
+    // Output source root dir.
+    {
+      std::ostringstream out;
+      writer.WriteDir(out, SourceDir("//"),
+                      PathOutput::DIR_INCLUDE_LAST_SLASH);
+      EXPECT_EQ("../../", out.str());
+    }
+    {
+      std::ostringstream out;
+      writer.WriteDir(out, SourceDir("//"),
+                      PathOutput::DIR_NO_LAST_SLASH);
+      EXPECT_EQ("../..", out.str());
+    }
+
+    // Output system root dir.
+    {
+      std::ostringstream out;
+      writer.WriteDir(out, SourceDir("/"),
+                      PathOutput::DIR_INCLUDE_LAST_SLASH);
+      EXPECT_EQ("/", out.str());
+    }
+    {
+      std::ostringstream out;
+      writer.WriteDir(out, SourceDir("/"),
+                      PathOutput::DIR_NO_LAST_SLASH);
+      EXPECT_EQ("/", out.str());
+    }
+  }
+  {
+    // Empty build dir writer.
+    PathOutput root_writer(SourceDir("//"), ESCAPE_NINJA, false);
+    {
+      std::ostringstream out;
+      root_writer.WriteDir(out, SourceDir("//"),
+                           PathOutput::DIR_INCLUDE_LAST_SLASH);
+      EXPECT_EQ("./", out.str());
+    }
+    {
+      std::ostringstream out;
+      root_writer.WriteDir(out, SourceDir("//"),
+                           PathOutput::DIR_NO_LAST_SLASH);
+      EXPECT_EQ(".", out.str());
+    }
+  }
+}
diff --git a/tools/gn/pattern.cc b/tools/gn/pattern.cc
new file mode 100644
index 0000000..cc08b2c
--- /dev/null
+++ b/tools/gn/pattern.cc
@@ -0,0 +1,185 @@
+// Copyright (c) 2013 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/pattern.h"
+
+#include "tools/gn/value.h"
+
+namespace {
+
+void ParsePattern(const std::string& s, std::vector<Pattern::Subrange>* out) {
+  // Set when the last subrange is a literal so we can just append when we
+  // find another literal.
+  Pattern::Subrange* last_literal = NULL;
+
+  for (size_t i = 0; i < s.size(); i++) {
+    if (s[i] == '*') {
+      // Don't allow two **.
+      if (out->size() == 0 ||
+          (*out)[out->size() - 1].type != Pattern::Subrange::ANYTHING)
+        out->push_back(Pattern::Subrange(Pattern::Subrange::ANYTHING));
+      last_literal = NULL;
+    } else if (s[i] == '\\') {
+      if (i < s.size() - 1 && s[i + 1] == 'b') {
+        // "\b" means path boundary.
+        i++;
+        out->push_back(Pattern::Subrange(Pattern::Subrange::PATH_BOUNDARY));
+        last_literal = NULL;
+      } else {
+        // Backslash + anything else means that literal char.
+        if (!last_literal) {
+          out->push_back(Pattern::Subrange(Pattern::Subrange::LITERAL));
+          last_literal = &(*out)[out->size() - 1];
+        }
+        if (i < s.size() - 1) {
+          i++;
+          last_literal->literal.push_back(s[i]);
+        } else {
+          // Single backslash at end, use literal backslash.
+          last_literal->literal.push_back('\\');
+        }
+      }
+    } else {
+      if (!last_literal) {
+        out->push_back(Pattern::Subrange(Pattern::Subrange::LITERAL));
+        last_literal = &(*out)[out->size() - 1];
+      }
+      last_literal->literal.push_back(s[i]);
+    }
+  }
+}
+
+}  // namespace
+
+Pattern::Pattern(const std::string& s) {
+  ParsePattern(s, &subranges_);
+  is_suffix_ =
+      (subranges_.size() == 2 &&
+       subranges_[0].type == Subrange::ANYTHING &&
+       subranges_[1].type == Subrange::LITERAL);
+}
+
+Pattern::~Pattern() {
+}
+
+bool Pattern::MatchesString(const std::string& s) const {
+  // Empty pattern matches only empty string.
+  if (subranges_.empty())
+    return s.empty();
+
+  if (is_suffix_) {
+    const std::string& suffix = subranges_[1].literal;
+    if (suffix.size() > s.size())
+      return false;  // Too short.
+    return s.compare(s.size() - suffix.size(), suffix.size(), suffix) == 0;
+  }
+
+  return RecursiveMatch(s, 0, 0, true);
+}
+
+// We assume the number of ranges is small so recursive is always reasonable.
+// Could be optimized to only be recursive for *.
+bool Pattern::RecursiveMatch(const std::string& s,
+                             size_t begin_char,
+                             size_t subrange_index,
+                             bool allow_implicit_path_boundary) const {
+  if (subrange_index >= subranges_.size()) {
+    // Hit the end of our subranges, the text should also be at the end for a
+    // match.
+    return begin_char == s.size();
+  }
+
+  const Subrange& sr = subranges_[subrange_index];
+  switch (sr.type) {
+    case Subrange::LITERAL: {
+      if (s.size() - begin_char < sr.literal.size())
+        return false;  // Not enough room.
+      if (s.compare(begin_char, sr.literal.size(), sr.literal) != 0)
+        return false;  // Literal doesn't match.
+
+      // Recursively check the next one.
+      return RecursiveMatch(s, begin_char + sr.literal.size(),
+                            subrange_index + 1, true);
+    }
+
+    case Subrange::PATH_BOUNDARY: {
+      // When we can accept an implicit path boundary, we have to check both
+      // a match of the literal and the implicit one.
+      if (allow_implicit_path_boundary &&
+          (begin_char == 0 || begin_char == s.size())) {
+        // At implicit path boundary, see if the rest of the pattern matches.
+        if (RecursiveMatch(s, begin_char, subrange_index + 1, false))
+          return true;
+      }
+
+      // Check for a literal "/".
+      if (begin_char < s.size() && s[begin_char] == '/') {
+        // At explicit boundary, see if the rest of the pattern matches.
+        if (RecursiveMatch(s, begin_char + 1, subrange_index + 1, true))
+          return true;
+      }
+      return false;
+    }
+
+    case Subrange::ANYTHING: {
+      if (subrange_index == subranges_.size() - 1)
+        return true;  // * at the end, consider it matching.
+
+      size_t min_next_size = sr.MinSize();
+
+      // We don't care about exactly what matched as long as there was a match,
+      // so we can do this front-to-back. If we needed the match, we would
+      // normally want "*" to be greedy so would work backwards.
+      for (size_t i = begin_char; i < s.size() - min_next_size; i++) {
+        // Note: this could probably be faster by detecting the type of the
+        // next match in advance and checking for a match in this loop rather
+        // than doing a full recursive call for each character.
+        if (RecursiveMatch(s, i, subrange_index + 1, true))
+          return true;
+      }
+      return false;
+    }
+
+    default:
+      NOTREACHED();
+  }
+
+  return false;
+}
+
+PatternList::PatternList() {
+}
+
+PatternList::~PatternList() {
+}
+
+void PatternList::SetFromValue(const Value& v, Err* err) {
+  patterns_.clear();
+
+  if (v.type() != Value::LIST) {
+    *err = Err(v.origin(), "This value must be a list.");
+    return;
+  }
+
+  const std::vector<Value>& list = v.list_value();
+  for (size_t i = 0; i < list.size(); i++) {
+    if (!list[i].VerifyTypeIs(Value::STRING, err))
+      return;
+    patterns_.push_back(Pattern(list[i].string_value()));
+  }
+}
+
+bool PatternList::MatchesString(const std::string& s) const {
+  for (size_t i = 0; i < patterns_.size(); i++) {
+    if (patterns_[i].MatchesString(s))
+      return true;
+  }
+  return false;
+}
+
+bool PatternList::MatchesValue(const Value& v) const {
+  if (v.type() == Value::STRING)
+    return MatchesString(v.string_value());
+  return false;
+}
diff --git a/tools/gn/pattern.h b/tools/gn/pattern.h
new file mode 100644
index 0000000..582cfea
--- /dev/null
+++ b/tools/gn/pattern.h
@@ -0,0 +1,86 @@
+// Copyright (c) 2013 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.
+
+#ifndef TOOLS_GN_PATTERN_H_
+#define TOOLS_GN_PATTERN_H_
+
+#include <string>
+#include <vector>
+
+#include "tools/gn/value.h"
+
+class Pattern {
+ public:
+  struct Subrange {
+    enum Type {
+      LITERAL,  // Matches exactly the contents of the string.
+      ANYTHING,  // * (zero or more chars).
+      PATH_BOUNDARY  // '/' or beginning of string.
+    };
+
+    Subrange(Type t, const std::string& l = std::string())
+        : type(t),
+          literal(l) {
+    }
+
+    // Returns the minimum number of chars that this subrange requires.
+    size_t MinSize() const {
+      switch (type) {
+        case LITERAL:
+          return literal.size();
+        case ANYTHING:
+          return 0;
+        case PATH_BOUNDARY:
+          return 0;  // Can match beginning or end of string, which is 0 len.
+        default:
+          return 0;
+      }
+    }
+
+    Type type;
+
+    // When type == LITERAL this is the text to match.
+    std::string literal;
+  };
+
+  Pattern(const std::string& s);
+  ~Pattern();
+
+  // Returns true if the current pattern matches the given string.
+  bool MatchesString(const std::string& s) const;
+
+ private:
+  // allow_implicit_path_boundary determines if a path boundary should accept
+  // matches at the beginning or end of the string.
+  bool RecursiveMatch(const std::string& s,
+                      size_t begin_char,
+                      size_t subrange_index,
+                      bool allow_implicit_path_boundary) const;
+
+  std::vector<Subrange> subranges_;
+
+  // Set to true when the subranges are "*foo" ("ANYTHING" followed by a
+  // literal). This covers most patterns so we optimize for this.
+  bool is_suffix_;
+};
+
+class PatternList {
+ public:
+  PatternList();
+  ~PatternList();
+
+  bool is_empty() const { return patterns_.empty(); }
+
+  // Initializes the pattern list from a give list of pattern strings. Sets
+  // |*err| on failure.
+  void SetFromValue(const Value& v, Err* err);
+
+  bool MatchesString(const std::string& s) const;
+  bool MatchesValue(const Value& v) const;
+
+ private:
+  std::vector<Pattern> patterns_;
+};
+
+#endif  // TOOLS_GN_PATTERN_H_
diff --git a/tools/gn/pattern_unittest.cc b/tools/gn/pattern_unittest.cc
new file mode 100644
index 0000000..e9ffea6
--- /dev/null
+++ b/tools/gn/pattern_unittest.cc
@@ -0,0 +1,61 @@
+// Copyright (c) 2013 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 "testing/gtest/include/gtest/gtest.h"
+#include "tools/gn/pattern.h"
+
+namespace {
+
+struct Case {
+  const char* pattern;
+  const char* candidate;
+  bool expected_match;
+};
+
+}  // namespace
+
+TEST(Pattern, Matches) {
+  Case pattern_cases[] = {
+    // Empty pattern matches only empty string.
+    { "", "", true },
+    { "", "foo", false },
+    // Exact matches.
+    { "foo", "foo", true },
+    { "foo", "bar", false },
+    // Path boundaries.
+    { "\\b", "", true },
+    { "\\b", "/", true },
+    { "\\b\\b", "/", true },
+    { "\\b\\b\\b", "", false },
+    { "\\b\\b\\b", "/", true },
+    { "\\b", "//", false },
+    { "\\bfoo\\b", "foo", true },
+    { "\\bfoo\\b", "/foo/", true },
+    { "\\b\\bfoo", "/foo", true },
+    // *
+    { "*", "", true },
+    { "*", "foo", true },
+    { "*foo", "foo", true },
+    { "*foo", "gagafoo", true },
+    { "*foo", "gagafoob", false },
+    { "foo*bar", "foobar", true },
+    { "foo*bar", "foo-bar", true },
+    { "foo*bar", "foolalalalabar", true },
+    { "foo*bar", "foolalalalabaz", false },
+    { "*a*b*c*d*", "abcd", true },
+    { "*a*b*c*d*", "1a2b3c4d5", true },
+    { "*a*b*c*d*", "1a2b3c45", false },
+    { "*\\bfoo\\b*", "foo", true },
+    { "*\\bfoo\\b*", "/foo/", true },
+    { "*\\bfoo\\b*", "foob", false },
+    { "*\\bfoo\\b*", "lala/foo/bar/baz", true },
+  };
+  for (size_t i = 0; i < arraysize(pattern_cases); i++) {
+    const Case& c = pattern_cases[i];
+    Pattern pattern(c.pattern);
+    bool result = pattern.MatchesString(c.candidate);
+    EXPECT_EQ(c.expected_match, result) << i << ": \"" << c.pattern
+        << "\", \"" << c.candidate << "\"";
+  }
+}
diff --git a/tools/gn/scheduler.cc b/tools/gn/scheduler.cc
new file mode 100644
index 0000000..33c8f1a
--- /dev/null
+++ b/tools/gn/scheduler.cc
@@ -0,0 +1,130 @@
+// Copyright (c) 2013 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/scheduler.h"
+
+#include "base/bind.h"
+#include "tools/gn/ninja_target_writer.h"
+#include "tools/gn/standard_out.h"
+
+Scheduler* g_scheduler = NULL;
+
+Scheduler::Scheduler()
+    : pool_(new base::SequencedWorkerPool(32, "worker_")),
+      input_file_manager_(new InputFileManager),
+      verbose_logging_(false),
+      work_count_(0),
+      is_failed_(false) {
+  g_scheduler = this;
+}
+
+Scheduler::~Scheduler() {
+  g_scheduler = NULL;
+}
+
+bool Scheduler::Run() {
+  runner_.Run();
+  pool_->Shutdown();
+  return !is_failed();
+}
+
+void Scheduler::Log(const std::string& verb, const std::string& msg) {
+  if (base::MessageLoop::current() == &main_loop_) {
+    LogOnMainThread(verb, msg);
+  } else {
+    // The run loop always joins on the sub threads, so the lifetime of this
+    // object outlives the invocations of this function, hence "unretained".
+    main_loop_.PostTask(FROM_HERE,
+                        base::Bind(&Scheduler::LogOnMainThread,
+                                   base::Unretained(this), verb, msg));
+  }
+}
+
+void Scheduler::FailWithError(const Err& err) {
+  DCHECK(err.has_error());
+  {
+    base::AutoLock lock(lock_);
+
+    if (is_failed_)
+      return;  // Ignore errors once we see one.
+    is_failed_ = true;
+  }
+
+  if (base::MessageLoop::current() == &main_loop_) {
+    FailWithErrorOnMainThread(err);
+  } else {
+    // The run loop always joins on the sub threads, so the lifetime of this
+    // object outlives the invocations of this function, hence "unretained".
+    main_loop_.PostTask(FROM_HERE,
+                        base::Bind(&Scheduler::FailWithErrorOnMainThread,
+                                   base::Unretained(this), err));
+  }
+}
+
+void Scheduler::ScheduleWork(const base::Closure& work) {
+  IncrementWorkCount();
+  pool_->PostWorkerTaskWithShutdownBehavior(
+      FROM_HERE, base::Bind(&Scheduler::DoWork,
+                            base::Unretained(this), work),
+      base::SequencedWorkerPool::BLOCK_SHUTDOWN);
+}
+
+void Scheduler::ScheduleTargetFileWrite(const Target* target) {
+  pool_->PostWorkerTaskWithShutdownBehavior(
+      FROM_HERE, base::Bind(&Scheduler::DoTargetFileWrite,
+                            base::Unretained(this), target),
+      base::SequencedWorkerPool::BLOCK_SHUTDOWN);
+}
+
+void Scheduler::AddGenDependency(const SourceFile& source_file) {
+  base::AutoLock lock(lock_);
+  gen_dependencies_.push_back(source_file);
+}
+
+std::vector<SourceFile> Scheduler::GetGenDependencies() const {
+  base::AutoLock lock(lock_);
+  return gen_dependencies_;
+}
+
+void Scheduler::IncrementWorkCount() {
+  base::AtomicRefCountInc(&work_count_);
+}
+
+void Scheduler::DecrementWorkCount() {
+  if (!base::AtomicRefCountDec(&work_count_)) {
+    if (base::MessageLoop::current() == &main_loop_) {
+      OnComplete();
+    } else {
+      main_loop_.PostTask(FROM_HERE,
+                          base::Bind(&Scheduler::OnComplete,
+                                     base::Unretained(this)));
+    }
+  }
+}
+
+void Scheduler::LogOnMainThread(const std::string& verb,
+                                const std::string& msg) {
+  OutputString(verb, DECORATION_YELLOW);
+  OutputString(" " + msg + "\n");
+}
+
+void Scheduler::FailWithErrorOnMainThread(const Err& err) {
+  err.PrintToStdout();
+  runner_.Quit();
+}
+
+void Scheduler::DoTargetFileWrite(const Target* target) {
+  NinjaTargetWriter::RunAndWriteFile(target);
+}
+
+void Scheduler::DoWork(const base::Closure& closure) {
+  closure.Run();
+  DecrementWorkCount();
+}
+
+void Scheduler::OnComplete() {
+  // Should be called on the main thread.
+  DCHECK(base::MessageLoop::current() == main_loop());
+  runner_.Quit();
+}
diff --git a/tools/gn/scheduler.h b/tools/gn/scheduler.h
new file mode 100644
index 0000000..eab996d
--- /dev/null
+++ b/tools/gn/scheduler.h
@@ -0,0 +1,90 @@
+// Copyright (c) 2013 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.
+
+#ifndef TOOLS_GN_SCHEDULER_H_
+#define TOOLS_GN_SCHEDULER_H_
+
+#include "base/atomic_ref_count.h"
+#include "base/basictypes.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/sequenced_worker_pool.h"
+#include "tools/gn/input_file_manager.h"
+
+class Target;
+
+// Maintains the thread pool and error state.
+class Scheduler {
+ public:
+  Scheduler();
+  ~Scheduler();
+
+  bool Run();
+
+  base::MessageLoop* main_loop() { return &main_loop_; }
+  base::SequencedWorkerPool* pool() { return pool_; }
+
+  InputFileManager* input_file_manager() { return input_file_manager_; }
+
+  bool verbose_logging() const { return verbose_logging_; }
+  void set_verbose_logging(bool v) { verbose_logging_ = v; }
+
+  // TODO(brettw) data race on this access (benign?).
+  bool is_failed() const { return is_failed_; }
+
+  void Log(const std::string& verb, const std::string& msg);
+  void FailWithError(const Err& err);
+
+  void ScheduleWork(const base::Closure& work);
+
+  void ScheduleTargetFileWrite(const Target* target);
+
+  // Declares that the given file was read and affected the build output.
+  //
+  // TODO(brettw) this is global rather than per-BuildSettings. If we
+  // start using >1 build settings, then we probably want this to take a
+  // BuildSettings object so we know the depdency on a per-build basis.
+  void AddGenDependency(const SourceFile& source_file);
+  std::vector<SourceFile> GetGenDependencies() const;
+
+  // We maintain a count of the things we need to do that works like a
+  // refcount. When this reaches 0, the program exits.
+  void IncrementWorkCount();
+  void DecrementWorkCount();
+
+ private:
+  void LogOnMainThread(const std::string& verb, const std::string& msg);
+  void FailWithErrorOnMainThread(const Err& err);
+
+  void DoTargetFileWrite(const Target* target);
+
+  void DoWork(const base::Closure& closure);
+
+  void OnComplete();
+
+  base::MessageLoop main_loop_;
+  scoped_refptr<base::SequencedWorkerPool> pool_;
+
+  scoped_refptr<InputFileManager> input_file_manager_;
+
+  base::RunLoop runner_;
+
+  bool verbose_logging_;
+
+  base::AtomicRefCount work_count_;
+
+  mutable base::Lock lock_;
+  bool is_failed_;
+
+  // Additional input dependencies. Protected by the lock.
+  std::vector<SourceFile> gen_dependencies_;
+
+  DISALLOW_COPY_AND_ASSIGN(Scheduler);
+};
+
+extern Scheduler* g_scheduler;
+
+#endif  // TOOLS_GN_SCHEDULER_H_
+
diff --git a/tools/gn/scope.cc b/tools/gn/scope.cc
new file mode 100644
index 0000000..72664d7
--- /dev/null
+++ b/tools/gn/scope.cc
@@ -0,0 +1,372 @@
+// Copyright (c) 2013 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/scope.h"
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "tools/gn/parse_tree.h"
+
+namespace {
+
+// FLags set in the mode_flags_ of a scope. If a bit is set, it applies
+// recursively to all dependent scopes.
+const unsigned kProcessingBuildConfigFlag = 1;
+const unsigned kProcessingDefaultBuildConfigFlag = 2;
+const unsigned kProcessingImportFlag = 4;
+
+}  // namespace
+
+Scope::Scope(const Settings* settings)
+    : const_containing_(NULL),
+      mutable_containing_(NULL),
+      settings_(settings),
+      mode_flags_(0) {
+}
+
+Scope::Scope(Scope* parent)
+    : const_containing_(NULL),
+      mutable_containing_(parent),
+      settings_(parent->settings()),
+      mode_flags_(0) {
+}
+
+Scope::Scope(const Scope* parent)
+    : const_containing_(parent),
+      mutable_containing_(NULL),
+      settings_(parent->settings()),
+      mode_flags_(0) {
+}
+
+Scope::~Scope() {
+  STLDeleteContainerPairSecondPointers(target_defaults_.begin(),
+                                       target_defaults_.end());
+}
+
+const Value* Scope::GetValue(const base::StringPiece& ident,
+                             bool counts_as_used) {
+  // First check for programatically-provided values.
+  for (ProviderSet::const_iterator i = programmatic_providers_.begin();
+       i != programmatic_providers_.end(); ++i) {
+    const Value* v = (*i)->GetProgrammaticValue(ident);
+    if (v)
+      return v;
+  }
+
+  RecordMap::iterator found = values_.find(ident);
+  if (found != values_.end()) {
+    if (counts_as_used)
+      found->second.used = true;
+    return &found->second.value;
+  }
+
+  // Search in the parent scope.
+  if (const_containing_)
+    return const_containing_->GetValue(ident);
+  if (mutable_containing_)
+    return mutable_containing_->GetValue(ident, counts_as_used);
+  return NULL;
+}
+
+Value* Scope::GetValueForcedToCurrentScope(const base::StringPiece& ident,
+                                           const ParseNode* set_node) {
+  RecordMap::iterator found = values_.find(ident);
+  if (found != values_.end())
+    return &found->second.value;  // Already have in the current scope.
+
+  // Search in the parent scope.
+  if (containing()) {
+    const Value* in_containing = containing()->GetValue(ident);
+    if (in_containing) {
+      // Promote to current scope.
+      return SetValue(ident, *in_containing, set_node);
+    }
+  }
+  return NULL;
+}
+
+const Value* Scope::GetValue(const base::StringPiece& ident) const {
+  RecordMap::const_iterator found = values_.find(ident);
+  if (found != values_.end())
+    return &found->second.value;
+  if (containing())
+    return containing()->GetValue(ident);
+  return NULL;
+}
+
+Value* Scope::SetValue(const base::StringPiece& ident,
+                       const Value& v,
+                       const ParseNode* set_node) {
+  Record& r = values_[ident];  // Clears any existing value.
+  r.value = v;
+  r.value.set_origin(set_node);
+  return &r.value;
+}
+
+bool Scope::AddTemplate(const std::string& name, const FunctionCallNode* decl) {
+  if (GetTemplate(name))
+    return false;
+  templates_[name] = decl;
+  return true;
+}
+
+const FunctionCallNode* Scope::GetTemplate(const std::string& name) const {
+  TemplateMap::const_iterator found = templates_.find(name);
+  if (found != templates_.end())
+    return found->second;
+  if (containing())
+    return containing()->GetTemplate(name);
+  return NULL;
+}
+
+void Scope::MarkUsed(const base::StringPiece& ident) {
+  RecordMap::iterator found = values_.find(ident);
+  if (found == values_.end()) {
+    NOTREACHED();
+    return;
+  }
+  found->second.used = true;
+}
+
+void Scope::MarkUnused(const base::StringPiece& ident) {
+  RecordMap::iterator found = values_.find(ident);
+  if (found == values_.end()) {
+    NOTREACHED();
+    return;
+  }
+  found->second.used = false;
+}
+
+bool Scope::IsSetButUnused(const base::StringPiece& ident) const {
+  RecordMap::const_iterator found = values_.find(ident);
+  if (found != values_.end()) {
+    if (!found->second.used) {
+      return true;
+    }
+  }
+  return false;
+}
+
+bool Scope::CheckForUnusedVars(Err* err) const {
+  for (RecordMap::const_iterator i = values_.begin();
+       i != values_.end(); ++i) {
+    if (!i->second.used) {
+      std::string help = "You set the variable \"" + i->first.as_string() +
+          "\" here and it was unused before it went\nout of scope.";
+
+      const BinaryOpNode* binary = i->second.value.origin()->AsBinaryOp();
+      if (binary) {
+        // Make a nicer error message for normal var sets.
+        *err = Err(binary->left()->GetRange(), "Assignment had no effect.",
+                   help);
+      } else {
+        // This will happen for internally-generated variables.
+        *err = Err(i->second.value.origin(), "Assignment had no effect.", help);
+      }
+      return false;
+    }
+  }
+  return true;
+}
+
+void Scope::GetCurrentScopeValues(KeyValueVector* output) const {
+  output->reserve(values_.size());
+  for (RecordMap::const_iterator i = values_.begin(); i != values_.end(); ++i) {
+    output->push_back(std::make_pair(i->first, i->second.value));
+  }
+}
+
+bool Scope::NonRecursiveMergeTo(Scope* dest,
+                                const ParseNode* node_for_err,
+                                const char* desc_for_err,
+                                Err* err) const {
+  // Values.
+  for (RecordMap::const_iterator i = values_.begin(); i != values_.end(); ++i) {
+    const Value* existing_value = dest->GetValue(i->first);
+    if (existing_value) {
+      // 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 \"" + i->first.as_string() + "\"");
+      err->AppendSubErr(Err(i->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."));
+      return false;
+    }
+    dest->values_[i->first] = i->second;
+  }
+
+  // Target defaults are owning pointers.
+  for (NamedScopeMap::const_iterator i = target_defaults_.begin();
+       i != target_defaults_.end(); ++i) {
+    if (dest->GetTargetDefaults(i->first)) {
+      // TODO(brettw) it would be nice to know the origin of a
+      // set_target_defaults so we can give locations for the colliding 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"
+          "\"" + i->first + "\" 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.");
+      return false;
+    }
+
+    Scope* s = new Scope(settings_);
+    i->second->NonRecursiveMergeTo(s, node_for_err, "<SHOULDN'T HAPPEN>", err);
+    dest->target_defaults_[i->first] = s;
+  }
+
+  // Sources assignment filter.
+  if (sources_assignment_filter_) {
+    if (dest->GetSourcesAssignmentFilter()) {
+      // 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\n"
+          "would clobber the one in your current scope.");
+      return false;
+    }
+    dest->sources_assignment_filter_.reset(
+        new PatternList(*sources_assignment_filter_));
+  }
+
+  // Templates.
+  for (TemplateMap::const_iterator i = templates_.begin();
+       i != templates_.end(); ++i) {
+    const FunctionCallNode* existing_template = dest->GetTemplate(i->first);
+    if (existing_template) {
+      // Rule present in both the source and the dest.
+      std::string desc_string(desc_for_err);
+      *err = Err(node_for_err, "Template collision.",
+          "This " + desc_string + " contains a template \"" + i->first + "\"");
+      err->AppendSubErr(Err(i->second->function(), "defined here.",
+          "Which would clobber the one in your current scope"));
+      err->AppendSubErr(Err(existing_template->function(), "defined here.",
+          "Executing " + desc_string + " should not conflict with anything "
+          "in the current\nscope."));
+      return false;
+    }
+    dest->templates_.insert(*i);
+  }
+
+  return true;
+}
+
+Scope* Scope::MakeTargetDefaults(const std::string& target_type) {
+  if (GetTargetDefaults(target_type))
+    return NULL;
+
+  Scope** dest = &target_defaults_[target_type];
+  if (*dest) {
+    NOTREACHED();  // Already set.
+    return *dest;
+  }
+  *dest = new Scope(settings_);
+  return *dest;
+}
+
+const Scope* Scope::GetTargetDefaults(const std::string& target_type) const {
+  NamedScopeMap::const_iterator found = target_defaults_.find(target_type);
+  if (found != target_defaults_.end())
+    return found->second;
+  if (containing())
+    return containing()->GetTargetDefaults(target_type);
+  return NULL;
+}
+
+const PatternList* Scope::GetSourcesAssignmentFilter() const {
+  if (sources_assignment_filter_)
+    return sources_assignment_filter_.get();
+  if (containing())
+    return containing()->GetSourcesAssignmentFilter();
+  return NULL;
+}
+
+void Scope::SetProcessingBuildConfig() {
+  DCHECK((mode_flags_ & kProcessingBuildConfigFlag) == 0);
+  mode_flags_ |= kProcessingBuildConfigFlag;
+}
+
+void Scope::ClearProcessingBuildConfig() {
+  DCHECK(mode_flags_ & kProcessingBuildConfigFlag);
+  mode_flags_ &= ~(kProcessingBuildConfigFlag);
+}
+
+bool Scope::IsProcessingBuildConfig() const {
+  if (mode_flags_ & kProcessingBuildConfigFlag)
+    return true;
+  if (containing())
+    return containing()->IsProcessingBuildConfig();
+  return false;
+}
+
+void Scope::SetProcessingDefaultBuildConfig() {
+  DCHECK((mode_flags_ & kProcessingDefaultBuildConfigFlag) == 0);
+  mode_flags_ |= kProcessingDefaultBuildConfigFlag;
+}
+
+void Scope::ClearProcessingDefaultBuildConfig() {
+  DCHECK(mode_flags_ & kProcessingDefaultBuildConfigFlag);
+  mode_flags_ &= ~(kProcessingDefaultBuildConfigFlag);
+}
+
+bool Scope::IsProcessingDefaultBuildConfig() const {
+  if (mode_flags_ & kProcessingDefaultBuildConfigFlag)
+    return true;
+  if (containing())
+    return containing()->IsProcessingDefaultBuildConfig();
+  return false;
+}
+
+void Scope::SetProcessingImport() {
+  DCHECK((mode_flags_ & kProcessingImportFlag) == 0);
+  mode_flags_ |= kProcessingImportFlag;
+}
+
+void Scope::ClearProcessingImport() {
+  DCHECK(mode_flags_ & kProcessingImportFlag);
+  mode_flags_ &= ~(kProcessingImportFlag);
+}
+
+bool Scope::IsProcessingImport() const {
+  if (mode_flags_ & kProcessingImportFlag)
+    return true;
+  if (containing())
+    return containing()->IsProcessingImport();
+  return false;
+}
+
+void Scope::SetProperty(const void* key, void* value) {
+  if (!value) {
+    DCHECK(properties_.find(key) != properties_.end());
+    properties_.erase(key);
+  } else {
+    properties_[key] = value;
+  }
+}
+
+void* Scope::GetProperty(const void* key, const Scope** found_on_scope) const {
+  PropertyMap::const_iterator found = properties_.find(key);
+  if (found != properties_.end()) {
+    if (found_on_scope)
+      *found_on_scope = this;
+    return found->second;
+  }
+  if (containing())
+    return containing()->GetProperty(key, found_on_scope);
+  return NULL;
+}
+
+void Scope::AddProvider(ProgrammaticProvider* p) {
+  programmatic_providers_.insert(p);
+}
+
+void Scope::RemoveProvider(ProgrammaticProvider* p) {
+  DCHECK(programmatic_providers_.find(p) != programmatic_providers_.end());
+  programmatic_providers_.erase(p);
+}
diff --git a/tools/gn/scope.h b/tools/gn/scope.h
new file mode 100644
index 0000000..7d0547e
--- /dev/null
+++ b/tools/gn/scope.h
@@ -0,0 +1,260 @@
+// Copyright (c) 2013 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.
+
+#ifndef TOOLS_GN_SCOPE_H_
+#define TOOLS_GN_SCOPE_H_
+
+#include <map>
+#include <set>
+
+#include "base/basictypes.h"
+#include "base/containers/hash_tables.h"
+#include "base/memory/scoped_ptr.h"
+#include "tools/gn/err.h"
+#include "tools/gn/pattern.h"
+#include "tools/gn/value.h"
+
+class FunctionCallNode;
+class ImportManager;
+class ParseNode;
+class Settings;
+class TargetManager;
+
+// Scope for the script execution.
+//
+// Scopes are nested. Writing goes into the toplevel scope, reading checks
+// values resursively down the stack until a match is found or there are no
+// more containing scopes.
+//
+// A containing scope can be const or non-const. The const containing scope is
+// used primarily to refer to the master build config which is shared across
+// many invocations. A const containing scope, however, prevents us from
+// marking variables "used" which prevents us from issuing errors on unused
+// variables. So you should use a non-const containing scope whenever possible.
+class Scope {
+ public:
+  typedef std::vector<std::pair<base::StringPiece, Value> > KeyValueVector;
+
+  // Allows code to provide values for built-in variables. This class will
+  // automatically register itself on construction and deregister itself on
+  // destruction.
+  class ProgrammaticProvider {
+   public:
+    ProgrammaticProvider(Scope* scope) : scope_(scope) {
+      scope_->AddProvider(this);
+    }
+    ~ProgrammaticProvider() {
+      scope_->RemoveProvider(this);
+    }
+
+    // Returns a non-null value if the given value can be programmatically
+    // generated, or NULL if there is none.
+    virtual const Value* GetProgrammaticValue(
+        const base::StringPiece& ident) = 0;
+
+   protected:
+    Scope* scope_;
+  };
+
+  // Creates an empty toplevel scope.
+  Scope(const Settings* settings);
+
+  // Creates a dependent scope.
+  Scope(Scope* parent);
+  Scope(const Scope* parent);
+
+  ~Scope();
+
+  const Settings* settings() const { return settings_; }
+
+  // See the const_/mutable_containing_ var declaraions below. Yes, it's a
+  // bit weird that we can have a const pointer to the "mutable" one.
+  Scope* mutable_containing() { return mutable_containing_; }
+  const Scope* mutable_containing() const { return mutable_containing_; }
+  const Scope* const_containing() const { return const_containing_; }
+  const Scope* containing() const {
+    return mutable_containing_ ? mutable_containing_ : const_containing_;
+  }
+
+  // Returns NULL if there's no such value.
+  //
+  // counts_as_used should be set if the variable is being read in a way that
+  // should count for unused variable checking.
+  const Value* GetValue(const base::StringPiece& ident,
+                        bool counts_as_used);
+  const Value* GetValue(const base::StringPiece& ident) const;
+
+  // Same as GetValue, but if the value exists in a parent scope, we'll copy
+  // it to the current scope. If the return value is non-null, the value is
+  // guaranteed to be set in the current scope. Generatlly this will be used
+  // if the calling code is planning on modifying the value in-place.
+  //
+  // Since this is used when doing read-modifies, we never count this access
+  // as reading the variable, since we assume it will be written to.
+  Value* GetValueForcedToCurrentScope(const base::StringPiece& ident,
+                                      const ParseNode* set_node);
+
+  // The set_node indicates the statement that caused the set, for displaying
+  // errors later. Returns a pointer to the value in the current scope (a copy
+  // is made for storage).
+  Value* SetValue(const base::StringPiece& ident,
+                  const Value& v,
+                  const ParseNode* set_node);
+
+  // Templates associated with this scope. A template can only be set once, so
+  // AddTemplate will fail and return NULL if a rule with that name already
+  // exists. GetTemplate returns NULL if the rule doesn't exist, and it will
+  // check all containing scoped rescursively.
+  bool AddTemplate(const std::string& name, const FunctionCallNode* decl);
+  const FunctionCallNode* GetTemplate(const std::string& name) const;
+
+  // Marks the given identifier as (un)used in the current scope.
+  void MarkUsed(const base::StringPiece& ident);
+  void MarkUnused(const base::StringPiece& ident);
+
+  // Checks to see if the scope has a var set that hasn't been used. This is
+  // called before replacing the var with a different one. It does not check
+  // containing scopes.
+  //
+  // If the identifier is present but hasnn't been used, return true.
+  bool IsSetButUnused(const base::StringPiece& ident) const;
+
+  // Checks the scope to see if any values were set but not used, and fills in
+  // the error and returns false if they were.
+  bool CheckForUnusedVars(Err* err) const;
+
+  // Returns all values set in the current scope, without going to the parent
+  // scopes.
+  void GetCurrentScopeValues(KeyValueVector* output) const;
+
+  // Copies this scope's values into the destination. Values from the
+  // containing scope(s) (normally shadowed into the current one) will not be
+  // copied, neither will the reference to the containing scope (this is why
+  // it's "non-recursive").
+  //
+  // It is an error to merge a variable into a scope that already has something
+  // with that name in scope (meaning in that scope or in any of its containing
+  // scopes). If this happens, the error will be set and the function will
+  // return false.
+  //
+  // This is used in different contexts. When generating the error, the given
+  // parse node will be blamed, and the given desc will be used to describe
+  // the operation that doesn't support doing this. For example, desc_for_err
+  // would be "import" when doing an import, and the error string would say
+  // something like "The import contains...".
+  bool NonRecursiveMergeTo(Scope* dest,
+                           const ParseNode* node_for_err,
+                           const char* desc_for_err,
+                           Err* err) const;
+
+  // Makes an empty scope with the given name. Returns NULL if the name is
+  // already set.
+  Scope* MakeTargetDefaults(const std::string& target_type);
+
+  // Gets the scope associated with the given target name, or null if it hasn't
+  // been set.
+  const Scope* GetTargetDefaults(const std::string& target_type) const;
+
+  // Filter to apply when the sources variable is assigned. May return NULL.
+  const PatternList* GetSourcesAssignmentFilter() const;
+  void set_sources_assignment_filter(
+      scoped_ptr<PatternList> f) {
+    sources_assignment_filter_ = f.Pass();
+  }
+
+  // Indicates if we're currently processing the build configuration file.
+  // This is true when processing the config file for any toolchain. See also
+  // *ProcessingDefaultBuildConfig() below.
+  //
+  // To set or clear the flag, it must currently be in the opposite state in
+  // the current scope. Note that querying the state of the flag recursively
+  // checks all containing scopes until it reaches the top or finds the flag
+  // set.
+  void SetProcessingBuildConfig();
+  void ClearProcessingBuildConfig();
+  bool IsProcessingBuildConfig() const;
+
+  // Indicates we're currently processing the default toolchain's build
+  // configuration file.
+  void SetProcessingDefaultBuildConfig();
+  void ClearProcessingDefaultBuildConfig();
+  bool IsProcessingDefaultBuildConfig() const;
+
+  // Indicates if we're currently processing an import file.
+  //
+  // See SetProcessingBaseConfig for how flags work.
+  void SetProcessingImport();
+  void ClearProcessingImport();
+  bool IsProcessingImport() const;
+
+  // Properties are opaque pointers that code can use to set state on a Scope
+  // that it can retrieve later.
+  //
+  // The key should be a pointer to some use-case-specific object (to avoid
+  // collisions, otherwise it doesn't matter). Memory management is up to the
+  // setter. Setting the value to NULL will delete the property.
+  //
+  // Getting a property recursively searches all scopes, and the optional
+  // |found_on_scope| variable will be filled with the actual scope containing
+  // the key (if the pointer is non-NULL).
+  void SetProperty(const void* key, void* value);
+  void* GetProperty(const void* key, const Scope** found_on_scope) const;
+
+ private:
+  friend class ProgrammaticProvider;
+
+  struct Record {
+    Record() : used(false) {}
+    Record(const Value& v) : used(false), value(v) {}
+
+    bool used;  // Set to true when the variable is used.
+    Value value;
+  };
+
+  void AddProvider(ProgrammaticProvider* p);
+  void RemoveProvider(ProgrammaticProvider* p);
+
+  // Scopes can have no containing scope (both null), a mutable containing
+  // scope, or a const containing scope. The reason is that when we're doing
+  // a new target, we want to refer to the base_config scope which will be read
+  // by multiple threads at the same time, so we REALLY want it to be const.
+  // When you jsut do a nested {}, however, we sometimes want to be able to
+  // change things (especially marking unused vars).
+  const Scope* const_containing_;
+  Scope* mutable_containing_;
+
+  const Settings* settings_;
+
+  // Bits set for different modes. See the flag definitions in the .cc file
+  // for more.
+  unsigned mode_flags_;
+
+  typedef base::hash_map<base::StringPiece, Record> RecordMap;
+  RecordMap values_;
+
+  // Owning pointers. Note that this can't use string pieces since the names
+  // are constructed from Values which might be deallocated before this goes
+  // out of scope.
+  typedef base::hash_map<std::string, Scope*> NamedScopeMap;
+  NamedScopeMap target_defaults_;
+
+  // Null indicates not set and that we should fallback to the containing
+  // scope's filter.
+  scoped_ptr<PatternList> sources_assignment_filter_;
+
+  // Non-owning pointers, the function calls are owned by the input file which
+  // should be kept around by the input file manager.
+  typedef std::map<std::string, const FunctionCallNode*> TemplateMap;
+  TemplateMap templates_;
+
+  typedef std::map<const void*, void*> PropertyMap;
+  PropertyMap properties_;
+
+  typedef std::set<ProgrammaticProvider*> ProviderSet;
+  ProviderSet programmatic_providers_;
+
+  DISALLOW_COPY_AND_ASSIGN(Scope);
+};
+
+#endif  // TOOLS_GN_SCOPE_H_
diff --git a/tools/gn/scope_per_file_provider.cc b/tools/gn/scope_per_file_provider.cc
new file mode 100644
index 0000000..1799d9e
--- /dev/null
+++ b/tools/gn/scope_per_file_provider.cc
@@ -0,0 +1,186 @@
+// Copyright (c) 2013 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/scope_per_file_provider.h"
+
+#include "tools/gn/filesystem_utils.h"
+#include "tools/gn/settings.h"
+#include "tools/gn/source_file.h"
+#include "tools/gn/toolchain_manager.h"
+#include "tools/gn/value.h"
+
+const char* ScopePerFileProvider::kDefaultToolchain =
+    "default_toolchain";
+const char* ScopePerFileProvider::kPythonPath =
+    "python_path";
+const char* ScopePerFileProvider::kToolchain =
+    "toolchain";
+const char* ScopePerFileProvider::kRootOutputDirName =
+    "root_output_dir";
+const char* ScopePerFileProvider::kRootGenDirName =
+    "root_gen_dir";
+const char* ScopePerFileProvider::kTargetOutputDirName =
+    "target_output_dir";
+const char* ScopePerFileProvider::kTargetGenDirName =
+    "target_gen_dir";
+const char* ScopePerFileProvider::kRelativeRootOutputDirName =
+    "relative_root_output_dir";
+const char* ScopePerFileProvider::kRelativeRootGenDirName =
+    "relative_root_gen_dir";
+const char* ScopePerFileProvider::kRelativeTargetOutputDirName =
+    "relative_target_output_dir";
+const char* ScopePerFileProvider::kRelativeTargetGenDirName =
+    "relative_target_gen_dir";
+
+ScopePerFileProvider::ScopePerFileProvider(Scope* scope,
+                                           const SourceFile& source_file)
+    : ProgrammaticProvider(scope),
+      source_file_(source_file) {
+}
+
+ScopePerFileProvider::~ScopePerFileProvider() {
+}
+
+const Value* ScopePerFileProvider::GetProgrammaticValue(
+    const base::StringPiece& ident) {
+  if (ident == kDefaultToolchain)
+    return GetDefaultToolchain();
+  if (ident == kPythonPath)
+    return GetPythonPath();
+
+  if (ident == kTargetOutputDirName)
+    return GetTargetOutputDir();
+  if (ident == kTargetGenDirName)
+    return GetTargetGenDir();
+
+  if (ident == kRelativeRootOutputDirName)
+    return GetRelativeRootOutputDir();
+  if (ident == kRelativeRootGenDirName)
+    return GetRelativeRootGenDir();
+  if (ident == kRelativeTargetOutputDirName)
+    return GetRelativeTargetOutputDir();
+  if (ident == kRelativeTargetGenDirName)
+    return GetRelativeTargetGenDir();
+  return NULL;
+}
+
+// static
+Value ScopePerFileProvider::GetRootOutputDir(const Settings* settings) {
+  return Value(NULL, GetRootOutputDirWithNoLastSlash(settings));
+}
+
+// static
+Value ScopePerFileProvider::GetRootGenDir(const Settings* settings) {
+  return Value(NULL, GetRootGenDirWithNoLastSlash(settings));
+}
+
+const Value* ScopePerFileProvider::GetDefaultToolchain() {
+  if (!default_toolchain_) {
+    const ToolchainManager& toolchain_manager =
+        scope_->settings()->build_settings()->toolchain_manager();
+    default_toolchain_.reset(new Value(NULL,
+        toolchain_manager.GetDefaultToolchainUnlocked().GetUserVisibleName(
+            false)));
+  }
+  return default_toolchain_.get();
+}
+
+const Value* ScopePerFileProvider::GetPythonPath() {
+  if (!python_path_) {
+    python_path_.reset(new Value(NULL,
+        FilePathToUTF8(scope_->settings()->build_settings()->python_path())));
+  }
+  return python_path_.get();
+}
+
+const Value* ScopePerFileProvider::GetToolchain() {
+  if (!toolchain_) {
+    toolchain_.reset(new Value(NULL,
+        scope_->settings()->toolchain()->label().GetUserVisibleName(false)));
+  }
+  return toolchain_.get();
+}
+
+const Value* ScopePerFileProvider::GetTargetOutputDir() {
+  if (!target_output_dir_) {
+    target_output_dir_.reset(new Value(NULL,
+        GetRootOutputDirWithNoLastSlash(scope_->settings()) +
+        GetFileDirWithNoLastSlash()));
+  }
+  return target_output_dir_.get();
+}
+
+const Value* ScopePerFileProvider::GetTargetGenDir() {
+  if (!target_output_dir_) {
+    target_gen_dir_.reset(new Value(NULL,
+        GetRootGenDirWithNoLastSlash(scope_->settings()) +
+        GetFileDirWithNoLastSlash()));
+  }
+  return target_gen_dir_.get();
+}
+
+const Value* ScopePerFileProvider::GetRelativeRootOutputDir() {
+  if (!relative_root_output_dir_) {
+    relative_root_output_dir_.reset(new Value(NULL,
+        GetRelativeRootWithNoLastSlash() +
+        GetRootOutputDirWithNoLastSlash(scope_->settings())));
+  }
+  return relative_root_output_dir_.get();
+}
+
+const Value* ScopePerFileProvider::GetRelativeRootGenDir() {
+  if (!relative_root_gen_dir_) {
+    relative_root_gen_dir_.reset(new Value(NULL,
+        GetRelativeRootWithNoLastSlash() +
+        GetRootGenDirWithNoLastSlash(scope_->settings())));
+  }
+  return relative_root_gen_dir_.get();
+}
+
+const Value* ScopePerFileProvider::GetRelativeTargetOutputDir() {
+  if (!relative_target_output_dir_) {
+    relative_target_output_dir_.reset(new Value(NULL,
+        GetRelativeRootWithNoLastSlash() +
+        GetRootOutputDirWithNoLastSlash(scope_->settings()) + "obj/" +
+        GetFileDirWithNoLastSlash()));
+  }
+  return relative_target_output_dir_.get();
+}
+
+const Value* ScopePerFileProvider::GetRelativeTargetGenDir() {
+  if (!relative_target_gen_dir_) {
+    relative_target_gen_dir_.reset(new Value(NULL,
+        GetRelativeRootWithNoLastSlash() +
+        GetRootGenDirWithNoLastSlash(scope_->settings()) +
+        GetFileDirWithNoLastSlash()));
+  }
+  return relative_target_gen_dir_.get();
+}
+
+// static
+std::string ScopePerFileProvider::GetRootOutputDirWithNoLastSlash(
+    const Settings* settings) {
+  const std::string& output_dir =
+      settings->build_settings()->build_dir().value();
+  CHECK(!output_dir.empty());
+  return output_dir.substr(0, output_dir.size() - 1);
+}
+
+// static
+std::string ScopePerFileProvider::GetRootGenDirWithNoLastSlash(
+    const Settings* settings) {
+  return GetRootOutputDirWithNoLastSlash(settings) + "/gen";
+}
+
+std::string ScopePerFileProvider::GetFileDirWithNoLastSlash() const {
+  std::string dir_value = source_file_.GetDir().value();
+  return dir_value.substr(0, dir_value.size() - 1);
+}
+
+std::string ScopePerFileProvider::GetRelativeRootWithNoLastSlash() const {
+  std::string inverted = InvertDir(source_file_.GetDir());
+  if (inverted.empty())
+    return ".";
+  return inverted.substr(0, inverted.size() - 1);
+}
diff --git a/tools/gn/scope_per_file_provider.h b/tools/gn/scope_per_file_provider.h
new file mode 100644
index 0000000..1edb769
--- /dev/null
+++ b/tools/gn/scope_per_file_provider.h
@@ -0,0 +1,79 @@
+// Copyright (c) 2013 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.
+
+#ifndef TOOLS_GN_SCOPE_PER_FILE_PROVIDER_H_
+#define TOOLS_GN_SCOPE_PER_FILE_PROVIDER_H_
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "tools/gn/scope.h"
+#include "tools/gn/source_file.h"
+
+// ProgrammaticProvider for a scope to provide it with per-file built-in
+// variable support.
+class ScopePerFileProvider : public Scope::ProgrammaticProvider {
+ public:
+  ScopePerFileProvider(Scope* scope, const SourceFile& source_file);
+  virtual ~ScopePerFileProvider();
+
+  // ProgrammaticProvider implementation.
+  virtual const Value* GetProgrammaticValue(
+      const base::StringPiece& ident) OVERRIDE;
+
+  // Returns the value to expose to script for the given thing. These values
+  // are acually set globally, but are put here so we can keep all logic
+  // for converting paths to built-in values in this one file.
+  static Value GetRootOutputDir(const Settings* settings);
+  static Value GetRootGenDir(const Settings* settings);
+
+  // Variable names. These two should be set globally independent of the file
+  // being processed.
+  static const char* kRootOutputDirName;
+  static const char* kRootGenDirName;
+
+  // Variable names. These are provided by this class as needed.
+  static const char* kDefaultToolchain;
+  static const char* kPythonPath;
+  static const char* kToolchain;
+  static const char* kTargetOutputDirName;
+  static const char* kTargetGenDirName;
+  static const char* kRelativeRootOutputDirName;
+  static const char* kRelativeRootGenDirName;
+  static const char* kRelativeTargetOutputDirName;
+  static const char* kRelativeTargetGenDirName;
+
+ private:
+  const Value* GetDefaultToolchain();
+  const Value* GetPythonPath();
+  const Value* GetToolchain();
+  const Value* GetTargetOutputDir();
+  const Value* GetTargetGenDir();
+  const Value* GetRelativeRootOutputDir();
+  const Value* GetRelativeRootGenDir();
+  const Value* GetRelativeTargetOutputDir();
+  const Value* GetRelativeTargetGenDir();
+
+  static std::string GetRootOutputDirWithNoLastSlash(const Settings* settings);
+  static std::string GetRootGenDirWithNoLastSlash(const Settings* settings);
+
+  std::string GetFileDirWithNoLastSlash() const;
+  std::string GetRelativeRootWithNoLastSlash() const;
+
+  SourceFile source_file_;
+
+  // All values are lazily created.
+  scoped_ptr<Value> default_toolchain_;
+  scoped_ptr<Value> python_path_;
+  scoped_ptr<Value> toolchain_;
+  scoped_ptr<Value> target_output_dir_;
+  scoped_ptr<Value> target_gen_dir_;
+  scoped_ptr<Value> relative_root_output_dir_;
+  scoped_ptr<Value> relative_root_gen_dir_;
+  scoped_ptr<Value> relative_target_output_dir_;
+  scoped_ptr<Value> relative_target_gen_dir_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopePerFileProvider);
+};
+
+#endif  // TOOLS_GN_SCOPE_PER_FILE_PROVIDER_H_
diff --git a/tools/gn/secondary/base/BUILD.gn b/tools/gn/secondary/base/BUILD.gn
new file mode 100644
index 0000000..2286b19
--- /dev/null
+++ b/tools/gn/secondary/base/BUILD.gn
@@ -0,0 +1,919 @@
+# found in the LICENSE file.

+

+component("base") {

+  sources = [

+    "../build/build_config.h",

+    "third_party/dmg_fp/dmg_fp.h",

+    "third_party/dmg_fp/g_fmt.cc",

+    "third_party/dmg_fp/dtoa_wrapper.cc",

+    "third_party/icu/icu_utf.cc",

+    "third_party/icu/icu_utf.h",

+    "third_party/nspr/prcpucfg.h",

+    "third_party/nspr/prcpucfg_freebsd.h",

+    "third_party/nspr/prcpucfg_linux.h",

+    "third_party/nspr/prcpucfg_mac.h",

+    "third_party/nspr/prcpucfg_nacl.h",

+    "third_party/nspr/prcpucfg_openbsd.h",

+    "third_party/nspr/prcpucfg_solaris.h",

+    "third_party/nspr/prcpucfg_win.h",

+    "third_party/nspr/prtime.cc",

+    "third_party/nspr/prtime.h",

+    "third_party/nspr/prtypes.h",

+    "third_party/xdg_mime/xdgmime.h",

+    "allocator/allocator_extension.cc",

+    "allocator/allocator_extension.h",

+    "allocator/type_profiler_control.cc",

+    "allocator/type_profiler_control.h",

+    "android/activity_status.cc",

+    "android/activity_status.h",

+    "android/base_jni_registrar.cc",

+    "android/base_jni_registrar.h",

+    "android/build_info.cc",

+    "android/build_info.h",

+    "android/cpu_features.cc",

+    "android/fifo_utils.cc",

+    "android/fifo_utils.h",

+    "android/important_file_writer_android.cc",

+    "android/important_file_writer_android.h",

+    "android/scoped_java_ref.cc",

+    "android/scoped_java_ref.h",

+    "android/jni_android.cc",

+    "android/jni_android.h",

+    "android/jni_array.cc",

+    "android/jni_array.h",

+    "android/jni_helper.cc",

+    "android/jni_helper.h",

+    "android/jni_registrar.cc",

+    "android/jni_registrar.h",

+    "android/jni_string.cc",

+    "android/jni_string.h",

+    "android/memory_pressure_listener_android.cc",

+    "android/memory_pressure_listener_android.h",

+    "android/path_service_android.cc",

+    "android/path_service_android.h",

+    "android/path_utils.cc",

+    "android/path_utils.h",

+    "android/sys_utils.cc",

+    "android/sys_utils.h",

+    "android/thread_utils.h",

+    "at_exit.cc",

+    "at_exit.h",

+    "atomic_ref_count.h",

+    "atomic_sequence_num.h",

+    "atomicops.h",

+    "atomicops_internals_gcc.h",

+    "atomicops_internals_mac.h",

+    "atomicops_internals_tsan.h",

+    "atomicops_internals_x86_gcc.cc",

+    "atomicops_internals_x86_gcc.h",

+    "atomicops_internals_x86_msvc.h",

+    "base_export.h",

+    "base_paths.cc",

+    "base_paths.h",

+    "base_paths_android.cc",

+    "base_paths_android.h",

+    "base_paths_mac.h",

+    "base_paths_mac.mm",

+    "base_paths_posix.cc",

+    "base_paths_posix.h",

+    "base_paths_win.cc",

+    "base_paths_win.h",

+    "base_switches.h",

+    "base64.cc",

+    "base64.h",

+    "basictypes.h",

+    "bind.h",

+    "bind_helpers.cc",

+    "bind_helpers.h",

+    "bind_internal.h",

+    "bind_internal_win.h",

+    "bits.h",

+    "build_time.cc",

+    "build_time.h",

+    "callback.h",

+    "callback_helpers.h",

+    "callback_internal.cc",

+    "callback_internal.h",

+    "cancelable_callback.h",

+    "chromeos/chromeos_version.cc",

+    "chromeos/chromeos_version.h",

+    "command_line.cc",

+    "command_line.h",

+    "compiler_specific.h",

+    "containers/hash_tables.h",

+    "containers/linked_list.h",

+    "containers/mru_cache.h",

+    "containers/small_map.h",

+    "containers/stack_container.h",

+    "cpu.cc",

+    "cpu.h",

+    "critical_closure.h",

+    "critical_closure_ios.mm",

+    "debug/alias.cc",

+    "debug/alias.h",

+    "debug/crash_logging.cc",

+    "debug/crash_logging.h",

+    "debug/debug_on_start_win.cc",

+    "debug/debug_on_start_win.h",

+    "debug/debugger.cc",

+    "debug/debugger.h",

+    "debug/debugger_posix.cc",

+    "debug/debugger_win.cc",

+    # This file depends on files from the "allocator" target,

+    # but this target does not depend on "allocator" (see

+    # allocator.gyp for details).

+    "debug/leak_annotations.h",

+    "debug/leak_tracker.h",

+    "debug/proc_maps_linux.cc",

+    "debug/proc_maps_linux.h",

+    "debug/profiler.cc",

+    "debug/profiler.h",

+    "debug/stack_trace.cc",

+    "debug/stack_trace.h",

+    "debug/stack_trace_android.cc",

+    "debug/stack_trace_ios.mm",

+    "debug/stack_trace_posix.cc",

+    "debug/stack_trace_win.cc",

+    "debug/trace_event.h",

+    "debug/trace_event_android.cc",

+    "debug/trace_event_impl.cc",

+    "debug/trace_event_impl.h",

+    "debug/trace_event_impl_constants.cc",

+    "debug/trace_event_win.cc",

+    "deferred_sequenced_task_runner.cc",

+    "deferred_sequenced_task_runner.h",

+    "environment.cc",

+    "environment.h",

+    "file_descriptor_posix.h",

+    "file_util.cc",

+    "file_util.h",

+    "file_util_android.cc",

+    "file_util_linux.cc",

+    "file_util_mac.mm",

+    "file_util_posix.cc",

+    "file_util_win.cc",

+    "file_version_info.h",

+    "file_version_info_mac.h",

+    "file_version_info_mac.mm",

+    "file_version_info_win.cc",

+    "file_version_info_win.h",

+    "files/dir_reader_fallback.h",

+    "files/dir_reader_linux.h",

+    "files/dir_reader_posix.h",

+    "files/file_enumerator.cc",

+    "files/file_enumerator.h",

+    "files/file_enumerator_posix.cc",

+    "files/file_enumerator_win.cc",

+    "files/file_path.cc",

+    "files/file_path.h",

+    "files/file_path_constants.cc",

+    "files/file_path_watcher.cc",

+    "files/file_path_watcher.h",

+    "files/file_path_watcher_kqueue.cc",

+    "files/file_path_watcher_linux.cc",

+    "files/file_path_watcher_stub.cc",

+    "files/file_path_watcher_win.cc",

+    "files/file_util_proxy.cc",

+    "files/file_util_proxy.h",

+    "files/important_file_writer.h",

+    "files/important_file_writer.cc",

+    "files/memory_mapped_file.cc",

+    "files/memory_mapped_file.h",

+    "files/memory_mapped_file_posix.cc",

+    "files/memory_mapped_file_win.cc",

+    "files/scoped_temp_dir.cc",

+    "files/scoped_temp_dir.h",

+    "float_util.h",

+    "format_macros.h",

+    "gtest_prod_util.h",

+    "guid.cc",

+    "guid.h",

+    "guid_posix.cc",

+    "guid_win.cc",

+    "hash.cc",

+    "hash.h",

+    "id_map.h",

+    "ini_parser.cc",

+    "ini_parser.h",

+    "ios/device_util.h",

+    "ios/device_util.mm",

+    "ios/ios_util.h",

+    "ios/ios_util.mm",

+    "ios/scoped_critical_action.h",

+    "ios/scoped_critical_action.mm",

+    "json/json_file_value_serializer.cc",

+    "json/json_file_value_serializer.h",

+    "json/json_parser.cc",

+    "json/json_parser.h",

+    "json/json_reader.cc",

+    "json/json_reader.h",

+    "json/json_string_value_serializer.cc",

+    "json/json_string_value_serializer.h",

+    "json/json_value_converter.h",

+    "json/json_writer.cc",

+    "json/json_writer.h",

+    "json/string_escape.cc",

+    "json/string_escape.h",

+    "lazy_instance.cc",

+    "lazy_instance.h",

+    "location.cc",

+    "location.h",

+    "logging.cc",

+    "logging.h",

+    "logging_win.cc",

+    "logging_win.h",

+    "mac/authorization_util.h",

+    "mac/authorization_util.mm",

+    "mac/bind_objc_block.h",

+    "mac/bundle_locations.h",

+    "mac/bundle_locations.mm",

+    "mac/cocoa_protocols.h",

+    "mac/foundation_util.h",

+    "mac/foundation_util.mm",

+    "mac/launch_services_util.cc",

+    "mac/launch_services_util.h",

+    "mac/launchd.cc",

+    "mac/launchd.h",

+    "mac/libdispatch_task_runner.cc",

+    "mac/libdispatch_task_runner.h",

+    "mac/mac_logging.h",

+    "mac/mac_logging.cc",

+    "mac/mac_util.h",

+    "mac/mac_util.mm",

+    "mac/objc_property_releaser.h",

+    "mac/objc_property_releaser.mm",

+    "mac/os_crash_dumps.cc",

+    "mac/os_crash_dumps.h",

+    "mac/scoped_aedesc.h",

+    "mac/scoped_authorizationref.h",

+    "mac/scoped_block.h",

+    "mac/scoped_cftyperef.h",

+    "mac/scoped_ioobject.h",

+    "mac/scoped_ioplugininterface.h",

+    "mac/scoped_launch_data.h",

+    "mac/scoped_mach_port.cc",

+    "mac/scoped_mach_port.h",

+    "mac/scoped_nsautorelease_pool.h",

+    "mac/scoped_nsautorelease_pool.mm",

+    "mac/scoped_nsexception_enabler.h",

+    "mac/scoped_nsexception_enabler.mm",

+    "mac/scoped_nsobject.h",

+    "mac/scoped_sending_event.h",

+    "mac/scoped_sending_event.mm",

+    "mac/sdk_forward_declarations.h",

+    "memory/aligned_memory.cc",

+    "memory/aligned_memory.h",

+    "memory/discardable_memory.cc",

+    "memory/discardable_memory.h",

+    "memory/discardable_memory_android.cc",

+    "memory/discardable_memory_mac.cc",

+    "memory/linked_ptr.h",

+    "memory/manual_constructor.h",

+    "memory/memory_pressure_listener.cc",

+    "memory/memory_pressure_listener.h",

+    "memory/raw_scoped_refptr_mismatch_checker.h",

+    "memory/ref_counted.cc",

+    "memory/ref_counted.h",

+    "memory/ref_counted_delete_on_message_loop.h",

+    "memory/ref_counted_memory.cc",

+    "memory/ref_counted_memory.h",

+    "memory/scoped_handle.h",

+    "memory/scoped_open_process.h",

+    "memory/scoped_policy.h",

+    "memory/scoped_ptr.h",

+    "memory/scoped_vector.h",

+    "memory/shared_memory.h",

+    "memory/shared_memory_android.cc",

+    "memory/shared_memory_nacl.cc",

+    "memory/shared_memory_posix.cc",

+    "memory/shared_memory_win.cc",

+    "memory/singleton.cc",

+    "memory/singleton.h",

+    "memory/weak_ptr.cc",

+    "memory/weak_ptr.h",

+    "message_loop/message_loop.cc",

+    "message_loop/message_loop.h",

+    "message_loop/message_loop_proxy.cc",

+    "message_loop/message_loop_proxy.h",

+    "message_loop/message_loop_proxy_impl.cc",

+    "message_loop/message_loop_proxy_impl.h",

+    "message_loop/message_pump.cc",

+    "message_loop/message_pump.h",

+    "message_loop/message_pump_android.cc",

+    "message_loop/message_pump_android.h",

+    "message_loop/message_pump_default.cc",

+    "message_loop/message_pump_default.h",

+    "message_loop/message_pump_ozone.cc",

+    "message_loop/message_pump_ozone.h",

+    "message_loop/message_pump_win.cc",

+    "message_loop/message_pump_win.h",

+    "metrics/sample_map.cc",

+    "metrics/sample_map.h",

+    "metrics/sample_vector.cc",

+    "metrics/sample_vector.h",

+    "metrics/bucket_ranges.cc",

+    "metrics/bucket_ranges.h",

+    "metrics/histogram.cc",

+    "metrics/histogram.h",

+    "metrics/histogram_base.cc",

+    "metrics/histogram_base.h",

+    "metrics/histogram_flattener.h",

+    "metrics/histogram_samples.cc",

+    "metrics/histogram_samples.h",

+    "metrics/histogram_snapshot_manager.cc",

+    "metrics/histogram_snapshot_manager.h",

+    "metrics/sparse_histogram.cc",

+    "metrics/sparse_histogram.h",

+    "metrics/statistics_recorder.cc",

+    "metrics/statistics_recorder.h",

+    "metrics/stats_counters.cc",

+    "metrics/stats_counters.h",

+    "metrics/stats_table.cc",

+    "metrics/stats_table.h",

+    "move.h",

+    "native_library.h",

+    "native_library_mac.mm",

+    "native_library_posix.cc",

+    "native_library_win.cc",

+    "nix/mime_util_xdg.cc",

+    "nix/mime_util_xdg.h",

+    "nix/xdg_util.cc",

+    "nix/xdg_util.h",

+    "observer_list.h",

+    "observer_list_threadsafe.h",

+    "os_compat_android.cc",

+    "os_compat_android.h",

+    "os_compat_nacl.cc",

+    "os_compat_nacl.h",

+    "path_service.cc",

+    "path_service.h",

+    "pending_task.cc",

+    "pending_task.h",

+    "pickle.cc",

+    "pickle.h",

+    "platform_file.cc",

+    "platform_file.h",

+    "platform_file_posix.cc",

+    "platform_file_win.cc",

+    "port.h",

+    "posix/eintr_wrapper.h",

+    "posix/global_descriptors.cc",

+    "posix/global_descriptors.h",

+    "posix/unix_domain_socket_linux.cc",

+    "posix/unix_domain_socket_linux.h",

+    "power_monitor/power_monitor.cc",

+    "power_monitor/power_monitor.h",

+    "power_monitor/power_monitor_android.cc",

+    "power_monitor/power_monitor_android.h",

+    "power_monitor/power_monitor_ios.mm",

+    "power_monitor/power_monitor_mac.mm",

+    "power_monitor/power_monitor_posix.cc",

+    "power_monitor/power_monitor_win.cc",

+    "power_monitor/power_observer.h",

+    "process.h",

+    "process_info.h",

+    "process_info_mac.cc",

+    "process_info_win.cc",

+    "process_linux.cc",

+    "process_posix.cc",

+    "process_util.h",

+    "process_win.cc",

+    "process/internal_linux.cc",

+    "process/internal_linux.h",

+    "process/kill.cc",

+    "process/kill.h",

+    "process/kill_mac.cc",

+    "process/kill_posix.cc",

+    "process/kill_win.cc",

+    "process/launch.h",

+    "process/launch_ios.cc",

+    "process/launch_mac.cc",

+    "process/launch_posix.cc",

+    "process/launch_win.cc",

+    "process/memory.h",

+    "process/memory_linux.cc",

+    "process/memory_mac.mm",

+    "process/memory_win.cc",

+    "process/process_handle_freebsd.cc",

+    "process/process_handle_linux.cc",

+    "process/process_handle_mac.cc",

+    "process/process_handle_openbsd.cc",

+    "process/process_handle_posix.cc",

+    "process/process_handle_win.cc",

+    "process/process_iterator.cc",

+    "process/process_iterator.h",

+    "process/process_iterator_freebsd.cc",

+    "process/process_iterator_linux.cc",

+    "process/process_iterator_mac.cc",

+    "process/process_iterator_openbsd.cc",

+    "process/process_iterator_win.cc",

+    "process/process_metrics.h",

+    "process/process_metrics_freebsd.cc",

+    "process/process_metrics_ios.cc",

+    "process/process_metrics_linux.cc",

+    "process/process_metrics_mac.cc",

+    "process/process_metrics_openbsd.cc",

+    "process/process_metrics_posix.cc",

+    "process/process_metrics_win.cc",

+    "profiler/scoped_profile.cc",

+    "profiler/scoped_profile.h",

+    "profiler/alternate_timer.cc",

+    "profiler/alternate_timer.h",

+    "profiler/tracked_time.cc",

+    "profiler/tracked_time.h",

+    "rand_util.cc",

+    "rand_util.h",

+    "rand_util_nacl.cc",

+    "rand_util_posix.cc",

+    "rand_util_win.cc",

+    "run_loop.cc",

+    "run_loop.h",

+    "safe_numerics.h",

+    "safe_strerror_posix.cc",

+    "safe_strerror_posix.h",

+    "scoped_native_library.cc",

+    "scoped_native_library.h",

+    "sequence_checker.h",

+    "sequence_checker_impl.cc",

+    "sequence_checker_impl.h",

+    "sequenced_task_runner.cc",

+    "sequenced_task_runner.h",

+    "sequenced_task_runner_helpers.h",

+    "sha1.h",

+    "sha1_portable.cc",

+    "sha1_win.cc",

+    "single_thread_task_runner.h",

+    "stl_util.h",

+    "strings/latin1_string_conversions.cc",

+    "strings/latin1_string_conversions.h",

+    "strings/nullable_string16.cc",

+    "strings/nullable_string16.h",

+    "strings/string16.cc",

+    "strings/string16.h",

+    "strings/string_number_conversions.cc",

+    "strings/string_split.cc",

+    "strings/string_split.h",

+    "strings/string_number_conversions.h",

+    "strings/string_piece.cc",

+    "strings/string_piece.h",

+    "strings/string_tokenizer.h",

+    "strings/string_util.cc",

+    "strings/string_util.h",

+    "strings/string_util_constants.cc",

+    "strings/string_util_posix.h",

+    "strings/string_util_win.h",

+    "strings/stringize_macros.h",

+    "strings/stringprintf.cc",

+    "strings/stringprintf.h",

+    "strings/sys_string_conversions.h",

+    "strings/sys_string_conversions_mac.mm",

+    "strings/sys_string_conversions_posix.cc",

+    "strings/sys_string_conversions_win.cc",

+    "strings/utf_offset_string_conversions.cc",

+    "strings/utf_offset_string_conversions.h",

+    "strings/utf_string_conversion_utils.cc",

+    "strings/utf_string_conversion_utils.h",

+    "strings/utf_string_conversions.cc",

+    "strings/utf_string_conversions.h",

+    "supports_user_data.cc",

+    "supports_user_data.h",

+    "synchronization/cancellation_flag.cc",

+    "synchronization/cancellation_flag.h",

+    "synchronization/condition_variable.h",

+    "synchronization/condition_variable_posix.cc",

+    "synchronization/condition_variable_win.cc",

+    "synchronization/lock.cc",

+    "synchronization/lock.h",

+    "synchronization/lock_impl.h",

+    "synchronization/lock_impl_posix.cc",

+    "synchronization/lock_impl_win.cc",

+    "synchronization/spin_wait.h",

+    "synchronization/waitable_event.h",

+    "synchronization/waitable_event_posix.cc",

+    "synchronization/waitable_event_watcher.h",

+    "synchronization/waitable_event_watcher_posix.cc",

+    "synchronization/waitable_event_watcher_win.cc",

+    "synchronization/waitable_event_win.cc",

+    "system_monitor/system_monitor.cc",

+    "system_monitor/system_monitor.h",

+    "sys_byteorder.h",

+    "sys_info.cc",

+    "sys_info.h",

+    "sys_info_android.cc",

+    "sys_info_chromeos.cc",

+    "sys_info_freebsd.cc",

+    "sys_info_ios.mm",

+    "sys_info_linux.cc",

+    "sys_info_mac.cc",

+    "sys_info_openbsd.cc",

+    "sys_info_posix.cc",

+    "sys_info_win.cc",

+    "task_runner.cc",

+    "task_runner.h",

+    "task_runner_util.h",

+    "template_util.h",

+    "thread_task_runner_handle.cc",

+    "thread_task_runner_handle.h",

+    "threading/non_thread_safe.h",

+    "threading/non_thread_safe_impl.cc",

+    "threading/non_thread_safe_impl.h",

+    "threading/platform_thread.h",

+    "threading/platform_thread_android.cc",

+    "threading/platform_thread_linux.cc",

+    "threading/platform_thread_mac.mm",

+    "threading/platform_thread_posix.cc",

+    "threading/platform_thread_win.cc",

+    "threading/post_task_and_reply_impl.cc",

+    "threading/post_task_and_reply_impl.h",

+    "threading/sequenced_worker_pool.cc",

+    "threading/sequenced_worker_pool.h",

+    "threading/simple_thread.cc",

+    "threading/simple_thread.h",

+    "threading/thread.cc",

+    "threading/thread.h",

+    "threading/thread_checker.h",

+    "threading/thread_checker_impl.cc",

+    "threading/thread_checker_impl.h",

+    "threading/thread_collision_warner.cc",

+    "threading/thread_collision_warner.h",

+    "threading/thread_id_name_manager.cc",

+    "threading/thread_id_name_manager.h",

+    "threading/thread_local.h",

+    "threading/thread_local_posix.cc",

+    "threading/thread_local_storage.h",

+    "threading/thread_local_storage_posix.cc",

+    "threading/thread_local_storage_win.cc",

+    "threading/thread_local_win.cc",

+    "threading/thread_restrictions.h",

+    "threading/thread_restrictions.cc",

+    "threading/watchdog.cc",

+    "threading/watchdog.h",

+    "threading/worker_pool.h",

+    "threading/worker_pool.cc",

+    "threading/worker_pool_posix.cc",

+    "threading/worker_pool_posix.h",

+    "threading/worker_pool_win.cc",

+    "time/clock.cc",

+    "time/clock.h",

+    "time/default_clock.cc",

+    "time/default_clock.h",

+    "time/default_tick_clock.cc",

+    "time/default_tick_clock.h",

+    "time/tick_clock.cc",

+    "time/tick_clock.h",

+    "time/time.cc",

+    "time/time.h",

+    "time/time_mac.cc",

+    "time/time_posix.cc",

+    "time/time_win.cc",

+    "timer/hi_res_timer_manager_posix.cc",

+    "timer/hi_res_timer_manager_win.cc",

+    "timer/hi_res_timer_manager.h",

+    "timer/timer.cc",

+    "timer/timer.h",

+    "tracked_objects.cc",

+    "tracked_objects.h",

+    "tracking_info.cc",

+    "tracking_info.h",

+    "tuple.h",

+    "values.cc",

+    "values.h",

+    "value_conversions.cc",

+    "value_conversions.h",

+    "version.cc",

+    "version.h",

+    "vlog.cc",

+    "vlog.h",

+    "win/enum_variant.cc",

+    "win/enum_variant.h",

+    "win/event_trace_consumer.h",

+    "win/event_trace_controller.cc",

+    "win/event_trace_controller.h",

+    "win/event_trace_provider.cc",

+    "win/event_trace_provider.h",

+    "win/i18n.cc",

+    "win/i18n.h",

+    "win/iat_patch_function.cc",

+    "win/iat_patch_function.h",

+    "win/iunknown_impl.cc",

+    "win/iunknown_impl.h",

+    "win/message_window.cc",

+    "win/message_window.h",

+    "win/metro.cc",

+    "win/metro.h",

+    "win/object_watcher.cc",

+    "win/object_watcher.h",

+    "win/registry.cc",

+    "win/registry.h",

+    "win/resource_util.cc",

+    "win/resource_util.h",

+    "win/sampling_profiler.cc",

+    "win/sampling_profiler.h",

+    "win/scoped_bstr.cc",

+    "win/scoped_bstr.h",

+    "win/scoped_co_mem.h",

+    "win/scoped_com_initializer.h",

+    "win/scoped_comptr.h",

+    "win/scoped_gdi_object.h",

+    "win/scoped_handle.cc",

+    "win/scoped_handle.h",

+    "win/scoped_hdc.h",

+    "win/scoped_hglobal.h",

+    "win/scoped_process_information.cc",

+    "win/scoped_process_information.h",

+    "win/scoped_propvariant.h",

+    "win/scoped_select_object.h",

+    "win/scoped_variant.cc",

+    "win/scoped_variant.h",

+    "win/shortcut.cc",

+    "win/shortcut.h",

+    "win/startup_information.cc",

+    "win/startup_information.h",

+    "win/text_services_message_filter.cc",

+    "win/text_services_message_filter.h",

+    "win/win_util.cc",

+    "win/win_util.h",

+    "win/windows_version.cc",

+    "win/windows_version.h",

+    "win/wrapped_window_proc.cc",

+    "win/wrapped_window_proc.h",

+  ]

+

+  # TODO(brettw) I don't understand the conditions this file is used.

+  sources -= "files/file_path_watcher_stub.cc"

+

+  sources -= [

+    # TODO(brettw) ozone

+    "message_loop/message_pump_ozone.cc",

+    "message_loop/message_pump_ozone.h",

+

+    "process/process_handle_freebsd.cc",

+    "process/process_handle_openbsd.cc",

+    "process/process_iterator_freebsd.cc",

+    "process/process_iterator_openbsd.cc",

+    "process/process_metrics_freebsd.cc",

+    "process/process_metrics_openbsd.cc",

+    "sys_info_freebsd.cc",

+    "sys_info_openbsd.cc",

+  ]

+

+  if (!is_chromeos) {

+    sources -= [

+      "sys_info_chromeos.cc",

+    ]

+  }

+  if (!is_mac) {

+    sources -= "files/file_path_watcher_kqueue.cc"

+  }

+

+  # Remove nacl stuff.

+  if (!is_nacl) {

+    sources -= [

+      "os_compat_nacl.cc",

+      "os_compat_nacl.h",

+      "rand_util_nacl.cc",

+      "third_party/nspr/prcpucfg_nacl.h",

+      "memory/shared_memory_nacl.cc",

+    ]

+  }

+

+  # Windows stuff.

+  if (is_win && !is_nacl) {

+    sources -= [

+      "strings/string16.cc",

+      # Not using sha1_win.cc because it may have caused a

+      # regression to page cycler moz.

+      "sha1_win.cc",

+    ]

+

+    if (is_component_build) {

+      sources -= "debug/debug_on_start_win.cc"

+    }

+  }

+

+  # Remove non-Mac Unix stuff.

+  if (!is_posix || is_mac) {

+    sources -= [

+      "nix/mime_util_xdg.cc",

+      "nix/mime_util_xdg.h",

+      "nix/xdg_util.cc",

+      "nix/xdg_util.h",

+    ]

+  }

+

+  defines = [

+    "BASE_IMPLEMENTATION",

+  ]

+

+  deps = [

+    ":base_static",

+    "//base/allocator:allocator_extension_thunks",

+    "//third_party/modp_b64",

+    "//base/third_party/dynamic_annotations",

+  ]

+}

+

+# This is the subset of files from base that should not be used with a dynamic

+# library. Note that this library cannot depend on base because base depends on

+# base_static.

+static_library("base_static") {

+  sources = [

+    "base_switches.cc",

+    "base_switches.h",

+    "win/pe_image.cc",

+    "win/pe_image.h",

+  ]

+}

+

+component("base_i18n") {

+  sources = [

+    "i18n/base_i18n_export.h",

+    "i18n/bidi_line_iterator.cc",

+    "i18n/bidi_line_iterator.h",

+    "i18n/break_iterator.cc",

+    "i18n/break_iterator.h",

+    "i18n/char_iterator.cc",

+    "i18n/char_iterator.h",

+    "i18n/case_conversion.cc",

+    "i18n/case_conversion.h",

+    "i18n/file_util_icu.cc",

+    "i18n/file_util_icu.h",

+    "i18n/icu_encoding_detection.cc",

+    "i18n/icu_encoding_detection.h",

+    "i18n/icu_string_conversions.cc",

+    "i18n/icu_string_conversions.h",

+    "i18n/icu_util.cc",

+    "i18n/icu_util.h",

+    "i18n/number_formatting.cc",

+    "i18n/number_formatting.h",

+    "i18n/rtl.cc",

+    "i18n/rtl.h",

+    "i18n/string_compare.cc",

+    "i18n/string_compare.h",

+    "i18n/string_search.cc",

+    "i18n/string_search.h",

+    "i18n/time_formatting.cc",

+    "i18n/time_formatting.h",

+  ]

+  deps = [

+    ":base",

+    "//base/third_party/dynamic_annotations",

+    "//third_party/icu:icui18n",

+    "//third_party/icu:icuuc",

+  ]

+  defines = [

+    "BASE_I18N_IMPLEMENTATION",

+  ]

+  #'conditions': [

+  #  ['toolkit_uses_gtk==1', {

+  #    'deps': [

+  #      # i18n/rtl.cc uses gtk

+  #      '../build/linux/system.gyp:gtk',

+  #    ],

+  #  }],

+  #  ['OS == "win"', {

+  #    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.

+  #    'msvs_disabled_warnings': [

+  #      4267,

+  #    ],

+  #  }],

+  #],

+  #'export_dependent_settings': [

+  #  'base',

+  #],

+  #'variables': {

+  #  'enable_wexit_time_destructors': 1,

+  #  'optimize': 'max',

+  #},

+}

+

+static_library("test_support_base") {

+  sources = [

+    "perftimer.cc",

+    "test/expectations/expectation.cc",

+    "test/expectations/expectation.h",

+    "test/expectations/parser.cc",

+    "test/expectations/parser.h",

+    "test/mock_chrome_application_mac.h",

+    "test/mock_chrome_application_mac.mm",

+    "test/mock_devices_changed_observer.cc",

+    "test/mock_devices_changed_observer.h",

+    "test/mock_time_provider.cc",

+    "test/mock_time_provider.h",

+    "test/multiprocess_test.cc",

+    "test/multiprocess_test.h",

+    "test/multiprocess_test_android.cc",

+    "test/null_task_runner.cc",

+    "test/null_task_runner.h",

+    "test/perf_test_suite.cc",

+    "test/perf_test_suite.h",

+    "test/scoped_locale.cc",

+    "test/scoped_locale.h",

+    "test/scoped_path_override.cc",

+    "test/scoped_path_override.h",

+    "test/sequenced_task_runner_test_template.cc",

+    "test/sequenced_task_runner_test_template.h",

+    "test/sequenced_worker_pool_owner.cc",

+    "test/sequenced_worker_pool_owner.h",

+    "test/simple_test_clock.cc",

+    "test/simple_test_clock.h",

+    "test/simple_test_tick_clock.cc",

+    "test/simple_test_tick_clock.h",

+    "test/task_runner_test_template.cc",

+    "test/task_runner_test_template.h",

+    "test/test_file_util.h",

+    "test/test_file_util_linux.cc",

+    "test/test_file_util_mac.cc",

+    "test/test_file_util_posix.cc",

+    "test/test_file_util_win.cc",

+    "test/test_listener_ios.h",

+    "test/test_listener_ios.mm",

+    "test/test_pending_task.cc",

+    "test/test_pending_task.h",

+    "test/test_process_killer_win.cc",

+    "test/test_process_killer_win.h",

+    "test/test_reg_util_win.cc",

+    "test/test_reg_util_win.h",

+    "test/test_shortcut_win.cc",

+    "test/test_shortcut_win.h",

+    "test/test_simple_task_runner.cc",

+    "test/test_simple_task_runner.h",

+    "test/test_suite.cc",

+    "test/test_suite.h",

+    "test/test_support_android.cc",

+    "test/test_support_android.h",

+    "test/test_support_ios.h",

+    "test/test_support_ios.mm",

+    "test/test_switches.cc",

+    "test/test_switches.h",

+    "test/test_timeouts.cc",

+    "test/test_timeouts.h",

+    "test/thread_test_helper.cc",

+    "test/thread_test_helper.h",

+    "test/trace_event_analyzer.cc",

+    "test/trace_event_analyzer.h",

+    "test/values_test_util.cc",

+    "test/values_test_util.h",

+  ]

+  deps = [

+    ":base",

+    ":base_static",

+    ":base_i18n",

+    "//testing:gmock",

+    "//testing:gtest",

+  ]

+

+  if (!is_posix) {

+    sources -= [

+      "test/scoped_locale.cc",

+      "test/scoped_locale.h",

+    ]

+  }

+  if (is_ios) {

+    # Pull in specific Mac files for iOS (which have been filtered out

+    # by file name rules).

+    {  # Temporarily override the assignment filter in a new scope.

+      set_sources_assignment_filter([])

+      sources += "test/test_file_util_mac.cc"

+    }

+  }

+  #if (!is_bsd) {

+  #  sources -= "test/test_file_util_linux.cc"

+  #}

+  #if (use_gtk) {

+  #  deps += "/build/linux/system:gtk"

+  #}

+  #export_dependent_settings [

+  #  'base',

+  #]

+}

+

+config("perf_test_config") {

+  defines = [ "PERF_TEST" ]

+}

+

+static_library("test_support_perf") {

+  sources = [

+    "perftimer.cc",

+    "test/run_all_perftests.cc",

+  ]

+  deps = [

+    ":base",

+    "//testing:gtest",

+  ]

+

+  direct_dependent_configs = [ ":perf_test_config" ]

+

+  #if (toolkit_uses_gtk) {

+  #  deps += "/build/linux/system:gtk",

+  #}

+}

+

+static_library("run_all_unittests") {

+  sources = [

+    "test/run_all_unittests.cc",

+  ]

+  deps = [

+    ":test_support_base",

+  ]

+}

diff --git a/tools/gn/secondary/base/allocator/BUILD.gn b/tools/gn/secondary/base/allocator/BUILD.gn
new file mode 100644
index 0000000..bbcfe65
--- /dev/null
+++ b/tools/gn/secondary/base/allocator/BUILD.gn
@@ -0,0 +1,6 @@
+static_library("allocator_extension_thunks") {

+  sources = [

+    "allocator_extension_thunks.cc",

+    "allocator_extension_thunks.h",

+  ]

+}

diff --git a/tools/gn/secondary/base/third_party/dynamic_annotations/BUILD.gn b/tools/gn/secondary/base/third_party/dynamic_annotations/BUILD.gn
new file mode 100644
index 0000000..e3939c3
--- /dev/null
+++ b/tools/gn/secondary/base/third_party/dynamic_annotations/BUILD.gn
@@ -0,0 +1,7 @@
+static_library("dynamic_annotations") {

+  sources = [

+    "dynamic_annotations.c",

+    "dynamic_annotations.h",

+    "../valgrind/valgrind.h",

+  ]

+}

diff --git a/tools/gn/secondary/build/config/BUILD.gn b/tools/gn/secondary/build/config/BUILD.gn
new file mode 100644
index 0000000..74c57af
--- /dev/null
+++ b/tools/gn/secondary/build/config/BUILD.gn
@@ -0,0 +1,52 @@
+config("my_msvs") {

+  includes = [ "../.." ]

+  cflags = [

+       "/Od", "/WX", "/Zi", "/Gy", "/GS", "/RTC1", "/EHsc",

+  ]

+  defines = [

+      "CHROMIUM_BUILD",

+      "TOOLKIT_VIEWS=1",

+      "USE_LIBJPEG_TURBO=1",

+      "ENABLE_ONE_CLICK_SIGNIN",

+      "ENABLE_REMOTING=1",

+      "ENABLE_WEBRTC=1",

+      "ENABLE_CONFIGURATION_POLICY",

+      "ENABLE_INPUT_SPEECH",

+      "ENABLE_NOTIFICATIONS",

+      "ENABLE_GPU=1",

+      "ENABLE_EGLIMAGE=1",

+      "ENABLE_TASK_MANAGER=1",

+      "ENABLE_EXTENSIONS=1",

+      "ENABLE_PLUGIN_INSTALLATION=1",

+      "ENABLE_PLUGINS=1",

+      "ENABLE_SESSION_SERVICE=1",

+      "ENABLE_THEMES=1",

+      "ENABLE_AUTOFILL_DIALOG=1",

+      "ENABLE_BACKGROUND=1",

+      "ENABLE_AUTOMATION=1",

+      "ENABLE_GOOGLE_NOW=1",

+      "ENABLE_LANGUAGE_DETECTION=1",

+      "ENABLE_PRINTING=1",

+      "ENABLE_CAPTIVE_PORTAL_DETECTION=1",

+      "ENABLE_APP_LIST=1",

+      "ENABLE_MESSAGE_CENTER=1",

+      "ENABLE_SETTINGS_APP=1",

+      "ENABLE_MANAGED_USERS=1",

+  ]

+}

+

+config("feature_flags") {

+  #defines = 

+}

+

+config("debug") {

+  defines = [

+    "_DEBUG",

+    "DYNAMIC_ANNOTATIONS_ENABLED=1",

+    "WTF_USE_DYNAMIC_ANNOTATIONS=1",

+  ]

+}

+

+config("release") {

+

+}

diff --git a/tools/gn/secondary/build/config/BUILDCONFIG.gn b/tools/gn/secondary/build/config/BUILDCONFIG.gn
new file mode 100644
index 0000000..0245597
--- /dev/null
+++ b/tools/gn/secondary/build/config/BUILDCONFIG.gn
@@ -0,0 +1,187 @@
+# =============================================================================

+# BUILD FLAGS

+# =============================================================================

+#

+# This block lists input arguments to the build, along with their default

+# values. GN requires listing them explicitly so it can validate input and have

+# a central place to manage the build flags.

+#

+# If a value is specified on the command line, it will overwrite the defaults

+# given here, otherwise the default will be injected into the root scope.

+#

+# KEEP IN ALPHABETICAL ORDER and write a good description for everything.

+# Use "is_*" names for intrinsic platform descriptions and build modes, and

+# "use_*" names for optional features libraries, and configurations.

+declare_args() {

+  is_component_build = 1

+  is_chromeos = 0

+  is_debug = 1

+  use_ash = 0

+  use_aura = 0

+  use_ozone = 0

+}

+

+# =============================================================================

+# SOURCES FILTERS

+# =============================================================================

+#

+# These patterns filter out platform-specific files when assigning to the

+# sources variable. The magic variable |sources_assignment_filter| is applied

+# to each assignment or appending to the sources variable and matches are

+# automatcally removed.

+#

+# We define lists of filters for each platform for all builds so they can

+# be used by individual targets if necessary (a target can always change

+# sources_assignment_filter on itself if it needs something more specific).

+#

+# Note that the patterns are NOT regular expressions. Only "*" and "\b" (path

+# boundary = end of string or slash) are supported, and the entire string

+# muct match the pattern (so you need "*.cc" to match all .cc files, for

+# example).

+

+windows_sources_filters = [

+  "*_win.cc",

+  "*_win.h",

+  "*_win_unittest.cc",

+  "*\bwin/*",

+]

+mac_sources_filters = [

+  "*_mac.h",

+  "*_mac.cc",

+  "*_mac.mm",

+  "*_mac_unittest.h",

+  "*_mac_unittest.cc",

+  "*_mac_unittest.mm",

+  "*\bmac/*",

+  "*_cocoa.h",

+  "*_cocoa.cc",

+  "*_cocoa.mm",

+  "*_cocoa_unittest.h",

+  "*_cocoa_unittest.cc",

+  "*_cocoa_unittest.mm",

+  "*\bcocoa/*",

+]

+ios_sources_filters = [

+  "*_ios.h",

+  "*_ios.cc",

+  "*_ios.mm",

+  "*_ios_unittest.h",

+  "*_ios_unittest.cc",

+  "*_ios_unittest.mm",

+  "*\bios/*",

+]

+objective_c_sources_filters = [

+  "*.mm",

+]

+linux_sources_filters = [

+  "*_linux.h",

+  "*_linux.cc",

+  "*_linux_unittest.h",

+  "*_linux_unittest.cc",

+  "*\blinux/*",

+]

+android_sources_filters = [

+  "*_android.h",

+  "*_android.cc",

+  "*_android_unittest.h",

+  "*_android_unittest.cc",

+  "*\bandroid/*",

+]

+posix_sources_filters = [

+  "*_posix.h",

+  "*_posix.cc",

+  "*_posix_unittest.h",

+  "*_posix_unittest.cc",

+  "*\bposix/*",

+]

+

+# Construct the full list of sources we're using for this platform.

+sources_assignment_filter = []

+if (is_win) {

+  sources_assignment_filter += posix_sources_filters

+} else {

+  sources_assignment_filter += windows_sources_filters

+}

+if (!is_mac) {

+  sources_assignment_filter += mac_sources_filters

+}

+if (!is_ios) {

+  sources_assignment_filter += ios_sources_filters

+}

+if (!is_mac && !is_ios) {

+  sources_assignment_filter += objective_c_sources_filters

+}

+if (!is_linux) {

+  sources_assignment_filter += linux_sources_filters

+}

+if (!is_android) {

+  sources_assignment_filter += android_sources_filters

+}

+

+# This is the actual set.

+set_sources_assignment_filter(sources_assignment_filter)

+

+# =============================================================================

+# SYSTEM CONFIG

+# =============================================================================

+

+is_nacl = 0

+

+# =============================================================================

+# BUILD OPTIONS

+# =============================================================================

+

+if (is_component_build) {

+  component_mode = "shared_library"

+} else {

+  component_mode = "static_library"

+}

+

+# =============================================================================

+# TARGET DEFAULTS

+# =============================================================================

+#

+# Set up the default configuration for every build target of the given type.

+# The values configured here will be automatically set on the scope of the

+# corresponding target. Target definitions can add or remove to the settings

+# here as needed.

+

+# Holds all configs used for making native executables and libraries, to avoid

+# duplication in each target below.

+native_compiler_configs = [

+  "//build/config:my_msvs",  # TODO(brettw) eraseme

+

+  "//build/config/compiler:chromium_code",

+  "//build/config/compiler:disable_annoying_warnings",

+  "//build/config/compiler:no_rtti",

+  "//build/config/compiler:runtime_library",

+]

+if (is_win) {

+  native_compiler_configs += "//build/config/win:sdk"

+}

+

+if (is_debug) {

+  native_compiler_configs += "//build/config:debug"

+} else {

+  native_compiler_configs += "//build/config::release"

+}

+

+set_defaults("executable") {

+  configs = native_compiler_configs

+}

+

+set_defaults("static_library") {

+  configs = native_compiler_configs

+}

+

+set_defaults("shared_library") {

+  configs = native_compiler_configs

+}

+

+# ==============================================================================

+# TOOLCHAIN SETUP

+# ==============================================================================

+

+if (is_win) {

+  set_default_toolchain("//build/config/win:32")

+}

diff --git a/tools/gn/secondary/build/config/compiler/BUILD.gn b/tools/gn/secondary/build/config/compiler/BUILD.gn
new file mode 100644
index 0000000..0487828
--- /dev/null
+++ b/tools/gn/secondary/build/config/compiler/BUILD.gn
@@ -0,0 +1,134 @@
+# Copyright (c) 2013 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.

+

+# runtime_library -------------------------------------------------------------

+#

+# Sets the runtime library and associated options.

+#

+# We don't bother making multiple versions that are toggle-able since there

+# is more than one axis of control (which makes it complicated) and there's

+# no practical reason for anybody to change this since the CRT must agree.

+

+config("runtime_library") {

+  if (is_component_build) {

+    # Component mode: dynamic CRT.

+    defines = [ "COMPONENT_BUILD" ]

+    if (is_win) {

+      # Since the library is shared, it requires exceptions or will give errors

+      # about things not matching, so keep exceptions on.

+      if (is_debug) {

+        cflags = [ "/MDd" ]

+      } else {

+        cflags = [ "/MD" ]

+      }

+    }

+  } else {

+    # Static CRT.

+    if (is_win) {

+      # We don't use exceptions, and when we link statically we can just get

+      # rid of them entirely.

+      defines = [ "_HAS_EXCEPTIONS=0" ]

+      if (is_debug) {

+        cflags = [ "/MTd" ]

+      } else {

+        cflags = [ "/MT" ]

+      }

+    }

+  }

+

+  if (is_win) {

+    defines += [

+      "__STD_C",

+      "__STDC_CONSTANT_MACROS",

+      "__STDC_FORMAT_MACROS",

+      "_CRT_RAND_S",

+      "_CRT_SECURE_NO_DEPRECATE",

+      "_SCL_SECURE_NO_DEPRECATE",

+      "_UNICODE",

+      "UNICODE", 

+    ]

+  }

+}

+

+# chromium_code ---------------------------------------------------------------

+#

+# Toggles between higher and lower warnings for code that is (or isn't)

+# part of Chromium.

+

+config("chromium_code") {

+  if (is_win) {

+    cflags = [

+      "/W4",  # Warning level 4.

+    ]

+  }

+}

+config("no_chromium_code") {

+  if (is_win) {

+    cflags = [

+      "/W3",  # Warning level 3.

+      "/wd4800",  # Disable warning when forcing value to bool.

+    ]

+    defines = [

+      "_CRT_NONSTDC_NO_WARNINGS",

+      "_CRT_NONSTDC_NO_DEPRECATE",

+    ]

+  }

+}

+

+# rtti ------------------------------------------------------------------------

+#

+# Allows turning Run-Time Type Identification on or off.

+

+config("rtti") {

+  if (is_win) {

+    cflags = [ "/GR" ]

+  }

+}

+config("no_rtti") {

+  if (is_win) {

+    cflags = [ "/GR-" ]

+  }

+}

+

+# Warnings ---------------------------------------------------------------------

+

+config("disable_annoying_warnings") {

+  if (is_win) {

+    # Please keep ordered and add names if you add more.

+    cflags = [

+      "/wd4018",  # Comparing signed and unsigned values.

+      "/wd4100",  # Unreferenced formal function parameter.

+      "/wd4121",  # Alignment of a member was sensitive to packing.

+      "/wd4125",  # Decimal digit terminates octal escape sequence.

+      "/wd4127",  # Conditional expression is constant.

+      "/wd4130",  # Logical operation on address of string constant.

+      # TODO(brettw) is this necessary? If so, it should probably be on whoever

+      # is silly enough to be doing this rather than globally.

+      #"/wd4131",  # Function uses old-style declarator.

+      "/wd4189",  # A variable was declared and initialized but never used.

+      "/wd4201",  # Nonstandard extension used: nameless struct/union.

+      "/wd4238",  # Nonstandard extension used: class rvalue used as lvalue.

+      "/wd4244",  # Conversion: possible loss of data.

+      "/wd4245",  # Conversion: signed/unsigned mismatch,

+      "/wd4251",  # Class needs to have dll-interface.

+      "/wd4310",  # Cast truncates constant value.

+      "/wd4351",  # Elements of array will be default initialized.

+      "/wd4355",  # 'this' used in base member initializer list.

+      "/wd4396",  # Inline friend template thing.

+      "/wd4428",  # Universal character name encountered in source.

+      "/wd4481",  # Nonstandard extension: override specifier.

+      "/wd4503",  # Decorated name length exceeded, name was truncated.

+      "/wd4505",  # Unreferenced local function has been removed.

+      "/wd4510",  # Default constructor could not be generated.

+      "/wd4512",  # Assignment operator could not be generated.

+      "/wd4530",  # Exception handler used, but unwind semantics not enabled.

+      "/wd4610",  # Class can never be instantiated, constructor required.

+      "/wd4611",  # C++ object destruction and 'catch'.

+      "/wd4701",  # Potentially uninitialized local variable name used.

+      "/wd4702",  # Unreachable code.

+      "/wd4706",  # Assignment within conditional expression.

+      "/wd4819",  # Character not in the current code page.

+    ]

+  }

+}

diff --git a/tools/gn/secondary/build/config/win/BUILD.gn b/tools/gn/secondary/build/config/win/BUILD.gn
new file mode 100644
index 0000000..955504a
--- /dev/null
+++ b/tools/gn/secondary/build/config/win/BUILD.gn
@@ -0,0 +1,184 @@
+# Copyright (c) 2013 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.

+

+# Should only be running on Windows.

+assert(is_win)

+

+# Setup the Visual Studio state.

+#

+# Its argument is the location to write the environment files.

+# It will write "environment.x86" and "environment.x64" to this directory,

+# and return a list to us.

+#

+# The list contains the include path as its only element. (I'm expecting to

+# add more so it's currently a list inside a list.)

+msvc_config = [["foo"]]

+#exec_script("get_msvc_config.py",

+                     #     [relative_root_output_dir],

+                     #     "value")

+

+# 32-bit toolchain -------------------------------------------------------------

+

+toolchain("32") {

+  tool("cc") {

+    command = "ninja -t msvc -e \$arch -- cl.exe /nologo /showIncludes /FC @\$out.rsp /c \$in /Fo\$out /Fd\$pdbname"

+    description = "CC \$out"

+    rspfile = "\$out.rsp"

+    rspfile_content = "\$defines \$includes \$cflags \$cflags_c"

+    deps = "msvc"

+  }

+  tool("cxx") {

+    command = "ninja -t msvc -e \$arch -- cl.exe /nologo /showIncludes /FC @\$out.rsp /c \$in /Fo\$out /Fd\$pdbname"

+    description = "CXX \$out"

+    rspfile = "\$out.rsp"

+    rspfile_content = "\$defines \$includes \$cflags \$cflags_cc"

+    deps = "msvc"

+  }

+  #tool("idl") {

+  #  command = $python_path gyp-win-tool midl-wrapper \$arch \$outdir \$tlb \$h \$dlldata \$iid \$

+  #      \$proxy \$in \$idlflags

+  #  description = IDL \$in

+  #}

+  #tool("rc") {

+  #  command = $python_path gyp-win-tool rc-wrapper \$arch rc.exe \$defines \$includes \$rcflags \$

+  #      /fo\$out \$in

+  #  description = RC \$in

+  #}

+  #tool("asm") {

+  #  command = $python_path gyp-win-tool asm-wrapper \$arch ml.exe \$defines \$includes /c /Fo \$

+  #      \$out \$in

+  #  description = ASM \$in

+  #}

+  tool("alink") {

+    command = "$python_path gyp-win-tool link-wrapper \$arch lib.exe /nologo /ignore:4221 /OUT:\$out @\$out.rsp"

+    description = "LIB \$out"

+    rspfile = "\$out.rsp"

+    rspfile_content = "\$in_newline \$libflags"

+  }

+  #tool("solink_embed_inc") {

+  #  command = cmd /c $python_path gyp-win-tool link-wrapper \$arch link.exe /nologo \$implibflag \$

+  #      /DLL /OUT:\$dll /PDB:\$dll.pdb @\$dll.rsp && $python_path gyp-win-tool \$

+  #      manifest-wrapper \$arch cmd /c if exist \$dll.manifest del \$dll.manifest && \$

+  #      $python_path gyp-win-tool manifest-wrapper \$arch mt.exe -nologo -manifest \$manifests \$

+  #      -out:\$dll.manifest && $python_path gyp-win-tool manifest-to-rc \$arch \$dll.manifest \$

+  #      \$dll.manifest.rc 2 && $python_path gyp-win-tool rc-wrapper \$arch rc.exe \$

+  #      \$dll.manifest.rc && $python_path gyp-win-tool link-wrapper \$arch link.exe /nologo \$

+  #      \$implibflag /DLL /OUT:\$dll /PDB:\$dll.pdb @\$dll.rsp \$dll.manifest.res

+  #  description = LINK_EMBED_INC(DLL) \$dll

+  #  restat = 1

+  #  rspfile = \$dll.rsp

+  #  rspfile_content = \$libs \$in_newline \$ldflags

+  #}

+  #tool("solink_module_embed_inc") {

+  #  command = cmd /c $python_path gyp-win-tool link-wrapper \$arch link.exe /nologo \$implibflag \$

+  #      /DLL /OUT:\$dll /PDB:\$dll.pdb @\$dll.rsp && $python_path gyp-win-tool \$

+  #      manifest-wrapper \$arch cmd /c if exist \$dll.manifest del \$dll.manifest && \$

+  #      $python_path gyp-win-tool manifest-wrapper \$arch mt.exe -nologo -manifest \$manifests \$

+  #      -out:\$dll.manifest && $python_path gyp-win-tool manifest-to-rc \$arch \$dll.manifest \$

+  #      \$dll.manifest.rc 2 && $python_path gyp-win-tool rc-wrapper \$arch rc.exe \$

+  #      \$dll.manifest.rc && $python_path gyp-win-tool link-wrapper \$arch link.exe /nologo \$

+  #      \$implibflag /DLL /OUT:\$dll /PDB:\$dll.pdb @\$dll.rsp \$dll.manifest.res

+  #  description = LINK_EMBED_INC(DLL) \$dll

+  #  restat = 1

+  #  rspfile = \$dll.rsp

+  #  rspfile_content = \$libs \$in_newline \$ldflags

+  #}

+  #rule link_embed_inc

+  #  command = cmd /c $python_path gyp-win-tool link-wrapper \$arch link.exe /nologo /OUT:\$out \$

+  #      /PDB:\$out.pdb @\$out.rsp && $python_path gyp-win-tool manifest-wrapper \$arch cmd /c \$

+  #      if exist \$out.manifest del \$out.manifest && $python_path gyp-win-tool \$

+  #      manifest-wrapper \$arch mt.exe -nologo -manifest \$manifests -out:\$out.manifest && \$

+  #      $python_path gyp-win-tool manifest-to-rc \$arch \$out.manifest \$out.manifest.rc 1 && \$

+  #      $python_path gyp-win-tool rc-wrapper \$arch rc.exe \$out.manifest.rc && \$

+  #      $python_path gyp-win-tool link-wrapper \$arch link.exe /nologo /OUT:\$out /PDB:\$out.pdb \$

+  #      @\$out.rsp \$out.manifest.res

+  #  description = LINK_EMBED_INC \$out

+  #  rspfile = \$out.rsp

+  #  rspfile_content = \$in_newline \$libs \$ldflags

+  #rule solink_embed

+  #  command = cmd /c $python_path gyp-win-tool link-wrapper \$arch link.exe /nologo \$implibflag \$

+  #      /DLL /OUT:\$dll /PDB:\$dll.pdb @\$dll.rsp && $python_path gyp-win-tool \$

+  #      manifest-wrapper \$arch cmd /c if exist \$dll.manifest del \$dll.manifest && \$

+  #      $python_path gyp-win-tool manifest-wrapper \$arch mt.exe -nologo -manifest \$manifests \$

+  #      -outputresource:\$dll;2

+  #  description = LINK_EMBED(DLL) \$dll

+  #  restat = 1

+  #  rspfile = \$dll.rsp

+  #  rspfile_content = \$libs \$in_newline \$ldflags

+  #rule solink_module_embed

+  #  command = cmd /c $python_path gyp-win-tool link-wrapper \$arch link.exe /nologo \$implibflag \$

+  #      /DLL /OUT:\$dll /PDB:\$dll.pdb @\$dll.rsp && $python_path gyp-win-tool \$

+  #      manifest-wrapper \$arch cmd /c if exist \$dll.manifest del \$dll.manifest && \$

+  #      $python_path gyp-win-tool manifest-wrapper \$arch mt.exe -nologo -manifest \$manifests \$

+  #      -outputresource:\$dll;2

+  #  description = LINK_EMBED(DLL) \$dll

+  #  restat = 1

+  #  rspfile = \$dll.rsp

+  #  rspfile_content = \$libs \$in_newline \$ldflags

+  #rule link_embed

+  #  command = cmd /c $python_path gyp-win-tool link-wrapper \$arch link.exe /nologo /OUT:\$out \$

+  #      /PDB:\$out.pdb @\$out.rsp && $python_path gyp-win-tool manifest-wrapper \$arch cmd /c \$

+  #      if exist \$out.manifest del \$out.manifest && $python_path gyp-win-tool \$

+  #      manifest-wrapper \$arch mt.exe -nologo -manifest \$manifests -outputresource:\$out;1

+  #  description = LINK_EMBED \$out

+  #  rspfile = \$out.rsp

+  #  rspfile_content = \$in_newline \$libs \$ldflags

+  tool("solink") {

+    command = "cmd /c $python_path gyp-win-tool link-wrapper \$arch link.exe /nologo \$implibflag /DLL /OUT:\$dll /PDB:\$dll.pdb @\$dll.rsp && $python_path gyp-win-tool manifest-wrapper \$arch cmd /c if exist \$dll.manifest del \$dll.manifest && $python_path gyp-win-tool manifest-wrapper \$arch mt.exe -nologo -manifest \$manifests -out:\$dll.manifest"

+    description = "LINK(DLL) \$dll"

+    restat = "1"

+    rspfile = "\$dll.rsp"

+    rspfile_content = "\$libs \$in_newline \$ldflags"

+  }

+  tool("solink_module") {

+    command = "cmd /c $python_path gyp-win-tool link-wrapper \$arch link.exe /nologo \$implibflag /DLL /OUT:\$dll /PDB:\$dll.pdb @\$dll.rsp && $python_path gyp-win-tool manifest-wrapper \$arch cmd /c if exist \$dll.manifest del \$dll.manifest && $python_path gyp-win-tool manifest-wrapper \$arch mt.exe -nologo -manifest \$manifests -out:\$dll.manifet"

+    description = "LINK(DLL) \$dll"

+    restat = "1"

+    rspfile = "\$dll.rsp"

+    rspfile_content = "\$libs \$in_newline \$ldflags"

+  }

+  tool("link") {

+    command = "cmd /c $python_path gyp-win-tool link-wrapper \$arch link.exe /nologo /OUT:\$out /PDB:\$out.pdb @\$out.rsp && $python_path gyp-win-tool manifest-wrapper \$arch cmd /c if exist \$out.manifest del \$out.manifest && $python_path gyp-win-tool manifest-wrapper \$arch mt.exe -nologo -manifest \$manifests -out:\$out.manifest"

+    description = "LINK \$out"

+    rspfile = "\$out.rsp"

+    rspfile_content = "\$in_newline \$libs \$ldflags"

+  }

+  tool("stamp") {

+    command = "$python_path gyp-win-tool stamp \$out"

+    description = "STAMP \$out"

+  }

+  tool("copy") {

+    command = "$python_path gyp-win-tool recursive-mirror \$in \$out"

+    description = "COPY \$in \$out"

+  }

+}

+

+# 64-bit toolchain -------------------------------------------------------------

+

+toolchain("64") {

+}

+

+# SDK setup --------------------------------------------------------------------

+

+config("sdk") {

+  # The include path is the stuff returned by the script plus out own WTL

+  # checkout.

+  # TODO(brettw) should adding WTL be at this level or should it be more on

+  # a per-project basis?

+  includes = msvc_config[0] + "../../third_party/wtl/include"

+

+  defines = [

+    "_ATL_NO_OPENGL",

+    "_SECURE_ATL",

+    "_WIN32_WINNT=0x0602",

+    "_WINDOWS",

+    "CERT_CHAIN_PARA_HAS_EXTRA_FIELDS",

+    "NOMINMAX",

+    "NTDDI_VERSION=0x06020000",

+    "PSAPI_VERSION=1",

+    "WIN32",

+    "WIN32_LEAN_AND_MEAN",

+    "WINVER=0x0602",

+  ]

+}

diff --git a/tools/gn/secondary/build/config/win/get_msvc_config.py b/tools/gn/secondary/build/config/win/get_msvc_config.py
new file mode 100644
index 0000000..01380cd
--- /dev/null
+++ b/tools/gn/secondary/build/config/win/get_msvc_config.py
@@ -0,0 +1,77 @@
+# Copyright (c) 2013 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.
+
+# This file returns the MSVC config used by the Windows build.
+# It's a bit hardcoded right now. I suspect we want to build this functionality
+# into GN itself in the future.
+
+import sys
+
+# This script expects one parameter: the path to the root output directory.
+
+# TODO(brettw): do escaping.
+def FormatStringForGN(x):
+  return '"' + x + '"'
+
+def PrintListOfStrings(x):
+  print '['
+  for i in x:
+    print FormatStringForGN(i) + ', '
+  print ']'
+
+# GN wants system-absolutepaths to begin in slashes.
+sdk_root = '/C:\\Program Files (x86)\\Windows Kits\\8.0\\'
+vs_root = '/C:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\'
+
+def GetIncludes():
+  return [
+    sdk_root + 'Include\\shared',
+    sdk_root + 'Include\\um',
+    sdk_root + 'Include\\winrt',
+    vs_root + 'VC\\atlmfc\\include'
+  ]
+
+def _FormatAsEnvironmentBlock(envvar_dict):
+  """Format as an 'environment block' directly suitable for CreateProcess.
+  Briefly this is a list of key=value\0, terminated by an additional \0. See
+  CreateProcess documentation for more details."""
+  block = ''
+  nul = '\0'
+  for key, value in envvar_dict.iteritems():
+    block += key + '=' + value + nul
+  block += nul
+  return block
+
+def WriteEnvFile(file_path, values):
+  f = open(file_path, "w")
+  f.write(_FormatAsEnvironmentBlock(values))
+
+includes = GetIncludes()
+
+# Write the environment files.
+WriteEnvFile(sys.argv[1] + '\\environment.x86',
+  { 'TMP': 'C:\\Users\\brettw\\AppData\\Local\\Temp',
+    'SYSTEMROOT': 'C:\\Windows',
+    'TEMP': 'C:\\Users\\brettw\\AppData\\Local\\Temp',
+    'LIB': 'c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\LIB;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\ATLMFC\\LIB;C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.0A\\lib;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\LIB;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\ATLMFC\\LIB;C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.0A\\lib;',
+    'LIBPATH': 'C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319;C:\\Windows\\Microsoft.NET\\Framework\\v3.5;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\LIB;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\ATLMFC\\LIB;C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319;C:\\Windows\\Microsoft.NET\\Framework\\v3.5;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\LIB;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\ATLMFC\\LIB;',
+    'PATH': 'C:\\apps\\depot_tools\\python_bin;c:\\Program Files (x86)\\Microsoft F#\\v4.0\\;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VSTSDB\\Deploy;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\Common7\\IDE\\;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\BIN;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\Common7\\Tools;C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319;C:\\Windows\\Microsoft.NET\\Framework\\v3.5;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\VCPackages;C:\\Program Files (x86)\\HTML Help Workshop;C:\\Program Files (x86)\\HTML Help Workshop;C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.0A\\bin\\NETFX 4.0 Tools;C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.0A\\bin;C:\\apps\\depot_tools\\python_bin;C:\\apps\\depot_tools\\;C:\\apps\\depot_tools\\;C:\\apps\\depot_tools\\;c:\\Program Files (x86)\\Microsoft F#\\v4.0\\;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VSTSDB\\Deploy;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\Common7\\IDE\\;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\BIN;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\Common7\\Tools;C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319;C:\\Windows\\Microsoft.NET\\Framework\\v3.5;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\VCPackages;C:\\Program Files (x86)\\HTML Help Workshop;C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.0A\\bin\\NETFX 4.0 Tools;C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.0A\\bin;C:\\Windows\\system32;C:\\Windows;C:\\Windows\\System32\\Wbem;C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\;C:\\windows\\corpam;C:\\python_26_amd64\\files;C:\\Windows\\ccmsetup;c:\\Program Files (x86)\\Microsoft SQL Server\\100\\Tools\\Binn\\;c:\\Program Files\\Microsoft SQL Server\\100\\Tools\\Binn\\;c:\\Program Files\\Microsoft SQL Server\\100\\DTS\\Binn\\;c:\\cygwin\\bin;C:\\apps\\;C:\\apps\\depot_tools;C:\\Program Files (x86)\\Windows Kits\\8.0\\Windows Performance Toolkit\\;C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\;C:\\Program Files (x86)\\Google\\Cert Installer;C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\;C:\\Program Files (x86)\\Google\\google_appengine\\',
+    'PATHEXT': '=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC',
+    'INCLUDE': 'c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\INCLUDE;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\ATLMFC\\INCLUDE;C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.0A\\include;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\INCLUDE;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\ATLMFC\\INCLUDE;C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.0A\\include;'})
+
+WriteEnvFile(sys.argv[1] + '\\environment.x64',
+  { 'TMP': 'C:\\Users\\brettw\\AppData\\Local\\Temp',
+    'SYSTEMROOT': 'C:\\Windows',
+    'TEMP': 'C:\\Users\\brettw\\AppData\\Local\\Temp',
+    'LIB': 'c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\LIB\\amd64;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\ATLMFC\\LIB\\amd64;C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.0A\\lib\\x64;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\LIB;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\ATLMFC\\LIB;C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.0A\\lib;',
+    'LIBPATH': 'C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319;C:\\Windows\\Microsoft.NET\\Framework64\\v3.5;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\LIB\\amd64;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\ATLMFC\\LIB\\amd64;C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319;C:\\Windows\\Microsoft.NET\\Framework\\v3.5;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\LIB;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\ATLMFC\\LIB;',
+    'PATH': 'C:\\apps\\depot_tools\\python_bin;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\BIN\\amd64;C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319;C:\\Windows\\Microsoft.NET\\Framework64\\v3.5;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\VCPackages;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\Common7\\IDE;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\Common7\\Tools;C:\\Program Files (x86)\\HTML Help Workshop;C:\\Program Files (x86)\\HTML Help Workshop;C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.0A\\bin\\NETFX 4.0 Tools\\x64;C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.0A\\bin\\x64;C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.0A\\bin;C:\\apps\\depot_tools\\python_bin;C:\\apps\\depot_tools\\;C:\\apps\\depot_tools\\;C:\\apps\\depot_tools\\;c:\\Program Files (x86)\\Microsoft F#\\v4.0\\;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VSTSDB\\Deploy;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\Common7\\IDE\\;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\BIN;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\Common7\\Tools;C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319;C:\\Windows\\Microsoft.NET\\Framework\\v3.5;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\VCPackages;C:\\Program Files (x86)\\HTML Help Workshop;C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.0A\\bin\\NETFX 4.0 Tools;C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.0A\\bin;C:\\Windows\\system32;C:\\Windows;C:\\Windows\\System32\\Wbem;C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\;C:\\windows\\corpam;C:\\python_26_amd64\\files;C:\\Windows\\ccmsetup;c:\\Program Files (x86)\\Microsoft SQL Server\\100\\Tools\\Binn\\;c:\\Program Files\\Microsoft SQL Server\\100\\Tools\\Binn\\;c:\\Program Files\\Microsoft SQL Server\\100\\DTS\\Binn\\;c:\\cygwin\\bin;C:\\apps\\;C:\\apps\\depot_tools;C:\\Program Files (x86)\\Windows Kits\\8.0\\Windows Performance Toolkit\\;C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\;C:\\Program Files (x86)\\Google\\Cert Installer;C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\;C:\\Program Files (x86)\\Google\\google_appengine\\',
+    'PATHEXT': '.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC',
+    'INCLUDE': 'c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\INCLUDE;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\ATLMFC\\INCLUDE;C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.0A\\include;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\INCLUDE;c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\ATLMFC\\INCLUDE;C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.0A\\include;'})
+
+# Return the includes and such.
+print '['
+PrintListOfStrings(includes)
+print ']'
+
diff --git a/tools/gn/secondary/build/config/win/get_msvc_config_real.py b/tools/gn/secondary/build/config/win/get_msvc_config_real.py
new file mode 100644
index 0000000..a209d7f
--- /dev/null
+++ b/tools/gn/secondary/build/config/win/get_msvc_config_real.py
@@ -0,0 +1,575 @@
+# Copyright (c) 2013 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.
+
+# This file copies the logic from GYP to find the MSVC configuration. It's not
+# currently used because it is too slow. We will probably build this
+# functionality into the C++ code in the future.
+
+"""Handle version information related to Visual Stuio."""
+
+import errno
+import os
+import re
+import subprocess
+import sys
+
+class VisualStudioVersion(object):
+  """Information regarding a version of Visual Studio."""
+
+  def __init__(self, short_name, description,
+               solution_version, project_version, flat_sln, uses_vcxproj,
+               path, sdk_based, default_toolset=None):
+    self.short_name = short_name
+    self.description = description
+    self.solution_version = solution_version
+    self.project_version = project_version
+    self.flat_sln = flat_sln
+    self.uses_vcxproj = uses_vcxproj
+    self.path = path
+    self.sdk_based = sdk_based
+    self.default_toolset = default_toolset
+
+  def ShortName(self):
+    return self.short_name
+
+  def Description(self):
+    """Get the full description of the version."""
+    return self.description
+
+  def SolutionVersion(self):
+    """Get the version number of the sln files."""
+    return self.solution_version
+
+  def ProjectVersion(self):
+    """Get the version number of the vcproj or vcxproj files."""
+    return self.project_version
+
+  def FlatSolution(self):
+    return self.flat_sln
+
+  def UsesVcxproj(self):
+    """Returns true if this version uses a vcxproj file."""
+    return self.uses_vcxproj
+
+  def ProjectExtension(self):
+    """Returns the file extension for the project."""
+    return self.uses_vcxproj and '.vcxproj' or '.vcproj'
+
+  def Path(self):
+    """Returns the path to Visual Studio installation."""
+    return self.path
+
+  def ToolPath(self, tool):
+    """Returns the path to a given compiler tool. """
+    return os.path.normpath(os.path.join(self.path, "VC/bin", tool))
+
+  def DefaultToolset(self):
+    """Returns the msbuild toolset version that will be used in the absence
+    of a user override."""
+    return self.default_toolset
+
+  def SetupScript(self, target_arch):
+    """Returns a command (with arguments) to be used to set up the
+    environment."""
+    # Check if we are running in the SDK command line environment and use
+    # the setup script from the SDK if so. |target_arch| should be either
+    # 'x86' or 'x64'.
+    assert target_arch in ('x86', 'x64')
+    sdk_dir = os.environ.get('WindowsSDKDir')
+    if self.sdk_based and sdk_dir:
+      return [os.path.normpath(os.path.join(sdk_dir, 'Bin/SetEnv.Cmd')),
+              '/' + target_arch]
+    else:
+      # We don't use VC/vcvarsall.bat for x86 because vcvarsall calls
+      # vcvars32, which it can only find if VS??COMNTOOLS is set, which it
+      # isn't always.
+      if target_arch == 'x86':
+        return [os.path.normpath(
+          os.path.join(self.path, 'Common7/Tools/vsvars32.bat'))]
+      else:
+        assert target_arch == 'x64'
+        arg = 'x86_amd64'
+        if (os.environ.get('PROCESSOR_ARCHITECTURE') == 'AMD64' or
+            os.environ.get('PROCESSOR_ARCHITEW6432') == 'AMD64'):
+          # Use the 64-on-64 compiler if we can.
+          arg = 'amd64'
+        return [os.path.normpath(
+            os.path.join(self.path, 'VC/vcvarsall.bat')), arg]
+
+
+def _RegistryQueryBase(sysdir, key, value):
+  """Use reg.exe to read a particular key.
+
+  While ideally we might use the win32 module, we would like gyp to be
+  python neutral, so for instance cygwin python lacks this module.
+
+  Arguments:
+    sysdir: The system subdirectory to attempt to launch reg.exe from.
+    key: The registry key to read from.
+    value: The particular value to read.
+  Return:
+    stdout from reg.exe, or None for failure.
+  """
+  # Skip if not on Windows or Python Win32 setup issue
+  if sys.platform not in ('win32', 'cygwin'):
+    return None
+  # Setup params to pass to and attempt to launch reg.exe
+  cmd = [os.path.join(os.environ.get('WINDIR', ''), sysdir, 'reg.exe'),
+         'query', key]
+  if value:
+    cmd.extend(['/v', value])
+  p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+  # Obtain the stdout from reg.exe, reading to the end so p.returncode is valid
+  # Note that the error text may be in [1] in some cases
+  text = p.communicate()[0]
+  # Check return code from reg.exe; officially 0==success and 1==error
+  if p.returncode:
+    return None
+  return text
+
+
+def _RegistryQuery(key, value=None):
+  """Use reg.exe to read a particular key through _RegistryQueryBase.
+
+  First tries to launch from %WinDir%\Sysnative to avoid WoW64 redirection. If
+  that fails, it falls back to System32.  Sysnative is available on Vista and
+  up and available on Windows Server 2003 and XP through KB patch 942589. Note
+  that Sysnative will always fail if using 64-bit python due to it being a
+  virtual directory and System32 will work correctly in the first place.
+
+  KB 942589 - http://support.microsoft.com/kb/942589/en-us.
+
+  Arguments:
+    key: The registry key.
+    value: The particular registry value to read (optional).
+  Return:
+    stdout from reg.exe, or None for failure.
+  """
+  text = None
+  try:
+    text = _RegistryQueryBase('Sysnative', key, value)
+  except OSError, e:
+    if e.errno == errno.ENOENT:
+      text = _RegistryQueryBase('System32', key, value)
+    else:
+      raise
+  return text
+
+
+def _RegistryGetValue(key, value):
+  """Use reg.exe to obtain the value of a registry key.
+
+  Args:
+    key: The registry key.
+    value: The particular registry value to read.
+  Return:
+    contents of the registry key's value, or None on failure.
+  """
+  text = _RegistryQuery(key, value)
+  if not text:
+    return None
+  # Extract value.
+  match = re.search(r'REG_\w+\s+([^\r]+)\r\n', text)
+  if not match:
+    return None
+  return match.group(1)
+
+
+def _RegistryKeyExists(key):
+  """Use reg.exe to see if a key exists.
+
+  Args:
+    key: The registry key to check.
+  Return:
+    True if the key exists
+  """
+  if not _RegistryQuery(key):
+    return False
+  return True
+
+
+def _CreateVersion(name, path, sdk_based=False):
+  """Sets up MSVS project generation.
+
+  Setup is based off the GYP_MSVS_VERSION environment variable or whatever is
+  autodetected if GYP_MSVS_VERSION is not explicitly specified. If a version is
+  passed in that doesn't match a value in versions python will throw a error.
+  """
+  if path:
+    path = os.path.normpath(path)
+  versions = {
+      '2013': VisualStudioVersion('2013',
+                                  'Visual Studio 2013',
+                                  solution_version='13.00',
+                                  project_version='4.0',
+                                  flat_sln=False,
+                                  uses_vcxproj=True,
+                                  path=path,
+                                  sdk_based=sdk_based,
+                                  default_toolset='v110'),
+      '2013e': VisualStudioVersion('2013e',
+                                   'Visual Studio 2013',
+                                   solution_version='13.00',
+                                   project_version='4.0',
+                                   flat_sln=True,
+                                   uses_vcxproj=True,
+                                   path=path,
+                                   sdk_based=sdk_based,
+                                   default_toolset='v110'),
+      '2012': VisualStudioVersion('2012',
+                                  'Visual Studio 2012',
+                                  solution_version='12.00',
+                                  project_version='4.0',
+                                  flat_sln=False,
+                                  uses_vcxproj=True,
+                                  path=path,
+                                  sdk_based=sdk_based,
+                                  default_toolset='v110'),
+      '2012e': VisualStudioVersion('2012e',
+                                   'Visual Studio 2012',
+                                   solution_version='12.00',
+                                   project_version='4.0',
+                                   flat_sln=True,
+                                   uses_vcxproj=True,
+                                   path=path,
+                                   sdk_based=sdk_based,
+                                   default_toolset='v110'),
+      '2010': VisualStudioVersion('2010',
+                                  'Visual Studio 2010',
+                                  solution_version='11.00',
+                                  project_version='4.0',
+                                  flat_sln=False,
+                                  uses_vcxproj=True,
+                                  path=path,
+                                  sdk_based=sdk_based),
+      '2010e': VisualStudioVersion('2010e',
+                                   'Visual Studio 2010',
+                                   solution_version='11.00',
+                                   project_version='4.0',
+                                   flat_sln=True,
+                                   uses_vcxproj=True,
+                                   path=path,
+                                   sdk_based=sdk_based),
+      '2008': VisualStudioVersion('2008',
+                                  'Visual Studio 2008',
+                                  solution_version='10.00',
+                                  project_version='9.00',
+                                  flat_sln=False,
+                                  uses_vcxproj=False,
+                                  path=path,
+                                  sdk_based=sdk_based),
+      '2008e': VisualStudioVersion('2008e',
+                                   'Visual Studio 2008',
+                                   solution_version='10.00',
+                                   project_version='9.00',
+                                   flat_sln=True,
+                                   uses_vcxproj=False,
+                                   path=path,
+                                   sdk_based=sdk_based),
+      '2005': VisualStudioVersion('2005',
+                                  'Visual Studio 2005',
+                                  solution_version='9.00',
+                                  project_version='8.00',
+                                  flat_sln=False,
+                                  uses_vcxproj=False,
+                                  path=path,
+                                  sdk_based=sdk_based),
+      '2005e': VisualStudioVersion('2005e',
+                                   'Visual Studio 2005',
+                                   solution_version='9.00',
+                                   project_version='8.00',
+                                   flat_sln=True,
+                                   uses_vcxproj=False,
+                                   path=path,
+                                   sdk_based=sdk_based),
+  }
+  return versions[str(name)]
+
+
+def _ConvertToCygpath(path):
+  """Convert to cygwin path if we are using cygwin."""
+  if sys.platform == 'cygwin':
+    p = subprocess.Popen(['cygpath', path], stdout=subprocess.PIPE)
+    path = p.communicate()[0].strip()
+  return path
+
+
+def _DetectVisualStudioVersions(versions_to_check, force_express):
+  """Collect the list of installed visual studio versions.
+
+  Returns:
+    A list of visual studio versions installed in descending order of
+    usage preference.
+    Base this on the registry and a quick check if devenv.exe exists.
+    Only versions 8-10 are considered.
+    Possibilities are:
+      2005(e) - Visual Studio 2005 (8)
+      2008(e) - Visual Studio 2008 (9)
+      2010(e) - Visual Studio 2010 (10)
+      2012(e) - Visual Studio 2012 (11)
+      2013(e) - Visual Studio 2013 (11)
+    Where (e) is e for express editions of MSVS and blank otherwise.
+  """
+  version_to_year = {
+      '8.0': '2005',
+      '9.0': '2008',
+      '10.0': '2010',
+      '11.0': '2012',
+      '12.0': '2013',
+  }
+  versions = []
+  for version in versions_to_check:
+    # Old method of searching for which VS version is installed
+    # We don't use the 2010-encouraged-way because we also want to get the
+    # path to the binaries, which it doesn't offer.
+    keys = [r'HKLM\Software\Microsoft\VisualStudio\%s' % version,
+            r'HKLM\Software\Wow6432Node\Microsoft\VisualStudio\%s' % version,
+            r'HKLM\Software\Microsoft\VCExpress\%s' % version,
+            r'HKLM\Software\Wow6432Node\Microsoft\VCExpress\%s' % version]
+    for index in range(len(keys)):
+      path = _RegistryGetValue(keys[index], 'InstallDir')
+      if not path:
+        continue
+      path = _ConvertToCygpath(path)
+      # Check for full.
+      full_path = os.path.join(path, 'devenv.exe')
+      express_path = os.path.join(path, 'vcexpress.exe')
+      if not force_express and os.path.exists(full_path):
+        # Add this one.
+        versions.append(_CreateVersion(version_to_year[version],
+            os.path.join(path, '..', '..')))
+      # Check for express.
+      elif os.path.exists(express_path):
+        # Add this one.
+        versions.append(_CreateVersion(version_to_year[version] + 'e',
+            os.path.join(path, '..', '..')))
+
+    # The old method above does not work when only SDK is installed.
+    keys = [r'HKLM\Software\Microsoft\VisualStudio\SxS\VC7',
+            r'HKLM\Software\Wow6432Node\Microsoft\VisualStudio\SxS\VC7']
+    for index in range(len(keys)):
+      path = _RegistryGetValue(keys[index], version)
+      if not path:
+        continue
+      path = _ConvertToCygpath(path)
+      versions.append(_CreateVersion(version_to_year[version] + 'e',
+          os.path.join(path, '..'), sdk_based=True))
+
+  return versions
+
+
+def SelectVisualStudioVersion(version='auto'):
+  """Select which version of Visual Studio projects to generate.
+
+  Arguments:
+    version: Hook to allow caller to force a particular version (vs auto).
+  Returns:
+    An object representing a visual studio project format version.
+  """
+  # In auto mode, check environment variable for override.
+  if version == 'auto':
+    version = os.environ.get('GYP_MSVS_VERSION', 'auto')
+  version_map = {
+    'auto': ('10.0', '9.0', '8.0', '11.0'),
+    '2005': ('8.0',),
+    '2005e': ('8.0',),
+    '2008': ('9.0',),
+    '2008e': ('9.0',),
+    '2010': ('10.0',),
+    '2010e': ('10.0',),
+    '2012': ('11.0',),
+    '2012e': ('11.0',),
+    '2013': ('12.0',),
+    '2013e': ('12.0',),
+  }
+  override_path = os.environ.get('GYP_MSVS_OVERRIDE_PATH')
+  if override_path:
+    msvs_version = os.environ.get('GYP_MSVS_VERSION')
+    if not msvs_version or 'e' not in msvs_version:
+      raise ValueError('GYP_MSVS_OVERRIDE_PATH requires GYP_MSVS_VERSION to be '
+                       'set to an "e" version (e.g. 2010e)')
+    return _CreateVersion(msvs_version, override_path, sdk_based=True)
+  version = str(version)
+  versions = _DetectVisualStudioVersions(version_map[version], 'e' in version)
+  if not versions:
+    if version == 'auto':
+      # Default to 2005 if we couldn't find anything
+      return _CreateVersion('2005', None)
+    else:
+      return _CreateVersion(version, None)
+  return versions[0]
+
+def GenerateEnvironmentFiles(toplevel_build_dir, generator_flags, open_out):
+  """It's not sufficient to have the absolute path to the compiler, linker,
+  etc. on Windows, as those tools rely on .dlls being in the PATH. We also
+  need to support both x86 and x64 compilers within the same build (to support
+  msvs_target_platform hackery). Different architectures require a different
+  compiler binary, and different supporting environment variables (INCLUDE,
+  LIB, LIBPATH). So, we extract the environment here, wrap all invocations
+  of compiler tools (cl, link, lib, rc, midl, etc.) via win_tool.py which
+  sets up the environment, and then we do not prefix the compiler with
+  an absolute path, instead preferring something like "cl.exe" in the rule
+  which will then run whichever the environment setup has put in the path.
+  When the following procedure to generate environment files does not
+  meet your requirement (e.g. for custom toolchains), you can pass
+  "-G ninja_use_custom_environment_files" to the gyp to suppress file
+  generation and use custom environment files prepared by yourself."""
+  archs = ('x86', 'x64')
+  if generator_flags.get('ninja_use_custom_environment_files', 0):
+    cl_paths = {}
+    for arch in archs:
+      cl_paths[arch] = 'cl.exe'
+    return cl_paths
+  vs = GetVSVersion(generator_flags)
+  cl_paths = {}
+  for arch in archs:
+    # Extract environment variables for subprocesses.
+    args = vs.SetupScript(arch)
+    args.extend(('&&', 'set'))
+    popen = subprocess.Popen(
+        args, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+    variables, _ = popen.communicate()
+    env = _ExtractImportantEnvironment(variables)
+    env_block = _FormatAsEnvironmentBlock(env)
+    f = open_out(os.path.join(toplevel_build_dir, 'environment.' + arch), 'wb')
+    f.write(env_block)
+    f.close()
+
+    # Find cl.exe location for this architecture.
+    args = vs.SetupScript(arch)
+    args.extend(('&&',
+      'for', '%i', 'in', '(cl.exe)', 'do', '@echo', 'LOC:%~$PATH:i'))
+    popen = subprocess.Popen(args, shell=True, stdout=subprocess.PIPE)
+    output, _ = popen.communicate()
+    cl_paths[arch] = _ExtractCLPath(output)
+  return cl_paths
+
+def OpenOutput(path, mode='w'):
+  """Open |path| for writing, creating directories if necessary."""
+  try:
+    os.makedirs(os.path.dirname(path))
+  except OSError:
+    pass
+  return open(path, mode)
+
+vs_version = None
+def GetVSVersion(generator_flags):
+  global vs_version
+  if not vs_version:
+    vs_version = SelectVisualStudioVersion(
+        generator_flags.get('msvs_version', 'auto'))
+  return vs_version
+
+def _ExtractImportantEnvironment(output_of_set):
+  """Extracts environment variables required for the toolchain to run from
+  a textual dump output by the cmd.exe 'set' command."""
+  envvars_to_save = (
+      'goma_.*', # TODO(scottmg): This is ugly, but needed for goma.
+      'include',
+      'lib',
+      'libpath',
+      'path',
+      'pathext',
+      'systemroot',
+      'temp',
+      'tmp',
+      )
+  env = {}
+  for line in output_of_set.splitlines():
+    for envvar in envvars_to_save:
+      if re.match(envvar + '=', line.lower()):
+        var, setting = line.split('=', 1)
+        if envvar == 'path':
+          # Our own rules (for running gyp-win-tool) and other actions in
+          # Chromium rely on python being in the path. Add the path to this
+          # python here so that if it's not in the path when ninja is run
+          # later, python will still be found.
+          setting = os.path.dirname(sys.executable) + os.pathsep + setting
+        env[var.upper()] = setting
+        break
+  for required in ('SYSTEMROOT', 'TEMP', 'TMP'):
+    if required not in env:
+      raise Exception('Environment variable "%s" '
+                      'required to be set to valid path' % required)
+  return env
+
+def _FormatAsEnvironmentBlock(envvar_dict):
+  """Format as an 'environment block' directly suitable for CreateProcess.
+  Briefly this is a list of key=value\0, terminated by an additional \0. See
+  CreateProcess documentation for more details."""
+  block = ''
+  nul = '\0'
+  for key, value in envvar_dict.iteritems():
+    block += key + '=' + value + nul
+  block += nul
+  return block
+
+
+def GenerateEnvironmentFiles(toplevel_build_dir, generator_flags):
+  """It's not sufficient to have the absolute path to the compiler, linker,
+  etc. on Windows, as those tools rely on .dlls being in the PATH. We also
+  need to support both x86 and x64 compilers within the same build (to support
+  msvs_target_platform hackery). Different architectures require a different
+  compiler binary, and different supporting environment variables (INCLUDE,
+  LIB, LIBPATH). So, we extract the environment here, wrap all invocations
+  of compiler tools (cl, link, lib, rc, midl, etc.) via win_tool.py which
+  sets up the environment, and then we do not prefix the compiler with
+  an absolute path, instead preferring something like "cl.exe" in the rule
+  which will then run whichever the environment setup has put in the path.
+  When the following procedure to generate environment files does not
+  meet your requirement (e.g. for custom toolchains), you can pass
+  "-G ninja_use_custom_environment_files" to the gyp to suppress file
+  generation and use custom environment files prepared by yourself."""
+  archs = ('x86', 'x64')
+  if generator_flags.get('ninja_use_custom_environment_files', 0):
+    cl_paths = {}
+    for arch in archs:
+      cl_paths[arch] = 'cl.exe'
+    return cl_paths
+  vs = GetVSVersion(generator_flags)
+  cl_paths = {}
+  for arch in archs:
+    # Extract environment variables for subprocesses.
+    args = vs.SetupScript(arch)
+    args.extend(('&&', 'set'))
+    popen = subprocess.Popen(
+        args, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+    variables, _ = popen.communicate()
+    env = _ExtractImportantEnvironment(variables)
+    env_block = _FormatAsEnvironmentBlock(env)
+    f = OpenOutput(os.path.join(toplevel_build_dir, 'environment.' + arch), 'wb')
+    f.write(env_block)
+    f.close()
+
+    # Find cl.exe location for this architecture.
+    args = vs.SetupScript(arch)
+    args.extend(('&&',
+      'for', '%i', 'in', '(cl.exe)', 'do', '@echo', 'LOC:%~$PATH:i'))
+    popen = subprocess.Popen(args, shell=True, stdout=subprocess.PIPE)
+    output, _ = popen.communicate()
+    cl_paths[arch] = _ExtractCLPath(output)
+  return cl_paths
+
+def _ExtractCLPath(output_of_where):
+  """Gets the path to cl.exe based on the output of calling the environment
+  setup batch file, followed by the equivalent of `where`."""
+  # Take the first line, as that's the first found in the PATH.
+  for line in output_of_where.strip().splitlines():
+    if line.startswith('LOC:'):
+      return line[len('LOC:'):].strip()
+
+#print SelectVisualStudioVersion().DefaultToolset()
+#GenerateEnvironmentFiles("D:\\src\\src1\\src\\out\\gn\\eraseme", {})
+#print '"', GetVSVersion({}).Path(), '"'
+print '"', GetVSVersion({}).sdk_based, '"'
+
+#-------------------------------------------------------------------------------
+
+version_info = {
+  '2010': {
+    'includes': [
+      'VC\\atlmfc\\include',
+    ],
+  },
+}
diff --git a/tools/gn/secondary/ipc/BUILD.gn b/tools/gn/secondary/ipc/BUILD.gn
new file mode 100644
index 0000000..5e55ee9
--- /dev/null
+++ b/tools/gn/secondary/ipc/BUILD.gn
@@ -0,0 +1,168 @@
+# Copyright (c) 2012 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.

+

+component("ipc") {

+  sources = [

+    "file_descriptor_set_posix.cc",

+    "file_descriptor_set_posix.h",

+    "ipc_channel.cc",

+    "ipc_channel.h",

+    "ipc_channel_factory.cc",

+    "ipc_channel_factory.h",

+    "ipc_channel_handle.h",

+    "ipc_channel_nacl.cc",

+    "ipc_channel_nacl.h",

+    "ipc_channel_posix.cc",

+    "ipc_channel_posix.h",

+    "ipc_channel_proxy.cc",

+    "ipc_channel_proxy.h",

+    "ipc_channel_reader.cc",

+    "ipc_channel_reader.h",

+    "ipc_channel_win.cc",

+    "ipc_channel_win.h",

+    "ipc_descriptors.h",

+    "ipc_export.h",

+    "ipc_forwarding_message_filter.cc",

+    "ipc_forwarding_message_filter.h",

+    "ipc_listener.h",

+    "ipc_logging.cc",

+    "ipc_logging.h",

+    "ipc_message.cc",

+    "ipc_message.h",

+    "ipc_message_macros.h",

+    "ipc_message_start.h",

+    "ipc_message_utils.cc",

+    "ipc_message_utils.h",

+    "ipc_param_traits.h",

+    "ipc_platform_file.cc",

+    "ipc_platform_file.h",

+    "ipc_sender.h",

+    "ipc_switches.cc",

+    "ipc_switches.h",

+    "ipc_sync_channel.cc",

+    "ipc_sync_channel.h",

+    "ipc_sync_message.cc",

+    "ipc_sync_message.h",

+    "ipc_sync_message_filter.cc",

+    "ipc_sync_message_filter.h",

+    "param_traits_log_macros.h",

+    "param_traits_macros.h",

+    "param_traits_read_macros.h",

+    "param_traits_write_macros.h",

+    "struct_constructor_macros.h",

+    "struct_destructor_macros.h",

+    "unix_domain_socket_util.cc",

+    "unix_domain_socket_util.h",

+  ]

+

+  #if (!is_untrusted_nacl) {

+    sources -= [

+      "ipc_channel_nacl.cc",

+      "ipc_channel_nacl.h",

+    ]

+

+  if (is_win || is_ios) {

+    sources -= [

+      "ipc_channel_factory.cc",

+      "unix_domain_socket_util.cc",

+    ]

+  }

+

+  defines = [ "IPC_IMPLEMENTATION" ]

+

+  deps = [

+    "//base",

+    # TODO(viettrungluu): Needed for base/lazy_instance.h, which is suspect.

+    "//base/third_party/dynamic_annotations",

+  ]

+}

+

+test("ipc_tests") {

+  sources = [

+    "file_descriptor_set_posix_unittest.cc",

+    "ipc_channel_posix_unittest.cc",

+    "ipc_channel_unittest.cc",

+    "ipc_fuzzing_tests.cc",

+    "ipc_message_unittest.cc",

+    "ipc_message_utils_unittest.cc",

+    "ipc_send_fds_test.cc",

+    "ipc_sync_channel_unittest.cc",

+    "ipc_sync_message_unittest.cc",

+    "ipc_sync_message_unittest.h",

+    "ipc_test_base.cc",

+    "ipc_test_base.h",

+    "sync_socket_unittest.cc",

+    "unix_domain_socket_util_unittest.cc",

+  ]

+

+  #if (toolkit_uses_gtk) {

+  #  deps += "/build/linux/system:gtk"

+  #}

+  if (is_win || is_ios) {

+    sources -= "unix_domain_socket_util_unittest.cc"

+  }

+  #if (is_android && gtest_target_type == "shared_library") {

+  #  deps += "/testing/android/native_test.gyp:native_testNative_code"

+  #}

+  #if (is_posix && !is_mac && !is_android) {

+  #  if (linux_use_tcmalloc) {

+  #    deps += "/base/allocator"

+  #  }

+  #}

+

+  deps = [

+    ":ipc",

+    ":test_support_ipc",

+    "//base",

+    "//base:base_i18n",

+    "//base:run_all_unittests",

+    "//base:test_support_base",

+    "//testing:gtest",

+  ]

+}

+

+test("ipc_perftests") {

+  sources = [

+    "ipc_perftests.cc",

+    "ipc_test_base.cc",

+    "ipc_test_base.h",

+  ]

+

+  #if (toolkit_uses_gtk) {

+  #  deps += "/build/linux/system:gtk"

+  #}

+  #if (is_android && gtest_target_type == "shared_library") {

+  #  deps += "/testing/android/native_test.gyp:native_testNative_code"

+  #}

+  #if (is_posix && !is_mac && !is_android) {

+  #  if (linux_use_tcmalloc) {

+  #    deps += "/base/allocator"

+  #  }

+  #}

+

+  deps = [

+    ":ipc",

+    ":test_support_ipc",

+    "//base",

+    "//base:base_i18n",

+    "//base:test_support_base",

+    "//base:test_support_perf",

+    "//testing:gtest",

+  ]

+}

+

+static_library("test_support_ipc") {

+  sources = [

+    "ipc_multiprocess_test.cc",

+    "ipc_multiprocess_test.h",

+    "ipc_test_sink.cc",

+    "ipc_test_sink.h",

+  ]

+  deps = [

+    ":ipc",

+    "//base",

+    "//testing:gtest",

+  ]

+}

+

diff --git a/tools/gn/secondary/testing/BUILD.gn b/tools/gn/secondary/testing/BUILD.gn
new file mode 100644
index 0000000..b54ceba
--- /dev/null
+++ b/tools/gn/secondary/testing/BUILD.gn
@@ -0,0 +1,44 @@
+# Copyright (c) 2013 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.

+

+static_library("gtest") {

+  sources = [

+    "gtest/include/gtest/gtest-death-test.h",

+    "gtest/include/gtest/gtest-message.h",

+    "gtest/include/gtest/gtest-param-test.h",

+    "gtest/include/gtest/gtest-printers.h",

+    "gtest/include/gtest/gtest-spi.h",

+    "gtest/include/gtest/gtest-test-part.h",

+    "gtest/include/gtest/gtest-typed-test.h",

+    "gtest/include/gtest/gtest.h",

+    "gtest/include/gtest/gtest_pred_impl.h",

+    "gtest/include/gtest/internal/gtest-death-test-internal.h",

+    "gtest/include/gtest/internal/gtest-filepath.h",

+    "gtest/include/gtest/internal/gtest-internal.h",

+    "gtest/include/gtest/internal/gtest-linked_ptr.h",

+    "gtest/include/gtest/internal/gtest-param-util-generated.h",

+    "gtest/include/gtest/internal/gtest-param-util.h",

+    "gtest/include/gtest/internal/gtest-port.h",

+    "gtest/include/gtest/internal/gtest-string.h",

+    "gtest/include/gtest/internal/gtest-tuple.h",

+    "gtest/include/gtest/internal/gtest-type-util.h",

+    #"gtest/src/gtest-all.cc",  # Not needed by our build.

+    "gtest/src/gtest-death-test.cc",

+    "gtest/src/gtest-filepath.cc",

+    "gtest/src/gtest-internal-inl.h",

+    "gtest/src/gtest-port.cc",

+    "gtest/src/gtest-printers.cc",

+    "gtest/src/gtest-test-part.cc",

+    "gtest/src/gtest-typed-test.cc",

+    "gtest/src/gtest.cc",

+    "multiprocess_func_list.cc",

+    "multiprocess_func_list.h",

+    "platform_test.h",

+  ]

+

+}

+

+static_library("gmock") {

+

+}

diff --git a/tools/gn/secondary/third_party/modp_b64/BUILD.gn b/tools/gn/secondary/third_party/modp_b64/BUILD.gn
new file mode 100644
index 0000000..5598f12
--- /dev/null
+++ b/tools/gn/secondary/third_party/modp_b64/BUILD.gn
@@ -0,0 +1,11 @@
+# Copyright (c) 2013 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.

+

+static_library("modp_b64") {

+  sources = [

+    "modp_b64.cc",

+    "modp_b64.h",

+    "modp_b64_data.h",

+  ]

+}

diff --git a/tools/gn/settings.cc b/tools/gn/settings.cc
new file mode 100644
index 0000000..82014a7
--- /dev/null
+++ b/tools/gn/settings.cc
@@ -0,0 +1,38 @@
+// Copyright (c) 2013 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/settings.h"
+
+#include "base/logging.h"
+#include "tools/gn/filesystem_utils.h"
+
+Settings::Settings(const BuildSettings* build_settings,
+                   const Toolchain* toolchain,
+                   const std::string& output_subdir_name)
+    : build_settings_(build_settings),
+      toolchain_(toolchain),
+      target_os_(WIN),  // FIXME(brettw) set this properly.
+      import_manager_(),
+      base_config_(this) {
+  DCHECK(output_subdir_name.find('/') == std::string::npos);
+  if (output_subdir_name.empty()) {
+    toolchain_output_dir_ = build_settings->build_dir();
+  } else {
+    // We guarantee this ends in a slash.
+    toolchain_output_subdir_.value().append(output_subdir_name);
+    toolchain_output_subdir_.value().push_back('/');
+
+    toolchain_output_dir_ = SourceDir(build_settings->build_dir().value() +
+                                      toolchain_output_subdir_.value());
+  }
+  // The output dir will be null in some tests and when invoked to parsed
+  // one-off data without doing generation.
+  if (!toolchain_output_dir_.is_null())
+    toolchain_gen_dir_ = SourceDir(toolchain_output_dir_.value() + "gen/");
+}
+
+Settings::~Settings() {
+}
+
+
diff --git a/tools/gn/settings.h b/tools/gn/settings.h
new file mode 100644
index 0000000..93a1ae6
--- /dev/null
+++ b/tools/gn/settings.h
@@ -0,0 +1,107 @@
+// Copyright (c) 2013 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.
+
+#ifndef TOOLS_GN_SETTINGS_H_
+#define TOOLS_GN_SETTINGS_H_
+
+#include "base/files/file_path.h"
+#include "tools/gn/build_settings.h"
+#include "tools/gn/import_manager.h"
+#include "tools/gn/output_file.h"
+#include "tools/gn/scope.h"
+#include "tools/gn/source_dir.h"
+#include "tools/gn/toolchain.h"
+
+// Holds the settings for one toolchain invocation. There will be one
+// Settings object for each toolchain type, each referring to the same
+// BuildSettings object for shared stuff.
+//
+// The Settings object is const once it is constructed, which allows us to
+// use it from multiple threads during target generation without locking (which
+// is important, because it gets used a lot).
+//
+// The Toolchain object holds the set of stuff that is set by the toolchain
+// declaration, which obviously needs to be set later when we actually parse
+// the file with the toolchain declaration in it.
+class Settings {
+ public:
+  enum TargetOS {
+    UNKNOWN,
+    LINUX,
+    MAC,
+    WIN
+  };
+
+  // Constructs a toolchain settings. The output_subdir_name is the name we
+  // should use for the subdirectory in the build output directory for this
+  // toolchain's outputs. It should have no slashes in it. The default
+  // toolchain should use an empty string.
+  Settings(const BuildSettings* build_settings,
+           const Toolchain* toolchain,
+           const std::string& output_subdir_name);
+  ~Settings();
+
+  const BuildSettings* build_settings() const { return build_settings_; }
+
+  // Danger: this must only be used for getting the toolchain label until the
+  // toolchain has been resolved. Otherwise, it will be modified on an
+  // arbitrary thread when the toolchain invocation is found. Generally, you
+  // will only read this from the target generation where we know everything
+  // has been resolved and won't change.
+  const Toolchain* toolchain() const { return toolchain_; }
+
+  bool IsMac() const { return target_os_ == MAC; }
+  bool IsWin() const { return target_os_ == WIN; }
+
+  TargetOS target_os() const { return target_os_; }
+  void set_target_os(TargetOS t) { target_os_ = t; }