buildRustCrate: support Cargo.toml [lints] table (#502755)
This commit is contained in:
@@ -842,17 +842,38 @@ general. A number of other parameters can be overridden:
|
||||
(hello { }).override { extraRustcOpts = "-Z debuginfo=2"; }
|
||||
```
|
||||
|
||||
- The lint level cap passed to `rustc` (`allow` by default, which
|
||||
silences all lints). Because `rustc` only honours the first
|
||||
`--cap-lints` it receives, this cannot be changed via
|
||||
`extraRustcOpts`; use this attribute instead. Useful when overriding
|
||||
the `rust` attribute to point at `clippy-driver`, since clippy lints
|
||||
are also capped by this flag:
|
||||
- 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`
|
||||
only honours the first `--cap-lints` it receives, this cannot be
|
||||
changed via `extraRustcOpts`; use this attribute instead. Useful
|
||||
when overriding the `rust` attribute to point at `clippy-driver`,
|
||||
since clippy lints are also capped by this flag:
|
||||
|
||||
```nix
|
||||
(hello { }).override { capLints = "warn"; }
|
||||
```
|
||||
|
||||
- Lint configuration mirroring Cargo.toml's `[lints]` table. Keys are
|
||||
tool names (`rust`, `clippy`, `rustdoc`); values map lint names to
|
||||
either a level string (`"allow"`, `"warn"`, `"deny"`, `"forbid"`) or
|
||||
`{ level = "..."; priority = <int>; }`. Lower priorities are emitted
|
||||
first so that more specific lints can override them. Setting a
|
||||
non-empty `lints` raises the default `capLints` to `"forbid"` so the
|
||||
lints actually apply:
|
||||
|
||||
```nix
|
||||
(hello { }).override {
|
||||
lints.rust = {
|
||||
unsafe_code = "forbid";
|
||||
unused = {
|
||||
level = "deny";
|
||||
priority = -1;
|
||||
};
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
- Phases, just like in any other derivation, can be specified using
|
||||
the following attributes: `preUnpack`, `postUnpack`, `prePatch`,
|
||||
`patches`, `postPatch`, `preConfigure` (in the case of a Rust crate,
|
||||
|
||||
@@ -62,6 +62,65 @@ let
|
||||
# Create feature arguments for rustc.
|
||||
mkRustcFeatureArgs = lib.concatMapStringsSep " " (f: ''--cfg feature=\"${f}\"'');
|
||||
|
||||
# Translate a Cargo.toml `[lints]` table into rustc flags.
|
||||
#
|
||||
# See <https://doc.rust-lang.org/cargo/reference/manifest.html#the-lints-section>.
|
||||
#
|
||||
# Cargo normally translates `[lints.<tool>]` entries into `-A`/`-W`/`-D`/`-F`
|
||||
# flags when invoking rustc. Since buildRustCrate calls rustc directly we
|
||||
# must perform that translation ourselves.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# lintsToRustcFlags {
|
||||
# rust = {
|
||||
# unsafe_code = "forbid";
|
||||
# unused = { level = "deny"; priority = -1; };
|
||||
# };
|
||||
# clippy.all = "warn";
|
||||
# }
|
||||
# => [ "-D unused" "-W clippy::all" "-F unsafe_code" ]
|
||||
#
|
||||
# Entries are sorted by ascending priority (default 0) so that lower-priority
|
||||
# groups are emitted first and can be overridden by higher-priority specific
|
||||
# lints — matching cargo's behaviour where later rustc flags win.
|
||||
lintsToRustcFlags =
|
||||
lints:
|
||||
let
|
||||
levelFlag = {
|
||||
allow = "-A";
|
||||
warn = "-W";
|
||||
force-warn = "--force-warn";
|
||||
deny = "-D";
|
||||
forbid = "-F";
|
||||
};
|
||||
toolPrefix = tool: if tool == "rust" then "" else "${tool}::";
|
||||
normalize =
|
||||
val:
|
||||
if builtins.isString val then
|
||||
{
|
||||
level = val;
|
||||
priority = 0;
|
||||
}
|
||||
else
|
||||
{ priority = 0; } // val;
|
||||
entries = lib.concatMap (
|
||||
tool:
|
||||
lib.mapAttrsToList (
|
||||
name: val:
|
||||
let
|
||||
e = normalize val;
|
||||
in
|
||||
{
|
||||
inherit (e) priority;
|
||||
flag = "${levelFlag.${e.level}} ${toolPrefix tool}${name}";
|
||||
}
|
||||
) lints.${tool}
|
||||
) (builtins.attrNames lints);
|
||||
sorted = lib.sort (a: b: a.priority < b.priority) entries;
|
||||
in
|
||||
map (e: e.flag) sorted;
|
||||
|
||||
# Whether we need to use unstable command line flags
|
||||
#
|
||||
# Currently just needed for standard library dependencies, which have a
|
||||
@@ -203,9 +262,35 @@ lib.makeOverridable
|
||||
# second one via `extraRustcOpts` has no effect. Use this parameter
|
||||
# instead if you need lints to fire (e.g. when running clippy).
|
||||
#
|
||||
# When left at `null`, resolves to `"allow"` if `lints` is empty (the
|
||||
# usual case for third-party dependencies), or `"forbid"` if `lints`
|
||||
# is set (so your own crate's lint policy actually applies).
|
||||
#
|
||||
# Example: "warn"
|
||||
# Default: "allow"
|
||||
# Default: null (auto: "allow" or "forbid" depending on `lints`)
|
||||
capLints,
|
||||
# Lint configuration mirroring Cargo.toml's `[lints]` table.
|
||||
# See <https://doc.rust-lang.org/cargo/reference/manifest.html#the-lints-section>.
|
||||
#
|
||||
# Keys are tool names (`rust`, `clippy`, `rustdoc`); values are attrsets
|
||||
# mapping lint names to either a level string (`"allow"`, `"warn"`,
|
||||
# `"force-warn"`, `"deny"`, `"forbid"`) or an attrset
|
||||
# `{ level = "..."; priority = <int>; }`. Lower priorities are emitted
|
||||
# first so that higher-priority (more specific) lints can override them.
|
||||
#
|
||||
# Setting a non-empty `lints` raises the default `capLints` from
|
||||
# `"allow"` to `"forbid"` so the lints actually fire.
|
||||
#
|
||||
# Example:
|
||||
# {
|
||||
# rust = {
|
||||
# unsafe_code = "forbid";
|
||||
# unused = { level = "deny"; priority = -1; };
|
||||
# };
|
||||
# clippy.all = "warn";
|
||||
# }
|
||||
# Default: {}
|
||||
lints,
|
||||
# Whether to enable building tests.
|
||||
# Use true to enable.
|
||||
# Default: false
|
||||
@@ -260,14 +345,26 @@ lib.makeOverridable
|
||||
"codegenUnits"
|
||||
"links"
|
||||
"capLints"
|
||||
"lints"
|
||||
];
|
||||
extraDerivationAttrs = removeAttrs crate processedAttrs;
|
||||
nativeBuildInputs_ = nativeBuildInputs;
|
||||
buildInputs_ = buildInputs;
|
||||
extraRustcOpts_ = extraRustcOpts;
|
||||
extraRustcOptsForBuildRs_ = extraRustcOptsForBuildRs;
|
||||
capLints_ = capLints;
|
||||
buildTests_ = buildTests;
|
||||
resolvedLints = crate.lints or lints;
|
||||
lintFlags = lintsToRustcFlags resolvedLints;
|
||||
resolvedCapLints =
|
||||
let
|
||||
requested = crate.capLints or capLints;
|
||||
in
|
||||
if requested != null then
|
||||
requested
|
||||
else if resolvedLints != { } then
|
||||
"forbid"
|
||||
else
|
||||
"allow";
|
||||
|
||||
# crate2nix has a hack for the old bash based build script that did split
|
||||
# entries at `,`. No we have to work around that hack.
|
||||
@@ -390,12 +487,14 @@ lib.makeOverridable
|
||||
extraRustcOpts =
|
||||
lib.optionals (crate ? extraRustcOpts) crate.extraRustcOpts
|
||||
++ extraRustcOpts_
|
||||
++ lintFlags
|
||||
++ (lib.optional (edition != null) "--edition ${edition}");
|
||||
extraRustcOptsForBuildRs =
|
||||
lib.optionals (crate ? extraRustcOptsForBuildRs) crate.extraRustcOptsForBuildRs
|
||||
++ extraRustcOptsForBuildRs_
|
||||
++ lintFlags
|
||||
++ (lib.optional (edition != null) "--edition ${edition}");
|
||||
capLints = crate.capLints or capLints_;
|
||||
capLints = resolvedCapLints;
|
||||
|
||||
configurePhase = configureCrate {
|
||||
inherit
|
||||
@@ -487,7 +586,8 @@ lib.makeOverridable
|
||||
verbose = crate_.verbose or true;
|
||||
extraRustcOpts = [ ];
|
||||
extraRustcOptsForBuildRs = [ ];
|
||||
capLints = "allow";
|
||||
capLints = null;
|
||||
lints = { };
|
||||
features = [ ];
|
||||
nativeBuildInputs = [ ];
|
||||
buildInputs = [ ];
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
runCommandCC,
|
||||
stdenv,
|
||||
symlinkJoin,
|
||||
testers,
|
||||
writeTextFile,
|
||||
pkgsCross,
|
||||
}:
|
||||
@@ -767,6 +768,26 @@ rec {
|
||||
];
|
||||
};
|
||||
};
|
||||
# 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
|
||||
# can override them. Here `-D unused` (priority -1) is followed by
|
||||
# `-A dead_code` (default priority 0); the build only succeeds if
|
||||
# both flags reach rustc in that order.
|
||||
lintsPriority = {
|
||||
lints.rust = {
|
||||
unused = {
|
||||
level = "deny";
|
||||
priority = -1;
|
||||
};
|
||||
dead_code = "allow";
|
||||
};
|
||||
src = mkFile "src/lib.rs" ''
|
||||
#![allow(nonstandard_style)]
|
||||
fn dead() {}
|
||||
pub fn alive() {}
|
||||
'';
|
||||
};
|
||||
};
|
||||
brotliCrates = (callPackage ./brotli-crates.nix { });
|
||||
rcgenCrates = callPackage ./rcgen-crates.nix {
|
||||
@@ -934,6 +955,25 @@ rec {
|
||||
test -e ${pkg}/bin/brotli-decompressor && touch $out
|
||||
'';
|
||||
|
||||
# A `deny` lint from the lints table should actually fail the build.
|
||||
lintsDenyFails =
|
||||
let
|
||||
crate = mkHostCrate {
|
||||
crateName = "lintsDenyFails";
|
||||
lints.rust.dead_code = "deny";
|
||||
src = mkFile "src/lib.rs" ''
|
||||
fn dead() {}
|
||||
pub fn alive() {}
|
||||
'';
|
||||
};
|
||||
failed = testers.testBuildFailure crate;
|
||||
in
|
||||
runCommand "assert-lintsDenyFails" { inherit failed; } ''
|
||||
grep -q 'function .dead. is never used' "$failed/testBuildFailure.log"
|
||||
grep -q '\-D dead.code' "$failed/testBuildFailure.log"
|
||||
touch $out
|
||||
'';
|
||||
|
||||
rcgenTest =
|
||||
let
|
||||
pkg = rcgenCrates.rootCrate.build;
|
||||
|
||||
Reference in New Issue
Block a user