[swift] Update `gn check ...` to consider the generated header

The compilation of a Swift module generates an Objective-C header
to allow the interop between Swift and Objective-C. Update checker
to consider this generated header as part of the source_set. This
ensure that missing deps on swift_source_set(...) targets will be
reported by `gn check ...` in Chromium.

  This would prevent regressions caused by missing deps such
  as found and fixed by https://crrev.com/c/5348787.

Fixed: 361
Change-Id: I5394097030e0b08d5ebda58066b16fe2a5bddebf
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/16840
Reviewed-by: David Turner <digit@google.com>
Commit-Queue: Sylvain Defresne <sdefresne@chromium.org>
Reviewed-by: Takuto Ikuta <tikuta@google.com>
diff --git a/src/gn/header_checker.cc b/src/gn/header_checker.cc
index 1a93417..197084b 100644
--- a/src/gn/header_checker.cc
+++ b/src/gn/header_checker.cc
@@ -17,6 +17,7 @@
 #include "gn/err.h"
 #include "gn/filesystem_utils.h"
 #include "gn/scheduler.h"
+#include "gn/swift_values.h"
 #include "gn/target.h"
 #include "gn/trace.h"
 #include "util/worker_pool.h"
@@ -214,16 +215,6 @@
     files_to_public[source].is_public = default_public;
   }
 
-  // If the target includes some .swift files, it may also use a header file
-  // to provide bridging Objective-C code. This header needs to be considered
-  // as a source file with the default visibility.
-  if (target->has_swift_values()) {
-    const SourceFile& bridge_header = target->swift_values().bridge_header();
-    if (!bridge_header.is_null()) {
-      files_to_public[bridge_header].is_public = default_public;
-    }
-  }
-
   // Add in the public files, forcing them to public. This may overwrite some
   // entries, and it may add new ones.
   if (default_public)  // List only used when default is not public.
@@ -232,6 +223,27 @@
     files_to_public[source].is_public = true;
   }
 
+  // If target generates a swiftmodule, then
+  //  - it may use a bridge header which has default visibility
+  //  - it may generate public header which must be considered public
+  if (target->builds_swift_module()) {
+    const SourceFile& bridge_header = target->swift_values().bridge_header();
+    if (!bridge_header.is_null()) {
+      files_to_public[bridge_header].is_public = default_public;
+    }
+
+    std::vector<SourceFile> outputs;
+    target->swift_values().GetOutputsAsSourceFiles(target, &outputs);
+
+    for (const SourceFile& output : outputs) {
+      if (output.GetType() == SourceFile::SOURCE_H) {
+        PublicGeneratedPair* pair = &files_to_public[output];
+        pair->is_public = true;
+        pair->is_generated = true;
+      }
+    }
+  }
+
   // Add in outputs from actions. These are treated as public (since if other
   // targets can't use them, then there wouldn't be any point in outputting).
   std::vector<SourceFile> outputs;
diff --git a/src/gn/header_checker.h b/src/gn/header_checker.h
index 55c9259..fd053dd 100644
--- a/src/gn/header_checker.h
+++ b/src/gn/header_checker.h
@@ -74,11 +74,10 @@
   FRIEND_TEST_ALL_PREFIXES(HeaderCheckerTest, CheckInclude);
   FRIEND_TEST_ALL_PREFIXES(HeaderCheckerTest, PublicFirst);
   FRIEND_TEST_ALL_PREFIXES(HeaderCheckerTest, CheckIncludeAllowCircular);
+  FRIEND_TEST_ALL_PREFIXES(HeaderCheckerTest, CheckIncludeSwiftModule);
   FRIEND_TEST_ALL_PREFIXES(HeaderCheckerTest, SourceFileForInclude);
   FRIEND_TEST_ALL_PREFIXES(HeaderCheckerTest,
                            SourceFileForInclude_FileNotFound);
-  FRIEND_TEST_ALL_PREFIXES(HeaderCheckerTest,
-                           SourceFileForInclude_SwiftBridgeHeader);
   FRIEND_TEST_ALL_PREFIXES(HeaderCheckerTest, Friend);
 
   ~HeaderChecker();
diff --git a/src/gn/header_checker_unittest.cc b/src/gn/header_checker_unittest.cc
index 2a168fd..00a82d3 100644
--- a/src/gn/header_checker_unittest.cc
+++ b/src/gn/header_checker_unittest.cc
@@ -314,6 +314,54 @@
   EXPECT_EQ(errors.size(), 0);
 }
 
+// Check that CheckInclude() support swift targets.
+TEST_F(HeaderCheckerTest, CheckIncludeSwiftModule) {
+  // A target S that build a swift module.
+  Target s(setup_.settings(), Label(SourceDir("//s"), "s"));
+  s.set_output_type(Target::SOURCE_SET);
+
+  Err err;
+  s.SetToolchain(setup_.toolchain(), &err);
+  ASSERT_FALSE(err.has_error());
+
+  const SourceFile bridge_header("//bridge.h");
+  const SourceFile generated_header("//out/Debug/gen/s/s.h");
+
+  // S contains Swift sources and has bridge header set.
+  s.swift_values().module_name() = "s";
+  s.swift_values().bridge_header() = SourceFile("//bridge.h");
+  s.sources().push_back(SourceFile("//some_file.swift"));
+  s.source_types_used().Set(SourceFile::SOURCE_SWIFT);
+  s.visibility().SetPublic();
+
+  s.OnResolved(&err);
+  ASSERT_FALSE(err.has_error());
+  targets_.push_back(&s);
+
+  auto checker = CreateChecker();
+
+  InputFile input_file(SourceFile("//some_file.cc"));
+  input_file.SetContents(std::string());
+  LocationRange range;  // Dummy value.
+
+  std::vector<Err> errors;
+  std::set<std::pair<const Target*, const Target*>> no_dependency_cache;
+
+  // Check that unrelated target D cannot include header generated by S.
+  errors.clear();
+  no_dependency_cache.clear();
+  checker->CheckInclude(&d_, input_file, generated_header, range,
+                        &no_dependency_cache, &errors);
+  EXPECT_GT(errors.size(), 0);
+
+  // Check that unrelated target D cannot include S's bridge header.
+  errors.clear();
+  no_dependency_cache.clear();
+  checker->CheckInclude(&d_, input_file, bridge_header, range,
+                        &no_dependency_cache, &errors);
+  EXPECT_GT(errors.size(), 0);
+}
+
 TEST_F(HeaderCheckerTest, SourceFileForInclude) {
   using base::FilePath;
   const std::vector<SourceDir> kIncludeDirs = {
@@ -391,16 +439,6 @@
   EXPECT_FALSE(err.has_error());
 }
 
-TEST_F(HeaderCheckerTest, SourceFileForInclude_SwiftBridgeHeader) {
-  const SourceFile bridge_header("//a/bridge_header.h");
-  a_.swift_values().bridge_header() = bridge_header;
-  auto checker = CreateChecker();
-
-  HeaderChecker::FileMap file_map;
-  checker->AddTargetToFileMap(&a_, &file_map);
-  EXPECT_NE(file_map.find(bridge_header), file_map.end());
-}
-
 TEST_F(HeaderCheckerTest, Friend) {
   // Note: we have a public dependency chain A -> B -> C set up already.
   InputFile input_file(SourceFile("//some_file.cc"));
diff --git a/src/gn/json_project_writer_unittest.cc b/src/gn/json_project_writer_unittest.cc
index cab6dd7..b581051 100644
--- a/src/gn/json_project_writer_unittest.cc
+++ b/src/gn/json_project_writer_unittest.cc
@@ -251,7 +251,7 @@
             "framework_switch": "-framework ",
             "lib_dir_switch": "-L",
             "lib_switch": "-l",
-            "outputs": [ "{{target_out_dir}}/{{module_name}}.swiftmodule" ],
+            "outputs": [ "{{target_gen_dir}}/{{target_output_name}}.h", "{{target_out_dir}}/{{module_name}}.swiftmodule" ],
             "partial_outputs": [ "{{target_out_dir}}/{{source_name_part}}.o" ],
             "weak_framework_switch": "-weak_framework "
          }
@@ -476,7 +476,7 @@
             "framework_switch": "-framework ",
             "lib_dir_switch": "-L",
             "lib_switch": "-l",
-            "outputs": [ "{{target_out_dir}}/{{module_name}}.swiftmodule" ],
+            "outputs": [ "{{target_gen_dir}}/{{target_output_name}}.h", "{{target_out_dir}}/{{module_name}}.swiftmodule" ],
             "partial_outputs": [ "{{target_out_dir}}/{{source_name_part}}.o" ],
             "weak_framework_switch": "-weak_framework "
          }
@@ -725,7 +725,7 @@
             "framework_switch": "-framework ",
             "lib_dir_switch": "-L",
             "lib_switch": "-l",
-            "outputs": [ "{{target_out_dir}}/{{module_name}}.swiftmodule" ],
+            "outputs": [ "{{target_gen_dir}}/{{target_output_name}}.h", "{{target_out_dir}}/{{module_name}}.swiftmodule" ],
             "partial_outputs": [ "{{target_out_dir}}/{{source_name_part}}.o" ],
             "weak_framework_switch": "-weak_framework "
          }
diff --git a/src/gn/ninja_binary_target_writer_unittest.cc b/src/gn/ninja_binary_target_writer_unittest.cc
index 5fd066b..ab12c68 100644
--- a/src/gn/ninja_binary_target_writer_unittest.cc
+++ b/src/gn/ninja_binary_target_writer_unittest.cc
@@ -38,6 +38,7 @@
       "cflags =\n"
       "cflags_cc =\n"
       "root_out_dir = .\n"
+      "target_gen_dir = gen/foo\n"
       "target_out_dir = obj/foo\n"
       "target_output_name = bar\n"
       "\n"
@@ -72,6 +73,7 @@
       "defines =\n"
       "include_dirs =\n"
       "root_out_dir = .\n"
+      "target_gen_dir = gen/foo\n"
       "target_out_dir = obj/foo\n"
       "target_output_name = bar\n"
       "\n"
@@ -99,6 +101,7 @@
       "defines =\n"
       "include_dirs =\n"
       "root_out_dir = .\n"
+      "target_gen_dir = gen/foo\n"
       "target_out_dir = obj/foo\n"
       "target_output_name = libbar\n"
       "\n"
@@ -136,6 +139,7 @@
         "cflags =\n"
         "cflags_cc =\n"
         "root_out_dir = .\n"
+        "target_gen_dir = gen/foo\n"
         "target_out_dir = obj/foo\n"
         "target_output_name = bar\n"
         "\n"
@@ -171,6 +175,7 @@
         "cflags =\n"
         "cflags_cc =\n"
         "root_out_dir = .\n"
+        "target_gen_dir = gen/foo\n"
         "target_out_dir = obj/foo\n"
         "target_output_name = bar\n"
         "\n"
diff --git a/src/gn/ninja_c_binary_target_writer.cc b/src/gn/ninja_c_binary_target_writer.cc
index 5be23d8..9b656bf 100644
--- a/src/gn/ninja_c_binary_target_writer.cc
+++ b/src/gn/ninja_c_binary_target_writer.cc
@@ -504,33 +504,14 @@
     const std::vector<OutputFile>& input_deps,
     const std::vector<OutputFile>& order_only_deps,
     std::vector<OutputFile>* object_files) {
-  DCHECK(target_->source_types_used().SwiftSourceUsed());
-  object_files->reserve(object_files->size() + target_->sources().size());
-
-  // 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).
-  const Tool* tool =
-      target_->toolchain()->GetToolForSourceType(SourceFile::SOURCE_SWIFT);
-  DCHECK(!tool->outputs().list().empty());
+  DCHECK(target_->builds_swift_module());
 
   std::vector<OutputFile> outputs;
-  SubstitutionWriter::ApplyListToLinkerAsOutputFile(target_, tool,
-                                                    tool->outputs(), &outputs);
+  target_->swift_values().GetOutputs(target_, &outputs);
 
-  // Expand partial outputs too (if any).
-  for (const SourceFile& source : target_->sources()) {
-    if (!source.IsSwiftType())
-      continue;
-
-    SubstitutionWriter::ApplyListToCompilerAsOutputFile(
-        target_, source, tool->partial_outputs(), &outputs);
-  }
-
+  const BuildSettings* build_settings = settings_->build_settings();
   for (const OutputFile& output : outputs) {
-    const SourceFile output_as_source =
-        output.AsSourceFile(target_->settings()->build_settings());
-
+    const SourceFile output_as_source = output.AsSourceFile(build_settings);
     if (output_as_source.IsObjectType()) {
       object_files->push_back(output);
     }
@@ -541,9 +522,11 @@
   swift_order_only_deps.Append(order_only_deps.begin(), order_only_deps.end());
 
   for (const Target* swiftmodule :
-       resolved().GetSwiftModuleDependencies(target_))
+       resolved().GetSwiftModuleDependencies(target_)) {
     swift_order_only_deps.push_back(swiftmodule->dependency_output_file());
+  }
 
+  const Tool* tool = target_->swift_values().GetTool(target_);
   WriteCompilerBuildLine(target_->sources(), input_deps,
                          swift_order_only_deps.vector(), tool->name(), outputs,
                          /*can_write_source_info=*/false,
diff --git a/src/gn/ninja_c_binary_target_writer_unittest.cc b/src/gn/ninja_c_binary_target_writer_unittest.cc
index d64e4a6..3815ab4 100644
--- a/src/gn/ninja_c_binary_target_writer_unittest.cc
+++ b/src/gn/ninja_c_binary_target_writer_unittest.cc
@@ -50,6 +50,7 @@
         "cflags =\n"
         "cflags_cc =\n"
         "root_out_dir = .\n"
+        "target_gen_dir = gen/foo\n"
         "target_out_dir = obj/foo\n"
         "target_output_name = bar\n"
         "\n"
@@ -82,6 +83,7 @@
         "defines =\n"
         "include_dirs =\n"
         "root_out_dir = .\n"
+        "target_gen_dir = gen/foo\n"
         "target_out_dir = obj/foo\n"
         "target_output_name = libshlib\n"
         "\n"
@@ -118,6 +120,7 @@
         "defines =\n"
         "include_dirs =\n"
         "root_out_dir = .\n"
+        "target_gen_dir = gen/foo\n"
         "target_out_dir = obj/foo\n"
         "target_output_name = libstlib\n"
         "\n"
@@ -143,6 +146,7 @@
         "defines =\n"
         "include_dirs =\n"
         "root_out_dir = .\n"
+        "target_gen_dir = gen/foo\n"
         "target_out_dir = obj/foo\n"
         "target_output_name = libstlib\n"
         "\n"
@@ -205,6 +209,7 @@
       "cflags =\n"
       "cflags_cc =\n"
       "root_out_dir = .\n"
+      "target_gen_dir = gen/foo\n"
       "target_out_dir = obj/foo\n"
       "target_output_name = libbar\n"
       "\n"
@@ -253,6 +258,7 @@
         "cflags =\n"
         "cflags_cc =\n"
         "root_out_dir = .\n"
+        "target_gen_dir = gen/foo\n"
         "target_out_dir = obj/foo\n"
         "target_output_name = libbar\n"
         "\n"
@@ -284,6 +290,7 @@
         "cflags =\n"
         "cflags_cc =\n"
         "root_out_dir = .\n"
+        "target_gen_dir = gen/foo\n"
         "target_out_dir = obj/foo\n"
         "target_output_name = libbar\n"
         "\n"
@@ -336,6 +343,7 @@
       "cflags =\n"
       "cflags_cc =\n"
       "root_out_dir = .\n"
+      "target_gen_dir = gen/foo\n"
       "target_out_dir = obj/foo\n"
       "target_output_name = libshlib\n"
       "\n"
@@ -403,6 +411,7 @@
       "cflags =\n"
       "cflags_cc =\n"
       "root_out_dir = .\n"
+      "target_gen_dir = gen/foo\n"
       "target_out_dir = obj/foo\n"
       "target_output_name = gen_obj\n"
       "\n"
@@ -439,6 +448,7 @@
       "defines =\n"
       "include_dirs =\n"
       "root_out_dir = .\n"
+      "target_gen_dir = gen/foo\n"
       "target_out_dir = obj/foo\n"
       "target_output_name = libgen_lib\n"
       "\n"
@@ -480,6 +490,7 @@
       "cflags =\n"
       "cflags_cc =\n"
       "root_out_dir = .\n"
+      "target_gen_dir = gen/foo\n"
       "target_out_dir = obj/foo\n"
       "target_output_name = final_target\n"
       "\n"
@@ -524,6 +535,7 @@
       "defines =\n"
       "include_dirs =\n"
       "root_out_dir = .\n"
+      "target_gen_dir = gen/foo\n"
       "target_out_dir = obj/foo\n"
       "target_output_name = libshlib\n"
       "\n"
@@ -586,6 +598,7 @@
       "defines =\n"
       "include_dirs =\n"
       "root_out_dir = .\n"
+      "target_gen_dir = gen/foo\n"
       "target_out_dir = obj/foo\n"
       "target_output_name = libshlib\n"
       "\n"
@@ -631,6 +644,7 @@
       "cflags =\n"
       "cflags_cc =\n"
       "root_out_dir = .\n"
+      "target_gen_dir = gen/foo\n"
       "target_out_dir = obj/foo\n"
       "target_output_name = shlib\n"
       "\n"
@@ -689,6 +703,7 @@
       "cflags =\n"
       "cflags_cc =\n"
       "root_out_dir = .\n"
+      "target_gen_dir = gen/foo\n"
       "target_out_dir = obj/foo\n"
       "target_output_name = inter\n"
       "\n"
@@ -724,6 +739,7 @@
       "cflags =\n"
       "cflags_cc =\n"
       "root_out_dir = .\n"
+      "target_gen_dir = gen/foo\n"
       "target_out_dir = obj/foo\n"
       "target_output_name = exe\n"
       "\n"
@@ -765,6 +781,7 @@
       "cflags =\n"
       "cflags_cc =\n"
       "root_out_dir = .\n"
+      "target_gen_dir = gen/foo\n"
       "target_out_dir = obj/foo\n"
       "target_output_name = libbar\n"
       "\n"
@@ -804,6 +821,7 @@
       "cflags =\n"
       "cflags_cc =\n"
       "root_out_dir = .\n"
+      "target_gen_dir = gen/foo\n"
       "target_out_dir = obj/foo\n"
       "target_output_name = libbar\n"
       "\n"
@@ -841,6 +859,7 @@
       "cflags =\n"
       "cflags_cc =\n"
       "root_out_dir = .\n"
+      "target_gen_dir = gen/foo\n"
       "target_out_dir = obj/foo\n"
       "target_output_name = exe\n"
       "\n"
@@ -1195,6 +1214,7 @@
         "cflags =\n"
         "cflags_cc =\n"
         "root_out_dir = .\n"
+        "target_gen_dir = gen/foo\n"
         "target_out_dir = obj/foo\n"
         "target_output_name = bar\n"
         "\n"
@@ -1230,6 +1250,7 @@
         "defines =\n"
         "include_dirs =\n"
         "root_out_dir = .\n"
+        "target_gen_dir = gen/foo\n"
         "target_out_dir = obj/foo\n"
         "target_output_name = libbar\n"
         "\n"
@@ -1268,6 +1289,7 @@
         "cflags =\n"
         "cflags_cc =\n"
         "root_out_dir = .\n"
+        "target_gen_dir = gen/foo\n"
         "target_out_dir = obj/foo\n"
         "target_output_name = bar\n"
         "\n"
@@ -1322,6 +1344,7 @@
         "cflags =\n"
         "cflags_cc =\n"
         "root_out_dir = .\n"
+        "target_gen_dir = gen/foo\n"
         "target_out_dir = obj/foo\n"
         "target_output_name = bar\n"
         "\n"
@@ -1378,6 +1401,7 @@
       "cflags =\n"
       "cflags_cc =\n"
       "root_out_dir = .\n"
+      "target_gen_dir = gen/bar\n"
       "target_out_dir = obj/bar\n"
       "target_output_name = bar\n"
       "\n"
@@ -1554,6 +1578,7 @@
       "cflags =\n"
       "cflags_cc =\n"
       "root_out_dir = .\n"
+      "target_gen_dir = gen/exe\n"
       "target_out_dir = obj/exe\n"
       "target_output_name = exe\n"
       "\n"
@@ -1721,6 +1746,7 @@
       "cflags =\n"
       "cflags_cc =\n"
       "root_out_dir = .\n"
+      "target_gen_dir = gen/exe\n"
       "target_out_dir = obj/exe\n"
       "target_output_name = exe\n"
       "\n"
@@ -1797,6 +1823,7 @@
       "cflags =\n"
       "cflags_cc =\n"
       "root_out_dir = .\n"
+      "target_gen_dir = gen/bar\n"
       "target_out_dir = obj/bar\n"
       "target_output_name = bar\n"
       "\n"
@@ -1899,6 +1926,7 @@
       "cflags =\n"
       "cflags_cc =\n"
       "root_out_dir = .\n"
+      "target_gen_dir = gen/exe\n"
       "target_out_dir = obj/exe\n"
       "target_output_name = binary\n"
       "\n"
@@ -1994,6 +2022,7 @@
       "cflags =\n"
       "cflags_cc =\n"
       "root_out_dir = .\n"
+      "target_gen_dir = gen/exe\n"
       "target_out_dir = obj/exe\n"
       "target_output_name = binary\n"
       "\n"
@@ -2091,6 +2120,7 @@
       "cflags =\n"
       "cflags_cc =\n"
       "root_out_dir = .\n"
+      "target_gen_dir = gen/exe\n"
       "target_out_dir = obj/exe\n"
       "target_output_name = binary\n"
       "\n"
@@ -2200,6 +2230,7 @@
       "cflags =\n"
       "cflags_cc =\n"
       "root_out_dir = .\n"
+      "target_gen_dir = gen/exe\n"
       "target_out_dir = obj/exe\n"
       "target_output_name = binary\n"
       "\n"
@@ -2249,6 +2280,7 @@
       "cflags =\n"
       "cflags_cc =\n"
       "root_out_dir = .\n"
+      "target_gen_dir = gen/foo\n"
       "target_out_dir = obj/foo\n"
       "target_output_name = libbar\n"
       "\n"
@@ -2295,11 +2327,12 @@
         "module_name = Foo\n"
         "module_dirs =\n"
         "root_out_dir = .\n"
+        "target_gen_dir = gen/foo\n"
         "target_out_dir = obj/foo\n"
         "target_output_name = foo\n"
         "\n"
-        "build obj/foo/Foo.swiftmodule obj/foo/file1.o obj/foo/file2.o: swift"
-        " ../../foo/file1.swift ../../foo/file2.swift\n"
+        "build gen/foo/foo.h obj/foo/Foo.swiftmodule obj/foo/file1.o "
+        "obj/foo/file2.o: swift ../../foo/file1.swift ../../foo/file2.swift\n"
         "  restat = 1\n"
         "\n"
         "build obj/foo/foo.stamp: stamp"
@@ -2331,11 +2364,12 @@
         "module_name = Bar\n"
         "module_dirs = -Iobj/foo\n"
         "root_out_dir = .\n"
+        "target_gen_dir = gen/bar\n"
         "target_out_dir = obj/bar\n"
         "target_output_name = bar\n"
         "\n"
-        "build obj/bar/Bar.swiftmodule obj/bar/bar.o: swift ../../bar/bar.swift"
-        " || obj/foo/foo.stamp\n"
+        "build gen/bar/bar.h obj/bar/Bar.swiftmodule obj/bar/bar.o: swift "
+        "../../bar/bar.swift || obj/foo/foo.stamp\n"
         "  restat = 1\n"
         "\n"
         "build obj/bar/bar.stamp: stamp obj/bar/bar.o "
@@ -2375,11 +2409,12 @@
         "module_name = Bar\n"
         "module_dirs = -Iobj/foo\n"
         "root_out_dir = .\n"
+        "target_gen_dir = gen/bar\n"
         "target_out_dir = obj/bar\n"
         "target_output_name = bar\n"
         "\n"
-        "build obj/bar/Bar.swiftmodule obj/bar/bar.o: swift ../../bar/bar.swift"
-        " || obj/bar/group.stamp obj/foo/foo.stamp\n"
+        "build gen/bar/bar.h obj/bar/Bar.swiftmodule obj/bar/bar.o: swift "
+        "../../bar/bar.swift || obj/bar/group.stamp obj/foo/foo.stamp\n"
         "  restat = 1\n"
         "\n"
         "build obj/bar/bar.stamp: stamp obj/bar/bar.o "
@@ -2406,6 +2441,7 @@
         "defines =\n"
         "include_dirs =\n"
         "root_out_dir = .\n"
+        "target_gen_dir = gen/bar\n"
         "target_out_dir = obj/bar\n"
         "target_output_name = bar\n"
         "\n"
@@ -2695,6 +2731,7 @@
   const char expected[] = R"(defines =
 include_dirs =
 root_out_dir = .
+target_gen_dir = gen/launchpad
 target_out_dir = obj/launchpad
 target_output_name = main
 
@@ -2745,6 +2782,7 @@
       "defines =\n"
       "include_dirs =\n"
       "root_out_dir = .\n"
+      "target_gen_dir = gen/foo\n"
       "target_out_dir = obj/foo\n"
       "target_output_name = bar\n"
       "\n"
diff --git a/src/gn/ninja_rust_binary_target_writer_unittest.cc b/src/gn/ninja_rust_binary_target_writer_unittest.cc
index 706f075..e3468a9 100644
--- a/src/gn/ninja_rust_binary_target_writer_unittest.cc
+++ b/src/gn/ninja_rust_binary_target_writer_unittest.cc
@@ -60,6 +60,7 @@
         "rustflags =\n"
         "rustenv =\n"
         "root_out_dir = .\n"
+        "target_gen_dir = gen/foo\n"
         "target_out_dir = obj/foo\n"
         "target_output_name = bar\n"
         "\n"
@@ -114,6 +115,7 @@
         "rustflags =\n"
         "rustenv =\n"
         "root_out_dir = .\n"
+        "target_gen_dir = gen/baz\n"
         "target_out_dir = obj/baz\n"
         "target_output_name = libprivatelib\n"
         "\n"
@@ -155,6 +157,7 @@
         "rustflags =\n"
         "rustenv =\n"
         "root_out_dir = .\n"
+        "target_gen_dir = gen/far\n"
         "target_out_dir = obj/far\n"
         "target_output_name = libfarlib\n"
         "\n"
@@ -196,6 +199,7 @@
         "rustflags =\n"
         "rustenv =\n"
         "root_out_dir = .\n"
+        "target_gen_dir = gen/bar\n"
         "target_out_dir = obj/bar\n"
         "target_output_name = libpubliclib\n"
         "\n"
@@ -251,6 +255,7 @@
         "rustflags =\n"
         "rustenv =\n"
         "root_out_dir = .\n"
+        "target_gen_dir = gen/main\n"
         "target_out_dir = obj/main\n"
         "target_output_name = main\n"
         "\n"
@@ -303,6 +308,7 @@
         "rustflags =\n"
         "rustenv =\n"
         "root_out_dir = .\n"
+        "target_gen_dir = gen/faz\n"
         "target_out_dir = obj/faz\n"
         "target_output_name = libprivate_inside\n"
         "\n"
@@ -343,6 +349,7 @@
         "rustflags =\n"
         "rustenv =\n"
         "root_out_dir = .\n"
+        "target_gen_dir = gen/baz\n"
         "target_out_dir = obj/baz\n"
         "target_output_name = libinside\n"
         "\n"
@@ -386,6 +393,7 @@
         "rustflags =\n"
         "rustenv =\n"
         "root_out_dir = .\n"
+        "target_gen_dir = gen/bar\n"
         "target_out_dir = obj/bar\n"
         "target_output_name = libmylib\n"
         "\n"
@@ -458,6 +466,7 @@
         "rustflags =\n"
         "rustenv =\n"
         "root_out_dir = .\n"
+        "target_gen_dir = gen/foo\n"
         "target_out_dir = obj/foo\n"
         "target_output_name = bar\n"
         "\n"
@@ -509,6 +518,7 @@
         "rustflags =\n"
         "rustenv =\n"
         "root_out_dir = .\n"
+        "target_gen_dir = gen/bar\n"
         "target_out_dir = obj/bar\n"
         "target_output_name = libmymacro\n"
         "\n"
@@ -568,6 +578,7 @@
         "rustflags =\n"
         "rustenv =\n"
         "root_out_dir = .\n"
+        "target_gen_dir = gen/bar\n"
         "target_out_dir = obj/bar\n"
         "target_output_name = libmylib\n"
         "\n"
@@ -610,6 +621,7 @@
         "rustflags =\n"
         "rustenv =\n"
         "root_out_dir = .\n"
+        "target_gen_dir = gen/foo\n"
         "target_out_dir = obj/foo\n"
         "target_output_name = bar\n"
         "\n"
@@ -701,6 +713,7 @@
         "rustflags =\n"
         "rustenv =\n"
         "root_out_dir = .\n"
+        "target_gen_dir = gen/foo\n"
         "target_out_dir = obj/foo\n"
         "target_output_name = bar\n"
         "\n"
@@ -803,6 +816,7 @@
         "rustflags =\n"
         "rustenv =\n"
         "root_out_dir = .\n"
+        "target_gen_dir = gen/foo\n"
         "target_out_dir = obj/foo\n"
         "target_output_name = bar\n"
         "\n"
@@ -850,6 +864,7 @@
         "rustflags =\n"
         "rustenv =\n"
         "root_out_dir = .\n"
+        "target_gen_dir = gen/foo\n"
         "target_out_dir = obj/foo\n"
         "target_output_name = bar\n"
         "\n"
@@ -891,6 +906,7 @@
         "rustflags =\n"
         "rustenv =\n"
         "root_out_dir = .\n"
+        "target_gen_dir = gen/baz\n"
         "target_out_dir = obj/baz\n"
         "target_output_name = libbaz\n"
         "\n"
@@ -1062,6 +1078,7 @@
       "rustflags =\n"
       "rustenv =\n"
       "root_out_dir = .\n"
+      "target_gen_dir = gen/exe\n"
       "target_out_dir = obj/exe\n"
       "target_output_name = exe\n"
       "\n"
@@ -1129,6 +1146,7 @@
         "rustflags =\n"
         "rustenv =\n"
         "root_out_dir = .\n"
+        "target_gen_dir = gen/foo\n"
         "target_out_dir = obj/foo\n"
         "target_output_name = bar\n"
         "\n"
@@ -1195,6 +1213,7 @@
         "rustflags =\n"
         "rustenv =\n"
         "root_out_dir = .\n"
+        "target_gen_dir = gen/foo\n"
         "target_out_dir = obj/foo\n"
         "target_output_name = bar\n"
         "\n"
@@ -1294,6 +1313,7 @@
         "rustflags =\n"
         "rustenv =\n"
         "root_out_dir = .\n"
+        "target_gen_dir = gen/foo\n"
         "target_out_dir = obj/foo\n"
         "target_output_name = librlibcrate\n"
         "\n"
@@ -1377,6 +1397,7 @@
         "rustflags =\n"
         "rustenv =\n"
         "root_out_dir = .\n"
+        "target_gen_dir = gen/bar\n"
         "target_out_dir = obj/bar\n"
         "target_output_name = libmymacro\n"
         "\n"
@@ -1423,6 +1444,7 @@
         "rustflags =\n"
         "rustenv =\n"
         "root_out_dir = .\n"
+        "target_gen_dir = gen/foo\n"
         "target_out_dir = obj/foo\n"
         "target_output_name = bar\n"
         "\n"
@@ -1468,6 +1490,7 @@
         "rustflags =\n"
         "rustenv =\n"
         "root_out_dir = .\n"
+        "target_gen_dir = gen/bar\n"
         "target_out_dir = obj/bar\n"
         "target_output_name = libmylib\n"
         "\n"
@@ -1516,6 +1539,7 @@
         "rustflags =\n"
         "rustenv =\n"
         "root_out_dir = .\n"
+        "target_gen_dir = gen/foo\n"
         "target_out_dir = obj/foo\n"
         "target_output_name = bar\n"
         "\n"
@@ -1570,6 +1594,7 @@
         "rustflags =\n"
         "rustenv =\n"
         "root_out_dir = .\n"
+        "target_gen_dir = gen/foo\n"
         "target_out_dir = obj/foo\n"
         "target_output_name = bar\n"
         "\n"
@@ -1620,6 +1645,7 @@
         "rustflags =\n"
         "rustenv =\n"
         "root_out_dir = .\n"
+        "target_gen_dir = gen/foo\n"
         "target_out_dir = obj/foo\n"
         "target_output_name = bar\n"
         "\n"
@@ -1664,6 +1690,7 @@
         "rustflags =\n"
         "rustenv =\n"
         "root_out_dir = .\n"
+        "target_gen_dir = gen/bar\n"
         "target_out_dir = obj/bar\n"
         "target_output_name = libmylib\n"
         "\n"
@@ -1704,6 +1731,7 @@
         "rustflags =\n"
         "rustenv =\n"
         "root_out_dir = .\n"
+        "target_gen_dir = gen/foo\n"
         "target_out_dir = obj/foo\n"
         "target_output_name = bar\n"
         "\n"
@@ -1781,6 +1809,7 @@
         "rustflags =\n"
         "rustenv =\n"
         "root_out_dir = .\n"
+        "target_gen_dir = gen/foo\n"
         "target_out_dir = obj/foo\n"
         "target_output_name = bar\n"
         "\n"
@@ -1865,6 +1894,7 @@
         "rustflags =\n"
         "rustenv =\n"
         "root_out_dir = .\n"
+        "target_gen_dir = gen/linked\n"
         "target_out_dir = obj/linked\n"
         "target_output_name = exe\n"
         "\n"
@@ -1918,6 +1948,7 @@
       "rustflags =\n"
       "rustenv =\n"
       "root_out_dir = .\n"
+      "target_gen_dir = gen/foo\n"
       "target_out_dir = obj/foo\n"
       "target_output_name = bar\n"
       "\n"
diff --git a/src/gn/swift_values.cc b/src/gn/swift_values.cc
index 6faa9af..08f32ac 100644
--- a/src/gn/swift_values.cc
+++ b/src/gn/swift_values.cc
@@ -4,11 +4,14 @@
 
 #include "gn/swift_values.h"
 
+#include <algorithm>
+
 #include "gn/deps_iterator.h"
 #include "gn/err.h"
 #include "gn/settings.h"
 #include "gn/substitution_writer.h"
 #include "gn/target.h"
+#include "gn/tool.h"
 
 SwiftValues::SwiftValues() = default;
 
@@ -16,49 +19,76 @@
 
 // static
 bool SwiftValues::OnTargetResolved(Target* target, Err* err) {
-  return FillModuleOutputFile(target, err);
-}
-
-// static
-bool SwiftValues::FillModuleOutputFile(Target* target, Err* err) {
   if (!target->builds_swift_module())
     return true;
 
-  const Tool* tool =
-      target->toolchain()->GetToolForSourceType(SourceFile::SOURCE_SWIFT);
+  return target->swift_values().FillModuleOutputFile(target, err);
+}
 
-  std::vector<OutputFile> outputs;
+const Tool* SwiftValues::GetTool(const Target* target) const {
+  DCHECK(target->builds_swift_module());
+  return target->toolchain()->GetToolForSourceType(SourceFile::SOURCE_SWIFT);
+}
+
+void SwiftValues::GetOutputs(const Target* target,
+                             std::vector<OutputFile>* result) const {
+  const Tool* tool = GetTool(target);
+
+  // Expand tool's outputs().
   SubstitutionWriter::ApplyListToLinkerAsOutputFile(target, tool,
-                                                    tool->outputs(), &outputs);
+                                                    tool->outputs(), result);
 
-  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()) {
+  // Expand tool's partial_outputs() for each .swift source file.
+  for (const SourceFile& source : target->sources()) {
+    if (!source.IsSwiftType()) {
       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;
+    SubstitutionWriter::ApplyListToCompilerAsOutputFile(
+        target, source, tool->partial_outputs(), result);
   }
+}
 
-  if (!swiftmodule_output_found) {
+void SwiftValues::GetOutputsAsSourceFiles(
+    const Target* target,
+    std::vector<SourceFile>* result) const {
+  std::vector<OutputFile> outputs;
+  GetOutputs(target, &outputs);
+
+  result->reserve(outputs.size());
+
+  const BuildSettings* build_settings = target->settings()->build_settings();
+  for (const OutputFile& output : outputs) {
+    result->push_back(output.AsSourceFile(build_settings));
+  }
+}
+
+bool SwiftValues::FillModuleOutputFile(Target* target, Err* err) {
+  std::vector<OutputFile> outputs;
+  GetOutputs(target, &outputs);
+
+  // Keep only .swiftmodule output files.
+  const BuildSettings* build_settings = target->settings()->build_settings();
+  const auto iter = std::remove_if(
+      outputs.begin(), outputs.end(),
+      [build_settings](const OutputFile& output) -> bool {
+        SourceFile output_as_source_file = output.AsSourceFile(build_settings);
+        return !output_as_source_file.IsSwiftModuleType();
+      });
+  outputs.erase(iter, outputs.end());
+
+  // A target should generate exactly one .swiftmodule file.
+  if (outputs.size() != 1) {
+    const Tool* tool = GetTool(target);
     *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;
   }
 
+  module_output_file_ = outputs.front();
+  module_output_dir_ =
+      module_output_file_.AsSourceFile(build_settings).GetDir();
+
   return true;
 }
diff --git a/src/gn/swift_values.h b/src/gn/swift_values.h
index 198ca85..1dde1c2 100644
--- a/src/gn/swift_values.h
+++ b/src/gn/swift_values.h
@@ -14,6 +14,7 @@
 
 class Err;
 class Target;
+class Tool;
 
 // Holds values specific to target that compile .swift files.
 class SwiftValues {
@@ -43,9 +44,20 @@
   // Computed when the target is resolved.
   const SourceDir& module_output_dir() const { return module_output_dir_; }
 
+  // Returns the tool used for target.
+  const Tool* GetTool(const Target* target) const;
+
+  // Expands the toolchain's outputs and partial outputs for the target.
+  void GetOutputs(const Target* target, std::vector<OutputFile>* result) const;
+
+  // Expands the toolchain's outputs and partial outputs for the target,
+  // returning the values as SourceFile list.
+  void GetOutputsAsSourceFiles(const Target* target,
+                               std::vector<SourceFile>* result) const;
+
  private:
   // Fill informations about .swiftmodule generated by this target.
-  static bool FillModuleOutputFile(Target* target, Err* err);
+  bool FillModuleOutputFile(Target* target, Err* err);
 
   // Name of the optional bridge header used to import Objective-C classes.
   // Filled from the target, may be empty even if the target include .swift
diff --git a/src/gn/test_with_scope.cc b/src/gn/test_with_scope.cc
index 21a4c22..4f33a59 100644
--- a/src/gn/test_with_scope.cc
+++ b/src/gn/test_with_scope.cc
@@ -237,6 +237,7 @@
       "swiftc --module-name {{module_name}} {{module_dirs}} {{inputs}}",
       swift_tool.get());
   swift_tool->set_outputs(SubstitutionList::MakeForTest(
+      "{{target_gen_dir}}/{{target_output_name}}.h",
       "{{target_out_dir}}/{{module_name}}.swiftmodule"));
   swift_tool->set_partial_outputs(SubstitutionList::MakeForTest(
       "{{target_out_dir}}/{{source_name_part}}.o"));