Repository: rust-lang/cc-rs
Pull request #1456 (2025-04-08 14:53:05)

support "vxworks" and "nto" OSes on `get_base_archiver_variant`
Extends the archiver detection logic to support the "vxworks" and "nto" operating systems. This change will allow us to remove [this custom bootstrap logic](https://github.com/rust-lang/rust/blob/e5fefc359bec532134013baaabc92560bfb61578/src/bootstrap/src/utils/cc_detect.rs#L32-L60) and rely entirely on the `cc` crate as it already supports all other AR finding conditions handled by bootstrap. You can try [this patch](https://github.com/onur-ozkan/rust/commit/b34017484f2139c5639c0e7e87412299549696a8) to test this.

The original PRs that introduced these conditions in the bootstrap are:

- For "vxworks": https://github.com/rust-lang/rust/pull/61946
- For "nto": https://github.com/rust-lang/rust/pull/134211

onur-ozkan: Thanks for the review, I will address your notes once I get some free time (probably around the weekend).
onur-ozkan: Any updates on this? It's blocking one of the FIXMEs on rustc bootstrap.
onur-ozkan: > Thanks, next release is scheduled for this Friday 😄

It would be super nice if we can land this before the release happens. :smile: 

Patch:
diff --git a/src/lib.rs b/src/lib.rs
index 260b850..05fec22 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -2139,8 +2139,8 @@ impl Build {
                     // QNX 8.0: https://www.qnx.com/developers/docs/8.0/com.qnx.doc.neutrino.utilities/topic/q/qcc.html
                     // This assumes qcc/q++ as compiler, which is currently the only supported compiler for QNX.
                     // See for details: https://github.com/rust-lang/cc-rs/pull/1319
-                    let arg = match target.arch {
-                        "i586" => "-Vgcc_ntox86_cxx",
+                    let arg = match target.full_arch {
+                        "x86" | "i586" => "-Vgcc_ntox86_cxx",
                         "aarch64" => "-Vgcc_ntoaarch64le_cxx",
                         "x86_64" => "-Vgcc_ntox86_64_cxx",
                         _ => {
@@ -3361,6 +3361,24 @@ impl Build {
                     // Use the GNU-variant to match other Unix systems.
                     name = format!("g{}", tool).into();
                     self.cmd(&name)
+                } else if target.os == "vxworks" {
+                    name = format!("wr-{}", tool).into();
+                    self.cmd(&name)
+                } else if target.os == "nto" {
+                    // Ref: https://www.qnx.com/developers/docs/8.0/com.qnx.doc.neutrino.utilities/topic/a/ar.html
+                    name = match target.full_arch {
+                        "i586" => format!("ntox86-{}", tool).into(),
+                        "x86" | "aarch64" | "x86_64" => {
+                            format!("nto{}-{}", target.arch, tool).into()
+                        }
+                        _ => {
+                            return Err(Error::new(
+                                ErrorKind::InvalidTarget,
+                                format!("Unknown architecture for Neutrino QNX: {}", target.arch),
+                            ))
+                        }
+                    };
+                    self.cmd(&name)
                 } else if self.get_is_cross_compile()? {
                     match self.prefix_for_target(&self.get_raw_target()?) {
                         Some(prefix) => {


Test patch:
diff --git a/tests/archiver.rs b/tests/archiver.rs
new file mode 100644
index 0000000..f94f81a
--- /dev/null
+++ b/tests/archiver.rs
@@ -0,0 +1,65 @@
+use std::env;
+
+#[test]
+fn main() {
+    unsafe { env::set_var("AR_i586_pc_nto_qnx700", "custom-ar") };
+    let ar = get_ar_for_target("i586-pc-nto-qnx700");
+    assert_eq!(ar, "custom-ar");
+    unsafe { env::remove_var("AR_i586_pc_nto_qnx700") };
+
+    unsafe { env::set_var("AR", "custom-ar2") };
+    let ar = get_ar_for_target("x86_64-unknown-linux-gnu");
+    assert_eq!(ar, "custom-ar2");
+    unsafe { env::remove_var("AR") };
+
+    let ar = get_ar_for_target("x86_64-unknown-linux-gnu");
+    assert_eq!(ar, "ar");
+
+    let ar = get_ar_for_target("x86_64-unknown-linux-musl");
+    assert_eq!(ar, "ar");
+
+    let ar = get_ar_for_target("riscv64gc-unknown-openbsd");
+    assert_eq!(ar, "ar");
+
+    let ar = get_ar_for_target("i686-wrs-vxworks");
+    assert_eq!(ar, "wr-ar");
+
+    let ar = get_ar_for_target("i586-pc-nto-qnx700");
+    assert_eq!(ar, "ntox86-ar");
+
+    let ar = get_ar_for_target("aarch64-unknown-nto-qnx700");
+    assert_eq!(ar, "ntoaarch64-ar");
+
+    let ar = get_ar_for_target("x86_64-pc-nto-qnx710");
+    assert_eq!(ar, "ntox86_64-ar");
+
+    let ar = get_ar_for_target("wasm32-wasip1");
+    assert!(
+        // `llvm-ar` is usually an absolute path for this target, so we check it with `ends_with`.
+        ar.ends_with(&maybe_exe("llvm-ar"))
+        // If `llvm-ar` doesn't exist, the logic falls back to `ar` for this target.
+        || ar == "ar"
+    );
+
+    let ar = get_ar_for_target("riscv64-linux-android");
+    // If `llvm-ar` is not available on the system, this will fall back to `$target-ar` (e.g., `riscv64-linux-android-ar` in this case)
+    assert!(ar == "llvm-ar" || ar == "riscv64-linux-android-ar");
+}
+
+fn get_ar_for_target(target: &'static str) -> String {
+    let mut cfg = cc::Build::new();
+    cfg.host("x86_64-unknown-linux-gnu").target(target);
+    let ar = cfg.get_archiver();
+    let ar = ar.get_program().to_str().unwrap().to_string();
+    println!("cc::Build::get_archiver -> target: '{target}': resolved archiver: '{ar}'");
+    ar
+}
+
+/// Appends `.exe` to the file name on Windows systems.
+fn maybe_exe(file: &'static str) -> String {
+    if cfg!(windows) {
+        format!("{file}.exe")
+    } else {
+        file.to_owned()
+    }
+}