|
| 1 | +//! Test codegen when setting SDK version on Apple platforms. |
| 2 | +//! |
| 3 | +//! This is important since its a compatibility hazard. The linker will |
| 4 | +//! generate load commands differently based on what minimum OS it can assume. |
| 5 | +//! |
| 6 | +//! See https://github.com/rust-lang/rust/issues/129432. |
| 7 | +
|
| 8 | +//@ only-apple |
| 9 | + |
| 10 | +use run_make_support::{apple_os, cmd, run_in_tmpdir, rustc, target}; |
| 11 | + |
| 12 | +/// Run vtool to check the `sdk` field in LC_BUILD_VERSION. |
| 13 | +/// |
| 14 | +/// On lower deployment targets, LC_VERSION_MIN_MACOSX, LC_VERSION_MIN_IPHONEOS and similar |
| 15 | +/// are used instead of LC_BUILD_VERSION, but both name the relevant variable `sdk`. |
| 16 | +#[track_caller] |
| 17 | +fn has_sdk_version(file: &str, version: &str) { |
| 18 | + cmd("vtool") |
| 19 | + .arg("-show-build") |
| 20 | + .arg(file) |
| 21 | + .run() |
| 22 | + .assert_stdout_contains(format!("sdk {version}")); |
| 23 | +} |
| 24 | + |
| 25 | +fn main() { |
| 26 | + // Fetch rustc's inferred deployment target. |
| 27 | + let current_deployment_target = |
| 28 | + rustc().target(target()).print("deployment-target").run().stdout_utf8(); |
| 29 | + let current_deployment_target = |
| 30 | + current_deployment_target.strip_prefix("deployment_target=").unwrap().trim(); |
| 31 | + |
| 32 | + // Fetch current SDK version via. xcrun. |
| 33 | + // |
| 34 | + // Assumes a standard Xcode distribution, where e.g. the macOS SDK's Mac Catalyst |
| 35 | + // and the iPhone Simulator version is the same as for the iPhone SDK. |
| 36 | + let sdk_name = match apple_os() { |
| 37 | + "macos" => "macosx", |
| 38 | + "ios" => "iphoneos", |
| 39 | + "watchos" => "watchos", |
| 40 | + "tvos" => "appletvos", |
| 41 | + "visionos" => "xros", |
| 42 | + _ => unreachable!(), |
| 43 | + }; |
| 44 | + let current_sdk_version = |
| 45 | + cmd("xcrun").arg("--show-sdk-version").arg("--sdk").arg(sdk_name).run().stdout_utf8(); |
| 46 | + let current_sdk_version = current_sdk_version.trim(); |
| 47 | + |
| 48 | + // Check the SDK version in the object file produced by the codegen backend. |
| 49 | + rustc().target(target()).crate_type("lib").emit("obj").input("foo.rs").output("foo.o").run(); |
| 50 | + // Set to 0, which means not set or "n/a". |
| 51 | + has_sdk_version("foo.o", "n/a"); |
| 52 | + |
| 53 | + // Test that version makes it to the linker. |
| 54 | + for (crate_type, file_ext) in [("bin", ""), ("dylib", ".dylib")] { |
| 55 | + // Non-simulator watchOS targets don't support dynamic linking, |
| 56 | + // for simplicity we disable the test on all watchOS targets. |
| 57 | + if crate_type == "dylib" && apple_os() == "watchos" { |
| 58 | + continue; |
| 59 | + } |
| 60 | + |
| 61 | + // Test with clang |
| 62 | + let file_name = format!("foo_cc{file_ext}"); |
| 63 | + rustc() |
| 64 | + .target(target()) |
| 65 | + .crate_type("bin") |
| 66 | + .arg("-Clinker-flavor=gcc") |
| 67 | + .input("foo.rs") |
| 68 | + .output(&file_name) |
| 69 | + .run(); |
| 70 | + has_sdk_version(&file_name, current_sdk_version); |
| 71 | + |
| 72 | + // Test with ld64 |
| 73 | + let file_name = format!("foo_ld{file_ext}"); |
| 74 | + rustc() |
| 75 | + .target(target()) |
| 76 | + .crate_type("bin") |
| 77 | + .arg("-Clinker-flavor=ld") |
| 78 | + .input("foo.rs") |
| 79 | + .output(&file_name) |
| 80 | + .run(); |
| 81 | + // FIXME(madsmtm): This uses the current deployment target |
| 82 | + // instead of the current SDK version like Clang does. |
| 83 | + // https://github.com/rust-lang/rust/issues/129432 |
| 84 | + has_sdk_version(&file_name, current_deployment_target); |
| 85 | + } |
| 86 | +} |
0 commit comments