-
-
Notifications
You must be signed in to change notification settings - Fork 13.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
__cleanAttrs
: Clean packages that don't expose all internals
#217243
Changes from all commits
ef06fe8
db2571b
3cbfa15
59db5a5
89ff705
9b45ecd
b9e7258
7a0bba8
3abc47f
416741d
dc32ea5
cd0de0e
d02174b
2377feb
fac41dd
b7477b9
bc10d59
2db600e
7458ee0
b2c0a99
36582b0
2295009
165a2de
ce39083
c24b772
c09aa93
dbb8745
b38a364
854693c
a4655ce
56a4141
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -4,7 +4,9 @@ The standard build environment in the Nix Packages collection provides an enviro | |||||
|
||||||
## Using `stdenv` {#sec-using-stdenv} | ||||||
|
||||||
To build a package with the standard environment, you use the function `stdenv.mkDerivation`, instead of the primitive built-in function `derivation`, e.g. | ||||||
To build a package with the standard environment, you use the function `stdenv.mkDerivation`. It calls the built-in `derivation` function for you, and turns its result into a package attribute set. | ||||||
|
||||||
A minimal invocation looks as follows. | ||||||
|
||||||
```nix | ||||||
stdenv.mkDerivation { | ||||||
|
@@ -362,6 +364,42 @@ Unless set to `false`, some build systems with good support for parallel buildin | |||||
|
||||||
### Special variables {#special-variables} | ||||||
|
||||||
#### `__cleanAttrs` {#var-__cleanAttrs} | ||||||
|
||||||
By default, `mkDerivation` will expose its arguments in the returned package attribute set. This is unnecessary and leads to some confusion and doubt. | ||||||
|
||||||
New packages may pass `__cleanAttrs = true;` to `mkDerivation`, so that it will return a minimal set of package attributes, which package authors can extend via [`passthru`](#var-stdenv-passthru). | ||||||
|
||||||
Existing packages may be modified to pass `__cleanAttrs = "warn";`, so that the legacy attributes remain available, but they will print a helpful warning when they are accessed. Doing this is best avoided until the packaging function used supports recursively defined arguments like [`mkDerivation` does](#mkderivation-recursive-attributes). | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Which packaging function? |
||||||
|
||||||
Benefits of `__cleanAttrs`: | ||||||
|
||||||
- Users don't have to sift through unnecessary attributes when exploring a package in the `nix repl`. | ||||||
- Users can confidently use non-standard attributes that a package provides. | ||||||
- Package authors know which variables are intended for use by the builder. They can change the builder environment with confidence that they don't break consumers of their package. | ||||||
- It is a little bit more efficient in terms of CPU cycles and memory use. | ||||||
|
||||||
##### What to do when warned {#warning-package-attr-impl-detail} | ||||||
|
||||||
If you encounter the warning `The attribute ... of package ... is an implementation detail`, you are invited to help us explicitly support ways in which a package attribute set may used, so that Nixpkgs contributors and users will be aware of your use case. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This could be phrased to be more immediately actionable.
Suggested change
|
||||||
|
||||||
Ideally the package can add support for whatever is the high level goal you are trying to achieve. This usually involves adding an attribute explicitly to the package attribute set using [`passthru`](#var-stdenv-passthru). Perhaps some commonly applied logic can be added to its value. The public attributes defined in `passthru` can make use of a [recursive package definition](#mkderivation-recursive-attributes) in order to access other parts of the `mkDerivation`-based package in a way that works with `overrideAttrs`. | ||||||
|
||||||
If you do not know why you got the warning, you may set environment variable `NIX_ABORT_ON_WARN=true` and pass `--show-trace` to Nix. This will cause an evaluation trace to be printed for the location of the warning. From this trace, you can usually derive which expression is responsible for getting the attribute. If you use the `nix-command` experimental feature, you also need to pass `--impure`. | ||||||
|
||||||
If the attribute can not reasonably be supported by the community, you may use the `internals` attribute to find the same value without the warning. We hope that this will be rare, and if you need `internals` to make some temporary hack bearable, it's perfectly reasonable to use it. | ||||||
|
||||||
#### `__structuredAttrs` {#var-__structuredAttrs} | ||||||
|
||||||
When `__structuredAttrs = true` is passed to `mkDerivation`, derivation attributes are serialized in JSON format and made available to the builder through a `.attrs.json` file. This will | ||||||
|
||||||
- allow large values to be passed, | ||||||
- eliminate the need for `passAsFile`, | ||||||
- allow for finer grained settings such as [`outputChecks`](https://nixos.org/manual/nix/unstable/language/advanced-attributes.html?highlight=__struct#adv-attr-outputChecks), | ||||||
- turn non-nested arrays into bash arrays rather than a concatenated string. | ||||||
|
||||||
See [`__structuredAttrs` in the Nix manual](https://nixos.org/manual/nix/unstable/language/advanced-attributes.html#adv-attr-structuredAttrs). | ||||||
|
||||||
#### `passthru` {#var-stdenv-passthru} | ||||||
|
||||||
This is an attribute set which can be filled with arbitrary values. For example: | ||||||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -165,6 +165,10 @@ In addition to numerous new and upgraded packages, this release has the followin | |||||
|
||||||
- Pantheon now defaults to Mutter 42 and GNOME settings daemon 42, all Pantheon packages are now tracking elementary OS 7 updates. | ||||||
|
||||||
- `mkDerivation` does not have to leak implementation details anymore. | ||||||
|
||||||
Passing [`__cleanAttrs = true`](https://nixos.org/manual/nixpkgs/unstable/#var-__cleanAttrs) to `mkDerivation` returns a minimal set of package attributes that can be extended via [`passthru`](https://nixos.org/manual/nixpkgs/unstable/#var-stdenv-passthru). This reduces confusion for package users and allows for confident use of non-standard attributes provided by a package. It also gives package authors more confidence in modifying the builder environment without breaking consumers of their package, and it is slightly more efficient. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I really don't like the wording of this. It encourages people to just set cleanAttrs for all their packages, only to later get feedback from people that they need x, y, z back. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about this?
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That sounds better. 👍🏼 |
||||||
|
||||||
- The module for the application firewall `opensnitch` got the ability to configure rules. Available as [services.opensnitch.rules](#opt-services.opensnitch.rules) | ||||||
|
||||||
- The module `usbmuxd` now has the ability to change the package used by the daemon. In case you're experiencing issues with `usbmuxd` you can try an alternative program like `usbmuxd2`. Available as [services.usbmuxd.package](#opt-services.usbmuxd.package) | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
/* | ||
This file showcases various techniques and features of Nixpkgs, whereas the | ||
regular hello serves as more of an introductory package definition. | ||
Salve mundi is latin for hello world. | ||
*/ | ||
{ callPackage | ||
, lib | ||
, stdenv | ||
, fetchurl | ||
, nixos | ||
, testers | ||
, salve-mundi | ||
, makeWrapper | ||
, runCommand | ||
}: | ||
|
||
stdenv.mkDerivation (finalAttrs: { | ||
pname = "salve-mundi"; | ||
version = "2.12.1"; | ||
|
||
src = fetchurl { | ||
url = "mirror://gnu/hello/hello-${finalAttrs.version}.tar.gz"; | ||
sha256 = "sha256-jZkUKv2SV28wsM18tCqNxoCZmLxdYH2Idh9RLibH2yA="; | ||
}; | ||
|
||
doCheck = true; | ||
|
||
|
||
/* Wrap the program */ | ||
|
||
postInstall = lib.optionalString (finalAttrs.passthru.greeting != null) '' | ||
wrapProgram $out/bin/hello --append-flags --greeting=${lib.escapeShellArg (lib.escapeShellArg finalAttrs.passthru.greeting)} | ||
''; | ||
nativeBuildInputs = [ makeWrapper ]; | ||
passthru.greeting = "SALVE MUNDI"; | ||
|
||
|
||
/* Tidy up the package attributes and make them useful */ | ||
|
||
__cleanAttrs = true; | ||
passthru.exe = lib.getExe finalAttrs.finalPackage; | ||
# We allow `hello.src` to be used in tests and examples, despite __cleanAttrs | ||
passthru.src = finalAttrs.src; | ||
Comment on lines
+42
to
+43
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IMO src should always be passed through. eg you sometimes want to (re-)build it and as I understand it, that would after that no longer be possible which would suck. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There is no general way to get all sources in using just the expression language, so we don't lose the capability to generically retrieve all sources, because we never had that in the language. Simply put, not all packages have a |
||
|
||
|
||
/* Strict deps enforce a separation of concerns that also benefits cross compilation | ||
and, for shells, shell completions support via nativeBuildInputs */ | ||
|
||
strictDeps = true; | ||
|
||
|
||
/* A fairly extensive suite of extra tests that we like to hold either for | ||
the package in the Nixpkgs package set, or even for all possible overrides | ||
of the package. */ | ||
|
||
passthru.tests = { | ||
version = testers.testVersion { package = salve-mundi; }; | ||
|
||
# We use Nixpkgs attributes instead of `finalAttrs.finalPackage` here | ||
# because overriding is not supported. Running the test on an overridden | ||
# finalPackage wouldn't work, and is a bit unnecessary anyway. | ||
invariant-under-noXlibs = | ||
testers.testEqualDerivation | ||
"hello must not be rebuilt when environment.noXlibs is set." | ||
salve-mundi | ||
(nixos { environment.noXlibs = true; }).pkgs.salve-mundi; | ||
|
||
run = runCommand "salve-mundi-test-run" { | ||
nativeBuildInputs = [ finalAttrs.finalPackage ]; | ||
} '' | ||
diff -U3 --color=auto <(hello) <(echo 'SALVE MUNDI') | ||
touch $out | ||
''; | ||
|
||
restore-default-greeting = callPackage ./test.nix { | ||
hello = finalAttrs.finalPackage.overrideAttrs (o: { | ||
passthru = o.passthru // { | ||
greeting = null; | ||
}; | ||
}); | ||
}; | ||
Comment on lines
+75
to
+81
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's this for? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not everyone speaks latin 😄 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That is a good point. It could come across as elitist, especially in this context now that I think about it more. May the first confirmation win:
I don't like the Esperanto version because it's basically Romance language vocabulary. Nothing wrong with that, but if it's not going to be English, I don't want to bias it. Sue me. ;) |
||
}; | ||
|
||
meta = with lib; { | ||
description = "A showcase of Nixpkgs features that produces an unfamiliar, archaic greeting"; | ||
longDescription = '' | ||
GNU Hello is a program that prints "Hello, world!" when you run it. | ||
It is fully customizable. This package proves it, and showcases some | ||
of the fancier things you can do with Nixpkgs. | ||
''; | ||
mainProgram = "hello"; | ||
homepage = "https://www.gnu.org/software/hello/manual/"; | ||
changelog = "https://git.savannah.gnu.org/cgit/hello.git/plain/NEWS?h=v${finalAttrs.version}"; | ||
license = licenses.gpl3Plus; | ||
maintainers = [ maintainers.roberth ]; | ||
platforms = platforms.all; | ||
}; | ||
}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -161,6 +161,11 @@ let | |
# but for anything complex, be prepared to debug if enabling. | ||
, __structuredAttrs ? config.structuredAttrsByDefault or false | ||
|
||
# Opt-in for those who want a clear separation between attributes that should | ||
# be used and those that are internal. Also expected to perform slightly better | ||
# when applied widely. | ||
, __cleanAttrs ? false | ||
|
||
, env ? { } | ||
|
||
, ... } @ attrs: | ||
|
@@ -288,6 +293,7 @@ else let | |
"nativeCheckInputs" "nativeInstallCheckInputs" | ||
"__darwinAllowLocalNetworking" | ||
"__impureHostDeps" "__propagatedImpureHostDeps" | ||
"__cleanAttrs" | ||
"sandboxProfile" "propagatedSandboxProfile"] | ||
++ lib.optional (__structuredAttrs || envIsExportable) "env")) | ||
// (lib.optionalAttrs (attrs ? name || (attrs ? pname && attrs ? version)) { | ||
|
@@ -542,11 +548,37 @@ else let | |
"The ‘env’ attribute set can only contain derivation, string, boolean or integer attributes. The ‘${n}’ attribute is of type ${builtins.typeOf v}."; v) | ||
env; | ||
|
||
drvAttrs = derivationArg // lib.optionalAttrs envIsExportable checkedEnv; | ||
strict = builtins.derivationStrict drvAttrs; | ||
outputName = lib.head outputs; | ||
|
||
name = attrs.name or attrs.pname; | ||
|
||
# begin __cleanAttrs helpers | ||
|
||
whatNow = | ||
( | ||
if meta.maintainers != [] | ||
then "You may work with the maintainer(s) of ${name}: ${lib.concatMapStringsSep ", " (m: m.github or m.name or m.email) meta.maintainers} (and/or the community), to add explicit support for your use case." | ||
else "You may work with the community to add explicit support for your use case to ${name}." | ||
) + | ||
" See https://nixos.org/manual/nixpkgs/unstable/#warning-package-attr-impl-detail" | ||
; | ||
|
||
warnCleanAttrs = lib.mapAttrs (k: lib.warn "The attribute ${lib.strings.escapeNixIdentifier k} of package ${name} ${if k == "passthru" then "is" else "seems to be"} an implementation detail and may be removed from the package attribute set in the future. ${whatNow}"); | ||
|
||
warnCleanDrvAttrs = lib.warn "The attribute drvAttrs of package ${name} is an implementation detail and may be removed from the package attribute set in the future. ${whatNow}"; | ||
|
||
maybeWarnCleanAttrs = if __cleanAttrs == "warn" then warnCleanAttrs else x: x; | ||
|
||
# end __cleanAttrs helpers | ||
|
||
in | ||
|
||
lib.extendDerivation | ||
validity.handled | ||
({ | ||
( | ||
lib.optionalAttrs (__cleanAttrs != true) (maybeWarnCleanAttrs { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This removes |
||
# A derivation that always builds successfully and whose runtime | ||
# dependencies are the original derivations build time dependencies | ||
# This allows easy building and distributing of all derivations | ||
|
@@ -577,14 +609,37 @@ lib.extendDerivation | |
disallowedReferences = [ ]; | ||
disallowedRequisites = [ ]; | ||
}); | ||
|
||
inherit meta passthru overrideAttrs; | ||
} // | ||
inherit passthru; | ||
}) | ||
// { | ||
inherit meta overrideAttrs; | ||
} | ||
# Pass through extra attributes that are not inputs, but | ||
# should be made available to Nix expressions using the | ||
# derivation (e.g., in assertions). | ||
passthru) | ||
(derivation (derivationArg // lib.optionalAttrs envIsExportable checkedEnv)); | ||
// passthru) | ||
( | ||
( | ||
if __cleanAttrs == true | ||
then builtins.intersectAttrs { version = null; } drvAttrs | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is version propagated? I guess it's somewhat important and stable enough. Should definitely be a comment here |
||
else if __cleanAttrs == "warn" | ||
then warnCleanAttrs drvAttrs | ||
else drvAttrs | ||
) | ||
// builtins.listToAttrs (map (outputName: { | ||
name = outputName; | ||
value = strict.${outputName}; | ||
}) outputs) | ||
// { | ||
type = "derivation"; | ||
inherit outputName outputs; | ||
inherit (strict) drvPath; | ||
inherit (drvAttrs) name; | ||
${if __cleanAttrs != false then "internals" else "drvAttrs"} = if __cleanAttrs == "warn" then warnCleanDrvAttrs drvAttrs else drvAttrs; | ||
${if __cleanAttrs == "warn" then "drvAttrs" else null} = warnCleanDrvAttrs drvAttrs; | ||
outPath = strict.${outputName}; | ||
} | ||
); | ||
|
||
in | ||
fnOrAttrs: | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should soften that a bit, because I don't see evidence for the originally definitive-sounding statement.