Checks
Reproducible example
Reproducer
No ARM hardware needed - runs on GitHub's free ubuntu-24.04-arm runners:
pixi-cc-leak-reproducer
The repro crate mirrors the shape that triggered this for us in practice (git-cliff-core -> include-flate-codegen -> zstd-sys), reduced to the essential mechanism: a proc-macro = true crate depending on a crate whose build.rs calls cc::Build::new().compile(..).
Issue description
Summary
Cross-compiling a Rust package with pixi-build-rust from an aarch64 machine to a linux-64 (x86-64) target fails whenever the crate graph includes something compiled for the BUILD platform (proc-macros, build-dependencies with native code) that goes through the cc crate. The build machine's own compiler ends up receiving a -march=nocona -mtune=haswell flag meant for the HOST (x86-64) target, which its aarch64 gcc rejects:
cc1: error: unknown value 'nocona' for '-march'
Confirmed test matrix
| BUILD runner |
HOST target (--target-platform) |
Result |
| x86-64 (local machine) |
linux-aarch64 |
✅ works |
| CI, amd64 runner |
linux-aarch64 |
✅ works |
| CI, aarch64 runner |
linux-aarch64 |
✅ works |
| CI, aarch64 runner |
linux-64 |
❌ fails |
Only the "aarch64 building for x86-64" direction fails, and only because x86-64's conda-forge activation sets a microarchitecture baseline (-march=nocona -mtune=haswell) while aarch64's does not - there's nothing to leak in the reverse direction.
Root cause
conda-forge's cross-compilation activation scripts follow a convention: unsuffixed CC/CFLAGS are scoped to HOST (the platform being built for); CC_<triple>/CFLAGS_<triple> are scoped to BUILD (the machine actually running the compiler - used for proc-macros and build scripts, since those always execute natively regardless of the overall cross-compilation target).
cc-rs (the cc crate almost every native-code build script uses) resolves these two kinds of variables differently:
CC/AR: first-match-wins across CC_<triple>, then generic CC (getenv_with_target_prefixes).
CFLAGS/CXXFLAGS: all matching variables are concatenated (envflags()) - generic CFLAGS first, then the more specific CFLAGS_<triple>. The specific one does not override the generic one, it's appended to it.
So a proc-macro's native build-dependency, correctly compiled for BUILD (aarch64) with a clean CFLAGS_aarch64_unknown_linux_gnu, still receives the HOST-scoped generic CFLAGS (-march=nocona -mtune=haswell, meant for x86-64) on top - and its aarch64 compiler rejects it.
This is visible directly in cc-rs's own debug output from a real failing
CI run:
CC_aarch64_unknown_linux_gnu = Some(aarch64-conda-linux-gnu-cc)
CFLAGS_aarch64_unknown_linux_gnu = Some(-isystem $BUILD_PREFIX/include) <- clean
CFLAGS = Some(-march=nocona -mtune=haswell ...) <- leaks in anyway
aarch64-conda-linux-gnu-cc: error: unknown value 'nocona' for '-march'
Current workaround
Forcing clean flags via a cargo config.toml (stripping -march=nocona -mtune=haswell) works but throws away the intended tuning for the real
HOST target too, not just the BUILD-platform proc-macro compiles.
Possible fix direction
Since cc-rs itself won't let a specific CFLAGS_<triple> override the generic one, the fix likely has to happen in pixi-build-rust's generated build script: when actually cross-compiling (BUILD != HOST), wrap CC_<build-triple>/CXX_<build-triple> so the wrapper strips HOST-only microarchitecture flags (-march=, -mtune=, -mcpu=, -mfpu=, -mfloat-abi=, Apple's -arch <value>) from argv before invoking the real compiler, leaving generic CFLAGS/CXXFLAGS (and HOST-target compiles) untouched. Happy to submit a PR along these lines if that approach sounds right.
Expected behavior
Cross-compiling from aarch64 runner to amd64 package should produce correct x86_64 package
Checks
I have checked that this issue has not already been reported.
I have confirmed this bug exists on the latest version of pixi, using
pixi --version.Reproducible example
Reproducer
No ARM hardware needed - runs on GitHub's free
ubuntu-24.04-armrunners:pixi-cc-leak-reproducer
aarch64-runner-build-linux-64- fails with the error above.amd64-runner-build-linux-aarch64- same crate graph,other cross-compile direction, passes - shown side by side to demonstrate
the asymmetry.
The repro crate mirrors the shape that triggered this for us in practice (
git-cliff-core -> include-flate-codegen -> zstd-sys), reduced to the essential mechanism: aproc-macro = truecrate depending on a crate whosebuild.rscallscc::Build::new().compile(..).Issue description
Summary
Cross-compiling a Rust package with
pixi-build-rustfrom an aarch64 machine to alinux-64(x86-64) target fails whenever the crate graph includes something compiled for the BUILD platform (proc-macros, build-dependencies with native code) that goes through thecccrate. The build machine's own compiler ends up receiving a-march=nocona -mtune=haswellflag meant for the HOST (x86-64) target, which its aarch64gccrejects:Confirmed test matrix
--target-platform)Only the "aarch64 building for x86-64" direction fails, and only because x86-64's conda-forge activation sets a microarchitecture baseline (
-march=nocona -mtune=haswell) while aarch64's does not - there's nothing to leak in the reverse direction.Root cause
conda-forge's cross-compilation activation scripts follow a convention: unsuffixed
CC/CFLAGSare scoped to HOST (the platform being built for);CC_<triple>/CFLAGS_<triple>are scoped to BUILD (the machine actually running the compiler - used for proc-macros and build scripts, since those always execute natively regardless of the overall cross-compilation target).cc-rs(thecccrate almost every native-code build script uses) resolves these two kinds of variables differently:CC/AR: first-match-wins acrossCC_<triple>, then genericCC(getenv_with_target_prefixes).CFLAGS/CXXFLAGS: all matching variables are concatenated (envflags()) - genericCFLAGSfirst, then the more specificCFLAGS_<triple>. The specific one does not override the generic one, it's appended to it.So a proc-macro's native build-dependency, correctly compiled for BUILD (aarch64) with a clean
CFLAGS_aarch64_unknown_linux_gnu, still receives the HOST-scoped genericCFLAGS(-march=nocona -mtune=haswell, meant for x86-64) on top - and its aarch64 compiler rejects it.This is visible directly in
cc-rs's own debug output from a real failingCI run:
Current workaround
Forcing clean flags via a cargo
config.toml(stripping-march=nocona -mtune=haswell) works but throws away the intended tuning for the realHOST target too, not just the BUILD-platform proc-macro compiles.
Possible fix direction
Since
cc-rsitself won't let a specificCFLAGS_<triple>override the generic one, the fix likely has to happen inpixi-build-rust's generated build script: when actually cross-compiling (BUILD != HOST), wrapCC_<build-triple>/CXX_<build-triple>so the wrapper strips HOST-only microarchitecture flags (-march=,-mtune=,-mcpu=,-mfpu=,-mfloat-abi=, Apple's-arch <value>) from argv before invoking the real compiler, leaving genericCFLAGS/CXXFLAGS(and HOST-target compiles) untouched. Happy to submit a PR along these lines if that approach sounds right.Expected behavior
Cross-compiling from
aarch64runner toamd64package should produce correctx86_64package