Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 4278100

Browse files
committedSep 7, 2024·
Apple: Rebuild when deployment target changes
Rebuild when the `*_DEPLOYMENT_TARGET` variables change when building Apple targets. This is done by: 1. Adding it as a tracked variable to `Options` to make sure it clears the incremental cache. 2. Emitting the variable in `--emit=dep-info` (`.d`) files, so that Cargo can pick up changes to it, and correctly trigger a rebuild.
1 parent 9afe713 commit 4278100

File tree

13 files changed

+217
-49
lines changed

13 files changed

+217
-49
lines changed
 

‎compiler/rustc_interface/src/passes.rs

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -391,8 +391,7 @@ fn escape_dep_filename(filename: &str) -> String {
391391

392392
// Makefile comments only need escaping newlines and `\`.
393393
// The result can be unescaped by anything that can unescape `escape_default` and friends.
394-
fn escape_dep_env(symbol: Symbol) -> String {
395-
let s = symbol.as_str();
394+
fn escape_dep_env(s: &str) -> String {
396395
let mut escaped = String::with_capacity(s.len());
397396
for c in s.chars() {
398397
match c {
@@ -492,16 +491,22 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P
492491

493492
// Emit special comments with information about accessed environment variables.
494493
let env_depinfo = sess.psess.env_depinfo.borrow();
494+
495+
// We will soon sort, so the initial order does not matter.
496+
#[allow(rustc::potential_query_instability)]
497+
let mut env_depinfo: Vec<_> = env_depinfo
498+
.iter()
499+
.map(|(k, v)| (escape_dep_env(k.as_str()), v.map(|v| escape_dep_env(v.as_str()))))
500+
.chain(tcx.sess.target.is_like_osx.then(|| {
501+
// On Apple targets, we also depend on the deployment target environment variable.
502+
let name = rustc_target::spec::apple_deployment_target_env(&tcx.sess.target.os);
503+
(name.into(), std::env::var(name).ok().map(|var| escape_dep_env(&var)))
504+
}))
505+
.collect();
506+
env_depinfo.sort_unstable();
495507
if !env_depinfo.is_empty() {
496-
// We will soon sort, so the initial order does not matter.
497-
#[allow(rustc::potential_query_instability)]
498-
let mut envs: Vec<_> = env_depinfo
499-
.iter()
500-
.map(|(k, v)| (escape_dep_env(*k), v.map(escape_dep_env)))
501-
.collect();
502-
envs.sort_unstable();
503508
writeln!(file)?;
504-
for (k, v) in envs {
509+
for (k, v) in env_depinfo {
505510
write!(file, "# env-dep:{k}")?;
506511
if let Some(v) = v {
507512
write!(file, "={v}")?;

‎compiler/rustc_session/src/config.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1188,6 +1188,7 @@ impl Default for Options {
11881188
color: ColorConfig::Auto,
11891189
logical_env: FxIndexMap::default(),
11901190
verbose: false,
1191+
apple_deployment_target_env: None,
11911192
}
11921193
}
11931194
}
@@ -2734,6 +2735,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M
27342735
color,
27352736
logical_env,
27362737
verbose,
2738+
apple_deployment_target_env: None,
27372739
}
27382740
}
27392741

‎compiler/rustc_session/src/options.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,15 @@ top_level_options!(
222222
color: ColorConfig [UNTRACKED],
223223

224224
verbose: bool [TRACKED_NO_CRATE_HASH],
225+
226+
/// The raw value of the `*_DEPLOYMENT_TARGET` environment variable
227+
/// for the selected target OS.
228+
///
229+
/// The exact environment variable to use depends on the target that
230+
/// the user has chosen, and we do not want to re-compile if an
231+
/// unrelated deployment target environment variable changed, so we
232+
/// defer the initialization of this to `build_session`.
233+
apple_deployment_target_env: Option<String> [TRACKED],
225234
}
226235
);
227236

‎compiler/rustc_session/src/session.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -995,7 +995,7 @@ fn default_emitter(
995995
#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
996996
pub fn build_session(
997997
early_dcx: EarlyDiagCtxt,
998-
sopts: config::Options,
998+
mut sopts: config::Options,
999999
io: CompilerIO,
10001000
bundle: Option<Lrc<rustc_errors::FluentBundle>>,
10011001
registry: rustc_errors::registry::Registry,
@@ -1099,6 +1099,13 @@ pub fn build_session(
10991099

11001100
let asm_arch = if target.allow_asm { InlineAsmArch::from_str(&target.arch).ok() } else { None };
11011101

1102+
// Configure the deployment target for change-tracking, now that target
1103+
// details are available.
1104+
if target.is_like_osx {
1105+
let name = rustc_target::spec::apple_deployment_target_env(&target.os);
1106+
sopts.apple_deployment_target_env = std::env::var(name).ok();
1107+
}
1108+
11021109
let sess = Session {
11031110
target,
11041111
host,

‎compiler/rustc_target/src/spec/base/apple/mod.rs

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,19 @@ pub fn platform(target: &Target) -> Option<u32> {
283283
})
284284
}
285285

286+
/// Name of the environment variable used to fetch the deployment target on
287+
/// the given OS.
288+
pub fn deployment_target_env(os: &str) -> &'static str {
289+
match os {
290+
"macos" => "MACOSX_DEPLOYMENT_TARGET",
291+
"ios" => "IPHONEOS_DEPLOYMENT_TARGET",
292+
"watchos" => "WATCHOS_DEPLOYMENT_TARGET",
293+
"tvos" => "TVOS_DEPLOYMENT_TARGET",
294+
"visionos" => "XROS_DEPLOYMENT_TARGET",
295+
_ => unreachable!("tried to get deployment target env var for non-Apple platform"),
296+
}
297+
}
298+
286299
/// Hack for calling `deployment_target` outside of this module.
287300
pub fn deployment_target_for_target(target: &Target) -> (u16, u8, u8) {
288301
let arch = if target.llvm_target.starts_with("arm64e") {
@@ -332,17 +345,13 @@ fn deployment_target(os: &str, arch: Arch, abi: TargetAbi) -> (u16, u8, u8) {
332345
_ => os_min,
333346
};
334347

335-
// The environment variable used to fetch the deployment target.
336-
let env_var = match os {
337-
"macos" => "MACOSX_DEPLOYMENT_TARGET",
338-
"ios" => "IPHONEOS_DEPLOYMENT_TARGET",
339-
"watchos" => "WATCHOS_DEPLOYMENT_TARGET",
340-
"tvos" => "TVOS_DEPLOYMENT_TARGET",
341-
"visionos" => "XROS_DEPLOYMENT_TARGET",
342-
_ => unreachable!("tried to get deployment target env var for non-Apple platform"),
343-
};
344-
345-
if let Ok(deployment_target) = env::var(env_var) {
348+
// NOTE: We access the deployment target environment variable here, which
349+
// makes the variable an **implicit** input which affects compilation.
350+
//
351+
// We make sure to rebuild when the variable changes, both by busting the
352+
// incremental cache, and by telling Cargo that it is a dependency.
353+
// Search for usages of `deployment_target_env` to see how.
354+
if let Ok(deployment_target) = env::var(deployment_target_env(os)) {
346355
match parse_version(&deployment_target) {
347356
// It is common that the deployment target is set too low, e.g. on
348357
// macOS Aarch64 to also target older x86_64, the user may set a

‎compiler/rustc_target/src/spec/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ pub mod crt_objects;
6060

6161
mod base;
6262
pub use base::apple::{
63+
deployment_target_env as apple_deployment_target_env,
6364
deployment_target_for_target as current_apple_deployment_target,
6465
platform as current_apple_platform, sdk_version as current_apple_sdk_version,
6566
};

‎src/tools/run-make-support/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ pub use env::{env_var, env_var_os, set_current_dir};
7373
pub use run::{cmd, run, run_fail, run_with_args};
7474

7575
/// Helpers for checking target information.
76-
pub use targets::{is_darwin, is_msvc, is_windows, llvm_components_contain, target, uname};
76+
pub use targets::{is_darwin, is_msvc, is_windows, llvm_components_contain, target, uname, apple_os};
7777

7878
/// Helpers for building names of output artifacts that are potentially target-specific.
7979
pub use artifact_names::{

‎src/tools/run-make-support/src/targets.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,24 @@ pub fn is_darwin() -> bool {
2828
target().contains("darwin")
2929
}
3030

31+
/// Get the target OS on Apple operating systems.
32+
#[must_use]
33+
pub fn apple_os() -> &'static str {
34+
if target().contains("darwin") {
35+
"macos"
36+
} else if target().contains("ios") {
37+
"ios"
38+
} else if target().contains("tvos") {
39+
"tvos"
40+
} else if target().contains("watchos") {
41+
"watchos"
42+
} else if target().contains("visionos") {
43+
"visionos"
44+
} else {
45+
panic!("not an Apple OS")
46+
}
47+
}
48+
3149
/// Check if `component` is within `LLVM_COMPONENTS`
3250
#[must_use]
3351
pub fn llvm_components_contain(component: &str) -> bool {

‎src/tools/tidy/src/allowed_run_make_makefiles.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ run-make/incr-add-rust-src-component/Makefile
66
run-make/issue-84395-lto-embed-bitcode/Makefile
77
run-make/jobserver-error/Makefile
88
run-make/libs-through-symlinks/Makefile
9-
run-make/macos-deployment-target/Makefile
109
run-make/split-debuginfo/Makefile
1110
run-make/symbol-mangling-hashed/Makefile
1211
run-make/translation/Makefile
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
fn main() {}
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
//! Test codegen when setting deployment targets 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/pull/105123.
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 `minos` 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 - these have a `version` field, so also check that.
16+
#[track_caller]
17+
fn minos(file: &str, version: &str) {
18+
cmd("vtool")
19+
.arg("-show-build")
20+
.arg(file)
21+
.run()
22+
.assert_stdout_contains_regex(format!("(minos|version) {version}"));
23+
}
24+
25+
fn main() {
26+
// These versions should generally be higher than the default versions
27+
let (env_var, example_version, higher_example_version) = match apple_os() {
28+
"macos" => ("MACOSX_DEPLOYMENT_TARGET", "12.0", "13.0"),
29+
// armv7s-apple-ios and i386-apple-ios only supports iOS 10.0
30+
"ios" if target() == "armv7s-apple-ios" || target() == "i386-apple-ios" => {
31+
("IPHONEOS_DEPLOYMENT_TARGET", "10.0", "10.0")
32+
}
33+
"ios" => ("IPHONEOS_DEPLOYMENT_TARGET", "15.0", "16.0"),
34+
"watchos" => ("WATCHOS_DEPLOYMENT_TARGET", "7.0", "9.0"),
35+
"tvos" => ("TVOS_DEPLOYMENT_TARGET", "14.0", "15.0"),
36+
"visionos" => ("XROS_DEPLOYMENT_TARGET", "1.1", "1.2"),
37+
_ => unreachable!(),
38+
};
39+
let default_version =
40+
rustc().target(target()).env_remove(env_var).print("deployment-target").run().stdout_utf8();
41+
let default_version = default_version.strip_prefix("deployment_target=").unwrap().trim();
42+
43+
// Test that version makes it to the object file.
44+
run_in_tmpdir(|| {
45+
let mut rustc = rustc();
46+
rustc.target(target());
47+
rustc.crate_type("lib");
48+
rustc.emit("obj");
49+
rustc.input("foo.rs");
50+
rustc.output("foo.o");
51+
52+
rustc.env(env_var, example_version).run();
53+
minos("foo.o", example_version);
54+
55+
// FIXME(madsmtm): Doesn't work on Mac Catalyst and the simulator.
56+
if !target().contains("macabi") && !target().contains("sim") {
57+
rustc.env_remove(env_var).run();
58+
minos("foo.o", default_version);
59+
}
60+
});
61+
62+
// Test that version makes it to the linker when linking dylibs.
63+
run_in_tmpdir(|| {
64+
// Certain watchOS targets don't support dynamic linking, so we disable the test on those.
65+
if apple_os() == "watchos" {
66+
return;
67+
}
68+
69+
let mut rustc = rustc();
70+
rustc.target(target());
71+
rustc.crate_type("dylib");
72+
rustc.input("foo.rs");
73+
rustc.output("libfoo.dylib");
74+
75+
rustc.env(env_var, example_version).run();
76+
minos("libfoo.dylib", example_version);
77+
78+
// FIXME(madsmtm): Deployment target is not currently passed properly to linker
79+
// rustc.env_remove(env_var).run();
80+
// minos("libfoo.dylib", default_version);
81+
82+
// Test with ld64 instead
83+
rustc.arg("-Clinker-flavor=ld");
84+
85+
rustc.env(env_var, example_version).run();
86+
minos("libfoo.dylib", example_version);
87+
88+
rustc.env_remove(env_var).run();
89+
minos("libfoo.dylib", default_version);
90+
});
91+
92+
// Test that version makes it to the linker when linking executables.
93+
run_in_tmpdir(|| {
94+
let mut rustc = rustc();
95+
rustc.target(target());
96+
rustc.crate_type("bin");
97+
rustc.input("foo.rs");
98+
rustc.output("foo");
99+
100+
// FIXME(madsmtm): Doesn't work on watchOS for some reason?
101+
if !target().contains("watchos") {
102+
rustc.env(env_var, example_version).run();
103+
minos("foo", example_version);
104+
105+
// FIXME(madsmtm): Deployment target is not currently passed properly to linker
106+
// rustc.env_remove(env_var).run();
107+
// minos("foo", default_version);
108+
}
109+
110+
// Test with ld64 instead
111+
rustc.arg("-Clinker-flavor=ld");
112+
113+
rustc.env(env_var, example_version).run();
114+
minos("foo", example_version);
115+
116+
rustc.env_remove(env_var).run();
117+
minos("foo", default_version);
118+
});
119+
120+
// Test that changing the deployment target busts the incremental cache.
121+
run_in_tmpdir(|| {
122+
let mut rustc = rustc();
123+
rustc.target(target());
124+
rustc.incremental("incremental");
125+
rustc.crate_type("lib");
126+
rustc.emit("obj");
127+
rustc.input("foo.rs");
128+
rustc.output("foo.o");
129+
130+
rustc.env(env_var, example_version).run();
131+
minos("foo.o", example_version);
132+
133+
rustc.env(env_var, higher_example_version).run();
134+
minos("foo.o", higher_example_version);
135+
136+
// FIXME(madsmtm): Doesn't work on Mac Catalyst and the simulator.
137+
if !target().contains("macabi") && !target().contains("sim") {
138+
rustc.env_remove(env_var).run();
139+
minos("foo.o", default_version);
140+
}
141+
});
142+
}

‎tests/run-make/macos-deployment-target/Makefile

Lines changed: 0 additions & 21 deletions
This file was deleted.

‎tests/run-make/macos-deployment-target/with_deployment_target.rs

Lines changed: 0 additions & 4 deletions
This file was deleted.

0 commit comments

Comments
 (0)
Please sign in to comment.