From c9de51dcf0b7b62d62b030682dceaca24cc7c9ec Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Wed, 21 Aug 2024 00:59:51 +0200 Subject: [PATCH] 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. --- compiler/rustc_interface/src/passes.rs | 25 +++++++++------ compiler/rustc_session/src/config.rs | 2 ++ compiler/rustc_session/src/options.rs | 9 ++++++ compiler/rustc_session/src/session.rs | 9 +++++- .../rustc_target/src/spec/base/apple/mod.rs | 31 ++++++++++++------- compiler/rustc_target/src/spec/mod.rs | 1 + 6 files changed, 55 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 96a6f52d60b6c..8151ae98cdb9e 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -391,8 +391,7 @@ fn escape_dep_filename(filename: &str) -> String { // Makefile comments only need escaping newlines and `\`. // The result can be unescaped by anything that can unescape `escape_default` and friends. -fn escape_dep_env(symbol: Symbol) -> String { - let s = symbol.as_str(); +fn escape_dep_env(s: &str) -> String { let mut escaped = String::with_capacity(s.len()); for c in s.chars() { match c { @@ -492,16 +491,22 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P // Emit special comments with information about accessed environment variables. let env_depinfo = sess.psess.env_depinfo.borrow(); + + // We will soon sort, so the initial order does not matter. + #[allow(rustc::potential_query_instability)] + let mut env_depinfo: Vec<_> = env_depinfo + .iter() + .map(|(k, v)| (escape_dep_env(k.as_str()), v.map(|v| escape_dep_env(v.as_str())))) + .chain(tcx.sess.target.is_like_osx.then(|| { + // On Apple targets, we also depend on the deployment target environment variable. + let name = rustc_target::spec::apple_deployment_target_env(&tcx.sess.target.os); + (name.into(), std::env::var(name).ok().map(|var| escape_dep_env(&var))) + })) + .collect(); + env_depinfo.sort_unstable(); if !env_depinfo.is_empty() { - // We will soon sort, so the initial order does not matter. - #[allow(rustc::potential_query_instability)] - let mut envs: Vec<_> = env_depinfo - .iter() - .map(|(k, v)| (escape_dep_env(*k), v.map(escape_dep_env))) - .collect(); - envs.sort_unstable(); writeln!(file)?; - for (k, v) in envs { + for (k, v) in env_depinfo { write!(file, "# env-dep:{k}")?; if let Some(v) = v { write!(file, "={v}")?; diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 95d171409d86d..6a259ccb272f3 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1164,6 +1164,7 @@ impl Default for Options { color: ColorConfig::Auto, logical_env: FxIndexMap::default(), verbose: false, + apple_deployment_target_env: None, } } } @@ -2710,6 +2711,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M color, logical_env, verbose, + apple_deployment_target_env: None, } } diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index df72e2430fd50..7ed20059f24ae 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -222,6 +222,15 @@ top_level_options!( color: ColorConfig [UNTRACKED], verbose: bool [TRACKED_NO_CRATE_HASH], + + /// The raw value of the `*_DEPLOYMENT_TARGET` environment variable + /// for the selected target OS. + /// + /// The exact environment variable to use depends on the target that + /// the user has chosen, and we do not want to re-compile if an + /// unrelated deployment target environment variable changed, so we + /// defer the initialization of this to `build_session`. + apple_deployment_target_env: Option [TRACKED], } ); diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 693867c3853da..4f269c9fda280 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1006,7 +1006,7 @@ fn default_emitter( #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable pub fn build_session( early_dcx: EarlyDiagCtxt, - sopts: config::Options, + mut sopts: config::Options, io: CompilerIO, bundle: Option>, registry: rustc_errors::registry::Registry, @@ -1110,6 +1110,13 @@ pub fn build_session( let asm_arch = if target.allow_asm { InlineAsmArch::from_str(&target.arch).ok() } else { None }; + // Configure the deployment target for change-tracking, now that target + // details are available. + if target.is_like_osx { + let name = rustc_target::spec::apple_deployment_target_env(&target.os); + sopts.apple_deployment_target_env = std::env::var(name).ok(); + } + let sess = Session { target, host, diff --git a/compiler/rustc_target/src/spec/base/apple/mod.rs b/compiler/rustc_target/src/spec/base/apple/mod.rs index aac2dc41da7bb..d939737acf2ef 100644 --- a/compiler/rustc_target/src/spec/base/apple/mod.rs +++ b/compiler/rustc_target/src/spec/base/apple/mod.rs @@ -251,6 +251,19 @@ pub fn platform(target: &Target) -> Option { }) } +/// Name of the environment variable used to fetch the deployment target on +/// the given OS. +pub fn deployment_target_env(os: &str) -> &'static str { + match os { + "macos" => "MACOSX_DEPLOYMENT_TARGET", + "ios" => "IPHONEOS_DEPLOYMENT_TARGET", + "watchos" => "WATCHOS_DEPLOYMENT_TARGET", + "tvos" => "TVOS_DEPLOYMENT_TARGET", + "visionos" => "XROS_DEPLOYMENT_TARGET", + _ => unreachable!("tried to get deployment target env var for non-Apple platform"), + } +} + /// Hack for calling `deployment_target` outside of this module. pub fn deployment_target_for_target(target: &Target) -> (u16, u8, u8) { let arch = if target.llvm_target.starts_with("arm64e") { @@ -300,17 +313,13 @@ fn deployment_target(os: &str, arch: Arch, abi: TargetAbi) -> (u16, u8, u8) { _ => os_min, }; - // The environment variable used to fetch the deployment target. - let env_var = match os { - "macos" => "MACOSX_DEPLOYMENT_TARGET", - "ios" => "IPHONEOS_DEPLOYMENT_TARGET", - "watchos" => "WATCHOS_DEPLOYMENT_TARGET", - "tvos" => "TVOS_DEPLOYMENT_TARGET", - "visionos" => "XROS_DEPLOYMENT_TARGET", - _ => unreachable!("tried to get deployment target env var for non-Apple platform"), - }; - - if let Ok(deployment_target) = env::var(env_var) { + // NOTE: We access the deployment target environment variable here, which + // makes the variable an **implicit** input which affects compilation. + // + // We make sure to rebuild when the variable changes, both by busting the + // incremental cache, and by telling Cargo that it is a dependency. + // Search for usages of `deployment_target_env` to see how. + if let Ok(deployment_target) = env::var(deployment_target_env(os)) { match parse_version(&deployment_target) { // It is common that the deployment target is set too low, e.g. on // macOS Aarch64 to also target older x86_64, the user may set a diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 5d0d68c26a3f9..27f5ed093a965 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -60,6 +60,7 @@ pub mod crt_objects; mod base; pub use base::apple::{ + deployment_target_env as apple_deployment_target_env, deployment_target_for_target as current_apple_deployment_target, platform as current_apple_platform, sdk_version as current_apple_sdk_version, };