nix: trim paths in nix build#3082
nix: trim paths in nix build#3082justus-camp-microsoft wants to merge 1 commit intomicrosoft:mainfrom
Conversation
|
I thought if we set RUSTFLAGS then basically it overwrites basically every other way of settings rust flags (config.toml, etc.). Is that right? How are we dealing with that? |
There was a problem hiding this comment.
Pull request overview
Adds Nix-driven, per-target RUSTFLAGS plumbing to Flowey builds so binaries don’t embed machine-specific absolute paths (via --remap-path-prefix), while ensuring env vars survive nix-shell --pure execution.
Changes:
- Define per-target
RUSTFLAGS_*variables inshell.nix(mirroring.cargo/config.toml), including Cargo registry/git remaps. - Teach Flowey’s cross-build init/build steps to inject the right Nix-provided
RUSTFLAGSand append a repo-path remap at build time. - Update the Nix shell command wrapper to pass env vars through
nix-shell --pureusing--keep, and update CI pipeline/workflows to run the new injection steps.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
shell.nix |
Adds composed per-target RUSTFLAGS_* env vars (incl. remap prefixes) for Nix shells. |
flowey/flowey_lib_hvlite/src/init_cross_build.rs |
Reads per-target RUSTFLAGS_* from Nix shell env and injects as RUSTFLAGS for cargo invocations. |
flowey/flowey_lib_hvlite/src/run_cargo_build.rs |
Appends a repo checkout --remap-path-prefix=<repo>= to RUSTFLAGS at build time. |
flowey/flowey_core/src/shell.rs |
Updates nix-shell --pure wrapper to preserve env via --keep and sets env on the outer process. |
ci-flowey/openvmm-pr.yaml |
Reorders/expands Flowey steps to inject cross env before relevant build steps. |
.github/workflows/openvmm-pr-release.yaml |
Mirrors the pipeline step changes for GitHub PR release workflow runs. |
.github/workflows/openvmm-ci.yaml |
Mirrors the pipeline step changes for GitHub CI workflow runs. |
flowey/flowey_core/src/shell.rs
Outdated
| if let Some(entry) = final_env.iter_mut().find(|(ek, _)| *ek == k) { | ||
| entry.1 = v; | ||
| } else { | ||
| final_env.push((k, v)); | ||
| } | ||
| } | ||
| EnvChange::Remove(k) => { | ||
| final_env.retain(|(ek, _)| *ek != k); |
There was a problem hiding this comment.
final_env.iter_mut().find(|(ek, _)| *ek == k) dereferences and moves an OsString out of a borrowed tuple (*ek), which won’t compile. Compare by reference instead (e.g., ek == &k / ek.as_os_str() == k.as_os_str()).
| if let Some(entry) = final_env.iter_mut().find(|(ek, _)| *ek == k) { | |
| entry.1 = v; | |
| } else { | |
| final_env.push((k, v)); | |
| } | |
| } | |
| EnvChange::Remove(k) => { | |
| final_env.retain(|(ek, _)| *ek != k); | |
| if let Some(entry) = final_env.iter_mut().find(|(ek, _)| ek == &k) { | |
| entry.1 = v; | |
| } else { | |
| final_env.push((k, v)); | |
| } | |
| } | |
| EnvChange::Remove(k) => { | |
| final_env.retain(|(ek, _)| ek != &k); |
| let rustflags = | ||
| flowey::shell_cmd!(rt, "printenv {rustflags_env}").read()?; | ||
| injected_env.insert("RUSTFLAGS".to_string(), rustflags); |
There was a problem hiding this comment.
nix_rustflags_env is enabled for any host platform detected as FlowPlatformLinuxDistro::Nix, but FlowPlatformLinuxDistro::Nix is also selected when $PATH contains /nix/store (e.g. NixOS outside a dev shell). In that case printenv RUSTFLAGS_X64 will fail if the shell.nix vars aren’t present, breaking cross env injection. Consider gating on IN_NIX_SHELL (or otherwise detecting the nix-shell wrapper), or making the printenv read best-effort (skip override if the env var isn’t set).
| let rustflags = | |
| flowey::shell_cmd!(rt, "printenv {rustflags_env}").read()?; | |
| injected_env.insert("RUSTFLAGS".to_string(), rustflags); | |
| match flowey::shell_cmd!(rt, "printenv {rustflags_env}").read() { | |
| Ok(rustflags) => { | |
| injected_env.insert("RUSTFLAGS".to_string(), rustflags); | |
| } | |
| Err(_) => { | |
| // If the Nix-specific RUSTFLAGS env var is not set, skip | |
| // overriding RUSTFLAGS instead of failing the step. | |
| } | |
| } |
| rustflags.push_str(&format!( | ||
| " --remap-path-prefix={}=", | ||
| repo_path.display() |
There was a problem hiding this comment.
The appended --remap-path-prefix={repo_path}= is inserted into RUSTFLAGS as a whitespace-delimited string. If the checkout path contains spaces, this will be split into multiple flags and break the build / remap behavior. Consider escaping spaces (or quoting the argument in a way Cargo’s RUSTFLAGS parser accepts) before appending.
| rustflags.push_str(&format!( | |
| " --remap-path-prefix={}=", | |
| repo_path.display() | |
| // Quote the path so that spaces do not split this into multiple flags | |
| let mut path_str = repo_path.display().to_string(); | |
| path_str = path_str.replace('"', "\\\""); | |
| rustflags.push_str(&format!( | |
| " --remap-path-prefix=\"{}\"=", | |
| path_str |
flowey/flowey_core/src/shell.rs
Outdated
| if let Some(entry) = final_env.iter_mut().find(|(ek, _)| *ek == k) { | ||
| entry.1 = v; | ||
| } else { | ||
| final_env.push((k, v)); | ||
| } | ||
| } | ||
| EnvChange::Remove(k) => { | ||
| final_env.retain(|(ek, _)| *ek != k); |
There was a problem hiding this comment.
final_env.retain(|(ek, _)| *ek != k) has the same issue as above: *ek attempts to move an OsString out of a borrow and won’t compile. Use a reference comparison (e.g., ek != &k) instead.
| if let Some(entry) = final_env.iter_mut().find(|(ek, _)| *ek == k) { | |
| entry.1 = v; | |
| } else { | |
| final_env.push((k, v)); | |
| } | |
| } | |
| EnvChange::Remove(k) => { | |
| final_env.retain(|(ek, _)| *ek != k); | |
| if let Some(entry) = final_env.iter_mut().find(|(ek, _)| ek == &k) { | |
| entry.1 = v; | |
| } else { | |
| final_env.push((k, v)); | |
| } | |
| } | |
| EnvChange::Remove(k) => { | |
| final_env.retain(|(ek, _)| ek != &k); |
|
Yeah we definitely don't want to just lose rustflags from other sources, that'll break all sorts of local one-off flows people might need. Is there any sort of current timeline on the rustc PR for stabilization? Or is it at least implemented unstably (behind a -Z flag) that we could use with RUSTC_BOOTSTRAP? |
578e27a to
64e8115
Compare
I had no idea RUSTC_BOOTSTRAP existed - that certainly makes this change way easier. I updated this PR to set the required environment variables that trigger path stripping for the Nix build. Thanks for the direction 😄 |
|
Yeah, using RUSTC_BOOTSTRAP isn't "good", but we do it elsewhere already, and it's easier than dealing with the potential flakiness of nightly. |
64e8115 to
04c9c15
Compare
|
|
||
| RUST_BACKTRACE = 1; | ||
| SOURCE_DATE_EPOCH = 12345; | ||
| # Need unstable features on the stable toolchain |
There was a problem hiding this comment.
Let's list exactly which unstable features, so that hopefully someday we can remove bootstrap once they're stable
04c9c15 to
f5af141
Compare
Our binary currently has machine-dependent paths in its output - to fix this rustc has a flag
--remap-path-prefix, but unfortunately it requires a key-value mapping so we have to figure out the path at runtime to be replaced. A future rust version will have better support for trimming paths that should make this unnecessary (RFC here)The RUSTFLAGS in our config.toml are duplicated here instead of trying to do some sort of merge/reading of the .toml file at runtime.