Fix complete_static_lib to include Rust libraries.

When a C++ `static_library` has `complete_static_lib = true` and depends
on a `rust_library`, the Rust library's symbols need to be available to
downstream consumers of the static library.

This change propagates `rust_library` dependencies through
`complete_static_lib` targets to downstream linking targets (like
executables), similar to how `shared_library` dependencies are handled.
This ensures the `.rlib` files are passed directly to the final linker
via the Ninja `rlibs` variable (using `{{rlibs}}` substitution).

Reproduction error before this change (with mixed C++/Rust example):
```
FAILED: c_main
g++  -o c_main -Wl,--start-group @c_main.rsp    -Wl,--end-group
/usr/bin/ld: obj/c_main.c_main.o: in function `main':
c_main.cc:(.text+0x5): undefined reference to `call_from_c'
collect2: error: ld returned 1 exit status
```

Verification success after this change:
```
[1/2] AR libc_lib.a
[2/2] LINK c_main
$ examples/rust_example/out/c_main
Value from c_lib (via Rust): 42
SUCCESS!
```

Bug: 42440276
Change-Id: I7ea3d6df444a25faf2847f59de5303884e0933a1
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/22100
Reviewed-by: Petr Hosek <phosek@google.com>
Commit-Queue: Evan Shrubsole <eshr@google.com>
diff --git a/examples/rust_example/BUILD.gn b/examples/rust_example/BUILD.gn
index 964b331..48da170 100644
--- a/examples/rust_example/BUILD.gn
+++ b/examples/rust_example/BUILD.gn
@@ -1,3 +1,17 @@
 group("default") {
-  deps = [ "//hello_world/src:hello_world" ]
+  deps = [
+    "//hello_world/src:hello_world",
+    ":c_main",
+  ]
+}
+
+static_library("c_lib") {
+  complete_static_lib = true
+  deps = [ "//hello_world/foo/src:foo" ]
+  sources = [ "c_lib.cc" ]
+}
+
+executable("c_main") {
+  deps = [ ":c_lib" ]
+  sources = [ "c_main.cc" ]
 }
diff --git a/examples/rust_example/build/BUILD.gn b/examples/rust_example/build/BUILD.gn
index ebfaa64..567cc15 100644
--- a/examples/rust_example/build/BUILD.gn
+++ b/examples/rust_example/build/BUILD.gn
@@ -2,7 +2,7 @@
   tool("rust_bin") {
     depfile = "{{target_out_dir}}/{{crate_name}}.d"
     outfile = "{{target_out_dir}}/{{crate_name}}"
-    command = "rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} --emit=dep-info=$depfile,link -Z dep-info-omit-d-target {{rustflags}} -o $outfile {{rustdeps}} {{externs}}"
+    command = "rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} --emit=dep-info=$depfile,link {{rustflags}} -o $outfile {{rustdeps}} {{externs}}"
     description = "RUST $outfile"
     outputs = [ outfile ]
   }
@@ -10,7 +10,7 @@
   tool("rust_staticlib") {
     depfile = "{{target_out_dir}}/{{crate_name}}.d"
     outfile = "{{target_out_dir}}/{{crate_name}}.a"
-    command = "rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} --emit=dep-info=$depfile,link -Z dep-info-omit-d-target {{rustflags}} -o $outfile {{rustdeps}} {{externs}}"
+    command = "rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} --emit=dep-info=$depfile,link {{rustflags}} -o $outfile {{rustdeps}} {{externs}}"
     description = "RUST $outfile"
     outputs = [ outfile ]
   }
@@ -18,7 +18,7 @@
   tool("rust_rlib") {
     depfile = "{{target_out_dir}}/{{crate_name}}.d"
     outfile = "{{target_out_dir}}/lib{{crate_name}}.rlib"
-    command = "rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} --emit=dep-info=$depfile,link -Z dep-info-omit-d-target {{rustflags}} -o $outfile {{rustdeps}} {{externs}}"
+    command = "rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} --emit=dep-info=$depfile,link {{rustflags}} -o $outfile {{rustdeps}} {{externs}}"
     description = "RUST $outfile"
     outputs = [ outfile ]
   }
@@ -26,7 +26,7 @@
   tool("rust_cdylib") {
     depfile = "{{target_out_dir}}/{{crate_name}}.d"
     outfile = "{{target_out_dir}}/lib{{crate_name}}.so"
-    command = "rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} --emit=dep-info=$depfile,link -Z dep-info-omit-d-target {{rustflags}} -o $outfile {{rustdeps}} {{externs}}"
+    command = "rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} --emit=dep-info=$depfile,link {{rustflags}} -o $outfile {{rustdeps}} {{externs}}"
     description = "RUST $outfile"
     outputs = [ outfile ]
   }
@@ -40,6 +40,32 @@
     command = "cp -af {{source}} {{output}}"
     description = "COPY {{source}} {{output}}"
   }
+
+  tool("cxx") {
+    depfile = "{{output}}.d"
+    command = "clang++ -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}} -c {{source}} -o {{output}}"
+    depsformat = "gcc"
+    description = "CXX {{output}}"
+    outputs = [ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ]
+  }
+
+  tool("alink") {
+    command = "llvm-ar rcs {{output}} {{inputs}}"
+    description = "AR {{target_output_name}}{{output_extension}}"
+    outputs = [ "{{target_out_dir}}/{{target_output_name}}{{output_extension}}" ]
+    default_output_extension = ".a"
+    output_prefix = "lib"
+  }
+
+  tool("link") {
+    outfile = "{{target_output_name}}{{output_extension}}"
+    rspfile = "$outfile.rsp"
+    command = "clang++ {{ldflags}} -o $outfile -Wl,--start-group @$rspfile {{solibs}} {{libs}} {{rlibs}} -Wl,--end-group"
+    description = "LINK $outfile"
+    default_output_dir = "{{root_out_dir}}"
+    rspfile_content = "{{inputs}}"
+    outputs = [ outfile ]
+  }
 }
 
 config("rust_defaults") {
diff --git a/examples/rust_example/c_lib.cc b/examples/rust_example/c_lib.cc
new file mode 100644
index 0000000..ec8b33e
--- /dev/null
+++ b/examples/rust_example/c_lib.cc
@@ -0,0 +1,5 @@
+extern "C" int call_from_c();
+
+int c_lib_get_value() {
+  return call_from_c();
+}
diff --git a/examples/rust_example/c_main.cc b/examples/rust_example/c_main.cc
new file mode 100644
index 0000000..23a868e
--- /dev/null
+++ b/examples/rust_example/c_main.cc
@@ -0,0 +1,13 @@
+#include <stdio.h>
+
+int c_lib_get_value();
+
+int main() {
+  int val = c_lib_get_value();
+  printf("Value from c_lib (via Rust): %d\n", val);
+  if (val == 42) {
+    printf("SUCCESS!\n");
+    return 0;
+  }
+  return 1;
+}
diff --git a/examples/rust_example/hello_world/foo/src/lib.rs b/examples/rust_example/hello_world/foo/src/lib.rs
index 73c615c..d4d2060 100644
--- a/examples/rust_example/hello_world/foo/src/lib.rs
+++ b/examples/rust_example/hello_world/foo/src/lib.rs
@@ -8,4 +8,9 @@
     pub fn new(s: &'static str) -> Foo {
         Foo{s: s, i: "foo"}
     }
+}
+
+#[no_mangle]
+pub extern "C" fn call_from_c() -> i32 {
+    42
 }
\ No newline at end of file
diff --git a/src/gn/ninja_c_binary_target_writer_unittest.cc b/src/gn/ninja_c_binary_target_writer_unittest.cc
index 161845c..01df8b5 100644
--- a/src/gn/ninja_c_binary_target_writer_unittest.cc
+++ b/src/gn/ninja_c_binary_target_writer_unittest.cc
@@ -615,6 +615,121 @@
   }
 }
 
+TEST_F(NinjaCBinaryTargetWriterTest, CompleteStaticLibraryWithRustDep) {
+  TestWithScope setup;
+  Err err;
+
+  Target rust_lib(setup.settings(), setup.ParseLabel("//foo:rust_lib"));
+  rust_lib.set_output_type(Target::RUST_LIBRARY);
+  rust_lib.visibility().SetPublic();
+  rust_lib.sources().push_back(SourceFile("//foo/lib.rs"));
+  rust_lib.source_types_used().Set(SourceFile::SOURCE_RS);
+  rust_lib.rust_values().crate_name() = "rust_lib";
+  SourceFile crate_root("//foo/lib.rs");
+  rust_lib.rust_values().set_crate_root(crate_root);
+  rust_lib.SetToolchain(setup.toolchain());
+  ASSERT_TRUE(rust_lib.OnResolved(&err));
+
+  TestTarget target(setup, "//foo:bar", Target::STATIC_LIBRARY);
+  target.sources().push_back(SourceFile("//foo/input1.cc"));
+  target.source_types_used().Set(SourceFile::SOURCE_CPP);
+  target.set_complete_static_lib(true);
+  target.public_deps().push_back(LabelTargetPair(&rust_lib));
+
+  ASSERT_TRUE(target.OnResolved(&err));
+
+  std::ostringstream out;
+  NinjaCBinaryTargetWriter writer(&target, out);
+  writer.Run();
+
+  const char expected[] =
+      "defines =\n"
+      "include_dirs =\n"
+      "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"
+      "build obj/foo/libbar.input1.o: cxx ../../foo/input1.cc\n"
+      "  source_file_part = input1.cc\n"
+      "  source_name_part = input1\n"
+      "\n"
+      "build obj/foo/libbar.a: alink obj/foo/libbar.input1.o | "
+      "obj/foo/librust_lib.rlib\n"
+      "  arflags =\n"
+      "  output_extension =\n"
+      "  output_dir =\n"
+      "  rlibs = obj/foo/librust_lib.rlib\n";
+  std::string out_str = out.str();
+  EXPECT_EQ(expected, out_str);
+}
+
+TEST_F(NinjaCBinaryTargetWriterTest,
+       CompleteStaticLibraryWithRustDepPropagation) {
+  TestWithScope setup;
+  Err err;
+
+  Target rust_lib(setup.settings(), setup.ParseLabel("//foo:rust_lib"));
+  rust_lib.set_output_type(Target::RUST_LIBRARY);
+  rust_lib.visibility().SetPublic();
+  rust_lib.sources().push_back(SourceFile("//foo/lib.rs"));
+  rust_lib.source_types_used().Set(SourceFile::SOURCE_RS);
+  rust_lib.rust_values().crate_name() = "rust_lib";
+  SourceFile crate_root("//foo/lib.rs");
+  rust_lib.rust_values().set_crate_root(crate_root);
+  rust_lib.SetToolchain(setup.toolchain());
+  ASSERT_TRUE(rust_lib.OnResolved(&err));
+
+  Target c_lib(setup.settings(), setup.ParseLabel("//foo:c_lib"));
+  c_lib.set_output_type(Target::STATIC_LIBRARY);
+  c_lib.visibility().SetPublic();
+  c_lib.sources().push_back(SourceFile("//foo/lib.cc"));
+  c_lib.source_types_used().Set(SourceFile::SOURCE_CPP);
+  c_lib.set_complete_static_lib(true);
+  c_lib.public_deps().push_back(LabelTargetPair(&rust_lib));
+  c_lib.SetToolchain(setup.toolchain());
+  ASSERT_TRUE(c_lib.OnResolved(&err));
+
+  TestTarget target(setup, "//foo:main", Target::EXECUTABLE);
+  target.sources().push_back(SourceFile("//foo/main.cc"));
+  target.source_types_used().Set(SourceFile::SOURCE_CPP);
+  target.private_deps().push_back(LabelTargetPair(&c_lib));
+
+  ASSERT_TRUE(target.OnResolved(&err));
+
+  std::ostringstream out;
+  NinjaCBinaryTargetWriter writer(&target, out);
+  writer.Run();
+
+  const char expected[] =
+      "defines =\n"
+      "include_dirs =\n"
+      "cflags =\n"
+      "cflags_cc =\n"
+      "root_out_dir = .\n"
+      "target_gen_dir = gen/foo\n"
+      "target_out_dir = obj/foo\n"
+      "target_output_name = main\n"
+      "\n"
+      "build obj/foo/main.main.o: cxx ../../foo/main.cc\n"
+      "  source_file_part = main.cc\n"
+      "  source_name_part = main\n"
+      "\n"
+      "build ./main: link obj/foo/main.main.o obj/foo/libc_lib.a | "
+      "obj/foo/librust_lib.rlib\n"
+      "  ldflags =\n"
+      "  libs =\n"
+      "  frameworks =\n"
+      "  swiftmodules =\n"
+      "  output_extension =\n"
+      "  output_dir =\n"
+      "  rlibs = obj/foo/librust_lib.rlib\n";
+  std::string out_str = out.str();
+  EXPECT_EQ(expected, out_str);
+}
+
 // This tests that output extension and output dir overrides apply, and input
 // dependencies are applied.
 TEST_F(NinjaCBinaryTargetWriterTest, OutputExtensionAndInputDeps) {
diff --git a/src/gn/resolved_target_data.cc b/src/gn/resolved_target_data.cc
index 7b4a495..628e6a4 100644
--- a/src/gn/resolved_target_data.cc
+++ b/src/gn/resolved_target_data.cc
@@ -177,7 +177,8 @@
       // inherited.
       const TargetInfo* dep_info = GetTargetInheritedLibs(dep);
       for (const auto& pair : dep_info->inherited_libs) {
-        if (pair.target()->IsFinal())
+        if (pair.target()->IsFinal() ||
+            pair.target()->output_type() == Target::RUST_LIBRARY)
           inherited_libraries->Append(pair.target(),
                                       is_public && pair.is_public());
       }