[swift] Remove problematic use of "stamp" tool

Recent versions of ninja supports listing multiple outputs for the
same build command, so remove a problematic "stamp" tool (caused
incremental build issues).

Previously, the ninja file for a swift source_set would look like:

    build output0: swift input0 input1 ...
    build output1 output2 ...: stamp output0

With this change, it becomes the following:

    build output0 output1 output2 ...: swift input0 input1 ...

This removes unnecessary target, and ensure that building any of
output$n will result in launching the swift command instead of
creating empty files if the output0 already exists.

Bug: none
Change-Id: Ibbe2433a87de0ca86a628d6120c9e004780818c3
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/16800
Reviewed-by: David Turner <digit@google.com>
Commit-Queue: Sylvain Defresne <sdefresne@chromium.org>
diff --git a/src/gn/ninja_c_binary_target_writer.cc b/src/gn/ninja_c_binary_target_writer.cc
index eada97d..aa5345c 100644
--- a/src/gn/ninja_c_binary_target_writer.cc
+++ b/src/gn/ninja_c_binary_target_writer.cc
@@ -510,76 +510,44 @@
   // If the target contains .swift source files, they needs to be compiled as
   // a single unit but still can produce more than one object file (if the
   // whole module optimization is disabled).
-  if (target_->source_types_used().SwiftSourceUsed()) {
-    const Tool* tool =
-        target_->toolchain()->GetToolForSourceType(SourceFile::SOURCE_SWIFT);
+  const Tool* tool =
+      target_->toolchain()->GetToolForSourceType(SourceFile::SOURCE_SWIFT);
+  DCHECK(!tool->outputs().list().empty());
 
-    const OutputFile swiftmodule_output_file =
-        target_->swift_values().module_output_file();
+  std::vector<OutputFile> outputs;
+  SubstitutionWriter::ApplyListToLinkerAsOutputFile(target_, tool,
+                                                    tool->outputs(), &outputs);
 
-    std::vector<OutputFile> additional_outputs;
-    SubstitutionWriter::ApplyListToLinkerAsOutputFile(
-        target_, tool, tool->outputs(), &additional_outputs);
+  // Expand partial outputs too (if any).
+  for (const SourceFile& source : target_->sources()) {
+    if (!source.IsSwiftType())
+      continue;
 
-    additional_outputs.erase(
-        std::remove(additional_outputs.begin(), additional_outputs.end(),
-                    swiftmodule_output_file),
-        additional_outputs.end());
+    SubstitutionWriter::ApplyListToCompilerAsOutputFile(
+        target_, source, tool->partial_outputs(), &outputs);
+  }
 
-    for (const OutputFile& output : additional_outputs) {
-      const SourceFile output_as_source =
-          output.AsSourceFile(target_->settings()->build_settings());
+  for (const OutputFile& output : outputs) {
+    const SourceFile output_as_source =
+        output.AsSourceFile(target_->settings()->build_settings());
 
-      if (output_as_source.IsObjectType()) {
-        object_files->push_back(output);
-      }
-    }
-
-    const SubstitutionList& partial_outputs_subst = tool->partial_outputs();
-    if (!partial_outputs_subst.list().empty()) {
-      // Avoid re-allocation during loop.
-      std::vector<OutputFile> partial_outputs;
-      for (const auto& source : target_->sources()) {
-        if (!source.IsSwiftType())
-          continue;
-
-        partial_outputs.resize(0);
-        SubstitutionWriter::ApplyListToCompilerAsOutputFile(
-            target_, source, partial_outputs_subst, &partial_outputs);
-
-        for (const OutputFile& output : partial_outputs) {
-          additional_outputs.push_back(output);
-          SourceFile output_as_source =
-              output.AsSourceFile(target_->settings()->build_settings());
-          if (output_as_source.IsObjectType()) {
-            object_files->push_back(output);
-          }
-        }
-      }
-    }
-
-    UniqueVector<OutputFile> swift_order_only_deps;
-    swift_order_only_deps.reserve(order_only_deps.size());
-    swift_order_only_deps.Append(order_only_deps.begin(),
-                                 order_only_deps.end());
-
-    for (const Target* swiftmodule :
-         resolved().GetSwiftModuleDependencies(target_))
-      swift_order_only_deps.push_back(swiftmodule->dependency_output_file());
-
-    WriteCompilerBuildLine(target_->sources(), input_deps,
-                           swift_order_only_deps.vector(), tool->name(),
-                           {swiftmodule_output_file}, false);
-
-    if (!additional_outputs.empty()) {
-      out_ << std::endl;
-      WriteCompilerBuildLine(
-          {swiftmodule_output_file.AsSourceFile(settings_->build_settings())},
-          input_deps, swift_order_only_deps.vector(),
-          GeneralTool::kGeneralToolStamp, additional_outputs, false);
+    if (output_as_source.IsObjectType()) {
+      object_files->push_back(output);
     }
   }
 
+  UniqueVector<OutputFile> swift_order_only_deps;
+  swift_order_only_deps.reserve(order_only_deps.size());
+  swift_order_only_deps.Append(order_only_deps.begin(), order_only_deps.end());
+
+  for (const Target* swiftmodule :
+       resolved().GetSwiftModuleDependencies(target_))
+    swift_order_only_deps.push_back(swiftmodule->dependency_output_file());
+
+  WriteCompilerBuildLine(target_->sources(), input_deps,
+                         swift_order_only_deps.vector(), tool->name(), outputs,
+                         false);
+
   out_ << std::endl;
 }
 
diff --git a/src/gn/ninja_c_binary_target_writer_unittest.cc b/src/gn/ninja_c_binary_target_writer_unittest.cc
index b4d49d9..085914a 100644
--- a/src/gn/ninja_c_binary_target_writer_unittest.cc
+++ b/src/gn/ninja_c_binary_target_writer_unittest.cc
@@ -2298,11 +2298,9 @@
         "target_out_dir = obj/foo\n"
         "target_output_name = foo\n"
         "\n"
-        "build obj/foo/Foo.swiftmodule: swift"
+        "build obj/foo/Foo.swiftmodule obj/foo/file1.o obj/foo/file2.o: swift"
         " ../../foo/file1.swift ../../foo/file2.swift\n"
         "\n"
-        "build obj/foo/file1.o obj/foo/file2.o: stamp obj/foo/Foo.swiftmodule\n"
-        "\n"
         "build obj/foo/foo.stamp: stamp"
         " obj/foo/file1.o obj/foo/file2.o\n";
 
@@ -2335,10 +2333,7 @@
         "target_out_dir = obj/bar\n"
         "target_output_name = bar\n"
         "\n"
-        "build obj/bar/Bar.swiftmodule: swift ../../bar/bar.swift"
-        " || obj/foo/foo.stamp\n"
-        "\n"
-        "build obj/bar/bar.o: stamp obj/bar/Bar.swiftmodule"
+        "build obj/bar/Bar.swiftmodule obj/bar/bar.o: swift ../../bar/bar.swift"
         " || obj/foo/foo.stamp\n"
         "\n"
         "build obj/bar/bar.stamp: stamp obj/bar/bar.o "
@@ -2381,10 +2376,7 @@
         "target_out_dir = obj/bar\n"
         "target_output_name = bar\n"
         "\n"
-        "build obj/bar/Bar.swiftmodule: swift ../../bar/bar.swift"
-        " || obj/bar/group.stamp obj/foo/foo.stamp\n"
-        "\n"
-        "build obj/bar/bar.o: stamp obj/bar/Bar.swiftmodule"
+        "build obj/bar/Bar.swiftmodule obj/bar/bar.o: swift ../../bar/bar.swift"
         " || obj/bar/group.stamp obj/foo/foo.stamp\n"
         "\n"
         "build obj/bar/bar.stamp: stamp obj/bar/bar.o "
diff --git a/src/gn/swift_values.cc b/src/gn/swift_values.cc
index 6065478..6faa9af 100644
--- a/src/gn/swift_values.cc
+++ b/src/gn/swift_values.cc
@@ -26,24 +26,39 @@
 
   const Tool* tool =
       target->toolchain()->GetToolForSourceType(SourceFile::SOURCE_SWIFT);
-  CHECK(tool->outputs().list().size() >= 1);
 
-  OutputFile module_output_file =
-      SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
-          target, tool, tool->outputs().list()[0]);
+  std::vector<OutputFile> outputs;
+  SubstitutionWriter::ApplyListToLinkerAsOutputFile(target, tool,
+                                                    tool->outputs(), &outputs);
 
-  const SourceFile module_output_file_as_source =
-      module_output_file.AsSourceFile(target->settings()->build_settings());
-  if (!module_output_file_as_source.IsSwiftModuleType()) {
-    *err = Err(tool->defined_from(), "Incorrect outputs for tool",
-               "The first output of tool " + std::string(tool->name()) +
-                   " must be a .swiftmodule file.");
-    return false;
+  bool swiftmodule_output_found = false;
+  SwiftValues& swift_values = target->swift_values();
+  for (const OutputFile& output : outputs) {
+    const SourceFile output_as_source =
+        output.AsSourceFile(target->settings()->build_settings());
+    if (!output_as_source.IsSwiftModuleType()) {
+      continue;
+    }
+
+    if (swiftmodule_output_found) {
+      *err = Err(tool->defined_from(), "Incorrect outputs for tool",
+                 "The outputs of tool " + std::string(tool->name()) +
+                     " must list exactly one .swiftmodule file");
+      return false;
+    }
+
+    swift_values.module_output_file_ = output;
+    swift_values.module_output_dir_ = output_as_source.GetDir();
+
+    swiftmodule_output_found = true;
   }
 
-  SwiftValues& swift_values = target->swift_values();
-  swift_values.module_output_file_ = std::move(module_output_file);
-  swift_values.module_output_dir_ = module_output_file_as_source.GetDir();
+  if (!swiftmodule_output_found) {
+    *err = Err(tool->defined_from(), "Incorrect outputs for tool",
+               "The outputs of tool " + std::string(tool->name()) +
+                   " must list exactly one .swiftmodule file");
+    return false;
+  }
 
   return true;
 }