| 1 | use std::fs; |
| 2 | use std::path::PathBuf; |
| 3 | use std::process::Command; |
| 4 | |
| 5 | fn have_xcrun() -> bool { |
| 6 | Command::new("xcrun") |
| 7 | .arg("-f") |
| 8 | .arg("as") |
| 9 | .output() |
| 10 | .map(|o| o.status.success()) |
| 11 | .unwrap_or(false) |
| 12 | } |
| 13 | |
| 14 | fn assemble(src: &str, out: &PathBuf) -> Result<(), String> { |
| 15 | let tmp = std::env::temp_dir().join(format!( |
| 16 | "afs-ld-cli-diag-{}-{}.s", |
| 17 | std::process::id(), |
| 18 | out.file_stem().and_then(|s| s.to_str()).unwrap_or("t") |
| 19 | )); |
| 20 | fs::write(&tmp, src).map_err(|e| format!("write: {e}"))?; |
| 21 | let output = Command::new("xcrun") |
| 22 | .args(["--sdk", "macosx", "as", "-arch", "arm64"]) |
| 23 | .arg(&tmp) |
| 24 | .arg("-o") |
| 25 | .arg(out) |
| 26 | .output() |
| 27 | .map_err(|e| format!("spawn xcrun as: {e}"))?; |
| 28 | let _ = fs::remove_file(&tmp); |
| 29 | if !output.status.success() { |
| 30 | return Err(format!( |
| 31 | "xcrun as failed: {}", |
| 32 | String::from_utf8_lossy(&output.stderr) |
| 33 | )); |
| 34 | } |
| 35 | Ok(()) |
| 36 | } |
| 37 | |
| 38 | fn scratch(name: &str) -> PathBuf { |
| 39 | std::env::temp_dir().join(format!("afs-ld-cli-diag-{}-{name}", std::process::id())) |
| 40 | } |
| 41 | |
| 42 | #[test] |
| 43 | fn undefined_symbol_diagnostic_is_not_double_prefixed() { |
| 44 | if !have_xcrun() { |
| 45 | eprintln!("skipping: xcrun as unavailable"); |
| 46 | return; |
| 47 | } |
| 48 | |
| 49 | let exe = env!("CARGO_BIN_EXE_afs-ld"); |
| 50 | let obj = scratch("missing.o"); |
| 51 | let src = r#" |
| 52 | .section __TEXT,__text,regular,pure_instructions |
| 53 | .globl _main |
| 54 | _main: |
| 55 | bl _missing |
| 56 | ret |
| 57 | .subsections_via_symbols |
| 58 | "#; |
| 59 | if let Err(e) = assemble(src, &obj) { |
| 60 | eprintln!("skipping: assemble failed: {e}"); |
| 61 | return; |
| 62 | } |
| 63 | |
| 64 | let out = Command::new(exe) |
| 65 | .arg("-o") |
| 66 | .arg(scratch("missing.out")) |
| 67 | .arg(&obj) |
| 68 | .output() |
| 69 | .expect("afs-ld should run"); |
| 70 | let stderr = String::from_utf8_lossy(&out.stderr); |
| 71 | assert!( |
| 72 | stderr.contains("afs-ld: error: undefined symbol: _missing"), |
| 73 | "missing expected undefined-symbol diagnostic:\n{stderr}" |
| 74 | ); |
| 75 | assert!( |
| 76 | !stderr.contains("afs-ld: error: afs-ld: error:"), |
| 77 | "diagnostic was double-prefixed:\n{stderr}" |
| 78 | ); |
| 79 | |
| 80 | let _ = fs::remove_file(obj); |
| 81 | } |
| 82 |