Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / tools / gn / ninja_binary_target_writer.cc
index f0ef57c..1480f69 100644 (file)
@@ -5,19 +5,25 @@
 #include "tools/gn/ninja_binary_target_writer.h"
 
 #include <set>
+#include <sstream>
 
 #include "base/strings/string_util.h"
 #include "tools/gn/config_values_extractors.h"
+#include "tools/gn/deps_iterator.h"
 #include "tools/gn/err.h"
 #include "tools/gn/escape.h"
+#include "tools/gn/ninja_utils.h"
+#include "tools/gn/settings.h"
 #include "tools/gn/string_utils.h"
+#include "tools/gn/substitution_writer.h"
+#include "tools/gn/target.h"
 
 namespace {
 
 // Returns the proper escape options for writing compiler and linker flags.
 EscapeOptions GetFlagOptions() {
   EscapeOptions opts;
-  opts.mode = ESCAPE_NINJA;
+  opts.mode = ESCAPE_NINJA_COMMAND;
 
   // Some flag strings are actually multiple flags that expect to be just
   // added to the command line. We assume that quoting is done by the
@@ -29,7 +35,7 @@ EscapeOptions GetFlagOptions() {
 
 struct DefineWriter {
   DefineWriter() {
-    options.mode = ESCAPE_SHELL;
+    options.mode = ESCAPE_NINJA_COMMAND;
   }
 
   void operator()(const std::string& s, std::ostream& out) const {
@@ -41,55 +47,30 @@ struct DefineWriter {
 };
 
 struct IncludeWriter {
-  IncludeWriter(PathOutput& path_output,
-                const NinjaHelper& h)
-      : helper(h),
-        path_output_(path_output),
-        old_inhibit_quoting_(path_output.inhibit_quoting()) {
-    // Inhibit quoting since we'll put quotes around the whole thing ourselves.
-    // Since we're writing in NINJA escaping mode, this won't actually do
-    // anything, but I think we may need to change to shell-and-then-ninja
-    // escaping for this in the future.
-    path_output_.set_inhibit_quoting(true);
+  IncludeWriter(PathOutput& path_output) : path_output_(path_output) {
   }
   ~IncludeWriter() {
-    path_output_.set_inhibit_quoting(old_inhibit_quoting_);
   }
 
   void operator()(const SourceDir& d, std::ostream& out) const {
-    out << " \"-I";
-    // It's important not to include the trailing slash on directories or on
-    // Windows it will be a backslash and the compiler might think we're
-    // escaping the quote!
-    path_output_.WriteDir(out, d, PathOutput::DIR_NO_LAST_SLASH);
-    out << "\"";
+    std::ostringstream path_out;
+    path_output_.WriteDir(path_out, d, PathOutput::DIR_NO_LAST_SLASH);
+    const std::string& path = path_out.str();
+    if (path[0] == '"')
+      out << " \"-I" << path.substr(1);
+    else
+      out << " -I" << path;
   }
 
-  const NinjaHelper& helper;
   PathOutput& path_output_;
-  bool old_inhibit_quoting_;  // So we can put the PathOutput back.
 };
 
-Toolchain::ToolType GetToolTypeForTarget(const Target* target) {
-  switch (target->output_type()) {
-    case Target::STATIC_LIBRARY:
-      return Toolchain::TYPE_ALINK;
-    case Target::SHARED_LIBRARY:
-      return Toolchain::TYPE_SOLINK;
-    case Target::EXECUTABLE:
-      return Toolchain::TYPE_LINK;
-    default:
-      return Toolchain::TYPE_NONE;
-  }
-}
-
 }  // namespace
 
 NinjaBinaryTargetWriter::NinjaBinaryTargetWriter(const Target* target,
-                                                 const Toolchain* toolchain,
                                                  std::ostream& out)
-    : NinjaTargetWriter(target, toolchain, out),
-      tool_type_(GetToolTypeForTarget(target)){
+    : NinjaTargetWriter(target, out),
+      tool_(target->toolchain()->GetToolForTargetFinalOutput(target)) {
 }
 
 NinjaBinaryTargetWriter::~NinjaBinaryTargetWriter() {
@@ -108,37 +89,46 @@ void NinjaBinaryTargetWriter::Run() {
 }
 
 void NinjaBinaryTargetWriter::WriteCompilerVars() {
+  const SubstitutionBits& subst = target_->toolchain()->substitution_bits();
+
   // Defines.
-  out_ << "defines =";
-  RecursiveTargetConfigToStream<std::string>(target_, &ConfigValues::defines,
-                                             DefineWriter(), out_);
-  out_ << std::endl;
+  if (subst.used[SUBSTITUTION_DEFINES]) {
+    out_ << kSubstitutionNinjaNames[SUBSTITUTION_DEFINES] << " =";
+    RecursiveTargetConfigToStream<std::string>(
+        target_, &ConfigValues::defines, DefineWriter(), out_);
+    out_ << std::endl;
+  }
 
   // Include directories.
-  out_ << "includes =";
-  RecursiveTargetConfigToStream<SourceDir>(target_, &ConfigValues::include_dirs,
-                                           IncludeWriter(path_output_, helper_),
-                                           out_);
-
-  out_ << std::endl;
+  if (subst.used[SUBSTITUTION_INCLUDE_DIRS]) {
+    out_ << kSubstitutionNinjaNames[SUBSTITUTION_INCLUDE_DIRS] << " =";
+    PathOutput include_path_output(path_output_.current_dir(),
+                                   ESCAPE_NINJA_COMMAND);
+    RecursiveTargetConfigToStream<SourceDir>(
+        target_, &ConfigValues::include_dirs,
+        IncludeWriter(include_path_output), out_);
+    out_ << std::endl;
+  }
 
   // C flags and friends.
   EscapeOptions flag_escape_options = GetFlagOptions();
-#define WRITE_FLAGS(name) \
-    out_ << #name " ="; \
-    RecursiveTargetConfigStringsToStream(target_, &ConfigValues::name, \
-                                         flag_escape_options, out_); \
-    out_ << std::endl;
+#define WRITE_FLAGS(name, subst_enum) \
+    if (subst.used[subst_enum]) { \
+      out_ << kSubstitutionNinjaNames[subst_enum] << " ="; \
+      RecursiveTargetConfigStringsToStream(target_, &ConfigValues::name, \
+                                           flag_escape_options, out_); \
+      out_ << std::endl; \
+    }
 
-  WRITE_FLAGS(cflags)
-  WRITE_FLAGS(cflags_c)
-  WRITE_FLAGS(cflags_cc)
-  WRITE_FLAGS(cflags_objc)
-  WRITE_FLAGS(cflags_objcc)
+  WRITE_FLAGS(cflags, SUBSTITUTION_CFLAGS)
+  WRITE_FLAGS(cflags_c, SUBSTITUTION_CFLAGS_C)
+  WRITE_FLAGS(cflags_cc, SUBSTITUTION_CFLAGS_CC)
+  WRITE_FLAGS(cflags_objc, SUBSTITUTION_CFLAGS_OBJC)
+  WRITE_FLAGS(cflags_objcc, SUBSTITUTION_CFLAGS_OBJCC)
 
 #undef WRITE_FLAGS
 
-  out_ << std::endl;
+  WriteSharedVars(subst);
 }
 
 void NinjaBinaryTargetWriter::WriteSources(
@@ -146,110 +136,148 @@ void NinjaBinaryTargetWriter::WriteSources(
   const Target::FileList& sources = target_->sources();
   object_files->reserve(sources.size());
 
-  std::string implicit_deps = GetSourcesImplicitDeps();
+  OutputFile input_dep =
+      WriteInputDepsStampAndGetDep(std::vector<const Target*>());
 
+  std::string rule_prefix = GetNinjaRulePrefixForToolchain(settings_);
+
+  std::vector<OutputFile> tool_outputs;  // Prevent reallocation in loop.
   for (size_t i = 0; i < sources.size(); i++) {
-    const SourceFile& input_file = sources[i];
-
-    SourceFileType input_file_type = GetSourceFileType(input_file,
-                                                       settings_->target_os());
-    if (input_file_type == SOURCE_UNKNOWN)
-      continue;  // Skip unknown file types.
-    std::string command =
-        helper_.GetRuleForSourceType(settings_, input_file_type);
-    if (command.empty())
-      continue;  // Skip files not needing compilation.
-
-    OutputFile output_file = helper_.GetOutputFileForSource(
-        target_, input_file, input_file_type);
-    object_files->push_back(output_file);
-
-    out_ << "build ";
-    path_output_.WriteFile(out_, output_file);
-    out_ << ": " << command << " ";
-    path_output_.WriteFile(out_, input_file);
-    out_ << implicit_deps << std::endl;
+    Toolchain::ToolType tool_type = Toolchain::TYPE_NONE;
+    if (!GetOutputFilesForSource(target_, sources[i],
+                                 &tool_type, &tool_outputs))
+      continue;  // No output for this source.
+
+    if (tool_type != Toolchain::TYPE_NONE) {
+      out_ << "build";
+      path_output_.WriteFiles(out_, tool_outputs);
+      out_ << ": " << rule_prefix << Toolchain::ToolTypeToName(tool_type);
+      out_ << " ";
+      path_output_.WriteFile(out_, sources[i]);
+      if (!input_dep.value().empty()) {
+        // Write out the input dependencies as an order-only dependency. This
+        // will cause Ninja to make sure the inputs are up-to-date before
+        // compiling this source, but changes in the inputs deps won't cause
+        // the file to be recompiled.
+        //
+        // This is important to prevent changes in unrelated actions that
+        // are upstream of this target from causing everything to be recompiled.
+        //
+        // Why can we get away with this rather than using implicit deps ("|",
+        // which will force rebuilds when the inputs change)?  For source code,
+        // the computed dependencies of all headers will be computed by the
+        // compiler, which will cause source rebuilds if any "real" upstream
+        // dependencies change.
+        //
+        // If a .cc file is generated by an input dependency, Ninja will see
+        // the input to the build rule doesn't exist, and that it is an output
+        // from a previous step, and build the previous step first.  This is a
+        // "real" dependency and doesn't need | or || to express.
+        //
+        // The only case where this rule matters is for the first build where
+        // no .d files exist, and Ninja doesn't know what that source file
+        // depends on. In this case it's sufficient to ensure that the upstream
+        // dependencies are built first. This is exactly what Ninja's order-
+        // only dependencies expresses.
+        out_ << " || ";
+        path_output_.WriteFile(out_, input_dep);
+      }
+      out_ << std::endl;
+    }
+
+    // It's theoretically possible for a compiler to produce more than one
+    // output, but we'll only link to the first output.
+    object_files->push_back(tool_outputs[0]);
   }
   out_ << std::endl;
 }
 
 void NinjaBinaryTargetWriter::WriteLinkerStuff(
     const std::vector<OutputFile>& object_files) {
-  // Manifest file on Windows.
-  // TODO(brettw) this seems not to be necessary for static libs, skip in
-  // that case?
-  OutputFile windows_manifest;
-  if (settings_->IsWin()) {
-    windows_manifest.value().assign(helper_.GetTargetOutputDir(target_));
-    windows_manifest.value().append(target_->label().name());
-    windows_manifest.value().append(".intermediate.manifest");
-    out_ << "manifests = ";
-    path_output_.WriteFile(out_, windows_manifest);
-    out_ << std::endl;
-  }
+  std::vector<OutputFile> output_files;
+  SubstitutionWriter::ApplyListToLinkerAsOutputFile(
+      target_, tool_, tool_->outputs(), &output_files);
 
-  const Toolchain::Tool& tool = toolchain_->GetTool(tool_type_);
-  WriteLinkerFlags(tool, windows_manifest);
-  WriteLibs(tool);
-
-  // The external output file is the one that other libs depend on.
-  OutputFile external_output_file = helper_.GetTargetOutputFile(target_);
-
-  // The internal output file is the "main thing" we think we're making. In
-  // the case of shared libraries, this is the shared library and the external
-  // output file is the import library. In other cases, the internal one and
-  // the external one are the same.
-  OutputFile internal_output_file;
-  if (target_->output_type() == Target::SHARED_LIBRARY) {
-    if (settings_->IsWin()) {
-      internal_output_file.value() =
-          target_->settings()->toolchain_output_subdir().value();
-      internal_output_file.value().append(target_->label().name());
-      internal_output_file.value().append(".dll");
-    } else {
-      internal_output_file = external_output_file;
-    }
-  } else {
-    internal_output_file = external_output_file;
-  }
+  out_ << "build";
+  path_output_.WriteFiles(out_, output_files);
 
-  // In Python see "self.ninja.build(output, command, input,"
-  WriteLinkCommand(external_output_file, internal_output_file, object_files);
+  out_ << ": "
+       << GetNinjaRulePrefixForToolchain(settings_)
+       << Toolchain::ToolTypeToName(
+              target_->toolchain()->GetToolTypeForTargetFinalOutput(target_));
 
-  if (target_->output_type() == Target::SHARED_LIBRARY) {
-    // The shared object name doesn't include a path.
-    out_ << "  soname = ";
-    out_ << FindFilename(&internal_output_file.value());
-    out_ << std::endl;
+  UniqueVector<OutputFile> extra_object_files;
+  UniqueVector<const Target*> linkable_deps;
+  UniqueVector<const Target*> non_linkable_deps;
+  GetDeps(&extra_object_files, &linkable_deps, &non_linkable_deps);
 
-    out_ << "  lib = ";
-    path_output_.WriteFile(out_, internal_output_file);
-    out_ << std::endl;
+  // Object files.
+  for (size_t i = 0; i < object_files.size(); i++) {
+    out_ << " ";
+    path_output_.WriteFile(out_, object_files[i]);
+  }
+  for (size_t i = 0; i < extra_object_files.size(); i++) {
+    out_ << " ";
+    path_output_.WriteFile(out_, extra_object_files[i]);
+  }
 
-    if (settings_->IsWin()) {
-      out_ << "  dll = ";
-      path_output_.WriteFile(out_, internal_output_file);
-      out_ << std::endl;
-    }
+  std::vector<OutputFile> implicit_deps;
+  std::vector<OutputFile> solibs;
 
-    if (settings_->IsWin()) {
-      out_ << "  implibflag = /IMPLIB:";
-      path_output_.WriteFile(out_, external_output_file);
-      out_ << std::endl;
+  for (size_t i = 0; i < linkable_deps.size(); i++) {
+    const Target* cur = linkable_deps[i];
+
+    // All linkable deps should have a link output file.
+    DCHECK(!cur->link_output_file().value().empty())
+        << "No link output file for "
+        << target_->label().GetUserVisibleName(false);
+
+    if (cur->dependency_output_file().value() !=
+        cur->link_output_file().value()) {
+      // This is a shared library with separate link and deps files. Save for
+      // later.
+      implicit_deps.push_back(cur->dependency_output_file());
+      solibs.push_back(cur->link_output_file());
+    } else {
+      // Normal case, just link to this target.
+      out_ << " ";
+      path_output_.WriteFile(out_, cur->link_output_file());
     }
+  }
 
-    // TODO(brettw) postbuild steps.
-    if (settings_->IsMac())
-      out_ << "  postbuilds = $ && (export BUILT_PRODUCTS_DIR=/Users/brettw/prj/src/out/gn; export CONFIGURATION=Debug; export DYLIB_INSTALL_NAME_BASE=@rpath; export EXECUTABLE_NAME=libbase.dylib; export EXECUTABLE_PATH=libbase.dylib; export FULL_PRODUCT_NAME=libbase.dylib; export LD_DYLIB_INSTALL_NAME=@rpath/libbase.dylib; export MACH_O_TYPE=mh_dylib; export PRODUCT_NAME=base; export PRODUCT_TYPE=com.apple.product-type.library.dynamic; export SDKROOT=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk; export SRCROOT=/Users/brettw/prj/src/out/gn/../../base; export SOURCE_ROOT=\"$${SRCROOT}\"; export TARGET_BUILD_DIR=/Users/brettw/prj/src/out/gn; export TEMP_DIR=\"$${TMPDIR}\"; (cd ../../base && ../build/mac/strip_from_xcode); G=$$?; ((exit $$G) || rm -rf libbase.dylib) && exit $$G)";
+  // Append implicit dependencies collected above.
+  if (!implicit_deps.empty()) {
+    out_ << " |";
+    path_output_.WriteFiles(out_, implicit_deps);
   }
 
+  // Append data dependencies as order-only dependencies.
+  //
+  // This will include data dependencies and input dependencies (like when
+  // this target depends on an action). Having the data dependencies in this
+  // list ensures that the data is available at runtime when the user builds
+  // this target.
+  //
+  // The action dependencies are not strictly necessary in this case. They
+  // should also have been collected via the input deps stamp that each source
+  // file has for an order-only dependency, and since this target depends on
+  // the sources, there is already an implicit order-only dependency. However,
+  // it's extra work to separate these out and there's no disadvantage to
+  // listing them again.
+  WriteOrderOnlyDependencies(non_linkable_deps);
+
+  // End of the link "build" line.
   out_ << std::endl;
+
+  // These go in the inner scope of the link line.
+  WriteLinkerFlags();
+  WriteLibs();
+  WriteOutputExtension();
+  WriteSolibs(solibs);
 }
 
-void NinjaBinaryTargetWriter::WriteLinkerFlags(
-    const Toolchain::Tool& tool,
-    const OutputFile& windows_manifest) {
-  out_ << "ldflags =";
+void NinjaBinaryTargetWriter::WriteLinkerFlags() {
+  out_ << "  ldflags =";
 
   // First the ldflags from the target and its config.
   EscapeOptions flag_options = GetFlagOptions();
@@ -262,30 +290,23 @@ void NinjaBinaryTargetWriter::WriteLinkerFlags(
   if (!all_lib_dirs.empty()) {
     // Since we're passing these on the command line to the linker and not
     // to Ninja, we need to do shell escaping.
-    PathOutput lib_path_output(path_output_.current_dir(), ESCAPE_NINJA_SHELL,
-                               true);
+    PathOutput lib_path_output(path_output_.current_dir(),
+                               ESCAPE_NINJA_COMMAND);
     for (size_t i = 0; i < all_lib_dirs.size(); i++) {
-      out_ << " " << tool.lib_dir_prefix;
+      out_ << " " << tool_->lib_dir_switch();
       lib_path_output.WriteDir(out_, all_lib_dirs[i],
                                PathOutput::DIR_NO_LAST_SLASH);
     }
   }
-
-  // Append manifest flag on Windows to reference our file.
-  // HACK ERASEME BRETTW FIXME
-  if (settings_->IsWin()) {
-    out_ << " /MANIFEST /ManifestFile:";
-    path_output_.WriteFile(out_, windows_manifest);
-  }
   out_ << std::endl;
 }
 
-void NinjaBinaryTargetWriter::WriteLibs(const Toolchain::Tool& tool) {
-  out_ << "libs =";
+void NinjaBinaryTargetWriter::WriteLibs() {
+  out_ << "  libs =";
 
   // Libraries that have been recursively pushed through the dependency tree.
   EscapeOptions lib_escape_opts;
-  lib_escape_opts.mode = ESCAPE_NINJA_SHELL;
+  lib_escape_opts.mode = ESCAPE_NINJA_COMMAND;
   const OrderedSet<std::string> all_libs = target_->all_libs();
   const std::string framework_ending(".framework");
   for (size_t i = 0; i < all_libs.size(); i++) {
@@ -297,52 +318,33 @@ void NinjaBinaryTargetWriter::WriteLibs(const Toolchain::Tool& tool) {
           all_libs[i].substr(0, all_libs[i].size() - framework_ending.size()),
           lib_escape_opts);
     } else {
-      out_ << " " << tool.lib_prefix;
+      out_ << " " << tool_->lib_switch();
       EscapeStringToStream(out_, all_libs[i], lib_escape_opts);
     }
   }
   out_ << std::endl;
 }
 
-void NinjaBinaryTargetWriter::WriteLinkCommand(
-    const OutputFile& external_output_file,
-    const OutputFile& internal_output_file,
-    const std::vector<OutputFile>& object_files) {
-  out_ << "build ";
-  path_output_.WriteFile(out_, internal_output_file);
-  if (external_output_file != internal_output_file) {
-    out_ << " ";
-    path_output_.WriteFile(out_, external_output_file);
-  }
-  out_ << ": "
-       << helper_.GetRulePrefix(target_->settings())
-       << Toolchain::ToolTypeToName(tool_type_);
-
-  std::set<OutputFile> extra_object_files;
-  std::vector<const Target*> linkable_deps;
-  std::vector<const Target*> non_linkable_deps;
-  GetDeps(&extra_object_files, &linkable_deps, &non_linkable_deps);
-
-  // Object files.
-  for (size_t i = 0; i < object_files.size(); i++) {
-    out_ << " ";
-    path_output_.WriteFile(out_, object_files[i]);
-  }
-  for (std::set<OutputFile>::iterator i = extra_object_files.begin();
-       i != extra_object_files.end(); ++i) {
-    out_ << " ";
-    path_output_.WriteFile(out_, *i);
-  }
-
-  // Libs.
-  for (size_t i = 0; i < linkable_deps.size(); i++) {
-    out_ << " ";
-    path_output_.WriteFile(out_, helper_.GetTargetOutputFile(linkable_deps[i]));
+void NinjaBinaryTargetWriter::WriteOutputExtension() {
+  out_ << "  output_extension = ";
+  if (target_->output_extension().empty()) {
+    // Use the default from the tool.
+    out_ << tool_->default_output_extension();
+  } else {
+    // Use the one specified in the target. Note that the one in the target
+    // does not include the leading dot, so add that.
+    out_ << "." << target_->output_extension();
   }
+  out_ << std::endl;
+}
 
-  // Append data dependencies as implicit dependencies.
-  WriteImplicitDependencies(non_linkable_deps);
+void NinjaBinaryTargetWriter::WriteSolibs(
+    const std::vector<OutputFile>& solibs) {
+  if (solibs.empty())
+    return;
 
+  out_ << "  solibs =";
+  path_output_.WriteFiles(out_, solibs);
   out_ << std::endl;
 }
 
@@ -352,15 +354,9 @@ void NinjaBinaryTargetWriter::WriteSourceSetStamp(
   // depend on this will reference the object files directly. However, writing
   // this rule allows the user to type the name of the target and get a build
   // which can be convenient for development.
-  out_ << "build ";
-  path_output_.WriteFile(out_, helper_.GetTargetOutputFile(target_));
-  out_ << ": "
-       << helper_.GetRulePrefix(target_->settings())
-       << "stamp";
-
-  std::set<OutputFile> extra_object_files;
-  std::vector<const Target*> linkable_deps;
-  std::vector<const Target*> non_linkable_deps;
+  UniqueVector<OutputFile> extra_object_files;
+  UniqueVector<const Target*> linkable_deps;
+  UniqueVector<const Target*> non_linkable_deps;
   GetDeps(&extra_object_files, &linkable_deps, &non_linkable_deps);
 
   // The classifier should never put extra object files in a source set:
@@ -368,75 +364,71 @@ void NinjaBinaryTargetWriter::WriteSourceSetStamp(
   // deps instead.
   DCHECK(extra_object_files.empty());
 
-  for (size_t i = 0; i < object_files.size(); i++) {
-    out_ << " ";
-    path_output_.WriteFile(out_, object_files[i]);
-  }
-
-  // Append data dependencies as implicit dependencies.
-  WriteImplicitDependencies(non_linkable_deps);
+  std::vector<OutputFile> order_only_deps;
+  for (size_t i = 0; i < non_linkable_deps.size(); i++)
+    order_only_deps.push_back(non_linkable_deps[i]->dependency_output_file());
 
-  out_ << std::endl;
+  WriteStampForTarget(object_files, order_only_deps);
 }
 
 void NinjaBinaryTargetWriter::GetDeps(
-    std::set<OutputFile>* extra_object_files,
-    std::vector<const Target*>* linkable_deps,
-    std::vector<const Target*>* non_linkable_deps) const {
-  const LabelTargetVector& deps = target_->deps();
-  const std::set<const Target*>& inherited = target_->inherited_libraries();
-
-  // Normal deps.
-  for (size_t i = 0; i < deps.size(); i++) {
-    if (inherited.find(deps[i].ptr) != inherited.end())
-      continue;  // Don't add dupes.
-    ClassifyDependency(deps[i].ptr, extra_object_files,
+    UniqueVector<OutputFile>* extra_object_files,
+    UniqueVector<const Target*>* linkable_deps,
+    UniqueVector<const Target*>* non_linkable_deps) const {
+  const UniqueVector<const Target*>& inherited =
+      target_->inherited_libraries();
+
+  // Normal public/private deps.
+  for (DepsIterator iter(target_, DepsIterator::LINKED_ONLY); !iter.done();
+       iter.Advance()) {
+    ClassifyDependency(iter.target(), extra_object_files,
                        linkable_deps, non_linkable_deps);
   }
 
   // Inherited libraries.
-  for (std::set<const Target*>::const_iterator i = inherited.begin();
-       i != inherited.end(); ++i) {
-    ClassifyDependency(*i, extra_object_files,
+  for (size_t i = 0; i < inherited.size(); i++) {
+    ClassifyDependency(inherited[i], extra_object_files,
                        linkable_deps, non_linkable_deps);
   }
 
   // Data deps.
-  const LabelTargetVector& datadeps = target_->datadeps();
-  for (size_t i = 0; i < datadeps.size(); i++)
-    non_linkable_deps->push_back(datadeps[i].ptr);
+  const LabelTargetVector& data_deps = target_->data_deps();
+  for (size_t i = 0; i < data_deps.size(); i++)
+    non_linkable_deps->push_back(data_deps[i].ptr);
 }
 
 void NinjaBinaryTargetWriter::ClassifyDependency(
     const Target* dep,
-    std::set<OutputFile>* extra_object_files,
-    std::vector<const Target*>* linkable_deps,
-    std::vector<const Target*>* non_linkable_deps) const {
-  // Only these types of outputs have libraries linked into them. Child deps of
-  // static libraries get pushed up the dependency tree until one of these is
-  // reached, and source sets don't link at all.
-  bool can_link_libs =
-      (target_->output_type() == Target::EXECUTABLE ||
-       target_->output_type() == Target::SHARED_LIBRARY);
+    UniqueVector<OutputFile>* extra_object_files,
+    UniqueVector<const Target*>* linkable_deps,
+    UniqueVector<const Target*>* non_linkable_deps) const {
+  // Only the following types of outputs have libraries linked into them:
+  //  EXECUTABLE
+  //  SHARED_LIBRARY
+  //  _complete_ STATIC_LIBRARY
+  //
+  // Child deps of intermediate static libraries get pushed up the
+  // dependency tree until one of these is reached, and source sets
+  // don't link at all.
+  bool can_link_libs = target_->IsFinal();
 
   if (dep->output_type() == Target::SOURCE_SET) {
-    if (target_->output_type() == Target::SOURCE_SET) {
-      // When a source set depends on another source set, add it as a data
-      // dependency so if the user says "ninja second_source_set" it will
-      // also compile the first (what you would expect) even though we'll
-      // never do anything with the first one's files.
-      non_linkable_deps->push_back(dep);
-    } else {
-      // Linking in a source set, copy its object files.
+    // Source sets have their object files linked into final targets
+    // (shared libraries, executables, and complete static
+    // libraries). Intermediate static libraries and other source sets
+    // just forward the dependency, otherwise the files in the source
+    // set can easily get linked more than once which will cause
+    // multiple definition errors.
+    if (can_link_libs) {
+      // Linking in a source set to an executable, shared library, or
+      // complete static library, so copy its object files.
+      std::vector<OutputFile> tool_outputs;  // Prevent allocation in loop.
       for (size_t i = 0; i < dep->sources().size(); i++) {
-        SourceFileType input_file_type = GetSourceFileType(
-            dep->sources()[i], dep->settings()->target_os());
-        if (input_file_type != SOURCE_UNKNOWN &&
-            input_file_type != SOURCE_H) {
-          // Note we need to specify the target as the source_set target
-          // itself, since this is used to prefix the object file name.
-          extra_object_files->insert(helper_.GetOutputFileForSource(
-              dep, dep->sources()[i], input_file_type));
+        Toolchain::ToolType tool_type = Toolchain::TYPE_NONE;
+        if (GetOutputFilesForSource(dep, dep->sources()[i], &tool_type,
+                                    &tool_outputs)) {
+          // Only link the first output if there are more than one.
+          extra_object_files->push_back(tool_outputs[0]);
         }
       }
     }
@@ -447,8 +439,8 @@ void NinjaBinaryTargetWriter::ClassifyDependency(
   }
 }
 
-void NinjaBinaryTargetWriter::WriteImplicitDependencies(
-    const std::vector<const Target*>& non_linkable_deps) {
+void NinjaBinaryTargetWriter::WriteOrderOnlyDependencies(
+    const UniqueVector<const Target*>& non_linkable_deps) {
   const std::vector<SourceFile>& data = target_->data();
   if (!non_linkable_deps.empty() || !data.empty()) {
     out_ << " ||";
@@ -456,15 +448,39 @@ void NinjaBinaryTargetWriter::WriteImplicitDependencies(
     // Non-linkable targets.
     for (size_t i = 0; i < non_linkable_deps.size(); i++) {
       out_ << " ";
-      path_output_.WriteFile(out_,
-                             helper_.GetTargetOutputFile(non_linkable_deps[i]));
+      path_output_.WriteFile(
+          out_, non_linkable_deps[i]->dependency_output_file());
     }
+  }
+}
 
-    // Data files.
-    const std::vector<SourceFile>& data = target_->data();
-    for (size_t i = 0; i < data.size(); i++) {
-      out_ << " ";
-      path_output_.WriteFile(out_, data[i]);
-    }
+bool NinjaBinaryTargetWriter::GetOutputFilesForSource(
+    const Target* target,
+    const SourceFile& source,
+    Toolchain::ToolType* computed_tool_type,
+    std::vector<OutputFile>* outputs) const {
+  outputs->clear();
+  *computed_tool_type = Toolchain::TYPE_NONE;
+
+  SourceFileType file_type = GetSourceFileType(source);
+  if (file_type == SOURCE_UNKNOWN)
+    return false;
+  if (file_type == SOURCE_O) {
+    // Object files just get passed to the output and not compiled.
+    outputs->push_back(OutputFile(settings_->build_settings(), source));
+    return true;
   }
+
+  *computed_tool_type =
+      target->toolchain()->GetToolTypeForSourceType(file_type);
+  if (*computed_tool_type == Toolchain::TYPE_NONE)
+    return false;  // No tool for this file (it's a header file or something).
+  const Tool* tool = target->toolchain()->GetTool(*computed_tool_type);
+  if (!tool)
+    return false;  // Tool does not apply for this toolchain.file.
+
+  // Figure out what output(s) this compiler produces.
+  SubstitutionWriter::ApplyListToCompilerAsOutputFile(
+      target, source, tool->outputs(), outputs);
+  return !outputs->empty();
 }