From 127a52082de70cd6dbe5957233d8a9e92b446b86 Mon Sep 17 00:00:00 2001 From: Bernardo Meurer Date: Wed, 22 Apr 2026 23:47:34 -0400 Subject: [PATCH] buildRustCrate: add extraRustcOptsForProcMacro Proc-macro crates are host dylibs that rustc dlopen()s. Instrumentation flags passed via extraRustcOpts (e.g. -Zsanitizer=address, -Cinstrument-coverage) leave unresolved runtime symbols in those dylibs and break the build. Cargo avoids this by not applying RUSTFLAGS to host artifacts; buildRustCrate already has extraRustcOptsForBuildRs for build scripts, so add the analogous knob for proc-macros. Defaults to null, which falls back to extraRustcOpts so existing callers are unchanged. Set to [] to opt proc-macros out when applying sanitizer/coverage flags tree-wide via crateOverrides. --- doc/languages-frameworks/rust.section.md | 9 +++++++ .../rust/build-rust-crate/default.nix | 27 ++++++++++++++++--- .../rust/build-rust-crate/test/default.nix | 26 ++++++++++++++++++ 3 files changed, 59 insertions(+), 3 deletions(-) diff --git a/doc/languages-frameworks/rust.section.md b/doc/languages-frameworks/rust.section.md index d7bb32d856af..b79e6e60493a 100644 --- a/doc/languages-frameworks/rust.section.md +++ b/doc/languages-frameworks/rust.section.md @@ -871,6 +871,15 @@ general. A number of other parameters can be overridden: (hello { }).override { extraRustcOpts = "-Z debuginfo=2"; } ``` +- Extra arguments passed to `rustc` when the crate is a proc-macro, + replacing `extraRustcOpts`. Useful to keep instrumentation flags + (sanitizers, coverage) off host dylibs. Defaults to `null`, which + inherits `extraRustcOpts`: + + ```nix + (myProcMacro { }).override { extraRustcOptsForProcMacro = [ ]; } + ``` + - The lint level cap passed to `rustc`. Defaults to `null`, which auto-resolves to `"allow"` (silences all lints) when `lints` is empty, or `"forbid"` (no cap) when `lints` is set. Because `rustc` diff --git a/pkgs/build-support/rust/build-rust-crate/default.nix b/pkgs/build-support/rust/build-rust-crate/default.nix index ca0d7331daaf..4e5000acc815 100644 --- a/pkgs/build-support/rust/build-rust-crate/default.nix +++ b/pkgs/build-support/rust/build-rust-crate/default.nix @@ -255,6 +255,12 @@ lib.makeOverridable # Example: [ "-Z debuginfo=2" ] # Default: [] extraRustcOptsForBuildRs, + # Extra rustc options for proc-macro crates, replacing + # `extraRustcOpts`. Lets callers keep instrumentation flags + # (sanitizers, coverage) off host dylibs, mirroring Cargo's + # behaviour of not applying RUSTFLAGS to host artifacts. + # Default: null (inherit `extraRustcOpts`) + extraRustcOptsForProcMacro, # The lint level cap passed to rustc via `--cap-lints`. # See . # @@ -352,7 +358,21 @@ lib.makeOverridable buildInputs_ = buildInputs; extraRustcOpts_ = extraRustcOpts; extraRustcOptsForBuildRs_ = extraRustcOptsForBuildRs; + extraRustcOptsForProcMacro_ = extraRustcOptsForProcMacro; buildTests_ = buildTests; + procMacro = lib.attrByPath [ "procMacro" ] false crate; + # For proc-macros, prefer the *ForProcMacro variant at each level + # (crate attr, override arg) and fall back to extraRustcOpts. + crateExtraRustcOpts = + if procMacro && crate ? extraRustcOptsForProcMacro then + crate.extraRustcOptsForProcMacro + else + crate.extraRustcOpts or [ ]; + overrideExtraRustcOpts = + if procMacro && extraRustcOptsForProcMacro_ != null then + extraRustcOptsForProcMacro_ + else + extraRustcOpts_; resolvedLints = crate.lints or lints; lintFlags = lintsToRustcFlags resolvedLints; resolvedCapLints = @@ -474,7 +494,7 @@ lib.makeOverridable crateRustVersion = crate.rust-version or ""; crateVersion = crate.version; crateType = - if lib.attrByPath [ "procMacro" ] false crate then + if procMacro then [ "proc-macro" ] else if lib.attrByPath [ "plugin" ] false crate then [ "dylib" ] @@ -485,8 +505,8 @@ lib.makeOverridable edition = crate.edition or null; codegenUnits = if crate ? codegenUnits then crate.codegenUnits else defaultCodegenUnits; extraRustcOpts = - lib.optionals (crate ? extraRustcOpts) crate.extraRustcOpts - ++ extraRustcOpts_ + crateExtraRustcOpts + ++ overrideExtraRustcOpts ++ lintFlags ++ (lib.optional (edition != null) "--edition ${edition}"); extraRustcOptsForBuildRs = @@ -586,6 +606,7 @@ lib.makeOverridable verbose = crate_.verbose or true; extraRustcOpts = [ ]; extraRustcOptsForBuildRs = [ ]; + extraRustcOptsForProcMacro = null; capLints = null; lints = { }; features = [ ]; diff --git a/pkgs/build-support/rust/build-rust-crate/test/default.nix b/pkgs/build-support/rust/build-rust-crate/test/default.nix index 2c9ada4e3dfd..c999448860a3 100644 --- a/pkgs/build-support/rust/build-rust-crate/test/default.nix +++ b/pkgs/build-support/rust/build-rust-crate/test/default.nix @@ -836,6 +836,32 @@ rec { ]; }; }; + # Default (null) inherits extraRustcOpts for proc-macros. + procMacroExtraOptsInherit = { + procMacro = true; + edition = "2018"; + extraRustcOpts = [ "--cfg=target_only" ]; + src = mkFile "src/lib.rs" '' + #[cfg(not(target_only))] + compile_error!("extraRustcOpts not inherited by proc-macro"); + use proc_macro as _; + ''; + }; + # When set, extraRustcOptsForProcMacro replaces extraRustcOpts + # for proc-macro crates. + procMacroExtraOptsOverride = { + procMacro = true; + edition = "2018"; + extraRustcOpts = [ "--cfg=target_only" ]; + extraRustcOptsForProcMacro = [ "--cfg=host_only" ]; + src = mkFile "src/lib.rs" '' + #[cfg(target_only)] + compile_error!("extraRustcOpts leaked into proc-macro"); + #[cfg(not(host_only))] + compile_error!("extraRustcOptsForProcMacro not applied"); + use proc_macro as _; + ''; + }; # The `lints` attr mirrors Cargo.toml's `[lints]` table and is # translated to rustc `-A`/`-W`/`-D`/`-F` flags. Lower-priority # entries are emitted first so that higher-priority specific lints