Fix output directories for generated source files.

NOTE: This is a re-land of [2] since another way to fix the issue
      could not be found.

This CL fixes an annoying issue where the local build directory path
leaks into the generated Ninja files when {{source_out_dir}} or
even {{source_gen_dir}} are expanded.

In the case of a generated source file, its path will be source-absolute
but located in the build directory, for example:

  //out/Debug/gen/header.h
  //out/Debug/toolchain/gen/src/source.cc

Before this CL, the computed output directory would leak the 'out/Debug'
prefix, i.e. compute an output path, relative to `out/Debug` as:

  obj/out/Debug/gen/
  obj/out/Debug/toolchain/gen/src/

After this CL, the build directory prefix is replaced with the
"BUILD_DIR" string literal, and the result becomes:

  obj/BUILD_DIR/gen/
  obj/BUILD_DIR/toolchain/gen/src

This makes the commands more hermetic, especially with regards to remote
builds which implement content-based caching, using the command hash as
a key. It is also similar to what GN does for absolute paths, where it
uses the ABS_PATH literal to create target-specific directories for
sources that have an absolute path.

I tested that I could still build Fuchsia, Chromium and Pigweed with
this patch (as long as Pigwedd's build system is fixed with [1] to
support upstream GN).

Note that this CL is required to unblock the GN auto-roller for the
Fuchsia project: without it, the output directory leak checks will fail
the builds since the latest Rust-related changes adds a ton of
`-Lnative=<path-with-build-directory>` to a very large number of
Rust link commands.

[1] https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/90080
[2] https://gn-review.googlesource.com/c/gn/+/13240

Change-Id: I85b7798b25f282085864a6cb58a7eecb85648881
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/13580
Reviewed-by: Brett Wilson <brettw@chromium.org>
Commit-Queue: David Turner <digit@google.com>
diff --git a/src/gn/filesystem_utils.cc b/src/gn/filesystem_utils.cc
index 6de3fbd..7a7078e 100644
--- a/src/gn/filesystem_utils.cc
+++ b/src/gn/filesystem_utils.cc
@@ -1025,10 +1025,25 @@
   OutputFile result = GetBuildDirAsOutputFile(context, type);
 
   if (source_dir.is_source_absolute()) {
-    // The source dir is source-absolute, so we trim off the two leading
-    // slashes to append to the toolchain object directory.
-    result.value().append(&source_dir.value()[2],
-                          source_dir.value().size() - 2);
+    std::string_view build_dir = context.build_settings->build_dir().value();
+    std::string_view source_dir_path = source_dir.value();
+    if (source_dir_path.substr(0, build_dir.size()) == build_dir) {
+      // The source dir is source-absolute, but in the build directory
+      // (e.g. `//out/Debug/gen/src/foo.cc` or
+      // `//out/Debug/toolchain1/gen/foo.cc`), which happens for generated
+      // sources. In this case, remove the build directory prefix, and replace
+      // it with `BUILD_DIR`. This will create results like `obj/BUILD_DIR/gen`
+      // or `toolchain2/obj/BUILD_DIR/toolchain1/gen` which look surprising,
+      // but guarantee unicity.
+      result.value().append("BUILD_DIR/");
+      result.value().append(&source_dir_path[build_dir.size()],
+                            source_dir_path.size() - build_dir.size());
+    } else {
+      // The source dir is source-absolute, so we trim off the two leading
+      // slashes to append to the toolchain object directory.
+      result.value().append(&source_dir.value()[2],
+                            source_dir.value().size() - 2);
+    }
   } else {
     // System-absolute.
     AppendFixedAbsolutePathSuffix(context.build_settings, source_dir, &result);
diff --git a/src/gn/ninja_c_binary_target_writer_unittest.cc b/src/gn/ninja_c_binary_target_writer_unittest.cc
index 6056c36..9476193 100644
--- a/src/gn/ninja_c_binary_target_writer_unittest.cc
+++ b/src/gn/ninja_c_binary_target_writer_unittest.cc
@@ -405,18 +405,18 @@
       "target_out_dir = obj/foo\n"
       "target_output_name = gen_obj\n"
       "\n"
-      "build obj/out/Debug/gen_obj.generated.o: cxx generated.cc"
+      "build obj/BUILD_DIR/gen_obj.generated.o: cxx generated.cc"
       " || obj/foo/generate.stamp\n"
       "  source_file_part = generated.cc\n"
       "  source_name_part = generated\n"
       "\n"
-      "build obj/foo/gen_obj.stamp: stamp obj/out/Debug/gen_obj.generated.o"
+      "build obj/foo/gen_obj.stamp: stamp obj/BUILD_DIR/gen_obj.generated.o"
       // The order-only dependency here is strictly unnecessary since the
       // sources list this as an order-only dep.
       " || obj/foo/generate.stamp\n";
 
   std::string obj_str = obj_out.str();
-  EXPECT_EQ(obj_expected, obj_str);
+  EXPECT_EQ(std::string(obj_expected), obj_str);
 
   // A shared library depends on gen_obj, having corresponding header for
   // generated obj.
@@ -442,7 +442,7 @@
       "target_output_name = libgen_lib\n"
       "\n"
       "\n"
-      "build ./libgen_lib.so: solink obj/out/Debug/gen_obj.generated.o"
+      "build ./libgen_lib.so: solink obj/BUILD_DIR/gen_obj.generated.o"
       // The order-only dependency here is strictly unnecessary since
       // obj/out/Debug/gen_obj.generated.o has dependency to
       // obj/foo/gen_obj.stamp
diff --git a/src/gn/substitution_writer_unittest.cc b/src/gn/substitution_writer_unittest.cc
index fc3c446..eaa521a 100644
--- a/src/gn/substitution_writer_unittest.cc
+++ b/src/gn/substitution_writer_unittest.cc
@@ -45,6 +45,12 @@
   SourceFile result = SubstitutionWriter::ApplyPatternToSource(
       nullptr, setup.settings(), pattern, SourceFile("//foo/bar/myfile.txt"));
   ASSERT_EQ("//out/Debug/gen/foo/bar/myfile.tmp", result.value());
+
+  result = SubstitutionWriter::ApplyPatternToSource(
+      nullptr, setup.settings(), pattern,
+      SourceFile("//out/Debug/gen/generated_file.cc"));
+  ASSERT_EQ("//out/Debug/gen/BUILD_DIR/gen/generated_file.tmp", result.value())
+      << result.value();
 }
 
 TEST(SubstitutionWriter, ApplyPatternToSourceAsOutputFile) {