Initial commit

This commit is contained in:
Victor Fuentes 2024-05-01 18:14:04 -04:00
commit 74ee9adefc
Signed by: vlinkz
GPG key ID: 0A88B68D6A9ACAE0
222 changed files with 28547 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/result

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
Copyright (c) 2003-2024 Eelco Dolstra and the Nixpkgs/NixOS contributors
Copyright (c) 2024 the Aux contributors
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

27
flake.lock Normal file
View file

@ -0,0 +1,27 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1714253743,
"narHash": "sha256-mdTQw2XlariysyScCv2tTE45QSU9v/ezLcHJ22f0Nxc=",
"owner": "auxolotl",
"repo": "nixpkgs",
"rev": "58a1abdbae3217ca6b702f03d3b35125d88a2994",
"type": "github"
},
"original": {
"owner": "auxolotl",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

28
flake.nix Normal file
View file

@ -0,0 +1,28 @@
{
inputs = {
nixpkgs.url = "github:auxolotl/nixpkgs/nixos-unstable";
};
outputs = { self, nixpkgs, ... }:
let
lib = import ./lib;
forAllSystems = lib.genAttrs lib.systems.flakeExposed;
in
{
nixPackages = forAllSystems (system:
(import nixpkgs { inherit system; })
);
# auxPackages = forAllSystems (system:
# (import ./. { inherit system; })
# );
# To test, run nix build .#tests.x86_64-linux.release
tests = forAllSystems (system:
({
systems = import ./lib/tests/systems.nix;
release = import ./lib/tests/release.nix { pkgs = self.nixPackages.${system}; };
})
);
};
}

1
lib/.version Normal file
View file

@ -0,0 +1 @@
24.05

159
lib/README.md Normal file
View file

@ -0,0 +1,159 @@
# Nixpkgs lib
This directory contains the implementation, documentation and tests for the Nixpkgs `lib` library.
## Overview
The evaluation entry point for `lib` is [`default.nix`](default.nix).
This file evaluates to an attribute set containing two separate kinds of attributes:
- Sub-libraries:
Attribute sets grouping together similar functionality.
Each sub-library is defined in a separate file usually matching its attribute name.
Example: `lib.lists` is a sub-library containing list-related functionality such as `lib.lists.take` and `lib.lists.imap0`.
These are defined in the file [`lists.nix`](lists.nix).
- Aliases:
Attributes that point to an attribute of the same name in some sub-library.
Example: `lib.take` is an alias for `lib.lists.take`.
Most files in this directory are definitions of sub-libraries, but there are a few others:
- [`minver.nix`](minver.nix): A string of the minimum version of Nix that is required to evaluate Nixpkgs.
- [`tests`](tests): Tests, see [Running tests](#running-tests)
- [`release.nix`](tests/release.nix): A derivation aggregating all tests
- [`misc.nix`](tests/misc.nix): Evaluation unit tests for most sub-libraries
- `*.sh`: Bash scripts that run tests for specific sub-libraries
- All other files in this directory exist to support the tests
- [`systems`](systems): The `lib.systems` sub-library, structured into a directory instead of a file due to its complexity
- [`path`](path): The `lib.path` sub-library, which includes tests as well as a document describing the design goals of `lib.path`
- All other files in this directory are sub-libraries
### Module system
The [module system](https://nixos.org/manual/nixpkgs/#module-system) spans multiple sub-libraries:
- [`modules.nix`](modules.nix): `lib.modules` for the core functions and anything not relating to option definitions
- [`options.nix`](options.nix): `lib.options` for anything relating to option definitions
- [`types.nix`](types.nix): `lib.types` for module system types
## PR Guidelines
Follow these guidelines for proposing a change to the interface of `lib`.
### Provide a Motivation
Clearly describe why the change is necessary and its use cases.
Make sure that the change benefits the user more than the added mental effort of looking it up and keeping track of its definition.
If the same can reasonably be done with the existing interface,
consider just updating the documentation with more examples and links.
This is also known as the [Fairbairn Threshold](https://wiki.haskell.org/Fairbairn_threshold).
Through this principle we avoid the human cost of duplicated functionality in an overly large library.
### Make one PR for each change
Don't have multiple changes in one PR, instead split it up into multiple ones.
This keeps the conversation focused and has a higher chance of getting merged.
### Name the interface appropriately
When introducing new names to the interface, such as new function, or new function attributes,
make sure to name it appropriately.
Names should be self-explanatory and consistent with the rest of `lib`.
If there's no obvious best name, include the alternatives you considered.
### Write documentation
Update the [reference documentation](#reference-documentation) to reflect the change.
Be generous with links to related functionality.
### Write tests
Add good test coverage for the change, including:
- Tests for edge cases, such as empty values or lists.
- Tests for tricky inputs, such as a string with string context or a path that doesn't exist.
- Test all code paths, such as `if-then-else` branches and returned attributes.
- If the tests for the sub-library are written in bash,
test messages of custom errors, such as `throw` or `abortMsg`,
At the time this is only not necessary for sub-libraries tested with [`tests/misc.nix`](./tests/misc.nix).
See [running tests](#running-tests) for more details on the test suites.
### Write tidy code
Name variables well, even if they're internal.
The code should be as self-explanatory as possible.
Be generous with code comments when appropriate.
As a baseline, follow the [Nixpkgs code conventions](https://github.com/NixOS/nixpkgs/blob/master/CONTRIBUTING.md#code-conventions).
### Write efficient code
Nix generally does not have free abstractions.
Be aware that seemingly straightforward changes can cause more allocations and a decrease in performance.
That said, don't optimise prematurely, especially in new code.
## Reference documentation
Reference documentation for library functions is written above each function as a multi-line comment.
These comments are processed using [nixdoc](https://github.com/nix-community/nixdoc) and [rendered in the Nixpkgs manual](https://nixos.org/manual/nixpkgs/stable/#chap-functions).
The nixdoc README describes the [comment format](https://github.com/nix-community/nixdoc#comment-format).
See [doc/README.md](../doc/README.md) for how to build the manual.
## Running tests
All library tests can be run by building the derivation in [`tests/release.nix`](tests/release.nix):
```bash
nix-build tests/release.nix
```
Some commands for quicker iteration over parts of the test suite are also available:
```bash
# Run all evaluation unit tests in tests/misc.nix
# if the resulting list is empty, all tests passed
nix-instantiate --eval --strict tests/misc.nix
# Run the module system tests
tests/modules.sh
# Run the lib.sources tests
tests/sources.sh
# Run the lib.filesystem tests
tests/filesystem.sh
# Run the lib.path property tests
path/tests/prop.sh
# Run the lib.fileset tests
fileset/tests.sh
```
## Commit conventions
- Make sure you read about the [commit conventions](../CONTRIBUTING.md#commit-conventions) common to Nixpkgs as a whole.
- Format the commit messages in the following way:
```
lib.(section): (init | add additional argument | refactor | etc)
(Motivation for change. Additional information.)
```
Examples:
* lib.getExe': check arguments
* lib.fileset: Add an additional argument in the design docs
Closes #264537

99
lib/ascii-table.nix Normal file
View file

@ -0,0 +1,99 @@
{ "\t" = 9;
"\n" = 10;
"\r" = 13;
" " = 32;
"!" = 33;
"\"" = 34;
"#" = 35;
"$" = 36;
"%" = 37;
"&" = 38;
"'" = 39;
"(" = 40;
")" = 41;
"*" = 42;
"+" = 43;
"," = 44;
"-" = 45;
"." = 46;
"/" = 47;
"0" = 48;
"1" = 49;
"2" = 50;
"3" = 51;
"4" = 52;
"5" = 53;
"6" = 54;
"7" = 55;
"8" = 56;
"9" = 57;
":" = 58;
";" = 59;
"<" = 60;
"=" = 61;
">" = 62;
"?" = 63;
"@" = 64;
"A" = 65;
"B" = 66;
"C" = 67;
"D" = 68;
"E" = 69;
"F" = 70;
"G" = 71;
"H" = 72;
"I" = 73;
"J" = 74;
"K" = 75;
"L" = 76;
"M" = 77;
"N" = 78;
"O" = 79;
"P" = 80;
"Q" = 81;
"R" = 82;
"S" = 83;
"T" = 84;
"U" = 85;
"V" = 86;
"W" = 87;
"X" = 88;
"Y" = 89;
"Z" = 90;
"[" = 91;
"\\" = 92;
"]" = 93;
"^" = 94;
"_" = 95;
"`" = 96;
"a" = 97;
"b" = 98;
"c" = 99;
"d" = 100;
"e" = 101;
"f" = 102;
"g" = 103;
"h" = 104;
"i" = 105;
"j" = 106;
"k" = 107;
"l" = 108;
"m" = 109;
"n" = 110;
"o" = 111;
"p" = 112;
"q" = 113;
"r" = 114;
"s" = 115;
"t" = 116;
"u" = 117;
"v" = 118;
"w" = 119;
"x" = 120;
"y" = 121;
"z" = 122;
"{" = 123;
"|" = 124;
"}" = 125;
"~" = 126;
}

144
lib/asserts.nix Normal file
View file

@ -0,0 +1,144 @@
{ lib }:
rec {
/**
Throw if pred is false, else return pred.
Intended to be used to augment asserts with helpful error messages.
# Inputs
`pred`
: Predicate that needs to succeed, otherwise `msg` is thrown
`msg`
: Message to throw in case `pred` fails
# Type
```
assertMsg :: Bool -> String -> Bool
```
# Examples
:::{.example}
## `lib.asserts.assertMsg` usage example
```nix
assertMsg false "nope"
stderr> error: nope
assert assertMsg ("foo" == "bar") "foo is not bar, silly"; ""
stderr> error: foo is not bar, silly
```
:::
*/
# TODO(Profpatsch): add tests that check stderr
assertMsg =
pred:
msg:
pred || builtins.throw msg;
/**
Specialized `assertMsg` for checking if `val` is one of the elements
of the list `xs`. Useful for checking enums.
# Inputs
`name`
: The name of the variable the user entered `val` into, for inclusion in the error message
`val`
: The value of what the user provided, to be compared against the values in `xs`
`xs`
: The list of valid values
# Type
```
assertOneOf :: String -> ComparableVal -> List ComparableVal -> Bool
```
# Examples
:::{.example}
## `lib.asserts.assertOneOf` usage example
```nix
let sslLibrary = "libressl";
in assertOneOf "sslLibrary" sslLibrary [ "openssl" "bearssl" ]
stderr> error: sslLibrary must be one of [
stderr> "openssl"
stderr> "bearssl"
stderr> ], but is: "libressl"
```
:::
*/
assertOneOf =
name:
val:
xs:
assertMsg
(lib.elem val xs)
"${name} must be one of ${
lib.generators.toPretty {} xs}, but is: ${
lib.generators.toPretty {} val}";
/**
Specialized `assertMsg` for checking if every one of `vals` is one of the elements
of the list `xs`. Useful for checking lists of supported attributes.
# Inputs
`name`
: The name of the variable the user entered `val` into, for inclusion in the error message
`vals`
: The list of values of what the user provided, to be compared against the values in `xs`
`xs`
: The list of valid values
# Type
```
assertEachOneOf :: String -> List ComparableVal -> List ComparableVal -> Bool
```
# Examples
:::{.example}
## `lib.asserts.assertEachOneOf` usage example
```nix
let sslLibraries = [ "libressl" "bearssl" ];
in assertEachOneOf "sslLibraries" sslLibraries [ "openssl" "bearssl" ]
stderr> error: each element in sslLibraries must be one of [
stderr> "openssl"
stderr> "bearssl"
stderr> ], but is: [
stderr> "libressl"
stderr> "bearssl"
stderr> ]
```
:::
*/
assertEachOneOf =
name:
vals:
xs:
assertMsg
(lib.all (val: lib.elem val xs) vals)
"each element in ${name} must be one of ${
lib.generators.toPretty {} xs}, but is: ${
lib.generators.toPretty {} vals}";
}

2040
lib/attrsets.nix Normal file

File diff suppressed because it is too large Load diff

104
lib/cli.nix Normal file
View file

@ -0,0 +1,104 @@
{ lib }:
rec {
/**
Automatically convert an attribute set to command-line options.
This helps protect against malformed command lines and also to reduce
boilerplate related to command-line construction for simple use cases.
`toGNUCommandLine` returns a list of nix strings.
`toGNUCommandLineShell` returns an escaped shell string.
# Inputs
`options`
: 1\. Function argument
`attrs`
: 2\. Function argument
# Examples
:::{.example}
## `lib.cli.toGNUCommandLineShell` usage example
```nix
cli.toGNUCommandLine {} {
data = builtins.toJSON { id = 0; };
X = "PUT";
retry = 3;
retry-delay = null;
url = [ "https://example.com/foo" "https://example.com/bar" ];
silent = false;
verbose = true;
}
=> [
"-X" "PUT"
"--data" "{\"id\":0}"
"--retry" "3"
"--url" "https://example.com/foo"
"--url" "https://example.com/bar"
"--verbose"
]
cli.toGNUCommandLineShell {} {
data = builtins.toJSON { id = 0; };
X = "PUT";
retry = 3;
retry-delay = null;
url = [ "https://example.com/foo" "https://example.com/bar" ];
silent = false;
verbose = true;
}
=> "'-X' 'PUT' '--data' '{\"id\":0}' '--retry' '3' '--url' 'https://example.com/foo' '--url' 'https://example.com/bar' '--verbose'";
```
:::
*/
toGNUCommandLineShell =
options: attrs: lib.escapeShellArgs (toGNUCommandLine options attrs);
toGNUCommandLine = {
# how to string-format the option name;
# by default one character is a short option (`-`),
# more than one characters a long option (`--`).
mkOptionName ?
k: if builtins.stringLength k == 1
then "-${k}"
else "--${k}",
# how to format a boolean value to a command list;
# by default its a flag option
# (only the option name if true, left out completely if false).
mkBool ? k: v: lib.optional v (mkOptionName k),
# how to format a list value to a command list;
# by default the option name is repeated for each value
# and `mkOption` is applied to the values themselves.
mkList ? k: v: lib.concatMap (mkOption k) v,
# how to format any remaining value to a command list;
# on the toplevel, booleans and lists are handled by `mkBool` and `mkList`,
# though they can still appear as values of a list.
# By default, everything is printed verbatim and complex types
# are forbidden (lists, attrsets, functions). `null` values are omitted.
mkOption ?
k: v: if v == null
then []
else [ (mkOptionName k) (lib.generators.mkValueStringDefault {} v) ]
}:
options:
let
render = k: v:
if builtins.isBool v then mkBool k v
else if builtins.isList v then mkList k v
else mkOption k v;
in
builtins.concatLists (lib.mapAttrsToList render options);
}

664
lib/customisation.nix Normal file
View file

@ -0,0 +1,664 @@
{ lib }:
let
inherit (builtins)
intersectAttrs;
inherit (lib)
functionArgs isFunction mirrorFunctionArgs isAttrs setFunctionArgs
optionalAttrs attrNames filter elemAt concatStringsSep sortOn take length
filterAttrs optionalString flip pathIsDirectory head pipe isDerivation listToAttrs
mapAttrs seq flatten deepSeq warnIf isInOldestRelease extends
;
inherit (lib.strings) levenshtein levenshteinAtMost;
in
rec {
/**
`overrideDerivation drv f` takes a derivation (i.e., the result
of a call to the builtin function `derivation`) and returns a new
derivation in which the attributes of the original are overridden
according to the function `f`. The function `f` is called with
the original derivation attributes.
`overrideDerivation` allows certain "ad-hoc" customisation
scenarios (e.g. in ~/.config/nixpkgs/config.nix). For instance,
if you want to "patch" the derivation returned by a package
function in Nixpkgs to build another version than what the
function itself provides.
For another application, see build-support/vm, where this
function is used to build arbitrary derivations inside a QEMU
virtual machine.
Note that in order to preserve evaluation errors, the new derivation's
outPath depends on the old one's, which means that this function cannot
be used in circular situations when the old derivation also depends on the
new one.
You should in general prefer `drv.overrideAttrs` over this function;
see the nixpkgs manual for more information on overriding.
# Inputs
`drv`
: 1\. Function argument
`f`
: 2\. Function argument
# Type
```
overrideDerivation :: Derivation -> ( Derivation -> AttrSet ) -> Derivation
```
# Examples
:::{.example}
## `lib.customisation.overrideDerivation` usage example
```nix
mySed = overrideDerivation pkgs.gnused (oldAttrs: {
name = "sed-4.2.2-pre";
src = fetchurl {
url = ftp://alpha.gnu.org/gnu/sed/sed-4.2.2-pre.tar.bz2;
hash = "sha256-MxBJRcM2rYzQYwJ5XKxhXTQByvSg5jZc5cSHEZoB2IY=";
};
patches = [];
});
```
:::
*/
overrideDerivation = drv: f:
let
newDrv = derivation (drv.drvAttrs // (f drv));
in flip (extendDerivation (seq drv.drvPath true)) newDrv (
{ meta = drv.meta or {};
passthru = if drv ? passthru then drv.passthru else {};
}
//
(drv.passthru or {})
//
optionalAttrs (drv ? __spliced) {
__spliced = {} // (mapAttrs (_: sDrv: overrideDerivation sDrv f) drv.__spliced);
});
/**
`makeOverridable` takes a function from attribute set to attribute set and
injects `override` attribute which can be used to override arguments of
the function.
Please refer to documentation on [`<pkg>.overrideDerivation`](#sec-pkg-overrideDerivation) to learn about `overrideDerivation` and caveats
related to its use.
# Inputs
`f`
: 1\. Function argument
# Type
```
makeOverridable :: (AttrSet -> a) -> AttrSet -> a
```
# Examples
:::{.example}
## `lib.customisation.makeOverridable` usage example
```nix
nix-repl> x = {a, b}: { result = a + b; }
nix-repl> y = lib.makeOverridable x { a = 1; b = 2; }
nix-repl> y
{ override = «lambda»; overrideDerivation = «lambda»; result = 3; }
nix-repl> y.override { a = 10; }
{ override = «lambda»; overrideDerivation = «lambda»; result = 12; }
```
:::
*/
makeOverridable = f:
let
# Creates a functor with the same arguments as f
mirrorArgs = mirrorFunctionArgs f;
in
mirrorArgs (origArgs:
let
result = f origArgs;
# Changes the original arguments with (potentially a function that returns) a set of new attributes
overrideWith = newArgs: origArgs // (if isFunction newArgs then newArgs origArgs else newArgs);
# Re-call the function but with different arguments
overrideArgs = mirrorArgs (newArgs: makeOverridable f (overrideWith newArgs));
# Change the result of the function call by applying g to it
overrideResult = g: makeOverridable (mirrorArgs (args: g (f args))) origArgs;
in
if isAttrs result then
result // {
override = overrideArgs;
overrideDerivation = fdrv: overrideResult (x: overrideDerivation x fdrv);
${if result ? overrideAttrs then "overrideAttrs" else null} = fdrv:
overrideResult (x: x.overrideAttrs fdrv);
}
else if isFunction result then
# Transform the result into a functor while propagating its arguments
setFunctionArgs result (functionArgs result) // {
override = overrideArgs;
}
else result);
/**
Call the package function in the file `fn` with the required
arguments automatically. The function is called with the
arguments `args`, but any missing arguments are obtained from
`autoArgs`. This function is intended to be partially
parameterised, e.g.,
```nix
callPackage = callPackageWith pkgs;
pkgs = {
libfoo = callPackage ./foo.nix { };
libbar = callPackage ./bar.nix { };
};
```
If the `libbar` function expects an argument named `libfoo`, it is
automatically passed as an argument. Overrides or missing
arguments can be supplied in `args`, e.g.
```nix
libbar = callPackage ./bar.nix {
libfoo = null;
enableX11 = true;
};
```
<!-- TODO: Apply "Example:" tag to the examples above -->
# Inputs
`autoArgs`
: 1\. Function argument
`fn`
: 2\. Function argument
`args`
: 3\. Function argument
# Type
```
callPackageWith :: AttrSet -> ((AttrSet -> a) | Path) -> AttrSet -> a
```
*/
callPackageWith = autoArgs: fn: args:
let
f = if isFunction fn then fn else import fn;
fargs = functionArgs f;
# All arguments that will be passed to the function
# This includes automatic ones and ones passed explicitly
allArgs = intersectAttrs fargs autoArgs // args;
# a list of argument names that the function requires, but
# wouldn't be passed to it
missingArgs =
# Filter out arguments that have a default value
(filterAttrs (name: value: ! value)
# Filter out arguments that would be passed
(removeAttrs fargs (attrNames allArgs)));
# Get a list of suggested argument names for a given missing one
getSuggestions = arg: pipe (autoArgs // args) [
attrNames
# Only use ones that are at most 2 edits away. While mork would work,
# levenshteinAtMost is only fast for 2 or less.
(filter (levenshteinAtMost 2 arg))
# Put strings with shorter distance first
(sortOn (levenshtein arg))
# Only take the first couple results
(take 3)
# Quote all entries
(map (x: "\"" + x + "\""))
];
prettySuggestions = suggestions:
if suggestions == [] then ""
else if length suggestions == 1 then ", did you mean ${elemAt suggestions 0}?"
else ", did you mean ${concatStringsSep ", " (lib.init suggestions)} or ${lib.last suggestions}?";
errorForArg = arg:
let
loc = builtins.unsafeGetAttrPos arg fargs;
# loc' can be removed once lib/minver.nix is >2.3.4, since that includes
# https://github.com/NixOS/nix/pull/3468 which makes loc be non-null
loc' = if loc != null then loc.file + ":" + toString loc.line
else if ! isFunction fn then
toString fn + optionalString (pathIsDirectory fn) "/default.nix"
else "<unknown location>";
in "Function called without required argument \"${arg}\" at "
+ "${loc'}${prettySuggestions (getSuggestions arg)}";
# Only show the error for the first missing argument
error = errorForArg (head (attrNames missingArgs));
in if missingArgs == {}
then makeOverridable f allArgs
# This needs to be an abort so it can't be caught with `builtins.tryEval`,
# which is used by nix-env and ofborg to filter out packages that don't evaluate.
# This way we're forced to fix such errors in Nixpkgs,
# which is especially relevant with allowAliases = false
else abort "lib.customisation.callPackageWith: ${error}";
/**
Like callPackage, but for a function that returns an attribute
set of derivations. The override function is added to the
individual attributes.
# Inputs
`autoArgs`
: 1\. Function argument
`fn`
: 2\. Function argument
`args`
: 3\. Function argument
# Type
```
callPackagesWith :: AttrSet -> ((AttrSet -> AttrSet) | Path) -> AttrSet -> AttrSet
```
*/
callPackagesWith = autoArgs: fn: args:
let
f = if isFunction fn then fn else import fn;
auto = intersectAttrs (functionArgs f) autoArgs;
mirrorArgs = mirrorFunctionArgs f;
origArgs = auto // args;
pkgs = f origArgs;
mkAttrOverridable = name: _: makeOverridable (mirrorArgs (newArgs: (f newArgs).${name})) origArgs;
in
if isDerivation pkgs then throw
("function `callPackages` was called on a *single* derivation "
+ ''"${pkgs.name or "<unknown-name>"}";''
+ " did you mean to use `callPackage` instead?")
else mapAttrs mkAttrOverridable pkgs;
/**
Add attributes to each output of a derivation without changing
the derivation itself and check a given condition when evaluating.
# Inputs
`condition`
: 1\. Function argument
`passthru`
: 2\. Function argument
`drv`
: 3\. Function argument
# Type
```
extendDerivation :: Bool -> Any -> Derivation -> Derivation
```
*/
extendDerivation = condition: passthru: drv:
let
outputs = drv.outputs or [ "out" ];
commonAttrs = drv // (listToAttrs outputsList) //
({ all = map (x: x.value) outputsList; }) // passthru;
outputToAttrListElement = outputName:
{ name = outputName;
value = commonAttrs // {
inherit (drv.${outputName}) type outputName;
outputSpecified = true;
drvPath = assert condition; drv.${outputName}.drvPath;
outPath = assert condition; drv.${outputName}.outPath;
} //
# TODO: give the derivation control over the outputs.
# `overrideAttrs` may not be the only attribute that needs
# updating when switching outputs.
optionalAttrs (passthru?overrideAttrs) {
# TODO: also add overrideAttrs when overrideAttrs is not custom, e.g. when not splicing.
overrideAttrs = f: (passthru.overrideAttrs f).${outputName};
};
};
outputsList = map outputToAttrListElement outputs;
in commonAttrs // {
drvPath = assert condition; drv.drvPath;
outPath = assert condition; drv.outPath;
};
/**
Strip a derivation of all non-essential attributes, returning
only those needed by hydra-eval-jobs. Also strictly evaluate the
result to ensure that there are no thunks kept alive to prevent
garbage collection.
# Inputs
`drv`
: 1\. Function argument
# Type
```
hydraJob :: (Derivation | Null) -> (Derivation | Null)
```
*/
hydraJob = drv:
let
outputs = drv.outputs or ["out"];
commonAttrs =
{ inherit (drv) name system meta; inherit outputs; }
// optionalAttrs (drv._hydraAggregate or false) {
_hydraAggregate = true;
constituents = map hydraJob (flatten drv.constituents);
}
// (listToAttrs outputsList);
makeOutput = outputName:
let output = drv.${outputName}; in
{ name = outputName;
value = commonAttrs // {
outPath = output.outPath;
drvPath = output.drvPath;
type = "derivation";
inherit outputName;
};
};
outputsList = map makeOutput outputs;
drv' = (head outputsList).value;
in if drv == null then null else
deepSeq drv' drv';
/**
Make an attribute set (a "scope") from functions that take arguments from that same attribute set.
See [](#ex-makeScope) for how to use it.
# Inputs
1. `newScope` (`AttrSet -> ((AttrSet -> a) | Path) -> AttrSet -> a`)
A function that takes an attribute set `attrs` and returns what ends up as `callPackage` in the output.
Typical values are `callPackageWith` or the output attribute `newScope`.
2. `f` (`AttrSet -> AttrSet`)
A function that takes an attribute set as returned by `makeScope newScope f` (a "scope") and returns any attribute set.
This function is used to compute the fixpoint of the resulting scope using `callPackage`.
Its argument is the lazily evaluated reference to the value of that fixpoint, and is typically called `self` or `final`.
See [](#ex-makeScope) for how to use it.
See [](#sec-functions-library-fixedPoints) for details on fixpoint computation.
# Output
`makeScope` returns an attribute set of a form called `scope`, which also contains the final attributes produced by `f`:
```
scope :: {
callPackage :: ((AttrSet -> a) | Path) -> AttrSet -> a
newScope = AttrSet -> scope
overrideScope = (scope -> scope -> AttrSet) -> scope
packages :: AttrSet -> AttrSet
}
```
- `callPackage` (`((AttrSet -> a) | Path) -> AttrSet -> a`)
A function that
1. Takes a function `p`, or a path to a Nix file that contains a function `p`, which takes an attribute set and returns value of arbitrary type `a`,
2. Takes an attribute set `args` with explicit attributes to pass to `p`,
3. Calls `f` with attributes from the original attribute set `attrs` passed to `newScope` updated with `args, i.e. `attrs // args`, if they match the attributes in the argument of `p`.
All such functions `p` will be called with the same value for `attrs`.
See [](#ex-makeScope-callPackage) for how to use it.
- `newScope` (`AttrSet -> scope`)
Takes an attribute set `attrs` and returns a scope that extends the original scope.
- `overrideScope` (`(scope -> scope -> AttrSet) -> scope`)
Takes a function `g` of the form `final: prev: { # attributes }` to act as an overlay on `f`, and returns a new scope with values determined by `extends g f`.
See [](https://nixos.org/manual/nixpkgs/unstable/#function-library-lib.fixedPoints.extends) for details.
This allows subsequent modification of the final attribute set in a consistent way, i.e. all functions `p` invoked with `callPackage` will be called with the modified values.
- `packages` (`AttrSet -> AttrSet`)
The value of the argument `f` to `makeScope`.
- final attributes
The final values returned by `f`.
# Examples
:::{#ex-makeScope .example}
# Create an interdependent package set on top of `pkgs`
The functions in `foo.nix` and `bar.nix` can depend on each other, in the sense that `foo.nix` can contain a function that expects `bar` as an attribute in its argument.
```nix
let
pkgs = import <nixpkgs> { };
in
pkgs.lib.makeScope pkgs.newScope (self: {
foo = self.callPackage ./foo.nix { };
bar = self.callPackage ./bar.nix { };
})
```
evaluates to
```nix
{
callPackage = «lambda»;
newScope = «lambda»;
overrideScope = «lambda»;
packages = «lambda»;
foo = «derivation»;
bar = «derivation»;
}
```
:::
:::{#ex-makeScope-callPackage .example}
# Using `callPackage` from a scope
```nix
let
pkgs = import <nixpkgs> { };
inherit (pkgs) lib;
scope = lib.makeScope lib.callPackageWith (self: { a = 1; b = 2; });
three = scope.callPackage ({ a, b }: a + b) { };
four = scope.callPackage ({ a, b }: a + b) { a = 2; };
in
[ three four ]
```
evaluates to
```nix
[ 3 4 ]
```
:::
# Type
```
makeScope :: (AttrSet -> ((AttrSet -> a) | Path) -> AttrSet -> a) -> (AttrSet -> AttrSet) -> scope
```
*/
makeScope = newScope: f:
let self = f self // {
newScope = scope: newScope (self // scope);
callPackage = self.newScope {};
overrideScope = g: makeScope newScope (extends g f);
# Remove after 24.11 is released.
overrideScope' = g: warnIf (isInOldestRelease 2311)
"`overrideScope'` (from `lib.makeScope`) has been renamed to `overrideScope`."
(makeScope newScope (extends g f));
packages = f;
};
in self;
/**
backward compatibility with old uncurried form; deprecated
# Inputs
`splicePackages`
: 1\. Function argument
`newScope`
: 2\. Function argument
`otherSplices`
: 3\. Function argument
`keep`
: 4\. Function argument
`extra`
: 5\. Function argument
`f`
: 6\. Function argument
*/
makeScopeWithSplicing =
splicePackages: newScope: otherSplices: keep: extra: f:
makeScopeWithSplicing'
{ inherit splicePackages newScope; }
{ inherit otherSplices keep extra f; };
/**
Like makeScope, but aims to support cross compilation. It's still ugly, but
hopefully it helps a little bit.
# Type
```
makeScopeWithSplicing' ::
{ splicePackages :: Splice -> AttrSet
, newScope :: AttrSet -> ((AttrSet -> a) | Path) -> AttrSet -> a
}
-> { otherSplices :: Splice, keep :: AttrSet -> AttrSet, extra :: AttrSet -> AttrSet }
-> AttrSet
Splice ::
{ pkgsBuildBuild :: AttrSet
, pkgsBuildHost :: AttrSet
, pkgsBuildTarget :: AttrSet
, pkgsHostHost :: AttrSet
, pkgsHostTarget :: AttrSet
, pkgsTargetTarget :: AttrSet
}
```
*/
makeScopeWithSplicing' =
{ splicePackages
, newScope
}:
{ otherSplices
# Attrs from `self` which won't be spliced.
# Avoid using keep, it's only used for a python hook workaround, added in PR #104201.
# ex: `keep = (self: { inherit (self) aAttr; })`
, keep ? (_self: {})
# Additional attrs to add to the sets `callPackage`.
# When the package is from a subset (but not a subset within a package IS #211340)
# within `spliced0` it will be spliced.
# When using an package outside the set but it's available from `pkgs`, use the package from `pkgs.__splicedPackages`.
# If the package is not available within the set or in `pkgs`, such as a package in a let binding, it will not be spliced
# ex:
# ```
# nix-repl> darwin.apple_sdk.frameworks.CoreFoundation
# «derivation ...CoreFoundation-11.0.0.drv»
# nix-repl> darwin.CoreFoundation
# error: attribute 'CoreFoundation' missing
# nix-repl> darwin.callPackage ({ CoreFoundation }: CoreFoundation) { }
# «derivation ...CoreFoundation-11.0.0.drv»
# ```
, extra ? (_spliced0: {})
, f
}:
let
spliced0 = splicePackages {
pkgsBuildBuild = otherSplices.selfBuildBuild;
pkgsBuildHost = otherSplices.selfBuildHost;
pkgsBuildTarget = otherSplices.selfBuildTarget;
pkgsHostHost = otherSplices.selfHostHost;
pkgsHostTarget = self; # Not `otherSplices.selfHostTarget`;
pkgsTargetTarget = otherSplices.selfTargetTarget;
};
spliced = extra spliced0 // spliced0 // keep self;
self = f self // {
newScope = scope: newScope (spliced // scope);
callPackage = newScope spliced; # == self.newScope {};
# N.B. the other stages of the package set spliced in are *not*
# overridden.
overrideScope = g: (makeScopeWithSplicing'
{ inherit splicePackages newScope; }
{ inherit otherSplices keep extra;
f = extends g f;
});
packages = f;
};
in self;
}

246
lib/debug.nix Normal file
View file

@ -0,0 +1,246 @@
/* Collection of functions useful for debugging
broken nix expressions.
* `trace`-like functions take two values, print
the first to stderr and return the second.
* `traceVal`-like functions take one argument
which both printed and returned.
* `traceSeq`-like functions fully evaluate their
traced value before printing (not just to weak
head normal form like trace does by default).
* Functions that end in `-Fn` take an additional
function as their first argument, which is applied
to the traced value before it is printed.
*/
{ lib }:
let
inherit (lib)
isList
isAttrs
substring
attrValues
concatLists
const
elem
generators
id
mapAttrs
trace;
in
rec {
# -- TRACING --
/* Conditionally trace the supplied message, based on a predicate.
Type: traceIf :: bool -> string -> a -> a
Example:
traceIf true "hello" 3
trace: hello
=> 3
*/
traceIf =
# Predicate to check
pred:
# Message that should be traced
msg:
# Value to return
x: if pred then trace msg x else x;
/* Trace the supplied value after applying a function to it, and
return the original value.
Type: traceValFn :: (a -> b) -> a -> a
Example:
traceValFn (v: "mystring ${v}") "foo"
trace: mystring foo
=> "foo"
*/
traceValFn =
# Function to apply
f:
# Value to trace and return
x: trace (f x) x;
/* Trace the supplied value and return it.
Type: traceVal :: a -> a
Example:
traceVal 42
# trace: 42
=> 42
*/
traceVal = traceValFn id;
/* `builtins.trace`, but the value is `builtins.deepSeq`ed first.
Type: traceSeq :: a -> b -> b
Example:
trace { a.b.c = 3; } null
trace: { a = <CODE>; }
=> null
traceSeq { a.b.c = 3; } null
trace: { a = { b = { c = 3; }; }; }
=> null
*/
traceSeq =
# The value to trace
x:
# The value to return
y: trace (builtins.deepSeq x x) y;
/* Like `traceSeq`, but only evaluate down to depth n.
This is very useful because lots of `traceSeq` usages
lead to an infinite recursion.
Example:
traceSeqN 2 { a.b.c = 3; } null
trace: { a = { b = {}; }; }
=> null
Type: traceSeqN :: Int -> a -> b -> b
*/
traceSeqN = depth: x: y:
let snip = v: if isList v then noQuotes "[]" v
else if isAttrs v then noQuotes "{}" v
else v;
noQuotes = str: v: { __pretty = const str; val = v; };
modify = n: fn: v: if (n == 0) then fn v
else if isList v then map (modify (n - 1) fn) v
else if isAttrs v then mapAttrs
(const (modify (n - 1) fn)) v
else v;
in trace (generators.toPretty { allowPrettyValues = true; }
(modify depth snip x)) y;
/* A combination of `traceVal` and `traceSeq` that applies a
provided function to the value to be traced after `deepSeq`ing
it.
*/
traceValSeqFn =
# Function to apply
f:
# Value to trace
v: traceValFn f (builtins.deepSeq v v);
/* A combination of `traceVal` and `traceSeq`. */
traceValSeq = traceValSeqFn id;
/* A combination of `traceVal` and `traceSeqN` that applies a
provided function to the value to be traced. */
traceValSeqNFn =
# Function to apply
f:
depth:
# Value to trace
v: traceSeqN depth (f v) v;
/* A combination of `traceVal` and `traceSeqN`. */
traceValSeqN = traceValSeqNFn id;
/* Trace the input and output of a function `f` named `name`,
both down to `depth`.
This is useful for adding around a function call,
to see the before/after of values as they are transformed.
Example:
traceFnSeqN 2 "id" (x: x) { a.b.c = 3; }
trace: { fn = "id"; from = { a.b = {}; }; to = { a.b = {}; }; }
=> { a.b.c = 3; }
*/
traceFnSeqN = depth: name: f: v:
let res = f v;
in lib.traceSeqN
(depth + 1)
{
fn = name;
from = v;
to = res;
}
res;
# -- TESTING --
/* Evaluates a set of tests.
A test is an attribute set `{expr, expected}`,
denoting an expression and its expected result.
The result is a `list` of __failed tests__, each represented as
`{name, expected, result}`,
- expected
- What was passed as `expected`
- result
- The actual `result` of the test
Used for regression testing of the functions in lib; see
tests.nix for more examples.
Important: Only attributes that start with `test` are executed.
- If you want to run only a subset of the tests add the attribute `tests = ["testName"];`
Example:
runTests {
testAndOk = {
expr = lib.and true false;
expected = false;
};
testAndFail = {
expr = lib.and true false;
expected = true;
};
}
->
[