Fix Windows GNU staticlib output naming (#4231)

## Summary

Make Rust static-library naming ABI-aware on Windows.

Rust emits `lib<crate>.a` for the GNU and GNULLVM Windows ABIs, while
MSVC emits `<crate>.lib`. The generated Rust toolchain currently selects
`.lib` from the operating system alone, and `determine_lib_name` removes
the `lib` prefix for every Windows static library. As a result, Bazel
declares an output different from the file emitted by rustc for Windows
GNU-like targets.

This change passes the target ABI through repository and toolchain
generation, selects `.a` for Windows GNU/GNULLVM static libraries, and
preserves their `lib` prefix. MSVC behavior and Windows dynamic-library
naming remain unchanged.

Regression coverage exercises the extension mapping, rendered toolchain
metadata, final GNU/GNULLVM/MSVC static-library names, and the unchanged
GNU `cdylib` name.
diff --git a/rust/platform/triple_mappings.bzl b/rust/platform/triple_mappings.bzl
index 7be462f..d6657c0 100644
--- a/rust/platform/triple_mappings.bzl
+++ b/rust/platform/triple_mappings.bzl
@@ -435,7 +435,9 @@
 def system_to_dylib_ext(system):
     return _SYSTEM_TO_DYLIB_EXT[system]
 
-def system_to_staticlib_ext(system):
+def system_to_staticlib_ext(system, abi = None):
+    if system == "windows" and abi in ("gnu", "gnullvm"):
+        return ".a"
     return _SYSTEM_TO_STATICLIB_EXT[system]
 
 def system_to_binary_ext(system):
diff --git a/rust/private/repository_utils.bzl b/rust/private/repository_utils.bzl
index e28165c..f8001cb 100644
--- a/rust/private/repository_utils.bzl
+++ b/rust/private/repository_utils.bzl
@@ -114,7 +114,7 @@
     """
     content = [_build_file_for_compiler_template.format(
         binary_ext = system_to_binary_ext(target_triple.system),
-        staticlib_ext = system_to_staticlib_ext(target_triple.system),
+        staticlib_ext = system_to_staticlib_ext(target_triple.system, target_triple.abi),
         dylib_ext = system_to_dylib_ext(target_triple.system),
         target_triple = target_triple.str,
     )]
@@ -123,7 +123,7 @@
         content.append(
             _build_file_for_linker_template.format(
                 binary_ext = system_to_binary_ext(target_triple.system),
-                staticlib_ext = system_to_staticlib_ext(target_triple.system),
+                staticlib_ext = system_to_staticlib_ext(target_triple.system, target_triple.abi),
                 dylib_ext = system_to_dylib_ext(target_triple.system),
                 target_triple = target_triple.str,
             ),
@@ -330,7 +330,7 @@
     """
     return _build_file_for_stdlib_template.format(
         binary_ext = system_to_binary_ext(target_triple.system),
-        staticlib_ext = system_to_staticlib_ext(target_triple.system),
+        staticlib_ext = system_to_staticlib_ext(target_triple.system, target_triple.abi),
         dylib_ext = system_to_dylib_ext(target_triple.system),
         target_triple = target_triple.str,
     )
@@ -462,7 +462,7 @@
     return _build_file_for_rust_toolchain_template.format(
         toolchain_name = name,
         binary_ext = system_to_binary_ext(target_triple.system),
-        staticlib_ext = system_to_staticlib_ext(target_triple.system),
+        staticlib_ext = system_to_staticlib_ext(target_triple.system, target_triple.abi),
         dylib_ext = system_to_dylib_ext(target_triple.system),
         allocator_library = repr(allocator_library_label),
         global_allocator_library = repr(global_allocator_library_label),
diff --git a/rust/private/utils.bzl b/rust/private/utils.bzl
index e54e513..476651a 100644
--- a/rust/private/utils.bzl
+++ b/rust/private/utils.bzl
@@ -863,7 +863,11 @@
               "please file an issue!").format(crate_type))
 
     prefix = "lib"
-    if toolchain.target_triple and toolchain.target_os == "windows" and crate_type not in ("lib", "rlib"):
+    if (toolchain.target_triple and
+        toolchain.target_os == "windows" and
+        crate_type not in ("lib", "rlib") and
+        (crate_type != "staticlib" or
+         toolchain.target_abi not in ("gnu", "gnullvm"))):
         prefix = ""
     if toolchain.target_arch in ("wasm32", "wasm64") and crate_type == "cdylib":
         prefix = ""
diff --git a/test/unit/platform_triple/platform_triple_test.bzl b/test/unit/platform_triple/platform_triple_test.bzl
index 0248782..ff69c4c 100644
--- a/test/unit/platform_triple/platform_triple_test.bzl
+++ b/test/unit/platform_triple/platform_triple_test.bzl
@@ -169,11 +169,22 @@
 
     return unittest.end(env)
 
+def _windows_staticlib_ext_test_impl(ctx):
+    env = unittest.begin(ctx)
+
+    asserts.equals(env, ".a", system_to_staticlib_ext("windows", "gnu"))
+    asserts.equals(env, ".a", system_to_staticlib_ext("windows", "gnullvm"))
+    asserts.equals(env, ".lib", system_to_staticlib_ext("windows", "msvc"))
+    asserts.equals(env, ".lib", system_to_staticlib_ext("windows"))
+
+    return unittest.end(env)
+
 construct_platform_triple_test = unittest.make(_construct_platform_triple_test_impl)
 construct_minimal_platform_triple_test = unittest.make(_construct_minimal_platform_triple_test_impl)
 supported_platform_triples_test = unittest.make(_supported_platform_triples_test_impl)
 construct_known_triples_test = unittest.make(_construct_known_triples_test_impl)
 wasm_staticlib_ext_test = unittest.make(_wasm_staticlib_ext_test_impl)
+windows_staticlib_ext_test = unittest.make(_windows_staticlib_ext_test_impl)
 
 def platform_triple_test_suite(name, **kwargs):
     """Define a test suite for testing the `triple` constructor
@@ -197,6 +208,9 @@
     wasm_staticlib_ext_test(
         name = "wasm_staticlib_ext_test",
     )
+    windows_staticlib_ext_test(
+        name = "windows_staticlib_ext_test",
+    )
 
     native.test_suite(
         name = name,
@@ -206,6 +220,7 @@
             ":supported_platform_triples_test",
             ":construct_known_triples_test",
             ":wasm_staticlib_ext_test",
+            ":windows_staticlib_ext_test",
         ],
         **kwargs
     )
diff --git a/test/unit/windows_lib_name/windows_lib_name_test.bzl b/test/unit/windows_lib_name/windows_lib_name_test.bzl
index 83d2726..a854247 100644
--- a/test/unit/windows_lib_name/windows_lib_name_test.bzl
+++ b/test/unit/windows_lib_name/windows_lib_name_test.bzl
@@ -1,12 +1,12 @@
 """Analysistests for Windows-specific library naming and link flags."""
 
-load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts")
+load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts", "unittest")
 
 # buildifier: disable=bzl-visibility
 load("//rust/private:rustc.bzl", "portable_link_flags", "symlink_for_ambiguous_lib")
 
 # buildifier: disable=bzl-visibility
-load("//rust/private:utils.bzl", "get_lib_name_default", "get_lib_name_for_windows")
+load("//rust/private:utils.bzl", "determine_lib_name", "get_lib_name_default", "get_lib_name_for_windows")
 
 # buildifier: disable=provider-params
 LinkFlagsInfo = provider(fields = {"flags": "List[str]"})
@@ -125,6 +125,72 @@
 
 symlink_name_windows_msvc_test = analysistest.make(_symlink_name_windows_msvc_test_impl)
 
+def _windows_toolchain(abi, staticlib_ext):
+    return struct(
+        dylib_ext = ".dll",
+        staticlib_ext = staticlib_ext,
+        target_abi = abi,
+        target_arch = "x86_64",
+        target_os = "windows",
+        target_triple = "x86_64-pc-windows-{}".format(abi),
+    )
+
+def _staticlib_name_windows_gnu_test_impl(ctx):
+    env = unittest.begin(ctx)
+
+    actual = determine_lib_name(
+        name = "native_dep",
+        crate_type = "staticlib",
+        toolchain = _windows_toolchain("gnu", ".a"),
+    )
+
+    asserts.equals(env, "libnative_dep.a", actual)
+    return unittest.end(env)
+
+staticlib_name_windows_gnu_test = unittest.make(_staticlib_name_windows_gnu_test_impl)
+
+def _staticlib_name_windows_gnullvm_test_impl(ctx):
+    env = unittest.begin(ctx)
+
+    actual = determine_lib_name(
+        name = "native_dep",
+        crate_type = "staticlib",
+        toolchain = _windows_toolchain("gnullvm", ".a"),
+    )
+
+    asserts.equals(env, "libnative_dep.a", actual)
+    return unittest.end(env)
+
+staticlib_name_windows_gnullvm_test = unittest.make(_staticlib_name_windows_gnullvm_test_impl)
+
+def _staticlib_name_windows_msvc_test_impl(ctx):
+    env = unittest.begin(ctx)
+
+    actual = determine_lib_name(
+        name = "native_dep",
+        crate_type = "staticlib",
+        toolchain = _windows_toolchain("msvc", ".lib"),
+    )
+
+    asserts.equals(env, "native_dep.lib", actual)
+    return unittest.end(env)
+
+staticlib_name_windows_msvc_test = unittest.make(_staticlib_name_windows_msvc_test_impl)
+
+def _cdylib_name_windows_gnu_test_impl(ctx):
+    env = unittest.begin(ctx)
+
+    actual = determine_lib_name(
+        name = "native_dep",
+        crate_type = "cdylib",
+        toolchain = _windows_toolchain("gnu", ".a"),
+    )
+
+    asserts.equals(env, "native_dep.dll", actual)
+    return unittest.end(env)
+
+cdylib_name_windows_gnu_test = unittest.make(_cdylib_name_windows_gnu_test_impl)
+
 def _define_targets():
     portable_link_flags_probe(
         name = "portable_link_flags_windows_gnu_probe",
@@ -172,6 +238,18 @@
         name = "symlink_name_windows_msvc_test",
         target_under_test = ":symlink_windows_msvc_probe",
     )
+    staticlib_name_windows_gnu_test(
+        name = "staticlib_name_windows_gnu_test",
+    )
+    staticlib_name_windows_gnullvm_test(
+        name = "staticlib_name_windows_gnullvm_test",
+    )
+    staticlib_name_windows_msvc_test(
+        name = "staticlib_name_windows_msvc_test",
+    )
+    cdylib_name_windows_gnu_test(
+        name = "cdylib_name_windows_gnu_test",
+    )
 
     native.test_suite(
         name = name,
@@ -180,5 +258,9 @@
             ":portable_link_flags_windows_msvc_test",
             ":symlink_name_windows_gnu_test",
             ":symlink_name_windows_msvc_test",
+            ":staticlib_name_windows_gnu_test",
+            ":staticlib_name_windows_gnullvm_test",
+            ":staticlib_name_windows_msvc_test",
+            ":cdylib_name_windows_gnu_test",
         ],
     )
diff --git a/test/unit/windows_stdlib/windows_stdlib_test.bzl b/test/unit/windows_stdlib/windows_stdlib_test.bzl
index d8c9978..67125e2 100644
--- a/test/unit/windows_stdlib/windows_stdlib_test.bzl
+++ b/test/unit/windows_stdlib/windows_stdlib_test.bzl
@@ -63,9 +63,19 @@
     )
     asserts.true(
         env,
+        'staticlib_ext = ".lib",' in rendered_msvc,
+        "MSVC toolchain should render a .lib staticlib extension:\n%s" % rendered_msvc,
+    )
+    asserts.true(
+        env,
         'stdlib_linkflags = ["-lws2_32", "-luserenv", "-lbcrypt", "-lntdll", "-lsynchronization"],' in rendered_gnu,
         "GNU toolchain should render -l stdlib linkflags:\n%s" % rendered_gnu,
     )
+    asserts.true(
+        env,
+        'staticlib_ext = ".a",' in rendered_gnu,
+        "GNU toolchain should render a .a staticlib extension:\n%s" % rendered_gnu,
+    )
 
     return analysistest.end(env)