Move Tool logic out of Toolchain

This sets up the ability for tools to self-manage and dispatch, and
replaces the ToolType enum with the more dynamic tool name constant.

This is part of the Tool refactoring to allow for additional sets of
tools to be added, enabling support for new languages to be added. Same
as in the previous patch, this unfortunately requires touching a bunch
of files since the enum is prevalent.

Change-Id: Ia9eebb2525749aa0c525f85a9d89ce9ca6ca0159
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/4462
Commit-Queue: Julie Hockett <juliehockett@google.com>
Reviewed-by: Brett Wilson <brettw@google.com>
diff --git a/build/gen.py b/build/gen.py
index d547a8f..75210d7 100755
--- a/build/gen.py
+++ b/build/gen.py
@@ -425,6 +425,7 @@
         'tools/gn/bundle_data_target_generator.cc',
         'tools/gn/bundle_file_rule.cc',
         'tools/gn/c_include_iterator.cc',
+        'tools/gn/c_tool.cc',
         'tools/gn/command_analyze.cc',
         'tools/gn/command_args.cc',
         'tools/gn/command_check.cc',
@@ -468,6 +469,7 @@
         'tools/gn/function_template.cc',
         'tools/gn/function_toolchain.cc',
         'tools/gn/function_write_file.cc',
+        'tools/gn/general_tool.cc',
         'tools/gn/generated_file_target_generator.cc',
         'tools/gn/group_target_generator.cc',
         'tools/gn/header_checker.cc',
diff --git a/tools/gn/analyzer_unittest.cc b/tools/gn/analyzer_unittest.cc
index a6d73a2..d796300 100644
--- a/tools/gn/analyzer_unittest.cc
+++ b/tools/gn/analyzer_unittest.cc
@@ -3,16 +3,19 @@
 // found in the LICENSE file.
 
 #include "tools/gn/analyzer.h"
+
+#include <tools/gn/c_tool.h>
 #include "tools/gn/build_settings.h"
 #include "tools/gn/builder.h"
 #include "tools/gn/config.h"
+#include "tools/gn/general_tool.h"
 #include "tools/gn/loader.h"
 #include "tools/gn/pool.h"
 #include "tools/gn/settings.h"
 #include "tools/gn/source_file.h"
 #include "tools/gn/substitution_list.h"
 #include "tools/gn/target.h"
-#include "tools/gn/tool.h"
+#include <tools/gn/tool.h>
 #include "tools/gn/toolchain.h"
 #include "util/test/test.h"
 
@@ -378,10 +381,10 @@
 
   // The tool is not used anywhere, but is required to construct the dependency
   // between a target and the toolchain.
-  std::unique_ptr<Tool> fake_tool(new Tool());
+  std::unique_ptr<Tool> fake_tool = Tool::CreateTool(CTool::kCToolLink);
   fake_tool->set_outputs(
       SubstitutionList::MakeForTest("//out/debug/output.txt"));
-  toolchain->SetTool(Toolchain::TYPE_LINK, std::move(fake_tool));
+  toolchain->SetTool(std::move(fake_tool));
   builder_.ItemDefined(std::unique_ptr<Item>(target));
   builder_.ItemDefined(std::unique_ptr<Item>(toolchain));
 
diff --git a/tools/gn/builder.cc b/tools/gn/builder.cc
index 1d04db0..fc271c4 100644
--- a/tools/gn/builder.cc
+++ b/tools/gn/builder.cc
@@ -276,14 +276,13 @@
   if (!AddDeps(record, toolchain->deps(), err))
     return false;
 
-  for (int i = Toolchain::TYPE_NONE + 1; i < Toolchain::TYPE_NUMTYPES; i++) {
-    Toolchain::ToolType tool_type = static_cast<Toolchain::ToolType>(i);
-    Tool* tool = toolchain->GetTool(tool_type);
-    if (!tool || tool->pool().label.is_null())
+  for (const auto& tool : toolchain->tools()) {
+    if (tool.second->pool().label.is_null())
       continue;
 
     BuilderRecord* dep_record = GetOrCreateRecordOfType(
-        tool->pool().label, tool->pool().origin, BuilderRecord::ITEM_POOL, err);
+        tool.second->pool().label, tool.second->pool().origin,
+        BuilderRecord::ITEM_POOL, err);
     if (!dep_record)
       return false;
     record->AddDep(dep_record);
@@ -565,23 +564,21 @@
 }
 
 bool Builder::ResolvePools(Toolchain* toolchain, Err* err) {
-  for (int i = Toolchain::TYPE_NONE + 1; i < Toolchain::TYPE_NUMTYPES; i++) {
-    Toolchain::ToolType tool_type = static_cast<Toolchain::ToolType>(i);
-    Tool* tool = toolchain->GetTool(tool_type);
-    if (!tool || tool->pool().label.is_null())
+  for (const auto& tool : toolchain->tools()) {
+    if (tool.second->pool().label.is_null())
       continue;
 
-    BuilderRecord* record =
-        GetResolvedRecordOfType(tool->pool().label, toolchain->defined_from(),
-                                BuilderRecord::ITEM_POOL, err);
+    BuilderRecord* record = GetResolvedRecordOfType(
+        tool.second->pool().label, toolchain->defined_from(),
+        BuilderRecord::ITEM_POOL, err);
     if (!record) {
-      *err = Err(tool->pool().origin, "Pool for tool not defined.",
+      *err = Err(tool.second->pool().origin, "Pool for tool not defined.",
                  "I was hoping to find a pool " +
-                     tool->pool().label.GetUserVisibleName(false));
+                     tool.second->pool().label.GetUserVisibleName(false));
       return false;
     }
 
-    tool->set_pool(LabelPtrPair<Pool>(record->item()->AsPool()));
+    tool.second->set_pool(LabelPtrPair<Pool>(record->item()->AsPool()));
   }
 
   return true;
diff --git a/tools/gn/c_tool.cc b/tools/gn/c_tool.cc
new file mode 100644
index 0000000..6167286
--- /dev/null
+++ b/tools/gn/c_tool.cc
@@ -0,0 +1,232 @@
+// Copyright 2019 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/c_tool.h"
+#include "tools/gn/target.h"
+
+const char* CTool::kCToolCc = "cc";
+const char* CTool::kCToolCxx = "cxx";
+const char* CTool::kCToolObjC = "objc";
+const char* CTool::kCToolObjCxx = "objcxx";
+const char* CTool::kCToolRc = "rc";
+const char* CTool::kCToolAsm = "asm";
+const char* CTool::kCToolAlink = "alink";
+const char* CTool::kCToolSolink = "solink";
+const char* CTool::kCToolSolinkModule = "solink_module";
+const char* CTool::kCToolLink = "link";
+
+CTool::CTool(const char* n)
+    : Tool(n), depsformat_(DEPS_GCC), precompiled_header_type_(PCH_NONE) {
+  CHECK(ValidateName(n));
+}
+
+CTool::~CTool() = default;
+
+CTool* CTool::AsC() {
+  return this;
+}
+const CTool* CTool::AsC() const {
+  return this;
+}
+
+bool CTool::ValidateName(const char* name) const {
+  return name == kCToolCc || name == kCToolCxx || name == kCToolObjC ||
+         name == kCToolObjCxx || name == kCToolRc || name == kCToolAsm ||
+         name == kCToolAlink || name == kCToolSolink ||
+         name == kCToolSolinkModule || name == kCToolLink;
+}
+
+void CTool::SetComplete() {
+  SetToolComplete();
+  link_output_.FillRequiredTypes(&substitution_bits_);
+  depend_output_.FillRequiredTypes(&substitution_bits_);
+}
+
+bool CTool::ValidateRuntimeOutputs(Err* err) {
+  if (runtime_outputs().list().empty())
+    return true;  // Empty is always OK.
+
+  if (name_ != kCToolSolink && name_ != kCToolSolinkModule &&
+      name_ != kCToolLink) {
+    *err = Err(defined_from(), "This tool specifies runtime_outputs.",
+               "This is only valid for linker tools (alink doesn't count).");
+    return false;
+  }
+
+  for (const SubstitutionPattern& pattern : runtime_outputs().list()) {
+    if (!IsPatternInOutputList(outputs(), pattern)) {
+      *err = Err(defined_from(), "This tool's runtime_outputs is bad.",
+                 "It must be a subset of the outputs. The bad one is:\n  " +
+                     pattern.AsString());
+      return false;
+    }
+  }
+  return true;
+}
+
+// Validates either link_output or depend_output. To generalize to either, pass
+// the associated pattern, and the variable name that should appear in error
+// messages.
+bool CTool::ValidateLinkAndDependOutput(const SubstitutionPattern& pattern,
+                                        const char* variable_name,
+                                        Err* err) {
+  if (pattern.empty())
+    return true;  // Empty is always OK.
+
+  // It should only be specified for certain tool types.
+  if (name_ != kCToolSolink && name_ != kCToolSolinkModule) {
+    *err = Err(defined_from(),
+               "This tool specifies a " + std::string(variable_name) + ".",
+               "This is only valid for solink and solink_module tools.");
+    return false;
+  }
+
+  if (!IsPatternInOutputList(outputs(), pattern)) {
+    *err = Err(defined_from(), "This tool's link_output is bad.",
+               "It must match one of the outputs.");
+    return false;
+  }
+
+  return true;
+}
+
+bool CTool::ReadPrecompiledHeaderType(Scope* scope, Err* err) {
+  const Value* value = scope->GetValue("precompiled_header_type", true);
+  if (!value)
+    return true;  // Not present is fine.
+  if (!value->VerifyTypeIs(Value::STRING, err))
+    return false;
+
+  if (value->string_value().empty())
+    return true;  // Accept empty string, do nothing (default is "no PCH").
+
+  if (value->string_value() == "gcc") {
+    set_precompiled_header_type(PCH_GCC);
+    return true;
+  } else if (value->string_value() == "msvc") {
+    set_precompiled_header_type(PCH_MSVC);
+    return true;
+  }
+  *err = Err(*value, "Invalid precompiled_header_type",
+             "Must either be empty, \"gcc\", or \"msvc\".");
+  return false;
+}
+
+bool CTool::ReadDepsFormat(Scope* scope, Err* err) {
+  const Value* value = scope->GetValue("depsformat", true);
+  if (!value)
+    return true;  // Not present is fine.
+  if (!value->VerifyTypeIs(Value::STRING, err))
+    return false;
+
+  if (value->string_value() == "gcc") {
+    set_depsformat(DEPS_GCC);
+  } else if (value->string_value() == "msvc") {
+    set_depsformat(DEPS_MSVC);
+  } else {
+    *err = Err(*value, "Deps format must be \"gcc\" or \"msvc\".");
+    return false;
+  }
+  return true;
+}
+
+bool CTool::ReadOutputsPatternList(Scope* scope,
+                                   const char* var,
+                                   SubstitutionList* field,
+                                   Err* err) {
+  DCHECK(!complete_);
+  const Value* value = scope->GetValue(var, true);
+  if (!value)
+    return true;  // Not present is fine.
+  if (!value->VerifyTypeIs(Value::LIST, err))
+    return false;
+
+  SubstitutionList list;
+  if (!list.Parse(*value, err))
+    return false;
+
+  // Validate the right kinds of patterns are used.
+  if (list.list().empty()) {
+    *err = Err(defined_from(), "\"outputs\" must be specified for this tool.");
+    return false;
+  }
+
+  for (const auto& cur_type : list.required_types()) {
+    if (!ValidateOutputSubstitution(cur_type)) {
+      *err = Err(*value, "Pattern not valid here.",
+                 "You used the pattern " +
+                     std::string(kSubstitutionNames[cur_type]) +
+                     " which is not valid\nfor this variable.");
+      return false;
+    }
+  }
+
+  *field = std::move(list);
+  return true;
+}
+
+bool CTool::InitTool(Scope* scope, Toolchain* toolchain, Err* err) {
+  // Initialize default vars.
+  if (!Tool::InitTool(scope, toolchain, err)) {
+    return false;
+  }
+
+  // All C tools should have outputs.
+  if (!ReadOutputsPatternList(scope, "outputs", &outputs_, err)) {
+    return false;
+  }
+
+  if (!ReadDepsFormat(scope, err) || !ReadPrecompiledHeaderType(scope, err) ||
+      !ReadString(scope, "lib_switch", &lib_switch_, err) ||
+      !ReadString(scope, "lib_dir_switch", &lib_dir_switch_, err) ||
+      !ReadPattern(scope, "link_output", &link_output_, err) ||
+      !ReadPattern(scope, "depend_output", &depend_output_, err)) {
+    return false;
+  }
+
+  // Validate link_output and depend_output.
+  if (!ValidateLinkAndDependOutput(link_output(), "link_output", err)) {
+    return false;
+  }
+  if (!ValidateLinkAndDependOutput(depend_output(), "depend_output", err)) {
+    return false;
+  }
+  if ((!link_output().empty() && depend_output().empty()) ||
+      (link_output().empty() && !depend_output().empty())) {
+    *err = Err(defined_from(),
+               "Both link_output and depend_output should either "
+               "be specified or they should both be empty.");
+    return false;
+  }
+
+  if (!ValidateRuntimeOutputs(err)) {
+    return false;
+  }
+  return true;
+}
+
+bool CTool::ValidateSubstitution(SubstitutionType sub_type) const {
+  if (name_ == kCToolCc || name_ == kCToolCxx || name_ == kCToolObjC ||
+      name_ == kCToolObjCxx || name_ == kCToolRc || name_ == kCToolAsm)
+    return IsValidCompilerSubstitution(sub_type);
+  else if (name_ == kCToolAlink)
+    return IsValidALinkSubstitution(sub_type);
+  else if (name_ == kCToolSolink || name_ == kCToolSolinkModule ||
+           name_ == kCToolLink)
+    return IsValidLinkerSubstitution(sub_type);
+  NOTREACHED();
+  return false;
+}
+
+bool CTool::ValidateOutputSubstitution(SubstitutionType sub_type) const {
+  if (name_ == kCToolCc || name_ == kCToolCxx || name_ == kCToolObjC ||
+      name_ == kCToolObjCxx || name_ == kCToolRc || name_ == kCToolAsm)
+    return IsValidCompilerOutputsSubstitution(sub_type);
+  // ALink uses the standard output file patterns as other linker tools.
+  else if (name_ == kCToolAlink || name_ == kCToolSolink ||
+           name_ == kCToolSolinkModule || name_ == kCToolLink)
+    return IsValidLinkerOutputsSubstitution(sub_type);
+  NOTREACHED();
+  return false;
+}
diff --git a/tools/gn/c_tool.h b/tools/gn/c_tool.h
new file mode 100644
index 0000000..045024c
--- /dev/null
+++ b/tools/gn/c_tool.h
@@ -0,0 +1,138 @@
+// Copyright 2019 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_C_TOOL_H_
+#define TOOLS_GN_C_TOOL_H_
+
+#include <string>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "tools/gn/label.h"
+#include "tools/gn/label_ptr.h"
+#include "tools/gn/scope.h"
+#include "tools/gn/source_file_type.h"
+#include "tools/gn/substitution_list.h"
+#include "tools/gn/substitution_pattern.h"
+#include "tools/gn/tool.h"
+#include "tools/gn/toolchain.h"
+
+class CTool : public Tool {
+ public:
+  // C compiler tools
+  static const char* kCToolCc;
+  static const char* kCToolCxx;
+  static const char* kCToolObjC;
+  static const char* kCToolObjCxx;
+  static const char* kCToolRc;
+  static const char* kCToolAsm;
+
+  // C linker tools
+  static const char* kCToolAlink;
+  static const char* kCToolSolink;
+  static const char* kCToolSolinkModule;
+  static const char* kCToolLink;
+
+  enum DepsFormat { DEPS_GCC = 0, DEPS_MSVC = 1 };
+
+  enum PrecompiledHeaderType { PCH_NONE = 0, PCH_GCC = 1, PCH_MSVC = 2 };
+
+  CTool(const char* n);
+  ~CTool();
+
+  // Manual RTTI and required functions ---------------------------------------
+
+  bool InitTool(Scope* block_scope, Toolchain* toolchain, Err* err);
+  bool ValidateName(const char* name) const override;
+  void SetComplete() override;
+  bool ValidateSubstitution(SubstitutionType sub_type) const override;
+
+  CTool* AsC() override;
+  const CTool* AsC() const override;
+
+  // Getters/setters ----------------------------------------------------------
+  //
+  // After the tool has had its attributes set, the caller must call
+  // SetComplete(), at which point no other changes can be made.
+
+  DepsFormat depsformat() const { return depsformat_; }
+  void set_depsformat(DepsFormat f) {
+    DCHECK(!complete_);
+    depsformat_ = f;
+  }
+
+  PrecompiledHeaderType precompiled_header_type() const {
+    return precompiled_header_type_;
+  }
+  void set_precompiled_header_type(PrecompiledHeaderType pch_type) {
+    DCHECK(!complete_);
+    precompiled_header_type_ = pch_type;
+  }
+
+  const std::string& lib_switch() const { return lib_switch_; }
+  void set_lib_switch(std::string s) {
+    DCHECK(!complete_);
+    lib_switch_ = std::move(s);
+  }
+
+  const std::string& lib_dir_switch() const { return lib_dir_switch_; }
+  void set_lib_dir_switch(std::string s) {
+    DCHECK(!complete_);
+    lib_dir_switch_ = std::move(s);
+  }
+
+  // Should match files in the outputs() if nonempty.
+  const SubstitutionPattern& link_output() const { return link_output_; }
+  void set_link_output(SubstitutionPattern link_out) {
+    DCHECK(!complete_);
+    link_output_ = std::move(link_out);
+  }
+
+  const SubstitutionPattern& depend_output() const { return depend_output_; }
+  void set_depend_output(SubstitutionPattern dep_out) {
+    DCHECK(!complete_);
+    depend_output_ = std::move(dep_out);
+  }
+
+  // Other functions ----------------------------------------------------------
+
+  // Returns true if this tool has separate outputs for dependency tracking
+  // and linking.
+  bool has_separate_solink_files() const {
+    return !link_output_.empty() || !depend_output_.empty();
+  }
+
+ private:
+  // Initialization functions -------------------------------------------------
+  //
+  // Initialization methods used by InitTool(). If successful, will set the
+  // field and return true, otherwise will return false. Must be called before
+  // SetComplete().
+  bool ValidateOutputSubstitution(SubstitutionType sub_type) const;
+  bool ValidateRuntimeOutputs(Err* err);
+  // Validates either link_output or depend_output. To generalize to either,
+  // pass
+  // the associated pattern, and the variable name that should appear in error
+  // messages.
+  bool ValidateLinkAndDependOutput(const SubstitutionPattern& pattern,
+                                   const char* variable_name,
+                                   Err* err);
+  bool ReadOutputsPatternList(Scope* scope,
+                              const char* var,
+                              SubstitutionList* field,
+                              Err* err);
+  bool ReadPrecompiledHeaderType(Scope* scope, Err* err);
+  bool ReadDepsFormat(Scope* scope, Err* err);
+
+  DepsFormat depsformat_;
+  PrecompiledHeaderType precompiled_header_type_;
+  std::string lib_switch_;
+  std::string lib_dir_switch_;
+  SubstitutionPattern link_output_;
+  SubstitutionPattern depend_output_;
+
+  DISALLOW_COPY_AND_ASSIGN(CTool);
+};
+
+#endif  // TOOLS_GN_C_TOOL_H_
diff --git a/tools/gn/compile_commands_writer.cc b/tools/gn/compile_commands_writer.cc
index 00eb00a..e0af121 100644
--- a/tools/gn/compile_commands_writer.cc
+++ b/tools/gn/compile_commands_writer.cc
@@ -10,6 +10,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/strings/string_split.h"
 #include "tools/gn/builder.h"
+#include "tools/gn/c_tool.h"
 #include "tools/gn/config_values_extractors.h"
 #include "tools/gn/deps_iterator.h"
 #include "tools/gn/escape.h"
@@ -67,33 +68,33 @@
   base::EscapeJSONString(includes_out.str(), false, &flags.includes);
 
   std::ostringstream cflags_out;
-  WriteOneFlag(target, SUBSTITUTION_CFLAGS, false, Toolchain::TYPE_NONE,
+  WriteOneFlag(target, SUBSTITUTION_CFLAGS, false, Tool::kToolNone,
                &ConfigValues::cflags, opts, path_output, cflags_out,
                /*write_substitution=*/false);
   base::EscapeJSONString(cflags_out.str(), false, &flags.cflags);
 
   std::ostringstream cflags_c_out;
   WriteOneFlag(target, SUBSTITUTION_CFLAGS_C, has_precompiled_headers,
-               Toolchain::TYPE_CC, &ConfigValues::cflags_c, opts, path_output,
+               CTool::kCToolCc, &ConfigValues::cflags_c, opts, path_output,
                cflags_c_out, /*write_substitution=*/false);
   base::EscapeJSONString(cflags_c_out.str(), false, &flags.cflags_c);
 
   std::ostringstream cflags_cc_out;
   WriteOneFlag(target, SUBSTITUTION_CFLAGS_CC, has_precompiled_headers,
-               Toolchain::TYPE_CXX, &ConfigValues::cflags_cc, opts, path_output,
+               CTool::kCToolCxx, &ConfigValues::cflags_cc, opts, path_output,
                cflags_cc_out, /*write_substitution=*/false);
   base::EscapeJSONString(cflags_cc_out.str(), false, &flags.cflags_cc);
 
   std::ostringstream cflags_objc_out;
   WriteOneFlag(target, SUBSTITUTION_CFLAGS_OBJC, has_precompiled_headers,
-               Toolchain::TYPE_OBJC, &ConfigValues::cflags_objc, opts,
-               path_output, cflags_objc_out,
+               CTool::kCToolObjC, &ConfigValues::cflags_objc, opts, path_output,
+               cflags_objc_out,
                /*write_substitution=*/false);
   base::EscapeJSONString(cflags_objc_out.str(), false, &flags.cflags_objc);
 
   std::ostringstream cflags_objcc_out;
   WriteOneFlag(target, SUBSTITUTION_CFLAGS_OBJCC, has_precompiled_headers,
-               Toolchain::TYPE_OBJCXX, &ConfigValues::cflags_objcc, opts,
+               CTool::kCToolObjCxx, &ConfigValues::cflags_objcc, opts,
                path_output, cflags_objcc_out, /*write_substitution=*/false);
   base::EscapeJSONString(cflags_objcc_out.str(), false, &flags.cflags_objcc);
 }
@@ -121,12 +122,12 @@
                   std::vector<OutputFile>& tool_outputs,
                   PathOutput& path_output,
                   SourceFileType source_type,
-                  Toolchain::ToolType tool_type,
+                  const char* tool_name,
                   EscapeOptions opts,
                   std::string* compile_commands) {
   EscapeOptions no_quoting(opts);
   no_quoting.inhibit_quoting = true;
-  const Tool* tool = target->toolchain()->GetTool(tool_type);
+  const Tool* tool = target->toolchain()->GetTool(tool_name);
   std::ostringstream command_out;
 
   for (const auto& range : tool->command().ranges()) {
@@ -237,8 +238,8 @@
           source_type != SOURCE_M && source_type != SOURCE_MM)
         continue;
 
-      Toolchain::ToolType tool_type = Toolchain::TYPE_NONE;
-      if (!target->GetOutputFilesForSource(source, &tool_type, &tool_outputs))
+      const char* tool_name = Tool::kToolNone;
+      if (!target->GetOutputFilesForSource(source, &tool_name, &tool_outputs))
         continue;
 
       if (!first) {
@@ -253,7 +254,7 @@
       WriteDirectory(base::StringPrintf("%" PRIsFP, build_dir.value().c_str()),
                      compile_commands);
       WriteCommand(target, source, flags, tool_outputs, path_output,
-                   source_type, tool_type, opts, compile_commands);
+                   source_type, tool_name, opts, compile_commands);
       compile_commands->append("\"");
       compile_commands->append(kPrettyPrintLineEnding);
       compile_commands->append("  }");
diff --git a/tools/gn/compile_commands_writer_unittest.cc b/tools/gn/compile_commands_writer_unittest.cc
index 90429a4..a3e3fb5 100644
--- a/tools/gn/compile_commands_writer_unittest.cc
+++ b/tools/gn/compile_commands_writer_unittest.cc
@@ -264,26 +264,28 @@
   pch_settings.set_default_toolchain_label(toolchain()->label());
 
   // Declare a C++ compiler that supports PCH.
-  std::unique_ptr<Tool> cxx_tool = std::make_unique<Tool>();
+  std::unique_ptr<Tool> cxx = Tool::CreateTool(CTool::kCToolCxx);
+  CTool* cxx_tool = cxx->AsC();
   TestWithScope::SetCommandForTool(
       "c++ {{source}} {{cflags}} {{cflags_cc}} {{defines}} {{include_dirs}} "
       "-o {{output}}",
-      cxx_tool.get());
+      cxx_tool);
   cxx_tool->set_outputs(SubstitutionList::MakeForTest(
       "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
-  cxx_tool->set_precompiled_header_type(Tool::PCH_MSVC);
-  pch_toolchain.SetTool(Toolchain::TYPE_CXX, std::move(cxx_tool));
+  cxx_tool->set_precompiled_header_type(CTool::PCH_MSVC);
+  pch_toolchain.SetTool(std::move(cxx));
 
   // Add a C compiler as well.
-  std::unique_ptr<Tool> cc_tool = std::make_unique<Tool>();
+  std::unique_ptr<Tool> cc = Tool::CreateTool(CTool::kCToolCc);
+  CTool* cc_tool = cc->AsC();
   TestWithScope::SetCommandForTool(
       "cc {{source}} {{cflags}} {{cflags_c}} {{defines}} {{include_dirs}} "
       "-o {{output}}",
-      cc_tool.get());
+      cc_tool);
   cc_tool->set_outputs(SubstitutionList::MakeForTest(
       "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
-  cc_tool->set_precompiled_header_type(Tool::PCH_MSVC);
-  pch_toolchain.SetTool(Toolchain::TYPE_CC, std::move(cc_tool));
+  cc_tool->set_precompiled_header_type(CTool::PCH_MSVC);
+  pch_toolchain.SetTool(std::move(cc));
   pch_toolchain.ToolchainSetupComplete();
 
   // This target doesn't specify precompiled headers.
@@ -412,27 +414,29 @@
   pch_settings.set_default_toolchain_label(toolchain()->label());
 
   // Declare a C++ compiler that supports PCH.
-  std::unique_ptr<Tool> cxx_tool = std::make_unique<Tool>();
+  std::unique_ptr<Tool> cxx = Tool::CreateTool(CTool::kCToolCxx);
+  CTool* cxx_tool = cxx->AsC();
   TestWithScope::SetCommandForTool(
       "c++ {{source}} {{cflags}} {{cflags_cc}} {{defines}} {{include_dirs}} "
       "-o {{output}}",
-      cxx_tool.get());
+      cxx_tool);
   cxx_tool->set_outputs(SubstitutionList::MakeForTest(
       "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
-  cxx_tool->set_precompiled_header_type(Tool::PCH_GCC);
-  pch_toolchain.SetTool(Toolchain::TYPE_CXX, std::move(cxx_tool));
+  cxx_tool->set_precompiled_header_type(CTool::PCH_GCC);
+  pch_toolchain.SetTool(std::move(cxx));
   pch_toolchain.ToolchainSetupComplete();
 
   // Add a C compiler as well.
-  std::unique_ptr<Tool> cc_tool = std::make_unique<Tool>();
+  std::unique_ptr<Tool> cc = Tool::CreateTool(CTool::kCToolCc);
+  CTool* cc_tool = cc->AsC();
   TestWithScope::SetCommandForTool(
       "cc {{source}} {{cflags}} {{cflags_c}} {{defines}} {{include_dirs}} "
       "-o {{output}}",
-      cc_tool.get());
+      cc_tool);
   cc_tool->set_outputs(SubstitutionList::MakeForTest(
       "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
-  cc_tool->set_precompiled_header_type(Tool::PCH_GCC);
-  pch_toolchain.SetTool(Toolchain::TYPE_CC, std::move(cc_tool));
+  cc_tool->set_precompiled_header_type(CTool::PCH_GCC);
+  pch_toolchain.SetTool(std::move(cc));
   pch_toolchain.ToolchainSetupComplete();
 
   // This target doesn't specify precompiled headers.
diff --git a/tools/gn/desc_builder.cc b/tools/gn/desc_builder.cc
index 57f27fd..ab6c686 100644
--- a/tools/gn/desc_builder.cc
+++ b/tools/gn/desc_builder.cc
@@ -634,8 +634,8 @@
     auto dict = std::make_unique<base::DictionaryValue>();
     for (const auto& source : target_->sources()) {
       std::vector<OutputFile> outputs;
-      Toolchain::ToolType tool_type = Toolchain::TYPE_NONE;
-      if (target_->GetOutputFilesForSource(source, &tool_type, &outputs)) {
+      const char* tool_name = Tool::kToolNone;
+      if (target_->GetOutputFilesForSource(source, &tool_name, &outputs)) {
         auto list = std::make_unique<base::ListValue>();
         for (const auto& output : outputs)
           list->AppendString(output.value());
diff --git a/tools/gn/function_toolchain.cc b/tools/gn/function_toolchain.cc
index cf45c67..7fc57c7 100644
--- a/tools/gn/function_toolchain.cc
+++ b/tools/gn/function_toolchain.cc
@@ -7,8 +7,10 @@
 #include <memory>
 #include <utility>
 
+#include "tools/gn/c_tool.h"
 #include "tools/gn/err.h"
 #include "tools/gn/functions.h"
+#include "tools/gn/general_tool.h"
 #include "tools/gn/label.h"
 #include "tools/gn/label_ptr.h"
 #include "tools/gn/parse_tree.h"
@@ -28,269 +30,6 @@
 // the toolchain property on a scope.
 const int kToolchainPropertyKey = 0;
 
-bool ReadBool(Scope* scope,
-              const char* var,
-              Tool* tool,
-              void (Tool::*set)(bool),
-              Err* err) {
-  const Value* v = scope->GetValue(var, true);
-  if (!v)
-    return true;  // Not present is fine.
-  if (!v->VerifyTypeIs(Value::BOOLEAN, err))
-    return false;
-
-  (tool->*set)(v->boolean_value());
-  return true;
-}
-
-// 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,
-                Tool* tool,
-                void (Tool::*set)(std::string),
-                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;
-
-  (tool->*set)(v->string_value());
-  return true;
-}
-
-// Reads the given label from the scope (if present) and puts the result into
-// dest. If the value is not a label, sets the error and returns false.
-bool ReadLabel(Scope* scope,
-               const char* var,
-               Tool* tool,
-               const Label& current_toolchain,
-               void (Tool::*set)(LabelPtrPair<Pool>),
-               Err* err) {
-  const Value* v = scope->GetValue(var, true);
-  if (!v)
-    return true;  // Not present is fine.
-
-  Label label =
-      Label::Resolve(scope->GetSourceDir(), current_toolchain, *v, err);
-  if (err->has_error())
-    return false;
-
-  LabelPtrPair<Pool> pair(label);
-  pair.origin = tool->defined_from();
-
-  (tool->*set)(std::move(pair));
-  return true;
-}
-
-// Calls the given validate function on each type in the list. On failure,
-// sets the error, blame the value, and return false.
-bool ValidateSubstitutionList(const std::vector<SubstitutionType>& list,
-                              bool (*validate)(SubstitutionType),
-                              const Value* origin,
-                              Err* err) {
-  for (const auto& cur_type : list) {
-    if (!validate(cur_type)) {
-      *err = Err(*origin, "Pattern not valid here.",
-                 "You used the pattern " +
-                     std::string(kSubstitutionNames[cur_type]) +
-                     " which is not valid\nfor this variable.");
-      return false;
-    }
-  }
-  return true;
-}
-
-bool ReadPattern(Scope* scope,
-                 const char* name,
-                 bool (*validate)(SubstitutionType),
-                 Tool* tool,
-                 void (Tool::*set)(SubstitutionPattern),
-                 Err* err) {
-  const Value* value = scope->GetValue(name, true);
-  if (!value)
-    return true;  // Not present is fine.
-  if (!value->VerifyTypeIs(Value::STRING, err))
-    return false;
-
-  SubstitutionPattern pattern;
-  if (!pattern.Parse(*value, err))
-    return false;
-  if (!ValidateSubstitutionList(pattern.required_types(), validate, value, err))
-    return false;
-
-  (tool->*set)(std::move(pattern));
-  return true;
-}
-
-bool ReadPatternList(Scope* scope,
-                     const char* name,
-                     bool (*validate)(SubstitutionType),
-                     Tool* tool,
-                     void (Tool::*set)(SubstitutionList),
-                     Err* err) {
-  const Value* value = scope->GetValue(name, true);
-  if (!value)
-    return true;  // Not present is fine.
-  if (!value->VerifyTypeIs(Value::LIST, err))
-    return false;
-
-  SubstitutionList list;
-  if (!list.Parse(*value, err))
-    return false;
-
-  // Validate the right kinds of patterns are used.
-  if (!ValidateSubstitutionList(list.required_types(), validate, value, err))
-    return false;
-
-  (tool->*set)(std::move(list));
-  return true;
-}
-
-bool ReadOutputExtension(Scope* scope, Tool* tool, Err* err) {
-  const Value* value = scope->GetValue("default_output_extension", true);
-  if (!value)
-    return true;  // Not present is fine.
-  if (!value->VerifyTypeIs(Value::STRING, err))
-    return false;
-
-  if (value->string_value().empty())
-    return true;  // Accept empty string.
-
-  if (value->string_value()[0] != '.') {
-    *err = Err(*value, "default_output_extension must begin with a '.'");
-    return false;
-  }
-
-  tool->set_default_output_extension(value->string_value());
-  return true;
-}
-
-bool ReadPrecompiledHeaderType(Scope* scope, Tool* tool, Err* err) {
-  const Value* value = scope->GetValue("precompiled_header_type", true);
-  if (!value)
-    return true;  // Not present is fine.
-  if (!value->VerifyTypeIs(Value::STRING, err))
-    return false;
-
-  if (value->string_value().empty())
-    return true;  // Accept empty string, do nothing (default is "no PCH").
-
-  if (value->string_value() == "gcc") {
-    tool->set_precompiled_header_type(Tool::PCH_GCC);
-    return true;
-  } else if (value->string_value() == "msvc") {
-    tool->set_precompiled_header_type(Tool::PCH_MSVC);
-    return true;
-  }
-  *err = Err(*value, "Invalid precompiled_header_type",
-             "Must either be empty, \"gcc\", or \"msvc\".");
-  return false;
-}
-
-bool ReadDepsFormat(Scope* scope, Tool* tool, Err* err) {
-  const Value* value = scope->GetValue("depsformat", true);
-  if (!value)
-    return true;  // Not present is fine.
-  if (!value->VerifyTypeIs(Value::STRING, err))
-    return false;
-
-  if (value->string_value() == "gcc") {
-    tool->set_depsformat(Tool::DEPS_GCC);
-  } else if (value->string_value() == "msvc") {
-    tool->set_depsformat(Tool::DEPS_MSVC);
-  } else {
-    *err = Err(*value, "Deps format must be \"gcc\" or \"msvc\".");
-    return false;
-  }
-  return true;
-}
-
-bool IsCompilerTool(Toolchain::ToolType type) {
-  return type == Toolchain::TYPE_CC || type == Toolchain::TYPE_CXX ||
-         type == Toolchain::TYPE_OBJC || type == Toolchain::TYPE_OBJCXX ||
-         type == Toolchain::TYPE_RC || type == Toolchain::TYPE_ASM;
-}
-
-bool IsLinkerTool(Toolchain::ToolType type) {
-  // "alink" is not counted as in the generic "linker" tool list.
-  return type == Toolchain::TYPE_SOLINK ||
-         type == Toolchain::TYPE_SOLINK_MODULE || type == Toolchain::TYPE_LINK;
-}
-
-bool IsPatternInOutputList(const SubstitutionList& output_list,
-                           const SubstitutionPattern& pattern) {
-  for (const auto& cur : output_list.list()) {
-    if (pattern.ranges().size() == cur.ranges().size() &&
-        std::equal(pattern.ranges().begin(), pattern.ranges().end(),
-                   cur.ranges().begin()))
-      return true;
-  }
-  return false;
-}
-
-bool ValidateOutputs(const Tool* tool, Err* err) {
-  if (tool->outputs().list().empty()) {
-    *err = Err(tool->defined_from(),
-               "\"outputs\" must be specified for this tool.");
-    return false;
-  }
-  return true;
-}
-
-// Validates either link_output or depend_output. To generalize to either, pass
-// the associated pattern, and the variable name that should appear in error
-// messages.
-bool ValidateLinkAndDependOutput(const Tool* tool,
-                                 Toolchain::ToolType tool_type,
-                                 const SubstitutionPattern& pattern,
-                                 const char* variable_name,
-                                 Err* err) {
-  if (pattern.empty())
-    return true;  // Empty is always OK.
-
-  // It should only be specified for certain tool types.
-  if (tool_type != Toolchain::TYPE_SOLINK &&
-      tool_type != Toolchain::TYPE_SOLINK_MODULE) {
-    *err = Err(tool->defined_from(),
-               "This tool specifies a " + std::string(variable_name) + ".",
-               "This is only valid for solink and solink_module tools.");
-    return false;
-  }
-
-  if (!IsPatternInOutputList(tool->outputs(), pattern)) {
-    *err = Err(tool->defined_from(), "This tool's link_output is bad.",
-               "It must match one of the outputs.");
-    return false;
-  }
-
-  return true;
-}
-
-bool ValidateRuntimeOutputs(const Tool* tool,
-                            Toolchain::ToolType tool_type,
-                            Err* err) {
-  if (tool->runtime_outputs().list().empty())
-    return true;  // Empty is always OK.
-
-  if (!IsLinkerTool(tool_type)) {
-    *err = Err(tool->defined_from(), "This tool specifies runtime_outputs.",
-               "This is only valid for linker tools (alink doesn't count).");
-    return false;
-  }
-
-  for (const SubstitutionPattern& pattern : tool->runtime_outputs().list()) {
-    if (!IsPatternInOutputList(tool->outputs(), pattern)) {
-      *err = Err(tool->defined_from(), "This tool's runtime_outputs is bad.",
-                 "It must be a subset of the outputs. The bad one is:\n  " +
-                     pattern.AsString());
-      return false;
-    }
-  }
-  return true;
-}
-
 }  // namespace
 
 // toolchain -------------------------------------------------------------------
@@ -1006,11 +745,6 @@
   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);
@@ -1018,103 +752,21 @@
   if (err->has_error())
     return Value();
 
-  // Figure out which validator to use for the substitution pattern for this
-  // tool type. There are different validators for the "outputs" than for the
-  // rest of the strings.
-  bool (*subst_validator)(SubstitutionType) = nullptr;
-  bool (*subst_output_validator)(SubstitutionType) = nullptr;
-  if (IsCompilerTool(tool_type)) {
-    subst_validator = &IsValidCompilerSubstitution;
-    subst_output_validator = &IsValidCompilerOutputsSubstitution;
-  } else if (IsLinkerTool(tool_type)) {
-    subst_validator = &IsValidLinkerSubstitution;
-    subst_output_validator = &IsValidLinkerOutputsSubstitution;
-  } else if (tool_type == Toolchain::TYPE_ALINK) {
-    subst_validator = &IsValidALinkSubstitution;
-    // ALink uses the standard output file patterns as other linker tools.
-    subst_output_validator = &IsValidLinkerOutputsSubstitution;
-  } else if (tool_type == Toolchain::TYPE_COPY ||
-             tool_type == Toolchain::TYPE_COPY_BUNDLE_DATA) {
-    subst_validator = &IsValidCopySubstitution;
-    subst_output_validator = &IsValidCopySubstitution;
-  } else if (tool_type == Toolchain::TYPE_COMPILE_XCASSETS) {
-    subst_validator = &IsValidCompileXCassetsSubstitution;
-    subst_output_validator = &IsValidCompileXCassetsSubstitution;
-  } else {
-    subst_validator = &IsValidToolSubstitution;
-    subst_output_validator = &IsValidToolSubstitution;
+  std::unique_ptr<Tool> tool =
+      Tool::CreateTool(tool_name, &block_scope, toolchain, err);
+
+  if (!tool) {
+    *err = Err(function, "Unknown tool type");
+    return Value();
   }
 
-  std::unique_ptr<Tool> tool = std::make_unique<Tool>();
   tool->set_defined_from(function);
-
-  if (!ReadPattern(&block_scope, "command", subst_validator, tool.get(),
-                   &Tool::set_command, err) ||
-      !ReadOutputExtension(&block_scope, tool.get(), err) ||
-      !ReadPattern(&block_scope, "depfile", subst_validator, tool.get(),
-                   &Tool::set_depfile, err) ||
-      !ReadDepsFormat(&block_scope, tool.get(), err) ||
-      !ReadPattern(&block_scope, "description", subst_validator, tool.get(),
-                   &Tool::set_description, err) ||
-      !ReadString(&block_scope, "lib_switch", tool.get(), &Tool::set_lib_switch,
-                  err) ||
-      !ReadString(&block_scope, "lib_dir_switch", tool.get(),
-                  &Tool::set_lib_dir_switch, err) ||
-      !ReadPattern(&block_scope, "link_output", subst_validator, tool.get(),
-                   &Tool::set_link_output, err) ||
-      !ReadPattern(&block_scope, "depend_output", subst_validator, tool.get(),
-                   &Tool::set_depend_output, err) ||
-      !ReadPatternList(&block_scope, "runtime_outputs", subst_validator,
-                       tool.get(), &Tool::set_runtime_outputs, err) ||
-      !ReadString(&block_scope, "output_prefix", tool.get(),
-                  &Tool::set_output_prefix, err) ||
-      !ReadPattern(&block_scope, "default_output_dir", subst_validator,
-                   tool.get(), &Tool::set_default_output_dir, err) ||
-      !ReadPrecompiledHeaderType(&block_scope, tool.get(), err) ||
-      !ReadBool(&block_scope, "restat", tool.get(), &Tool::set_restat, err) ||
-      !ReadPattern(&block_scope, "rspfile", subst_validator, tool.get(),
-                   &Tool::set_rspfile, err) ||
-      !ReadPattern(&block_scope, "rspfile_content", subst_validator, tool.get(),
-                   &Tool::set_rspfile_content, err) ||
-      !ReadLabel(&block_scope, "pool", tool.get(), toolchain->label(),
-                 &Tool::set_pool, err)) {
-    return Value();
-  }
-
-  if (tool_type != Toolchain::TYPE_COPY && tool_type != Toolchain::TYPE_STAMP &&
-      tool_type != Toolchain::TYPE_COPY_BUNDLE_DATA &&
-      tool_type != Toolchain::TYPE_COMPILE_XCASSETS &&
-      tool_type != Toolchain::TYPE_ACTION) {
-    // All tools should have outputs, except the copy, stamp, copy_bundle_data
-    // compile_xcassets and action tools that generate their outputs internally.
-    if (!ReadPatternList(&block_scope, "outputs", subst_output_validator,
-                         tool.get(), &Tool::set_outputs, err) ||
-        !ValidateOutputs(tool.get(), err))
-      return Value();
-  }
-  if (!ValidateRuntimeOutputs(tool.get(), tool_type, err))
-    return Value();
-
-  // Validate link_output and depend_output.
-  if (!ValidateLinkAndDependOutput(tool.get(), tool_type, tool->link_output(),
-                                   "link_output", err))
-    return Value();
-  if (!ValidateLinkAndDependOutput(tool.get(), tool_type, tool->depend_output(),
-                                   "depend_output", err))
-    return Value();
-  if ((!tool->link_output().empty() && tool->depend_output().empty()) ||
-      (tool->link_output().empty() && !tool->depend_output().empty())) {
-    *err = Err(function,
-               "Both link_output and depend_output should either "
-               "be specified or they should both be empty.");
-    return Value();
-  }
+  toolchain->SetTool(std::move(tool));
 
   // 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, std::move(tool));
   return Value();
 }
 
diff --git a/tools/gn/function_toolchain_unittest.cc b/tools/gn/function_toolchain_unittest.cc
index 599d7a1..e3aa598 100644
--- a/tools/gn/function_toolchain_unittest.cc
+++ b/tools/gn/function_toolchain_unittest.cc
@@ -34,7 +34,7 @@
     ASSERT_TRUE(toolchain);
 
     // The toolchain should have a link tool with the two outputs.
-    const Tool* link = toolchain->GetTool(Toolchain::TYPE_LINK);
+    const Tool* link = toolchain->GetTool(CTool::kCToolLink);
     ASSERT_TRUE(link);
     ASSERT_EQ(1u, link->outputs().list().size());
     EXPECT_EQ("foo", link->outputs().list()[0].AsString());
diff --git a/tools/gn/general_tool.cc b/tools/gn/general_tool.cc
new file mode 100644
index 0000000..d89dd30
--- /dev/null
+++ b/tools/gn/general_tool.cc
@@ -0,0 +1,51 @@
+// Copyright 2019 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/general_tool.h"
+#include "tools/gn/target.h"
+
+const char* GeneralTool::kGeneralToolStamp = "stamp";
+const char* GeneralTool::kGeneralToolCopy = "copy";
+const char* GeneralTool::kGeneralToolCopyBundleData = "copy_bundle_data";
+const char* GeneralTool::kGeneralToolCompileXCAssets = "compile_xcassets";
+const char* GeneralTool::kGeneralToolAction = "action";
+
+GeneralTool::GeneralTool(const char* n) : Tool(n) {
+  CHECK(ValidateName(n));
+}
+
+GeneralTool::~GeneralTool() = default;
+
+GeneralTool* GeneralTool::AsGeneral() {
+  return this;
+}
+const GeneralTool* GeneralTool::AsGeneral() const {
+  return this;
+}
+
+bool GeneralTool::ValidateName(const char* name) const {
+  return name == kGeneralToolStamp || name == kGeneralToolCopy ||
+         name == kGeneralToolCopyBundleData ||
+         name == kGeneralToolCompileXCAssets || name == kGeneralToolAction;
+}
+
+void GeneralTool::SetComplete() {
+  SetToolComplete();
+}
+
+bool GeneralTool::InitTool(Scope* scope, Toolchain* toolchain, Err* err) {
+  // Initialize default vars.
+  return Tool::InitTool(scope, toolchain, err);
+}
+
+bool GeneralTool::ValidateSubstitution(SubstitutionType sub_type) const {
+  if (name_ == kGeneralToolStamp || name_ == kGeneralToolAction)
+    return IsValidToolSubstitution(sub_type);
+  else if (name_ == kGeneralToolCopy || name_ == kGeneralToolCopyBundleData)
+    return IsValidCopySubstitution(sub_type);
+  else if (name_ == kGeneralToolCompileXCAssets)
+    return IsValidCompileXCassetsSubstitution(sub_type);
+  NOTREACHED();
+  return false;
+}
diff --git a/tools/gn/general_tool.h b/tools/gn/general_tool.h
new file mode 100644
index 0000000..df9caf2
--- /dev/null
+++ b/tools/gn/general_tool.h
@@ -0,0 +1,47 @@
+// Copyright 2019 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_GENERAL_TOOL_H_
+#define TOOLS_GN_GENERAL_TOOL_H_
+
+#include <string>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "tools/gn/label.h"
+#include "tools/gn/label_ptr.h"
+#include "tools/gn/source_file_type.h"
+#include "tools/gn/substitution_list.h"
+#include "tools/gn/substitution_pattern.h"
+#include "tools/gn/tool.h"
+
+class GeneralTool : public Tool {
+ public:
+  // General tools
+  static const char* kGeneralToolStamp;
+  static const char* kGeneralToolCopy;
+  static const char* kGeneralToolAction;
+
+  // Platform-specific tools
+  static const char* kGeneralToolCopyBundleData;
+  static const char* kGeneralToolCompileXCAssets;
+
+  GeneralTool(const char* n);
+  ~GeneralTool();
+
+  // Manual RTTI and required functions ---------------------------------------
+
+  bool InitTool(Scope* block_scope, Toolchain* toolchain, Err* err);
+  bool ValidateName(const char* name) const override;
+  void SetComplete() override;
+  bool ValidateSubstitution(SubstitutionType sub_type) const override;
+
+  GeneralTool* AsGeneral() override;
+  const GeneralTool* AsGeneral() const override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(GeneralTool);
+};
+
+#endif  // TOOLS_GN_GENERAL_TOOL_H_
diff --git a/tools/gn/ninja_action_target_writer.cc b/tools/gn/ninja_action_target_writer.cc
index c2016dc..31f5261 100644
--- a/tools/gn/ninja_action_target_writer.cc
+++ b/tools/gn/ninja_action_target_writer.cc
@@ -9,6 +9,7 @@
 #include "base/strings/string_util.h"
 #include "tools/gn/deps_iterator.h"
 #include "tools/gn/err.h"
+#include "tools/gn/general_tool.h"
 #include "tools/gn/pool.h"
 #include "tools/gn/settings.h"
 #include "tools/gn/string_utils.h"
@@ -145,7 +146,7 @@
   out_ << std::endl;
   out_ << "  description = ACTION " << target_label << std::endl;
   out_ << "  restat = 1" << std::endl;
-  const Tool* tool = target_->toolchain()->GetTool(Toolchain::TYPE_ACTION);
+  const Tool* tool = target_->toolchain()->GetTool(GeneralTool::kGeneralToolAction);
   if (tool && tool->pool().ptr) {
     out_ << "  pool = ";
     out_ << tool->pool().ptr->GetNinjaName(
diff --git a/tools/gn/ninja_binary_target_writer.cc b/tools/gn/ninja_binary_target_writer.cc
index 9e5632e..9f0e949 100644
--- a/tools/gn/ninja_binary_target_writer.cc
+++ b/tools/gn/ninja_binary_target_writer.cc
@@ -18,8 +18,9 @@
 #include "tools/gn/err.h"
 #include "tools/gn/escape.h"
 #include "tools/gn/filesystem_utils.h"
-#include "tools/gn/ninja_utils.h"
+#include "tools/gn/general_tool.h"
 #include "tools/gn/ninja_target_command_util.h"
+#include "tools/gn/ninja_utils.h"
 #include "tools/gn/scheduler.h"
 #include "tools/gn/settings.h"
 #include "tools/gn/source_file_type.h"
@@ -53,20 +54,17 @@
 
 // Returns the language-specific lang recognized by gcc’s -x flag for
 // precompiled header files.
-const char* GetPCHLangForToolType(Toolchain::ToolType type) {
-  switch (type) {
-    case Toolchain::TYPE_CC:
-      return "c-header";
-    case Toolchain::TYPE_CXX:
-      return "c++-header";
-    case Toolchain::TYPE_OBJC:
-      return "objective-c-header";
-    case Toolchain::TYPE_OBJCXX:
-      return "objective-c++-header";
-    default:
-      NOTREACHED() << "Not a valid PCH tool type: " << type;
-      return "";
-  }
+const char* GetPCHLangForToolType(const char* name) {
+  if (name == CTool::kCToolCc)
+    return "c-header";
+  if (name == CTool::kCToolCxx)
+    return "c++-header";
+  if (name == CTool::kCToolObjC)
+    return "objective-c-header";
+  if (name == CTool::kCToolObjCxx)
+    return "objective-c++-header";
+  NOTREACHED() << "Not a valid PCH tool type: " << name;
+  return "";
 }
 
 // Appends the object files generated by the given source set to the given
@@ -79,8 +77,8 @@
   // Compute object files for all sources. Only link the first output from
   // the tool if there are more than one.
   for (const auto& source : source_set->sources()) {
-    Toolchain::ToolType tool_type = Toolchain::TYPE_NONE;
-    if (source_set->GetOutputFilesForSource(source, &tool_type, &tool_outputs))
+    const char* tool_name = Tool::kToolNone;
+    if (source_set->GetOutputFilesForSource(source, &tool_name, &tool_outputs))
       obj_files->push_back(tool_outputs[0]);
 
     used_types.Set(GetSourceFileType(source));
@@ -90,31 +88,32 @@
   // files so they are omitted.
   if (source_set->config_values().has_precompiled_headers()) {
     if (used_types.Get(SOURCE_C)) {
-      const Tool* tool = source_set->toolchain()->GetTool(Toolchain::TYPE_CC);
-      if (tool && tool->precompiled_header_type() == Tool::PCH_MSVC) {
-        GetPCHOutputFiles(source_set, Toolchain::TYPE_CC, &tool_outputs);
+      const CTool* tool = source_set->toolchain()->GetToolAsC(CTool::kCToolCc);
+      if (tool && tool->precompiled_header_type() == CTool::PCH_MSVC) {
+        GetPCHOutputFiles(source_set, CTool::kCToolCc, &tool_outputs);
         obj_files->Append(tool_outputs.begin(), tool_outputs.end());
       }
     }
     if (used_types.Get(SOURCE_CPP)) {
-      const Tool* tool = source_set->toolchain()->GetTool(Toolchain::TYPE_CXX);
-      if (tool && tool->precompiled_header_type() == Tool::PCH_MSVC) {
-        GetPCHOutputFiles(source_set, Toolchain::TYPE_CXX, &tool_outputs);
+      const CTool* tool = source_set->toolchain()->GetToolAsC(CTool::kCToolCxx);
+      if (tool && tool->precompiled_header_type() == CTool::PCH_MSVC) {
+        GetPCHOutputFiles(source_set, CTool::kCToolCxx, &tool_outputs);
         obj_files->Append(tool_outputs.begin(), tool_outputs.end());
       }
     }
     if (used_types.Get(SOURCE_M)) {
-      const Tool* tool = source_set->toolchain()->GetTool(Toolchain::TYPE_OBJC);
-      if (tool && tool->precompiled_header_type() == Tool::PCH_MSVC) {
-        GetPCHOutputFiles(source_set, Toolchain::TYPE_OBJC, &tool_outputs);
+      const CTool* tool =
+          source_set->toolchain()->GetToolAsC(CTool::kCToolObjC);
+      if (tool && tool->precompiled_header_type() == CTool::PCH_MSVC) {
+        GetPCHOutputFiles(source_set, CTool::kCToolObjC, &tool_outputs);
         obj_files->Append(tool_outputs.begin(), tool_outputs.end());
       }
     }
     if (used_types.Get(SOURCE_MM)) {
-      const Tool* tool =
-          source_set->toolchain()->GetTool(Toolchain::TYPE_OBJCXX);
-      if (tool && tool->precompiled_header_type() == Tool::PCH_MSVC) {
-        GetPCHOutputFiles(source_set, Toolchain::TYPE_OBJCXX, &tool_outputs);
+      const CTool* tool =
+          source_set->toolchain()->GetToolAsC(CTool::kCToolObjCxx);
+      if (tool && tool->precompiled_header_type() == CTool::PCH_MSVC) {
+        GetPCHOutputFiles(source_set, CTool::kCToolObjCxx, &tool_outputs);
         obj_files->Append(tool_outputs.begin(), tool_outputs.end());
       }
     }
@@ -126,7 +125,7 @@
 NinjaBinaryTargetWriter::NinjaBinaryTargetWriter(const Target* target,
                                                  std::ostream& out)
     : NinjaTargetWriter(target, out),
-      tool_(target->toolchain()->GetToolForTargetFinalOutput(target)),
+      tool_(target->toolchain()->GetToolForTargetFinalOutputAsC(target)),
       rule_prefix_(GetNinjaRulePrefixForToolchain(settings_)) {}
 
 NinjaBinaryTargetWriter::~NinjaBinaryTargetWriter() = default;
@@ -252,29 +251,33 @@
 
   EscapeOptions opts = GetFlagOptions();
   if (used_types.Get(SOURCE_S) || used_types.Get(SOURCE_ASM)) {
-    WriteOneFlag(target_, SUBSTITUTION_ASMFLAGS, false, Toolchain::TYPE_NONE,
+    WriteOneFlag(target_, SUBSTITUTION_ASMFLAGS, false, Tool::kToolNone,
                  &ConfigValues::asmflags, opts, path_output_, out_);
   }
   if (used_types.Get(SOURCE_C) || used_types.Get(SOURCE_CPP) ||
       used_types.Get(SOURCE_M) || used_types.Get(SOURCE_MM)) {
-    WriteOneFlag(target_, SUBSTITUTION_CFLAGS, false, Toolchain::TYPE_NONE,
+    WriteOneFlag(target_, SUBSTITUTION_CFLAGS, false, Tool::kToolNone,
                  &ConfigValues::cflags, opts, path_output_, out_);
   }
   if (used_types.Get(SOURCE_C)) {
     WriteOneFlag(target_, SUBSTITUTION_CFLAGS_C, has_precompiled_headers,
-                 Toolchain::TYPE_CC, &ConfigValues::cflags_c, opts, path_output_, out_);
+                 CTool::kCToolCc, &ConfigValues::cflags_c, opts, path_output_,
+                 out_);
   }
   if (used_types.Get(SOURCE_CPP)) {
     WriteOneFlag(target_, SUBSTITUTION_CFLAGS_CC, has_precompiled_headers,
-                 Toolchain::TYPE_CXX, &ConfigValues::cflags_cc, opts, path_output_, out_);
+                 CTool::kCToolCxx, &ConfigValues::cflags_cc, opts, path_output_,
+                 out_);
   }
   if (used_types.Get(SOURCE_M)) {
     WriteOneFlag(target_, SUBSTITUTION_CFLAGS_OBJC, has_precompiled_headers,
-                 Toolchain::TYPE_OBJC, &ConfigValues::cflags_objc, opts, path_output_, out_);
+                 CTool::kCToolObjC, &ConfigValues::cflags_objc, opts,
+                 path_output_, out_);
   }
   if (used_types.Get(SOURCE_MM)) {
     WriteOneFlag(target_, SUBSTITUTION_CFLAGS_OBJCC, has_precompiled_headers,
-                 Toolchain::TYPE_OBJCXX, &ConfigValues::cflags_objcc, opts, path_output_, out_);
+                 CTool::kCToolObjCxx, &ConfigValues::cflags_objcc, opts,
+                 path_output_, out_);
   }
 
   WriteSharedVars(subst);
@@ -308,7 +311,7 @@
   out_ << "build ";
   path_output_.WriteFile(out_, input_stamp_file);
   out_ << ": " << GetNinjaRulePrefixForToolchain(settings_)
-       << Toolchain::ToolTypeToName(Toolchain::TYPE_STAMP);
+       << GeneralTool::kGeneralToolStamp;
 
   // File inputs.
   for (const auto* input : inputs) {
@@ -329,34 +332,34 @@
   if (!target_->config_values().has_precompiled_headers())
     return;
 
-  const Tool* tool_c = target_->toolchain()->GetTool(Toolchain::TYPE_CC);
-  if (tool_c && tool_c->precompiled_header_type() != Tool::PCH_NONE &&
+  const CTool* tool_c = target_->toolchain()->GetToolAsC(CTool::kCToolCc);
+  if (tool_c && tool_c->precompiled_header_type() != CTool::PCH_NONE &&
       used_types.Get(SOURCE_C)) {
-    WritePCHCommand(SUBSTITUTION_CFLAGS_C, Toolchain::TYPE_CC,
+    WritePCHCommand(SUBSTITUTION_CFLAGS_C, CTool::kCToolCc,
                     tool_c->precompiled_header_type(), input_dep,
                     order_only_deps, object_files, other_files);
   }
-  const Tool* tool_cxx = target_->toolchain()->GetTool(Toolchain::TYPE_CXX);
-  if (tool_cxx && tool_cxx->precompiled_header_type() != Tool::PCH_NONE &&
+  const CTool* tool_cxx = target_->toolchain()->GetToolAsC(CTool::kCToolCxx);
+  if (tool_cxx && tool_cxx->precompiled_header_type() != CTool::PCH_NONE &&
       used_types.Get(SOURCE_CPP)) {
-    WritePCHCommand(SUBSTITUTION_CFLAGS_CC, Toolchain::TYPE_CXX,
+    WritePCHCommand(SUBSTITUTION_CFLAGS_CC, CTool::kCToolCxx,
                     tool_cxx->precompiled_header_type(), input_dep,
                     order_only_deps, object_files, other_files);
   }
 
-  const Tool* tool_objc = target_->toolchain()->GetTool(Toolchain::TYPE_OBJC);
-  if (tool_objc && tool_objc->precompiled_header_type() == Tool::PCH_GCC &&
+  const CTool* tool_objc = target_->toolchain()->GetToolAsC(CTool::kCToolObjC);
+  if (tool_objc && tool_objc->precompiled_header_type() == CTool::PCH_GCC &&
       used_types.Get(SOURCE_M)) {
-    WritePCHCommand(SUBSTITUTION_CFLAGS_OBJC, Toolchain::TYPE_OBJC,
+    WritePCHCommand(SUBSTITUTION_CFLAGS_OBJC, CTool::kCToolObjC,
                     tool_objc->precompiled_header_type(), input_dep,
                     order_only_deps, object_files, other_files);
   }
 
-  const Tool* tool_objcxx =
-      target_->toolchain()->GetTool(Toolchain::TYPE_OBJCXX);
-  if (tool_objcxx && tool_objcxx->precompiled_header_type() == Tool::PCH_GCC &&
+  const CTool* tool_objcxx =
+      target_->toolchain()->GetToolAsC(CTool::kCToolObjCxx);
+  if (tool_objcxx && tool_objcxx->precompiled_header_type() == CTool::PCH_GCC &&
       used_types.Get(SOURCE_MM)) {
-    WritePCHCommand(SUBSTITUTION_CFLAGS_OBJCC, Toolchain::TYPE_OBJCXX,
+    WritePCHCommand(SUBSTITUTION_CFLAGS_OBJCC, CTool::kCToolObjCxx,
                     tool_objcxx->precompiled_header_type(), input_dep,
                     order_only_deps, object_files, other_files);
   }
@@ -364,22 +367,22 @@
 
 void NinjaBinaryTargetWriter::WritePCHCommand(
     SubstitutionType flag_type,
-    Toolchain::ToolType tool_type,
-    Tool::PrecompiledHeaderType header_type,
+    const char* tool_name,
+    CTool::PrecompiledHeaderType header_type,
     const OutputFile& input_dep,
     const std::vector<OutputFile>& order_only_deps,
     std::vector<OutputFile>* object_files,
     std::vector<OutputFile>* other_files) {
   switch (header_type) {
-    case Tool::PCH_MSVC:
-      WriteWindowsPCHCommand(flag_type, tool_type, input_dep, order_only_deps,
+    case CTool::PCH_MSVC:
+      WriteWindowsPCHCommand(flag_type, tool_name, input_dep, order_only_deps,
                              object_files);
       break;
-    case Tool::PCH_GCC:
-      WriteGCCPCHCommand(flag_type, tool_type, input_dep, order_only_deps,
+    case CTool::PCH_GCC:
+      WriteGCCPCHCommand(flag_type, tool_name, input_dep, order_only_deps,
                          other_files);
       break;
-    case Tool::PCH_NONE:
+    case CTool::PCH_NONE:
       NOTREACHED() << "Cannot write a PCH command with no PCH header type";
       break;
   }
@@ -387,13 +390,13 @@
 
 void NinjaBinaryTargetWriter::WriteGCCPCHCommand(
     SubstitutionType flag_type,
-    Toolchain::ToolType tool_type,
+    const char* tool_name,
     const OutputFile& input_dep,
     const std::vector<OutputFile>& order_only_deps,
     std::vector<OutputFile>* gch_files) {
   // Compute the pch output file (it will be language-specific).
   std::vector<OutputFile> outputs;
-  GetPCHOutputFiles(target_, tool_type, &outputs);
+  GetPCHOutputFiles(target_, tool_name, &outputs);
   if (outputs.empty())
     return;
 
@@ -405,7 +408,7 @@
 
   // Build line to compile the file.
   WriteCompilerBuildLine(target_->config_values().precompiled_source(),
-                         extra_deps, order_only_deps, tool_type, outputs);
+                         extra_deps, order_only_deps, tool_name, outputs);
 
   // This build line needs a custom language-specific flags value. Rule-specific
   // variables are just indented underneath the rule line.
@@ -415,22 +418,22 @@
   // implicitly generated -include flag with the -x <header lang> flag required
   // for .gch targets.
   EscapeOptions opts = GetFlagOptions();
-  if (tool_type == Toolchain::TYPE_CC) {
+  if (tool_name == CTool::kCToolCc) {
     RecursiveTargetConfigStringsToStream(target_, &ConfigValues::cflags_c, opts,
                                          out_);
-  } else if (tool_type == Toolchain::TYPE_CXX) {
+  } else if (tool_name == CTool::kCToolCxx) {
     RecursiveTargetConfigStringsToStream(target_, &ConfigValues::cflags_cc,
                                          opts, out_);
-  } else if (tool_type == Toolchain::TYPE_OBJC) {
+  } else if (tool_name == CTool::kCToolObjC) {
     RecursiveTargetConfigStringsToStream(target_, &ConfigValues::cflags_objc,
                                          opts, out_);
-  } else if (tool_type == Toolchain::TYPE_OBJCXX) {
+  } else if (tool_name == CTool::kCToolObjCxx) {
     RecursiveTargetConfigStringsToStream(target_, &ConfigValues::cflags_objcc,
                                          opts, out_);
   }
 
   // Append the command to specify the language of the .gch file.
-  out_ << " -x " << GetPCHLangForToolType(tool_type);
+  out_ << " -x " << GetPCHLangForToolType(tool_name);
 
   // Write two blank lines to help separate the PCH build lines from the
   // regular source build lines.
@@ -439,13 +442,13 @@
 
 void NinjaBinaryTargetWriter::WriteWindowsPCHCommand(
     SubstitutionType flag_type,
-    Toolchain::ToolType tool_type,
+    const char* tool_name,
     const OutputFile& input_dep,
     const std::vector<OutputFile>& order_only_deps,
     std::vector<OutputFile>* object_files) {
   // Compute the pch output file (it will be language-specific).
   std::vector<OutputFile> outputs;
-  GetPCHOutputFiles(target_, tool_type, &outputs);
+  GetPCHOutputFiles(target_, tool_name, &outputs);
   if (outputs.empty())
     return;
 
@@ -457,7 +460,7 @@
 
   // Build line to compile the file.
   WriteCompilerBuildLine(target_->config_values().precompiled_source(),
-                         extra_deps, order_only_deps, tool_type, outputs);
+                         extra_deps, order_only_deps, tool_name, outputs);
 
   // This build line needs a custom language-specific flags value. Rule-specific
   // variables are just indented underneath the rule line.
@@ -486,8 +489,8 @@
   for (const auto& source : target_->sources()) {
     // Clear the vector but maintain the max capacity to prevent reallocations.
     deps.resize(0);
-    Toolchain::ToolType tool_type = Toolchain::TYPE_NONE;
-    if (!target_->GetOutputFilesForSource(source, &tool_type, &tool_outputs)) {
+    const char* tool_name = Tool::kToolNone;
+    if (!target_->GetOutputFilesForSource(source, &tool_name, &tool_outputs)) {
       if (GetSourceFileType(source) == SOURCE_DEF)
         other_files->push_back(source);
       continue;  // No output for this source.
@@ -496,7 +499,7 @@
     if (!input_dep.value().empty())
       deps.push_back(input_dep);
 
-    if (tool_type != Toolchain::TYPE_NONE) {
+    if (tool_name != Tool::kToolNone) {
       // Only include PCH deps that correspond to the tool type, for instance,
       // do not specify target_name.precompile.cc.obj (a CXX PCH file) as a dep
       // for the output of a C tool type.
@@ -504,19 +507,19 @@
       // This makes the assumption that pch_deps only contains pch output files
       // with the naming scheme specified in GetWindowsPCHObjectExtension or
       // GetGCCPCHOutputExtension.
-      const Tool* tool = target_->toolchain()->GetTool(tool_type);
-      if (tool->precompiled_header_type() != Tool::PCH_NONE) {
+      const CTool* tool = target_->toolchain()->GetToolAsC(tool_name);
+      if (tool->precompiled_header_type() != CTool::PCH_NONE) {
         for (const auto& dep : pch_deps) {
           const std::string& output_value = dep.value();
           size_t extension_offset = FindExtensionOffset(output_value);
           if (extension_offset == std::string::npos)
             continue;
           std::string output_extension;
-          if (tool->precompiled_header_type() == Tool::PCH_MSVC) {
+          if (tool->precompiled_header_type() == CTool::PCH_MSVC) {
             output_extension = GetWindowsPCHObjectExtension(
-                tool_type, output_value.substr(extension_offset - 1));
-          } else if (tool->precompiled_header_type() == Tool::PCH_GCC) {
-            output_extension = GetGCCPCHOutputExtension(tool_type);
+                tool_name, output_value.substr(extension_offset - 1));
+          } else if (tool->precompiled_header_type() == CTool::PCH_GCC) {
+            output_extension = GetGCCPCHOutputExtension(tool_name);
           }
           if (output_value.compare(
                   output_value.size() - output_extension.size(),
@@ -525,7 +528,7 @@
           }
         }
       }
-      WriteCompilerBuildLine(source, deps, order_only_deps, tool_type,
+      WriteCompilerBuildLine(source, deps, order_only_deps, tool_name,
                              tool_outputs);
     }
 
@@ -540,12 +543,12 @@
     const SourceFile& source,
     const std::vector<OutputFile>& extra_deps,
     const std::vector<OutputFile>& order_only_deps,
-    Toolchain::ToolType tool_type,
+    const char* tool_name,
     const std::vector<OutputFile>& outputs) {
   out_ << "build";
   path_output_.WriteFiles(out_, outputs);
 
-  out_ << ": " << rule_prefix_ << Toolchain::ToolTypeToName(tool_type);
+  out_ << ": " << rule_prefix_ << tool_name;
   out_ << " ";
   path_output_.WriteFile(out_, source);
 
@@ -573,8 +576,7 @@
   path_output_.WriteFiles(out_, output_files);
 
   out_ << ": " << rule_prefix_
-       << Toolchain::ToolTypeToName(
-              target_->toolchain()->GetToolTypeForTargetFinalOutput(target_));
+       << Tool::GetToolTypeForTargetFinalOutput(target_);
 
   UniqueVector<OutputFile> extra_object_files;
   UniqueVector<const Target*> linkable_deps;
diff --git a/tools/gn/ninja_binary_target_writer.h b/tools/gn/ninja_binary_target_writer.h
index 5cb7ab5..8a31219 100644
--- a/tools/gn/ninja_binary_target_writer.h
+++ b/tools/gn/ninja_binary_target_writer.h
@@ -6,6 +6,7 @@
 #define TOOLS_GN_NINJA_BINARY_TARGET_WRITER_H_
 
 #include "base/macros.h"
+#include "tools/gn/c_tool.h"
 #include "tools/gn/config_values.h"
 #include "tools/gn/ninja_target_writer.h"
 #include "tools/gn/toolchain.h"
@@ -52,21 +53,21 @@
 
   // Writes a .pch compile build line for a language type.
   void WritePCHCommand(SubstitutionType flag_type,
-                       Toolchain::ToolType tool_type,
-                       Tool::PrecompiledHeaderType header_type,
+                       const char* tool_name,
+                       CTool::PrecompiledHeaderType header_type,
                        const OutputFile& input_dep,
                        const std::vector<OutputFile>& order_only_deps,
                        std::vector<OutputFile>* object_files,
                        std::vector<OutputFile>* other_files);
 
   void WriteGCCPCHCommand(SubstitutionType flag_type,
-                          Toolchain::ToolType tool_type,
+                          const char* tool_name,
                           const OutputFile& input_dep,
                           const std::vector<OutputFile>& order_only_deps,
                           std::vector<OutputFile>* gch_files);
 
   void WriteWindowsPCHCommand(SubstitutionType flag_type,
-                              Toolchain::ToolType tool_type,
+                              const char* tool_name,
                               const OutputFile& input_dep,
                               const std::vector<OutputFile>& order_only_deps,
                               std::vector<OutputFile>* object_files);
@@ -88,7 +89,7 @@
   void WriteCompilerBuildLine(const SourceFile& source,
                               const std::vector<OutputFile>& extra_deps,
                               const std::vector<OutputFile>& order_only_deps,
-                              Toolchain::ToolType tool_type,
+                              const char* tool_name,
                               const std::vector<OutputFile>& outputs);
 
   void WriteLinkerStuff(const std::vector<OutputFile>& object_files,
@@ -125,12 +126,11 @@
   void WriteOrderOnlyDependencies(
       const UniqueVector<const Target*>& non_linkable_deps);
 
-
   // Checks for duplicates in the given list of output files. If any duplicates
   // are found, throws an error and return false.
   bool CheckForDuplicateObjectFiles(const std::vector<OutputFile>& files) const;
 
-  const Tool* tool_;
+  const CTool* tool_;
 
   // Cached version of the prefix used for rule types for this toolchain.
   std::string rule_prefix_;
diff --git a/tools/gn/ninja_binary_target_writer_unittest.cc b/tools/gn/ninja_binary_target_writer_unittest.cc
index a0a7ce8..f066536 100644
--- a/tools/gn/ninja_binary_target_writer_unittest.cc
+++ b/tools/gn/ninja_binary_target_writer_unittest.cc
@@ -738,26 +738,28 @@
   pch_settings.set_default_toolchain_label(setup.toolchain()->label());
 
   // Declare a C++ compiler that supports PCH.
-  std::unique_ptr<Tool> cxx_tool = std::make_unique<Tool>();
+  std::unique_ptr<Tool> cxx = Tool::CreateTool(CTool::kCToolCxx);
+  CTool* cxx_tool = cxx->AsC();
   TestWithScope::SetCommandForTool(
       "c++ {{source}} {{cflags}} {{cflags_cc}} {{defines}} {{include_dirs}} "
       "-o {{output}}",
-      cxx_tool.get());
+      cxx_tool);
   cxx_tool->set_outputs(SubstitutionList::MakeForTest(
       "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
-  cxx_tool->set_precompiled_header_type(Tool::PCH_MSVC);
-  pch_toolchain.SetTool(Toolchain::TYPE_CXX, std::move(cxx_tool));
+  cxx_tool->set_precompiled_header_type(CTool::PCH_MSVC);
+  pch_toolchain.SetTool(std::move(cxx));
 
   // Add a C compiler as well.
-  std::unique_ptr<Tool> cc_tool = std::make_unique<Tool>();
+  std::unique_ptr<Tool> cc = Tool::CreateTool(CTool::kCToolCc);
+  CTool* cc_tool = cc->AsC();
   TestWithScope::SetCommandForTool(
       "cc {{source}} {{cflags}} {{cflags_c}} {{defines}} {{include_dirs}} "
       "-o {{output}}",
-      cc_tool.get());
+      cc_tool);
   cc_tool->set_outputs(SubstitutionList::MakeForTest(
       "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
-  cc_tool->set_precompiled_header_type(Tool::PCH_MSVC);
-  pch_toolchain.SetTool(Toolchain::TYPE_CC, std::move(cc_tool));
+  cc_tool->set_precompiled_header_type(CTool::PCH_MSVC);
+  pch_toolchain.SetTool(std::move(cc));
   pch_toolchain.ToolchainSetupComplete();
 
   // This target doesn't specify precompiled headers.
@@ -847,7 +849,7 @@
         // The precompiled object files were added to the outputs.
         "withpch/obj/build/pch_target.precompile.c.o "
         "withpch/obj/build/pch_target.precompile.cc.o\n";
-    EXPECT_EQ(pch_win_expected, out.str());
+    EXPECT_EQ(pch_win_expected, out.str()) << pch_win_expected << "--BREAK--" << out.str();
   }
 }
 
@@ -865,27 +867,29 @@
   pch_settings.set_default_toolchain_label(setup.toolchain()->label());
 
   // Declare a C++ compiler that supports PCH.
-  std::unique_ptr<Tool> cxx_tool = std::make_unique<Tool>();
+  std::unique_ptr<Tool> cxx = Tool::CreateTool(CTool::kCToolCxx);
+  CTool* cxx_tool = cxx->AsC();
   TestWithScope::SetCommandForTool(
       "c++ {{source}} {{cflags}} {{cflags_cc}} {{defines}} {{include_dirs}} "
       "-o {{output}}",
-      cxx_tool.get());
+      cxx_tool);
   cxx_tool->set_outputs(SubstitutionList::MakeForTest(
       "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
-  cxx_tool->set_precompiled_header_type(Tool::PCH_GCC);
-  pch_toolchain.SetTool(Toolchain::TYPE_CXX, std::move(cxx_tool));
+  cxx_tool->set_precompiled_header_type(CTool::PCH_GCC);
+  pch_toolchain.SetTool(std::move(cxx));
   pch_toolchain.ToolchainSetupComplete();
 
   // Add a C compiler as well.
-  std::unique_ptr<Tool> cc_tool = std::make_unique<Tool>();
+  std::unique_ptr<Tool> cc = Tool::CreateTool(CTool::kCToolCc);
+  CTool* cc_tool = cc->AsC();
   TestWithScope::SetCommandForTool(
       "cc {{source}} {{cflags}} {{cflags_c}} {{defines}} {{include_dirs}} "
       "-o {{output}}",
-      cc_tool.get());
+      cc_tool);
   cc_tool->set_outputs(SubstitutionList::MakeForTest(
       "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
-  cc_tool->set_precompiled_header_type(Tool::PCH_GCC);
-  pch_toolchain.SetTool(Toolchain::TYPE_CC, std::move(cc_tool));
+  cc_tool->set_precompiled_header_type(CTool::PCH_GCC);
+  pch_toolchain.SetTool(std::move(cc));
   pch_toolchain.ToolchainSetupComplete();
 
   // This target doesn't specify precompiled headers.
diff --git a/tools/gn/ninja_build_writer.cc b/tools/gn/ninja_build_writer.cc
index f13a8b9..c1df3bc 100644
--- a/tools/gn/ninja_build_writer.cc
+++ b/tools/gn/ninja_build_writer.cc
@@ -319,11 +319,9 @@
   // Compute the pools referenced by all tools of all used toolchains.
   std::unordered_set<const Pool*> used_pools;
   for (const auto& pair : used_toolchains_) {
-    for (int j = Toolchain::TYPE_NONE + 1; j < Toolchain::TYPE_NUMTYPES; j++) {
-      Toolchain::ToolType tool_type = static_cast<Toolchain::ToolType>(j);
-      const Tool* tool = pair.second->GetTool(tool_type);
-      if (tool && tool->pool().ptr)
-        used_pools.insert(tool->pool().ptr);
+    for (const auto& tool : pair.second->tools()) {
+      if (tool.second->pool().ptr)
+        used_pools.insert(tool.second->pool().ptr);
     }
   }
 
diff --git a/tools/gn/ninja_build_writer_unittest.cc b/tools/gn/ninja_build_writer_unittest.cc
index 50530b1..2c73cd8 100644
--- a/tools/gn/ninja_build_writer_unittest.cc
+++ b/tools/gn/ninja_build_writer_unittest.cc
@@ -96,7 +96,7 @@
       Label(SourceDir("//other/"), "depth_pool", other_toolchain_label.dir(),
             other_toolchain_label.name()));
   other_regular_pool.set_depth(42);
-  other_toolchain.GetTool(Toolchain::TYPE_LINK)
+  other_toolchain.GetTool(CTool::kCToolLink)
       ->set_pool(LabelPtrPair<Pool>(&other_regular_pool));
 
   // Make another target that uses its own pool
@@ -122,7 +122,7 @@
                                             setup.toolchain()->label().dir(),
                                             setup.toolchain()->label().name()));
   console_pool.set_depth(1);
-  other_toolchain.GetTool(Toolchain::TYPE_STAMP)
+  other_toolchain.GetTool(GeneralTool::kGeneralToolStamp)
       ->set_pool(LabelPtrPair<Pool>(&console_pool));
 
   // Settings to go with the other toolchain.
diff --git a/tools/gn/ninja_copy_target_writer.cc b/tools/gn/ninja_copy_target_writer.cc
index 2a6f001..ee5cf52 100644
--- a/tools/gn/ninja_copy_target_writer.cc
+++ b/tools/gn/ninja_copy_target_writer.cc
@@ -5,6 +5,7 @@
 #include "tools/gn/ninja_copy_target_writer.h"
 
 #include "base/strings/string_util.h"
+#include "tools/gn/general_tool.h"
 #include "tools/gn/ninja_utils.h"
 #include "tools/gn/output_file.h"
 #include "tools/gn/scheduler.h"
@@ -21,7 +22,7 @@
 NinjaCopyTargetWriter::~NinjaCopyTargetWriter() = default;
 
 void NinjaCopyTargetWriter::Run() {
-  const Tool* copy_tool = target_->toolchain()->GetTool(Toolchain::TYPE_COPY);
+  const Tool* copy_tool = target_->toolchain()->GetTool(GeneralTool::kGeneralToolCopy);
   if (!copy_tool) {
     g_scheduler->FailWithError(Err(
         nullptr, "Copy tool not defined",
@@ -32,7 +33,7 @@
     return;
   }
 
-  const Tool* stamp_tool = target_->toolchain()->GetTool(Toolchain::TYPE_STAMP);
+  const Tool* stamp_tool = target_->toolchain()->GetTool(GeneralTool::kGeneralToolStamp);
   if (!stamp_tool) {
     g_scheduler->FailWithError(Err(
         nullptr, "Copy tool not defined",
@@ -66,7 +67,7 @@
   const SubstitutionPattern& output_subst = output_subst_list.list()[0];
 
   std::string tool_name = GetNinjaRulePrefixForToolchain(settings_) +
-                          Toolchain::ToolTypeToName(Toolchain::TYPE_COPY);
+                          GeneralTool::kGeneralToolCopy;
 
   size_t num_stamp_uses = target_->sources().size();
   std::vector<OutputFile> input_deps = WriteInputDepsStampAndGetDep(
diff --git a/tools/gn/ninja_create_bundle_target_writer.cc b/tools/gn/ninja_create_bundle_target_writer.cc
index cfc3d91..32ae58e 100644
--- a/tools/gn/ninja_create_bundle_target_writer.cc
+++ b/tools/gn/ninja_create_bundle_target_writer.cc
@@ -7,6 +7,7 @@
 #include "base/macros.h"
 #include "base/strings/string_util.h"
 #include "tools/gn/filesystem_utils.h"
+#include "tools/gn/general_tool.h"
 #include "tools/gn/ninja_utils.h"
 #include "tools/gn/output_file.h"
 #include "tools/gn/scheduler.h"
@@ -21,10 +22,9 @@
          !target->bundle_data().partial_info_plist().is_null();
 }
 
-void FailWithMissingToolError(Toolchain::ToolType tool, const Target* target) {
-  const std::string& tool_name = Toolchain::ToolTypeToName(tool);
+void FailWithMissingToolError(const char* tool_name, const Target* target) {
   g_scheduler->FailWithError(
-      Err(nullptr, tool_name + " tool not defined",
+      Err(nullptr, std::string(tool_name) + " tool not defined",
           "The toolchain " +
               target->toolchain()->label().GetUserVisibleName(false) +
               "\n"
@@ -36,9 +36,9 @@
 }
 
 bool EnsureAllToolsAvailable(const Target* target) {
-  const Toolchain::ToolType kRequiredTools[] = {
-      Toolchain::TYPE_COPY_BUNDLE_DATA,
-      Toolchain::TYPE_STAMP,
+  const char* kRequiredTools[] = {
+      GeneralTool::kGeneralToolCopyBundleData,
+      GeneralTool::kGeneralToolStamp,
   };
 
   for (size_t i = 0; i < arraysize(kRequiredTools); ++i) {
@@ -51,8 +51,8 @@
   // The compile_xcassets tool is only required if the target has asset
   // catalog resources to compile.
   if (TargetRequireAssetCatalogCompilation(target)) {
-    if (!target->toolchain()->GetTool(Toolchain::TYPE_COMPILE_XCASSETS)) {
-      FailWithMissingToolError(Toolchain::TYPE_COMPILE_XCASSETS, target);
+    if (!target->toolchain()->GetTool(GeneralTool::kGeneralToolCompileXCAssets)) {
+      FailWithMissingToolError(GeneralTool::kGeneralToolCompileXCAssets, target);
       return false;
     }
   }
@@ -161,7 +161,7 @@
     out_ << "build ";
     path_output_.WriteFile(out_, expanded_output_file);
     out_ << ": " << GetNinjaRulePrefixForToolchain(settings_)
-         << Toolchain::ToolTypeToName(Toolchain::TYPE_COPY_BUNDLE_DATA) << " ";
+         << GeneralTool::kGeneralToolCopyBundleData << " ";
     path_output_.WriteFile(out_, source_file);
 
     if (!order_only_deps.empty()) {
@@ -205,7 +205,7 @@
     out_ << "build ";
     path_output_.WriteFile(out_, partial_info_plist);
     out_ << ": " << GetNinjaRulePrefixForToolchain(settings_)
-         << Toolchain::ToolTypeToName(Toolchain::TYPE_STAMP);
+         << GeneralTool::kGeneralToolStamp;
     if (!order_only_deps.empty()) {
       out_ << " ||";
       path_output_.WriteFiles(out_, order_only_deps);
@@ -230,7 +230,7 @@
   }
 
   out_ << ": " << GetNinjaRulePrefixForToolchain(settings_)
-       << Toolchain::ToolTypeToName(Toolchain::TYPE_COMPILE_XCASSETS);
+       << GeneralTool::kGeneralToolCompileXCAssets;
 
   std::set<SourceFile> asset_catalog_bundles;
   for (const auto& source : target_->bundle_data().assets_catalog_sources()) {
@@ -274,7 +274,7 @@
   out_ << "build ";
   path_output_.WriteFile(out_, xcassets_input_stamp_file);
   out_ << ": " << GetNinjaRulePrefixForToolchain(settings_)
-       << Toolchain::ToolTypeToName(Toolchain::TYPE_STAMP);
+       << GeneralTool::kGeneralToolStamp;
 
   for (const Target* target : dependencies) {
     out_ << " ";
@@ -340,7 +340,7 @@
   out_ << "build ";
   path_output_.WriteFile(out_, code_signing_input_stamp_file);
   out_ << ": " << GetNinjaRulePrefixForToolchain(settings_)
-       << Toolchain::ToolTypeToName(Toolchain::TYPE_STAMP);
+       << GeneralTool::kGeneralToolStamp;
 
   for (const SourceFile& source : code_signing_input_files) {
     out_ << " ";
diff --git a/tools/gn/ninja_target_command_util.cc b/tools/gn/ninja_target_command_util.cc
index 29662cd..2aa3e4b 100644
--- a/tools/gn/ninja_target_command_util.cc
+++ b/tools/gn/ninja_target_command_util.cc
@@ -6,39 +6,36 @@
 
 #include <string.h>
 
+#include "tools/gn/c_tool.h"
 #include "tools/gn/substitution_writer.h"
 
 namespace {
 
 // Returns the language-specific suffix for precompiled header files.
-const char* GetPCHLangSuffixForToolType(Toolchain::ToolType type) {
-  switch (type) {
-    case Toolchain::TYPE_CC:
-      return "c";
-    case Toolchain::TYPE_CXX:
-      return "cc";
-    case Toolchain::TYPE_OBJC:
-      return "m";
-    case Toolchain::TYPE_OBJCXX:
-      return "mm";
-    default:
-      NOTREACHED() << "Not a valid PCH tool type: " << type;
-      return "";
-  }
+const char* GetPCHLangSuffixForToolType(const char* name) {
+  if (name == CTool::kCToolCc)
+    return "c";
+  if (name == CTool::kCToolCxx)
+    return "cc";
+  if (name == CTool::kCToolObjC)
+    return "m";
+  if (name == CTool::kCToolObjCxx)
+    return "mm";
+  NOTREACHED() << "Not a valid PCH tool type: " << name;
+  return "";
 }
 
 }  // namespace
 
 // Returns the computed name of the Windows .pch file for the given
 // tool type. The tool must support precompiled headers.
-OutputFile GetWindowsPCHFile(const Target* target,
-                             Toolchain::ToolType tool_type) {
+OutputFile GetWindowsPCHFile(const Target* target, const char* tool_name) {
   // Use "obj/{dir}/{target_name}_{lang}.pch" which ends up
   // looking like "obj/chrome/browser/browser_cc.pch"
   OutputFile ret = GetBuildDirForTargetAsOutputFile(target, BuildDirType::OBJ);
   ret.value().append(target->label().name());
   ret.value().push_back('_');
-  ret.value().append(GetPCHLangSuffixForToolType(tool_type));
+  ret.value().append(GetPCHLangSuffixForToolType(tool_name));
   ret.value().append(".pch");
 
   return ret;
@@ -47,7 +44,7 @@
 void WriteOneFlag(const Target* target,
                   SubstitutionType subst_enum,
                   bool has_precompiled_headers,
-                  Toolchain::ToolType tool_type,
+                  const char* tool_name,
                   const std::vector<std::string>& (ConfigValues::*getter)()
                       const,
                   EscapeOptions flag_escape_options,
@@ -61,28 +58,29 @@
     out << kSubstitutionNinjaNames[subst_enum] << " =";
 
   if (has_precompiled_headers) {
-    const Tool* tool = target->toolchain()->GetTool(tool_type);
-    if (tool && tool->precompiled_header_type() == Tool::PCH_MSVC) {
+    const CTool* tool = target->toolchain()->GetToolAsC(tool_name);
+    if (tool && tool->precompiled_header_type() == CTool::PCH_MSVC) {
       // Name the .pch file.
       out << " /Fp";
-      path_output.WriteFile(out, GetWindowsPCHFile(target, tool_type));
+      path_output.WriteFile(out, GetWindowsPCHFile(target, tool_name));
 
       // Enables precompiled headers and names the .h file. It's a string
       // rather than a file name (so no need to rebase or use path_output).
       out << " /Yu" << target->config_values().precompiled_header();
       RecursiveTargetConfigStringsToStream(target, getter, flag_escape_options,
                                            out);
-    } else if (tool && tool->precompiled_header_type() == Tool::PCH_GCC) {
+    } else if (tool && tool->precompiled_header_type() == CTool::PCH_GCC) {
       // The targets to build the .gch files should omit the -include flag
-      // below. To accomplish this, each substitution flag is overwritten in the
-      // target rule and these values are repeated. The -include flag is omitted
-      // in place of the required -x <header lang> flag for .gch targets.
+      // below. To accomplish this, each substitution flag is overwritten in
+      // the target rule and these values are repeated. The -include flag is
+      // omitted in place of the required -x <header lang> flag for .gch
+      // targets.
       RecursiveTargetConfigStringsToStream(target, getter, flag_escape_options,
                                            out);
 
       // Compute the gch file (it will be language-specific).
       std::vector<OutputFile> outputs;
-      GetPCHOutputFiles(target, tool_type, &outputs);
+      GetPCHOutputFiles(target, tool_name, &outputs);
       if (!outputs.empty()) {
         // Trim the .gch suffix for the -include flag.
         // e.g. for gch file foo/bar/target.precompiled.h.gch:
@@ -105,14 +103,14 @@
 }
 
 void GetPCHOutputFiles(const Target* target,
-                       Toolchain::ToolType tool_type,
+                       const char* tool_name,
                        std::vector<OutputFile>* outputs) {
   outputs->clear();
 
   // Compute the tool. This must use the tool type passed in rather than the
   // detected file type of the precompiled source file since the same
   // precompiled source file will be used for separate C/C++ compiles.
-  const Tool* tool = target->toolchain()->GetTool(tool_type);
+  const CTool* tool = target->toolchain()->GetToolAsC(tool_name);
   if (!tool)
     return;
   SubstitutionWriter::ApplyListToCompilerAsOutputFile(
@@ -134,16 +132,16 @@
   DCHECK(output_value[extension_offset - 1] == '.');
 
   std::string output_extension;
-  Tool::PrecompiledHeaderType header_type = tool->precompiled_header_type();
+  CTool::PrecompiledHeaderType header_type = tool->precompiled_header_type();
   switch (header_type) {
-    case Tool::PCH_MSVC:
+    case CTool::PCH_MSVC:
       output_extension = GetWindowsPCHObjectExtension(
-          tool_type, output_value.substr(extension_offset - 1));
+          tool_name, output_value.substr(extension_offset - 1));
       break;
-    case Tool::PCH_GCC:
-      output_extension = GetGCCPCHOutputExtension(tool_type);
+    case CTool::PCH_GCC:
+      output_extension = GetGCCPCHOutputExtension(tool_name);
       break;
-    case Tool::PCH_NONE:
+    case CTool::PCH_NONE:
       NOTREACHED() << "No outputs for no PCH type.";
       break;
   }
@@ -151,8 +149,8 @@
                        output_extension);
 }
 
-std::string GetGCCPCHOutputExtension(Toolchain::ToolType tool_type) {
-  const char* lang_suffix = GetPCHLangSuffixForToolType(tool_type);
+std::string GetGCCPCHOutputExtension(const char* tool_name) {
+  const char* lang_suffix = GetPCHLangSuffixForToolType(tool_name);
   std::string result = ".";
   // For GCC, the output name must have a .gch suffix and be annotated with
   // the language type. For example:
@@ -166,9 +164,9 @@
   return result;
 }
 
-std::string GetWindowsPCHObjectExtension(Toolchain::ToolType tool_type,
+std::string GetWindowsPCHObjectExtension(const char* tool_name,
                                          const std::string& obj_extension) {
-  const char* lang_suffix = GetPCHLangSuffixForToolType(tool_type);
+  const char* lang_suffix = GetPCHLangSuffixForToolType(tool_name);
   std::string result = ".";
   // For MSVC, annotate the obj files with the language type. For example:
   //   obj/foo/target_name.precompile.obj ->
diff --git a/tools/gn/ninja_target_command_util.h b/tools/gn/ninja_target_command_util.h
index 619add3..686fada 100644
--- a/tools/gn/ninja_target_command_util.h
+++ b/tools/gn/ninja_target_command_util.h
@@ -63,7 +63,7 @@
 void WriteOneFlag(const Target* target,
                   SubstitutionType subst_enum,
                   bool has_precompiled_headers,
-                  Toolchain::ToolType tool_type,
+                  const char* tool_name,
                   const std::vector<std::string>& (ConfigValues::*getter)()
                       const,
                   EscapeOptions flag_escape_options,
@@ -74,11 +74,11 @@
 // Fills |outputs| with the object or gch file for the precompiled header of the
 // given type (flag type and tool type must match).
 void GetPCHOutputFiles(const Target* target,
-                       Toolchain::ToolType tool_type,
+                       const char* tool_name,
                        std::vector<OutputFile>* outputs);
 
-std::string GetGCCPCHOutputExtension(Toolchain::ToolType tool_type);
-std::string GetWindowsPCHObjectExtension(Toolchain::ToolType tool_type,
+std::string GetGCCPCHOutputExtension(const char* tool_name);
+std::string GetWindowsPCHObjectExtension(const char* tool_name,
                                          const std::string& obj_extension);
 
 #endif  // TOOLS_GN_NINJA_TARGET_COMMAND_WRITER_H_
diff --git a/tools/gn/ninja_target_writer.cc b/tools/gn/ninja_target_writer.cc
index 253436b..8287ec0 100644
--- a/tools/gn/ninja_target_writer.cc
+++ b/tools/gn/ninja_target_writer.cc
@@ -12,6 +12,7 @@
 #include "tools/gn/err.h"
 #include "tools/gn/escape.h"
 #include "tools/gn/filesystem_utils.h"
+#include "tools/gn/general_tool.h"
 #include "tools/gn/ninja_action_target_writer.h"
 #include "tools/gn/ninja_binary_target_writer.h"
 #include "tools/gn/ninja_bundle_data_target_writer.h"
@@ -294,7 +295,7 @@
   out_ << "build ";
   path_output_.WriteFile(out_, input_stamp_file);
   out_ << ": " << GetNinjaRulePrefixForToolchain(settings_)
-       << Toolchain::ToolTypeToName(Toolchain::TYPE_STAMP);
+       << GeneralTool::kGeneralToolStamp;
   path_output_.WriteFiles(out_, outs);
 
   out_ << "\n";
@@ -317,7 +318,7 @@
   path_output_.WriteFile(out_, stamp_file);
 
   out_ << ": " << GetNinjaRulePrefixForToolchain(settings_)
-       << Toolchain::ToolTypeToName(Toolchain::TYPE_STAMP);
+       << GeneralTool::kGeneralToolStamp;
   path_output_.WriteFiles(out_, files);
 
   if (!order_only_deps.empty()) {
diff --git a/tools/gn/ninja_toolchain_writer.cc b/tools/gn/ninja_toolchain_writer.cc
index ee7c5cd..faf9d8a 100644
--- a/tools/gn/ninja_toolchain_writer.cc
+++ b/tools/gn/ninja_toolchain_writer.cc
@@ -9,7 +9,9 @@
 #include "base/files/file_util.h"
 #include "base/strings/stringize_macros.h"
 #include "tools/gn/build_settings.h"
+#include "tools/gn/c_tool.h"
 #include "tools/gn/filesystem_utils.h"
+#include "tools/gn/general_tool.h"
 #include "tools/gn/ninja_utils.h"
 #include "tools/gn/pool.h"
 #include "tools/gn/settings.h"
@@ -40,13 +42,10 @@
     const std::vector<NinjaWriter::TargetRulePair>& rules) {
   std::string rule_prefix = GetNinjaRulePrefixForToolchain(settings_);
 
-  for (int i = Toolchain::TYPE_NONE + 1; i < Toolchain::TYPE_NUMTYPES; i++) {
-    Toolchain::ToolType tool_type = static_cast<Toolchain::ToolType>(i);
-    const Tool* tool = toolchain_->GetTool(tool_type);
-    if (tool_type == Toolchain::TYPE_ACTION)
+  for (const auto& tool : toolchain_->tools()) {
+    if (tool.second->name() == GeneralTool::kGeneralToolAction)
       continue;
-    if (tool)
-      WriteToolRule(tool_type, tool, rule_prefix);
+    WriteToolRule(tool.second.get(), rule_prefix);
   }
   out_ << std::endl;
 
@@ -76,11 +75,9 @@
   return true;
 }
 
-void NinjaToolchainWriter::WriteToolRule(const Toolchain::ToolType type,
-                                         const Tool* tool,
+void NinjaToolchainWriter::WriteToolRule(Tool* tool,
                                          const std::string& rule_prefix) {
-  out_ << "rule " << rule_prefix << Toolchain::ToolTypeToName(type)
-       << std::endl;
+  out_ << "rule " << rule_prefix << tool->name() << std::endl;
 
   // Rules explicitly include shell commands, so don't try to escape.
   EscapeOptions options;
@@ -93,15 +90,17 @@
   WriteRulePattern("rspfile", tool->rspfile(), options);
   WriteRulePattern("rspfile_content", tool->rspfile_content(), options);
 
-  if (tool->depsformat() == Tool::DEPS_GCC) {
-    // GCC-style deps require a depfile.
-    if (!tool->depfile().empty()) {
-      WriteRulePattern("depfile", tool->depfile(), options);
-      out_ << kIndent << "deps = gcc" << std::endl;
+  if (CTool* c_tool = tool->AsC()) {
+    if (c_tool->depsformat() == CTool::DEPS_GCC) {
+      // GCC-style deps require a depfile.
+      if (!tool->depfile().empty()) {
+        WriteRulePattern("depfile", tool->depfile(), options);
+        out_ << kIndent << "deps = gcc" << std::endl;
+      }
+    } else if (c_tool->depsformat() == CTool::DEPS_MSVC) {
+      // MSVC deps don't have a depfile.
+      out_ << kIndent << "deps = msvc" << std::endl;
     }
-  } else if (tool->depsformat() == Tool::DEPS_MSVC) {
-    // MSVC deps don't have a depfile.
-    out_ << kIndent << "deps = msvc" << std::endl;
   }
 
   // Use pool is specified.
diff --git a/tools/gn/ninja_toolchain_writer.h b/tools/gn/ninja_toolchain_writer.h
index 9c6ad24..6db8d3c 100644
--- a/tools/gn/ninja_toolchain_writer.h
+++ b/tools/gn/ninja_toolchain_writer.h
@@ -40,9 +40,7 @@
   void Run(const std::vector<NinjaWriter::TargetRulePair>& extra_rules);
 
   void WriteRules();
-  void WriteToolRule(Toolchain::ToolType type,
-                     const Tool* tool,
-                     const std::string& rule_prefix);
+  void WriteToolRule(Tool* tool, const std::string& rule_prefix);
   void WriteRulePattern(const char* name,
                         const SubstitutionPattern& pattern,
                         const EscapeOptions& options);
diff --git a/tools/gn/ninja_toolchain_writer_unittest.cc b/tools/gn/ninja_toolchain_writer_unittest.cc
index a1963fd..2edc26a 100644
--- a/tools/gn/ninja_toolchain_writer_unittest.cc
+++ b/tools/gn/ninja_toolchain_writer_unittest.cc
@@ -13,8 +13,7 @@
 
   std::ostringstream stream;
   NinjaToolchainWriter writer(setup.settings(), setup.toolchain(), stream);
-  writer.WriteToolRule(Toolchain::TYPE_CC,
-                       setup.toolchain()->GetTool(Toolchain::TYPE_CC),
+  writer.WriteToolRule(setup.toolchain()->GetTool(CTool::kCToolCc),
                        std::string("prefix_"));
 
   EXPECT_EQ(
diff --git a/tools/gn/substitution_writer_unittest.cc b/tools/gn/substitution_writer_unittest.cc
index 8093e41..cc476e4 100644
--- a/tools/gn/substitution_writer_unittest.cc
+++ b/tools/gn/substitution_writer_unittest.cc
@@ -286,12 +286,12 @@
 
   // This tool has an output directory pattern and uses that for the
   // output name.
-  Tool tool;
+  std::unique_ptr<Tool> tool = Tool::CreateTool(CTool::kCToolLink);
   SubstitutionPattern out_dir_pattern;
   ASSERT_TRUE(out_dir_pattern.Parse("{{root_out_dir}}/{{target_output_name}}",
                                     nullptr, &err));
-  tool.set_default_output_dir(out_dir_pattern);
-  tool.SetComplete();
+  tool->set_default_output_dir(out_dir_pattern);
+  tool->SetComplete();
 
   // Default target with no output dir overrides.
   Target target(setup.settings(), Label(SourceDir("//foo/"), "baz"));
@@ -304,20 +304,20 @@
   ASSERT_TRUE(output_name.Parse("{{output_dir}}/{{target_output_name}}.exe",
                                 nullptr, &err));
   EXPECT_EQ("./baz/baz.exe",
-            SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(&target, &tool,
+            SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(&target, tool.get(),
                                                                  output_name)
                 .value());
 
   // Override the output name to the root build dir.
   target.set_output_dir(SourceDir("//out/Debug/"));
   EXPECT_EQ("./baz.exe", SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
-                             &target, &tool, output_name)
+                             &target, tool.get(), output_name)
                              .value());
 
   // Override the output name to a new subdirectory.
   target.set_output_dir(SourceDir("//out/Debug/foo/bar"));
   EXPECT_EQ("foo/bar/baz.exe",
-            SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(&target, &tool,
+            SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(&target, tool.get(),
                                                                  output_name)
                 .value());
 }
diff --git a/tools/gn/target.cc b/tools/gn/target.cc
index 376e09f..6272b44 100644
--- a/tools/gn/target.cc
+++ b/tools/gn/target.cc
@@ -10,6 +10,7 @@
 #include "base/stl_util.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
+#include "tools/gn/c_tool.h"
 #include "tools/gn/config_values_extractors.h"
 #include "tools/gn/deps_iterator.h"
 #include "tools/gn/filesystem_utils.h"
@@ -94,8 +95,8 @@
   if (consider_object_files && target->IsBinary()) {
     std::vector<OutputFile> source_outputs;
     for (const SourceFile& source : target->sources()) {
-      Toolchain::ToolType tool_type;
-      if (!target->GetOutputFilesForSource(source, &tool_type, &source_outputs))
+      const char* tool_name;
+      if (!target->GetOutputFilesForSource(source, &tool_name, &source_outputs))
         continue;
       if (base::ContainsValue(source_outputs, file))
         return true;
@@ -464,29 +465,27 @@
 
   // Tool not specified for this target type.
   if (err) {
-    *err =
-        Err(defined_from(), "This target uses an undefined tool.",
-            base::StringPrintf(
-                "The target %s\n"
-                "of type \"%s\"\n"
-                "uses toolchain %s\n"
-                "which doesn't have the tool \"%s\" defined.\n\n"
-                "Alas, I can not continue.",
-                label().GetUserVisibleName(false).c_str(),
-                GetStringForOutputType(output_type_),
-                label().GetToolchainLabel().GetUserVisibleName(false).c_str(),
-                Toolchain::ToolTypeToName(
-                    toolchain->GetToolTypeForTargetFinalOutput(this))
-                    .c_str()));
+    *err = Err(
+        defined_from(), "This target uses an undefined tool.",
+        base::StringPrintf(
+            "The target %s\n"
+            "of type \"%s\"\n"
+            "uses toolchain %s\n"
+            "which doesn't have the tool \"%s\" defined.\n\n"
+            "Alas, I can not continue.",
+            label().GetUserVisibleName(false).c_str(),
+            GetStringForOutputType(output_type_),
+            label().GetToolchainLabel().GetUserVisibleName(false).c_str(),
+            Tool::GetToolTypeForTargetFinalOutput(this)));
   }
   return false;
 }
 
 bool Target::GetOutputFilesForSource(const SourceFile& source,
-                                     Toolchain::ToolType* computed_tool_type,
+                                     const char** computed_tool_type,
                                      std::vector<OutputFile>* outputs) const {
   outputs->clear();
-  *computed_tool_type = Toolchain::TYPE_NONE;
+  *computed_tool_type = Tool::kToolNone;
 
   SourceFileType file_type = GetSourceFileType(source);
   if (file_type == SOURCE_UNKNOWN)
@@ -497,8 +496,8 @@
     return true;
   }
 
-  *computed_tool_type = toolchain_->GetToolTypeForSourceType(file_type);
-  if (*computed_tool_type == Toolchain::TYPE_NONE)
+  *computed_tool_type = Tool::GetToolTypeForSourceType(file_type);
+  if (*computed_tool_type == Tool::kToolNone)
     return false;  // No tool for this file (it's a header file or something).
   const Tool* tool = toolchain_->GetTool(*computed_tool_type);
   if (!tool)
@@ -681,31 +680,33 @@
     case SHARED_LIBRARY:
       CHECK(tool->outputs().list().size() >= 1);
       check_tool_outputs = true;
-      if (tool->link_output().empty() && tool->depend_output().empty()) {
-        // Default behavior, use the first output file for both.
-        link_output_file_ = dependency_output_file_ =
-            SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
-                this, tool, tool->outputs().list()[0]);
-      } else {
-        // Use the tool-specified ones.
-        if (!tool->link_output().empty()) {
-          link_output_file_ =
+      if (const CTool* ctool = tool->AsC()) {
+        if (ctool->link_output().empty() && ctool->depend_output().empty()) {
+          // Default behavior, use the first output file for both.
+          link_output_file_ = dependency_output_file_ =
               SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
-                  this, tool, tool->link_output());
+                  this, tool, tool->outputs().list()[0]);
+        } else {
+          // Use the tool-specified ones.
+          if (!ctool->link_output().empty()) {
+            link_output_file_ =
+                SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
+                    this, tool, ctool->link_output());
+          }
+          if (!ctool->depend_output().empty()) {
+            dependency_output_file_ =
+                SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
+                    this, tool, ctool->depend_output());
+          }
         }
-        if (!tool->depend_output().empty()) {
-          dependency_output_file_ =
-              SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
-                  this, tool, tool->depend_output());
+        if (tool->runtime_outputs().list().empty()) {
+          // Default to the link output for the runtime output.
+          runtime_outputs_.push_back(link_output_file_);
+        } else {
+          SubstitutionWriter::ApplyListToLinkerAsOutputFile(
+              this, tool, tool->runtime_outputs(), &runtime_outputs_);
         }
       }
-      if (tool->runtime_outputs().list().empty()) {
-        // Default to the link output for the runtime output.
-        runtime_outputs_.push_back(link_output_file_);
-      } else {
-        SubstitutionWriter::ApplyListToLinkerAsOutputFile(
-            this, tool, tool->runtime_outputs(), &runtime_outputs_);
-      }
       break;
     case UNKNOWN:
     default:
diff --git a/tools/gn/target.h b/tools/gn/target.h
index b73ae57..e99d813 100644
--- a/tools/gn/target.h
+++ b/tools/gn/target.h
@@ -331,7 +331,7 @@
   // are just passed to the output. The output will always be overwritten, not
   // appended to.
   bool GetOutputFilesForSource(const SourceFile& source,
-                               Toolchain::ToolType* computed_tool_type,
+                               const char** computed_tool_type,
                                std::vector<OutputFile>* outputs) const;
 
  private:
diff --git a/tools/gn/target_unittest.cc b/tools/gn/target_unittest.cc
index b6ad0ff..f117846 100644
--- a/tools/gn/target_unittest.cc
+++ b/tools/gn/target_unittest.cc
@@ -637,7 +637,8 @@
 
   Toolchain toolchain(setup.settings(), Label(SourceDir("//tc/"), "tc"));
 
-  std::unique_ptr<Tool> solink_tool = std::make_unique<Tool>();
+  std::unique_ptr<Tool> solink = Tool::CreateTool(CTool::kCToolSolink);
+  CTool* solink_tool = solink->AsC();
   solink_tool->set_output_prefix("lib");
   solink_tool->set_default_output_extension(".so");
 
@@ -656,7 +657,7 @@
   solink_tool->set_outputs(
       SubstitutionList::MakeForTest(kLinkPattern, kDependPattern));
 
-  toolchain.SetTool(Toolchain::TYPE_SOLINK, std::move(solink_tool));
+  toolchain.SetTool(std::move(solink));
 
   Target target(setup.settings(), Label(SourceDir("//a/"), "a"));
   target.set_output_type(Target::SHARED_LIBRARY);
@@ -678,7 +679,8 @@
 
   Toolchain toolchain(setup.settings(), Label(SourceDir("//tc/"), "tc"));
 
-  std::unique_ptr<Tool> solink_tool = std::make_unique<Tool>();
+  std::unique_ptr<Tool> solink = Tool::CreateTool(CTool::kCToolSolink);
+  CTool* solink_tool = solink->AsC();
   solink_tool->set_output_prefix("");
   solink_tool->set_default_output_extension(".dll");
 
@@ -699,7 +701,7 @@
   solink_tool->set_runtime_outputs(
       SubstitutionList::MakeForTest(kDllPattern, kPdbPattern));
 
-  toolchain.SetTool(Toolchain::TYPE_SOLINK, std::move(solink_tool));
+  toolchain.SetTool(std::move(solink));
 
   Target target(setup.settings(), Label(SourceDir("//a/"), "a"));
   target.set_output_type(Target::SHARED_LIBRARY);
diff --git a/tools/gn/test_with_scope.cc b/tools/gn/test_with_scope.cc
index 7063acd..9dfb6c4 100644
--- a/tools/gn/test_with_scope.cc
+++ b/tools/gn/test_with_scope.cc
@@ -74,119 +74,125 @@
   Err err;
 
   // CC
-  std::unique_ptr<Tool> cc_tool = std::make_unique<Tool>();
+  std::unique_ptr<Tool> cc_tool = Tool::CreateTool(CTool::kCToolCc);
   SetCommandForTool(
       "cc {{source}} {{cflags}} {{cflags_c}} {{defines}} {{include_dirs}} "
       "-o {{output}}",
       cc_tool.get());
   cc_tool->set_outputs(SubstitutionList::MakeForTest(
       "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
-  toolchain->SetTool(Toolchain::TYPE_CC, std::move(cc_tool));
+  toolchain->SetTool(std::move(cc_tool));
 
   // CXX
-  std::unique_ptr<Tool> cxx_tool = std::make_unique<Tool>();
+  std::unique_ptr<Tool> cxx_tool = Tool::CreateTool(CTool::kCToolCxx);
   SetCommandForTool(
       "c++ {{source}} {{cflags}} {{cflags_cc}} {{defines}} {{include_dirs}} "
       "-o {{output}}",
       cxx_tool.get());
   cxx_tool->set_outputs(SubstitutionList::MakeForTest(
       "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
-  toolchain->SetTool(Toolchain::TYPE_CXX, std::move(cxx_tool));
+  toolchain->SetTool(std::move(cxx_tool));
 
   // OBJC
-  std::unique_ptr<Tool> objc_tool = std::make_unique<Tool>();
+  std::unique_ptr<Tool> objc_tool = Tool::CreateTool(CTool::kCToolObjC);
   SetCommandForTool(
       "objcc {{source}} {{cflags}} {{cflags_objc}} {{defines}} "
       "{{include_dirs}} -o {{output}}",
       objc_tool.get());
   objc_tool->set_outputs(SubstitutionList::MakeForTest(
       "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
-  toolchain->SetTool(Toolchain::TYPE_OBJC, std::move(objc_tool));
+  toolchain->SetTool(std::move(objc_tool));
 
   // OBJC
-  std::unique_ptr<Tool> objcxx_tool = std::make_unique<Tool>();
+  std::unique_ptr<Tool> objcxx_tool = Tool::CreateTool(CTool::kCToolObjCxx);
   SetCommandForTool(
       "objcxx {{source}} {{cflags}} {{cflags_objcc}} {{defines}} "
       "{{include_dirs}} -o {{output}}",
       objcxx_tool.get());
   objcxx_tool->set_outputs(SubstitutionList::MakeForTest(
       "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
-  toolchain->SetTool(Toolchain::TYPE_OBJCXX, std::move(objcxx_tool));
+  toolchain->SetTool(std::move(objcxx_tool));
 
   // Don't use RC and ASM tools in unit tests yet. Add here if needed.
 
   // ALINK
-  std::unique_ptr<Tool> alink_tool = std::make_unique<Tool>();
-  SetCommandForTool("ar {{output}} {{source}}", alink_tool.get());
+  std::unique_ptr<Tool> alink = Tool::CreateTool(CTool::kCToolAlink);
+  CTool* alink_tool = alink->AsC();
+  SetCommandForTool("ar {{output}} {{source}}", alink_tool);
   alink_tool->set_lib_switch("-l");
   alink_tool->set_lib_dir_switch("-L");
   alink_tool->set_output_prefix("lib");
   alink_tool->set_outputs(SubstitutionList::MakeForTest(
       "{{target_out_dir}}/{{target_output_name}}.a"));
-  toolchain->SetTool(Toolchain::TYPE_ALINK, std::move(alink_tool));
+  toolchain->SetTool(std::move(alink));
 
   // SOLINK
-  std::unique_ptr<Tool> solink_tool = std::make_unique<Tool>();
+  std::unique_ptr<Tool> solink = Tool::CreateTool(CTool::kCToolSolink);
+  CTool* solink_tool = solink->AsC();
   SetCommandForTool(
       "ld -shared -o {{target_output_name}}.so {{inputs}} "
       "{{ldflags}} {{libs}}",
-      solink_tool.get());
+      solink_tool);
   solink_tool->set_lib_switch("-l");
   solink_tool->set_lib_dir_switch("-L");
   solink_tool->set_output_prefix("lib");
   solink_tool->set_default_output_extension(".so");
   solink_tool->set_outputs(SubstitutionList::MakeForTest(
       "{{root_out_dir}}/{{target_output_name}}{{output_extension}}"));
-  toolchain->SetTool(Toolchain::TYPE_SOLINK, std::move(solink_tool));
+  toolchain->SetTool(std::move(solink));
 
   // SOLINK_MODULE
-  std::unique_ptr<Tool> solink_module_tool = std::make_unique<Tool>();
+  std::unique_ptr<Tool> solink_module =
+      Tool::CreateTool(CTool::kCToolSolinkModule);
+  CTool* solink_module_tool = solink_module->AsC();
   SetCommandForTool(
       "ld -bundle -o {{target_output_name}}.so {{inputs}} "
       "{{ldflags}} {{libs}}",
-      solink_module_tool.get());
+      solink_module_tool);
   solink_module_tool->set_lib_switch("-l");
   solink_module_tool->set_lib_dir_switch("-L");
   solink_module_tool->set_output_prefix("lib");
   solink_module_tool->set_default_output_extension(".so");
   solink_module_tool->set_outputs(SubstitutionList::MakeForTest(
       "{{root_out_dir}}/{{target_output_name}}{{output_extension}}"));
-  toolchain->SetTool(Toolchain::TYPE_SOLINK_MODULE,
-                     std::move(solink_module_tool));
+  toolchain->SetTool(std::move(solink_module));
 
   // LINK
-  std::unique_ptr<Tool> link_tool = std::make_unique<Tool>();
+  std::unique_ptr<Tool> link = Tool::CreateTool(CTool::kCToolLink);
+  CTool* link_tool = link->AsC();
   SetCommandForTool(
       "ld -o {{target_output_name}} {{source}} "
       "{{ldflags}} {{libs}}",
-      link_tool.get());
+      link_tool);
   link_tool->set_lib_switch("-l");
   link_tool->set_lib_dir_switch("-L");
   link_tool->set_outputs(
       SubstitutionList::MakeForTest("{{root_out_dir}}/{{target_output_name}}"));
-  toolchain->SetTool(Toolchain::TYPE_LINK, std::move(link_tool));
+  toolchain->SetTool(std::move(link));
 
   // STAMP
-  std::unique_ptr<Tool> stamp_tool = std::make_unique<Tool>();
+  std::unique_ptr<Tool> stamp_tool =
+      Tool::CreateTool(GeneralTool::kGeneralToolStamp);
   SetCommandForTool("touch {{output}}", stamp_tool.get());
-  toolchain->SetTool(Toolchain::TYPE_STAMP, std::move(stamp_tool));
+  toolchain->SetTool(std::move(stamp_tool));
 
   // COPY
-  std::unique_ptr<Tool> copy_tool = std::make_unique<Tool>();
+  std::unique_ptr<Tool> copy_tool =
+      Tool::CreateTool(GeneralTool::kGeneralToolCopy);
   SetCommandForTool("cp {{source}} {{output}}", copy_tool.get());
-  toolchain->SetTool(Toolchain::TYPE_COPY, std::move(copy_tool));
+  toolchain->SetTool(std::move(copy_tool));
 
   // COPY_BUNDLE_DATA
-  std::unique_ptr<Tool> copy_bundle_data_tool = std::make_unique<Tool>();
+  std::unique_ptr<Tool> copy_bundle_data_tool =
+      Tool::CreateTool(GeneralTool::kGeneralToolCopyBundleData);
   SetCommandForTool("cp {{source}} {{output}}", copy_bundle_data_tool.get());
-  toolchain->SetTool(Toolchain::TYPE_COPY_BUNDLE_DATA,
-                     std::move(copy_bundle_data_tool));
+  toolchain->SetTool(std::move(copy_bundle_data_tool));
 
   // COMPILE_XCASSETS
-  std::unique_ptr<Tool> compile_xcassets_tool = std::make_unique<Tool>();
+  std::unique_ptr<Tool> compile_xcassets_tool =
+      Tool::CreateTool(GeneralTool::kGeneralToolCompileXCAssets);
   SetCommandForTool("touch {{output}}", compile_xcassets_tool.get());
-  toolchain->SetTool(Toolchain::TYPE_COMPILE_XCASSETS,
-                     std::move(compile_xcassets_tool));
+  toolchain->SetTool(std::move(compile_xcassets_tool));
 
   toolchain->ToolchainSetupComplete();
 }
diff --git a/tools/gn/test_with_scope.h b/tools/gn/test_with_scope.h
index 8853243..7950259 100644
--- a/tools/gn/test_with_scope.h
+++ b/tools/gn/test_with_scope.h
@@ -10,7 +10,9 @@
 
 #include "base/macros.h"
 #include "tools/gn/build_settings.h"
+#include "tools/gn/c_tool.h"
 #include "tools/gn/err.h"
+#include "tools/gn/general_tool.h"
 #include "tools/gn/input_file.h"
 #include "tools/gn/parse_tree.h"
 #include "tools/gn/scope.h"
diff --git a/tools/gn/tool.cc b/tools/gn/tool.cc
index b066fba..ef4c94b 100644
--- a/tools/gn/tool.cc
+++ b/tools/gn/tool.cc
@@ -3,17 +3,18 @@
 // found in the LICENSE file.
 
 #include "tools/gn/tool.h"
+#include "tools/gn/c_tool.h"
+#include "tools/gn/general_tool.h"
+#include "tools/gn/target.h"
 
-Tool::Tool()
-    : defined_from_(nullptr),
-      depsformat_(DEPS_GCC),
-      precompiled_header_type_(PCH_NONE),
-      restat_(false),
-      complete_(false) {}
+const char* Tool::kToolNone = "";
+
+Tool::Tool(const char* n)
+    : defined_from_(nullptr), restat_(false), complete_(false), name_(n) {}
 
 Tool::~Tool() = default;
 
-void Tool::SetComplete() {
+void Tool::SetToolComplete() {
   DCHECK(!complete_);
   complete_ = true;
 
@@ -21,8 +22,292 @@
   depfile_.FillRequiredTypes(&substitution_bits_);
   description_.FillRequiredTypes(&substitution_bits_);
   outputs_.FillRequiredTypes(&substitution_bits_);
-  link_output_.FillRequiredTypes(&substitution_bits_);
-  depend_output_.FillRequiredTypes(&substitution_bits_);
   rspfile_.FillRequiredTypes(&substitution_bits_);
   rspfile_content_.FillRequiredTypes(&substitution_bits_);
 }
+
+GeneralTool* Tool::AsGeneral() {
+  return nullptr;
+}
+
+const GeneralTool* Tool::AsGeneral() const {
+  return nullptr;
+}
+
+CTool* Tool::AsC() {
+  return nullptr;
+}
+
+const CTool* Tool::AsC() const {
+  return nullptr;
+}
+
+bool Tool::IsPatternInOutputList(const SubstitutionList& output_list,
+                                 const SubstitutionPattern& pattern) const {
+  for (const auto& cur : output_list.list()) {
+    if (pattern.ranges().size() == cur.ranges().size() &&
+        std::equal(pattern.ranges().begin(), pattern.ranges().end(),
+                   cur.ranges().begin()))
+      return true;
+  }
+  return false;
+}
+
+bool Tool::ValidateSubstitutionList(const std::vector<SubstitutionType>& list,
+                                    const Value* origin,
+                                    Err* err) const {
+  for (const auto& cur_type : list) {
+    if (!ValidateSubstitution(cur_type)) {
+      *err = Err(*origin, "Pattern not valid here.",
+                 "You used the pattern " +
+                     std::string(kSubstitutionNames[cur_type]) +
+                     " which is not valid\nfor this variable.");
+      return false;
+    }
+  }
+  return true;
+}
+
+bool Tool::ReadBool(Scope* scope, const char* var, bool* field, Err* err) {
+  DCHECK(!complete_);
+  const Value* v = scope->GetValue(var, true);
+  if (!v)
+    return true;  // Not present is fine.
+  if (!v->VerifyTypeIs(Value::BOOLEAN, err))
+    return false;
+  *field = v->boolean_value();
+  return true;
+}
+
+bool Tool::ReadString(Scope* scope,
+                      const char* var,
+                      std::string* field,
+                      Err* err) {
+  DCHECK(!complete_);
+  const Value* v = scope->GetValue(var, true);
+  if (!v)
+    return true;  // Not present is fine.
+  if (!v->VerifyTypeIs(Value::STRING, err))
+    return false;
+  *field = v->string_value();
+  return true;
+}
+
+bool Tool::ReadPattern(Scope* scope,
+                       const char* var,
+                       SubstitutionPattern* field,
+                       Err* err) {
+  DCHECK(!complete_);
+  const Value* value = scope->GetValue(var, true);
+  if (!value)
+    return true;  // Not present is fine.
+  if (!value->VerifyTypeIs(Value::STRING, err))
+    return false;
+
+  SubstitutionPattern pattern;
+  if (!pattern.Parse(*value, err))
+    return false;
+  if (!ValidateSubstitutionList(pattern.required_types(), value, err))
+    return false;
+
+  *field = std::move(pattern);
+  return true;
+}
+
+bool Tool::ReadPatternList(Scope* scope,
+                           const char* var,
+                           SubstitutionList* field,
+                           Err* err) {
+  DCHECK(!complete_);
+  const Value* value = scope->GetValue(var, true);
+  if (!value)
+    return true;  // Not present is fine.
+  if (!value->VerifyTypeIs(Value::LIST, err))
+    return false;
+
+  SubstitutionList list;
+  if (!list.Parse(*value, err))
+    return false;
+
+  // Validate the right kinds of patterns are used.
+  if (!ValidateSubstitutionList(list.required_types(), value, err))
+    return false;
+
+  *field = std::move(list);
+  return true;
+}
+
+bool Tool::ReadLabel(Scope* scope,
+                     const char* var,
+                     const Label& current_toolchain,
+                     LabelPtrPair<Pool>* field,
+                     Err* err) {
+  DCHECK(!complete_);
+  const Value* v = scope->GetValue(var, true);
+  if (!v)
+    return true;  // Not present is fine.
+
+  Label label =
+      Label::Resolve(scope->GetSourceDir(), current_toolchain, *v, err);
+  if (err->has_error())
+    return false;
+
+  LabelPtrPair<Pool> pair(label);
+  pair.origin = defined_from();
+
+  *field = std::move(pair);
+  return true;
+}
+
+bool Tool::ReadOutputExtension(Scope* scope, Err* err) {
+  DCHECK(!complete_);
+  const Value* value = scope->GetValue("default_output_extension", true);
+  if (!value)
+    return true;  // Not present is fine.
+  if (!value->VerifyTypeIs(Value::STRING, err))
+    return false;
+
+  if (value->string_value().empty())
+    return true;  // Accept empty string.
+
+  if (value->string_value()[0] != '.') {
+    *err = Err(*value, "default_output_extension must begin with a '.'");
+    return false;
+  }
+
+  set_default_output_extension(value->string_value());
+  return true;
+}
+
+bool Tool::InitTool(Scope* scope, Toolchain* toolchain, Err* err) {
+  if (!ReadPattern(scope, "command", &command_, err) ||
+      !ReadOutputExtension(scope, err) ||
+      !ReadPattern(scope, "depfile", &depfile_, err) ||
+      !ReadPattern(scope, "description", &description_, err) ||
+      !ReadPatternList(scope, "runtime_outputs", &runtime_outputs_, err) ||
+      !ReadString(scope, "output_prefix", &output_prefix_, err) ||
+      !ReadPattern(scope, "default_output_dir", &default_output_dir_, err) ||
+      !ReadBool(scope, "restat", &restat_, err) ||
+      !ReadPattern(scope, "rspfile", &rspfile_, err) ||
+      !ReadPattern(scope, "rspfile_content", &rspfile_content_, err) ||
+      !ReadLabel(scope, "pool", toolchain->label(), &pool_, err)) {
+    return false;
+  }
+  return true;
+}
+
+std::unique_ptr<Tool> Tool::CreateTool(const std::string& name,
+                                       Scope* scope,
+                                       Toolchain* toolchain,
+                                       Err* err) {
+  std::unique_ptr<Tool> tool = CreateTool(name);
+  if (CTool* c_tool = tool->AsC()) {
+    if (c_tool->InitTool(scope, toolchain, err))
+      return tool;
+    return nullptr;
+  }
+  if (GeneralTool* general_tool = tool->AsGeneral()) {
+    if (general_tool->InitTool(scope, toolchain, err))
+      return tool;
+    return nullptr;
+  }
+  NOTREACHED();
+  return nullptr;
+}
+
+// static
+std::unique_ptr<Tool> Tool::CreateTool(const std::string& name) {
+  if (name == CTool::kCToolCc)
+    return std::make_unique<CTool>(CTool::kCToolCc);
+  else if (name == CTool::kCToolCxx)
+    return std::make_unique<CTool>(CTool::kCToolCxx);
+  else if (name == CTool::kCToolObjC)
+    return std::make_unique<CTool>(CTool::kCToolObjC);
+  else if (name == CTool::kCToolObjCxx)
+    return std::make_unique<CTool>(CTool::kCToolObjCxx);
+  else if (name == CTool::kCToolRc)
+    return std::make_unique<CTool>(CTool::kCToolRc);
+  else if (name == CTool::kCToolAsm)
+    return std::make_unique<CTool>(CTool::kCToolAsm);
+  else if (name == CTool::kCToolAlink)
+    return std::make_unique<CTool>(CTool::kCToolAlink);
+  else if (name == CTool::kCToolSolink)
+    return std::make_unique<CTool>(CTool::kCToolSolink);
+  else if (name == CTool::kCToolSolinkModule)
+    return std::make_unique<CTool>(CTool::kCToolSolinkModule);
+  else if (name == CTool::kCToolLink)
+    return std::make_unique<CTool>(CTool::kCToolLink);
+
+  else if (name == GeneralTool::kGeneralToolAction)
+    return std::make_unique<GeneralTool>(GeneralTool::kGeneralToolAction);
+  else if (name == GeneralTool::kGeneralToolStamp)
+    return std::make_unique<GeneralTool>(GeneralTool::kGeneralToolStamp);
+  else if (name == GeneralTool::kGeneralToolCopy)
+    return std::make_unique<GeneralTool>(GeneralTool::kGeneralToolCopy);
+  else if (name == GeneralTool::kGeneralToolCopyBundleData)
+    return std::make_unique<GeneralTool>(
+        GeneralTool::kGeneralToolCopyBundleData);
+  else if (name == GeneralTool::kGeneralToolCompileXCAssets)
+    return std::make_unique<GeneralTool>(
+        GeneralTool::kGeneralToolCompileXCAssets);
+
+  return nullptr;
+}
+
+// static
+const char* Tool::GetToolTypeForSourceType(SourceFileType type) {
+  switch (type) {
+    case SOURCE_C:
+      return CTool::kCToolCc;
+    case SOURCE_CPP:
+      return CTool::kCToolCxx;
+    case SOURCE_M:
+      return CTool::kCToolObjC;
+    case SOURCE_MM:
+      return CTool::kCToolObjCxx;
+    case SOURCE_ASM:
+    case SOURCE_S:
+      return CTool::kCToolAsm;
+    case SOURCE_RC:
+      return CTool::kCToolRc;
+    case SOURCE_UNKNOWN:
+    case SOURCE_H:
+    case SOURCE_O:
+    case SOURCE_DEF:
+      return kToolNone;
+    default:
+      NOTREACHED();
+      return kToolNone;
+  }
+}
+
+// static
+const char* Tool::GetToolTypeForTargetFinalOutput(const Target* target) {
+  // The contents of this list might be suprising (i.e. stamp tool for copy
+  // rules). See the header for why.
+  // TODO(crbug.com/gn/39): Don't emit stamp files for single-output targets.
+  switch (target->output_type()) {
+    case Target::GROUP:
+      return GeneralTool::kGeneralToolStamp;
+    case Target::EXECUTABLE:
+      return CTool::kCToolLink;
+    case Target::SHARED_LIBRARY:
+      return CTool::kCToolSolink;
+    case Target::LOADABLE_MODULE:
+      return CTool::kCToolSolinkModule;
+    case Target::STATIC_LIBRARY:
+      return CTool::kCToolAlink;
+    case Target::SOURCE_SET:
+      return GeneralTool::kGeneralToolStamp;
+    case Target::ACTION:
+    case Target::ACTION_FOREACH:
+    case Target::BUNDLE_DATA:
+    case Target::CREATE_BUNDLE:
+    case Target::COPY_FILES:
+    case Target::GENERATED_FILE:
+      return GeneralTool::kGeneralToolStamp;
+    default:
+      NOTREACHED();
+      return kToolNone;
+  }
+}
diff --git a/tools/gn/tool.h b/tools/gn/tool.h
index d2d0b7c..4060527 100644
--- a/tools/gn/tool.h
+++ b/tools/gn/tool.h
@@ -11,24 +11,63 @@
 #include "base/macros.h"
 #include "tools/gn/label.h"
 #include "tools/gn/label_ptr.h"
+#include "tools/gn/scope.h"
+#include "tools/gn/source_file_type.h"
 #include "tools/gn/substitution_list.h"
 #include "tools/gn/substitution_pattern.h"
 
 class ParseNode;
 class Pool;
+class Target;
+class Toolchain;
 
+class CTool;
+class GeneralTool;
+
+// To add a new Tool category, create a subclass implementing SetComplete()
+// Add a new category to ToolCategories
+// Add a GetAs* function
 class Tool {
  public:
-  enum DepsFormat { DEPS_GCC = 0, DEPS_MSVC = 1 };
+  static const char* kToolNone;
 
-  enum PrecompiledHeaderType { PCH_NONE = 0, PCH_GCC = 1, PCH_MSVC = 2 };
+  virtual ~Tool();
 
-  Tool();
-  ~Tool();
+  // Manual RTTI and required functions ---------------------------------------
+  //
+  // To implement a new tool category to compile binaries in a different way,
+  // inherit this class, implement the following functions, and add the
+  // appropriate ToolTypes and RTTI getters. New tools will also need to
+  // implement a corresponding class inheriting NinjaBinaryTargetWriter that
+  // does the actual rule writing.
+
+  // Initialize tool from a scope. Child classes should override this function
+  // and call the parent.
+  bool InitTool(Scope* block_scope, Toolchain* toolchain, Err* err);
+
+  // Validate the char* passed to the creation.
+  virtual bool ValidateName(const char* name) const = 0;
+
+  // Called when the toolchain is saving this tool, after everything is filled
+  // in.
+  virtual void SetComplete() = 0;
+
+  // Validate substitutions in this tool.
+  virtual bool ValidateSubstitution(SubstitutionType sub_type) const = 0;
+
+  // Manual RTTI
+  virtual CTool* AsC();
+  virtual const CTool* AsC() const;
+  virtual GeneralTool* AsGeneral();
+  virtual const GeneralTool* AsGeneral() const;
+
+  // Basic information ---------------------------------------------------------
 
   const ParseNode* defined_from() const { return defined_from_; }
   void set_defined_from(const ParseNode* df) { defined_from_ = df; }
 
+  const char* name() const { return name_; }
+
   // Getters/setters ----------------------------------------------------------
   //
   // After the tool has had its attributes set, the caller must call
@@ -66,56 +105,18 @@
     depfile_ = std::move(df);
   }
 
-  DepsFormat depsformat() const { return depsformat_; }
-  void set_depsformat(DepsFormat f) {
-    DCHECK(!complete_);
-    depsformat_ = f;
-  }
-
-  PrecompiledHeaderType precompiled_header_type() const {
-    return precompiled_header_type_;
-  }
-  void set_precompiled_header_type(PrecompiledHeaderType pch_type) {
-    precompiled_header_type_ = pch_type;
-  }
-
   const SubstitutionPattern& description() const { return description_; }
   void set_description(SubstitutionPattern desc) {
     DCHECK(!complete_);
     description_ = std::move(desc);
   }
 
-  const std::string& lib_switch() const { return lib_switch_; }
-  void set_lib_switch(std::string s) {
-    DCHECK(!complete_);
-    lib_switch_ = std::move(s);
-  }
-
-  const std::string& lib_dir_switch() const { return lib_dir_switch_; }
-  void set_lib_dir_switch(std::string s) {
-    DCHECK(!complete_);
-    lib_dir_switch_ = std::move(s);
-  }
-
   const SubstitutionList& outputs() const { return outputs_; }
   void set_outputs(SubstitutionList out) {
     DCHECK(!complete_);
     outputs_ = std::move(out);
   }
 
-  // Should match files in the outputs() if nonempty.
-  const SubstitutionPattern& link_output() const { return link_output_; }
-  void set_link_output(SubstitutionPattern link_out) {
-    DCHECK(!complete_);
-    link_output_ = std::move(link_out);
-  }
-
-  const SubstitutionPattern& depend_output() const { return depend_output_; }
-  void set_depend_output(SubstitutionPattern dep_out) {
-    DCHECK(!complete_);
-    depend_output_ = std::move(dep_out);
-  }
-
   const SubstitutionList& runtime_outputs() const { return runtime_outputs_; }
   void set_runtime_outputs(SubstitutionList run_out) {
     DCHECK(!complete_);
@@ -153,15 +154,8 @@
 
   // Other functions ----------------------------------------------------------
 
-  // Called when the toolchain is saving this tool, after everything is filled
-  // in.
-  void SetComplete();
-
-  // Returns true if this tool has separate outputs for dependency tracking
-  // and linking.
-  bool has_separate_solink_files() const {
-    return !link_output_.empty() || !depend_output_.empty();
-  }
+  // Function for the above override to call to complete the tool.
+  void SetToolComplete();
 
   // Substitutions required by this tool.
   const SubstitutionBits& substitution_bits() const {
@@ -169,23 +163,56 @@
     return substitution_bits_;
   }
 
-  bool OnResolved(Err* err);
+  // Create a tool based on given features.
+  static std::unique_ptr<Tool> CreateTool(const std::string& name);
+  static std::unique_ptr<Tool> CreateTool(const std::string& name,
+                                          Scope* scope,
+                                          Toolchain* toolchain,
+                                          Err* err);
 
- private:
+  static const char* GetToolTypeForSourceType(SourceFileType type);
+  static const char* GetToolTypeForTargetFinalOutput(const Target* target);
+
+ protected:
+  explicit Tool(const char* t);
+
+  // Initialization functions -------------------------------------------------
+  //
+  // Initialization methods used by InitTool(). If successful, will set the
+  // field and return true, otherwise will return false. Must be called before
+  // SetComplete().
+  bool IsPatternInOutputList(const SubstitutionList& output_list,
+                             const SubstitutionPattern& pattern) const;
+  bool ValidateSubstitutionList(const std::vector<SubstitutionType>& list,
+                                const Value* origin,
+                                Err* err) const;
+  bool ValidateOutputs(Err* err) const;
+  bool ReadBool(Scope* scope, const char* var, bool* field, Err* err);
+  bool ReadString(Scope* scope, const char* var, std::string* field, Err* err);
+  bool ReadPattern(Scope* scope,
+                   const char* var,
+                   SubstitutionPattern* field,
+                   Err* err);
+  bool ReadPatternList(Scope* scope,
+                       const char* var,
+                       SubstitutionList* field,
+                       Err* err);
+  bool ReadLabel(Scope* scope,
+                 const char* var,
+                 const Label& current_toolchain,
+                 LabelPtrPair<Pool>* field,
+                 Err* err);
+  bool ReadOutputExtension(Scope* scope, Err* err);
+
   const ParseNode* defined_from_;
+  const char* name_;
 
   SubstitutionPattern command_;
   std::string default_output_extension_;
   SubstitutionPattern default_output_dir_;
   SubstitutionPattern depfile_;
-  DepsFormat depsformat_;
-  PrecompiledHeaderType precompiled_header_type_;
   SubstitutionPattern description_;
-  std::string lib_switch_;
-  std::string lib_dir_switch_;
   SubstitutionList outputs_;
-  SubstitutionPattern link_output_;
-  SubstitutionPattern depend_output_;
   SubstitutionList runtime_outputs_;
   std::string output_prefix_;
   bool restat_;
diff --git a/tools/gn/toolchain.cc b/tools/gn/toolchain.cc
index d9aff43..b718052 100644
--- a/tools/gn/toolchain.cc
+++ b/tools/gn/toolchain.cc
@@ -12,22 +12,6 @@
 #include "tools/gn/target.h"
 #include "tools/gn/value.h"
 
-const char* Toolchain::kToolCc = "cc";
-const char* Toolchain::kToolCxx = "cxx";
-const char* Toolchain::kToolObjC = "objc";
-const char* Toolchain::kToolObjCxx = "objcxx";
-const char* Toolchain::kToolRc = "rc";
-const char* Toolchain::kToolAsm = "asm";
-const char* Toolchain::kToolAlink = "alink";
-const char* Toolchain::kToolSolink = "solink";
-const char* Toolchain::kToolSolinkModule = "solink_module";
-const char* Toolchain::kToolLink = "link";
-const char* Toolchain::kToolStamp = "stamp";
-const char* Toolchain::kToolCopy = "copy";
-const char* Toolchain::kToolCopyBundleData = "copy_bundle_data";
-const char* Toolchain::kToolCompileXCAssets = "compile_xcassets";
-const char* Toolchain::kToolAction = "action";
-
 Toolchain::Toolchain(const Settings* settings,
                      const Label& label,
                      const std::set<SourceFile>& build_dependency_files)
@@ -43,170 +27,90 @@
   return this;
 }
 
-// static
-Toolchain::ToolType Toolchain::ToolNameToType(const base::StringPiece& str) {
-  if (str == kToolCc)
-    return TYPE_CC;
-  if (str == kToolCxx)
-    return TYPE_CXX;
-  if (str == kToolObjC)
-    return TYPE_OBJC;
-  if (str == kToolObjCxx)
-    return TYPE_OBJCXX;
-  if (str == kToolRc)
-    return TYPE_RC;
-  if (str == kToolAsm)
-    return TYPE_ASM;
-  if (str == kToolAlink)
-    return TYPE_ALINK;
-  if (str == kToolSolink)
-    return TYPE_SOLINK;
-  if (str == kToolSolinkModule)
-    return TYPE_SOLINK_MODULE;
-  if (str == kToolLink)
-    return TYPE_LINK;
-  if (str == kToolStamp)
-    return TYPE_STAMP;
-  if (str == kToolCopy)
-    return TYPE_COPY;
-  if (str == kToolCopyBundleData)
-    return TYPE_COPY_BUNDLE_DATA;
-  if (str == kToolCompileXCAssets)
-    return TYPE_COMPILE_XCASSETS;
-  if (str == kToolAction)
-    return TYPE_ACTION;
-  return TYPE_NONE;
-}
-
-// static
-std::string Toolchain::ToolTypeToName(ToolType type) {
-  switch (type) {
-    case TYPE_CC:
-      return kToolCc;
-    case TYPE_CXX:
-      return kToolCxx;
-    case TYPE_OBJC:
-      return kToolObjC;
-    case TYPE_OBJCXX:
-      return kToolObjCxx;
-    case TYPE_RC:
-      return kToolRc;
-    case TYPE_ASM:
-      return kToolAsm;
-    case TYPE_ALINK:
-      return kToolAlink;
-    case TYPE_SOLINK:
-      return kToolSolink;
-    case TYPE_SOLINK_MODULE:
-      return kToolSolinkModule;
-    case TYPE_LINK:
-      return kToolLink;
-    case TYPE_STAMP:
-      return kToolStamp;
-    case TYPE_COPY:
-      return kToolCopy;
-    case TYPE_COPY_BUNDLE_DATA:
-      return kToolCopyBundleData;
-    case TYPE_COMPILE_XCASSETS:
-      return kToolCompileXCAssets;
-    case TYPE_ACTION:
-      return kToolAction;
-    default:
-      NOTREACHED();
-      return std::string();
+Tool* Toolchain::GetTool(const char* name) {
+  DCHECK(name != Tool::kToolNone);
+  auto pair = tools_.find(name);
+  if (pair != tools_.end()) {
+    return pair->second.get();
   }
+  return nullptr;
 }
 
-Tool* Toolchain::GetTool(ToolType type) {
-  DCHECK(type != TYPE_NONE);
-  return tools_[static_cast<size_t>(type)].get();
+const Tool* Toolchain::GetTool(const char* name) const {
+  DCHECK(name != Tool::kToolNone);
+  auto pair = tools_.find(name);
+  if (pair != tools_.end()) {
+    return pair->second.get();
+  }
+  return nullptr;
 }
 
-const Tool* Toolchain::GetTool(ToolType type) const {
-  DCHECK(type != TYPE_NONE);
-  return tools_[static_cast<size_t>(type)].get();
+GeneralTool* Toolchain::GetToolAsGeneral(const char* name) {
+  if (Tool* tool = GetTool(name)) {
+    return tool->AsGeneral();
+  }
+  return nullptr;
 }
 
-void Toolchain::SetTool(ToolType type, std::unique_ptr<Tool> t) {
-  DCHECK(type != TYPE_NONE);
-  DCHECK(!tools_[type].get());
+const GeneralTool* Toolchain::GetToolAsGeneral(const char* name) const {
+  if (const Tool* tool = GetTool(name)) {
+    return tool->AsGeneral();
+  }
+  return nullptr;
+}
+
+CTool* Toolchain::GetToolAsC(const char* name) {
+  if (Tool* tool = GetTool(name)) {
+    return tool->AsC();
+  }
+  return nullptr;
+}
+
+const CTool* Toolchain::GetToolAsC(const char* name) const {
+  if (const Tool* tool = GetTool(name)) {
+    return tool->AsC();
+  }
+  return nullptr;
+}
+
+void Toolchain::SetTool(std::unique_ptr<Tool> t) {
+  DCHECK(t->name() != Tool::kToolNone);
+  DCHECK(tools_.find(t->name()) == tools_.end());
   t->SetComplete();
-  tools_[type] = std::move(t);
+  tools_[t->name()] = std::move(t);
 }
 
 void Toolchain::ToolchainSetupComplete() {
   // Collect required bits from all tools.
   for (const auto& tool : tools_) {
-    if (tool)
-      substitution_bits_.MergeFrom(tool->substitution_bits());
+    substitution_bits_.MergeFrom(tool.second->substitution_bits());
   }
-
   setup_complete_ = true;
 }
 
-// static
-Toolchain::ToolType Toolchain::GetToolTypeForSourceType(SourceFileType type) {
-  switch (type) {
-    case SOURCE_C:
-      return TYPE_CC;
-    case SOURCE_CPP:
-      return TYPE_CXX;
-    case SOURCE_M:
-      return TYPE_OBJC;
-    case SOURCE_MM:
-      return TYPE_OBJCXX;
-    case SOURCE_ASM:
-    case SOURCE_S:
-      return TYPE_ASM;
-    case SOURCE_RC:
-      return TYPE_RC;
-    case SOURCE_UNKNOWN:
-    case SOURCE_H:
-    case SOURCE_O:
-    case SOURCE_DEF:
-      return TYPE_NONE;
-    default:
-      NOTREACHED();
-      return TYPE_NONE;
-  }
+const Tool* Toolchain::GetToolForSourceType(SourceFileType type) const {
+  return GetTool(Tool::GetToolTypeForSourceType(type));
 }
 
-const Tool* Toolchain::GetToolForSourceType(SourceFileType type) {
-  return tools_[GetToolTypeForSourceType(type)].get();
+const CTool* Toolchain::GetToolForSourceTypeAsC(SourceFileType type) const {
+  return GetToolAsC(Tool::GetToolTypeForSourceType(type));
 }
 
-// static
-Toolchain::ToolType Toolchain::GetToolTypeForTargetFinalOutput(
-    const Target* target) {
-  // The contents of this list might be suprising (i.e. stamp tool for copy
-  // rules). See the header for why.
-  // TODO(crbug.com/gn/39): Don't emit stamp files for single-output targets.
-  switch (target->output_type()) {
-    case Target::GROUP:
-      return TYPE_STAMP;
-    case Target::EXECUTABLE:
-      return Toolchain::TYPE_LINK;
-    case Target::SHARED_LIBRARY:
-      return Toolchain::TYPE_SOLINK;
-    case Target::LOADABLE_MODULE:
-      return Toolchain::TYPE_SOLINK_MODULE;
-    case Target::STATIC_LIBRARY:
-      return Toolchain::TYPE_ALINK;
-    case Target::SOURCE_SET:
-      return TYPE_STAMP;
-    case Target::ACTION:
-    case Target::ACTION_FOREACH:
-    case Target::BUNDLE_DATA:
-    case Target::CREATE_BUNDLE:
-    case Target::COPY_FILES:
-    case Target::GENERATED_FILE:
-      return TYPE_STAMP;
-    default:
-      NOTREACHED();
-      return Toolchain::TYPE_NONE;
-  }
+const GeneralTool* Toolchain::GetToolForSourceTypeAsGeneral(
+    SourceFileType type) const {
+  return GetToolAsGeneral(Tool::GetToolTypeForSourceType(type));
 }
 
 const Tool* Toolchain::GetToolForTargetFinalOutput(const Target* target) const {
-  return tools_[GetToolTypeForTargetFinalOutput(target)].get();
+  return GetTool(Tool::GetToolTypeForTargetFinalOutput(target));
+}
+
+const CTool* Toolchain::GetToolForTargetFinalOutputAsC(
+    const Target* target) const {
+  return GetToolAsC(Tool::GetToolTypeForTargetFinalOutput(target));
+}
+
+const GeneralTool* Toolchain::GetToolForTargetFinalOutputAsGeneral(
+    const Target* target) const {
+  return GetToolAsGeneral(Tool::GetToolTypeForTargetFinalOutput(target));
 }
diff --git a/tools/gn/toolchain.h b/tools/gn/toolchain.h
index dd8637c..e36475d 100644
--- a/tools/gn/toolchain.h
+++ b/tools/gn/toolchain.h
@@ -31,43 +31,6 @@
 // be accessed until this Item is resolved.
 class Toolchain : public Item {
  public:
-  enum ToolType {
-    TYPE_NONE = 0,
-    TYPE_CC,
-    TYPE_CXX,
-    TYPE_OBJC,
-    TYPE_OBJCXX,
-    TYPE_RC,
-    TYPE_ASM,
-    TYPE_ALINK,
-    TYPE_SOLINK,
-    TYPE_SOLINK_MODULE,
-    TYPE_LINK,
-    TYPE_STAMP,
-    TYPE_COPY,
-    TYPE_COPY_BUNDLE_DATA,
-    TYPE_COMPILE_XCASSETS,
-    TYPE_ACTION,
-
-    TYPE_NUMTYPES  // Must be last.
-  };
-
-  static const char* kToolCc;
-  static const char* kToolCxx;
-  static const char* kToolObjC;
-  static const char* kToolObjCxx;
-  static const char* kToolRc;
-  static const char* kToolAsm;
-  static const char* kToolAlink;
-  static const char* kToolSolink;
-  static const char* kToolSolinkModule;
-  static const char* kToolLink;
-  static const char* kToolStamp;
-  static const char* kToolCopy;
-  static const char* kToolCopyBundleData;
-  static const char* kToolCompileXCAssets;
-  static const char* kToolAction;
-
   // The Settings of an Item is always the context in which the Item was
   // defined. For a toolchain this is confusing because this is NOT the
   // settings object that applies to the things in the toolchain.
@@ -89,17 +52,19 @@
   Toolchain* AsToolchain() override;
   const Toolchain* AsToolchain() const override;
 
-  // Returns TYPE_NONE on failure.
-  static ToolType ToolNameToType(const base::StringPiece& str);
-  static std::string ToolTypeToName(ToolType type);
-
   // Returns null if the tool hasn't been defined.
-  Tool* GetTool(ToolType type);
-  const Tool* GetTool(ToolType type) const;
+  Tool* GetTool(const char* name);
+  const Tool* GetTool(const char* name) const;
+
+  // Returns null if the tool hasn't been defined or is not the correct type.
+  GeneralTool* GetToolAsGeneral(const char* name);
+  const GeneralTool* GetToolAsGeneral(const char* name) const;
+  CTool* GetToolAsC(const char* name);
+  const CTool* GetToolAsC(const char* name) const;
 
   // Set a tool. When all tools are configured, you should call
   // ToolchainSetupComplete().
-  void SetTool(ToolType type, std::unique_ptr<Tool> t);
+  void SetTool(std::unique_ptr<Tool> t);
 
   // Does final setup on the toolchain once all tools are known.
   void ToolchainSetupComplete();
@@ -123,23 +88,30 @@
   }
 
   // Returns the tool for compiling the given source file type.
-  static ToolType GetToolTypeForSourceType(SourceFileType type);
-  const Tool* GetToolForSourceType(SourceFileType type);
+  const Tool* GetToolForSourceType(SourceFileType type) const;
+  const CTool* GetToolForSourceTypeAsC(SourceFileType type) const;
+  const GeneralTool* GetToolForSourceTypeAsGeneral(SourceFileType type) const;
 
   // Returns the tool that produces the final output for the given target type.
   // This isn't necessarily the tool you would expect. For copy target, this
   // will return the stamp tool instead since the final output of a copy
   // target is to stamp the set of copies done so there is one output.
-  static ToolType GetToolTypeForTargetFinalOutput(const Target* target);
   const Tool* GetToolForTargetFinalOutput(const Target* target) const;
+  const CTool* GetToolForTargetFinalOutputAsC(const Target* target) const;
+  const GeneralTool* GetToolForTargetFinalOutputAsGeneral(
+      const Target* target) const;
 
   const SubstitutionBits& substitution_bits() const {
     DCHECK(setup_complete_);
     return substitution_bits_;
   }
 
+  const std::map<const char*, std::unique_ptr<Tool>>& tools() const {
+    return tools_;
+  }
+
  private:
-  std::unique_ptr<Tool> tools_[TYPE_NUMTYPES];
+  std::map<const char*, std::unique_ptr<Tool>> tools_;
 
   bool setup_complete_ = false;
 
diff --git a/tools/gn/visual_studio_writer.cc b/tools/gn/visual_studio_writer.cc
index 9870fbc..f804b44 100644
--- a/tools/gn/visual_studio_writer.cc
+++ b/tools/gn/visual_studio_writer.cc
@@ -611,8 +611,8 @@
 
     for (const SourceFile& file : target->sources()) {
       const char* compile_type;
-      Toolchain::ToolType tool_type = Toolchain::TYPE_NONE;
-      if (target->GetOutputFilesForSource(file, &tool_type, &tool_outputs)) {
+      const char* tool_name = Tool::kToolNone;
+      if (target->GetOutputFilesForSource(file, &tool_name, &tool_outputs)) {
         compile_type = "CustomBuild";
         std::unique_ptr<XmlElementWriter> build = group->SubElement(
             compile_type, "Include", SourceFileWriter(path_output, file));
diff --git a/tools/gn/visual_studio_writer_unittest.cc b/tools/gn/visual_studio_writer_unittest.cc
index 165bbb4..36b9bcc 100644
--- a/tools/gn/visual_studio_writer_unittest.cc
+++ b/tools/gn/visual_studio_writer_unittest.cc
@@ -175,12 +175,12 @@
           "base", path, MakeGuid(path, "project"), MakeTestPath("/foo"),
           "Win32"));
 
-  std::unique_ptr<Tool> tool = std::make_unique<Tool>();
+  std::unique_ptr<Tool> tool = Tool::CreateTool(CTool::kCToolAlink);
   tool->set_outputs(SubstitutionList::MakeForTest(
       "{{root_out_dir}}/{{target_output_name}}{{output_extension}}", ""));
 
   Toolchain toolchain(setup_.settings(), Label(SourceDir("//tc/"), "tc"));
-  toolchain.SetTool(Toolchain::TYPE_ALINK, std::move(tool));
+  toolchain.SetTool(std::move(tool));
 
   Target target(setup_.settings(), Label(SourceDir("//baz/"), "baz"));
   target.set_output_type(Target::STATIC_LIBRARY);