mirror of
https://github.com/NixOS/nixpkgs.git
synced 2025-06-10 03:23:29 +03:00
[release-24.11] Full treewide Nix format and enforcement [skip treewide] (#395018)
This commit is contained in:
commit
8e03b4396b
1791 changed files with 1009935 additions and 534064 deletions
|
@ -209,3 +209,6 @@ ce21e97a1f20dee15da85c084f9d1148d84f853b
|
|||
|
||||
# treewide nixfmt reformat pass 1, master, staging and staging-next
|
||||
d9d87c51960050e89c79e4025082ed965e770d68
|
||||
|
||||
# treewide format of all Nix files
|
||||
14182c19701221692b84e7428e5b7281b099967a # !autorebase nix-shell --run treefmt
|
||||
|
|
82
.github/workflows/check-nix-format.yml
vendored
82
.github/workflows/check-nix-format.yml
vendored
|
@ -1,8 +1,5 @@
|
|||
# This file was copied mostly from check-maintainers-sorted.yaml.
|
||||
# NOTE: Formatting with the RFC-style nixfmt command is not yet stable. See
|
||||
# https://github.com/NixOS/rfcs/pull/166.
|
||||
# Because of this, this action is not yet enabled for all files -- only for
|
||||
# those who have opted in.
|
||||
# NOTE: Formatting with the RFC-style nixfmt command is not yet stable.
|
||||
# See https://github.com/NixOS/rfcs/pull/166.
|
||||
|
||||
name: Check that Nix files are formatted
|
||||
|
||||
|
@ -20,80 +17,27 @@ jobs:
|
|||
name: nixfmt-check
|
||||
runs-on: ubuntu-24.04
|
||||
needs: get-merge-commit
|
||||
if: "needs.get-merge-commit.outputs.mergedSha && !contains(github.event.pull_request.title, '[skip treewide]')"
|
||||
if: needs.get-merge-commit.outputs.mergedSha
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
ref: ${{ needs.get-merge-commit.outputs.mergedSha }}
|
||||
# Fetches the merge commit and its parents
|
||||
fetch-depth: 2
|
||||
|
||||
- name: Checking out target branch
|
||||
run: |
|
||||
target=$(mktemp -d)
|
||||
targetRev=$(git rev-parse HEAD^1)
|
||||
git worktree add "$target" "$targetRev"
|
||||
echo "targetRev=$targetRev" >> "$GITHUB_ENV"
|
||||
echo "target=$target" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Get Nixpkgs revision for nixfmt
|
||||
run: |
|
||||
# pin to a commit from nixpkgs-unstable to avoid e.g. building nixfmt
|
||||
# from staging
|
||||
# This should not be a URL, because it would allow PRs to run arbitrary code in CI!
|
||||
rev=$(jq -r .rev ci/pinned-nixpkgs.json)
|
||||
echo "url=https://github.com/NixOS/nixpkgs/archive/$rev.tar.gz" >> "$GITHUB_ENV"
|
||||
|
||||
- uses: cachix/install-nix-action@02a151ada4993995686f9ed4f1be7cfbb229e56f # v31
|
||||
with:
|
||||
extra_nix_config: sandbox = true
|
||||
nix_path: nixpkgs=${{ env.url }}
|
||||
|
||||
- name: Install nixfmt
|
||||
run: "nix-env -f '<nixpkgs>' -iAP nixfmt-rfc-style"
|
||||
|
||||
- name: Check that Nix files are formatted according to the RFC style
|
||||
- name: Check that Nix files are formatted
|
||||
run: |
|
||||
unformattedFiles=()
|
||||
|
||||
# TODO: Make this more parallel
|
||||
|
||||
# Loop through all Nix files touched by the PR
|
||||
while readarray -d '' -n 2 entry && (( ${#entry[@]} != 0 )); do
|
||||
type=${entry[0]}
|
||||
file=${entry[1]}
|
||||
case $type in
|
||||
A*)
|
||||
source=""
|
||||
dest=$file
|
||||
;;
|
||||
M*)
|
||||
source=$file
|
||||
dest=$file
|
||||
;;
|
||||
C*|R*)
|
||||
source=$file
|
||||
read -r -d '' dest
|
||||
;;
|
||||
*)
|
||||
echo "Ignoring file $file with type $type"
|
||||
continue
|
||||
esac
|
||||
|
||||
# Ignore files that weren't already formatted
|
||||
if [[ -n "$source" ]] && ! nixfmt --check ${{ env.target }}/"$source" 2>/dev/null; then
|
||||
echo "Ignoring file $file because it's not formatted in the target commit"
|
||||
elif ! nixfmt --check "$dest"; then
|
||||
unformattedFiles+=("$dest")
|
||||
fi
|
||||
done < <(git diff -z --name-status ${{ env.targetRev }} -- '*.nix')
|
||||
|
||||
if (( "${#unformattedFiles[@]}" > 0 )); then
|
||||
echo "Some new/changed Nix files are not properly formatted"
|
||||
echo "Please format them using the Nixpkgs-specific \`nixfmt\` by going to the Nixpkgs root directory, running \`nix-shell\`, then:"
|
||||
echo
|
||||
echo "nixfmt ${unformattedFiles[*]@Q}"
|
||||
echo
|
||||
# Note that it's fine to run this on untrusted code because:
|
||||
# - There's no secrets accessible here
|
||||
# - The build is sandboxed
|
||||
if ! nix-build ci -A fmt.check; then
|
||||
echo "Some Nix files are not properly formatted"
|
||||
echo "Please format them by going to the Nixpkgs root directory and running one of:"
|
||||
echo " nix-shell --run treefmt"
|
||||
echo " nix develop --command treefmt"
|
||||
echo " nix fmt"
|
||||
echo "Make sure your branch is up to date with master; rebase if not."
|
||||
echo "If you're having trouble, please ping @NixOS/nix-formatting"
|
||||
exit 1
|
||||
|
|
|
@ -531,14 +531,31 @@ If you removed packages or made some major NixOS changes, write about it in the
|
|||
|
||||
Names of files and directories should be in lowercase, with dashes between words — not in camel case. For instance, it should be `all-packages.nix`, not `allPackages.nix` or `AllPackages.nix`.
|
||||
|
||||
### Formatting
|
||||
|
||||
CI [enforces](./.github/workflows/check-nix-format.yml) all Nix files to be
|
||||
formatted using the [official Nix formatter](https://github.com/NixOS/nixfmt).
|
||||
|
||||
You can ensure this locally using either of these commands:
|
||||
```
|
||||
nix-shell --run treefmt
|
||||
nix develop --command treefmt
|
||||
nix fmt
|
||||
```
|
||||
|
||||
If you're starting your editor in `nix-shell` or `nix develop`,
|
||||
you can also set it up to automatically format the file with `treefmt` on save.
|
||||
|
||||
If you have any problems with formatting, please ping the
|
||||
[formatting team](https://nixos.org/community/teams/formatting/) via
|
||||
[@NixOS/nix-formatting](https://github.com/orgs/NixOS/teams/nix-formatting).
|
||||
|
||||
### Syntax
|
||||
|
||||
- Set up [editorconfig](https://editorconfig.org/) for your editor, such that [the settings](./.editorconfig) are automatically applied.
|
||||
|
||||
- Use `lowerCamelCase` for variable names, not `UpperCamelCase`. Note, this rule does not apply to package attribute names, which instead follow the rules in [package naming](./pkgs/README.md#package-naming).
|
||||
|
||||
- New files must be formatted by entering the `nix-shell` from the repository root and running `nixfmt`.
|
||||
|
||||
- Functions should list their expected arguments as precisely as possible. That is, write
|
||||
|
||||
```nix
|
||||
|
|
|
@ -21,9 +21,52 @@ let
|
|||
config = { };
|
||||
overlays = [ ];
|
||||
};
|
||||
|
||||
fmt =
|
||||
let
|
||||
treefmtNixSrc = fetchTarball {
|
||||
# Master at 2025-02-12
|
||||
url = "https://github.com/numtide/treefmt-nix/archive/4f09b473c936d41582dd744e19f34ec27592c5fd.tar.gz";
|
||||
sha256 = "051vh6raskrxw5k6jncm8zbk9fhbzgm1gxpq9gm5xw1b6wgbgcna";
|
||||
};
|
||||
treefmtEval = (import treefmtNixSrc).evalModule pkgs {
|
||||
# Important: The auto-rebase script uses `git filter-branch --tree-filter`,
|
||||
# which creates trees within the Git repository under `.git-rewrite/t`,
|
||||
# notably without having a `.git` themselves.
|
||||
# So if this projectRootFile were the default `.git/config`,
|
||||
# having the auto-rebase script use treefmt on such a tree would make it
|
||||
# format all files in the _parent_ Git tree as well.
|
||||
projectRootFile = ".git-blame-ignore-revs";
|
||||
|
||||
# Be a bit more verbose by default, so we can see progress happening
|
||||
settings.verbose = 1;
|
||||
|
||||
# By default it's info, which is too noisy since we have many unmatched files
|
||||
settings.on-unmatched = "debug";
|
||||
|
||||
# This uses nixfmt-rfc-style underneath,
|
||||
# the default formatter for Nix code.
|
||||
# See https://github.com/NixOS/nixfmt
|
||||
programs.nixfmt.enable = true;
|
||||
};
|
||||
fs = pkgs.lib.fileset;
|
||||
nixFilesSrc = fs.toSource {
|
||||
root = ../.;
|
||||
fileset = fs.difference (fs.unions [
|
||||
(fs.fileFilter (file: file.hasExt "nix") ../.)
|
||||
../.git-blame-ignore-revs
|
||||
]) (fs.maybeMissing ../.git);
|
||||
};
|
||||
in
|
||||
{
|
||||
shell = treefmtEval.config.build.devShell;
|
||||
pkg = treefmtEval.config.build.wrapper;
|
||||
check = treefmtEval.config.build.check nixFilesSrc;
|
||||
};
|
||||
|
||||
in
|
||||
{
|
||||
inherit pkgs;
|
||||
inherit pkgs fmt;
|
||||
requestReviews = pkgs.callPackage ./request-reviews { };
|
||||
codeownersValidator = pkgs.callPackage ./codeowners-validator { };
|
||||
eval = pkgs.callPackage ./eval { };
|
||||
|
|
255
flake.nix
255
flake.nix
|
@ -3,17 +3,21 @@
|
|||
{
|
||||
description = "A collection of packages for the Nix package manager";
|
||||
|
||||
outputs = { self }:
|
||||
outputs =
|
||||
{ self }:
|
||||
let
|
||||
libVersionInfoOverlay = import ./lib/flake-version-info.nix self;
|
||||
lib = (import ./lib).extend libVersionInfoOverlay;
|
||||
|
||||
forAllSystems = lib.genAttrs lib.systems.flakeExposed;
|
||||
|
||||
jobs = forAllSystems (system: import ./pkgs/top-level/release.nix {
|
||||
nixpkgs = self;
|
||||
inherit system;
|
||||
});
|
||||
jobs = forAllSystems (
|
||||
system:
|
||||
import ./pkgs/top-level/release.nix {
|
||||
nixpkgs = self;
|
||||
inherit system;
|
||||
}
|
||||
);
|
||||
in
|
||||
{
|
||||
/**
|
||||
|
@ -24,116 +28,148 @@
|
|||
|
||||
- `lib.nixos` for other NixOS-provided functionality, such as [`runTest`](https://nixos.org/manual/nixos/unstable/#sec-call-nixos-test-outside-nixos)
|
||||
*/
|
||||
lib = lib.extend (final: prev: {
|
||||
lib = lib.extend (
|
||||
final: prev: {
|
||||
|
||||
/**
|
||||
Other NixOS-provided functionality, such as [`runTest`](https://nixos.org/manual/nixos/unstable/#sec-call-nixos-test-outside-nixos).
|
||||
See also `lib.nixosSystem`.
|
||||
*/
|
||||
nixos = import ./nixos/lib { lib = final; };
|
||||
/**
|
||||
Other NixOS-provided functionality, such as [`runTest`](https://nixos.org/manual/nixos/unstable/#sec-call-nixos-test-outside-nixos).
|
||||
See also `lib.nixosSystem`.
|
||||
*/
|
||||
nixos = import ./nixos/lib { lib = final; };
|
||||
|
||||
/**
|
||||
Create a NixOS system configuration.
|
||||
/**
|
||||
Create a NixOS system configuration.
|
||||
|
||||
Example:
|
||||
Example:
|
||||
|
||||
lib.nixosSystem {
|
||||
modules = [ ./configuration.nix ];
|
||||
lib.nixosSystem {
|
||||
modules = [ ./configuration.nix ];
|
||||
}
|
||||
|
||||
Inputs:
|
||||
|
||||
- `modules` (list of paths or inline modules): The NixOS modules to include in the system configuration.
|
||||
|
||||
- `specialArgs` (attribute set): Extra arguments to pass to all modules, that are available in `imports` but can not be extended or overridden by the `modules`.
|
||||
|
||||
- `modulesLocation` (path): A default location for modules that aren't passed by path, used for error messages.
|
||||
|
||||
Legacy inputs:
|
||||
|
||||
- `system`: Legacy alias for `nixpkgs.hostPlatform`, but this is already set in the generated `hardware-configuration.nix`, included by `configuration.nix`.
|
||||
- `pkgs`: Legacy alias for `nixpkgs.pkgs`; use `nixpkgs.pkgs` and `nixosModules.readOnlyPkgs` instead.
|
||||
*/
|
||||
nixosSystem =
|
||||
args:
|
||||
import ./nixos/lib/eval-config.nix (
|
||||
{
|
||||
lib = final;
|
||||
# Allow system to be set modularly in nixpkgs.system.
|
||||
# We set it to null, to remove the "legacy" entrypoint's
|
||||
# non-hermetic default.
|
||||
system = null;
|
||||
|
||||
modules = args.modules ++ [
|
||||
# This module is injected here since it exposes the nixpkgs self-path in as
|
||||
# constrained of contexts as possible to avoid more things depending on it and
|
||||
# introducing unnecessary potential fragility to changes in flakes itself.
|
||||
#
|
||||
# See: failed attempt to make pkgs.path not copy when using flakes:
|
||||
# https://github.com/NixOS/nixpkgs/pull/153594#issuecomment-1023287913
|
||||
(
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
config.nixpkgs.flake.source = self.outPath;
|
||||
}
|
||||
)
|
||||
];
|
||||
}
|
||||
// builtins.removeAttrs args [ "modules" ]
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
Inputs:
|
||||
|
||||
- `modules` (list of paths or inline modules): The NixOS modules to include in the system configuration.
|
||||
|
||||
- `specialArgs` (attribute set): Extra arguments to pass to all modules, that are available in `imports` but can not be extended or overridden by the `modules`.
|
||||
|
||||
- `modulesLocation` (path): A default location for modules that aren't passed by path, used for error messages.
|
||||
|
||||
Legacy inputs:
|
||||
|
||||
- `system`: Legacy alias for `nixpkgs.hostPlatform`, but this is already set in the generated `hardware-configuration.nix`, included by `configuration.nix`.
|
||||
- `pkgs`: Legacy alias for `nixpkgs.pkgs`; use `nixpkgs.pkgs` and `nixosModules.readOnlyPkgs` instead.
|
||||
*/
|
||||
nixosSystem = args:
|
||||
import ./nixos/lib/eval-config.nix (
|
||||
checks = forAllSystems (
|
||||
system:
|
||||
{
|
||||
tarball = jobs.${system}.tarball;
|
||||
}
|
||||
//
|
||||
lib.optionalAttrs
|
||||
(
|
||||
self.legacyPackages.${system}.stdenv.hostPlatform.isLinux
|
||||
# Exclude power64 due to "libressl is not available on the requested hostPlatform" with hostPlatform being power64
|
||||
&& !self.legacyPackages.${system}.targetPlatform.isPower64
|
||||
# Exclude armv6l-linux due to "cannot bootstrap GHC on this platform ('armv6l-linux' with libc 'defaultLibc')"
|
||||
&& system != "armv6l-linux"
|
||||
# Exclude riscv64-linux due to "cannot bootstrap GHC on this platform ('riscv64-linux' with libc 'defaultLibc')"
|
||||
&& system != "riscv64-linux"
|
||||
)
|
||||
{
|
||||
lib = final;
|
||||
# Allow system to be set modularly in nixpkgs.system.
|
||||
# We set it to null, to remove the "legacy" entrypoint's
|
||||
# non-hermetic default.
|
||||
system = null;
|
||||
|
||||
modules = args.modules ++ [
|
||||
# This module is injected here since it exposes the nixpkgs self-path in as
|
||||
# constrained of contexts as possible to avoid more things depending on it and
|
||||
# introducing unnecessary potential fragility to changes in flakes itself.
|
||||
#
|
||||
# See: failed attempt to make pkgs.path not copy when using flakes:
|
||||
# https://github.com/NixOS/nixpkgs/pull/153594#issuecomment-1023287913
|
||||
({ config, pkgs, lib, ... }: {
|
||||
config.nixpkgs.flake.source = self.outPath;
|
||||
})
|
||||
];
|
||||
} // builtins.removeAttrs args [ "modules" ]
|
||||
);
|
||||
});
|
||||
|
||||
checks = forAllSystems (system: {
|
||||
tarball = jobs.${system}.tarball;
|
||||
} // lib.optionalAttrs
|
||||
(
|
||||
self.legacyPackages.${system}.stdenv.hostPlatform.isLinux
|
||||
# Exclude power64 due to "libressl is not available on the requested hostPlatform" with hostPlatform being power64
|
||||
&& !self.legacyPackages.${system}.targetPlatform.isPower64
|
||||
# Exclude armv6l-linux due to "cannot bootstrap GHC on this platform ('armv6l-linux' with libc 'defaultLibc')"
|
||||
&& system != "armv6l-linux"
|
||||
# Exclude riscv64-linux due to "cannot bootstrap GHC on this platform ('riscv64-linux' with libc 'defaultLibc')"
|
||||
&& system != "riscv64-linux"
|
||||
)
|
||||
{
|
||||
# Test that ensures that the nixosSystem function can accept a lib argument
|
||||
# Note: prefer not to extend or modify `lib`, especially if you want to share reusable modules
|
||||
# alternatives include: `import` a file, or put a custom library in an option or in `_module.args.<libname>`
|
||||
nixosSystemAcceptsLib = (self.lib.nixosSystem {
|
||||
pkgs = self.legacyPackages.${system};
|
||||
lib = self.lib.extend (final: prev: {
|
||||
ifThisFunctionIsMissingTheTestFails = final.id;
|
||||
});
|
||||
modules = [
|
||||
./nixos/modules/profiles/minimal.nix
|
||||
({ lib, ... }: lib.ifThisFunctionIsMissingTheTestFails {
|
||||
# Define a minimal config without eval warnings
|
||||
nixpkgs.hostPlatform = "x86_64-linux";
|
||||
boot.loader.grub.enable = false;
|
||||
fileSystems."/".device = "nodev";
|
||||
# See https://search.nixos.org/options?show=system.stateVersion&query=stateversion
|
||||
system.stateVersion = lib.trivial.release; # DON'T do this in real configs!
|
||||
})
|
||||
];
|
||||
}).config.system.build.toplevel;
|
||||
});
|
||||
# Test that ensures that the nixosSystem function can accept a lib argument
|
||||
# Note: prefer not to extend or modify `lib`, especially if you want to share reusable modules
|
||||
# alternatives include: `import` a file, or put a custom library in an option or in `_module.args.<libname>`
|
||||
nixosSystemAcceptsLib =
|
||||
(self.lib.nixosSystem {
|
||||
pkgs = self.legacyPackages.${system};
|
||||
lib = self.lib.extend (
|
||||
final: prev: {
|
||||
ifThisFunctionIsMissingTheTestFails = final.id;
|
||||
}
|
||||
);
|
||||
modules = [
|
||||
./nixos/modules/profiles/minimal.nix
|
||||
(
|
||||
{ lib, ... }:
|
||||
lib.ifThisFunctionIsMissingTheTestFails {
|
||||
# Define a minimal config without eval warnings
|
||||
nixpkgs.hostPlatform = "x86_64-linux";
|
||||
boot.loader.grub.enable = false;
|
||||
fileSystems."/".device = "nodev";
|
||||
# See https://search.nixos.org/options?show=system.stateVersion&query=stateversion
|
||||
system.stateVersion = lib.trivial.release; # DON'T do this in real configs!
|
||||
}
|
||||
)
|
||||
];
|
||||
}).config.system.build.toplevel;
|
||||
}
|
||||
);
|
||||
|
||||
htmlDocs = {
|
||||
nixpkgsManual = builtins.mapAttrs (_: jobSet: jobSet.manual) jobs;
|
||||
nixosManual = (import ./nixos/release-small.nix {
|
||||
nixpkgs = self;
|
||||
}).nixos.manual;
|
||||
nixosManual =
|
||||
(import ./nixos/release-small.nix {
|
||||
nixpkgs = self;
|
||||
}).nixos.manual;
|
||||
};
|
||||
|
||||
devShells = forAllSystems (system:
|
||||
{ } // lib.optionalAttrs
|
||||
(
|
||||
# Exclude armv6l-linux because "Package ‘ghc-9.6.6’ in .../pkgs/development/compilers/ghc/common-hadrian.nix:579 is not available on the requested hostPlatform"
|
||||
system != "armv6l-linux"
|
||||
# Exclude riscv64-linux because "Package ‘ghc-9.6.6’ in .../pkgs/development/compilers/ghc/common-hadrian.nix:579 is not available on the requested hostPlatform"
|
||||
&& system != "riscv64-linux"
|
||||
# Exclude FreeBSD because "Package ‘ghc-9.6.6’ in .../pkgs/development/compilers/ghc/common-hadrian.nix:579 is not available on the requested hostPlatform"
|
||||
&& !self.legacyPackages.${system}.stdenv.hostPlatform.isFreeBSD
|
||||
)
|
||||
{
|
||||
/** A shell to get tooling for Nixpkgs development. See nixpkgs/shell.nix. */
|
||||
default = import ./shell.nix { inherit system; };
|
||||
});
|
||||
devShells = forAllSystems (
|
||||
system:
|
||||
{ }
|
||||
//
|
||||
lib.optionalAttrs
|
||||
(
|
||||
# Exclude armv6l-linux because "Package ‘ghc-9.6.6’ in .../pkgs/development/compilers/ghc/common-hadrian.nix:579 is not available on the requested hostPlatform"
|
||||
system != "armv6l-linux"
|
||||
# Exclude riscv64-linux because "Package ‘ghc-9.6.6’ in .../pkgs/development/compilers/ghc/common-hadrian.nix:579 is not available on the requested hostPlatform"
|
||||
&& system != "riscv64-linux"
|
||||
# Exclude FreeBSD because "Package ‘ghc-9.6.6’ in .../pkgs/development/compilers/ghc/common-hadrian.nix:579 is not available on the requested hostPlatform"
|
||||
&& !self.legacyPackages.${system}.stdenv.hostPlatform.isFreeBSD
|
||||
)
|
||||
{
|
||||
/**
|
||||
A shell to get tooling for Nixpkgs development. See nixpkgs/shell.nix.
|
||||
*/
|
||||
default = import ./shell.nix { inherit system; };
|
||||
}
|
||||
);
|
||||
|
||||
formatter = forAllSystems (system: (import ./ci { inherit system; }).fmt.pkg);
|
||||
|
||||
/**
|
||||
A nested structure of [packages](https://nix.dev/manual/nix/latest/glossary#package-attribute-set) and other values.
|
||||
|
@ -154,10 +190,13 @@
|
|||
evaluation. Evaluating the attribute value tends to require a significant
|
||||
amount of computation, even considering lazy evaluation.
|
||||
*/
|
||||
legacyPackages = forAllSystems (system:
|
||||
(import ./. { inherit system; }).extend (final: prev: {
|
||||
lib = prev.lib.extend libVersionInfoOverlay;
|
||||
})
|
||||
legacyPackages = forAllSystems (
|
||||
system:
|
||||
(import ./. { inherit system; }).extend (
|
||||
final: prev: {
|
||||
lib = prev.lib.extend libVersionInfoOverlay;
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
/**
|
||||
|
@ -176,7 +215,7 @@
|
|||
};
|
||||
};
|
||||
};
|
||||
*/
|
||||
*/
|
||||
nixosModules = {
|
||||
notDetected = ./nixos/modules/installer/scan/not-detected.nix;
|
||||
|
||||
|
|
587
lib/attrsets.nix
587
lib/attrsets.nix
File diff suppressed because it is too large
Load diff
|
@ -2,19 +2,45 @@
|
|||
|
||||
let
|
||||
inherit (builtins)
|
||||
intersectAttrs;
|
||||
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 toFunction id
|
||||
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
|
||||
toFunction
|
||||
id
|
||||
;
|
||||
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
|
||||
|
@ -40,7 +66,6 @@ rec {
|
|||
You should in general prefer `drv.overrideAttrs` over this function;
|
||||
see the nixpkgs manual for more information on overriding.
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`drv`
|
||||
|
@ -74,20 +99,21 @@ rec {
|
|||
|
||||
:::
|
||||
*/
|
||||
overrideDerivation = drv: f:
|
||||
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 {};
|
||||
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);
|
||||
});
|
||||
|
||||
// (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
|
||||
|
@ -97,7 +123,6 @@ rec {
|
|||
Please refer to documentation on [`<pkg>.overrideDerivation`](#sec-pkg-overrideDerivation) to learn about `overrideDerivation` and caveats
|
||||
related to its use.
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`f`
|
||||
|
@ -128,37 +153,42 @@ rec {
|
|||
|
||||
:::
|
||||
*/
|
||||
makeOverridable = f:
|
||||
makeOverridable =
|
||||
f:
|
||||
let
|
||||
# Creates a functor with the same arguments as f
|
||||
mirrorArgs = mirrorFunctionArgs f;
|
||||
in
|
||||
mirrorArgs (origArgs:
|
||||
let
|
||||
result = f origArgs;
|
||||
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);
|
||||
# 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
|
||||
# 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 // {
|
||||
result
|
||||
// {
|
||||
override = overrideArgs;
|
||||
overrideDerivation = fdrv: overrideResult (x: overrideDerivation x fdrv);
|
||||
${if result ? overrideAttrs then "overrideAttrs" else null} = fdrv:
|
||||
overrideResult (x: x.overrideAttrs 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) // {
|
||||
setFunctionArgs result (functionArgs result)
|
||||
// {
|
||||
override = overrideArgs;
|
||||
}
|
||||
else result);
|
||||
|
||||
else
|
||||
result
|
||||
);
|
||||
|
||||
/**
|
||||
Call the package function in the file `fn` with the required
|
||||
|
@ -188,7 +218,6 @@ rec {
|
|||
|
||||
<!-- TODO: Apply "Example:" tag to the examples above -->
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`autoArgs`
|
||||
|
@ -209,7 +238,8 @@ rec {
|
|||
callPackageWith :: AttrSet -> ((AttrSet -> a) | Path) -> AttrSet -> a
|
||||
```
|
||||
*/
|
||||
callPackageWith = autoArgs: fn: args:
|
||||
callPackageWith =
|
||||
autoArgs: fn: args:
|
||||
let
|
||||
f = if isFunction fn then fn else import fn;
|
||||
fargs = functionArgs f;
|
||||
|
@ -222,59 +252,72 @@ rec {
|
|||
# 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)));
|
||||
(
|
||||
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 + "\""))
|
||||
];
|
||||
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}?";
|
||||
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:
|
||||
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
|
||||
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 "
|
||||
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}";
|
||||
|
||||
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`
|
||||
|
@ -295,7 +338,8 @@ rec {
|
|||
callPackagesWith :: AttrSet -> ((AttrSet -> AttrSet) | Path) -> AttrSet -> AttrSet
|
||||
```
|
||||
*/
|
||||
callPackagesWith = autoArgs: fn: args:
|
||||
callPackagesWith =
|
||||
autoArgs: fn: args:
|
||||
let
|
||||
f = if isFunction fn then fn else import fn;
|
||||
auto = intersectAttrs (functionArgs f) autoArgs;
|
||||
|
@ -304,18 +348,19 @@ rec {
|
|||
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;
|
||||
|
||||
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`
|
||||
|
@ -336,34 +381,48 @@ rec {
|
|||
extendDerivation :: Bool -> Any -> Derivation -> Derivation
|
||||
```
|
||||
*/
|
||||
extendDerivation = condition: passthru: drv:
|
||||
extendDerivation =
|
||||
condition: passthru: drv:
|
||||
let
|
||||
outputs = drv.outputs or [ "out" ];
|
||||
|
||||
commonAttrs = drv // (listToAttrs outputsList) //
|
||||
({ all = map (x: x.value) outputsList; }) // passthru;
|
||||
commonAttrs =
|
||||
drv // (listToAttrs outputsList) // ({ all = map (x: x.value) outputsList; }) // passthru;
|
||||
|
||||
outputToAttrListElement = outputName:
|
||||
{ name = outputName;
|
||||
value = commonAttrs // {
|
||||
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;
|
||||
} //
|
||||
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) {
|
||||
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;
|
||||
in
|
||||
commonAttrs
|
||||
// {
|
||||
drvPath =
|
||||
assert condition;
|
||||
drv.drvPath;
|
||||
outPath =
|
||||
assert condition;
|
||||
drv.outPath;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -372,7 +431,6 @@ rec {
|
|||
result to ensure that there are no thunks kept alive to prevent
|
||||
garbage collection.
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`drv`
|
||||
|
@ -385,21 +443,29 @@ rec {
|
|||
hydraJob :: (Derivation | Null) -> (Derivation | Null)
|
||||
```
|
||||
*/
|
||||
hydraJob = drv:
|
||||
hydraJob =
|
||||
drv:
|
||||
let
|
||||
outputs = drv.outputs or ["out"];
|
||||
outputs = drv.outputs or [ "out" ];
|
||||
|
||||
commonAttrs =
|
||||
{ inherit (drv) name system meta; inherit outputs; }
|
||||
{
|
||||
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;
|
||||
makeOutput =
|
||||
outputName:
|
||||
let
|
||||
output = drv.${outputName};
|
||||
in
|
||||
{
|
||||
name = outputName;
|
||||
value = commonAttrs // {
|
||||
outPath = output.outPath;
|
||||
drvPath = output.drvPath;
|
||||
|
@ -411,8 +477,8 @@ rec {
|
|||
outputsList = map makeOutput outputs;
|
||||
|
||||
drv' = (head outputsList).value;
|
||||
in if drv == null then null else
|
||||
deepSeq drv' drv';
|
||||
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.
|
||||
|
@ -538,23 +604,27 @@ rec {
|
|||
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)
|
||||
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;
|
||||
packages = f;
|
||||
};
|
||||
in
|
||||
self;
|
||||
|
||||
/**
|
||||
backward compatibility with old uncurried form; deprecated
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`splicePackages`
|
||||
|
@ -583,9 +653,14 @@ rec {
|
|||
*/
|
||||
makeScopeWithSplicing =
|
||||
splicePackages: newScope: otherSplices: keep: extra: f:
|
||||
makeScopeWithSplicing'
|
||||
{ inherit splicePackages newScope; }
|
||||
{ inherit 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
|
||||
|
@ -612,30 +687,32 @@ rec {
|
|||
```
|
||||
*/
|
||||
makeScopeWithSplicing' =
|
||||
{ splicePackages
|
||||
, newScope
|
||||
{
|
||||
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
|
||||
{
|
||||
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 {
|
||||
|
@ -652,14 +729,16 @@ rec {
|
|||
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;
|
||||
overrideScope =
|
||||
g:
|
||||
(makeScopeWithSplicing' { inherit splicePackages newScope; } {
|
||||
inherit otherSplices keep extra;
|
||||
f = extends g f;
|
||||
});
|
||||
packages = f;
|
||||
};
|
||||
in self;
|
||||
in
|
||||
self;
|
||||
|
||||
/**
|
||||
Define a `mkDerivation`-like function based on another `mkDerivation`-like function.
|
||||
|
|
674
lib/default.nix
674
lib/default.nix
|
@ -1,179 +1,529 @@
|
|||
/* Library of low-level helper functions for nix expressions.
|
||||
*
|
||||
* Please implement (mostly) exhaustive unit tests
|
||||
* for new functions in `./tests.nix`.
|
||||
*/
|
||||
/*
|
||||
Library of low-level helper functions for nix expressions.
|
||||
|
||||
Please implement (mostly) exhaustive unit tests
|
||||
for new functions in `./tests.nix`.
|
||||
*/
|
||||
let
|
||||
|
||||
inherit (import ./fixed-points.nix { inherit lib; }) makeExtensible;
|
||||
|
||||
lib = makeExtensible (self: let
|
||||
callLibs = file: import file { lib = self; };
|
||||
in {
|
||||
lib = makeExtensible (
|
||||
self:
|
||||
let
|
||||
callLibs = file: import file { lib = self; };
|
||||
in
|
||||
{
|
||||
|
||||
# often used, or depending on very little
|
||||
trivial = callLibs ./trivial.nix;
|
||||
fixedPoints = callLibs ./fixed-points.nix;
|
||||
# often used, or depending on very little
|
||||
trivial = callLibs ./trivial.nix;
|
||||
fixedPoints = callLibs ./fixed-points.nix;
|
||||
|
||||
# datatypes
|
||||
attrsets = callLibs ./attrsets.nix;
|
||||
lists = callLibs ./lists.nix;
|
||||
strings = callLibs ./strings.nix;
|
||||
stringsWithDeps = callLibs ./strings-with-deps.nix;
|
||||
# datatypes
|
||||
attrsets = callLibs ./attrsets.nix;
|
||||
lists = callLibs ./lists.nix;
|
||||
strings = callLibs ./strings.nix;
|
||||
stringsWithDeps = callLibs ./strings-with-deps.nix;
|
||||
|
||||
# packaging
|
||||
customisation = callLibs ./customisation.nix;
|
||||
derivations = callLibs ./derivations.nix;
|
||||
maintainers = import ../maintainers/maintainer-list.nix;
|
||||
teams = callLibs ../maintainers/team-list.nix;
|
||||
meta = callLibs ./meta.nix;
|
||||
versions = callLibs ./versions.nix;
|
||||
# packaging
|
||||
customisation = callLibs ./customisation.nix;
|
||||
derivations = callLibs ./derivations.nix;
|
||||
maintainers = import ../maintainers/maintainer-list.nix;
|
||||
teams = callLibs ../maintainers/team-list.nix;
|
||||
meta = callLibs ./meta.nix;
|
||||
versions = callLibs ./versions.nix;
|
||||
|
||||
# module system
|
||||
modules = callLibs ./modules.nix;
|
||||
options = callLibs ./options.nix;
|
||||
types = callLibs ./types.nix;
|
||||
# module system
|
||||
modules = callLibs ./modules.nix;
|
||||
options = callLibs ./options.nix;
|
||||
types = callLibs ./types.nix;
|
||||
|
||||
# constants
|
||||
licenses = callLibs ./licenses.nix;
|
||||
sourceTypes = callLibs ./source-types.nix;
|
||||
systems = callLibs ./systems;
|
||||
# constants
|
||||
licenses = callLibs ./licenses.nix;
|
||||
sourceTypes = callLibs ./source-types.nix;
|
||||
systems = callLibs ./systems;
|
||||
|
||||
# serialization
|
||||
cli = callLibs ./cli.nix;
|
||||
gvariant = callLibs ./gvariant.nix;
|
||||
generators = callLibs ./generators.nix;
|
||||
# serialization
|
||||
cli = callLibs ./cli.nix;
|
||||
gvariant = callLibs ./gvariant.nix;
|
||||
generators = callLibs ./generators.nix;
|
||||
|
||||
# misc
|
||||
asserts = callLibs ./asserts.nix;
|
||||
debug = callLibs ./debug.nix;
|
||||
misc = callLibs ./deprecated/misc.nix;
|
||||
# misc
|
||||
asserts = callLibs ./asserts.nix;
|
||||
debug = callLibs ./debug.nix;
|
||||
misc = callLibs ./deprecated/misc.nix;
|
||||
|
||||
# domain-specific
|
||||
fetchers = callLibs ./fetchers.nix;
|
||||
# domain-specific
|
||||
fetchers = callLibs ./fetchers.nix;
|
||||
|
||||
# Eval-time filesystem handling
|
||||
path = callLibs ./path;
|
||||
filesystem = callLibs ./filesystem.nix;
|
||||
fileset = callLibs ./fileset;
|
||||
sources = callLibs ./sources.nix;
|
||||
# Eval-time filesystem handling
|
||||
path = callLibs ./path;
|
||||
filesystem = callLibs ./filesystem.nix;
|
||||
fileset = callLibs ./fileset;
|
||||
sources = callLibs ./sources.nix;
|
||||
|
||||
# back-compat aliases
|
||||
platforms = self.systems.doubles;
|
||||
# back-compat aliases
|
||||
platforms = self.systems.doubles;
|
||||
|
||||
# linux kernel configuration
|
||||
kernel = callLibs ./kernel.nix;
|
||||
# linux kernel configuration
|
||||
kernel = callLibs ./kernel.nix;
|
||||
|
||||
# network
|
||||
network = callLibs ./network;
|
||||
# network
|
||||
network = callLibs ./network;
|
||||
|
||||
# TODO: For consistency, all builtins should also be available from a sub-library;
|
||||
# these are the only ones that are currently not
|
||||
inherit (builtins) addErrorContext isPath trace typeOf unsafeGetAttrPos;
|
||||
inherit (self.trivial) id const pipe concat or and xor bitAnd bitOr bitXor
|
||||
bitNot boolToString mergeAttrs flip defaultTo mapNullable inNixShell isFloat min max
|
||||
importJSON importTOML warn warnIf warnIfNot throwIf throwIfNot checkListOfEnum
|
||||
info showWarnings nixpkgsVersion version isInOldestRelease oldestSupportedReleaseIsAtLeast
|
||||
mod compare splitByAndCompare seq deepSeq lessThan add sub
|
||||
functionArgs setFunctionArgs isFunction toFunction mirrorFunctionArgs
|
||||
fromHexString toHexString toBaseDigits inPureEvalMode isBool isInt pathExists
|
||||
genericClosure readFile;
|
||||
inherit (self.fixedPoints) fix fix' converge extends composeExtensions
|
||||
composeManyExtensions makeExtensible makeExtensibleWithCustomName
|
||||
toExtension;
|
||||
inherit (self.attrsets) attrByPath hasAttrByPath setAttrByPath
|
||||
getAttrFromPath attrVals attrNames attrValues getAttrs catAttrs filterAttrs
|
||||
filterAttrsRecursive foldlAttrs foldAttrs collect nameValuePair mapAttrs
|
||||
mapAttrs' mapAttrsToList attrsToList concatMapAttrs mapAttrsRecursive
|
||||
mapAttrsRecursiveCond genAttrs isDerivation toDerivation optionalAttrs
|
||||
zipAttrsWithNames zipAttrsWith zipAttrs recursiveUpdateUntil
|
||||
recursiveUpdate matchAttrs mergeAttrsList overrideExisting showAttrPath getOutput getFirstOutput
|
||||
getBin getLib getStatic getDev getInclude getMan chooseDevOutputs zipWithNames zip
|
||||
recurseIntoAttrs dontRecurseIntoAttrs cartesianProduct cartesianProductOfSets
|
||||
mapCartesianProduct updateManyAttrsByPath listToAttrs hasAttr getAttr isAttrs intersectAttrs removeAttrs;
|
||||
inherit (self.lists) singleton forEach map foldr fold foldl foldl' imap0 imap1
|
||||
filter ifilter0 concatMap flatten remove findSingle findFirst any all count
|
||||
optional optionals toList range replicate partition zipListsWith zipLists
|
||||
reverseList listDfs toposort sort sortOn naturalSort compareLists
|
||||
take drop dropEnd sublist last init
|
||||
crossLists unique allUnique intersectLists
|
||||
subtractLists mutuallyExclusive groupBy groupBy' concatLists genList
|
||||
length head tail elem elemAt isList;
|
||||
inherit (self.strings) concatStrings concatMapStrings concatImapStrings
|
||||
stringLength substring isString replaceStrings
|
||||
intersperse concatStringsSep concatMapStringsSep
|
||||
concatImapStringsSep concatLines makeSearchPath makeSearchPathOutput
|
||||
makeLibraryPath makeIncludePath makeBinPath optionalString
|
||||
hasInfix hasPrefix hasSuffix stringToCharacters stringAsChars escape
|
||||
escapeShellArg escapeShellArgs
|
||||
isStorePath isStringLike
|
||||
isValidPosixName toShellVar toShellVars trim trimWith
|
||||
escapeRegex escapeURL escapeXML replaceChars lowerChars
|
||||
upperChars toLower toUpper addContextFrom splitString
|
||||
removePrefix removeSuffix versionOlder versionAtLeast
|
||||
getName getVersion match split
|
||||
cmakeOptionType cmakeBool cmakeFeature
|
||||
mesonOption mesonBool mesonEnable
|
||||
nameFromURL enableFeature enableFeatureAs withFeature
|
||||
withFeatureAs fixedWidthString fixedWidthNumber
|
||||
toInt toIntBase10 readPathsFromFile fileContents;
|
||||
inherit (self.stringsWithDeps) textClosureList textClosureMap
|
||||
noDepEntry fullDepEntry packEntry stringAfter;
|
||||
inherit (self.customisation) overrideDerivation makeOverridable
|
||||
callPackageWith callPackagesWith extendDerivation hydraJob
|
||||
makeScope makeScopeWithSplicing makeScopeWithSplicing'
|
||||
extendMkDerivation;
|
||||
inherit (self.derivations) lazyDerivation optionalDrvAttr warnOnInstantiate;
|
||||
inherit (self.meta) addMetaAttrs dontDistribute setName updateName
|
||||
appendToName mapDerivationAttrset setPrio lowPrio lowPrioSet hiPrio
|
||||
hiPrioSet licensesSpdx getLicenseFromSpdxId getLicenseFromSpdxIdOr
|
||||
getExe getExe';
|
||||
inherit (self.filesystem) pathType pathIsDirectory pathIsRegularFile
|
||||
packagesFromDirectoryRecursive;
|
||||
inherit (self.sources) cleanSourceFilter
|
||||
cleanSource sourceByRegex sourceFilesBySuffices
|
||||
commitIdFromGitRepo cleanSourceWith pathHasContext
|
||||
canCleanSource pathIsGitRepo;
|
||||
inherit (self.modules) evalModules setDefaultModuleLocation
|
||||
unifyModuleSyntax applyModuleArgsIfFunction mergeModules
|
||||
mergeModules' mergeOptionDecls mergeDefinitions
|
||||
pushDownProperties dischargeProperties filterOverrides
|
||||
sortProperties fixupOptionType mkIf mkAssert mkMerge mkOverride
|
||||
mkOptionDefault mkDefault mkImageMediaOverride mkForce mkVMOverride
|
||||
mkFixStrictness mkOrder mkBefore mkAfter mkAliasDefinitions
|
||||
mkAliasAndWrapDefinitions fixMergeModules mkRemovedOptionModule
|
||||
mkRenamedOptionModule mkRenamedOptionModuleWith
|
||||
mkMergedOptionModule mkChangedOptionModule
|
||||
mkAliasOptionModule mkDerivedConfig doRename
|
||||
mkAliasOptionModuleMD;
|
||||
evalOptionValue = lib.warn "External use of `lib.evalOptionValue` is deprecated. If your use case isn't covered by non-deprecated functions, we'd like to know more and perhaps support your use case well, instead of providing access to these low level functions. In this case please open an issue in https://github.com/nixos/nixpkgs/issues/." self.modules.evalOptionValue;
|
||||
inherit (self.options) isOption mkEnableOption mkSinkUndeclaredOptions
|
||||
mergeDefaultOption mergeOneOption mergeEqualOption mergeUniqueOption
|
||||
getValues getFiles
|
||||
optionAttrSetToDocList optionAttrSetToDocList'
|
||||
scrubOptionValue literalExpression literalExample
|
||||
showOption showOptionWithDefLocs showFiles
|
||||
unknownModule mkOption mkPackageOption mkPackageOptionMD
|
||||
literalMD;
|
||||
inherit (self.types) isType setType defaultTypeMerge defaultFunctor
|
||||
isOptionType mkOptionType;
|
||||
inherit (self.asserts)
|
||||
assertMsg assertOneOf;
|
||||
inherit (self.debug) traceIf traceVal traceValFn
|
||||
traceSeq traceSeqN traceValSeq
|
||||
traceValSeqFn traceValSeqN traceValSeqNFn traceFnSeqN
|
||||
runTests testAllTrue;
|
||||
inherit (self.misc) maybeEnv defaultMergeArg defaultMerge foldArgs
|
||||
maybeAttrNullable maybeAttr ifEnable checkFlag getValue
|
||||
checkReqs uniqList uniqListExt condConcat lazyGenericClosure
|
||||
innerModifySumArgs modifySumArgs innerClosePropagation
|
||||
closePropagation mapAttrsFlatten nvs setAttr setAttrMerge
|
||||
mergeAttrsWithFunc mergeAttrsConcatenateValues
|
||||
mergeAttrsNoOverride mergeAttrByFunc mergeAttrsByFuncDefaults
|
||||
mergeAttrsByFuncDefaultsClean mergeAttrBy
|
||||
fakeHash fakeSha256 fakeSha512
|
||||
nixType imap;
|
||||
inherit (self.versions)
|
||||
splitVersion;
|
||||
});
|
||||
in lib
|
||||
# TODO: For consistency, all builtins should also be available from a sub-library;
|
||||
# these are the only ones that are currently not
|
||||
inherit (builtins)
|
||||
addErrorContext
|
||||
isPath
|
||||
trace
|
||||
typeOf
|
||||
unsafeGetAttrPos
|
||||
;
|
||||
inherit (self.trivial)
|
||||
id
|
||||
const
|
||||
pipe
|
||||
concat
|
||||
or
|
||||
and
|
||||
xor
|
||||
bitAnd
|
||||
bitOr
|
||||
bitXor
|
||||
bitNot
|
||||
boolToString
|
||||
mergeAttrs
|
||||
flip
|
||||
defaultTo
|
||||
mapNullable
|
||||
inNixShell
|
||||
isFloat
|
||||
min
|
||||
max
|
||||
importJSON
|
||||
importTOML
|
||||
warn
|
||||
warnIf
|
||||
warnIfNot
|
||||
throwIf
|
||||
throwIfNot
|
||||
checkListOfEnum
|
||||
info
|
||||
showWarnings
|
||||
nixpkgsVersion
|
||||
version
|
||||
isInOldestRelease
|
||||
oldestSupportedReleaseIsAtLeast
|
||||
mod
|
||||
compare
|
||||
splitByAndCompare
|
||||
seq
|
||||
deepSeq
|
||||
lessThan
|
||||
add
|
||||
sub
|
||||
functionArgs
|
||||
setFunctionArgs
|
||||
isFunction
|
||||
toFunction
|
||||
mirrorFunctionArgs
|
||||
fromHexString
|
||||
toHexString
|
||||
toBaseDigits
|
||||
inPureEvalMode
|
||||
isBool
|
||||
isInt
|
||||
pathExists
|
||||
genericClosure
|
||||
readFile
|
||||
;
|
||||
inherit (self.fixedPoints)
|
||||
fix
|
||||
fix'
|
||||
converge
|
||||
extends
|
||||
composeExtensions
|
||||
composeManyExtensions
|
||||
makeExtensible
|
||||
makeExtensibleWithCustomName
|
||||
toExtension
|
||||
;
|
||||
inherit (self.attrsets)
|
||||
attrByPath
|
||||
hasAttrByPath
|
||||
setAttrByPath
|
||||
getAttrFromPath
|
||||
attrVals
|
||||
attrNames
|
||||
attrValues
|
||||
getAttrs
|
||||
catAttrs
|
||||
filterAttrs
|
||||
filterAttrsRecursive
|
||||
foldlAttrs
|
||||
foldAttrs
|
||||
collect
|
||||
nameValuePair
|
||||
mapAttrs
|
||||
mapAttrs'
|
||||
mapAttrsToList
|
||||
attrsToList
|
||||
concatMapAttrs
|
||||
mapAttrsRecursive
|
||||
mapAttrsRecursiveCond
|
||||
genAttrs
|
||||
isDerivation
|
||||
toDerivation
|
||||
optionalAttrs
|
||||
zipAttrsWithNames
|
||||
zipAttrsWith
|
||||
zipAttrs
|
||||
recursiveUpdateUntil
|
||||
recursiveUpdate
|
||||
matchAttrs
|
||||
mergeAttrsList
|
||||
overrideExisting
|
||||
showAttrPath
|
||||
getOutput
|
||||
getFirstOutput
|
||||
getBin
|
||||
getLib
|
||||
getStatic
|
||||
getDev
|
||||
getInclude
|
||||
getMan
|
||||
chooseDevOutputs
|
||||
zipWithNames
|
||||
zip
|
||||
recurseIntoAttrs
|
||||
dontRecurseIntoAttrs
|
||||
cartesianProduct
|
||||
cartesianProductOfSets
|
||||
mapCartesianProduct
|
||||
updateManyAttrsByPath
|
||||
listToAttrs
|
||||
hasAttr
|
||||
getAttr
|
||||
isAttrs
|
||||
intersectAttrs
|
||||
removeAttrs
|
||||
;
|
||||
inherit (self.lists)
|
||||
singleton
|
||||
forEach
|
||||
map
|
||||
foldr
|
||||
fold
|
||||
foldl
|
||||
foldl'
|
||||
imap0
|
||||
imap1
|
||||
filter
|
||||
ifilter0
|
||||
concatMap
|
||||
flatten
|
||||
remove
|
||||
findSingle
|
||||
findFirst
|
||||
any
|
||||
all
|
||||
count
|
||||
optional
|
||||
optionals
|
||||
toList
|
||||
range
|
||||
replicate
|
||||
partition
|
||||
zipListsWith
|
||||
zipLists
|
||||
reverseList
|
||||
listDfs
|
||||
toposort
|
||||
sort
|
||||
sortOn
|
||||
naturalSort
|
||||
compareLists
|
||||
take
|
||||
drop
|
||||
dropEnd
|
||||
sublist
|
||||
last
|
||||
init
|
||||
crossLists
|
||||
unique
|
||||
allUnique
|
||||
intersectLists
|
||||
subtractLists
|
||||
mutuallyExclusive
|
||||
groupBy
|
||||
groupBy'
|
||||
concatLists
|
||||
genList
|
||||
length
|
||||
head
|
||||
tail
|
||||
elem
|
||||
elemAt
|
||||
isList
|
||||
;
|
||||
inherit (self.strings)
|
||||
concatStrings
|
||||
concatMapStrings
|
||||
concatImapStrings
|
||||
stringLength
|
||||
substring
|
||||
isString
|
||||
replaceStrings
|
||||
intersperse
|
||||
concatStringsSep
|
||||
concatMapStringsSep
|
||||
concatImapStringsSep
|
||||
concatLines
|
||||
makeSearchPath
|
||||
makeSearchPathOutput
|
||||
makeLibraryPath
|
||||
makeIncludePath
|
||||
makeBinPath
|
||||
optionalString
|
||||
hasInfix
|
||||
hasPrefix
|
||||
hasSuffix
|
||||
stringToCharacters
|
||||
stringAsChars
|
||||
escape
|
||||
escapeShellArg
|
||||
escapeShellArgs
|
||||
isStorePath
|
||||
isStringLike
|
||||
isValidPosixName
|
||||
toShellVar
|
||||
toShellVars
|
||||
trim
|
||||
trimWith
|
||||
escapeRegex
|
||||
escapeURL
|
||||
escapeXML
|
||||
replaceChars
|
||||
lowerChars
|
||||
upperChars
|
||||
toLower
|
||||
toUpper
|
||||
addContextFrom
|
||||
splitString
|
||||
removePrefix
|
||||
removeSuffix
|
||||
versionOlder
|
||||
versionAtLeast
|
||||
getName
|
||||
getVersion
|
||||
match
|
||||
split
|
||||
cmakeOptionType
|
||||
cmakeBool
|
||||
cmakeFeature
|
||||
mesonOption
|
||||
mesonBool
|
||||
mesonEnable
|
||||
nameFromURL
|
||||
enableFeature
|
||||
enableFeatureAs
|
||||
withFeature
|
||||
withFeatureAs
|
||||
fixedWidthString
|
||||
fixedWidthNumber
|
||||
toInt
|
||||
toIntBase10
|
||||
readPathsFromFile
|
||||
fileContents
|
||||
;
|
||||
inherit (self.stringsWithDeps)
|
||||
textClosureList
|
||||
textClosureMap
|
||||
noDepEntry
|
||||
fullDepEntry
|
||||
packEntry
|
||||
stringAfter
|
||||
;
|
||||
inherit (self.customisation)
|
||||
overrideDerivation
|
||||
makeOverridable
|
||||
callPackageWith
|
||||
callPackagesWith
|
||||
extendDerivation
|
||||
hydraJob
|
||||
makeScope
|
||||
makeScopeWithSplicing
|
||||
makeScopeWithSplicing'
|
||||
extendMkDerivation
|
||||
;
|
||||
inherit (self.derivations) lazyDerivation optionalDrvAttr warnOnInstantiate;
|
||||
inherit (self.meta)
|
||||
addMetaAttrs
|
||||
dontDistribute
|
||||
setName
|
||||
updateName
|
||||
appendToName
|
||||
mapDerivationAttrset
|
||||
setPrio
|
||||
lowPrio
|
||||
lowPrioSet
|
||||
hiPrio
|
||||
hiPrioSet
|
||||
licensesSpdx
|
||||
getLicenseFromSpdxId
|
||||
getLicenseFromSpdxIdOr
|
||||
getExe
|
||||
getExe'
|
||||
;
|
||||
inherit (self.filesystem)
|
||||
pathType
|
||||
pathIsDirectory
|
||||
pathIsRegularFile
|
||||
packagesFromDirectoryRecursive
|
||||
;
|
||||
inherit (self.sources)
|
||||
cleanSourceFilter
|
||||
cleanSource
|
||||
sourceByRegex
|
||||
sourceFilesBySuffices
|
||||
commitIdFromGitRepo
|
||||
cleanSourceWith
|
||||
pathHasContext
|
||||
canCleanSource
|
||||
pathIsGitRepo
|
||||
;
|
||||
inherit (self.modules)
|
||||
evalModules
|
||||
setDefaultModuleLocation
|
||||
unifyModuleSyntax
|
||||
applyModuleArgsIfFunction
|
||||
mergeModules
|
||||
mergeModules'
|
||||
mergeOptionDecls
|
||||
mergeDefinitions
|
||||
pushDownProperties
|
||||
dischargeProperties
|
||||
filterOverrides
|
||||
sortProperties
|
||||
fixupOptionType
|
||||
mkIf
|
||||
mkAssert
|
||||
mkMerge
|
||||
mkOverride
|
||||
mkOptionDefault
|
||||
mkDefault
|
||||
mkImageMediaOverride
|
||||
mkForce
|
||||
mkVMOverride
|
||||
mkFixStrictness
|
||||
mkOrder
|
||||
mkBefore
|
||||
mkAfter
|
||||
mkAliasDefinitions
|
||||
mkAliasAndWrapDefinitions
|
||||
fixMergeModules
|
||||
mkRemovedOptionModule
|
||||
mkRenamedOptionModule
|
||||
mkRenamedOptionModuleWith
|
||||
mkMergedOptionModule
|
||||
mkChangedOptionModule
|
||||
mkAliasOptionModule
|
||||
mkDerivedConfig
|
||||
doRename
|
||||
mkAliasOptionModuleMD
|
||||
;
|
||||
evalOptionValue = lib.warn "External use of `lib.evalOptionValue` is deprecated. If your use case isn't covered by non-deprecated functions, we'd like to know more and perhaps support your use case well, instead of providing access to these low level functions. In this case please open an issue in https://github.com/nixos/nixpkgs/issues/." self.modules.evalOptionValue;
|
||||
inherit (self.options)
|
||||
isOption
|
||||
mkEnableOption
|
||||
mkSinkUndeclaredOptions
|
||||
mergeDefaultOption
|
||||
mergeOneOption
|
||||
mergeEqualOption
|
||||
mergeUniqueOption
|
||||
getValues
|
||||
getFiles
|
||||
optionAttrSetToDocList
|
||||
optionAttrSetToDocList'
|
||||
scrubOptionValue
|
||||
literalExpression
|
||||
literalExample
|
||||
showOption
|
||||
showOptionWithDefLocs
|
||||
showFiles
|
||||
unknownModule
|
||||
mkOption
|
||||
mkPackageOption
|
||||
mkPackageOptionMD
|
||||
literalMD
|
||||
;
|
||||
inherit (self.types)
|
||||
isType
|
||||
setType
|
||||
defaultTypeMerge
|
||||
defaultFunctor
|
||||
isOptionType
|
||||
mkOptionType
|
||||
;
|
||||
inherit (self.asserts)
|
||||
assertMsg
|
||||
assertOneOf
|
||||
;
|
||||
inherit (self.debug)
|
||||
traceIf
|
||||
traceVal
|
||||
traceValFn
|
||||
traceSeq
|
||||
traceSeqN
|
||||
traceValSeq
|
||||
traceValSeqFn
|
||||
traceValSeqN
|
||||
traceValSeqNFn
|
||||
traceFnSeqN
|
||||
runTests
|
||||
testAllTrue
|
||||
;
|
||||
inherit (self.misc)
|
||||
maybeEnv
|
||||
defaultMergeArg
|
||||
defaultMerge
|
||||
foldArgs
|
||||
maybeAttrNullable
|
||||
maybeAttr
|
||||
ifEnable
|
||||
checkFlag
|
||||
getValue
|
||||
checkReqs
|
||||
uniqList
|
||||
uniqListExt
|
||||
condConcat
|
||||
lazyGenericClosure
|
||||
innerModifySumArgs
|
||||
modifySumArgs
|
||||
innerClosePropagation
|
||||
closePropagation
|
||||
mapAttrsFlatten
|
||||
nvs
|
||||
setAttr
|
||||
setAttrMerge
|
||||
mergeAttrsWithFunc
|
||||
mergeAttrsConcatenateValues
|
||||
mergeAttrsNoOverride
|
||||
mergeAttrByFunc
|
||||
mergeAttrsByFuncDefaults
|
||||
mergeAttrsByFuncDefaultsClean
|
||||
mergeAttrBy
|
||||
fakeHash
|
||||
fakeSha256
|
||||
fakeSha512
|
||||
nixType
|
||||
imap
|
||||
;
|
||||
inherit (self.versions)
|
||||
splitVersion
|
||||
;
|
||||
}
|
||||
);
|
||||
in
|
||||
lib
|
||||
|
|
|
@ -35,153 +35,212 @@ let
|
|||
inherit (lib.attrsets) removeAttrs mapAttrsToList;
|
||||
|
||||
# returns default if env var is not set
|
||||
maybeEnv = name: default:
|
||||
let value = builtins.getEnv name; in
|
||||
maybeEnv =
|
||||
name: default:
|
||||
let
|
||||
value = builtins.getEnv name;
|
||||
in
|
||||
if value == "" then default else value;
|
||||
|
||||
defaultMergeArg = x : y: if builtins.isAttrs y then
|
||||
y
|
||||
else
|
||||
(y x);
|
||||
defaultMergeArg = x: y: if builtins.isAttrs y then y else (y x);
|
||||
defaultMerge = x: y: x // (defaultMergeArg x y);
|
||||
foldArgs = merger: f: init: x:
|
||||
let arg = (merger init (defaultMergeArg init x));
|
||||
# now add the function with composed args already applied to the final attrs
|
||||
base = (setAttrMerge "passthru" {} (f arg)
|
||||
( z: z // {
|
||||
function = foldArgs merger f arg;
|
||||
args = (attrByPath ["passthru" "args"] {} z) // x;
|
||||
} ));
|
||||
withStdOverrides = base // {
|
||||
override = base.passthru.function;
|
||||
};
|
||||
in
|
||||
withStdOverrides;
|
||||
|
||||
foldArgs =
|
||||
merger: f: init: x:
|
||||
let
|
||||
arg = (merger init (defaultMergeArg init x));
|
||||
# now add the function with composed args already applied to the final attrs
|
||||
base = (
|
||||
setAttrMerge "passthru" { } (f arg) (
|
||||
z:
|
||||
z
|
||||
// {
|
||||
function = foldArgs merger f arg;
|
||||
args = (attrByPath [ "passthru" "args" ] { } z) // x;
|
||||
}
|
||||
)
|
||||
);
|
||||
withStdOverrides = base // {
|
||||
override = base.passthru.function;
|
||||
};
|
||||
in
|
||||
withStdOverrides;
|
||||
|
||||
# shortcut for attrByPath ["name"] default attrs
|
||||
maybeAttrNullable = maybeAttr;
|
||||
|
||||
# shortcut for attrByPath ["name"] default attrs
|
||||
maybeAttr = name: default: attrs: attrs.${name} or default;
|
||||
|
||||
maybeAttr =
|
||||
name: default: attrs:
|
||||
attrs.${name} or default;
|
||||
|
||||
# Return the second argument if the first one is true or the empty version
|
||||
# of the second argument.
|
||||
ifEnable = cond: val:
|
||||
if cond then val
|
||||
else if builtins.isList val then []
|
||||
else if builtins.isAttrs val then {}
|
||||
ifEnable =
|
||||
cond: val:
|
||||
if cond then
|
||||
val
|
||||
else if builtins.isList val then
|
||||
[ ]
|
||||
else if builtins.isAttrs val then
|
||||
{ }
|
||||
# else if builtins.isString val then ""
|
||||
else if val == true || val == false then false
|
||||
else null;
|
||||
|
||||
else if val == true || val == false then
|
||||
false
|
||||
else
|
||||
null;
|
||||
|
||||
# Return true only if there is an attribute and it is true.
|
||||
checkFlag = attrSet: name:
|
||||
if name == "true" then true else
|
||||
if name == "false" then false else
|
||||
if (elem name (attrByPath ["flags"] [] attrSet)) then true else
|
||||
attrByPath [name] false attrSet ;
|
||||
|
||||
checkFlag =
|
||||
attrSet: name:
|
||||
if name == "true" then
|
||||
true
|
||||
else if name == "false" then
|
||||
false
|
||||
else if (elem name (attrByPath [ "flags" ] [ ] attrSet)) then
|
||||
true
|
||||
else
|
||||
attrByPath [ name ] false attrSet;
|
||||
|
||||
# Input : attrSet, [ [name default] ... ], name
|
||||
# Output : its value or default.
|
||||
getValue = attrSet: argList: name:
|
||||
( attrByPath [name] (if checkFlag attrSet name then true else
|
||||
if argList == [] then null else
|
||||
let x = builtins.head argList; in
|
||||
if (head x) == name then
|
||||
(head (tail x))
|
||||
else (getValue attrSet
|
||||
(tail argList) name)) attrSet );
|
||||
|
||||
getValue =
|
||||
attrSet: argList: name:
|
||||
(attrByPath [ name ] (
|
||||
if checkFlag attrSet name then
|
||||
true
|
||||
else if argList == [ ] then
|
||||
null
|
||||
else
|
||||
let
|
||||
x = builtins.head argList;
|
||||
in
|
||||
if (head x) == name then (head (tail x)) else (getValue attrSet (tail argList) name)
|
||||
) attrSet);
|
||||
|
||||
# Input : attrSet, [[name default] ...], [ [flagname reqs..] ... ]
|
||||
# Output : are reqs satisfied? It's asserted.
|
||||
checkReqs = attrSet: argList: condList:
|
||||
(
|
||||
foldr and true
|
||||
(map (x: let name = (head x); in
|
||||
|
||||
((checkFlag attrSet name) ->
|
||||
(foldr and true
|
||||
(map (y: let val=(getValue attrSet argList y); in
|
||||
(val!=null) && (val!=false))
|
||||
(tail x))))) condList));
|
||||
checkReqs =
|
||||
attrSet: argList: condList:
|
||||
(foldr and true (
|
||||
map (
|
||||
x:
|
||||
let
|
||||
name = (head x);
|
||||
in
|
||||
|
||||
(
|
||||
(checkFlag attrSet name)
|
||||
-> (foldr and true (
|
||||
map (
|
||||
y:
|
||||
let
|
||||
val = (getValue attrSet argList y);
|
||||
in
|
||||
(val != null) && (val != false)
|
||||
) (tail x)
|
||||
))
|
||||
)
|
||||
) condList
|
||||
));
|
||||
|
||||
# This function has O(n^2) performance.
|
||||
uniqList = { inputList, acc ? [] }:
|
||||
let go = xs: acc:
|
||||
if xs == []
|
||||
then []
|
||||
else let x = head xs;
|
||||
y = if elem x acc then [] else [x];
|
||||
in y ++ go (tail xs) (y ++ acc);
|
||||
in go inputList acc;
|
||||
|
||||
uniqListExt = { inputList,
|
||||
outputList ? [],
|
||||
getter ? (x: x),
|
||||
compare ? (x: y: x==y) }:
|
||||
if inputList == [] then outputList else
|
||||
let x = head inputList;
|
||||
isX = y: (compare (getter y) (getter x));
|
||||
newOutputList = outputList ++
|
||||
(if any isX outputList then [] else [x]);
|
||||
in uniqListExt { outputList = newOutputList;
|
||||
inputList = (tail inputList);
|
||||
inherit getter compare;
|
||||
};
|
||||
|
||||
condConcat = name: list: checker:
|
||||
if list == [] then name else
|
||||
if checker (head list) then
|
||||
condConcat
|
||||
(name + (head (tail list)))
|
||||
(tail (tail list))
|
||||
checker
|
||||
else condConcat
|
||||
name (tail (tail list)) checker;
|
||||
|
||||
lazyGenericClosure = {startSet, operator}:
|
||||
uniqList =
|
||||
{
|
||||
inputList,
|
||||
acc ? [ ],
|
||||
}:
|
||||
let
|
||||
work = list: doneKeys: result:
|
||||
if list == [] then
|
||||
go =
|
||||
xs: acc:
|
||||
if xs == [ ] then
|
||||
[ ]
|
||||
else
|
||||
let
|
||||
x = head xs;
|
||||
y = if elem x acc then [ ] else [ x ];
|
||||
in
|
||||
y ++ go (tail xs) (y ++ acc);
|
||||
in
|
||||
go inputList acc;
|
||||
|
||||
uniqListExt =
|
||||
{
|
||||
inputList,
|
||||
outputList ? [ ],
|
||||
getter ? (x: x),
|
||||
compare ? (x: y: x == y),
|
||||
}:
|
||||
if inputList == [ ] then
|
||||
outputList
|
||||
else
|
||||
let
|
||||
x = head inputList;
|
||||
isX = y: (compare (getter y) (getter x));
|
||||
newOutputList = outputList ++ (if any isX outputList then [ ] else [ x ]);
|
||||
in
|
||||
uniqListExt {
|
||||
outputList = newOutputList;
|
||||
inputList = (tail inputList);
|
||||
inherit getter compare;
|
||||
};
|
||||
|
||||
condConcat =
|
||||
name: list: checker:
|
||||
if list == [ ] then
|
||||
name
|
||||
else if checker (head list) then
|
||||
condConcat (name + (head (tail list))) (tail (tail list)) checker
|
||||
else
|
||||
condConcat name (tail (tail list)) checker;
|
||||
|
||||
lazyGenericClosure =
|
||||
{ startSet, operator }:
|
||||
let
|
||||
work =
|
||||
list: doneKeys: result:
|
||||
if list == [ ] then
|
||||
result
|
||||
else
|
||||
let x = head list; key = x.key; in
|
||||
let
|
||||
x = head list;
|
||||
key = x.key;
|
||||
in
|
||||
if elem key doneKeys then
|
||||
work (tail list) doneKeys result
|
||||
else
|
||||
work (tail list ++ operator x) ([key] ++ doneKeys) ([x] ++ result);
|
||||
work (tail list ++ operator x) ([ key ] ++ doneKeys) ([ x ] ++ result);
|
||||
in
|
||||
work startSet [] [];
|
||||
work startSet [ ] [ ];
|
||||
|
||||
innerModifySumArgs = f: x: a: b: if b == null then (f a b) // x else
|
||||
innerModifySumArgs f x (a // b);
|
||||
modifySumArgs = f: x: innerModifySumArgs f x {};
|
||||
innerModifySumArgs =
|
||||
f: x: a: b:
|
||||
if b == null then (f a b) // x else innerModifySumArgs f x (a // b);
|
||||
modifySumArgs = f: x: innerModifySumArgs f x { };
|
||||
|
||||
innerClosePropagation =
|
||||
acc: xs:
|
||||
if xs == [ ] then
|
||||
acc
|
||||
else
|
||||
let
|
||||
y = head xs;
|
||||
ys = tail xs;
|
||||
in
|
||||
if !isAttrs y then
|
||||
innerClosePropagation acc ys
|
||||
else
|
||||
let
|
||||
acc' = [ y ] ++ acc;
|
||||
in
|
||||
innerClosePropagation acc' (uniqList {
|
||||
inputList =
|
||||
(maybeAttrNullable "propagatedBuildInputs" [ ] y)
|
||||
++ (maybeAttrNullable "propagatedNativeBuildInputs" [ ] y)
|
||||
++ ys;
|
||||
acc = acc';
|
||||
});
|
||||
|
||||
innerClosePropagation = acc: xs:
|
||||
if xs == []
|
||||
then acc
|
||||
else let y = head xs;
|
||||
ys = tail xs;
|
||||
in if ! isAttrs y
|
||||
then innerClosePropagation acc ys
|
||||
else let acc' = [y] ++ acc;
|
||||
in innerClosePropagation
|
||||
acc'
|
||||
(uniqList { inputList = (maybeAttrNullable "propagatedBuildInputs" [] y)
|
||||
++ (maybeAttrNullable "propagatedNativeBuildInputs" [] y)
|
||||
++ ys;
|
||||
acc = acc';
|
||||
}
|
||||
);
|
||||
|
||||
closePropagationSlow = list: (uniqList {inputList = (innerClosePropagation [] list);});
|
||||
closePropagationSlow = list: (uniqList { inputList = (innerClosePropagation [ ] list); });
|
||||
|
||||
# This is an optimisation of closePropagation which avoids the O(n^2) behavior
|
||||
# Using a list of derivations, it generates the full closure of the propagatedXXXBuildInputs
|
||||
|
@ -189,28 +248,35 @@ let
|
|||
# attribute of each derivation.
|
||||
# On some benchmarks, it performs up to 15 times faster than closePropagation.
|
||||
# See https://github.com/NixOS/nixpkgs/pull/194391 for details.
|
||||
closePropagationFast = list:
|
||||
builtins.map (x: x.val) (builtins.genericClosure {
|
||||
startSet = builtins.map (x: {
|
||||
key = x.outPath;
|
||||
val = x;
|
||||
}) (builtins.filter (x: x != null) list);
|
||||
operator = item:
|
||||
if !builtins.isAttrs item.val then
|
||||
[ ]
|
||||
else
|
||||
builtins.concatMap (x:
|
||||
if x != null then [{
|
||||
key = x.outPath;
|
||||
val = x;
|
||||
}] else
|
||||
[ ]) ((item.val.propagatedBuildInputs or [ ])
|
||||
++ (item.val.propagatedNativeBuildInputs or [ ]));
|
||||
});
|
||||
closePropagationFast =
|
||||
list:
|
||||
builtins.map (x: x.val) (
|
||||
builtins.genericClosure {
|
||||
startSet = builtins.map (x: {
|
||||
key = x.outPath;
|
||||
val = x;
|
||||
}) (builtins.filter (x: x != null) list);
|
||||
operator =
|
||||
item:
|
||||
if !builtins.isAttrs item.val then
|
||||
[ ]
|
||||
else
|
||||
builtins.concatMap (
|
||||
x:
|
||||
if x != null then
|
||||
[
|
||||
{
|
||||
key = x.outPath;
|
||||
val = x;
|
||||
}
|
||||
]
|
||||
else
|
||||
[ ]
|
||||
) ((item.val.propagatedBuildInputs or [ ]) ++ (item.val.propagatedNativeBuildInputs or [ ]));
|
||||
}
|
||||
);
|
||||
|
||||
closePropagation = if builtins ? genericClosure
|
||||
then closePropagationFast
|
||||
else closePropagationSlow;
|
||||
closePropagation = if builtins ? genericClosure then closePropagationFast else closePropagationSlow;
|
||||
|
||||
# calls a function (f attr value ) for each record item. returns a list
|
||||
mapAttrsFlatten = warn "lib.misc.mapAttrsFlatten is deprecated, please use lib.attrsets.mapAttrsToList instead." mapAttrsToList;
|
||||
|
@ -218,26 +284,29 @@ let
|
|||
# attribute set containing one attribute
|
||||
nvs = name: value: listToAttrs [ (nameValuePair name value) ];
|
||||
# adds / replaces an attribute of an attribute set
|
||||
setAttr = set: name: v: set // (nvs name v);
|
||||
setAttr =
|
||||
set: name: v:
|
||||
set // (nvs name v);
|
||||
|
||||
# setAttrMerge (similar to mergeAttrsWithFunc but only merges the values of a particular name)
|
||||
# setAttrMerge "a" [] { a = [2];} (x: x ++ [3]) -> { a = [2 3]; }
|
||||
# setAttrMerge "a" [] { } (x: x ++ [3]) -> { a = [ 3]; }
|
||||
setAttrMerge = name: default: attrs: f:
|
||||
setAttrMerge =
|
||||
name: default: attrs: f:
|
||||
setAttr attrs name (f (maybeAttr name default attrs));
|
||||
|
||||
# Using f = a: b = b the result is similar to //
|
||||
# merge attributes with custom function handling the case that the attribute
|
||||
# exists in both sets
|
||||
mergeAttrsWithFunc = f: set1: set2:
|
||||
foldr (n: set: if set ? ${n}
|
||||
then setAttr set n (f set.${n} set2.${n})
|
||||
else set )
|
||||
(set2 // set1) (attrNames set2);
|
||||
mergeAttrsWithFunc =
|
||||
f: set1: set2:
|
||||
foldr (n: set: if set ? ${n} then setAttr set n (f set.${n} set2.${n}) else set) (set2 // set1) (
|
||||
attrNames set2
|
||||
);
|
||||
|
||||
# merging two attribute set concatenating the values of same attribute names
|
||||
# eg { a = 7; } { a = [ 2 3 ]; } becomes { a = [ 7 2 3 ]; }
|
||||
mergeAttrsConcatenateValues = mergeAttrsWithFunc ( a: b: (toList a) ++ (toList b) );
|
||||
mergeAttrsConcatenateValues = mergeAttrsWithFunc (a: b: (toList a) ++ (toList b));
|
||||
|
||||
# merges attributes using //, if a name exists in both attributes
|
||||
# an error will be triggered unless its listed in mergeLists
|
||||
|
@ -246,20 +315,31 @@ let
|
|||
# merging buildPhase doesn't really make sense. The cases will be rare where appending /prefixing will fit your needs?
|
||||
# in these cases the first buildPhase will override the second one
|
||||
# ! deprecated, use mergeAttrByFunc instead
|
||||
mergeAttrsNoOverride = { mergeLists ? ["buildInputs" "propagatedBuildInputs"],
|
||||
overrideSnd ? [ "buildPhase" ]
|
||||
}: attrs1: attrs2:
|
||||
foldr (n: set:
|
||||
setAttr set n ( if set ? ${n}
|
||||
then # merge
|
||||
if elem n mergeLists # attribute contains list, merge them by concatenating
|
||||
then attrs2.${n} ++ attrs1.${n}
|
||||
else if elem n overrideSnd
|
||||
then attrs1.${n}
|
||||
else throw "error mergeAttrsNoOverride, attribute ${n} given in both attributes - no merge func defined"
|
||||
else attrs2.${n} # add attribute not existing in attr1
|
||||
)) attrs1 (attrNames attrs2);
|
||||
|
||||
mergeAttrsNoOverride =
|
||||
{
|
||||
mergeLists ? [
|
||||
"buildInputs"
|
||||
"propagatedBuildInputs"
|
||||
],
|
||||
overrideSnd ? [ "buildPhase" ],
|
||||
}:
|
||||
attrs1: attrs2:
|
||||
foldr (
|
||||
n: set:
|
||||
setAttr set n (
|
||||
if set ? ${n} then # merge
|
||||
if
|
||||
elem n mergeLists # attribute contains list, merge them by concatenating
|
||||
then
|
||||
attrs2.${n} ++ attrs1.${n}
|
||||
else if elem n overrideSnd then
|
||||
attrs1.${n}
|
||||
else
|
||||
throw "error mergeAttrsNoOverride, attribute ${n} given in both attributes - no merge func defined"
|
||||
else
|
||||
attrs2.${n} # add attribute not existing in attr1
|
||||
)
|
||||
) attrs1 (attrNames attrs2);
|
||||
|
||||
# example usage:
|
||||
# mergeAttrByFunc {
|
||||
|
@ -272,48 +352,82 @@ let
|
|||
# { mergeAttrsBy = [...]; buildInputs = [ a b c d ]; }
|
||||
# is used by defaultOverridableDelayableArgs and can be used when composing using
|
||||
# foldArgs, composedArgsAndFun or applyAndFun. Example: composableDerivation in all-packages.nix
|
||||
mergeAttrByFunc = x: y:
|
||||
mergeAttrByFunc =
|
||||
x: y:
|
||||
let
|
||||
mergeAttrBy2 = { mergeAttrBy = mergeAttrs; }
|
||||
// (maybeAttr "mergeAttrBy" {} x)
|
||||
// (maybeAttr "mergeAttrBy" {} y); in
|
||||
foldr mergeAttrs {} [
|
||||
x y
|
||||
(mapAttrs ( a: v: # merge special names using given functions
|
||||
if x ? ${a}
|
||||
then if y ? ${a}
|
||||
then v x.${a} y.${a} # both have attr, use merge func
|
||||
else x.${a} # only x has attr
|
||||
else y.${a} # only y has attr)
|
||||
) (removeAttrs mergeAttrBy2
|
||||
# don't merge attrs which are neither in x nor y
|
||||
(filter (a: ! x ? ${a} && ! y ? ${a})
|
||||
(attrNames mergeAttrBy2))
|
||||
)
|
||||
mergeAttrBy2 =
|
||||
{ mergeAttrBy = mergeAttrs; } // (maybeAttr "mergeAttrBy" { } x) // (maybeAttr "mergeAttrBy" { } y);
|
||||
in
|
||||
foldr mergeAttrs { } [
|
||||
x
|
||||
y
|
||||
(mapAttrs
|
||||
(
|
||||
a: v: # merge special names using given functions
|
||||
if x ? ${a} then
|
||||
if y ? ${a} then
|
||||
v x.${a} y.${a} # both have attr, use merge func
|
||||
else
|
||||
x.${a} # only x has attr
|
||||
else
|
||||
y.${a} # only y has attr)
|
||||
)
|
||||
(
|
||||
removeAttrs mergeAttrBy2
|
||||
# don't merge attrs which are neither in x nor y
|
||||
(filter (a: !x ? ${a} && !y ? ${a}) (attrNames mergeAttrBy2))
|
||||
)
|
||||
)
|
||||
];
|
||||
mergeAttrsByFuncDefaults = foldl mergeAttrByFunc { inherit mergeAttrBy; };
|
||||
mergeAttrsByFuncDefaultsClean = list: removeAttrs (mergeAttrsByFuncDefaults list) ["mergeAttrBy"];
|
||||
mergeAttrsByFuncDefaultsClean = list: removeAttrs (mergeAttrsByFuncDefaults list) [ "mergeAttrBy" ];
|
||||
|
||||
# sane defaults (same name as attr name so that inherit can be used)
|
||||
mergeAttrBy = # { buildInputs = concatList; [...]; passthru = mergeAttr; [..]; }
|
||||
listToAttrs (map (n: nameValuePair n concat)
|
||||
[ "nativeBuildInputs" "buildInputs" "propagatedBuildInputs" "configureFlags" "prePhases" "postAll" "patches" ])
|
||||
// listToAttrs (map (n: nameValuePair n mergeAttrs) [ "passthru" "meta" "cfg" "flags" ])
|
||||
// listToAttrs (map (n: nameValuePair n (a: b: "${a}\n${b}") ) [ "preConfigure" "postInstall" ])
|
||||
;
|
||||
listToAttrs (
|
||||
map (n: nameValuePair n concat) [
|
||||
"nativeBuildInputs"
|
||||
"buildInputs"
|
||||
"propagatedBuildInputs"
|
||||
"configureFlags"
|
||||
"prePhases"
|
||||
"postAll"
|
||||
"patches"
|
||||
]
|
||||
)
|
||||
// listToAttrs (
|
||||
map (n: nameValuePair n mergeAttrs) [
|
||||
"passthru"
|
||||
"meta"
|
||||
"cfg"
|
||||
"flags"
|
||||
]
|
||||
)
|
||||
// listToAttrs (
|
||||
map (n: nameValuePair n (a: b: "${a}\n${b}")) [
|
||||
"preConfigure"
|
||||
"postInstall"
|
||||
]
|
||||
);
|
||||
|
||||
nixType = x:
|
||||
if isAttrs x then
|
||||
if x ? outPath then "derivation"
|
||||
else "attrs"
|
||||
else if isFunction x then "function"
|
||||
else if isList x then "list"
|
||||
else if x == true then "bool"
|
||||
else if x == false then "bool"
|
||||
else if x == null then "null"
|
||||
else if isInt x then "int"
|
||||
else "string";
|
||||
nixType =
|
||||
x:
|
||||
if isAttrs x then
|
||||
if x ? outPath then "derivation" else "attrs"
|
||||
else if isFunction x then
|
||||
"function"
|
||||
else if isList x then
|
||||
"list"
|
||||
else if x == true then
|
||||
"bool"
|
||||
else if x == false then
|
||||
"bool"
|
||||
else if x == null then
|
||||
"null"
|
||||
else if isInt x then
|
||||
"int"
|
||||
else
|
||||
"string";
|
||||
|
||||
/**
|
||||
# Deprecated
|
||||
|
|
|
@ -242,8 +242,11 @@ in
|
|||
warnOnInstantiate =
|
||||
msg: drv:
|
||||
let
|
||||
drvToWrap = removeAttrs drv [ "meta" "name" "type" ];
|
||||
drvToWrap = removeAttrs drv [
|
||||
"meta"
|
||||
"name"
|
||||
"type"
|
||||
];
|
||||
in
|
||||
drv
|
||||
// mapAttrs (_: lib.warn msg) drvToWrap;
|
||||
drv // mapAttrs (_: lib.warn msg) drvToWrap;
|
||||
}
|
||||
|
|
|
@ -62,23 +62,26 @@ in
|
|||
pathType =
|
||||
builtins.readFileType or
|
||||
# Nix <2.14 compatibility shim
|
||||
(path:
|
||||
if ! pathExists path
|
||||
(
|
||||
path:
|
||||
if
|
||||
!pathExists path
|
||||
# Fail irrecoverably to mimic the historic behavior of this function and
|
||||
# the new builtins.readFileType
|
||||
then abort "lib.filesystem.pathType: Path ${toString path} does not exist."
|
||||
then
|
||||
abort "lib.filesystem.pathType: Path ${toString path} does not exist."
|
||||
# The filesystem root is the only path where `dirOf / == /` and
|
||||
# `baseNameOf /` is not valid. We can detect this and directly return
|
||||
# "directory", since we know the filesystem root can't be anything else.
|
||||
else if dirOf path == path
|
||||
then "directory"
|
||||
else (readDir (dirOf path)).${baseNameOf path}
|
||||
else if dirOf path == path then
|
||||
"directory"
|
||||
else
|
||||
(readDir (dirOf path)).${baseNameOf path}
|
||||
);
|
||||
|
||||
/**
|
||||
Whether a path exists and is a directory.
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`path`
|
||||
|
@ -108,13 +111,11 @@ in
|
|||
|
||||
:::
|
||||
*/
|
||||
pathIsDirectory = path:
|
||||
pathExists path && pathType path == "directory";
|
||||
pathIsDirectory = path: pathExists path && pathType path == "directory";
|
||||
|
||||
/**
|
||||
Whether a path exists and is a regular file, meaning not a symlink or any other special file type.
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`path`
|
||||
|
@ -144,15 +145,13 @@ in
|
|||
|
||||
:::
|
||||
*/
|
||||
pathIsRegularFile = path:
|
||||
pathExists path && pathType path == "regular";
|
||||
pathIsRegularFile = path: pathExists path && pathType path == "regular";
|
||||
|
||||
/**
|
||||
A map of all haskell packages defined in the given path,
|
||||
identified by having a cabal file with the same name as the
|
||||
directory itself.
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`root`
|
||||
|
@ -167,25 +166,25 @@ in
|
|||
*/
|
||||
haskellPathsInDir =
|
||||
root:
|
||||
let # Files in the root
|
||||
root-files = builtins.attrNames (builtins.readDir root);
|
||||
# Files with their full paths
|
||||
root-files-with-paths =
|
||||
map (file:
|
||||
{ name = file; value = root + "/${file}"; }
|
||||
) root-files;
|
||||
# Subdirectories of the root with a cabal file.
|
||||
cabal-subdirs =
|
||||
builtins.filter ({ name, value }:
|
||||
builtins.pathExists (value + "/${name}.cabal")
|
||||
) root-files-with-paths;
|
||||
in builtins.listToAttrs cabal-subdirs;
|
||||
let
|
||||
# Files in the root
|
||||
root-files = builtins.attrNames (builtins.readDir root);
|
||||
# Files with their full paths
|
||||
root-files-with-paths = map (file: {
|
||||
name = file;
|
||||
value = root + "/${file}";
|
||||
}) root-files;
|
||||
# Subdirectories of the root with a cabal file.
|
||||
cabal-subdirs = builtins.filter (
|
||||
{ name, value }: builtins.pathExists (value + "/${name}.cabal")
|
||||
) root-files-with-paths;
|
||||
in
|
||||
builtins.listToAttrs cabal-subdirs;
|
||||
/**
|
||||
Find the first directory containing a file matching 'pattern'
|
||||
upward from a given 'file'.
|
||||
Returns 'null' if no directories contain a file matching 'pattern'.
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`pattern`
|
||||
|
@ -203,30 +202,33 @@ in
|
|||
```
|
||||
*/
|
||||
locateDominatingFile =
|
||||
pattern:
|
||||
file:
|
||||
let go = path:
|
||||
let files = builtins.attrNames (builtins.readDir path);
|
||||
matches = builtins.filter (match: match != null)
|
||||
(map (builtins.match pattern) files);
|
||||
in
|
||||
if builtins.length matches != 0
|
||||
then { inherit path matches; }
|
||||
else if path == /.
|
||||
then null
|
||||
else go (dirOf path);
|
||||
parent = dirOf file;
|
||||
isDir =
|
||||
let base = baseNameOf file;
|
||||
type = (builtins.readDir parent).${base} or null;
|
||||
in file == /. || type == "directory";
|
||||
in go (if isDir then file else parent);
|
||||
|
||||
pattern: file:
|
||||
let
|
||||
go =
|
||||
path:
|
||||
let
|
||||
files = builtins.attrNames (builtins.readDir path);
|
||||
matches = builtins.filter (match: match != null) (map (builtins.match pattern) files);
|
||||
in
|
||||
if builtins.length matches != 0 then
|
||||
{ inherit path matches; }
|
||||
else if path == /. then
|
||||
null
|
||||
else
|
||||
go (dirOf path);
|
||||
parent = dirOf file;
|
||||
isDir =
|
||||
let
|
||||
base = baseNameOf file;
|
||||
type = (builtins.readDir parent).${base} or null;
|
||||
in
|
||||
file == /. || type == "directory";
|
||||
in
|
||||
go (if isDir then file else parent);
|
||||
|
||||
/**
|
||||
Given a directory, return a flattened list of all files within it recursively.
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`dir`
|
||||
|
@ -241,12 +243,15 @@ in
|
|||
*/
|
||||
listFilesRecursive =
|
||||
dir:
|
||||
lib.flatten (lib.mapAttrsToList (name: type:
|
||||
if type == "directory" then
|
||||
lib.filesystem.listFilesRecursive (dir + "/${name}")
|
||||
else
|
||||
dir + "/${name}"
|
||||
) (builtins.readDir dir));
|
||||
lib.flatten (
|
||||
lib.mapAttrsToList (
|
||||
name: type:
|
||||
if type == "directory" then
|
||||
lib.filesystem.listFilesRecursive (dir + "/${name}")
|
||||
else
|
||||
dir + "/${name}"
|
||||
) (builtins.readDir dir)
|
||||
);
|
||||
|
||||
/**
|
||||
Transform a directory tree containing package files suitable for
|
||||
|
@ -325,7 +330,6 @@ in
|
|||
|
||||
Type: `Path`
|
||||
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
|
@ -363,49 +367,44 @@ in
|
|||
let
|
||||
# Determine if a directory entry from `readDir` indicates a package or
|
||||
# directory of packages.
|
||||
directoryEntryIsPackage = basename: type:
|
||||
type == "directory" || hasSuffix ".nix" basename;
|
||||
directoryEntryIsPackage = basename: type: type == "directory" || hasSuffix ".nix" basename;
|
||||
|
||||
# List directory entries that indicate packages in the given `path`.
|
||||
packageDirectoryEntries = path:
|
||||
filterAttrs directoryEntryIsPackage (readDir path);
|
||||
packageDirectoryEntries = path: filterAttrs directoryEntryIsPackage (readDir path);
|
||||
|
||||
# Transform a directory entry (a `basename` and `type` pair) into a
|
||||
# package.
|
||||
directoryEntryToAttrPair = subdirectory: basename: type:
|
||||
directoryEntryToAttrPair =
|
||||
subdirectory: basename: type:
|
||||
let
|
||||
path = subdirectory + "/${basename}";
|
||||
in
|
||||
if type == "regular"
|
||||
then
|
||||
{
|
||||
name = removeSuffix ".nix" basename;
|
||||
value = callPackage path { };
|
||||
}
|
||||
if type == "regular" then
|
||||
{
|
||||
name = removeSuffix ".nix" basename;
|
||||
value = callPackage path { };
|
||||
}
|
||||
else if type == "directory" then
|
||||
{
|
||||
name = basename;
|
||||
value = packagesFromDirectory path;
|
||||
}
|
||||
else
|
||||
if type == "directory"
|
||||
then
|
||||
{
|
||||
name = basename;
|
||||
value = packagesFromDirectory path;
|
||||
}
|
||||
else
|
||||
throw
|
||||
''
|
||||
throw ''
|
||||
lib.filesystem.packagesFromDirectoryRecursive: Unsupported file type ${type} at path ${toString subdirectory}
|
||||
'';
|
||||
|
||||
# Transform a directory into a package (if there's a `package.nix`) or
|
||||
# set of packages (otherwise).
|
||||
packagesFromDirectory = path:
|
||||
packagesFromDirectory =
|
||||
path:
|
||||
let
|
||||
defaultPackagePath = path + "/package.nix";
|
||||
in
|
||||
if pathExists defaultPackagePath
|
||||
then callPackage defaultPackagePath { }
|
||||
else mapAttrs'
|
||||
(directoryEntryToAttrPair path)
|
||||
(packageDirectoryEntries path);
|
||||
if pathExists defaultPackagePath then
|
||||
callPackage defaultPackagePath { }
|
||||
else
|
||||
mapAttrs' (directoryEntryToAttrPair path) (packageDirectoryEntries path);
|
||||
in
|
||||
packagesFromDirectory directory;
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ let
|
|||
filter
|
||||
flatten
|
||||
foldl
|
||||
functionArgs # Note: not the builtin; considers `__functor` in attrsets.
|
||||
functionArgs # Note: not the builtin; considers `__functor` in attrsets.
|
||||
gvariant
|
||||
hasInfix
|
||||
head
|
||||
|
@ -45,7 +45,7 @@ let
|
|||
isBool
|
||||
isDerivation
|
||||
isFloat
|
||||
isFunction # Note: not the builtin; considers `__functor` in attrsets.
|
||||
isFunction # Note: not the builtin; considers `__functor` in attrsets.
|
||||
isInt
|
||||
isList
|
||||
isPath
|
||||
|
@ -73,7 +73,8 @@ let
|
|||
;
|
||||
|
||||
## -- HELPER FUNCTIONS & DEFAULTS --
|
||||
in rec {
|
||||
in
|
||||
rec {
|
||||
/**
|
||||
Convert a value to a sensible default string representation.
|
||||
The builtin `toString` function has some strange defaults,
|
||||
|
@ -87,32 +88,44 @@ in rec {
|
|||
`v`
|
||||
: 2\. Function argument
|
||||
*/
|
||||
mkValueStringDefault = {}: v:
|
||||
let err = t: v: abort
|
||||
("generators.mkValueStringDefault: " +
|
||||
"${t} not supported: ${toPretty {} v}");
|
||||
in if isInt v then toString v
|
||||
mkValueStringDefault =
|
||||
{ }:
|
||||
v:
|
||||
let
|
||||
err = t: v: abort ("generators.mkValueStringDefault: " + "${t} not supported: ${toPretty { } v}");
|
||||
in
|
||||
if isInt v then
|
||||
toString v
|
||||
# convert derivations to store paths
|
||||
else if isDerivation v then toString v
|
||||
else if isDerivation v then
|
||||
toString v
|
||||
# we default to not quoting strings
|
||||
else if isString v then v
|
||||
else if isString v then
|
||||
v
|
||||
# isString returns "1", which is not a good default
|
||||
else if true == v then "true"
|
||||
else if true == v then
|
||||
"true"
|
||||
# here it returns to "", which is even less of a good default
|
||||
else if false == v then "false"
|
||||
else if null == v then "null"
|
||||
else if false == v then
|
||||
"false"
|
||||
else if null == v then
|
||||
"null"
|
||||
# if you have lists you probably want to replace this
|
||||
else if isList v then err "lists" v
|
||||
else if isList v then
|
||||
err "lists" v
|
||||
# same as for lists, might want to replace
|
||||
else if isAttrs v then err "attrsets" v
|
||||
else if isAttrs v then
|
||||
err "attrsets" v
|
||||
# functions can’t be printed of course
|
||||
else if isFunction v then err "functions" v
|
||||
else if isFunction v then
|
||||
err "functions" v
|
||||
# Floats currently can't be converted to precise strings,
|
||||
# condition warning on nix version once this isn't a problem anymore
|
||||
# See https://github.com/NixOS/nix/pull/3480
|
||||
else if isFloat v then floatToString v
|
||||
else err "this value is" (toString v);
|
||||
|
||||
else if isFloat v then
|
||||
floatToString v
|
||||
else
|
||||
err "this value is" (toString v);
|
||||
|
||||
/**
|
||||
Generate a line of key k and value v, separated by
|
||||
|
@ -144,15 +157,15 @@ in rec {
|
|||
|
||||
: 4\. Function argument
|
||||
*/
|
||||
mkKeyValueDefault = {
|
||||
mkValueString ? mkValueStringDefault {}
|
||||
}: sep: k: v:
|
||||
"${escape [sep] k}${sep}${mkValueString v}";
|
||||
|
||||
mkKeyValueDefault =
|
||||
{
|
||||
mkValueString ? mkValueStringDefault { },
|
||||
}:
|
||||
sep: k: v:
|
||||
"${escape [ sep ] k}${sep}${mkValueString v}";
|
||||
|
||||
## -- FILE FORMAT GENERATORS --
|
||||
|
||||
|
||||
/**
|
||||
Generate a key-value-style config file from an attrset.
|
||||
|
||||
|
@ -168,19 +181,22 @@ in rec {
|
|||
|
||||
: indent (optional, default: `""`)
|
||||
: Initial indentation level
|
||||
|
||||
*/
|
||||
toKeyValue = {
|
||||
mkKeyValue ? mkKeyValueDefault {} "=",
|
||||
listsAsDuplicateKeys ? false,
|
||||
indent ? ""
|
||||
}:
|
||||
let mkLine = k: v: indent + mkKeyValue k v + "\n";
|
||||
mkLines = if listsAsDuplicateKeys
|
||||
then k: v: map (mkLine k) (if isList v then v else [v])
|
||||
else k: v: [ (mkLine k v) ];
|
||||
in attrs: concatStrings (concatLists (mapAttrsToList mkLines attrs));
|
||||
|
||||
toKeyValue =
|
||||
{
|
||||
mkKeyValue ? mkKeyValueDefault { } "=",
|
||||
listsAsDuplicateKeys ? false,
|
||||
indent ? "",
|
||||
}:
|
||||
let
|
||||
mkLine = k: v: indent + mkKeyValue k v + "\n";
|
||||
mkLines =
|
||||
if listsAsDuplicateKeys then
|
||||
k: v: map (mkLine k) (if isList v then v else [ v ])
|
||||
else
|
||||
k: v: [ (mkLine k v) ];
|
||||
in
|
||||
attrs: concatStrings (concatLists (mapAttrsToList mkLines attrs));
|
||||
|
||||
/**
|
||||
Generate an INI-style config file from an
|
||||
|
@ -224,22 +240,27 @@ in rec {
|
|||
|
||||
:::
|
||||
*/
|
||||
toINI = {
|
||||
mkSectionName ? (name: escape [ "[" "]" ] name),
|
||||
mkKeyValue ? mkKeyValueDefault {} "=",
|
||||
listsAsDuplicateKeys ? false
|
||||
}: attrsOfAttrs:
|
||||
toINI =
|
||||
{
|
||||
mkSectionName ? (name: escape [ "[" "]" ] name),
|
||||
mkKeyValue ? mkKeyValueDefault { } "=",
|
||||
listsAsDuplicateKeys ? false,
|
||||
}:
|
||||
attrsOfAttrs:
|
||||
let
|
||||
# map function to string for each key val
|
||||
mapAttrsToStringsSep = sep: mapFn: attrs:
|
||||
concatStringsSep sep
|
||||
(mapAttrsToList mapFn attrs);
|
||||
mkSection = sectName: sectValues: ''
|
||||
# map function to string for each key val
|
||||
mapAttrsToStringsSep =
|
||||
sep: mapFn: attrs:
|
||||
concatStringsSep sep (mapAttrsToList mapFn attrs);
|
||||
mkSection =
|
||||
sectName: sectValues:
|
||||
''
|
||||
[${mkSectionName sectName}]
|
||||
'' + toKeyValue { inherit mkKeyValue listsAsDuplicateKeys; } sectValues;
|
||||
''
|
||||
+ toKeyValue { inherit mkKeyValue listsAsDuplicateKeys; } sectValues;
|
||||
in
|
||||
# map input to ini sections
|
||||
mapAttrsToStringsSep "\n" mkSection attrsOfAttrs;
|
||||
# map input to ini sections
|
||||
mapAttrsToStringsSep "\n" mkSection attrsOfAttrs;
|
||||
|
||||
/**
|
||||
Generate an INI-style config file from an attrset
|
||||
|
@ -302,15 +323,22 @@ in rec {
|
|||
`generators.toINI` directly, which only takes
|
||||
the part in `sections`.
|
||||
*/
|
||||
toINIWithGlobalSection = {
|
||||
mkSectionName ? (name: escape [ "[" "]" ] name),
|
||||
mkKeyValue ? mkKeyValueDefault {} "=",
|
||||
listsAsDuplicateKeys ? false
|
||||
}: { globalSection, sections ? {} }:
|
||||
( if globalSection == {}
|
||||
then ""
|
||||
else (toKeyValue { inherit mkKeyValue listsAsDuplicateKeys; } globalSection)
|
||||
+ "\n")
|
||||
toINIWithGlobalSection =
|
||||
{
|
||||
mkSectionName ? (name: escape [ "[" "]" ] name),
|
||||
mkKeyValue ? mkKeyValueDefault { } "=",
|
||||
listsAsDuplicateKeys ? false,
|
||||
}:
|
||||
{
|
||||
globalSection,
|
||||
sections ? { },
|
||||
}:
|
||||
(
|
||||
if globalSection == { } then
|
||||
""
|
||||
else
|
||||
(toKeyValue { inherit mkKeyValue listsAsDuplicateKeys; } globalSection) + "\n"
|
||||
)
|
||||
+ (toINI { inherit mkSectionName mkKeyValue listsAsDuplicateKeys; } sections);
|
||||
|
||||
/**
|
||||
|
@ -348,50 +376,57 @@ in rec {
|
|||
|
||||
: Key-value pairs to be converted to a git-config file.
|
||||
See: https://git-scm.com/docs/git-config#_variables for possible values.
|
||||
|
||||
*/
|
||||
toGitINI = attrs:
|
||||
toGitINI =
|
||||
attrs:
|
||||
let
|
||||
mkSectionName = name:
|
||||
mkSectionName =
|
||||
name:
|
||||
let
|
||||
containsQuote = hasInfix ''"'' name;
|
||||
sections = splitString "." name;
|
||||
section = head sections;
|
||||
subsections = tail sections;
|
||||
subsection = concatStringsSep "." subsections;
|
||||
in if containsQuote || subsections == [ ] then
|
||||
name
|
||||
else
|
||||
''${section} "${subsection}"'';
|
||||
in
|
||||
if containsQuote || subsections == [ ] then name else ''${section} "${subsection}"'';
|
||||
|
||||
mkValueString = v:
|
||||
mkValueString =
|
||||
v:
|
||||
let
|
||||
escapedV = ''
|
||||
"${
|
||||
replaceStrings [ "\n" " " ''"'' "\\" ] [ "\\n" "\\t" ''\"'' "\\\\" ] v
|
||||
}"'';
|
||||
in mkValueStringDefault { } (if isString v then escapedV else v);
|
||||
escapedV = ''"${replaceStrings [ "\n" " " ''"'' "\\" ] [ "\\n" "\\t" ''\"'' "\\\\" ] v}"'';
|
||||
in
|
||||
mkValueStringDefault { } (if isString v then escapedV else v);
|
||||
|
||||
# generation for multiple ini values
|
||||
mkKeyValue = k: v:
|
||||
let mkKeyValue = mkKeyValueDefault { inherit mkValueString; } " = " k;
|
||||
in concatStringsSep "\n" (map (kv: "\t" + mkKeyValue kv) (toList v));
|
||||
mkKeyValue =
|
||||
k: v:
|
||||
let
|
||||
mkKeyValue = mkKeyValueDefault { inherit mkValueString; } " = " k;
|
||||
in
|
||||
concatStringsSep "\n" (map (kv: "\t" + mkKeyValue kv) (toList v));
|
||||
|
||||
# converts { a.b.c = 5; } to { "a.b".c = 5; } for toINI
|
||||
gitFlattenAttrs = let
|
||||
recurse = path: value:
|
||||
if isAttrs value && !isDerivation value then
|
||||
mapAttrsToList (name: value: recurse ([ name ] ++ path) value) value
|
||||
else if length path > 1 then {
|
||||
${concatStringsSep "." (reverseList (tail path))}.${head path} = value;
|
||||
} else {
|
||||
${head path} = value;
|
||||
};
|
||||
in attrs: foldl recursiveUpdate { } (flatten (recurse [ ] attrs));
|
||||
gitFlattenAttrs =
|
||||
let
|
||||
recurse =
|
||||
path: value:
|
||||
if isAttrs value && !isDerivation value then
|
||||
mapAttrsToList (name: value: recurse ([ name ] ++ path) value) value
|
||||
else if length path > 1 then
|
||||
{
|
||||
${concatStringsSep "." (reverseList (tail path))}.${head path} = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
${head path} = value;
|
||||
};
|
||||
in
|
||||
attrs: foldl recursiveUpdate { } (flatten (recurse [ ] attrs));
|
||||
|
||||
toINI_ = toINI { inherit mkKeyValue mkSectionName; };
|
||||
in
|
||||
toINI_ (gitFlattenAttrs attrs);
|
||||
toINI_ (gitFlattenAttrs attrs);
|
||||
|
||||
/**
|
||||
mkKeyValueDefault wrapper that handles dconf INI quirks.
|
||||
|
@ -426,35 +461,39 @@ in rec {
|
|||
withRecursion =
|
||||
{
|
||||
depthLimit,
|
||||
throwOnDepthLimit ? true
|
||||
throwOnDepthLimit ? true,
|
||||
}:
|
||||
assert isInt depthLimit;
|
||||
let
|
||||
specialAttrs = [
|
||||
"__functor"
|
||||
"__functionArgs"
|
||||
"__toString"
|
||||
"__pretty"
|
||||
];
|
||||
stepIntoAttr = evalNext: name:
|
||||
if elem name specialAttrs
|
||||
then id
|
||||
else evalNext;
|
||||
transform = depth:
|
||||
if depthLimit != null && depth > depthLimit then
|
||||
if throwOnDepthLimit
|
||||
then throw "Exceeded maximum eval-depth limit of ${toString depthLimit} while trying to evaluate with `generators.withRecursion'!"
|
||||
else const "<unevaluated>"
|
||||
else id;
|
||||
mapAny = depth: v:
|
||||
let
|
||||
evalNext = x: mapAny (depth + 1) (transform (depth + 1) x);
|
||||
in
|
||||
if isAttrs v then mapAttrs (stepIntoAttr evalNext) v
|
||||
else if isList v then map evalNext v
|
||||
else transform (depth + 1) v;
|
||||
in
|
||||
mapAny 0;
|
||||
assert isInt depthLimit;
|
||||
let
|
||||
specialAttrs = [
|
||||
"__functor"
|
||||
"__functionArgs"
|
||||
"__toString"
|
||||
"__pretty"
|
||||
];
|
||||
stepIntoAttr = evalNext: name: if elem name specialAttrs then id else evalNext;
|
||||
transform =
|
||||
depth:
|
||||
if depthLimit != null && depth > depthLimit then
|
||||
if throwOnDepthLimit then
|
||||
throw "Exceeded maximum eval-depth limit of ${toString depthLimit} while trying to evaluate with `generators.withRecursion'!"
|
||||
else
|
||||
const "<unevaluated>"
|
||||
else
|
||||
id;
|
||||
mapAny =
|
||||
depth: v:
|
||||
let
|
||||
evalNext = x: mapAny (depth + 1) (transform (depth + 1) x);
|
||||
in
|
||||
if isAttrs v then
|
||||
mapAttrs (stepIntoAttr evalNext) v
|
||||
else if isList v then
|
||||
map evalNext v
|
||||
else
|
||||
transform (depth + 1) v;
|
||||
in
|
||||
mapAny 0;
|
||||
|
||||
/**
|
||||
Pretty print a value, akin to `builtins.trace`.
|
||||
|
@ -480,68 +519,96 @@ in rec {
|
|||
Value
|
||||
: The value to be pretty printed
|
||||
*/
|
||||
toPretty = {
|
||||
allowPrettyValues ? false,
|
||||
multiline ? true,
|
||||
indent ? ""
|
||||
}:
|
||||
toPretty =
|
||||
{
|
||||
allowPrettyValues ? false,
|
||||
multiline ? true,
|
||||
indent ? "",
|
||||
}:
|
||||
let
|
||||
go = indent: v:
|
||||
let introSpace = if multiline then "\n${indent} " else " ";
|
||||
outroSpace = if multiline then "\n${indent}" else " ";
|
||||
in if isInt v then toString v
|
||||
# toString loses precision on floats, so we use toJSON instead. This isn't perfect
|
||||
# as the resulting string may not parse back as a float (e.g. 42, 1e-06), but for
|
||||
# pretty-printing purposes this is acceptable.
|
||||
else if isFloat v then builtins.toJSON v
|
||||
else if isString v then
|
||||
let
|
||||
lines = filter (v: ! isList v) (split "\n" v);
|
||||
escapeSingleline = escape [ "\\" "\"" "\${" ];
|
||||
escapeMultiline = replaceStrings [ "\${" "''" ] [ "''\${" "'''" ];
|
||||
singlelineResult = "\"" + concatStringsSep "\\n" (map escapeSingleline lines) + "\"";
|
||||
multilineResult = let
|
||||
escapedLines = map escapeMultiline lines;
|
||||
# The last line gets a special treatment: if it's empty, '' is on its own line at the "outer"
|
||||
# indentation level. Otherwise, '' is appended to the last line.
|
||||
lastLine = last escapedLines;
|
||||
in "''" + introSpace + concatStringsSep introSpace (init escapedLines)
|
||||
+ (if lastLine == "" then outroSpace else introSpace + lastLine) + "''";
|
||||
in
|
||||
if multiline && length lines > 1 then multilineResult else singlelineResult
|
||||
else if true == v then "true"
|
||||
else if false == v then "false"
|
||||
else if null == v then "null"
|
||||
else if isPath v then toString v
|
||||
else if isList v then
|
||||
if v == [] then "[ ]"
|
||||
else "[" + introSpace
|
||||
+ concatMapStringsSep introSpace (go (indent + " ")) v
|
||||
+ outroSpace + "]"
|
||||
else if isFunction v then
|
||||
let fna = functionArgs v;
|
||||
showFnas = concatStringsSep ", " (mapAttrsToList
|
||||
(name: hasDefVal: if hasDefVal then name + "?" else name)
|
||||
fna);
|
||||
in if fna == {} then "<function>"
|
||||
else "<function, args: {${showFnas}}>"
|
||||
else if isAttrs v then
|
||||
# apply pretty values if allowed
|
||||
if allowPrettyValues && v ? __pretty && v ? val
|
||||
then v.__pretty v.val
|
||||
else if v == {} then "{ }"
|
||||
else if v ? type && v.type == "derivation" then
|
||||
"<derivation ${v.name or "???"}>"
|
||||
else "{" + introSpace
|
||||
+ concatStringsSep introSpace (mapAttrsToList
|
||||
(name: value:
|
||||
go =
|
||||
indent: v:
|
||||
let
|
||||
introSpace = if multiline then "\n${indent} " else " ";
|
||||
outroSpace = if multiline then "\n${indent}" else " ";
|
||||
in
|
||||
if isInt v then
|
||||
toString v
|
||||
# toString loses precision on floats, so we use toJSON instead. This isn't perfect
|
||||
# as the resulting string may not parse back as a float (e.g. 42, 1e-06), but for
|
||||
# pretty-printing purposes this is acceptable.
|
||||
else if isFloat v then
|
||||
builtins.toJSON v
|
||||
else if isString v then
|
||||
let
|
||||
lines = filter (v: !isList v) (split "\n" v);
|
||||
escapeSingleline = escape [
|
||||
"\\"
|
||||
"\""
|
||||
"\${"
|
||||
];
|
||||
escapeMultiline = replaceStrings [ "\${" "''" ] [ "''\${" "'''" ];
|
||||
singlelineResult = "\"" + concatStringsSep "\\n" (map escapeSingleline lines) + "\"";
|
||||
multilineResult =
|
||||
let
|
||||
escapedLines = map escapeMultiline lines;
|
||||
# The last line gets a special treatment: if it's empty, '' is on its own line at the "outer"
|
||||
# indentation level. Otherwise, '' is appended to the last line.
|
||||
lastLine = last escapedLines;
|
||||
in
|
||||
"''"
|
||||
+ introSpace
|
||||
+ concatStringsSep introSpace (init escapedLines)
|
||||
+ (if lastLine == "" then outroSpace else introSpace + lastLine)
|
||||
+ "''";
|
||||
in
|
||||
if multiline && length lines > 1 then multilineResult else singlelineResult
|
||||
else if true == v then
|
||||
"true"
|
||||
else if false == v then
|
||||
"false"
|
||||
else if null == v then
|
||||
"null"
|
||||
else if isPath v then
|
||||
toString v
|
||||
else if isList v then
|
||||
if v == [ ] then
|
||||
"[ ]"
|
||||
else
|
||||
"[" + introSpace + concatMapStringsSep introSpace (go (indent + " ")) v + outroSpace + "]"
|
||||
else if isFunction v then
|
||||
let
|
||||
fna = functionArgs v;
|
||||
showFnas = concatStringsSep ", " (
|
||||
mapAttrsToList (name: hasDefVal: if hasDefVal then name + "?" else name) fna
|
||||
);
|
||||
in
|
||||
if fna == { } then "<function>" else "<function, args: {${showFnas}}>"
|
||||
else if isAttrs v then
|
||||
# apply pretty values if allowed
|
||||
if allowPrettyValues && v ? __pretty && v ? val then
|
||||
v.__pretty v.val
|
||||
else if v == { } then
|
||||
"{ }"
|
||||
else if v ? type && v.type == "derivation" then
|
||||
"<derivation ${v.name or "???"}>"
|
||||
else
|
||||
"{"
|
||||
+ introSpace
|
||||
+ concatStringsSep introSpace (
|
||||
mapAttrsToList (
|
||||
name: value:
|
||||
"${escapeNixIdentifier name} = ${
|
||||
addErrorContext "while evaluating an attribute `${name}`"
|
||||
(go (indent + " ") value)
|
||||
};") v)
|
||||
+ outroSpace + "}"
|
||||
else abort "generators.toPretty: should never happen (v = ${v})";
|
||||
in go indent;
|
||||
addErrorContext "while evaluating an attribute `${name}`" (go (indent + " ") value)
|
||||
};"
|
||||
) v
|
||||
)
|
||||
+ outroSpace
|
||||
+ "}"
|
||||
else
|
||||
abort "generators.toPretty: should never happen (v = ${v})";
|
||||
in
|
||||
go indent;
|
||||
|
||||
/**
|
||||
Translate a simple Nix expression to [Plist notation](https://en.wikipedia.org/wiki/Property_list).
|
||||
|
@ -554,56 +621,83 @@ in rec {
|
|||
Value
|
||||
: The value to be converted to Plist
|
||||
*/
|
||||
toPlist = {}: v: let
|
||||
expr = ind: x:
|
||||
if x == null then "" else
|
||||
if isBool x then bool ind x else
|
||||
if isInt x then int ind x else
|
||||
if isString x then str ind x else
|
||||
if isList x then list ind x else
|
||||
if isAttrs x then attrs ind x else
|
||||
if isPath x then str ind (toString x) else
|
||||
if isFloat x then float ind x else
|
||||
abort "generators.toPlist: should never happen (v = ${v})";
|
||||
toPlist =
|
||||
{ }:
|
||||
v:
|
||||
let
|
||||
expr =
|
||||
ind: x:
|
||||
if x == null then
|
||||
""
|
||||
else if isBool x then
|
||||
bool ind x
|
||||
else if isInt x then
|
||||
int ind x
|
||||
else if isString x then
|
||||
str ind x
|
||||
else if isList x then
|
||||
list ind x
|
||||
else if isAttrs x then
|
||||
attrs ind x
|
||||
else if isPath x then
|
||||
str ind (toString x)
|
||||
else if isFloat x then
|
||||
float ind x
|
||||
else
|
||||
abort "generators.toPlist: should never happen (v = ${v})";
|
||||
|
||||
literal = ind: x: ind + x;
|
||||
literal = ind: x: ind + x;
|
||||
|
||||
bool = ind: x: literal ind (if x then "<true/>" else "<false/>");
|
||||
int = ind: x: literal ind "<integer>${toString x}</integer>";
|
||||
str = ind: x: literal ind "<string>${x}</string>";
|
||||
key = ind: x: literal ind "<key>${x}</key>";
|
||||
float = ind: x: literal ind "<real>${toString x}</real>";
|
||||
bool = ind: x: literal ind (if x then "<true/>" else "<false/>");
|
||||
int = ind: x: literal ind "<integer>${toString x}</integer>";
|
||||
str = ind: x: literal ind "<string>${x}</string>";
|
||||
key = ind: x: literal ind "<key>${x}</key>";
|
||||
float = ind: x: literal ind "<real>${toString x}</real>";
|
||||
|
||||
indent = ind: expr "\t${ind}";
|
||||
indent = ind: expr "\t${ind}";
|
||||
|
||||
item = ind: concatMapStringsSep "\n" (indent ind);
|
||||
item = ind: concatMapStringsSep "\n" (indent ind);
|
||||
|
||||
list = ind: x: concatStringsSep "\n" [
|
||||
(literal ind "<array>")
|
||||
(item ind x)
|
||||
(literal ind "</array>")
|
||||
];
|
||||
list =
|
||||
ind: x:
|
||||
concatStringsSep "\n" [
|
||||
(literal ind "<array>")
|
||||
(item ind x)
|
||||
(literal ind "</array>")
|
||||
];
|
||||
|
||||
attrs = ind: x: concatStringsSep "\n" [
|
||||
(literal ind "<dict>")
|
||||
(attr ind x)
|
||||
(literal ind "</dict>")
|
||||
];
|
||||
attrs =
|
||||
ind: x:
|
||||
concatStringsSep "\n" [
|
||||
(literal ind "<dict>")
|
||||
(attr ind x)
|
||||
(literal ind "</dict>")
|
||||
];
|
||||
|
||||
attr = let attrFilter = name: value: name != "_module" && value != null;
|
||||
in ind: x: concatStringsSep "\n" (flatten (mapAttrsToList
|
||||
(name: value: optionals (attrFilter name value) [
|
||||
(key "\t${ind}" name)
|
||||
(expr "\t${ind}" value)
|
||||
]) x));
|
||||
attr =
|
||||
let
|
||||
attrFilter = name: value: name != "_module" && value != null;
|
||||
in
|
||||
ind: x:
|
||||
concatStringsSep "\n" (
|
||||
flatten (
|
||||
mapAttrsToList (
|
||||
name: value:
|
||||
optionals (attrFilter name value) [
|
||||
(key "\t${ind}" name)
|
||||
(expr "\t${ind}" value)
|
||||
]
|
||||
) x
|
||||
)
|
||||
);
|
||||
|
||||
in
|
||||
''
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
${expr "" v}
|
||||
</plist>'';
|
||||
in
|
||||
''
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
${expr "" v}
|
||||
</plist>'';
|
||||
|
||||
/**
|
||||
Translate a simple Nix expression to Dhall notation.
|
||||
|
@ -621,13 +715,14 @@ in rec {
|
|||
|
||||
: The value to be converted to Dhall
|
||||
*/
|
||||
toDhall = { }@args: v:
|
||||
let concatItems = concatStringsSep ", ";
|
||||
in if isAttrs v then
|
||||
"{ ${
|
||||
concatItems (mapAttrsToList
|
||||
(key: value: "${key} = ${toDhall args value}") v)
|
||||
} }"
|
||||
toDhall =
|
||||
{ }@args:
|
||||
v:
|
||||
let
|
||||
concatItems = concatStringsSep ", ";
|
||||
in
|
||||
if isAttrs v then
|
||||
"{ ${concatItems (mapAttrsToList (key: value: "${key} = ${toDhall args value}") v)} }"
|
||||
else if isList v then
|
||||
"[ ${concatItems (map (toDhall args) v)} ]"
|
||||
else if isInt v then
|
||||
|
@ -655,7 +750,6 @@ in rec {
|
|||
|
||||
Regardless of multiline parameter there is no trailing newline.
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
Structured function argument
|
||||
|
@ -703,11 +797,13 @@ in rec {
|
|||
|
||||
:::
|
||||
*/
|
||||
toLua = {
|
||||
multiline ? true,
|
||||
indent ? "",
|
||||
asBindings ? false,
|
||||
}@args: v:
|
||||
toLua =
|
||||
{
|
||||
multiline ? true,
|
||||
indent ? "",
|
||||
asBindings ? false,
|
||||
}@args:
|
||||
v:
|
||||
let
|
||||
innerIndent = "${indent} ";
|
||||
introSpace = if multiline then "\n${innerIndent}" else " ";
|
||||
|
@ -717,13 +813,16 @@ in rec {
|
|||
asBindings = false;
|
||||
};
|
||||
concatItems = concatStringsSep ",${introSpace}";
|
||||
isLuaInline = { _type ? null, ... }: _type == "lua-inline";
|
||||
isLuaInline =
|
||||
{
|
||||
_type ? null,
|
||||
...
|
||||
}:
|
||||
_type == "lua-inline";
|
||||
|
||||
generatedBindings =
|
||||
assert assertMsg (badVarNames == []) "Bad Lua var names: ${toPretty {} badVarNames}";
|
||||
concatStrings (
|
||||
mapAttrsToList (key: value: "${indent}${key} = ${toLua innerArgs value}\n") v
|
||||
);
|
||||
assert assertMsg (badVarNames == [ ]) "Bad Lua var names: ${toPretty { } badVarNames}";
|
||||
concatStrings (mapAttrsToList (key: value: "${indent}${key} = ${toLua innerArgs value}\n") v);
|
||||
|
||||
# https://en.wikibooks.org/wiki/Lua_Programming/variable#Variable_names
|
||||
matchVarName = match "[[:alpha:]_][[:alnum:]_]*(\\.[[:alpha:]_][[:alnum:]_]*)*";
|
||||
|
@ -736,8 +835,12 @@ in rec {
|
|||
else if isInt v || isFloat v || isString v || isBool v then
|
||||
toJSON v
|
||||
else if isList v then
|
||||
(if v == [ ] then "{}" else
|
||||
"{${introSpace}${concatItems (map (value: "${toLua innerArgs value}") v)}${outroSpace}}")
|
||||
(
|
||||
if v == [ ] then
|
||||
"{}"
|
||||
else
|
||||
"{${introSpace}${concatItems (map (value: "${toLua innerArgs value}") v)}${outroSpace}}"
|
||||
)
|
||||
else if isAttrs v then
|
||||
(
|
||||
if isLuaInline v then
|
||||
|
@ -747,9 +850,9 @@ in rec {
|
|||
else if isDerivation v then
|
||||
''"${toString v}"''
|
||||
else
|
||||
"{${introSpace}${concatItems (
|
||||
mapAttrsToList (key: value: "[${toJSON key}] = ${toLua innerArgs value}") v
|
||||
)}${outroSpace}}"
|
||||
"{${introSpace}${
|
||||
concatItems (mapAttrsToList (key: value: "[${toJSON key}] = ${toLua innerArgs value}") v)
|
||||
}${outroSpace}}"
|
||||
)
|
||||
else
|
||||
abort "generators.toLua: type ${typeOf v} is unsupported";
|
||||
|
@ -757,7 +860,6 @@ in rec {
|
|||
/**
|
||||
Mark string as Lua expression to be inlined when processed by toLua.
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`expr`
|
||||
|
@ -770,8 +872,12 @@ in rec {
|
|||
mkLuaInline :: String -> AttrSet
|
||||
```
|
||||
*/
|
||||
mkLuaInline = expr: { _type = "lua-inline"; inherit expr; };
|
||||
} // {
|
||||
mkLuaInline = expr: {
|
||||
_type = "lua-inline";
|
||||
inherit expr;
|
||||
};
|
||||
}
|
||||
// {
|
||||
/**
|
||||
Generates JSON from an arbitrary (non-function) value.
|
||||
For more information see the documentation of the builtin.
|
||||
|
@ -786,7 +892,7 @@ in rec {
|
|||
|
||||
: The value to be converted to JSON
|
||||
*/
|
||||
toJSON = {}: lib.strings.toJSON;
|
||||
toJSON = { }: lib.strings.toJSON;
|
||||
|
||||
/**
|
||||
YAML has been a strict superset of JSON since 1.2, so we
|
||||
|
@ -804,5 +910,5 @@ in rec {
|
|||
|
||||
: The value to be converted to YAML
|
||||
*/
|
||||
toYAML = {}: lib.strings.toJSON;
|
||||
toYAML = { }: lib.strings.toJSON;
|
||||
}
|
||||
|
|
2831
lib/licenses.nix
2831
lib/licenses.nix
File diff suppressed because it is too large
Load diff
378
lib/lists.nix
378
lib/lists.nix
|
@ -4,13 +4,30 @@
|
|||
{ lib }:
|
||||
let
|
||||
inherit (lib.strings) toInt;
|
||||
inherit (lib.trivial) compare min id warn pipe;
|
||||
inherit (lib.trivial)
|
||||
compare
|
||||
min
|
||||
id
|
||||
warn
|
||||
pipe
|
||||
;
|
||||
inherit (lib.attrsets) mapAttrs;
|
||||
inherit (lib) max;
|
||||
in
|
||||
rec {
|
||||
|
||||
inherit (builtins) head tail length isList elemAt concatLists filter elem genList map;
|
||||
inherit (builtins)
|
||||
head
|
||||
tail
|
||||
length
|
||||
isList
|
||||
elemAt
|
||||
concatLists
|
||||
filter
|
||||
elem
|
||||
genList
|
||||
map
|
||||
;
|
||||
|
||||
/**
|
||||
Create a list consisting of a single element. `singleton x` is
|
||||
|
@ -40,7 +57,7 @@ rec {
|
|||
|
||||
:::
|
||||
*/
|
||||
singleton = x: [x];
|
||||
singleton = x: [ x ];
|
||||
|
||||
/**
|
||||
Apply the function to each element in the list.
|
||||
|
@ -82,7 +99,6 @@ rec {
|
|||
`list` with `nul` as the starting value, i.e.,
|
||||
`foldr op nul [x_1 x_2 ... x_n] == op x_1 (op x_2 ... (op x_n nul))`.
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`op`
|
||||
|
@ -119,14 +135,13 @@ rec {
|
|||
|
||||
:::
|
||||
*/
|
||||
foldr = op: nul: list:
|
||||
foldr =
|
||||
op: nul: list:
|
||||
let
|
||||
len = length list;
|
||||
fold' = n:
|
||||
if n == len
|
||||
then nul
|
||||
else op (elemAt list n) (fold' (n + 1));
|
||||
in fold' 0;
|
||||
fold' = n: if n == len then nul else op (elemAt list n) (fold' (n + 1));
|
||||
in
|
||||
fold' 0;
|
||||
|
||||
/**
|
||||
`fold` is an alias of `foldr` for historic reasons
|
||||
|
@ -134,7 +149,6 @@ rec {
|
|||
# FIXME(Profpatsch): deprecate?
|
||||
fold = foldr;
|
||||
|
||||
|
||||
/**
|
||||
“left fold”, like `foldr`, but from the left:
|
||||
|
||||
|
@ -176,13 +190,12 @@ rec {
|
|||
|
||||
:::
|
||||
*/
|
||||
foldl = op: nul: list:
|
||||
foldl =
|
||||
op: nul: list:
|
||||
let
|
||||
foldl' = n:
|
||||
if n == -1
|
||||
then nul
|
||||
else op (foldl' (n - 1)) (elemAt list n);
|
||||
in foldl' (length list - 1);
|
||||
foldl' = n: if n == -1 then nul else op (foldl' (n - 1)) (elemAt list n);
|
||||
in
|
||||
foldl' (length list - 1);
|
||||
|
||||
/**
|
||||
Reduce a list by applying a binary operator from left to right,
|
||||
|
@ -261,13 +274,11 @@ rec {
|
|||
:::
|
||||
*/
|
||||
foldl' =
|
||||
op:
|
||||
acc:
|
||||
op: acc:
|
||||
# The builtin `foldl'` is a bit lazier than one might expect.
|
||||
# See https://github.com/NixOS/nix/pull/7158.
|
||||
# In particular, the initial accumulator value is not forced before the first iteration starts.
|
||||
builtins.seq acc
|
||||
(builtins.foldl' op acc);
|
||||
builtins.seq acc (builtins.foldl' op acc);
|
||||
|
||||
/**
|
||||
Map with index starting from 0
|
||||
|
@ -304,7 +315,6 @@ rec {
|
|||
/**
|
||||
Map with index starting from 1
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`f`
|
||||
|
@ -374,12 +384,9 @@ rec {
|
|||
:::
|
||||
*/
|
||||
ifilter0 =
|
||||
ipred:
|
||||
input:
|
||||
ipred: input:
|
||||
map (idx: elemAt input idx) (
|
||||
filter (idx: ipred idx (elemAt input idx)) (
|
||||
genList (x: x) (length input)
|
||||
)
|
||||
filter (idx: ipred idx (elemAt input idx)) (genList (x: x) (length input))
|
||||
);
|
||||
|
||||
/**
|
||||
|
@ -408,14 +415,12 @@ rec {
|
|||
Flatten the argument into a single list; that is, nested lists are
|
||||
spliced into the top-level lists.
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`x`
|
||||
|
||||
: 1\. Function argument
|
||||
|
||||
|
||||
# Examples
|
||||
:::{.example}
|
||||
## `lib.lists.flatten` usage example
|
||||
|
@ -429,15 +434,11 @@ rec {
|
|||
|
||||
:::
|
||||
*/
|
||||
flatten = x:
|
||||
if isList x
|
||||
then concatMap (y: flatten y) x
|
||||
else [x];
|
||||
flatten = x: if isList x then concatMap (y: flatten y) x else [ x ];
|
||||
|
||||
/**
|
||||
Remove elements equal to 'e' from a list. Useful for buildInputs.
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`e`
|
||||
|
@ -465,8 +466,7 @@ rec {
|
|||
|
||||
:::
|
||||
*/
|
||||
remove =
|
||||
e: filter (x: x != e);
|
||||
remove = e: filter (x: x != e);
|
||||
|
||||
/**
|
||||
Find the sole element in the list matching the specified
|
||||
|
@ -475,7 +475,6 @@ rec {
|
|||
Returns `default` if no such element exists, or
|
||||
`multiple` if there are multiple matching elements.
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`pred`
|
||||
|
@ -516,14 +515,17 @@ rec {
|
|||
:::
|
||||
*/
|
||||
findSingle =
|
||||
pred:
|
||||
default:
|
||||
multiple:
|
||||
list:
|
||||
let found = filter pred list; len = length found;
|
||||
in if len == 0 then default
|
||||
else if len != 1 then multiple
|
||||
else head found;
|
||||
pred: default: multiple: list:
|
||||
let
|
||||
found = filter pred list;
|
||||
len = length found;
|
||||
in
|
||||
if len == 0 then
|
||||
default
|
||||
else if len != 1 then
|
||||
multiple
|
||||
else
|
||||
head found;
|
||||
|
||||
/**
|
||||
Find the first index in the list matching the specified
|
||||
|
@ -563,9 +565,7 @@ rec {
|
|||
:::
|
||||
*/
|
||||
findFirstIndex =
|
||||
pred:
|
||||
default:
|
||||
list:
|
||||
pred: default: list:
|
||||
let
|
||||
# A naive recursive implementation would be much simpler, but
|
||||
# would also overflow the evaluator stack. We use `foldl'` as a workaround
|
||||
|
@ -580,12 +580,13 @@ rec {
|
|||
# - if index >= 0 then pred (elemAt list index) and all elements before (elemAt list index) didn't satisfy pred
|
||||
#
|
||||
# We start with index -1 and the 0'th element of the list, which satisfies the invariant
|
||||
resultIndex = foldl' (index: el:
|
||||
resultIndex = foldl' (
|
||||
index: el:
|
||||
if index < 0 then
|
||||
# No match yet before the current index, we need to check the element
|
||||
if pred el then
|
||||
# We have a match! Turn it into the actual index to prevent future iterations from modifying it
|
||||
- index - 1
|
||||
-index - 1
|
||||
else
|
||||
# Still no match, update the index to the next element (we're counting down, so minus one)
|
||||
index - 1
|
||||
|
@ -594,10 +595,7 @@ rec {
|
|||
index
|
||||
) (-1) list;
|
||||
in
|
||||
if resultIndex < 0 then
|
||||
default
|
||||
else
|
||||
resultIndex;
|
||||
if resultIndex < 0 then default else resultIndex;
|
||||
|
||||
/**
|
||||
Find the first element in the list matching the specified
|
||||
|
@ -637,16 +635,11 @@ rec {
|
|||
:::
|
||||
*/
|
||||
findFirst =
|
||||
pred:
|
||||
default:
|
||||
list:
|
||||
pred: default: list:
|
||||
let
|
||||
index = findFirstIndex pred null list;
|
||||
in
|
||||
if index == null then
|
||||
default
|
||||
else
|
||||
elemAt list index;
|
||||
if index == null then default else elemAt list index;
|
||||
|
||||
/**
|
||||
Return true if function `pred` returns true for at least one
|
||||
|
@ -745,8 +738,7 @@ rec {
|
|||
|
||||
:::
|
||||
*/
|
||||
count =
|
||||
pred: foldl' (c: x: if pred x then c + 1 else c) 0;
|
||||
count = pred: foldl' (c: x: if pred x then c + 1 else c) 0;
|
||||
|
||||
/**
|
||||
Return a singleton list or an empty list, depending on a boolean
|
||||
|
@ -782,7 +774,7 @@ rec {
|
|||
|
||||
:::
|
||||
*/
|
||||
optional = cond: elem: if cond then [elem] else [];
|
||||
optional = cond: elem: if cond then [ elem ] else [ ];
|
||||
|
||||
/**
|
||||
Return a list or an empty list, depending on a boolean value.
|
||||
|
@ -816,10 +808,7 @@ rec {
|
|||
|
||||
:::
|
||||
*/
|
||||
optionals =
|
||||
cond:
|
||||
elems: if cond then elems else [];
|
||||
|
||||
optionals = cond: elems: if cond then elems else [ ];
|
||||
|
||||
/**
|
||||
If argument is a list, return it; else, wrap it in a singleton
|
||||
|
@ -845,7 +834,7 @@ rec {
|
|||
|
||||
:::
|
||||
*/
|
||||
toList = x: if isList x then x else [x];
|
||||
toList = x: if isList x then x else [ x ];
|
||||
|
||||
/**
|
||||
Return a list of integers from `first` up to and including `last`.
|
||||
|
@ -879,13 +868,7 @@ rec {
|
|||
|
||||
:::
|
||||
*/
|
||||
range =
|
||||
first:
|
||||
last:
|
||||
if first > last then
|
||||
[]
|
||||
else
|
||||
genList (n: first + n) (last - first + 1);
|
||||
range = first: last: if first > last then [ ] else genList (n: first + n) (last - first + 1);
|
||||
|
||||
/**
|
||||
Return a list with `n` copies of an element.
|
||||
|
@ -977,7 +960,6 @@ rec {
|
|||
|
||||
: 4\. Function argument
|
||||
|
||||
|
||||
# Examples
|
||||
:::{.example}
|
||||
## `lib.lists.groupBy'` usage example
|
||||
|
@ -1002,15 +984,21 @@ rec {
|
|||
|
||||
:::
|
||||
*/
|
||||
groupBy' = op: nul: pred: lst: mapAttrs (name: foldl op nul) (groupBy pred lst);
|
||||
groupBy' =
|
||||
op: nul: pred: lst:
|
||||
mapAttrs (name: foldl op nul) (groupBy pred lst);
|
||||
|
||||
groupBy = builtins.groupBy or (
|
||||
pred: foldl' (r: e:
|
||||
let
|
||||
key = pred e;
|
||||
in
|
||||
r // { ${key} = (r.${key} or []) ++ [e]; }
|
||||
) {});
|
||||
groupBy =
|
||||
builtins.groupBy or (
|
||||
pred:
|
||||
foldl' (
|
||||
r: e:
|
||||
let
|
||||
key = pred e;
|
||||
in
|
||||
r // { ${key} = (r.${key} or [ ]) ++ [ e ]; }
|
||||
) { }
|
||||
);
|
||||
|
||||
/**
|
||||
Merges two lists of the same size together. If the sizes aren't the same
|
||||
|
@ -1049,11 +1037,8 @@ rec {
|
|||
:::
|
||||
*/
|
||||
zipListsWith =
|
||||
f:
|
||||
fst:
|
||||
snd:
|
||||
genList
|
||||
(n: f (elemAt fst n) (elemAt snd n)) (min (length fst) (length snd));
|
||||
f: fst: snd:
|
||||
genList (n: f (elemAt fst n) (elemAt snd n)) (min (length fst) (length snd));
|
||||
|
||||
/**
|
||||
Merges two lists of the same size together. If the sizes aren't the same
|
||||
|
@ -1114,8 +1099,12 @@ rec {
|
|||
|
||||
:::
|
||||
*/
|
||||
reverseList = xs:
|
||||
let l = length xs; in genList (n: elemAt xs (l - n - 1)) l;
|
||||
reverseList =
|
||||
xs:
|
||||
let
|
||||
l = length xs;
|
||||
in
|
||||
genList (n: elemAt xs (l - n - 1)) l;
|
||||
|
||||
/**
|
||||
Depth-First Search (DFS) for lists `list != []`.
|
||||
|
@ -1123,7 +1112,6 @@ rec {
|
|||
`before a b == true` means that `b` depends on `a` (there's an
|
||||
edge from `b` to `a`).
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`stopOnCycles`
|
||||
|
@ -1138,7 +1126,6 @@ rec {
|
|||
|
||||
: 3\. Function argument
|
||||
|
||||
|
||||
# Examples
|
||||
:::{.example}
|
||||
## `lib.lists.listDfs` usage example
|
||||
|
@ -1159,22 +1146,32 @@ rec {
|
|||
|
||||
:::
|
||||
*/
|
||||
listDfs = stopOnCycles: before: list:
|
||||
listDfs =
|
||||
stopOnCycles: before: list:
|
||||
let
|
||||
dfs' = us: visited: rest:
|
||||
dfs' =
|
||||
us: visited: rest:
|
||||
let
|
||||
c = filter (x: before x us) visited;
|
||||
b = partition (x: before x us) rest;
|
||||
in if stopOnCycles && (length c > 0)
|
||||
then { cycle = us; loops = c; inherit visited rest; }
|
||||
else if length b.right == 0
|
||||
then # nothing is before us
|
||||
{ minimal = us; inherit visited rest; }
|
||||
else # grab the first one before us and continue
|
||||
dfs' (head b.right)
|
||||
([ us ] ++ visited)
|
||||
(tail b.right ++ b.wrong);
|
||||
in dfs' (head list) [] (tail list);
|
||||
in
|
||||
if stopOnCycles && (length c > 0) then
|
||||
{
|
||||
cycle = us;
|
||||
loops = c;
|
||||
inherit visited rest;
|
||||
}
|
||||
else if length b.right == 0 then
|
||||
# nothing is before us
|
||||
{
|
||||
minimal = us;
|
||||
inherit visited rest;
|
||||
}
|
||||
else
|
||||
# grab the first one before us and continue
|
||||
dfs' (head b.right) ([ us ] ++ visited) (tail b.right ++ b.wrong);
|
||||
in
|
||||
dfs' (head list) [ ] (tail list);
|
||||
|
||||
/**
|
||||
Sort a list based on a partial ordering using DFS. This
|
||||
|
@ -1184,7 +1181,6 @@ rec {
|
|||
`before a b == true` means that `b` should be after `a`
|
||||
in the result.
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`before`
|
||||
|
@ -1195,7 +1191,6 @@ rec {
|
|||
|
||||
: 2\. Function argument
|
||||
|
||||
|
||||
# Examples
|
||||
:::{.example}
|
||||
## `lib.lists.toposort` usage example
|
||||
|
@ -1216,24 +1211,28 @@ rec {
|
|||
|
||||
:::
|
||||
*/
|
||||
toposort = before: list:
|
||||
toposort =
|
||||
before: list:
|
||||
let
|
||||
dfsthis = listDfs true before list;
|
||||
toporest = toposort before (dfsthis.visited ++ dfsthis.rest);
|
||||
in
|
||||
if length list < 2
|
||||
then # finish
|
||||
{ result = list; }
|
||||
else if dfsthis ? cycle
|
||||
then # there's a cycle, starting from the current vertex, return it
|
||||
{ cycle = reverseList ([ dfsthis.cycle ] ++ dfsthis.visited);
|
||||
inherit (dfsthis) loops; }
|
||||
else if toporest ? cycle
|
||||
then # there's a cycle somewhere else in the graph, return it
|
||||
toporest
|
||||
# Slow, but short. Can be made a bit faster with an explicit stack.
|
||||
else # there are no cycles
|
||||
{ result = [ dfsthis.minimal ] ++ toporest.result; };
|
||||
if length list < 2 then
|
||||
# finish
|
||||
{ result = list; }
|
||||
else if dfsthis ? cycle then
|
||||
# there's a cycle, starting from the current vertex, return it
|
||||
{
|
||||
cycle = reverseList ([ dfsthis.cycle ] ++ dfsthis.visited);
|
||||
inherit (dfsthis) loops;
|
||||
}
|
||||
else if toporest ? cycle then
|
||||
# there's a cycle somewhere else in the graph, return it
|
||||
toporest
|
||||
# Slow, but short. Can be made a bit faster with an explicit stack.
|
||||
else
|
||||
# there are no cycles
|
||||
{ result = [ dfsthis.minimal ] ++ toporest.result; };
|
||||
|
||||
/**
|
||||
Sort a list based on a comparator function which compares two
|
||||
|
@ -1289,7 +1288,6 @@ rec {
|
|||
sortOn f == sort (p: q: f p < f q)
|
||||
```
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`f`
|
||||
|
@ -1317,18 +1315,22 @@ rec {
|
|||
|
||||
:::
|
||||
*/
|
||||
sortOn = f: list:
|
||||
sortOn =
|
||||
f: list:
|
||||
let
|
||||
# Heterogenous list as pair may be ugly, but requires minimal allocations.
|
||||
pairs = map (x: [(f x) x]) list;
|
||||
pairs = map (x: [
|
||||
(f x)
|
||||
x
|
||||
]) list;
|
||||
in
|
||||
map
|
||||
(x: builtins.elemAt x 1)
|
||||
(sort
|
||||
# Compare the first element of the pairs
|
||||
# Do not factor out the `<`, to avoid calls in hot code; duplicate instead.
|
||||
(a: b: head a < head b)
|
||||
pairs);
|
||||
map (x: builtins.elemAt x 1) (
|
||||
sort
|
||||
# Compare the first element of the pairs
|
||||
# Do not factor out the `<`, to avoid calls in hot code; duplicate instead.
|
||||
(a: b: head a < head b)
|
||||
pairs
|
||||
);
|
||||
|
||||
/**
|
||||
Compare two lists element-by-element.
|
||||
|
@ -1347,7 +1349,6 @@ rec {
|
|||
|
||||
: 3\. Function argument
|
||||
|
||||
|
||||
# Examples
|
||||
:::{.example}
|
||||
## `lib.lists.compareLists` usage example
|
||||
|
@ -1365,30 +1366,28 @@ rec {
|
|||
|
||||
:::
|
||||
*/
|
||||
compareLists = cmp: a: b:
|
||||
if a == []
|
||||
then if b == []
|
||||
then 0
|
||||
else -1
|
||||
else if b == []
|
||||
then 1
|
||||
else let rel = cmp (head a) (head b); in
|
||||
if rel == 0
|
||||
then compareLists cmp (tail a) (tail b)
|
||||
else rel;
|
||||
compareLists =
|
||||
cmp: a: b:
|
||||
if a == [ ] then
|
||||
if b == [ ] then 0 else -1
|
||||
else if b == [ ] then
|
||||
1
|
||||
else
|
||||
let
|
||||
rel = cmp (head a) (head b);
|
||||
in
|
||||
if rel == 0 then compareLists cmp (tail a) (tail b) else rel;
|
||||
|
||||
/**
|
||||
Sort list using "Natural sorting".
|
||||
Numeric portions of strings are sorted in numeric order.
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`lst`
|
||||
|
||||
: 1\. Function argument
|
||||
|
||||
|
||||
# Examples
|
||||
:::{.example}
|
||||
## `lib.lists.naturalSort` usage example
|
||||
|
@ -1404,18 +1403,21 @@ rec {
|
|||
|
||||
:::
|
||||
*/
|
||||
naturalSort = lst:
|
||||
naturalSort =
|
||||
lst:
|
||||
let
|
||||
vectorise = s: map (x: if isList x then toInt (head x) else x) (builtins.split "(0|[1-9][0-9]*)" s);
|
||||
prepared = map (x: [ (vectorise x) x ]) lst; # remember vectorised version for O(n) regex splits
|
||||
prepared = map (x: [
|
||||
(vectorise x)
|
||||
x
|
||||
]) lst; # remember vectorised version for O(n) regex splits
|
||||
less = a: b: (compareLists compare (head a) (head b)) < 0;
|
||||
in
|
||||
map (x: elemAt x 1) (sort less prepared);
|
||||
map (x: elemAt x 1) (sort less prepared);
|
||||
|
||||
/**
|
||||
Return the first (at most) N elements of a list.
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`count`
|
||||
|
@ -1445,13 +1447,11 @@ rec {
|
|||
|
||||
:::
|
||||
*/
|
||||
take =
|
||||
count: sublist 0 count;
|
||||
take = count: sublist 0 count;
|
||||
|
||||
/**
|
||||
Remove the first (at most) N elements of a list.
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`count`
|
||||
|
@ -1481,14 +1481,11 @@ rec {
|
|||
|
||||
:::
|
||||
*/
|
||||
drop =
|
||||
count:
|
||||
list: sublist count (length list) list;
|
||||
drop = count: list: sublist count (length list) list;
|
||||
|
||||
/**
|
||||
Remove the last (at most) N elements of a list.
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`count`
|
||||
|
@ -1517,18 +1514,12 @@ rec {
|
|||
=> [ ]
|
||||
```
|
||||
:::
|
||||
|
||||
*/
|
||||
dropEnd =
|
||||
n: xs:
|
||||
take
|
||||
(max 0 (length xs - n))
|
||||
xs;
|
||||
*/
|
||||
dropEnd = n: xs: take (max 0 (length xs - n)) xs;
|
||||
|
||||
/**
|
||||
Whether the first list is a prefix of the second list.
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`list1`
|
||||
|
@ -1558,10 +1549,7 @@ rec {
|
|||
|
||||
:::
|
||||
*/
|
||||
hasPrefix =
|
||||
list1:
|
||||
list2:
|
||||
take (length list1) list2 == list1;
|
||||
hasPrefix = list1: list2: take (length list1) list2 == list1;
|
||||
|
||||
/**
|
||||
Remove the first list as a prefix from the second list.
|
||||
|
@ -1597,8 +1585,7 @@ rec {
|
|||
:::
|
||||
*/
|
||||
removePrefix =
|
||||
list1:
|
||||
list2:
|
||||
list1: list2:
|
||||
if hasPrefix list1 list2 then
|
||||
drop (length list1) list2
|
||||
else
|
||||
|
@ -1642,20 +1629,22 @@ rec {
|
|||
:::
|
||||
*/
|
||||
sublist =
|
||||
start:
|
||||
count:
|
||||
list:
|
||||
let len = length list; in
|
||||
genList
|
||||
(n: elemAt list (n + start))
|
||||
(if start >= len then 0
|
||||
else if start + count > len then len - start
|
||||
else count);
|
||||
start: count: list:
|
||||
let
|
||||
len = length list;
|
||||
in
|
||||
genList (n: elemAt list (n + start)) (
|
||||
if start >= len then
|
||||
0
|
||||
else if start + count > len then
|
||||
len - start
|
||||
else
|
||||
count
|
||||
);
|
||||
|
||||
/**
|
||||
The common prefix of two lists.
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`list1`
|
||||
|
@ -1688,8 +1677,7 @@ rec {
|
|||
:::
|
||||
*/
|
||||
commonPrefix =
|
||||
list1:
|
||||
list2:
|
||||
list1: list2:
|
||||
let
|
||||
# Zip the lists together into a list of booleans whether each element matches
|
||||
matchings = zipListsWith (fst: snd: fst != snd) list1 list2;
|
||||
|
@ -1706,7 +1694,6 @@ rec {
|
|||
|
||||
This function throws an error if the list is empty.
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`list`
|
||||
|
@ -1730,8 +1717,9 @@ rec {
|
|||
|
||||
:::
|
||||
*/
|
||||
last = list:
|
||||
assert lib.assertMsg (list != []) "lists.last: list must not be empty!";
|
||||
last =
|
||||
list:
|
||||
assert lib.assertMsg (list != [ ]) "lists.last: list must not be empty!";
|
||||
elemAt list (length list - 1);
|
||||
|
||||
/**
|
||||
|
@ -1739,7 +1727,6 @@ rec {
|
|||
|
||||
This function throws an error if the list is empty.
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`list`
|
||||
|
@ -1763,15 +1750,14 @@ rec {
|
|||
|
||||
:::
|
||||
*/
|
||||
init = list:
|
||||
assert lib.assertMsg (list != []) "lists.init: list must not be empty!";
|
||||
init =
|
||||
list:
|
||||
assert lib.assertMsg (list != [ ]) "lists.init: list must not be empty!";
|
||||
take (length list - 1) list;
|
||||
|
||||
|
||||
/**
|
||||
Return the image of the cross product of some lists by a function.
|
||||
|
||||
|
||||
# Examples
|
||||
:::{.example}
|
||||
## `lib.lists.crossLists` usage example
|
||||
|
@ -1801,13 +1787,11 @@ rec {
|
|||
|
||||
nix-repl> lib.mapCartesianProduct ({x,y}: x+y) { x = [1 2]; y = [3 4]; }
|
||||
[ 4 5 5 6 ]
|
||||
''
|
||||
(f: foldl (fs: args: concatMap (f: map f args) fs) [f]);
|
||||
'' (f: foldl (fs: args: concatMap (f: map f args) fs) [ f ]);
|
||||
|
||||
/**
|
||||
Remove duplicate elements from the `list`. O(n^2) complexity.
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`list`
|
||||
|
@ -1831,12 +1815,11 @@ rec {
|
|||
|
||||
:::
|
||||
*/
|
||||
unique = foldl' (acc: e: if elem e acc then acc else acc ++ [ e ]) [];
|
||||
unique = foldl' (acc: e: if elem e acc then acc else acc ++ [ e ]) [ ];
|
||||
|
||||
/**
|
||||
Check if list contains only unique elements. O(n^2) complexity.
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`list`
|
||||
|
@ -1864,7 +1847,6 @@ rec {
|
|||
*/
|
||||
allUnique = list: (length (unique list) == length list);
|
||||
|
||||
|
||||
/**
|
||||
Intersects list 'list1' and another list (`list2`).
|
||||
|
||||
|
@ -1880,7 +1862,6 @@ rec {
|
|||
|
||||
: Second list
|
||||
|
||||
|
||||
# Examples
|
||||
:::{.example}
|
||||
## `lib.lists.intersectLists` usage example
|
||||
|
@ -1909,7 +1890,6 @@ rec {
|
|||
|
||||
: Second list
|
||||
|
||||
|
||||
# Examples
|
||||
:::{.example}
|
||||
## `lib.lists.subtractLists` usage example
|
||||
|
|
138
lib/meta.nix
138
lib/meta.nix
|
@ -6,14 +6,20 @@
|
|||
{ lib }:
|
||||
|
||||
let
|
||||
inherit (lib) matchAttrs any all isDerivation getBin assertMsg;
|
||||
inherit (lib)
|
||||
matchAttrs
|
||||
any
|
||||
all
|
||||
isDerivation
|
||||
getBin
|
||||
assertMsg
|
||||
;
|
||||
inherit (lib.attrsets) mapAttrs' filterAttrs;
|
||||
inherit (builtins) isString match typeOf;
|
||||
|
||||
in
|
||||
rec {
|
||||
|
||||
|
||||
/**
|
||||
Add to or override the meta attributes of the given
|
||||
derivation.
|
||||
|
@ -28,7 +34,6 @@ rec {
|
|||
|
||||
: 2\. Function argument
|
||||
|
||||
|
||||
# Examples
|
||||
:::{.example}
|
||||
## `lib.meta.addMetaAttrs` usage example
|
||||
|
@ -39,9 +44,7 @@ rec {
|
|||
|
||||
:::
|
||||
*/
|
||||
addMetaAttrs = newAttrs: drv:
|
||||
drv // { meta = (drv.meta or {}) // newAttrs; };
|
||||
|
||||
addMetaAttrs = newAttrs: drv: drv // { meta = (drv.meta or { }) // newAttrs; };
|
||||
|
||||
/**
|
||||
Disable Hydra builds of given derivation.
|
||||
|
@ -52,8 +55,7 @@ rec {
|
|||
|
||||
: 1\. Function argument
|
||||
*/
|
||||
dontDistribute = drv: addMetaAttrs { hydraPlatforms = []; } drv;
|
||||
|
||||
dontDistribute = drv: addMetaAttrs { hydraPlatforms = [ ]; } drv;
|
||||
|
||||
/**
|
||||
Change the [symbolic name of a derivation](https://nixos.org/manual/nix/stable/language/derivations.html#attr-name).
|
||||
|
@ -72,8 +74,7 @@ rec {
|
|||
|
||||
: 2\. Function argument
|
||||
*/
|
||||
setName = name: drv: drv // {inherit name;};
|
||||
|
||||
setName = name: drv: drv // { inherit name; };
|
||||
|
||||
/**
|
||||
Like `setName`, but takes the previous name as an argument.
|
||||
|
@ -88,7 +89,6 @@ rec {
|
|||
|
||||
: 2\. Function argument
|
||||
|
||||
|
||||
# Examples
|
||||
:::{.example}
|
||||
## `lib.meta.updateName` usage example
|
||||
|
@ -99,8 +99,7 @@ rec {
|
|||
|
||||
:::
|
||||
*/
|
||||
updateName = updater: drv: drv // {name = updater (drv.name);};
|
||||
|
||||
updateName = updater: drv: drv // { name = updater (drv.name); };
|
||||
|
||||
/**
|
||||
Append a suffix to the name of a package (before the version
|
||||
|
@ -112,14 +111,19 @@ rec {
|
|||
|
||||
: 1\. Function argument
|
||||
*/
|
||||
appendToName = suffix: updateName (name:
|
||||
let x = builtins.parseDrvName name; in "${x.name}-${suffix}-${x.version}");
|
||||
|
||||
appendToName =
|
||||
suffix:
|
||||
updateName (
|
||||
name:
|
||||
let
|
||||
x = builtins.parseDrvName name;
|
||||
in
|
||||
"${x.name}-${suffix}-${x.version}"
|
||||
);
|
||||
|
||||
/**
|
||||
Apply a function to each derivation and only to derivations in an attrset.
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`f`
|
||||
|
@ -130,11 +134,12 @@ rec {
|
|||
|
||||
: 2\. Function argument
|
||||
*/
|
||||
mapDerivationAttrset = f: set: lib.mapAttrs (name: pkg: if lib.isDerivation pkg then (f pkg) else pkg) set;
|
||||
mapDerivationAttrset =
|
||||
f: set: lib.mapAttrs (name: pkg: if lib.isDerivation pkg then (f pkg) else pkg) set;
|
||||
|
||||
/**
|
||||
The default priority of packages in Nix. See `defaultPriority` in [`src/nix/profile.cc`](https://github.com/NixOS/nix/blob/master/src/nix/profile.cc#L47).
|
||||
*/
|
||||
*/
|
||||
defaultPriority = 5;
|
||||
|
||||
/**
|
||||
|
@ -159,7 +164,6 @@ rec {
|
|||
`drv`
|
||||
|
||||
: 1\. Function argument
|
||||
|
||||
*/
|
||||
lowPrio = setPrio 10;
|
||||
|
||||
|
@ -174,7 +178,6 @@ rec {
|
|||
*/
|
||||
lowPrioSet = set: mapDerivationAttrset lowPrio set;
|
||||
|
||||
|
||||
/**
|
||||
Increase the nix-env priority of the package, i.e., this
|
||||
version/variant of the package will be preferred.
|
||||
|
@ -198,7 +201,6 @@ rec {
|
|||
*/
|
||||
hiPrioSet = set: mapDerivationAttrset hiPrio set;
|
||||
|
||||
|
||||
/**
|
||||
Check to see if a platform is matched by the given `meta.platforms`
|
||||
element.
|
||||
|
@ -214,7 +216,6 @@ rec {
|
|||
We can inject these into a pattern for the whole of a structured platform,
|
||||
and then match that.
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`platform`
|
||||
|
@ -225,7 +226,6 @@ rec {
|
|||
|
||||
: 2\. Function argument
|
||||
|
||||
|
||||
# Examples
|
||||
:::{.example}
|
||||
## `lib.meta.platformMatch` usage example
|
||||
|
@ -237,21 +237,24 @@ rec {
|
|||
|
||||
:::
|
||||
*/
|
||||
platformMatch = platform: elem: (
|
||||
# Check with simple string comparison if elem was a string.
|
||||
#
|
||||
# The majority of comparisons done with this function will be against meta.platforms
|
||||
# which contains a simple platform string.
|
||||
#
|
||||
# Avoiding an attrset allocation results in significant performance gains (~2-30) across the board in OfBorg
|
||||
# because this is a hot path for nixpkgs.
|
||||
if isString elem then platform ? system && elem == platform.system
|
||||
else matchAttrs (
|
||||
# Normalize platform attrset.
|
||||
if elem ? parsed then elem
|
||||
else { parsed = elem; }
|
||||
) platform
|
||||
);
|
||||
platformMatch =
|
||||
platform: elem:
|
||||
(
|
||||
# Check with simple string comparison if elem was a string.
|
||||
#
|
||||
# The majority of comparisons done with this function will be against meta.platforms
|
||||
# which contains a simple platform string.
|
||||
#
|
||||
# Avoiding an attrset allocation results in significant performance gains (~2-30) across the board in OfBorg
|
||||
# because this is a hot path for nixpkgs.
|
||||
if isString elem then
|
||||
platform ? system && elem == platform.system
|
||||
else
|
||||
matchAttrs (
|
||||
# Normalize platform attrset.
|
||||
if elem ? parsed then elem else { parsed = elem; }
|
||||
) platform
|
||||
);
|
||||
|
||||
/**
|
||||
Check if a package is available on a given platform.
|
||||
|
@ -263,7 +266,6 @@ rec {
|
|||
|
||||
2. None of `meta.badPlatforms` pattern matches the given platform.
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`platform`
|
||||
|
@ -274,7 +276,6 @@ rec {
|
|||
|
||||
: 2\. Function argument
|
||||
|
||||
|
||||
# Examples
|
||||
:::{.example}
|
||||
## `lib.meta.availableOn` usage example
|
||||
|
@ -286,9 +287,10 @@ rec {
|
|||
|
||||
:::
|
||||
*/
|
||||
availableOn = platform: pkg:
|
||||
((!pkg?meta.platforms) || any (platformMatch platform) pkg.meta.platforms) &&
|
||||
all (elem: !platformMatch platform elem) (pkg.meta.badPlatforms or []);
|
||||
availableOn =
|
||||
platform: pkg:
|
||||
((!pkg ? meta.platforms) || any (platformMatch platform) pkg.meta.platforms)
|
||||
&& all (elem: !platformMatch platform elem) (pkg.meta.badPlatforms or [ ]);
|
||||
|
||||
/**
|
||||
Mapping of SPDX ID to the attributes in lib.licenses.
|
||||
|
@ -309,13 +311,10 @@ rec {
|
|||
|
||||
:::
|
||||
*/
|
||||
licensesSpdx =
|
||||
mapAttrs'
|
||||
(_key: license: {
|
||||
name = license.spdxId;
|
||||
value = license;
|
||||
})
|
||||
(filterAttrs (_key: license: license ? spdxId) lib.licenses);
|
||||
licensesSpdx = mapAttrs' (_key: license: {
|
||||
name = license.spdxId;
|
||||
value = license;
|
||||
}) (filterAttrs (_key: license: license ? spdxId) lib.licenses);
|
||||
|
||||
/**
|
||||
Get the corresponding attribute in lib.licenses from the SPDX ID
|
||||
|
@ -348,10 +347,11 @@ rec {
|
|||
*/
|
||||
getLicenseFromSpdxId =
|
||||
licstr:
|
||||
getLicenseFromSpdxIdOr licstr (
|
||||
lib.warn "getLicenseFromSpdxId: No license matches the given SPDX ID: ${licstr}"
|
||||
{ shortName = licstr; }
|
||||
);
|
||||
getLicenseFromSpdxIdOr licstr (
|
||||
lib.warn "getLicenseFromSpdxId: No license matches the given SPDX ID: ${licstr}" {
|
||||
shortName = licstr;
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
Get the corresponding attribute in lib.licenses from the SPDX ID
|
||||
|
@ -398,13 +398,12 @@ rec {
|
|||
name = lib.toLower name;
|
||||
inherit value;
|
||||
}) licensesSpdx;
|
||||
in licstr: default:
|
||||
lowercaseLicenses.${ lib.toLower licstr } or default;
|
||||
in
|
||||
licstr: default: lowercaseLicenses.${lib.toLower licstr} or default;
|
||||
|
||||
/**
|
||||
Get the path to the main program of a package based on meta.mainProgram
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`x`
|
||||
|
@ -430,17 +429,23 @@ rec {
|
|||
|
||||
:::
|
||||
*/
|
||||
getExe = x: getExe' x (x.meta.mainProgram or (
|
||||
# This could be turned into an error when 23.05 is at end of life
|
||||
lib.warn "getExe: Package ${lib.strings.escapeNixIdentifier x.meta.name or x.pname or x.name} does not have the meta.mainProgram attribute. We'll assume that the main program has the same name for now, but this behavior is deprecated, because it leads to surprising errors when the assumption does not hold. If the package has a main program, please set `meta.mainProgram` in its definition to make this warning go away. Otherwise, if the package does not have a main program, or if you don't control its definition, use getExe' to specify the name to the program, such as lib.getExe' foo \"bar\"."
|
||||
lib.getName
|
||||
x
|
||||
));
|
||||
getExe =
|
||||
x:
|
||||
getExe' x (
|
||||
x.meta.mainProgram or (
|
||||
# This could be turned into an error when 23.05 is at end of life
|
||||
lib.warn
|
||||
"getExe: Package ${
|
||||
lib.strings.escapeNixIdentifier x.meta.name or x.pname or x.name
|
||||
} does not have the meta.mainProgram attribute. We'll assume that the main program has the same name for now, but this behavior is deprecated, because it leads to surprising errors when the assumption does not hold. If the package has a main program, please set `meta.mainProgram` in its definition to make this warning go away. Otherwise, if the package does not have a main program, or if you don't control its definition, use getExe' to specify the name to the program, such as lib.getExe' foo \"bar\"."
|
||||
lib.getName
|
||||
x
|
||||
)
|
||||
);
|
||||
|
||||
/**
|
||||
Get the path of a program of a derivation.
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`x`
|
||||
|
@ -470,7 +475,8 @@ rec {
|
|||
|
||||
:::
|
||||
*/
|
||||
getExe' = x: y:
|
||||
getExe' =
|
||||
x: y:
|
||||
assert assertMsg (isDerivation x)
|
||||
"lib.meta.getExe': The first argument is of type ${typeOf x}, but it should be a derivation instead.";
|
||||
assert assertMsg (isString y)
|
||||
|
|
1910
lib/modules.nix
1910
lib/modules.nix
File diff suppressed because it is too large
Load diff
708
lib/options.nix
708
lib/options.nix
|
@ -1,4 +1,4 @@
|
|||
/* Nixpkgs/NixOS option handling. */
|
||||
# Nixpkgs/NixOS option handling.
|
||||
{ lib }:
|
||||
|
||||
let
|
||||
|
@ -40,426 +40,520 @@ let
|
|||
last
|
||||
;
|
||||
prioritySuggestion = ''
|
||||
Use `lib.mkForce value` or `lib.mkDefault value` to change the priority on any of these definitions.
|
||||
Use `lib.mkForce value` or `lib.mkDefault value` to change the priority on any of these definitions.
|
||||
'';
|
||||
in
|
||||
rec {
|
||||
|
||||
/* Returns true when the given argument is an option
|
||||
/*
|
||||
Returns true when the given argument is an option
|
||||
|
||||
Type: isOption :: a -> bool
|
||||
Type: isOption :: a -> bool
|
||||
|
||||
Example:
|
||||
isOption 1 // => false
|
||||
isOption (mkOption {}) // => true
|
||||
Example:
|
||||
isOption 1 // => false
|
||||
isOption (mkOption {}) // => true
|
||||
*/
|
||||
isOption = lib.isType "option";
|
||||
|
||||
/* Creates an Option attribute set. mkOption accepts an attribute set with the following keys:
|
||||
/*
|
||||
Creates an Option attribute set. mkOption accepts an attribute set with the following keys:
|
||||
|
||||
All keys default to `null` when not given.
|
||||
All keys default to `null` when not given.
|
||||
|
||||
Example:
|
||||
mkOption { } // => { _type = "option"; }
|
||||
mkOption { default = "foo"; } // => { _type = "option"; default = "foo"; }
|
||||
Example:
|
||||
mkOption { } // => { _type = "option"; }
|
||||
mkOption { default = "foo"; } // => { _type = "option"; default = "foo"; }
|
||||
*/
|
||||
mkOption =
|
||||
{
|
||||
# Default value used when no definition is given in the configuration.
|
||||
default ? null,
|
||||
# Textual representation of the default, for the manual.
|
||||
defaultText ? null,
|
||||
# Example value used in the manual.
|
||||
example ? null,
|
||||
# String describing the option.
|
||||
description ? null,
|
||||
# Related packages used in the manual (see `genRelatedPackages` in ../nixos/lib/make-options-doc/default.nix).
|
||||
relatedPackages ? null,
|
||||
# Option type, providing type-checking and value merging.
|
||||
type ? null,
|
||||
# Function that converts the option value to something else.
|
||||
apply ? null,
|
||||
# Whether the option is for NixOS developers only.
|
||||
internal ? null,
|
||||
# Whether the option shows up in the manual. Default: true. Use false to hide the option and any sub-options from submodules. Use "shallow" to hide only sub-options.
|
||||
visible ? null,
|
||||
# Whether the option can be set only once
|
||||
readOnly ? null,
|
||||
} @ attrs:
|
||||
# Default value used when no definition is given in the configuration.
|
||||
default ? null,
|
||||
# Textual representation of the default, for the manual.
|
||||
defaultText ? null,
|
||||
# Example value used in the manual.
|
||||
example ? null,
|
||||
# String describing the option.
|
||||
description ? null,
|
||||
# Related packages used in the manual (see `genRelatedPackages` in ../nixos/lib/make-options-doc/default.nix).
|
||||
relatedPackages ? null,
|
||||
# Option type, providing type-checking and value merging.
|
||||
type ? null,
|
||||
# Function that converts the option value to something else.
|
||||
apply ? null,
|
||||
# Whether the option is for NixOS developers only.
|
||||
internal ? null,
|
||||
# Whether the option shows up in the manual. Default: true. Use false to hide the option and any sub-options from submodules. Use "shallow" to hide only sub-options.
|
||||
visible ? null,
|
||||
# Whether the option can be set only once
|
||||
readOnly ? null,
|
||||
}@attrs:
|
||||
attrs // { _type = "option"; };
|
||||
|
||||
/* Creates an Option attribute set for a boolean value option i.e an
|
||||
option to be toggled on or off:
|
||||
/*
|
||||
Creates an Option attribute set for a boolean value option i.e an
|
||||
option to be toggled on or off:
|
||||
|
||||
Example:
|
||||
mkEnableOption "foo"
|
||||
=> { _type = "option"; default = false; description = "Whether to enable foo."; example = true; type = { ... }; }
|
||||
Example:
|
||||
mkEnableOption "foo"
|
||||
=> { _type = "option"; default = false; description = "Whether to enable foo."; example = true; type = { ... }; }
|
||||
*/
|
||||
mkEnableOption =
|
||||
# Name for the created option
|
||||
name: mkOption {
|
||||
default = false;
|
||||
example = true;
|
||||
description = "Whether to enable ${name}.";
|
||||
type = lib.types.bool;
|
||||
};
|
||||
name:
|
||||
mkOption {
|
||||
default = false;
|
||||
example = true;
|
||||
description = "Whether to enable ${name}.";
|
||||
type = lib.types.bool;
|
||||
};
|
||||
|
||||
/* Creates an Option attribute set for an option that specifies the
|
||||
package a module should use for some purpose.
|
||||
/*
|
||||
Creates an Option attribute set for an option that specifies the
|
||||
package a module should use for some purpose.
|
||||
|
||||
The package is specified in the third argument under `default` as a list of strings
|
||||
representing its attribute path in nixpkgs (or another package set).
|
||||
Because of this, you need to pass nixpkgs itself (usually `pkgs` in a module;
|
||||
alternatively to nixpkgs itself, another package set) as the first argument.
|
||||
The package is specified in the third argument under `default` as a list of strings
|
||||
representing its attribute path in nixpkgs (or another package set).
|
||||
Because of this, you need to pass nixpkgs itself (usually `pkgs` in a module;
|
||||
alternatively to nixpkgs itself, another package set) as the first argument.
|
||||
|
||||
If you pass another package set you should set the `pkgsText` option.
|
||||
This option is used to display the expression for the package set. It is `"pkgs"` by default.
|
||||
If your expression is complex you should parenthesize it, as the `pkgsText` argument
|
||||
is usually immediately followed by an attribute lookup (`.`).
|
||||
If you pass another package set you should set the `pkgsText` option.
|
||||
This option is used to display the expression for the package set. It is `"pkgs"` by default.
|
||||
If your expression is complex you should parenthesize it, as the `pkgsText` argument
|
||||
is usually immediately followed by an attribute lookup (`.`).
|
||||
|
||||
The second argument may be either a string or a list of strings.
|
||||
It provides the display name of the package in the description of the generated option
|
||||
(using only the last element if the passed value is a list)
|
||||
and serves as the fallback value for the `default` argument.
|
||||
The second argument may be either a string or a list of strings.
|
||||
It provides the display name of the package in the description of the generated option
|
||||
(using only the last element if the passed value is a list)
|
||||
and serves as the fallback value for the `default` argument.
|
||||
|
||||
To include extra information in the description, pass `extraDescription` to
|
||||
append arbitrary text to the generated description.
|
||||
To include extra information in the description, pass `extraDescription` to
|
||||
append arbitrary text to the generated description.
|
||||
|
||||
You can also pass an `example` value, either a literal string or an attribute path.
|
||||
You can also pass an `example` value, either a literal string or an attribute path.
|
||||
|
||||
The `default` argument can be omitted if the provided name is
|
||||
an attribute of pkgs (if `name` is a string) or a valid attribute path in pkgs (if `name` is a list).
|
||||
You can also set `default` to just a string in which case it is interpreted as an attribute name
|
||||
(a singleton attribute path, if you will).
|
||||
The `default` argument can be omitted if the provided name is
|
||||
an attribute of pkgs (if `name` is a string) or a valid attribute path in pkgs (if `name` is a list).
|
||||
You can also set `default` to just a string in which case it is interpreted as an attribute name
|
||||
(a singleton attribute path, if you will).
|
||||
|
||||
If you wish to explicitly provide no default, pass `null` as `default`.
|
||||
If you wish to explicitly provide no default, pass `null` as `default`.
|
||||
|
||||
If you want users to be able to set no package, pass `nullable = true`.
|
||||
In this mode a `default = null` will not be interpreted as no default and is interpreted literally.
|
||||
If you want users to be able to set no package, pass `nullable = true`.
|
||||
In this mode a `default = null` will not be interpreted as no default and is interpreted literally.
|
||||
|
||||
Type: mkPackageOption :: pkgs -> (string|[string]) -> { nullable? :: bool, default? :: string|[string], example? :: null|string|[string], extraDescription? :: string, pkgsText? :: string } -> option
|
||||
Type: mkPackageOption :: pkgs -> (string|[string]) -> { nullable? :: bool, default? :: string|[string], example? :: null|string|[string], extraDescription? :: string, pkgsText? :: string } -> option
|
||||
|
||||
Example:
|
||||
mkPackageOption pkgs "hello" { }
|
||||
=> { ...; default = pkgs.hello; defaultText = literalExpression "pkgs.hello"; description = "The hello package to use."; type = package; }
|
||||
Example:
|
||||
mkPackageOption pkgs "hello" { }
|
||||
=> { ...; default = pkgs.hello; defaultText = literalExpression "pkgs.hello"; description = "The hello package to use."; type = package; }
|
||||
|
||||
Example:
|
||||
mkPackageOption pkgs "GHC" {
|
||||
default = [ "ghc" ];
|
||||
example = "pkgs.haskell.packages.ghc92.ghc.withPackages (hkgs: [ hkgs.primes ])";
|
||||
}
|
||||
=> { ...; default = pkgs.ghc; defaultText = literalExpression "pkgs.ghc"; description = "The GHC package to use."; example = literalExpression "pkgs.haskell.packages.ghc92.ghc.withPackages (hkgs: [ hkgs.primes ])"; type = package; }
|
||||
Example:
|
||||
mkPackageOption pkgs "GHC" {
|
||||
default = [ "ghc" ];
|
||||
example = "pkgs.haskell.packages.ghc92.ghc.withPackages (hkgs: [ hkgs.primes ])";
|
||||
}
|
||||
=> { ...; default = pkgs.ghc; defaultText = literalExpression "pkgs.ghc"; description = "The GHC package to use."; example = literalExpression "pkgs.haskell.packages.ghc92.ghc.withPackages (hkgs: [ hkgs.primes ])"; type = package; }
|
||||
|
||||
Example:
|
||||
mkPackageOption pkgs [ "python3Packages" "pytorch" ] {
|
||||
extraDescription = "This is an example and doesn't actually do anything.";
|
||||
}
|
||||
=> { ...; default = pkgs.python3Packages.pytorch; defaultText = literalExpression "pkgs.python3Packages.pytorch"; description = "The pytorch package to use. This is an example and doesn't actually do anything."; type = package; }
|
||||
Example:
|
||||
mkPackageOption pkgs [ "python3Packages" "pytorch" ] {
|
||||
extraDescription = "This is an example and doesn't actually do anything.";
|
||||
}
|
||||
=> { ...; default = pkgs.python3Packages.pytorch; defaultText = literalExpression "pkgs.python3Packages.pytorch"; description = "The pytorch package to use. This is an example and doesn't actually do anything."; type = package; }
|
||||
|
||||
Example:
|
||||
mkPackageOption pkgs "nushell" {
|
||||
nullable = true;
|
||||
}
|
||||
=> { ...; default = pkgs.nushell; defaultText = literalExpression "pkgs.nushell"; description = "The nushell package to use."; type = nullOr package; }
|
||||
Example:
|
||||
mkPackageOption pkgs "nushell" {
|
||||
nullable = true;
|
||||
}
|
||||
=> { ...; default = pkgs.nushell; defaultText = literalExpression "pkgs.nushell"; description = "The nushell package to use."; type = nullOr package; }
|
||||
|
||||
Example:
|
||||
mkPackageOption pkgs "coreutils" {
|
||||
default = null;
|
||||
}
|
||||
=> { ...; description = "The coreutils package to use."; type = package; }
|
||||
Example:
|
||||
mkPackageOption pkgs "coreutils" {
|
||||
default = null;
|
||||
}
|
||||
=> { ...; description = "The coreutils package to use."; type = package; }
|
||||
|
||||
Example:
|
||||
mkPackageOption pkgs "dbus" {
|
||||
nullable = true;
|
||||
default = null;
|
||||
}
|
||||
=> { ...; default = null; description = "The dbus package to use."; type = nullOr package; }
|
||||
Example:
|
||||
mkPackageOption pkgs "dbus" {
|
||||
nullable = true;
|
||||
default = null;
|
||||
}
|
||||
=> { ...; default = null; description = "The dbus package to use."; type = nullOr package; }
|
||||
|
||||
Example:
|
||||
mkPackageOption pkgs.javaPackages "OpenJFX" {
|
||||
default = "openjfx20";
|
||||
pkgsText = "pkgs.javaPackages";
|
||||
}
|
||||
=> { ...; default = pkgs.javaPackages.openjfx20; defaultText = literalExpression "pkgs.javaPackages.openjfx20"; description = "The OpenJFX package to use."; type = package; }
|
||||
Example:
|
||||
mkPackageOption pkgs.javaPackages "OpenJFX" {
|
||||
default = "openjfx20";
|
||||
pkgsText = "pkgs.javaPackages";
|
||||
}
|
||||
=> { ...; default = pkgs.javaPackages.openjfx20; defaultText = literalExpression "pkgs.javaPackages.openjfx20"; description = "The OpenJFX package to use."; type = package; }
|
||||
*/
|
||||
mkPackageOption =
|
||||
# Package set (an instantiation of nixpkgs such as pkgs in modules or another package set)
|
||||
pkgs:
|
||||
# Name for the package, shown in option description
|
||||
name:
|
||||
{
|
||||
# Whether the package can be null, for example to disable installing a package altogether (defaults to false)
|
||||
nullable ? false,
|
||||
# The attribute path where the default package is located (may be omitted, in which case it is copied from `name`)
|
||||
default ? name,
|
||||
# A string or an attribute path to use as an example (may be omitted)
|
||||
example ? null,
|
||||
# Additional text to include in the option description (may be omitted)
|
||||
extraDescription ? "",
|
||||
# Representation of the package set passed as pkgs (defaults to `"pkgs"`)
|
||||
pkgsText ? "pkgs"
|
||||
}:
|
||||
let
|
||||
name' = if isList name then last name else name;
|
||||
default' = if isList default then default else [ default ];
|
||||
defaultText = concatStringsSep "." default';
|
||||
defaultValue = attrByPath default'
|
||||
(throw "${defaultText} cannot be found in ${pkgsText}") pkgs;
|
||||
defaults = if default != null then {
|
||||
default = defaultValue;
|
||||
defaultText = literalExpression ("${pkgsText}." + defaultText);
|
||||
} else optionalAttrs nullable {
|
||||
default = null;
|
||||
};
|
||||
in mkOption (defaults // {
|
||||
description = "The ${name'} package to use."
|
||||
+ (if extraDescription == "" then "" else " ") + extraDescription;
|
||||
# Name for the package, shown in option description
|
||||
name:
|
||||
{
|
||||
# Whether the package can be null, for example to disable installing a package altogether (defaults to false)
|
||||
nullable ? false,
|
||||
# The attribute path where the default package is located (may be omitted, in which case it is copied from `name`)
|
||||
default ? name,
|
||||
# A string or an attribute path to use as an example (may be omitted)
|
||||
example ? null,
|
||||
# Additional text to include in the option description (may be omitted)
|
||||
extraDescription ? "",
|
||||
# Representation of the package set passed as pkgs (defaults to `"pkgs"`)
|
||||
pkgsText ? "pkgs",
|
||||
}:
|
||||
let
|
||||
name' = if isList name then last name else name;
|
||||
default' = if isList default then default else [ default ];
|
||||
defaultText = concatStringsSep "." default';
|
||||
defaultValue = attrByPath default' (throw "${defaultText} cannot be found in ${pkgsText}") pkgs;
|
||||
defaults =
|
||||
if default != null then
|
||||
{
|
||||
default = defaultValue;
|
||||
defaultText = literalExpression ("${pkgsText}." + defaultText);
|
||||
}
|
||||
else
|
||||
optionalAttrs nullable {
|
||||
default = null;
|
||||
};
|
||||
in
|
||||
mkOption (
|
||||
defaults
|
||||
// {
|
||||
description =
|
||||
"The ${name'} package to use." + (if extraDescription == "" then "" else " ") + extraDescription;
|
||||
type = with lib.types; (if nullable then nullOr else lib.id) package;
|
||||
} // optionalAttrs (example != null) {
|
||||
example = literalExpression
|
||||
(if isList example then "${pkgsText}." + concatStringsSep "." example else example);
|
||||
});
|
||||
}
|
||||
// optionalAttrs (example != null) {
|
||||
example = literalExpression (
|
||||
if isList example then "${pkgsText}." + concatStringsSep "." example else example
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
/* Deprecated alias of mkPackageOption, to be removed in 25.05.
|
||||
Previously used to create options with markdown documentation, which is no longer required.
|
||||
/*
|
||||
Deprecated alias of mkPackageOption, to be removed in 25.05.
|
||||
Previously used to create options with markdown documentation, which is no longer required.
|
||||
*/
|
||||
mkPackageOptionMD = lib.warn "mkPackageOptionMD is deprecated and will be removed in 25.05; please use mkPackageOption." mkPackageOption;
|
||||
|
||||
/* This option accepts anything, but it does not produce any result.
|
||||
/*
|
||||
This option accepts anything, but it does not produce any result.
|
||||
|
||||
This is useful for sharing a module across different module sets
|
||||
without having to implement similar features as long as the
|
||||
values of the options are not accessed. */
|
||||
mkSinkUndeclaredOptions = attrs: mkOption ({
|
||||
internal = true;
|
||||
visible = false;
|
||||
default = false;
|
||||
description = "Sink for option definitions.";
|
||||
type = mkOptionType {
|
||||
name = "sink";
|
||||
check = x: true;
|
||||
merge = loc: defs: false;
|
||||
};
|
||||
apply = x: throw "Option value is not readable because the option is not declared.";
|
||||
} // attrs);
|
||||
This is useful for sharing a module across different module sets
|
||||
without having to implement similar features as long as the
|
||||
values of the options are not accessed.
|
||||
*/
|
||||
mkSinkUndeclaredOptions =
|
||||
attrs:
|
||||
mkOption (
|
||||
{
|
||||
internal = true;
|
||||
visible = false;
|
||||
default = false;
|
||||
description = "Sink for option definitions.";
|
||||
type = mkOptionType {
|
||||
name = "sink";
|
||||
check = x: true;
|
||||
merge = loc: defs: false;
|
||||
};
|
||||
apply = x: throw "Option value is not readable because the option is not declared.";
|
||||
}
|
||||
// attrs
|
||||
);
|
||||
|
||||
mergeDefaultOption = loc: defs:
|
||||
let list = getValues defs; in
|
||||
if length list == 1 then head list
|
||||
else if all isFunction list then x: mergeDefaultOption loc (map (f: f x) list)
|
||||
else if all isList list then concatLists list
|
||||
else if all isAttrs list then foldl' lib.mergeAttrs {} list
|
||||
else if all isBool list then foldl' lib.or false list
|
||||
else if all isString list then lib.concatStrings list
|
||||
else if all isInt list && all (x: x == head list) list then head list
|
||||
else throw "Cannot merge definitions of `${showOption loc}'. Definition values:${showDefs defs}";
|
||||
mergeDefaultOption =
|
||||
loc: defs:
|
||||
let
|
||||
list = getValues defs;
|
||||
in
|
||||
if length list == 1 then
|
||||
head list
|
||||
else if all isFunction list then
|
||||
x: mergeDefaultOption loc (map (f: f x) list)
|
||||
else if all isList list then
|
||||
concatLists list
|
||||
else if all isAttrs list then
|
||||
foldl' lib.mergeAttrs { } list
|
||||
else if all isBool list then
|
||||
foldl' lib.or false list
|
||||
else if all isString list then
|
||||
lib.concatStrings list
|
||||
else if all isInt list && all (x: x == head list) list then
|
||||
head list
|
||||
else
|
||||
throw "Cannot merge definitions of `${showOption loc}'. Definition values:${showDefs defs}";
|
||||
|
||||
/*
|
||||
Require a single definition.
|
||||
|
||||
WARNING: Does not perform nested checks, as this does not run the merge function!
|
||||
*/
|
||||
*/
|
||||
mergeOneOption = mergeUniqueOption { message = ""; };
|
||||
|
||||
/*
|
||||
Require a single definition.
|
||||
|
||||
NOTE: When the type is not checked completely by check, pass a merge function for further checking (of sub-attributes, etc).
|
||||
*/
|
||||
mergeUniqueOption = args@{
|
||||
*/
|
||||
mergeUniqueOption =
|
||||
args@{
|
||||
message,
|
||||
# WARNING: the default merge function assumes that the definition is a valid (option) value. You MUST pass a merge function if the return value needs to be
|
||||
# - type checked beyond what .check does (which should be very litte; only on the value head; not attribute values, etc)
|
||||
# - if you want attribute values to be checked, or list items
|
||||
# - if you want coercedTo-like behavior to work
|
||||
merge ? loc: defs: (head defs).value }:
|
||||
merge ? loc: defs: (head defs).value,
|
||||
}:
|
||||
loc: defs:
|
||||
if length defs == 1
|
||||
then merge loc defs
|
||||
else
|
||||
assert length defs > 1;
|
||||
throw "The option `${showOption loc}' is defined multiple times while it's expected to be unique.\n${message}\nDefinition values:${showDefs defs}\n${prioritySuggestion}";
|
||||
if length defs == 1 then
|
||||
merge loc defs
|
||||
else
|
||||
assert length defs > 1;
|
||||
throw "The option `${showOption loc}' is defined multiple times while it's expected to be unique.\n${message}\nDefinition values:${showDefs defs}\n${prioritySuggestion}";
|
||||
|
||||
/* "Merge" option definitions by checking that they all have the same value. */
|
||||
mergeEqualOption = loc: defs:
|
||||
if defs == [] then abort "This case should never happen."
|
||||
# "Merge" option definitions by checking that they all have the same value.
|
||||
mergeEqualOption =
|
||||
loc: defs:
|
||||
if defs == [ ] then
|
||||
abort "This case should never happen."
|
||||
# Return early if we only have one element
|
||||
# This also makes it work for functions, because the foldl' below would try
|
||||
# to compare the first element with itself, which is false for functions
|
||||
else if length defs == 1 then (head defs).value
|
||||
else (foldl' (first: def:
|
||||
if def.value != first.value then
|
||||
throw "The option `${showOption loc}' has conflicting definition values:${showDefs [ first def ]}\n${prioritySuggestion}"
|
||||
else
|
||||
first) (head defs) (tail defs)).value;
|
||||
else if length defs == 1 then
|
||||
(head defs).value
|
||||
else
|
||||
(foldl' (
|
||||
first: def:
|
||||
if def.value != first.value then
|
||||
throw "The option `${showOption loc}' has conflicting definition values:${
|
||||
showDefs [
|
||||
first
|
||||
def
|
||||
]
|
||||
}\n${prioritySuggestion}"
|
||||
else
|
||||
first
|
||||
) (head defs) (tail defs)).value;
|
||||
|
||||
/* Extracts values of all "value" keys of the given list.
|
||||
/*
|
||||
Extracts values of all "value" keys of the given list.
|
||||
|
||||
Type: getValues :: [ { value :: a; } ] -> [a]
|
||||
Type: getValues :: [ { value :: a; } ] -> [a]
|
||||
|
||||
Example:
|
||||
getValues [ { value = 1; } { value = 2; } ] // => [ 1 2 ]
|
||||
getValues [ ] // => [ ]
|
||||
Example:
|
||||
getValues [ { value = 1; } { value = 2; } ] // => [ 1 2 ]
|
||||
getValues [ ] // => [ ]
|
||||
*/
|
||||
getValues = map (x: x.value);
|
||||
|
||||
/* Extracts values of all "file" keys of the given list
|
||||
/*
|
||||
Extracts values of all "file" keys of the given list
|
||||
|
||||
Type: getFiles :: [ { file :: a; } ] -> [a]
|
||||
Type: getFiles :: [ { file :: a; } ] -> [a]
|
||||
|
||||
Example:
|
||||
getFiles [ { file = "file1"; } { file = "file2"; } ] // => [ "file1" "file2" ]
|
||||
getFiles [ ] // => [ ]
|
||||
Example:
|
||||
getFiles [ { file = "file1"; } { file = "file2"; } ] // => [ "file1" "file2" ]
|
||||
getFiles [ ] // => [ ]
|
||||
*/
|
||||
getFiles = map (x: x.file);
|
||||
|
||||
# Generate documentation template from the list of option declaration like
|
||||
# the set generated with filterOptionSets.
|
||||
optionAttrSetToDocList = optionAttrSetToDocList' [];
|
||||
optionAttrSetToDocList = optionAttrSetToDocList' [ ];
|
||||
|
||||
optionAttrSetToDocList' = _: options:
|
||||
concatMap (opt:
|
||||
optionAttrSetToDocList' =
|
||||
_: options:
|
||||
concatMap (
|
||||
opt:
|
||||
let
|
||||
name = showOption opt.loc;
|
||||
docOption = {
|
||||
loc = opt.loc;
|
||||
inherit name;
|
||||
description = opt.description or null;
|
||||
declarations = filter (x: x != unknownModule) opt.declarations;
|
||||
internal = opt.internal or false;
|
||||
visible =
|
||||
if (opt?visible && opt.visible == "shallow")
|
||||
then true
|
||||
else opt.visible or true;
|
||||
readOnly = opt.readOnly or false;
|
||||
type = opt.type.description or "unspecified";
|
||||
}
|
||||
// optionalAttrs (opt ? example) {
|
||||
example =
|
||||
builtins.addErrorContext "while evaluating the example of option `${name}`" (
|
||||
docOption =
|
||||
{
|
||||
loc = opt.loc;
|
||||
inherit name;
|
||||
description = opt.description or null;
|
||||
declarations = filter (x: x != unknownModule) opt.declarations;
|
||||
internal = opt.internal or false;
|
||||
visible = if (opt ? visible && opt.visible == "shallow") then true else opt.visible or true;
|
||||
readOnly = opt.readOnly or false;
|
||||
type = opt.type.description or "unspecified";
|
||||
}
|
||||
// optionalAttrs (opt ? example) {
|
||||
example = builtins.addErrorContext "while evaluating the example of option `${name}`" (
|
||||
renderOptionValue opt.example
|
||||
);
|
||||
}
|
||||
// optionalAttrs (opt ? defaultText || opt ? default) {
|
||||
default =
|
||||
builtins.addErrorContext "while evaluating the ${if opt?defaultText then "defaultText" else "default value"} of option `${name}`" (
|
||||
renderOptionValue (opt.defaultText or opt.default)
|
||||
);
|
||||
}
|
||||
// optionalAttrs (opt ? relatedPackages && opt.relatedPackages != null) { inherit (opt) relatedPackages; };
|
||||
}
|
||||
// optionalAttrs (opt ? defaultText || opt ? default) {
|
||||
default = builtins.addErrorContext "while evaluating the ${
|
||||
if opt ? defaultText then "defaultText" else "default value"
|
||||
} of option `${name}`" (renderOptionValue (opt.defaultText or opt.default));
|
||||
}
|
||||
// optionalAttrs (opt ? relatedPackages && opt.relatedPackages != null) {
|
||||
inherit (opt) relatedPackages;
|
||||
};
|
||||
|
||||
subOptions =
|
||||
let ss = opt.type.getSubOptions opt.loc;
|
||||
in if ss != {} then optionAttrSetToDocList' opt.loc ss else [];
|
||||
let
|
||||
ss = opt.type.getSubOptions opt.loc;
|
||||
in
|
||||
if ss != { } then optionAttrSetToDocList' opt.loc ss else [ ];
|
||||
subOptionsVisible = docOption.visible && opt.visible or null != "shallow";
|
||||
in
|
||||
# To find infinite recursion in NixOS option docs:
|
||||
# builtins.trace opt.loc
|
||||
[ docOption ] ++ optionals subOptionsVisible subOptions) (collect isOption options);
|
||||
# To find infinite recursion in NixOS option docs:
|
||||
# builtins.trace opt.loc
|
||||
[ docOption ] ++ optionals subOptionsVisible subOptions
|
||||
) (collect isOption options);
|
||||
|
||||
/*
|
||||
This function recursively removes all derivation attributes from
|
||||
`x` except for the `name` attribute.
|
||||
|
||||
/* This function recursively removes all derivation attributes from
|
||||
`x` except for the `name` attribute.
|
||||
This is to make the generation of `options.xml` much more
|
||||
efficient: the XML representation of derivations is very large
|
||||
(on the order of megabytes) and is not actually used by the
|
||||
manual generator.
|
||||
|
||||
This is to make the generation of `options.xml` much more
|
||||
efficient: the XML representation of derivations is very large
|
||||
(on the order of megabytes) and is not actually used by the
|
||||
manual generator.
|
||||
|
||||
This function was made obsolete by renderOptionValue and is kept for
|
||||
compatibility with out-of-tree code.
|
||||
This function was made obsolete by renderOptionValue and is kept for
|
||||
compatibility with out-of-tree code.
|
||||
*/
|
||||
scrubOptionValue = x:
|
||||
scrubOptionValue =
|
||||
x:
|
||||
if isDerivation x then
|
||||
{ type = "derivation"; drvPath = x.name; outPath = x.name; name = x.name; }
|
||||
else if isList x then map scrubOptionValue x
|
||||
else if isAttrs x then mapAttrs (n: v: scrubOptionValue v) (removeAttrs x ["_args"])
|
||||
else x;
|
||||
{
|
||||
type = "derivation";
|
||||
drvPath = x.name;
|
||||
outPath = x.name;
|
||||
name = x.name;
|
||||
}
|
||||
else if isList x then
|
||||
map scrubOptionValue x
|
||||
else if isAttrs x then
|
||||
mapAttrs (n: v: scrubOptionValue v) (removeAttrs x [ "_args" ])
|
||||
else
|
||||
x;
|
||||
|
||||
|
||||
/* Ensures that the given option value (default or example) is a `_type`d string
|
||||
by rendering Nix values to `literalExpression`s.
|
||||
/*
|
||||
Ensures that the given option value (default or example) is a `_type`d string
|
||||
by rendering Nix values to `literalExpression`s.
|
||||
*/
|
||||
renderOptionValue = v:
|
||||
if v ? _type && v ? text then v
|
||||
else literalExpression (lib.generators.toPretty {
|
||||
multiline = true;
|
||||
allowPrettyValues = true;
|
||||
} v);
|
||||
renderOptionValue =
|
||||
v:
|
||||
if v ? _type && v ? text then
|
||||
v
|
||||
else
|
||||
literalExpression (
|
||||
lib.generators.toPretty {
|
||||
multiline = true;
|
||||
allowPrettyValues = true;
|
||||
} v
|
||||
);
|
||||
|
||||
|
||||
/* For use in the `defaultText` and `example` option attributes. Causes the
|
||||
given string to be rendered verbatim in the documentation as Nix code. This
|
||||
is necessary for complex values, e.g. functions, or values that depend on
|
||||
other values or packages.
|
||||
/*
|
||||
For use in the `defaultText` and `example` option attributes. Causes the
|
||||
given string to be rendered verbatim in the documentation as Nix code. This
|
||||
is necessary for complex values, e.g. functions, or values that depend on
|
||||
other values or packages.
|
||||
*/
|
||||
literalExpression = text:
|
||||
if ! isString text then throw "literalExpression expects a string."
|
||||
else { _type = "literalExpression"; inherit text; };
|
||||
literalExpression =
|
||||
text:
|
||||
if !isString text then
|
||||
throw "literalExpression expects a string."
|
||||
else
|
||||
{
|
||||
_type = "literalExpression";
|
||||
inherit text;
|
||||
};
|
||||
|
||||
literalExample = lib.warn "lib.literalExample is deprecated, use lib.literalExpression instead, or use lib.literalMD for a non-Nix description." literalExpression;
|
||||
|
||||
/* For use in the `defaultText` and `example` option attributes. Causes the
|
||||
given MD text to be inserted verbatim in the documentation, for when
|
||||
a `literalExpression` would be too hard to read.
|
||||
/*
|
||||
For use in the `defaultText` and `example` option attributes. Causes the
|
||||
given MD text to be inserted verbatim in the documentation, for when
|
||||
a `literalExpression` would be too hard to read.
|
||||
*/
|
||||
literalMD = text:
|
||||
if ! isString text then throw "literalMD expects a string."
|
||||
else { _type = "literalMD"; inherit text; };
|
||||
literalMD =
|
||||
text:
|
||||
if !isString text then
|
||||
throw "literalMD expects a string."
|
||||
else
|
||||
{
|
||||
_type = "literalMD";
|
||||
inherit text;
|
||||
};
|
||||
|
||||
# Helper functions.
|
||||
|
||||
/* Convert an option, described as a list of the option parts to a
|
||||
human-readable version.
|
||||
/*
|
||||
Convert an option, described as a list of the option parts to a
|
||||
human-readable version.
|
||||
|
||||
Example:
|
||||
(showOption ["foo" "bar" "baz"]) == "foo.bar.baz"
|
||||
(showOption ["foo" "bar.baz" "tux"]) == "foo.\"bar.baz\".tux"
|
||||
(showOption ["windowManager" "2bwm" "enable"]) == "windowManager.\"2bwm\".enable"
|
||||
Example:
|
||||
(showOption ["foo" "bar" "baz"]) == "foo.bar.baz"
|
||||
(showOption ["foo" "bar.baz" "tux"]) == "foo.\"bar.baz\".tux"
|
||||
(showOption ["windowManager" "2bwm" "enable"]) == "windowManager.\"2bwm\".enable"
|
||||
|
||||
Placeholders will not be quoted as they are not actual values:
|
||||
(showOption ["foo" "*" "bar"]) == "foo.*.bar"
|
||||
(showOption ["foo" "<name>" "bar"]) == "foo.<name>.bar"
|
||||
Placeholders will not be quoted as they are not actual values:
|
||||
(showOption ["foo" "*" "bar"]) == "foo.*.bar"
|
||||
(showOption ["foo" "<name>" "bar"]) == "foo.<name>.bar"
|
||||
*/
|
||||
showOption = parts: let
|
||||
escapeOptionPart = part:
|
||||
let
|
||||
# We assume that these are "special values" and not real configuration data.
|
||||
# If it is real configuration data, it is rendered incorrectly.
|
||||
specialIdentifiers = [
|
||||
"<name>" # attrsOf (submodule {})
|
||||
"*" # listOf (submodule {})
|
||||
"<function body>" # functionTo
|
||||
];
|
||||
in if builtins.elem part specialIdentifiers
|
||||
then part
|
||||
else lib.strings.escapeNixIdentifier part;
|
||||
in (concatStringsSep ".") (map escapeOptionPart parts);
|
||||
showOption =
|
||||
parts:
|
||||
let
|
||||
escapeOptionPart =
|
||||
part:
|
||||
let
|
||||
# We assume that these are "special values" and not real configuration data.
|
||||
# If it is real configuration data, it is rendered incorrectly.
|
||||
specialIdentifiers = [
|
||||
"<name>" # attrsOf (submodule {})
|
||||
"*" # listOf (submodule {})
|
||||
"<function body>" # functionTo
|
||||
];
|
||||
in
|
||||
if builtins.elem part specialIdentifiers then part else lib.strings.escapeNixIdentifier part;
|
||||
in
|
||||
(concatStringsSep ".") (map escapeOptionPart parts);
|
||||
showFiles = files: concatStringsSep " and " (map (f: "`${f}'") files);
|
||||
|
||||
showDefs = defs: concatMapStrings (def:
|
||||
let
|
||||
# Pretty print the value for display, if successful
|
||||
prettyEval = builtins.tryEval
|
||||
(lib.generators.toPretty { }
|
||||
(lib.generators.withRecursion { depthLimit = 10; throwOnDepthLimit = false; } def.value));
|
||||
# Split it into its lines
|
||||
lines = filter (v: ! isList v) (builtins.split "\n" prettyEval.value);
|
||||
# Only display the first 5 lines, and indent them for better visibility
|
||||
value = concatStringsSep "\n " (take 5 lines ++ optional (length lines > 5) "...");
|
||||
result =
|
||||
# Don't print any value if evaluating the value strictly fails
|
||||
if ! prettyEval.success then ""
|
||||
# Put it on a new line if it consists of multiple
|
||||
else if length lines > 1 then ":\n " + value
|
||||
else ": " + value;
|
||||
in "\n- In `${def.file}'${result}"
|
||||
) defs;
|
||||
showDefs =
|
||||
defs:
|
||||
concatMapStrings (
|
||||
def:
|
||||
let
|
||||
# Pretty print the value for display, if successful
|
||||
prettyEval = builtins.tryEval (
|
||||
lib.generators.toPretty { } (
|
||||
lib.generators.withRecursion {
|
||||
depthLimit = 10;
|
||||
throwOnDepthLimit = false;
|
||||
} def.value
|
||||
)
|
||||
);
|
||||
# Split it into its lines
|
||||
lines = filter (v: !isList v) (builtins.split "\n" prettyEval.value);
|
||||
# Only display the first 5 lines, and indent them for better visibility
|
||||
value = concatStringsSep "\n " (take 5 lines ++ optional (length lines > 5) "...");
|
||||
result =
|
||||
# Don't print any value if evaluating the value strictly fails
|
||||
if !prettyEval.success then
|
||||
""
|
||||
# Put it on a new line if it consists of multiple
|
||||
else if length lines > 1 then
|
||||
":\n " + value
|
||||
else
|
||||
": " + value;
|
||||
in
|
||||
"\n- In `${def.file}'${result}"
|
||||
) defs;
|
||||
|
||||
showOptionWithDefLocs = opt: ''
|
||||
${showOption opt.loc}, with values defined in:
|
||||
${concatMapStringsSep "\n" (defFile: " - ${defFile}") opt.files}
|
||||
'';
|
||||
${showOption opt.loc}, with values defined in:
|
||||
${concatMapStringsSep "\n" (defFile: " - ${defFile}") opt.files}
|
||||
'';
|
||||
|
||||
unknownModule = "<unknown-file>";
|
||||
|
||||
|
|
|
@ -1,44 +1,44 @@
|
|||
{ lib }:
|
||||
/*
|
||||
Usage:
|
||||
Usage:
|
||||
|
||||
You define you custom builder script by adding all build steps to a list.
|
||||
for example:
|
||||
builder = writeScript "fsg-4.4-builder"
|
||||
(textClosure [doUnpack addInputs preBuild doMake installPhase doForceShare]);
|
||||
You define you custom builder script by adding all build steps to a list.
|
||||
for example:
|
||||
builder = writeScript "fsg-4.4-builder"
|
||||
(textClosure [doUnpack addInputs preBuild doMake installPhase doForceShare]);
|
||||
|
||||
a step is defined by noDepEntry, fullDepEntry or packEntry.
|
||||
To ensure that prerequisite are met those are added before the task itself by
|
||||
textClosureDupList. Duplicated items are removed again.
|
||||
a step is defined by noDepEntry, fullDepEntry or packEntry.
|
||||
To ensure that prerequisite are met those are added before the task itself by
|
||||
textClosureDupList. Duplicated items are removed again.
|
||||
|
||||
See trace/nixpkgs/trunk/pkgs/top-level/builder-defs.nix for some predefined build steps
|
||||
See trace/nixpkgs/trunk/pkgs/top-level/builder-defs.nix for some predefined build steps
|
||||
|
||||
Attention:
|
||||
Attention:
|
||||
|
||||
let
|
||||
pkgs = (import <nixpkgs>) {};
|
||||
in let
|
||||
inherit (pkgs.stringsWithDeps) fullDepEntry packEntry noDepEntry textClosureMap;
|
||||
inherit (pkgs.lib) id;
|
||||
let
|
||||
pkgs = (import <nixpkgs>) {};
|
||||
in let
|
||||
inherit (pkgs.stringsWithDeps) fullDepEntry packEntry noDepEntry textClosureMap;
|
||||
inherit (pkgs.lib) id;
|
||||
|
||||
nameA = noDepEntry "Text a";
|
||||
nameB = fullDepEntry "Text b" ["nameA"];
|
||||
nameC = fullDepEntry "Text c" ["nameA"];
|
||||
nameA = noDepEntry "Text a";
|
||||
nameB = fullDepEntry "Text b" ["nameA"];
|
||||
nameC = fullDepEntry "Text c" ["nameA"];
|
||||
|
||||
stages = {
|
||||
nameHeader = noDepEntry "#! /bin/sh \n";
|
||||
inherit nameA nameB nameC;
|
||||
};
|
||||
in
|
||||
textClosureMap id stages
|
||||
[ "nameHeader" "nameA" "nameB" "nameC"
|
||||
nameC # <- added twice. add a dep entry if you know that it will be added once only [1]
|
||||
"nameB" # <- this will not be added again because the attr name (reference) is used
|
||||
]
|
||||
stages = {
|
||||
nameHeader = noDepEntry "#! /bin/sh \n";
|
||||
inherit nameA nameB nameC;
|
||||
};
|
||||
in
|
||||
textClosureMap id stages
|
||||
[ "nameHeader" "nameA" "nameB" "nameC"
|
||||
nameC # <- added twice. add a dep entry if you know that it will be added once only [1]
|
||||
"nameB" # <- this will not be added again because the attr name (reference) is used
|
||||
]
|
||||
|
||||
# result: Str("#! /bin/sh \n\nText a\nText b\nText c\nText c",[])
|
||||
# result: Str("#! /bin/sh \n\nText a\nText b\nText c\nText c",[])
|
||||
|
||||
[1] maybe this behaviour should be removed to keep things simple (?)
|
||||
[1] maybe this behaviour should be removed to keep things simple (?)
|
||||
*/
|
||||
|
||||
let
|
||||
|
@ -52,32 +52,63 @@ let
|
|||
in
|
||||
rec {
|
||||
|
||||
/* !!! The interface of this function is kind of messed up, since
|
||||
it's way too overloaded and almost but not quite computes a
|
||||
topological sort of the depstrings. */
|
||||
/*
|
||||
!!! The interface of this function is kind of messed up, since
|
||||
it's way too overloaded and almost but not quite computes a
|
||||
topological sort of the depstrings.
|
||||
*/
|
||||
|
||||
textClosureList = predefined: arg:
|
||||
textClosureList =
|
||||
predefined: arg:
|
||||
let
|
||||
f = done: todo:
|
||||
if todo == [] then {result = []; inherit done;}
|
||||
f =
|
||||
done: todo:
|
||||
if todo == [ ] then
|
||||
{
|
||||
result = [ ];
|
||||
inherit done;
|
||||
}
|
||||
else
|
||||
let entry = head todo; in
|
||||
let
|
||||
entry = head todo;
|
||||
in
|
||||
if isAttrs entry then
|
||||
let x = f done entry.deps;
|
||||
y = f x.done (tail todo);
|
||||
in { result = x.result ++ [entry.text] ++ y.result;
|
||||
done = y.done;
|
||||
}
|
||||
else if done ? ${entry} then f done (tail todo)
|
||||
else f (done // listToAttrs [{name = entry; value = 1;}]) ([predefined.${entry}] ++ tail todo);
|
||||
in (f {} arg).result;
|
||||
let
|
||||
x = f done entry.deps;
|
||||
y = f x.done (tail todo);
|
||||
in
|
||||
{
|
||||
result = x.result ++ [ entry.text ] ++ y.result;
|
||||
done = y.done;
|
||||
}
|
||||
else if done ? ${entry} then
|
||||
f done (tail todo)
|
||||
else
|
||||
f (
|
||||
done
|
||||
// listToAttrs [
|
||||
{
|
||||
name = entry;
|
||||
value = 1;
|
||||
}
|
||||
]
|
||||
) ([ predefined.${entry} ] ++ tail todo);
|
||||
in
|
||||
(f { } arg).result;
|
||||
|
||||
textClosureMap = f: predefined: names:
|
||||
textClosureMap =
|
||||
f: predefined: names:
|
||||
concatStringsSep "\n" (map f (textClosureList predefined names));
|
||||
|
||||
noDepEntry = text: {inherit text; deps = [];};
|
||||
fullDepEntry = text: deps: {inherit text deps;};
|
||||
packEntry = deps: {inherit deps; text="";};
|
||||
noDepEntry = text: {
|
||||
inherit text;
|
||||
deps = [ ];
|
||||
};
|
||||
fullDepEntry = text: deps: { inherit text deps; };
|
||||
packEntry = deps: {
|
||||
inherit deps;
|
||||
text = "";
|
||||
};
|
||||
|
||||
stringAfter = deps: text: { inherit text deps; };
|
||||
|
||||
|
|
747
lib/strings.nix
747
lib/strings.nix
File diff suppressed because it is too large
Load diff
|
@ -5,90 +5,372 @@ rec {
|
|||
features = {
|
||||
# x86_64 Generic
|
||||
# Spec: https://gitlab.com/x86-psABIs/x86-64-ABI/
|
||||
default = [ ];
|
||||
x86-64 = [ ];
|
||||
x86-64-v2 = [ "sse3" "ssse3" "sse4_1" "sse4_2" ];
|
||||
x86-64-v3 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "avx" "avx2" "fma" ];
|
||||
x86-64-v4 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "avx" "avx2" "avx512" "fma" ];
|
||||
default = [ ];
|
||||
x86-64 = [ ];
|
||||
x86-64-v2 = [
|
||||
"sse3"
|
||||
"ssse3"
|
||||
"sse4_1"
|
||||
"sse4_2"
|
||||
];
|
||||
x86-64-v3 = [
|
||||
"sse3"
|
||||
"ssse3"
|
||||
"sse4_1"
|
||||
"sse4_2"
|
||||
"avx"
|
||||
"avx2"
|
||||
"fma"
|
||||
];
|
||||
x86-64-v4 = [
|
||||
"sse3"
|
||||
"ssse3"
|
||||
"sse4_1"
|
||||
"sse4_2"
|
||||
"avx"
|
||||
"avx2"
|
||||
"avx512"
|
||||
"fma"
|
||||
];
|
||||
# x86_64 Intel
|
||||
nehalem = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" ];
|
||||
westmere = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" ];
|
||||
sandybridge = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" ];
|
||||
ivybridge = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" ];
|
||||
haswell = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "fma" ];
|
||||
broadwell = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "fma" ];
|
||||
skylake = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "fma" ];
|
||||
skylake-avx512 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "avx512" "fma" ];
|
||||
cannonlake = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "avx512" "fma" ];
|
||||
icelake-client = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "avx512" "fma" ];
|
||||
icelake-server = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "avx512" "fma" ];
|
||||
cascadelake = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "avx512" "fma" ];
|
||||
cooperlake = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "avx512" "fma" ];
|
||||
tigerlake = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "avx512" "fma" ];
|
||||
alderlake = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "fma" ];
|
||||
sapphirerapids = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "avx512" "fma" ];
|
||||
emeraldrapids = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "avx512" "fma" ];
|
||||
nehalem = [
|
||||
"sse3"
|
||||
"ssse3"
|
||||
"sse4_1"
|
||||
"sse4_2"
|
||||
"aes"
|
||||
];
|
||||
westmere = [
|
||||
"sse3"
|
||||
"ssse3"
|
||||
"sse4_1"
|
||||
"sse4_2"
|
||||
"aes"
|
||||
];
|
||||
sandybridge = [
|
||||
"sse3"
|
||||
"ssse3"
|
||||
"sse4_1"
|
||||
"sse4_2"
|
||||
"aes"
|
||||
"avx"
|
||||
];
|
||||
ivybridge = [
|
||||
"sse3"
|
||||
"ssse3"
|
||||
"sse4_1"
|
||||
"sse4_2"
|
||||
"aes"
|
||||
"avx"
|
||||
];
|
||||
haswell = [
|
||||
"sse3"
|
||||
"ssse3"
|
||||
"sse4_1"
|
||||
"sse4_2"
|
||||
"aes"
|
||||
"avx"
|
||||
"avx2"
|
||||
"fma"
|
||||
];
|
||||
broadwell = [
|
||||
"sse3"
|
||||
"ssse3"
|
||||
"sse4_1"
|
||||
"sse4_2"
|
||||
"aes"
|
||||
"avx"
|
||||
"avx2"
|
||||
"fma"
|
||||
];
|
||||
skylake = [
|
||||
"sse3"
|
||||
"ssse3"
|
||||
"sse4_1"
|
||||
"sse4_2"
|
||||
"aes"
|
||||
"avx"
|
||||
"avx2"
|
||||
"fma"
|
||||
];
|
||||
skylake-avx512 = [
|
||||
"sse3"
|
||||
"ssse3"
|
||||
"sse4_1"
|
||||
"sse4_2"
|
||||
"aes"
|
||||
"avx"
|
||||
"avx2"
|
||||
"avx512"
|
||||
"fma"
|
||||
];
|
||||
cannonlake = [
|
||||
"sse3"
|
||||
"ssse3"
|
||||
"sse4_1"
|
||||
"sse4_2"
|
||||
"aes"
|
||||
"avx"
|
||||
"avx2"
|
||||
"avx512"
|
||||
"fma"
|
||||
];
|
||||
icelake-client = [
|
||||
"sse3"
|
||||
"ssse3"
|
||||
"sse4_1"
|
||||
"sse4_2"
|
||||
"aes"
|
||||
"avx"
|
||||
"avx2"
|
||||
"avx512"
|
||||
"fma"
|
||||
];
|
||||
icelake-server = [
|
||||
"sse3"
|
||||
"ssse3"
|
||||
"sse4_1"
|
||||
"sse4_2"
|
||||
"aes"
|
||||
"avx"
|
||||
"avx2"
|
||||
"avx512"
|
||||
"fma"
|
||||
];
|
||||
cascadelake = [
|
||||
"sse3"
|
||||
"ssse3"
|
||||
"sse4_1"
|
||||
"sse4_2"
|
||||
"aes"
|
||||
"avx"
|
||||
"avx2"
|
||||
"avx512"
|
||||
"fma"
|
||||
];
|
||||
cooperlake = [
|
||||
"sse3"
|
||||
"ssse3"
|
||||
"sse4_1"
|
||||
"sse4_2"
|
||||
"aes"
|
||||
"avx"
|
||||
"avx2"
|
||||
"avx512"
|
||||
"fma"
|
||||
];
|
||||
tigerlake = [
|
||||
"sse3"
|
||||
"ssse3"
|
||||
"sse4_1"
|
||||
"sse4_2"
|
||||
"aes"
|
||||
"avx"
|
||||
"avx2"
|
||||
"avx512"
|
||||
"fma"
|
||||
];
|
||||
alderlake = [
|
||||
"sse3"
|
||||
"ssse3"
|
||||
"sse4_1"
|
||||
"sse4_2"
|
||||
"aes"
|
||||
"avx"
|
||||
"avx2"
|
||||
"fma"
|
||||
];
|
||||
sapphirerapids = [
|
||||
"sse3"
|
||||
"ssse3"
|
||||
"sse4_1"
|
||||
"sse4_2"
|
||||
"aes"
|
||||
"avx"
|
||||
"avx2"
|
||||
"avx512"
|
||||
"fma"
|
||||
];
|
||||
emeraldrapids = [
|
||||
"sse3"
|
||||
"ssse3"
|
||||
"sse4_1"
|
||||
"sse4_2"
|
||||
"aes"
|
||||
"avx"
|
||||
"avx2"
|
||||
"avx512"
|
||||
"fma"
|
||||
];
|
||||
# x86_64 AMD
|
||||
btver1 = [ "sse3" "ssse3" "sse4_1" "sse4_2" ];
|
||||
btver2 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" ];
|
||||
bdver1 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "sse4a" "aes" "avx" "fma" "fma4" ];
|
||||
bdver2 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "sse4a" "aes" "avx" "fma" "fma4" ];
|
||||
bdver3 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "sse4a" "aes" "avx" "fma" "fma4" ];
|
||||
bdver4 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "sse4a" "aes" "avx" "avx2" "fma" "fma4" ];
|
||||
znver1 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "sse4a" "aes" "avx" "avx2" "fma" ];
|
||||
znver2 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "sse4a" "aes" "avx" "avx2" "fma" ];
|
||||
znver3 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "sse4a" "aes" "avx" "avx2" "fma" ];
|
||||
znver4 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "sse4a" "aes" "avx" "avx2" "avx512" "fma" ];
|
||||
btver1 = [
|
||||
"sse3"
|
||||
"ssse3"
|
||||
"sse4_1"
|
||||
"sse4_2"
|
||||
];
|
||||
btver2 = [
|
||||
"sse3"
|
||||
"ssse3"
|
||||
"sse4_1"
|
||||
"sse4_2"
|
||||
"aes"
|
||||
"avx"
|
||||
];
|
||||
bdver1 = [
|
||||
"sse3"
|
||||
"ssse3"
|
||||
"sse4_1"
|
||||
"sse4_2"
|
||||
"sse4a"
|
||||
"aes"
|
||||
"avx"
|
||||
"fma"
|
||||
"fma4"
|
||||
];
|
||||
bdver2 = [
|
||||
"sse3"
|
||||
"ssse3"
|
||||
"sse4_1"
|
||||
"sse4_2"
|
||||
"sse4a"
|
||||
"aes"
|
||||
"avx"
|
||||
"fma"
|
||||
"fma4"
|
||||
];
|
||||
bdver3 = [
|
||||
"sse3"
|
||||
"ssse3"
|
||||
"sse4_1"
|
||||
"sse4_2"
|
||||
"sse4a"
|
||||
"aes"
|
||||
"avx"
|
||||
"fma"
|
||||
"fma4"
|
||||
];
|
||||
bdver4 = [
|
||||
"sse3"
|
||||
"ssse3"
|
||||
"sse4_1"
|
||||
"sse4_2"
|
||||
"sse4a"
|
||||
"aes"
|
||||
"avx"
|
||||
"avx2"
|
||||
"fma"
|
||||
"fma4"
|
||||
];
|
||||
znver1 = [
|
||||
"sse3"
|
||||
"ssse3"
|
||||
"sse4_1"
|
||||
"sse4_2"
|
||||
"sse4a"
|
||||
"aes"
|
||||
"avx"
|
||||
"avx2"
|
||||
"fma"
|
||||
];
|
||||
znver2 = [
|
||||
"sse3"
|
||||
"ssse3"
|
||||
"sse4_1"
|
||||
"sse4_2"
|
||||
"sse4a"
|
||||
"aes"
|
||||
"avx"
|
||||
"avx2"
|
||||
"fma"
|
||||
];
|
||||
znver3 = [
|
||||
"sse3"
|
||||
"ssse3"
|
||||
"sse4_1"
|
||||
"sse4_2"
|
||||
"sse4a"
|
||||
"aes"
|
||||
"avx"
|
||||
"avx2"
|
||||
"fma"
|
||||
];
|
||||
znver4 = [
|
||||
"sse3"
|
||||
"ssse3"
|
||||
"sse4_1"
|
||||
"sse4_2"
|
||||
"sse4a"
|
||||
"aes"
|
||||
"avx"
|
||||
"avx2"
|
||||
"avx512"
|
||||
"fma"
|
||||
];
|
||||
# other
|
||||
armv5te = [ ];
|
||||
armv6 = [ ];
|
||||
armv7-a = [ ];
|
||||
armv8-a = [ ];
|
||||
mips32 = [ ];
|
||||
loongson2f = [ ];
|
||||
armv5te = [ ];
|
||||
armv6 = [ ];
|
||||
armv7-a = [ ];
|
||||
armv8-a = [ ];
|
||||
mips32 = [ ];
|
||||
loongson2f = [ ];
|
||||
};
|
||||
|
||||
# a superior CPU has all the features of an inferior and is able to build and test code for it
|
||||
inferiors = {
|
||||
# x86_64 Generic
|
||||
default = [ ];
|
||||
x86-64 = [ ];
|
||||
x86-64-v2 = [ "x86-64" ];
|
||||
default = [ ];
|
||||
x86-64 = [ ];
|
||||
x86-64-v2 = [ "x86-64" ];
|
||||
x86-64-v3 = [ "x86-64-v2" ] ++ inferiors.x86-64-v2;
|
||||
x86-64-v4 = [ "x86-64-v3" ] ++ inferiors.x86-64-v3;
|
||||
|
||||
# x86_64 Intel
|
||||
# https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html
|
||||
nehalem = [ "x86-64-v2" ] ++ inferiors.x86-64-v2;
|
||||
westmere = [ "nehalem" ] ++ inferiors.nehalem;
|
||||
sandybridge = [ "westmere" ] ++ inferiors.westmere;
|
||||
ivybridge = [ "sandybridge" ] ++ inferiors.sandybridge;
|
||||
nehalem = [ "x86-64-v2" ] ++ inferiors.x86-64-v2;
|
||||
westmere = [ "nehalem" ] ++ inferiors.nehalem;
|
||||
sandybridge = [ "westmere" ] ++ inferiors.westmere;
|
||||
ivybridge = [ "sandybridge" ] ++ inferiors.sandybridge;
|
||||
|
||||
haswell = lib.unique ([ "ivybridge" "x86-64-v3" ] ++ inferiors.ivybridge ++ inferiors.x86-64-v3);
|
||||
broadwell = [ "haswell" ] ++ inferiors.haswell;
|
||||
skylake = [ "broadwell" ] ++ inferiors.broadwell;
|
||||
haswell = lib.unique (
|
||||
[
|
||||
"ivybridge"
|
||||
"x86-64-v3"
|
||||
]
|
||||
++ inferiors.ivybridge
|
||||
++ inferiors.x86-64-v3
|
||||
);
|
||||
broadwell = [ "haswell" ] ++ inferiors.haswell;
|
||||
skylake = [ "broadwell" ] ++ inferiors.broadwell;
|
||||
|
||||
skylake-avx512 = lib.unique ([ "skylake" "x86-64-v4" ] ++ inferiors.skylake ++ inferiors.x86-64-v4);
|
||||
cannonlake = [ "skylake-avx512" ] ++ inferiors.skylake-avx512;
|
||||
icelake-client = [ "cannonlake" ] ++ inferiors.cannonlake;
|
||||
skylake-avx512 = lib.unique (
|
||||
[
|
||||
"skylake"
|
||||
"x86-64-v4"
|
||||
]
|
||||
++ inferiors.skylake
|
||||
++ inferiors.x86-64-v4
|
||||
);
|
||||
cannonlake = [ "skylake-avx512" ] ++ inferiors.skylake-avx512;
|
||||
icelake-client = [ "cannonlake" ] ++ inferiors.cannonlake;
|
||||
icelake-server = [ "icelake-client" ] ++ inferiors.icelake-client;
|
||||
cascadelake = [ "cannonlake" ] ++ inferiors.cannonlake;
|
||||
cooperlake = [ "cascadelake" ] ++ inferiors.cascadelake;
|
||||
tigerlake = [ "icelake-server" ] ++ inferiors.icelake-server;
|
||||
sapphirerapids = [ "tigerlake" ] ++ inferiors.tigerlake;
|
||||
emeraldrapids = [ "sapphirerapids" ] ++ inferiors.sapphirerapids;
|
||||
cascadelake = [ "cannonlake" ] ++ inferiors.cannonlake;
|
||||
cooperlake = [ "cascadelake" ] ++ inferiors.cascadelake;
|
||||
tigerlake = [ "icelake-server" ] ++ inferiors.icelake-server;
|
||||
sapphirerapids = [ "tigerlake" ] ++ inferiors.tigerlake;
|
||||
emeraldrapids = [ "sapphirerapids" ] ++ inferiors.sapphirerapids;
|
||||
|
||||
# CX16 does not exist on alderlake, while it does on nearly all other intel CPUs
|
||||
alderlake = [ ];
|
||||
alderlake = [ ];
|
||||
|
||||
# x86_64 AMD
|
||||
# TODO: fill this (need testing)
|
||||
btver1 = [ ];
|
||||
btver2 = [ ];
|
||||
bdver1 = [ ];
|
||||
bdver2 = [ ];
|
||||
bdver3 = [ ];
|
||||
bdver4 = [ ];
|
||||
btver1 = [ ];
|
||||
btver2 = [ ];
|
||||
bdver1 = [ ];
|
||||
bdver2 = [ ];
|
||||
bdver3 = [ ];
|
||||
bdver4 = [ ];
|
||||
# Regarding `skylake` as inferior of `znver1`, there are reports of
|
||||
# successful usage by Gentoo users and Phoronix benchmarking of different
|
||||
# `-march` targets.
|
||||
|
@ -108,33 +390,42 @@ rec {
|
|||
# https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html
|
||||
# https://en.wikichip.org/wiki/amd/microarchitectures/zen
|
||||
# https://en.wikichip.org/wiki/intel/microarchitectures/skylake
|
||||
znver1 = [ "skylake" ] ++ inferiors.skylake; # Includes haswell and x86-64-v3
|
||||
znver2 = [ "znver1" ] ++ inferiors.znver1;
|
||||
znver3 = [ "znver2" ] ++ inferiors.znver2;
|
||||
znver4 = lib.unique ([ "znver3" "x86-64-v4" ] ++ inferiors.znver3 ++ inferiors.x86-64-v4);
|
||||
znver1 = [ "skylake" ] ++ inferiors.skylake; # Includes haswell and x86-64-v3
|
||||
znver2 = [ "znver1" ] ++ inferiors.znver1;
|
||||
znver3 = [ "znver2" ] ++ inferiors.znver2;
|
||||
znver4 = lib.unique (
|
||||
[
|
||||
"znver3"
|
||||
"x86-64-v4"
|
||||
]
|
||||
++ inferiors.znver3
|
||||
++ inferiors.x86-64-v4
|
||||
);
|
||||
|
||||
# other
|
||||
armv5te = [ ];
|
||||
armv6 = [ ];
|
||||
armv7-a = [ ];
|
||||
armv8-a = [ ];
|
||||
mips32 = [ ];
|
||||
loongson2f = [ ];
|
||||
armv5te = [ ];
|
||||
armv6 = [ ];
|
||||
armv7-a = [ ];
|
||||
armv8-a = [ ];
|
||||
mips32 = [ ];
|
||||
loongson2f = [ ];
|
||||
};
|
||||
|
||||
predicates = let
|
||||
featureSupport = feature: x: builtins.elem feature features.${x} or [];
|
||||
in {
|
||||
sse3Support = featureSupport "sse3";
|
||||
ssse3Support = featureSupport "ssse3";
|
||||
sse4_1Support = featureSupport "sse4_1";
|
||||
sse4_2Support = featureSupport "sse4_2";
|
||||
sse4_aSupport = featureSupport "sse4a";
|
||||
avxSupport = featureSupport "avx";
|
||||
avx2Support = featureSupport "avx2";
|
||||
avx512Support = featureSupport "avx512";
|
||||
aesSupport = featureSupport "aes";
|
||||
fmaSupport = featureSupport "fma";
|
||||
fma4Support = featureSupport "fma4";
|
||||
};
|
||||
predicates =
|
||||
let
|
||||
featureSupport = feature: x: builtins.elem feature features.${x} or [ ];
|
||||
in
|
||||
{
|
||||
sse3Support = featureSupport "sse3";
|
||||
ssse3Support = featureSupport "ssse3";
|
||||
sse4_1Support = featureSupport "sse4_1";
|
||||
sse4_2Support = featureSupport "sse4_2";
|
||||
sse4_aSupport = featureSupport "sse4a";
|
||||
avxSupport = featureSupport "avx";
|
||||
avx2Support = featureSupport "avx2";
|
||||
avx512Support = featureSupport "avx512";
|
||||
aesSupport = featureSupport "aes";
|
||||
fmaSupport = featureSupport "fma";
|
||||
fma4Support = featureSupport "fma4";
|
||||
};
|
||||
}
|
||||
|
|
|
@ -42,8 +42,10 @@ let
|
|||
both arguments have been `elaborate`-d.
|
||||
*/
|
||||
equals =
|
||||
let removeFunctions = a: filterAttrs (_: v: !isFunction v) a;
|
||||
in a: b: removeFunctions a == removeFunctions b;
|
||||
let
|
||||
removeFunctions = a: filterAttrs (_: v: !isFunction v) a;
|
||||
in
|
||||
a: b: removeFunctions a == removeFunctions b;
|
||||
|
||||
/**
|
||||
List of all Nix system doubles the nixpkgs flake will expose the package set
|
||||
|
@ -61,364 +63,467 @@ let
|
|||
# `parsed` is inferred from args, both because there are two options with one
|
||||
# clearly preferred, and to prevent cycles. A simpler fixed point where the RHS
|
||||
# always just used `final.*` would fail on both counts.
|
||||
elaborate = args': let
|
||||
args = if isString args' then { system = args'; }
|
||||
else args';
|
||||
elaborate =
|
||||
args':
|
||||
let
|
||||
args = if isString args' then { system = args'; } else args';
|
||||
|
||||
# TODO: deprecate args.rustc in favour of args.rust after 23.05 is EOL.
|
||||
rust = args.rust or args.rustc or {};
|
||||
# TODO: deprecate args.rustc in favour of args.rust after 23.05 is EOL.
|
||||
rust = args.rust or args.rustc or { };
|
||||
|
||||
final = {
|
||||
# Prefer to parse `config` as it is strictly more informative.
|
||||
parsed = parse.mkSystemFromString (if args ? config then args.config else args.system);
|
||||
# Either of these can be losslessly-extracted from `parsed` iff parsing succeeds.
|
||||
system = parse.doubleFromSystem final.parsed;
|
||||
config = parse.tripleFromSystem final.parsed;
|
||||
# Determine whether we can execute binaries built for the provided platform.
|
||||
canExecute = platform:
|
||||
final.isAndroid == platform.isAndroid &&
|
||||
parse.isCompatible final.parsed.cpu platform.parsed.cpu
|
||||
&& final.parsed.kernel == platform.parsed.kernel;
|
||||
isCompatible = _: throw "2022-05-23: isCompatible has been removed in favor of canExecute, refer to the 22.11 changelog for details";
|
||||
# Derived meta-data
|
||||
useLLVM = final.isFreeBSD || final.isOpenBSD;
|
||||
final =
|
||||
{
|
||||
# Prefer to parse `config` as it is strictly more informative.
|
||||
parsed = parse.mkSystemFromString (if args ? config then args.config else args.system);
|
||||
# Either of these can be losslessly-extracted from `parsed` iff parsing succeeds.
|
||||
system = parse.doubleFromSystem final.parsed;
|
||||
config = parse.tripleFromSystem final.parsed;
|
||||
# Determine whether we can execute binaries built for the provided platform.
|
||||
canExecute =
|
||||
platform:
|
||||
final.isAndroid == platform.isAndroid
|
||||
&& parse.isCompatible final.parsed.cpu platform.parsed.cpu
|
||||
&& final.parsed.kernel == platform.parsed.kernel;
|
||||
isCompatible =
|
||||
_:
|
||||
throw "2022-05-23: isCompatible has been removed in favor of canExecute, refer to the 22.11 changelog for details";
|
||||
# Derived meta-data
|
||||
useLLVM = final.isFreeBSD || final.isOpenBSD;
|
||||
|
||||
libc =
|
||||
/**/ if final.isDarwin then "libSystem"
|
||||
else if final.isMinGW then "msvcrt"
|
||||
else if final.isWasi then "wasilibc"
|
||||
else if final.isWasm && !final.isWasi then null
|
||||
else if final.isRedox then "relibc"
|
||||
else if final.isMusl then "musl"
|
||||
else if final.isUClibc then "uclibc"
|
||||
else if final.isAndroid then "bionic"
|
||||
else if final.isLinux /* default */ then "glibc"
|
||||
else if final.isFreeBSD then "fblibc"
|
||||
else if final.isOpenBSD then "oblibc"
|
||||
else if final.isNetBSD then "nblibc"
|
||||
else if final.isAvr then "avrlibc"
|
||||
else if final.isGhcjs then null
|
||||
else if final.isNone then "newlib"
|
||||
# TODO(@Ericson2314) think more about other operating systems
|
||||
else "native/impure";
|
||||
# Choose what linker we wish to use by default. Someday we might also
|
||||
# choose the C compiler, runtime library, C++ standard library, etc. in
|
||||
# this way, nice and orthogonally, and deprecate `useLLVM`. But due to
|
||||
# the monolithic GCC build we cannot actually make those choices
|
||||
# independently, so we are just doing `linker` and keeping `useLLVM` for
|
||||
# now.
|
||||
linker =
|
||||
/**/ if final.useLLVM or false then "lld"
|
||||
else if final.isDarwin then "cctools"
|
||||
# "bfd" and "gold" both come from GNU binutils. The existence of Gold
|
||||
# is why we use the more obscure "bfd" and not "binutils" for this
|
||||
# choice.
|
||||
else "bfd";
|
||||
# The standard lib directory name that non-nixpkgs binaries distributed
|
||||
# for this platform normally assume.
|
||||
libDir = if final.isLinux then
|
||||
if final.isx86_64 || final.isMips64 || final.isPower64
|
||||
then "lib64"
|
||||
else "lib"
|
||||
else null;
|
||||
extensions = optionalAttrs final.hasSharedLibraries {
|
||||
sharedLibrary =
|
||||
if final.isDarwin then ".dylib"
|
||||
else if final.isWindows then ".dll"
|
||||
else ".so";
|
||||
} // {
|
||||
staticLibrary =
|
||||
/**/ if final.isWindows then ".lib"
|
||||
else ".a";
|
||||
library =
|
||||
/**/ if final.isStatic then final.extensions.staticLibrary
|
||||
else final.extensions.sharedLibrary;
|
||||
executable =
|
||||
/**/ if final.isWindows then ".exe"
|
||||
else "";
|
||||
};
|
||||
# Misc boolean options
|
||||
useAndroidPrebuilt = false;
|
||||
useiOSPrebuilt = false;
|
||||
libc =
|
||||
if final.isDarwin then
|
||||
"libSystem"
|
||||
else if final.isMinGW then
|
||||
"msvcrt"
|
||||
else if final.isWasi then
|
||||
"wasilibc"
|
||||
else if final.isWasm && !final.isWasi then
|
||||
null
|
||||
else if final.isRedox then
|
||||
"relibc"
|
||||
else if final.isMusl then
|
||||
"musl"
|
||||
else if final.isUClibc then
|
||||
"uclibc"
|
||||
else if final.isAndroid then
|
||||
"bionic"
|
||||
else if
|
||||
final.isLinux # default
|
||||
then
|
||||
"glibc"
|
||||
else if final.isFreeBSD then
|
||||
"fblibc"
|
||||
else if final.isOpenBSD then
|
||||
"oblibc"
|
||||
else if final.isNetBSD then
|
||||
"nblibc"
|
||||
else if final.isAvr then
|
||||
"avrlibc"
|
||||
else if final.isGhcjs then
|
||||
null
|
||||
else if final.isNone then
|
||||
"newlib"
|
||||
# TODO(@Ericson2314) think more about other operating systems
|
||||
else
|
||||
"native/impure";
|
||||
# Choose what linker we wish to use by default. Someday we might also
|
||||
# choose the C compiler, runtime library, C++ standard library, etc. in
|
||||
# this way, nice and orthogonally, and deprecate `useLLVM`. But due to
|
||||
# the monolithic GCC build we cannot actually make those choices
|
||||
# independently, so we are just doing `linker` and keeping `useLLVM` for
|
||||
# now.
|
||||
linker =
|
||||
if final.useLLVM or false then
|
||||
"lld"
|
||||
else if final.isDarwin then
|
||||
"cctools"
|
||||
# "bfd" and "gold" both come from GNU binutils. The existence of Gold
|
||||
# is why we use the more obscure "bfd" and not "binutils" for this
|
||||
# choice.
|
||||
else
|
||||
"bfd";
|
||||
# The standard lib directory name that non-nixpkgs binaries distributed
|
||||
# for this platform normally assume.
|
||||
libDir =
|
||||
if final.isLinux then
|
||||
if final.isx86_64 || final.isMips64 || final.isPower64 then "lib64" else "lib"
|
||||
else
|
||||
null;
|
||||
extensions =
|
||||
optionalAttrs final.hasSharedLibraries {
|
||||
sharedLibrary =
|
||||
if final.isDarwin then
|
||||
".dylib"
|
||||
else if final.isWindows then
|
||||
".dll"
|
||||
else
|
||||
".so";
|
||||
}
|
||||
// {
|
||||
staticLibrary = if final.isWindows then ".lib" else ".a";
|
||||
library = if final.isStatic then final.extensions.staticLibrary else final.extensions.sharedLibrary;
|
||||
executable = if final.isWindows then ".exe" else "";
|
||||
};
|
||||
# Misc boolean options
|
||||
useAndroidPrebuilt = false;
|
||||
useiOSPrebuilt = false;
|
||||
|
||||
# Output from uname
|
||||
uname = {
|
||||
# uname -s
|
||||
system = {
|
||||
linux = "Linux";
|
||||
windows = "Windows";
|
||||
darwin = "Darwin";
|
||||
netbsd = "NetBSD";
|
||||
freebsd = "FreeBSD";
|
||||
openbsd = "OpenBSD";
|
||||
wasi = "Wasi";
|
||||
redox = "Redox";
|
||||
genode = "Genode";
|
||||
}.${final.parsed.kernel.name} or null;
|
||||
# Output from uname
|
||||
uname = {
|
||||
# uname -s
|
||||
system =
|
||||
{
|
||||
linux = "Linux";
|
||||
windows = "Windows";
|
||||
darwin = "Darwin";
|
||||
netbsd = "NetBSD";
|
||||
freebsd = "FreeBSD";
|
||||
openbsd = "OpenBSD";
|
||||
wasi = "Wasi";
|
||||
redox = "Redox";
|
||||
genode = "Genode";
|
||||
}
|
||||
.${final.parsed.kernel.name} or null;
|
||||
|
||||
# uname -m
|
||||
processor =
|
||||
if final.isPower64
|
||||
then "ppc64${optionalString final.isLittleEndian "le"}"
|
||||
else if final.isPower
|
||||
then "ppc${optionalString final.isLittleEndian "le"}"
|
||||
else if final.isMips64
|
||||
then "mips64" # endianness is *not* included on mips64
|
||||
else final.parsed.cpu.name;
|
||||
# uname -m
|
||||
processor =
|
||||
if final.isPower64 then
|
||||
"ppc64${optionalString final.isLittleEndian "le"}"
|
||||
else if final.isPower then
|
||||
"ppc${optionalString final.isLittleEndian "le"}"
|
||||
else if final.isMips64 then
|
||||
"mips64" # endianness is *not* included on mips64
|
||||
else
|
||||
final.parsed.cpu.name;
|
||||
|
||||
# uname -r
|
||||
release = null;
|
||||
};
|
||||
|
||||
# It is important that hasSharedLibraries==false when the platform has no
|
||||
# dynamic library loader. Various tools (including the gcc build system)
|
||||
# have knowledge of which platforms are incapable of dynamic linking, and
|
||||
# will still build on/for those platforms with --enable-shared, but simply
|
||||
# omit any `.so` build products such as libgcc_s.so. When that happens,
|
||||
# it causes hard-to-troubleshoot build failures.
|
||||
hasSharedLibraries = with final;
|
||||
(isAndroid || isGnu || isMusl # Linux (allows multiple libcs)
|
||||
|| isDarwin || isSunOS || isOpenBSD || isFreeBSD || isNetBSD # BSDs
|
||||
|| isCygwin || isMinGW || isWindows # Windows
|
||||
|| isWasm # WASM
|
||||
) && !isStatic;
|
||||
|
||||
# The difference between `isStatic` and `hasSharedLibraries` is mainly the
|
||||
# addition of the `staticMarker` (see make-derivation.nix). Some
|
||||
# platforms, like embedded machines without a libc (e.g. arm-none-eabi)
|
||||
# don't support dynamic linking, but don't get the `staticMarker`.
|
||||
# `pkgsStatic` sets `isStatic=true`, so `pkgsStatic.hostPlatform` always
|
||||
# has the `staticMarker`.
|
||||
isStatic = final.isWasi || final.isRedox;
|
||||
|
||||
# Just a guess, based on `system`
|
||||
inherit
|
||||
({
|
||||
linux-kernel = args.linux-kernel or {};
|
||||
gcc = args.gcc or {};
|
||||
} // platforms.select final)
|
||||
linux-kernel gcc;
|
||||
|
||||
# TODO: remove after 23.05 is EOL, with an error pointing to the rust.* attrs.
|
||||
rustc = args.rustc or {};
|
||||
|
||||
linuxArch =
|
||||
if final.isAarch32 then "arm"
|
||||
else if final.isAarch64 then "arm64"
|
||||
else if final.isx86_32 then "i386"
|
||||
else if final.isx86_64 then "x86_64"
|
||||
# linux kernel does not distinguish microblaze/microblazeel
|
||||
else if final.isMicroBlaze then "microblaze"
|
||||
else if final.isMips32 then "mips"
|
||||
else if final.isMips64 then "mips" # linux kernel does not distinguish mips32/mips64
|
||||
else if final.isPower then "powerpc"
|
||||
else if final.isRiscV then "riscv"
|
||||
else if final.isS390 then "s390"
|
||||
else if final.isLoongArch64 then "loongarch"
|
||||
else final.parsed.cpu.name;
|
||||
|
||||
# https://source.denx.de/u-boot/u-boot/-/blob/9bfb567e5f1bfe7de8eb41f8c6d00f49d2b9a426/common/image.c#L81-106
|
||||
ubootArch =
|
||||
if final.isx86_32 then "x86" # not i386
|
||||
else if final.isMips64 then "mips64" # uboot *does* distinguish between mips32/mips64
|
||||
else final.linuxArch; # other cases appear to agree with linuxArch
|
||||
|
||||
qemuArch =
|
||||
if final.isAarch32 then "arm"
|
||||
else if final.isS390 && !final.isS390x then null
|
||||
else if final.isx86_64 then "x86_64"
|
||||
else if final.isx86 then "i386"
|
||||
else if final.isMips64n32 then "mipsn32${optionalString final.isLittleEndian "el"}"
|
||||
else if final.isMips64 then "mips64${optionalString final.isLittleEndian "el"}"
|
||||
else final.uname.processor;
|
||||
|
||||
# Name used by UEFI for architectures.
|
||||
efiArch =
|
||||
if final.isx86_32 then "ia32"
|
||||
else if final.isx86_64 then "x64"
|
||||
else if final.isAarch32 then "arm"
|
||||
else if final.isAarch64 then "aa64"
|
||||
else final.parsed.cpu.name;
|
||||
|
||||
darwinArch = {
|
||||
armv7a = "armv7";
|
||||
aarch64 = "arm64";
|
||||
}.${final.parsed.cpu.name} or final.parsed.cpu.name;
|
||||
|
||||
darwinPlatform =
|
||||
if final.isMacOS then "macos"
|
||||
else if final.isiOS then "ios"
|
||||
else null;
|
||||
# The canonical name for this attribute is darwinSdkVersion, but some
|
||||
# platforms define the old name "sdkVer".
|
||||
darwinSdkVersion = final.sdkVer or (if final.isAarch64 then "11.0" else "10.12");
|
||||
darwinMinVersion = final.darwinSdkVersion;
|
||||
darwinMinVersionVariable =
|
||||
if final.isMacOS then "MACOSX_DEPLOYMENT_TARGET"
|
||||
else if final.isiOS then "IPHONEOS_DEPLOYMENT_TARGET"
|
||||
else null;
|
||||
|
||||
# Remove before 25.05
|
||||
androidSdkVersion =
|
||||
if (args ? sdkVer && !args ? androidSdkVersion) then
|
||||
throw "For android `sdkVer` has been renamed to `androidSdkVersion`"
|
||||
else if (args ? androidSdkVersion) then
|
||||
args.androidSdkVersion
|
||||
else
|
||||
null;
|
||||
androidNdkVersion =
|
||||
if (args ? ndkVer && !args ? androidNdkVersion) then
|
||||
throw "For android `ndkVer` has been renamed to `androidNdkVersion`"
|
||||
else if (args ? androidSdkVersion) then
|
||||
args.androidNdkVersion
|
||||
else
|
||||
null;
|
||||
} // (
|
||||
let
|
||||
selectEmulator = pkgs:
|
||||
let
|
||||
wine = (pkgs.winePackagesFor "wine${toString final.parsed.cpu.bits}").minimal;
|
||||
in
|
||||
# Note: we guarantee that the return value is either `null` or a path
|
||||
# to an emulator program. That is, if an emulator requires additional
|
||||
# arguments, a wrapper should be used.
|
||||
if pkgs.stdenv.hostPlatform.canExecute final
|
||||
then "${pkgs.execline}/bin/exec"
|
||||
else if final.isWindows
|
||||
then "${wine}/bin/wine${optionalString (final.parsed.cpu.bits == 64) "64"}"
|
||||
else if final.isLinux && pkgs.stdenv.hostPlatform.isLinux && final.qemuArch != null
|
||||
then "${pkgs.qemu-user}/bin/qemu-${final.qemuArch}"
|
||||
else if final.isWasi
|
||||
then "${pkgs.wasmtime}/bin/wasmtime"
|
||||
else if final.isMmix
|
||||
then "${pkgs.mmixware}/bin/mmix"
|
||||
else null;
|
||||
in {
|
||||
emulatorAvailable = pkgs: (selectEmulator pkgs) != null;
|
||||
|
||||
# whether final.emulator pkgs.pkgsStatic works
|
||||
staticEmulatorAvailable = pkgs: final.emulatorAvailable pkgs
|
||||
&& (final.isLinux || final.isWasi || final.isMmix);
|
||||
|
||||
emulator = pkgs:
|
||||
if (final.emulatorAvailable pkgs)
|
||||
then selectEmulator pkgs
|
||||
else throw "Don't know how to run ${final.config} executables.";
|
||||
|
||||
}) // mapAttrs (n: v: v final.parsed) inspect.predicates
|
||||
// mapAttrs (n: v: v final.gcc.arch or "default") architectures.predicates
|
||||
// args // {
|
||||
rust = rust // {
|
||||
# Once args.rustc.platform.target-family is deprecated and
|
||||
# removed, there will no longer be any need to modify any
|
||||
# values from args.rust.platform, so we can drop all the
|
||||
# "args ? rust" etc. checks, and merge args.rust.platform in
|
||||
# /after/.
|
||||
platform = rust.platform or {} // {
|
||||
# https://doc.rust-lang.org/reference/conditional-compilation.html#target_arch
|
||||
arch =
|
||||
/**/ if rust ? platform then rust.platform.arch
|
||||
else if final.isAarch32 then "arm"
|
||||
else if final.isMips64 then "mips64" # never add "el" suffix
|
||||
else if final.isPower64 then "powerpc64" # never add "le" suffix
|
||||
else final.parsed.cpu.name;
|
||||
|
||||
# https://doc.rust-lang.org/reference/conditional-compilation.html#target_os
|
||||
os =
|
||||
/**/ if rust ? platform then rust.platform.os or "none"
|
||||
else if final.isDarwin then "macos"
|
||||
else if final.isWasm && !final.isWasi then "unknown" # Needed for {wasm32,wasm64}-unknown-unknown.
|
||||
else final.parsed.kernel.name;
|
||||
|
||||
# https://doc.rust-lang.org/reference/conditional-compilation.html#target_family
|
||||
target-family =
|
||||
/**/ if args ? rust.platform.target-family then args.rust.platform.target-family
|
||||
else if args ? rustc.platform.target-family
|
||||
then
|
||||
(
|
||||
# Since https://github.com/rust-lang/rust/pull/84072
|
||||
# `target-family` is a list instead of single value.
|
||||
let
|
||||
f = args.rustc.platform.target-family;
|
||||
in
|
||||
if isList f then f else [ f ]
|
||||
)
|
||||
else optional final.isUnix "unix"
|
||||
++ optional final.isWindows "windows"
|
||||
++ optional final.isWasm "wasm";
|
||||
|
||||
# https://doc.rust-lang.org/reference/conditional-compilation.html#target_vendor
|
||||
vendor = let
|
||||
inherit (final.parsed) vendor;
|
||||
in rust.platform.vendor or {
|
||||
"w64" = "pc";
|
||||
}.${vendor.name} or vendor.name;
|
||||
# uname -r
|
||||
release = null;
|
||||
};
|
||||
|
||||
# The name of the rust target, even if it is custom. Adjustments are
|
||||
# because rust has slightly different naming conventions than we do.
|
||||
rustcTarget = let
|
||||
inherit (final.parsed) cpu kernel abi;
|
||||
cpu_ = rust.platform.arch or {
|
||||
"armv7a" = "armv7";
|
||||
"armv7l" = "armv7";
|
||||
"armv6l" = "arm";
|
||||
"armv5tel" = "armv5te";
|
||||
"riscv32" = "riscv32gc";
|
||||
"riscv64" = "riscv64gc";
|
||||
}.${cpu.name} or cpu.name;
|
||||
vendor_ = final.rust.platform.vendor;
|
||||
# TODO: deprecate args.rustc in favour of args.rust after 23.05 is EOL.
|
||||
# It is important that hasSharedLibraries==false when the platform has no
|
||||
# dynamic library loader. Various tools (including the gcc build system)
|
||||
# have knowledge of which platforms are incapable of dynamic linking, and
|
||||
# will still build on/for those platforms with --enable-shared, but simply
|
||||
# omit any `.so` build products such as libgcc_s.so. When that happens,
|
||||
# it causes hard-to-troubleshoot build failures.
|
||||
hasSharedLibraries =
|
||||
with final;
|
||||
(
|
||||
isAndroid
|
||||
|| isGnu
|
||||
|| isMusl # Linux (allows multiple libcs)
|
||||
|| isDarwin
|
||||
|| isSunOS
|
||||
|| isOpenBSD
|
||||
|| isFreeBSD
|
||||
|| isNetBSD # BSDs
|
||||
|| isCygwin
|
||||
|| isMinGW
|
||||
|| isWindows # Windows
|
||||
|| isWasm # WASM
|
||||
)
|
||||
&& !isStatic;
|
||||
|
||||
# The difference between `isStatic` and `hasSharedLibraries` is mainly the
|
||||
# addition of the `staticMarker` (see make-derivation.nix). Some
|
||||
# platforms, like embedded machines without a libc (e.g. arm-none-eabi)
|
||||
# don't support dynamic linking, but don't get the `staticMarker`.
|
||||
# `pkgsStatic` sets `isStatic=true`, so `pkgsStatic.hostPlatform` always
|
||||
# has the `staticMarker`.
|
||||
isStatic = final.isWasi || final.isRedox;
|
||||
|
||||
# Just a guess, based on `system`
|
||||
inherit
|
||||
(
|
||||
{
|
||||
linux-kernel = args.linux-kernel or { };
|
||||
gcc = args.gcc or { };
|
||||
}
|
||||
// platforms.select final
|
||||
)
|
||||
linux-kernel
|
||||
gcc
|
||||
;
|
||||
|
||||
# TODO: remove after 23.05 is EOL, with an error pointing to the rust.* attrs.
|
||||
rustc = args.rustc or { };
|
||||
|
||||
linuxArch =
|
||||
if final.isAarch32 then
|
||||
"arm"
|
||||
else if final.isAarch64 then
|
||||
"arm64"
|
||||
else if final.isx86_32 then
|
||||
"i386"
|
||||
else if final.isx86_64 then
|
||||
"x86_64"
|
||||
# linux kernel does not distinguish microblaze/microblazeel
|
||||
else if final.isMicroBlaze then
|
||||
"microblaze"
|
||||
else if final.isMips32 then
|
||||
"mips"
|
||||
else if final.isMips64 then
|
||||
"mips" # linux kernel does not distinguish mips32/mips64
|
||||
else if final.isPower then
|
||||
"powerpc"
|
||||
else if final.isRiscV then
|
||||
"riscv"
|
||||
else if final.isS390 then
|
||||
"s390"
|
||||
else if final.isLoongArch64 then
|
||||
"loongarch"
|
||||
else
|
||||
final.parsed.cpu.name;
|
||||
|
||||
# https://source.denx.de/u-boot/u-boot/-/blob/9bfb567e5f1bfe7de8eb41f8c6d00f49d2b9a426/common/image.c#L81-106
|
||||
ubootArch =
|
||||
if final.isx86_32 then
|
||||
"x86" # not i386
|
||||
else if final.isMips64 then
|
||||
"mips64" # uboot *does* distinguish between mips32/mips64
|
||||
else
|
||||
final.linuxArch; # other cases appear to agree with linuxArch
|
||||
|
||||
qemuArch =
|
||||
if final.isAarch32 then
|
||||
"arm"
|
||||
else if final.isS390 && !final.isS390x then
|
||||
null
|
||||
else if final.isx86_64 then
|
||||
"x86_64"
|
||||
else if final.isx86 then
|
||||
"i386"
|
||||
else if final.isMips64n32 then
|
||||
"mipsn32${optionalString final.isLittleEndian "el"}"
|
||||
else if final.isMips64 then
|
||||
"mips64${optionalString final.isLittleEndian "el"}"
|
||||
else
|
||||
final.uname.processor;
|
||||
|
||||
# Name used by UEFI for architectures.
|
||||
efiArch =
|
||||
if final.isx86_32 then
|
||||
"ia32"
|
||||
else if final.isx86_64 then
|
||||
"x64"
|
||||
else if final.isAarch32 then
|
||||
"arm"
|
||||
else if final.isAarch64 then
|
||||
"aa64"
|
||||
else
|
||||
final.parsed.cpu.name;
|
||||
|
||||
darwinArch =
|
||||
{
|
||||
armv7a = "armv7";
|
||||
aarch64 = "arm64";
|
||||
}
|
||||
.${final.parsed.cpu.name} or final.parsed.cpu.name;
|
||||
|
||||
darwinPlatform =
|
||||
if final.isMacOS then
|
||||
"macos"
|
||||
else if final.isiOS then
|
||||
"ios"
|
||||
else
|
||||
null;
|
||||
# The canonical name for this attribute is darwinSdkVersion, but some
|
||||
# platforms define the old name "sdkVer".
|
||||
darwinSdkVersion = final.sdkVer or (if final.isAarch64 then "11.0" else "10.12");
|
||||
darwinMinVersion = final.darwinSdkVersion;
|
||||
darwinMinVersionVariable =
|
||||
if final.isMacOS then
|
||||
"MACOSX_DEPLOYMENT_TARGET"
|
||||
else if final.isiOS then
|
||||
"IPHONEOS_DEPLOYMENT_TARGET"
|
||||
else
|
||||
null;
|
||||
|
||||
# Remove before 25.05
|
||||
androidSdkVersion =
|
||||
if (args ? sdkVer && !args ? androidSdkVersion) then
|
||||
throw "For android `sdkVer` has been renamed to `androidSdkVersion`"
|
||||
else if (args ? androidSdkVersion) then
|
||||
args.androidSdkVersion
|
||||
else
|
||||
null;
|
||||
androidNdkVersion =
|
||||
if (args ? ndkVer && !args ? androidNdkVersion) then
|
||||
throw "For android `ndkVer` has been renamed to `androidNdkVersion`"
|
||||
else if (args ? androidSdkVersion) then
|
||||
args.androidNdkVersion
|
||||
else
|
||||
null;
|
||||
}
|
||||
// (
|
||||
let
|
||||
selectEmulator =
|
||||
pkgs:
|
||||
let
|
||||
wine = (pkgs.winePackagesFor "wine${toString final.parsed.cpu.bits}").minimal;
|
||||
in
|
||||
# Note: we guarantee that the return value is either `null` or a path
|
||||
# to an emulator program. That is, if an emulator requires additional
|
||||
# arguments, a wrapper should be used.
|
||||
if pkgs.stdenv.hostPlatform.canExecute final then
|
||||
"${pkgs.execline}/bin/exec"
|
||||
else if final.isWindows then
|
||||
"${wine}/bin/wine${optionalString (final.parsed.cpu.bits == 64) "64"}"
|
||||
else if final.isLinux && pkgs.stdenv.hostPlatform.isLinux && final.qemuArch != null then
|
||||
"${pkgs.qemu-user}/bin/qemu-${final.qemuArch}"
|
||||
else if final.isWasi then
|
||||
"${pkgs.wasmtime}/bin/wasmtime"
|
||||
else if final.isMmix then
|
||||
"${pkgs.mmixware}/bin/mmix"
|
||||
else
|
||||
null;
|
||||
in
|
||||
args.rust.rustcTarget or
|
||||
args.rustc.config or (
|
||||
# Rust uses `wasm32-wasip?` rather than `wasm32-unknown-wasi`.
|
||||
# We cannot know which subversion does the user want, and
|
||||
# currently use WASI 0.1 as default for compatibility. Custom
|
||||
# users can set `rust.rustcTarget` to override it.
|
||||
if final.isWasi
|
||||
then "${cpu_}-wasip1"
|
||||
else "${cpu_}-${vendor_}-${kernel.name}${optionalString (abi.name != "unknown") "-${abi.name}"}"
|
||||
);
|
||||
{
|
||||
emulatorAvailable = pkgs: (selectEmulator pkgs) != null;
|
||||
|
||||
# The name of the rust target if it is standard, or the json file
|
||||
# containing the custom target spec.
|
||||
rustcTargetSpec = rust.rustcTargetSpec or (
|
||||
/**/ if rust ? platform
|
||||
then builtins.toFile (final.rust.rustcTarget + ".json") (toJSON rust.platform)
|
||||
else final.rust.rustcTarget);
|
||||
# whether final.emulator pkgs.pkgsStatic works
|
||||
staticEmulatorAvailable =
|
||||
pkgs: final.emulatorAvailable pkgs && (final.isLinux || final.isWasi || final.isMmix);
|
||||
|
||||
# The name of the rust target if it is standard, or the
|
||||
# basename of the file containing the custom target spec,
|
||||
# without the .json extension.
|
||||
#
|
||||
# This is the name used by Cargo for target subdirectories.
|
||||
cargoShortTarget =
|
||||
removeSuffix ".json" (baseNameOf "${final.rust.rustcTargetSpec}");
|
||||
emulator =
|
||||
pkgs:
|
||||
if (final.emulatorAvailable pkgs) then
|
||||
selectEmulator pkgs
|
||||
else
|
||||
throw "Don't know how to run ${final.config} executables.";
|
||||
|
||||
# When used as part of an environment variable name, triples are
|
||||
# uppercased and have all hyphens replaced by underscores:
|
||||
#
|
||||
# https://github.com/rust-lang/cargo/pull/9169
|
||||
# https://github.com/rust-lang/cargo/issues/8285#issuecomment-634202431
|
||||
cargoEnvVarTarget =
|
||||
replaceStrings ["-"] ["_"]
|
||||
(toUpper final.rust.cargoShortTarget);
|
||||
}
|
||||
)
|
||||
// mapAttrs (n: v: v final.parsed) inspect.predicates
|
||||
// mapAttrs (n: v: v final.gcc.arch or "default") architectures.predicates
|
||||
// args
|
||||
// {
|
||||
rust = rust // {
|
||||
# Once args.rustc.platform.target-family is deprecated and
|
||||
# removed, there will no longer be any need to modify any
|
||||
# values from args.rust.platform, so we can drop all the
|
||||
# "args ? rust" etc. checks, and merge args.rust.platform in
|
||||
# /after/.
|
||||
platform = rust.platform or { } // {
|
||||
# https://doc.rust-lang.org/reference/conditional-compilation.html#target_arch
|
||||
arch =
|
||||
if rust ? platform then
|
||||
rust.platform.arch
|
||||
else if final.isAarch32 then
|
||||
"arm"
|
||||
else if final.isMips64 then
|
||||
"mips64" # never add "el" suffix
|
||||
else if final.isPower64 then
|
||||
"powerpc64" # never add "le" suffix
|
||||
else
|
||||
final.parsed.cpu.name;
|
||||
|
||||
# True if the target is no_std
|
||||
# https://github.com/rust-lang/rust/blob/2e44c17c12cec45b6a682b1e53a04ac5b5fcc9d2/src/bootstrap/config.rs#L415-L421
|
||||
isNoStdTarget =
|
||||
any (t: hasInfix t final.rust.rustcTarget) ["-none" "nvptx" "switch" "-uefi"];
|
||||
# https://doc.rust-lang.org/reference/conditional-compilation.html#target_os
|
||||
os =
|
||||
if rust ? platform then
|
||||
rust.platform.os or "none"
|
||||
else if final.isDarwin then
|
||||
"macos"
|
||||
else if final.isWasm && !final.isWasi then
|
||||
"unknown" # Needed for {wasm32,wasm64}-unknown-unknown.
|
||||
else
|
||||
final.parsed.kernel.name;
|
||||
|
||||
# https://doc.rust-lang.org/reference/conditional-compilation.html#target_family
|
||||
target-family =
|
||||
if args ? rust.platform.target-family then
|
||||
args.rust.platform.target-family
|
||||
else if args ? rustc.platform.target-family then
|
||||
(
|
||||
# Since https://github.com/rust-lang/rust/pull/84072
|
||||
# `target-family` is a list instead of single value.
|
||||
let
|
||||
f = args.rustc.platform.target-family;
|
||||
in
|
||||
if isList f then f else [ f ]
|
||||
)
|
||||
else
|
||||
optional final.isUnix "unix" ++ optional final.isWindows "windows" ++ optional final.isWasm "wasm";
|
||||
|
||||
# https://doc.rust-lang.org/reference/conditional-compilation.html#target_vendor
|
||||
vendor =
|
||||
let
|
||||
inherit (final.parsed) vendor;
|
||||
in
|
||||
rust.platform.vendor or {
|
||||
"w64" = "pc";
|
||||
}
|
||||
.${vendor.name} or vendor.name;
|
||||
};
|
||||
|
||||
# The name of the rust target, even if it is custom. Adjustments are
|
||||
# because rust has slightly different naming conventions than we do.
|
||||
rustcTarget =
|
||||
let
|
||||
inherit (final.parsed) cpu kernel abi;
|
||||
cpu_ =
|
||||
rust.platform.arch or {
|
||||
"armv7a" = "armv7";
|
||||
"armv7l" = "armv7";
|
||||
"armv6l" = "arm";
|
||||
"armv5tel" = "armv5te";
|
||||
"riscv32" = "riscv32gc";
|
||||
"riscv64" = "riscv64gc";
|
||||
}
|
||||
.${cpu.name} or cpu.name;
|
||||
vendor_ = final.rust.platform.vendor;
|
||||
# TODO: deprecate args.rustc in favour of args.rust after 23.05 is EOL.
|
||||
in
|
||||
args.rust.rustcTarget or args.rustc.config or (
|
||||
# Rust uses `wasm32-wasip?` rather than `wasm32-unknown-wasi`.
|
||||
# We cannot know which subversion does the user want, and
|
||||
# currently use WASI 0.1 as default for compatibility. Custom
|
||||
# users can set `rust.rustcTarget` to override it.
|
||||
if final.isWasi then
|
||||
"${cpu_}-wasip1"
|
||||
else
|
||||
"${cpu_}-${vendor_}-${kernel.name}${optionalString (abi.name != "unknown") "-${abi.name}"}"
|
||||
);
|
||||
|
||||
# The name of the rust target if it is standard, or the json file
|
||||
# containing the custom target spec.
|
||||
rustcTargetSpec =
|
||||
rust.rustcTargetSpec or (
|
||||
if rust ? platform then
|
||||
builtins.toFile (final.rust.rustcTarget + ".json") (toJSON rust.platform)
|
||||
else
|
||||
final.rust.rustcTarget
|
||||
);
|
||||
|
||||
# The name of the rust target if it is standard, or the
|
||||
# basename of the file containing the custom target spec,
|
||||
# without the .json extension.
|
||||
#
|
||||
# This is the name used by Cargo for target subdirectories.
|
||||
cargoShortTarget = removeSuffix ".json" (baseNameOf "${final.rust.rustcTargetSpec}");
|
||||
|
||||
# When used as part of an environment variable name, triples are
|
||||
# uppercased and have all hyphens replaced by underscores:
|
||||
#
|
||||
# https://github.com/rust-lang/cargo/pull/9169
|
||||
# https://github.com/rust-lang/cargo/issues/8285#issuecomment-634202431
|
||||
cargoEnvVarTarget = replaceStrings [ "-" ] [ "_" ] (toUpper final.rust.cargoShortTarget);
|
||||
|
||||
# True if the target is no_std
|
||||
# https://github.com/rust-lang/rust/blob/2e44c17c12cec45b6a682b1e53a04ac5b5fcc9d2/src/bootstrap/config.rs#L415-L421
|
||||
isNoStdTarget = any (t: hasInfix t final.rust.rustcTarget) [
|
||||
"-none"
|
||||
"nvptx"
|
||||
"switch"
|
||||
"-uefi"
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
in assert final.useAndroidPrebuilt -> final.isAndroid;
|
||||
assert foldl
|
||||
(pass: { assertion, message }:
|
||||
if assertion final
|
||||
then pass
|
||||
else throw message)
|
||||
true
|
||||
(final.parsed.abi.assertions or []);
|
||||
in
|
||||
assert final.useAndroidPrebuilt -> final.isAndroid;
|
||||
assert foldl (pass: { assertion, message }: if assertion final then pass else throw message) true (
|
||||
final.parsed.abi.assertions or [ ]
|
||||
);
|
||||
final;
|
||||
|
||||
in
|
||||
|
|
|
@ -7,16 +7,23 @@ let
|
|||
|
||||
all = [
|
||||
# Cygwin
|
||||
"i686-cygwin" "x86_64-cygwin"
|
||||
"i686-cygwin"
|
||||
"x86_64-cygwin"
|
||||
|
||||
# Darwin
|
||||
"x86_64-darwin" "i686-darwin" "aarch64-darwin" "armv7a-darwin"
|
||||
"x86_64-darwin"
|
||||
"i686-darwin"
|
||||
"aarch64-darwin"
|
||||
"armv7a-darwin"
|
||||
|
||||
# FreeBSD
|
||||
"i686-freebsd" "x86_64-freebsd"
|
||||
"i686-freebsd"
|
||||
"x86_64-freebsd"
|
||||
|
||||
# Genode
|
||||
"aarch64-genode" "i686-genode" "x86_64-genode"
|
||||
"aarch64-genode"
|
||||
"i686-genode"
|
||||
"x86_64-genode"
|
||||
|
||||
# illumos
|
||||
"x86_64-solaris"
|
||||
|
@ -25,94 +32,163 @@ let
|
|||
"javascript-ghcjs"
|
||||
|
||||
# Linux
|
||||
"aarch64-linux" "armv5tel-linux" "armv6l-linux" "armv7a-linux"
|
||||
"armv7l-linux" "i686-linux" "loongarch64-linux" "m68k-linux" "microblaze-linux"
|
||||
"microblazeel-linux" "mips-linux" "mips64-linux" "mips64el-linux"
|
||||
"mipsel-linux" "powerpc64-linux" "powerpc64le-linux" "riscv32-linux"
|
||||
"riscv64-linux" "s390-linux" "s390x-linux" "x86_64-linux"
|
||||
"aarch64-linux"
|
||||
"armv5tel-linux"
|
||||
"armv6l-linux"
|
||||
"armv7a-linux"
|
||||
"armv7l-linux"
|
||||
"i686-linux"
|
||||
"loongarch64-linux"
|
||||
"m68k-linux"
|
||||
"microblaze-linux"
|
||||
"microblazeel-linux"
|
||||
"mips-linux"
|
||||
"mips64-linux"
|
||||
"mips64el-linux"
|
||||
"mipsel-linux"
|
||||
"powerpc64-linux"
|
||||
"powerpc64le-linux"
|
||||
"riscv32-linux"
|
||||
"riscv64-linux"
|
||||
"s390-linux"
|
||||
"s390x-linux"
|
||||
"x86_64-linux"
|
||||
|
||||
# MMIXware
|
||||
"mmix-mmixware"
|
||||
|
||||
# NetBSD
|
||||
"aarch64-netbsd" "armv6l-netbsd" "armv7a-netbsd" "armv7l-netbsd"
|
||||
"i686-netbsd" "m68k-netbsd" "mipsel-netbsd" "powerpc-netbsd"
|
||||
"riscv32-netbsd" "riscv64-netbsd" "x86_64-netbsd"
|
||||
"aarch64-netbsd"
|
||||
"armv6l-netbsd"
|
||||
"armv7a-netbsd"
|
||||
"armv7l-netbsd"
|
||||
"i686-netbsd"
|
||||
"m68k-netbsd"
|
||||
"mipsel-netbsd"
|
||||
"powerpc-netbsd"
|
||||
"riscv32-netbsd"
|
||||
"riscv64-netbsd"
|
||||
"x86_64-netbsd"
|
||||
|
||||
# none
|
||||
"aarch64_be-none" "aarch64-none" "arm-none" "armv6l-none" "avr-none" "i686-none"
|
||||
"microblaze-none" "microblazeel-none" "mips-none" "mips64-none" "msp430-none" "or1k-none" "m68k-none"
|
||||
"powerpc-none" "powerpcle-none" "riscv32-none" "riscv64-none" "rx-none"
|
||||
"s390-none" "s390x-none" "vc4-none" "x86_64-none"
|
||||
"aarch64_be-none"
|
||||
"aarch64-none"
|
||||
"arm-none"
|
||||
"armv6l-none"
|
||||
"avr-none"
|
||||
"i686-none"
|
||||
"microblaze-none"
|
||||
"microblazeel-none"
|
||||
"mips-none"
|
||||
"mips64-none"
|
||||
"msp430-none"
|
||||
"or1k-none"
|
||||
"m68k-none"
|
||||
"powerpc-none"
|
||||
"powerpcle-none"
|
||||
"riscv32-none"
|
||||
"riscv64-none"
|
||||
"rx-none"
|
||||
"s390-none"
|
||||
"s390x-none"
|
||||
"vc4-none"
|
||||
"x86_64-none"
|
||||
|
||||
# OpenBSD
|
||||
"i686-openbsd" "x86_64-openbsd"
|
||||
"i686-openbsd"
|
||||
"x86_64-openbsd"
|
||||
|
||||
# Redox
|
||||
"x86_64-redox"
|
||||
|
||||
# WASI
|
||||
"wasm64-wasi" "wasm32-wasi"
|
||||
"wasm64-wasi"
|
||||
"wasm32-wasi"
|
||||
|
||||
# Windows
|
||||
"aarch64-windows" "x86_64-windows" "i686-windows"
|
||||
"aarch64-windows"
|
||||
"x86_64-windows"
|
||||
"i686-windows"
|
||||
];
|
||||
|
||||
allParsed = map parse.mkSystemFromString all;
|
||||
|
||||
filterDoubles = f: map parse.doubleFromSystem (lists.filter f allParsed);
|
||||
|
||||
in {
|
||||
in
|
||||
{
|
||||
inherit all;
|
||||
|
||||
none = [];
|
||||
none = [ ];
|
||||
|
||||
arm = filterDoubles predicates.isAarch32;
|
||||
armv7 = filterDoubles predicates.isArmv7;
|
||||
aarch = filterDoubles predicates.isAarch;
|
||||
aarch64 = filterDoubles predicates.isAarch64;
|
||||
x86 = filterDoubles predicates.isx86;
|
||||
i686 = filterDoubles predicates.isi686;
|
||||
x86_64 = filterDoubles predicates.isx86_64;
|
||||
microblaze = filterDoubles predicates.isMicroBlaze;
|
||||
mips = filterDoubles predicates.isMips;
|
||||
mmix = filterDoubles predicates.isMmix;
|
||||
power = filterDoubles predicates.isPower;
|
||||
riscv = filterDoubles predicates.isRiscV;
|
||||
riscv32 = filterDoubles predicates.isRiscV32;
|
||||
riscv64 = filterDoubles predicates.isRiscV64;
|
||||
rx = filterDoubles predicates.isRx;
|
||||
vc4 = filterDoubles predicates.isVc4;
|
||||
or1k = filterDoubles predicates.isOr1k;
|
||||
m68k = filterDoubles predicates.isM68k;
|
||||
s390 = filterDoubles predicates.isS390;
|
||||
s390x = filterDoubles predicates.isS390x;
|
||||
loongarch64 = filterDoubles predicates.isLoongArch64;
|
||||
js = filterDoubles predicates.isJavaScript;
|
||||
arm = filterDoubles predicates.isAarch32;
|
||||
armv7 = filterDoubles predicates.isArmv7;
|
||||
aarch = filterDoubles predicates.isAarch;
|
||||
aarch64 = filterDoubles predicates.isAarch64;
|
||||
x86 = filterDoubles predicates.isx86;
|
||||
i686 = filterDoubles predicates.isi686;
|
||||
x86_64 = filterDoubles predicates.isx86_64;
|
||||
microblaze = filterDoubles predicates.isMicroBlaze;
|
||||
mips = filterDoubles predicates.isMips;
|
||||
mmix = filterDoubles predicates.isMmix;
|
||||
power = filterDoubles predicates.isPower;
|
||||
riscv = filterDoubles predicates.isRiscV;
|
||||
riscv32 = filterDoubles predicates.isRiscV32;
|
||||
riscv64 = filterDoubles predicates.isRiscV64;
|
||||
rx = filterDoubles predicates.isRx;
|
||||
vc4 = filterDoubles predicates.isVc4;
|
||||
or1k = filterDoubles predicates.isOr1k;
|
||||
m68k = filterDoubles predicates.isM68k;
|
||||
s390 = filterDoubles predicates.isS390;
|
||||
s390x = filterDoubles predicates.isS390x;
|
||||
loongarch64 = filterDoubles predicates.isLoongArch64;
|
||||
js = filterDoubles predicates.isJavaScript;
|
||||
|
||||
bigEndian = filterDoubles predicates.isBigEndian;
|
||||
littleEndian = filterDoubles predicates.isLittleEndian;
|
||||
bigEndian = filterDoubles predicates.isBigEndian;
|
||||
littleEndian = filterDoubles predicates.isLittleEndian;
|
||||
|
||||
cygwin = filterDoubles predicates.isCygwin;
|
||||
darwin = filterDoubles predicates.isDarwin;
|
||||
freebsd = filterDoubles predicates.isFreeBSD;
|
||||
cygwin = filterDoubles predicates.isCygwin;
|
||||
darwin = filterDoubles predicates.isDarwin;
|
||||
freebsd = filterDoubles predicates.isFreeBSD;
|
||||
# Should be better, but MinGW is unclear.
|
||||
gnu = filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.gnu; })
|
||||
++ filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.gnueabi; })
|
||||
++ filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.gnueabihf; })
|
||||
++ filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.gnuabin32; })
|
||||
++ filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.gnuabi64; })
|
||||
++ filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.gnuabielfv1; })
|
||||
++ filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.gnuabielfv2; });
|
||||
illumos = filterDoubles predicates.isSunOS;
|
||||
linux = filterDoubles predicates.isLinux;
|
||||
netbsd = filterDoubles predicates.isNetBSD;
|
||||
openbsd = filterDoubles predicates.isOpenBSD;
|
||||
unix = filterDoubles predicates.isUnix;
|
||||
wasi = filterDoubles predicates.isWasi;
|
||||
redox = filterDoubles predicates.isRedox;
|
||||
windows = filterDoubles predicates.isWindows;
|
||||
genode = filterDoubles predicates.isGenode;
|
||||
gnu =
|
||||
filterDoubles (matchAttrs {
|
||||
kernel = parse.kernels.linux;
|
||||
abi = parse.abis.gnu;
|
||||
})
|
||||
++ filterDoubles (matchAttrs {
|
||||
kernel = parse.kernels.linux;
|
||||
abi = parse.abis.gnueabi;
|
||||
})
|
||||
++ filterDoubles (matchAttrs {
|
||||
kernel = parse.kernels.linux;
|
||||
abi = parse.abis.gnueabihf;
|
||||
})
|
||||
++ filterDoubles (matchAttrs {
|
||||
kernel = parse.kernels.linux;
|
||||
abi = parse.abis.gnuabin32;
|
||||
})
|
||||
++ filterDoubles (matchAttrs {
|
||||
kernel = parse.kernels.linux;
|
||||
abi = parse.abis.gnuabi64;
|
||||
})
|
||||
++ filterDoubles (matchAttrs {
|
||||
kernel = parse.kernels.linux;
|
||||
abi = parse.abis.gnuabielfv1;
|
||||
})
|
||||
++ filterDoubles (matchAttrs {
|
||||
kernel = parse.kernels.linux;
|
||||
abi = parse.abis.gnuabielfv2;
|
||||
});
|
||||
illumos = filterDoubles predicates.isSunOS;
|
||||
linux = filterDoubles predicates.isLinux;
|
||||
netbsd = filterDoubles predicates.isNetBSD;
|
||||
openbsd = filterDoubles predicates.isOpenBSD;
|
||||
unix = filterDoubles predicates.isUnix;
|
||||
wasi = filterDoubles predicates.isWasi;
|
||||
redox = filterDoubles predicates.isRedox;
|
||||
windows = filterDoubles predicates.isWindows;
|
||||
genode = filterDoubles predicates.isGenode;
|
||||
|
||||
embedded = filterDoubles predicates.isNone;
|
||||
embedded = filterDoubles predicates.isNone;
|
||||
}
|
||||
|
|
|
@ -26,7 +26,9 @@ rec {
|
|||
};
|
||||
ppc64-musl = {
|
||||
config = "powerpc64-unknown-linux-musl";
|
||||
gcc = { abi = "elfv2"; };
|
||||
gcc = {
|
||||
abi = "elfv2";
|
||||
};
|
||||
};
|
||||
|
||||
sheevaplug = {
|
||||
|
@ -95,16 +97,28 @@ rec {
|
|||
} // platforms.fuloong2f_n32;
|
||||
|
||||
# can execute on 32bit chip
|
||||
mips-linux-gnu = { config = "mips-unknown-linux-gnu"; } // platforms.gcc_mips32r2_o32;
|
||||
mipsel-linux-gnu = { config = "mipsel-unknown-linux-gnu"; } // platforms.gcc_mips32r2_o32;
|
||||
mips-linux-gnu = {
|
||||
config = "mips-unknown-linux-gnu";
|
||||
} // platforms.gcc_mips32r2_o32;
|
||||
mipsel-linux-gnu = {
|
||||
config = "mipsel-unknown-linux-gnu";
|
||||
} // platforms.gcc_mips32r2_o32;
|
||||
|
||||
# require 64bit chip (for more registers, 64-bit floating point, 64-bit "long long") but use 32bit pointers
|
||||
mips64-linux-gnuabin32 = { config = "mips64-unknown-linux-gnuabin32"; } // platforms.gcc_mips64r2_n32;
|
||||
mips64el-linux-gnuabin32 = { config = "mips64el-unknown-linux-gnuabin32"; } // platforms.gcc_mips64r2_n32;
|
||||
mips64-linux-gnuabin32 = {
|
||||
config = "mips64-unknown-linux-gnuabin32";
|
||||
} // platforms.gcc_mips64r2_n32;
|
||||
mips64el-linux-gnuabin32 = {
|
||||
config = "mips64el-unknown-linux-gnuabin32";
|
||||
} // platforms.gcc_mips64r2_n32;
|
||||
|
||||
# 64bit pointers
|
||||
mips64-linux-gnuabi64 = { config = "mips64-unknown-linux-gnuabi64"; } // platforms.gcc_mips64r2_64;
|
||||
mips64el-linux-gnuabi64 = { config = "mips64el-unknown-linux-gnuabi64"; } // platforms.gcc_mips64r2_64;
|
||||
mips64-linux-gnuabi64 = {
|
||||
config = "mips64-unknown-linux-gnuabi64";
|
||||
} // platforms.gcc_mips64r2_64;
|
||||
mips64el-linux-gnuabi64 = {
|
||||
config = "mips64el-unknown-linux-gnuabi64";
|
||||
} // platforms.gcc_mips64r2_64;
|
||||
|
||||
muslpi = raspberryPi // {
|
||||
config = "armv6l-unknown-linux-musleabihf";
|
||||
|
@ -114,12 +128,20 @@ rec {
|
|||
config = "aarch64-unknown-linux-musl";
|
||||
};
|
||||
|
||||
gnu64 = { config = "x86_64-unknown-linux-gnu"; };
|
||||
gnu64 = {
|
||||
config = "x86_64-unknown-linux-gnu";
|
||||
};
|
||||
gnu64_simplekernel = gnu64 // platforms.pc_simplekernel; # see test/cross/default.nix
|
||||
gnu32 = { config = "i686-unknown-linux-gnu"; };
|
||||
gnu32 = {
|
||||
config = "i686-unknown-linux-gnu";
|
||||
};
|
||||
|
||||
musl64 = { config = "x86_64-unknown-linux-musl"; };
|
||||
musl32 = { config = "i686-unknown-linux-musl"; };
|
||||
musl64 = {
|
||||
config = "x86_64-unknown-linux-musl";
|
||||
};
|
||||
musl32 = {
|
||||
config = "i686-unknown-linux-musl";
|
||||
};
|
||||
|
||||
riscv64 = riscv "64";
|
||||
riscv32 = riscv "32";
|
||||
|
@ -294,13 +316,13 @@ rec {
|
|||
aarch64-darwin = {
|
||||
config = "aarch64-apple-darwin";
|
||||
xcodePlatform = "MacOSX";
|
||||
platform = {};
|
||||
platform = { };
|
||||
};
|
||||
|
||||
x86_64-darwin = {
|
||||
config = "x86_64-apple-darwin";
|
||||
xcodePlatform = "MacOSX";
|
||||
platform = {};
|
||||
platform = { };
|
||||
};
|
||||
|
||||
#
|
||||
|
|
|
@ -38,123 +38,428 @@ rec {
|
|||
# `lib.attrsets.matchAttrs`, which requires a match on *all* attributes of
|
||||
# the product.
|
||||
|
||||
isi686 = { cpu = cpuTypes.i686; };
|
||||
isx86_32 = { cpu = { family = "x86"; bits = 32; }; };
|
||||
isx86_64 = { cpu = { family = "x86"; bits = 64; }; };
|
||||
isPower = { cpu = { family = "power"; }; };
|
||||
isPower64 = { cpu = { family = "power"; bits = 64; }; };
|
||||
isi686 = {
|
||||
cpu = cpuTypes.i686;
|
||||
};
|
||||
isx86_32 = {
|
||||
cpu = {
|
||||
family = "x86";
|
||||
bits = 32;
|
||||
};
|
||||
};
|
||||
isx86_64 = {
|
||||
cpu = {
|
||||
family = "x86";
|
||||
bits = 64;
|
||||
};
|
||||
};
|
||||
isPower = {
|
||||
cpu = {
|
||||
family = "power";
|
||||
};
|
||||
};
|
||||
isPower64 = {
|
||||
cpu = {
|
||||
family = "power";
|
||||
bits = 64;
|
||||
};
|
||||
};
|
||||
# This ABI is the default in NixOS PowerPC64 BE, but not on mainline GCC,
|
||||
# so it sometimes causes issues in certain packages that makes the wrong
|
||||
# assumption on the used ABI.
|
||||
isAbiElfv2 = [
|
||||
{ abi = { abi = "elfv2"; }; }
|
||||
{ abi = { name = "musl"; }; cpu = { family = "power"; bits = 64; }; }
|
||||
{
|
||||
abi = {
|
||||
abi = "elfv2";
|
||||
};
|
||||
}
|
||||
{
|
||||
abi = {
|
||||
name = "musl";
|
||||
};
|
||||
cpu = {
|
||||
family = "power";
|
||||
bits = 64;
|
||||
};
|
||||
}
|
||||
];
|
||||
isx86 = { cpu = { family = "x86"; }; };
|
||||
isAarch32 = { cpu = { family = "arm"; bits = 32; }; };
|
||||
isArmv7 = map ({ arch, ... }: { cpu = { inherit arch; }; })
|
||||
(filter (cpu: hasPrefix "armv7" cpu.arch or "")
|
||||
(attrValues cpuTypes));
|
||||
isAarch64 = { cpu = { family = "arm"; bits = 64; }; };
|
||||
isAarch = { cpu = { family = "arm"; }; };
|
||||
isMicroBlaze = { cpu = { family = "microblaze"; }; };
|
||||
isMips = { cpu = { family = "mips"; }; };
|
||||
isMips32 = { cpu = { family = "mips"; bits = 32; }; };
|
||||
isMips64 = { cpu = { family = "mips"; bits = 64; }; };
|
||||
isMips64n32 = { cpu = { family = "mips"; bits = 64; }; abi = { abi = "n32"; }; };
|
||||
isMips64n64 = { cpu = { family = "mips"; bits = 64; }; abi = { abi = "64"; }; };
|
||||
isMmix = { cpu = { family = "mmix"; }; };
|
||||
isRiscV = { cpu = { family = "riscv"; }; };
|
||||
isRiscV32 = { cpu = { family = "riscv"; bits = 32; }; };
|
||||
isRiscV64 = { cpu = { family = "riscv"; bits = 64; }; };
|
||||
isRx = { cpu = { family = "rx"; }; };
|
||||
isSparc = { cpu = { family = "sparc"; }; };
|
||||
isSparc64 = { cpu = { family = "sparc"; bits = 64; }; };
|
||||
isWasm = { cpu = { family = "wasm"; }; };
|
||||
isMsp430 = { cpu = { family = "msp430"; }; };
|
||||
isVc4 = { cpu = { family = "vc4"; }; };
|
||||
isAvr = { cpu = { family = "avr"; }; };
|
||||
isAlpha = { cpu = { family = "alpha"; }; };
|
||||
isOr1k = { cpu = { family = "or1k"; }; };
|
||||
isM68k = { cpu = { family = "m68k"; }; };
|
||||
isS390 = { cpu = { family = "s390"; }; };
|
||||
isS390x = { cpu = { family = "s390"; bits = 64; }; };
|
||||
isLoongArch64 = { cpu = { family = "loongarch"; bits = 64; }; };
|
||||
isJavaScript = { cpu = cpuTypes.javascript; };
|
||||
isx86 = {
|
||||
cpu = {
|
||||
family = "x86";
|
||||
};
|
||||
};
|
||||
isAarch32 = {
|
||||
cpu = {
|
||||
family = "arm";
|
||||
bits = 32;
|
||||
};
|
||||
};
|
||||
isArmv7 = map (
|
||||
{ arch, ... }:
|
||||
{
|
||||
cpu = { inherit arch; };
|
||||
}
|
||||
) (filter (cpu: hasPrefix "armv7" cpu.arch or "") (attrValues cpuTypes));
|
||||
isAarch64 = {
|
||||
cpu = {
|
||||
family = "arm";
|
||||
bits = 64;
|
||||
};
|
||||
};
|
||||
isAarch = {
|
||||
cpu = {
|
||||
family = "arm";
|
||||
};
|
||||
};
|
||||
isMicroBlaze = {
|
||||
cpu = {
|
||||
family = "microblaze";
|
||||
};
|
||||
};
|
||||
isMips = {
|
||||
cpu = {
|
||||
family = "mips";
|
||||
};
|
||||
};
|
||||
isMips32 = {
|
||||
cpu = {
|
||||
family = "mips";
|
||||
bits = 32;
|
||||
};
|
||||
};
|
||||
isMips64 = {
|
||||
cpu = {
|
||||
family = "mips";
|
||||
bits = 64;
|
||||
};
|
||||
};
|
||||
isMips64n32 = {
|
||||
cpu = {
|
||||
family = "mips";
|
||||
bits = 64;
|
||||
};
|
||||
abi = {
|
||||
abi = "n32";
|
||||
};
|
||||
};
|
||||
isMips64n64 = {
|
||||
cpu = {
|
||||
family = "mips";
|
||||
bits = 64;
|
||||
};
|
||||
abi = {
|
||||
abi = "64";
|
||||
};
|
||||
};
|
||||
isMmix = {
|
||||
cpu = {
|
||||
family = "mmix";
|
||||
};
|
||||
};
|
||||
isRiscV = {
|
||||
cpu = {
|
||||
family = "riscv";
|
||||
};
|
||||
};
|
||||
isRiscV32 = {
|
||||
cpu = {
|
||||
family = "riscv";
|
||||
bits = 32;
|
||||
};
|
||||
};
|
||||
isRiscV64 = {
|
||||
cpu = {
|
||||
family = "riscv";
|
||||
bits = 64;
|
||||
};
|
||||
};
|
||||
isRx = {
|
||||
cpu = {
|
||||
family = "rx";
|
||||
};
|
||||
};
|
||||
isSparc = {
|
||||
cpu = {
|
||||
family = "sparc";
|
||||
};
|
||||
};
|
||||
isSparc64 = {
|
||||
cpu = {
|
||||
family = "sparc";
|
||||
bits = 64;
|
||||
};
|
||||
};
|
||||
isWasm = {
|
||||
cpu = {
|
||||
family = "wasm";
|
||||
};
|
||||
};
|
||||
isMsp430 = {
|
||||
cpu = {
|
||||
family = "msp430";
|
||||
};
|
||||
};
|
||||
isVc4 = {
|
||||
cpu = {
|
||||
family = "vc4";
|
||||
};
|
||||
};
|
||||
isAvr = {
|
||||
cpu = {
|
||||
family = "avr";
|
||||
};
|
||||
};
|
||||
isAlpha = {
|
||||
cpu = {
|
||||
family = "alpha";
|
||||
};
|
||||
};
|
||||
isOr1k = {
|
||||
cpu = {
|
||||
family = "or1k";
|
||||
};
|
||||
};
|
||||
isM68k = {
|
||||
cpu = {
|
||||
family = "m68k";
|
||||
};
|
||||
};
|
||||
isS390 = {
|
||||
cpu = {
|
||||
family = "s390";
|
||||
};
|
||||
};
|
||||
isS390x = {
|
||||
cpu = {
|
||||
family = "s390";
|
||||
bits = 64;
|
||||
};
|
||||
};
|
||||
isLoongArch64 = {
|
||||
cpu = {
|
||||
family = "loongarch";
|
||||
bits = 64;
|
||||
};
|
||||
};
|
||||
isJavaScript = {
|
||||
cpu = cpuTypes.javascript;
|
||||
};
|
||||
|
||||
is32bit = { cpu = { bits = 32; }; };
|
||||
is64bit = { cpu = { bits = 64; }; };
|
||||
isILP32 = [ { cpu = { family = "wasm"; bits = 32; }; } ] ++
|
||||
map (a: { abi = { abi = a; }; }) [ "n32" "ilp32" "x32" ];
|
||||
isBigEndian = { cpu = { significantByte = significantBytes.bigEndian; }; };
|
||||
isLittleEndian = { cpu = { significantByte = significantBytes.littleEndian; }; };
|
||||
is32bit = {
|
||||
cpu = {
|
||||
bits = 32;
|
||||
};
|
||||
};
|
||||
is64bit = {
|
||||
cpu = {
|
||||
bits = 64;
|
||||
};
|
||||
};
|
||||
isILP32 =
|
||||
[
|
||||
{
|
||||
cpu = {
|
||||
family = "wasm";
|
||||
bits = 32;
|
||||
};
|
||||
}
|
||||
]
|
||||
++ map
|
||||
(a: {
|
||||
abi = {
|
||||
abi = a;
|
||||
};
|
||||
})
|
||||
[
|
||||
"n32"
|
||||
"ilp32"
|
||||
"x32"
|
||||
];
|
||||
isBigEndian = {
|
||||
cpu = {
|
||||
significantByte = significantBytes.bigEndian;
|
||||
};
|
||||
};
|
||||
isLittleEndian = {
|
||||
cpu = {
|
||||
significantByte = significantBytes.littleEndian;
|
||||
};
|
||||
};
|
||||
|
||||
isBSD = { kernel = { families = { inherit (kernelFamilies) bsd; }; }; };
|
||||
isDarwin = { kernel = { families = { inherit (kernelFamilies) darwin; }; }; };
|
||||
isUnix = [ isBSD isDarwin isLinux isSunOS isCygwin isRedox ];
|
||||
isBSD = {
|
||||
kernel = {
|
||||
families = { inherit (kernelFamilies) bsd; };
|
||||
};
|
||||
};
|
||||
isDarwin = {
|
||||
kernel = {
|
||||
families = { inherit (kernelFamilies) darwin; };
|
||||
};
|
||||
};
|
||||
isUnix = [
|
||||
isBSD
|
||||
isDarwin
|
||||
isLinux
|
||||
isSunOS
|
||||
isCygwin
|
||||
isRedox
|
||||
];
|
||||
|
||||
isMacOS = { kernel = kernels.macos; };
|
||||
isiOS = { kernel = kernels.ios; };
|
||||
isLinux = { kernel = kernels.linux; };
|
||||
isSunOS = { kernel = kernels.solaris; };
|
||||
isFreeBSD = { kernel = { name = "freebsd"; }; };
|
||||
isNetBSD = { kernel = kernels.netbsd; };
|
||||
isOpenBSD = { kernel = kernels.openbsd; };
|
||||
isWindows = { kernel = kernels.windows; };
|
||||
isCygwin = { kernel = kernels.windows; abi = abis.cygnus; };
|
||||
isMinGW = { kernel = kernels.windows; abi = abis.gnu; };
|
||||
isWasi = { kernel = kernels.wasi; };
|
||||
isRedox = { kernel = kernels.redox; };
|
||||
isGhcjs = { kernel = kernels.ghcjs; };
|
||||
isGenode = { kernel = kernels.genode; };
|
||||
isNone = { kernel = kernels.none; };
|
||||
isMacOS = {
|
||||
kernel = kernels.macos;
|
||||
};
|
||||
isiOS = {
|
||||
kernel = kernels.ios;
|
||||
};
|
||||
isLinux = {
|
||||
kernel = kernels.linux;
|
||||
};
|
||||
isSunOS = {
|
||||
kernel = kernels.solaris;
|
||||
};
|
||||
isFreeBSD = {
|
||||
kernel = {
|
||||
name = "freebsd";
|
||||
};
|
||||
};
|
||||
isNetBSD = {
|
||||
kernel = kernels.netbsd;
|
||||
};
|
||||
isOpenBSD = {
|
||||
kernel = kernels.openbsd;
|
||||
};
|
||||
isWindows = {
|
||||
kernel = kernels.windows;
|
||||
};
|
||||
isCygwin = {
|
||||
kernel = kernels.windows;
|
||||
abi = abis.cygnus;
|
||||
};
|
||||
isMinGW = {
|
||||
kernel = kernels.windows;
|
||||
abi = abis.gnu;
|
||||
};
|
||||
isWasi = {
|
||||
kernel = kernels.wasi;
|
||||
};
|
||||
isRedox = {
|
||||
kernel = kernels.redox;
|
||||
};
|
||||
isGhcjs = {
|
||||
kernel = kernels.ghcjs;
|
||||
};
|
||||
isGenode = {
|
||||
kernel = kernels.genode;
|
||||
};
|
||||
isNone = {
|
||||
kernel = kernels.none;
|
||||
};
|
||||
|
||||
isAndroid = [ { abi = abis.android; } { abi = abis.androideabi; } ];
|
||||
isGnu = with abis; map (a: { abi = a; }) [ gnuabi64 gnuabin32 gnu gnueabi gnueabihf gnuabielfv1 gnuabielfv2 ];
|
||||
isMusl = with abis; map (a: { abi = a; }) [ musl musleabi musleabihf muslabin32 muslabi64 ];
|
||||
isUClibc = with abis; map (a: { abi = a; }) [ uclibc uclibceabi uclibceabihf ];
|
||||
isAndroid = [
|
||||
{ abi = abis.android; }
|
||||
{ abi = abis.androideabi; }
|
||||
];
|
||||
isGnu =
|
||||
with abis;
|
||||
map (a: { abi = a; }) [
|
||||
gnuabi64
|
||||
gnuabin32
|
||||
gnu
|
||||
gnueabi
|
||||
gnueabihf
|
||||
gnuabielfv1
|
||||
gnuabielfv2
|
||||
];
|
||||
isMusl =
|
||||
with abis;
|
||||
map (a: { abi = a; }) [
|
||||
musl
|
||||
musleabi
|
||||
musleabihf
|
||||
muslabin32
|
||||
muslabi64
|
||||
];
|
||||
isUClibc =
|
||||
with abis;
|
||||
map (a: { abi = a; }) [
|
||||
uclibc
|
||||
uclibceabi
|
||||
uclibceabihf
|
||||
];
|
||||
|
||||
isEfi = [
|
||||
{ cpu = { family = "arm"; version = "6"; }; }
|
||||
{ cpu = { family = "arm"; version = "7"; }; }
|
||||
{ cpu = { family = "arm"; version = "8"; }; }
|
||||
{ cpu = { family = "riscv"; }; }
|
||||
{ cpu = { family = "x86"; }; }
|
||||
{
|
||||
cpu = {
|
||||
family = "arm";
|
||||
version = "6";
|
||||
};
|
||||
}
|
||||
{
|
||||
cpu = {
|
||||
family = "arm";
|
||||
version = "7";
|
||||
};
|
||||
}
|
||||
{
|
||||
cpu = {
|
||||
family = "arm";
|
||||
version = "8";
|
||||
};
|
||||
}
|
||||
{
|
||||
cpu = {
|
||||
family = "riscv";
|
||||
};
|
||||
}
|
||||
{
|
||||
cpu = {
|
||||
family = "x86";
|
||||
};
|
||||
}
|
||||
];
|
||||
|
||||
isElf = { kernel.execFormat = execFormats.elf; };
|
||||
isMacho = { kernel.execFormat = execFormats.macho; };
|
||||
isElf = {
|
||||
kernel.execFormat = execFormats.elf;
|
||||
};
|
||||
isMacho = {
|
||||
kernel.execFormat = execFormats.macho;
|
||||
};
|
||||
};
|
||||
|
||||
# given two patterns, return a pattern which is their logical AND.
|
||||
# Since a pattern is a list-of-disjuncts, this needs to
|
||||
patternLogicalAnd = pat1_: pat2_:
|
||||
patternLogicalAnd =
|
||||
pat1_: pat2_:
|
||||
let
|
||||
# patterns can be either a list or a (bare) singleton; turn
|
||||
# them into singletons for uniform handling
|
||||
pat1 = toList pat1_;
|
||||
pat2 = toList pat2_;
|
||||
in
|
||||
concatMap (attr1:
|
||||
map (attr2:
|
||||
recursiveUpdateUntil
|
||||
(path: subattr1: subattr2:
|
||||
if (builtins.intersectAttrs subattr1 subattr2) == {} || subattr1 == subattr2
|
||||
then true
|
||||
else throw ''
|
||||
pattern conflict at path ${toString path}:
|
||||
${toJSON subattr1}
|
||||
${toJSON subattr2}
|
||||
'')
|
||||
attr1
|
||||
attr2
|
||||
)
|
||||
pat2)
|
||||
pat1;
|
||||
concatMap (
|
||||
attr1:
|
||||
map (
|
||||
attr2:
|
||||
recursiveUpdateUntil (
|
||||
path: subattr1: subattr2:
|
||||
if (builtins.intersectAttrs subattr1 subattr2) == { } || subattr1 == subattr2 then
|
||||
true
|
||||
else
|
||||
throw ''
|
||||
pattern conflict at path ${toString path}:
|
||||
${toJSON subattr1}
|
||||
${toJSON subattr2}
|
||||
''
|
||||
) attr1 attr2
|
||||
) pat2
|
||||
) pat1;
|
||||
|
||||
matchAnyAttrs = patterns:
|
||||
if isList patterns then attrs: any (pattern: matchAttrs pattern attrs) patterns
|
||||
else matchAttrs patterns;
|
||||
matchAnyAttrs =
|
||||
patterns:
|
||||
if isList patterns then
|
||||
attrs: any (pattern: matchAttrs pattern attrs) patterns
|
||||
else
|
||||
matchAttrs patterns;
|
||||
|
||||
predicates = mapAttrs (_: matchAnyAttrs) patterns;
|
||||
|
||||
|
@ -163,7 +468,9 @@ rec {
|
|||
# that `lib.meta.availableOn` can distinguish them from the patterns which
|
||||
# apply only to the `parsed` field.
|
||||
|
||||
platformPatterns = mapAttrs (_: p: { parsed = {}; } // p) {
|
||||
isStatic = { isStatic = true; };
|
||||
platformPatterns = mapAttrs (_: p: { parsed = { }; } // p) {
|
||||
isStatic = {
|
||||
isStatic = true;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
2479
lib/tests/misc.nix
2479
lib/tests/misc.nix
File diff suppressed because it is too large
Load diff
|
@ -1,7 +1,8 @@
|
|||
{ lib, ... }:
|
||||
let
|
||||
inherit (builtins)
|
||||
storeDir;
|
||||
storeDir
|
||||
;
|
||||
inherit (lib)
|
||||
types
|
||||
mkOption
|
||||
|
|
|
@ -1,18 +1,24 @@
|
|||
{ # The pkgs used for dependencies for the testing itself
|
||||
{
|
||||
# The pkgs used for dependencies for the testing itself
|
||||
# Don't test properties of pkgs.lib, but rather the lib in the parent directory
|
||||
pkgs ? import ../.. {} // { lib = throw "pkgs.lib accessed, but the lib tests should use nixpkgs' lib path directly!"; },
|
||||
pkgs ? import ../.. { } // {
|
||||
lib = throw "pkgs.lib accessed, but the lib tests should use nixpkgs' lib path directly!";
|
||||
},
|
||||
nix ? pkgs-nixVersions.stable,
|
||||
nixVersions ? [ pkgs-nixVersions.minimum nix pkgs-nixVersions.latest ],
|
||||
nixVersions ? [
|
||||
pkgs-nixVersions.minimum
|
||||
nix
|
||||
pkgs-nixVersions.latest
|
||||
],
|
||||
pkgs-nixVersions ? import ./nix-for-tests.nix { inherit pkgs; },
|
||||
}:
|
||||
|
||||
let
|
||||
lib = import ../.;
|
||||
testWithNix = nix:
|
||||
import ./test-with-nix.nix { inherit lib nix pkgs; };
|
||||
testWithNix = nix: import ./test-with-nix.nix { inherit lib nix pkgs; };
|
||||
|
||||
in
|
||||
pkgs.symlinkJoin {
|
||||
name = "nixpkgs-lib-tests";
|
||||
paths = map testWithNix nixVersions;
|
||||
}
|
||||
pkgs.symlinkJoin {
|
||||
name = "nixpkgs-lib-tests";
|
||||
paths = map testWithNix nixVersions;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
let
|
||||
lib = import ../default.nix;
|
||||
mseteq = x: y: {
|
||||
expr = lib.sort lib.lessThan x;
|
||||
expr = lib.sort lib.lessThan x;
|
||||
expected = lib.sort lib.lessThan y;
|
||||
};
|
||||
|
||||
|
@ -20,87 +20,248 @@ let
|
|||
NOTE: This property is not guaranteed when `sys` was elaborated by a different
|
||||
version of Nixpkgs.
|
||||
*/
|
||||
toLosslessStringMaybe = sys:
|
||||
if lib.isString sys then sys
|
||||
else if lib.systems.equals sys (lib.systems.elaborate sys.system) then sys.system
|
||||
else null;
|
||||
toLosslessStringMaybe =
|
||||
sys:
|
||||
if lib.isString sys then
|
||||
sys
|
||||
else if lib.systems.equals sys (lib.systems.elaborate sys.system) then
|
||||
sys.system
|
||||
else
|
||||
null;
|
||||
|
||||
in
|
||||
lib.runTests (
|
||||
# We assert that the new algorithmic way of generating these lists matches the
|
||||
# way they were hard-coded before.
|
||||
#
|
||||
# One might think "if we exhaustively test, what's the point of procedurally
|
||||
# calculating the lists anyway?". The answer is one can mindlessly update these
|
||||
# tests as new platforms become supported, and then just give the diff a quick
|
||||
# sanity check before committing :).
|
||||
# We assert that the new algorithmic way of generating these lists matches the
|
||||
# way they were hard-coded before.
|
||||
#
|
||||
# One might think "if we exhaustively test, what's the point of procedurally
|
||||
# calculating the lists anyway?". The answer is one can mindlessly update these
|
||||
# tests as new platforms become supported, and then just give the diff a quick
|
||||
# sanity check before committing :).
|
||||
|
||||
(with lib.systems.doubles; {
|
||||
testall = mseteq all (linux ++ darwin ++ freebsd ++ openbsd ++ netbsd ++ illumos ++ wasi ++ windows ++ embedded ++ mmix ++ js ++ genode ++ redox);
|
||||
(with lib.systems.doubles; {
|
||||
testall = mseteq all (
|
||||
linux
|
||||
++ darwin
|
||||
++ freebsd
|
||||
++ openbsd
|
||||
++ netbsd
|
||||
++ illumos
|
||||
++ wasi
|
||||
++ windows
|
||||
++ embedded
|
||||
++ mmix
|
||||
++ js
|
||||
++ genode
|
||||
++ redox
|
||||
);
|
||||
|
||||
testarm = mseteq arm [ "armv5tel-linux" "armv6l-linux" "armv6l-netbsd" "armv6l-none" "armv7a-linux" "armv7a-netbsd" "armv7l-linux" "armv7l-netbsd" "arm-none" "armv7a-darwin" ];
|
||||
testarmv7 = mseteq armv7 [ "armv7a-darwin" "armv7a-linux" "armv7l-linux" "armv7a-netbsd" "armv7l-netbsd" ];
|
||||
testi686 = mseteq i686 [ "i686-linux" "i686-freebsd" "i686-genode" "i686-netbsd" "i686-openbsd" "i686-cygwin" "i686-windows" "i686-none" "i686-darwin" ];
|
||||
testmips = mseteq mips [ "mips-none" "mips64-none" "mips-linux" "mips64-linux" "mips64el-linux" "mipsel-linux" "mipsel-netbsd" ];
|
||||
testmmix = mseteq mmix [ "mmix-mmixware" ];
|
||||
testpower = mseteq power [ "powerpc-netbsd" "powerpc-none" "powerpc64-linux" "powerpc64le-linux" "powerpcle-none" ];
|
||||
testriscv = mseteq riscv [ "riscv32-linux" "riscv64-linux" "riscv32-netbsd" "riscv64-netbsd" "riscv32-none" "riscv64-none" ];
|
||||
testriscv32 = mseteq riscv32 [ "riscv32-linux" "riscv32-netbsd" "riscv32-none" ];
|
||||
testriscv64 = mseteq riscv64 [ "riscv64-linux" "riscv64-netbsd" "riscv64-none" ];
|
||||
tests390x = mseteq s390x [ "s390x-linux" "s390x-none" ];
|
||||
testx86_64 = mseteq x86_64 [ "x86_64-linux" "x86_64-darwin" "x86_64-freebsd" "x86_64-genode" "x86_64-redox" "x86_64-openbsd" "x86_64-netbsd" "x86_64-cygwin" "x86_64-solaris" "x86_64-windows" "x86_64-none" ];
|
||||
testarm = mseteq arm [
|
||||
"armv5tel-linux"
|
||||
"armv6l-linux"
|
||||
"armv6l-netbsd"
|
||||
"armv6l-none"
|
||||
"armv7a-linux"
|
||||
"armv7a-netbsd"
|
||||
"armv7l-linux"
|
||||
"armv7l-netbsd"
|
||||
"arm-none"
|
||||
"armv7a-darwin"
|
||||
];
|
||||
testarmv7 = mseteq armv7 [
|
||||
"armv7a-darwin"
|
||||
"armv7a-linux"
|
||||
"armv7l-linux"
|
||||
"armv7a-netbsd"
|
||||
"armv7l-netbsd"
|
||||
];
|
||||
testi686 = mseteq i686 [
|
||||
"i686-linux"
|
||||
"i686-freebsd"
|
||||
"i686-genode"
|
||||
"i686-netbsd"
|
||||
"i686-openbsd"
|
||||
"i686-cygwin"
|
||||
"i686-windows"
|
||||
"i686-none"
|
||||
"i686-darwin"
|
||||
];
|
||||
testmips = mseteq mips [
|
||||
"mips-none"
|
||||
"mips64-none"
|
||||
"mips-linux"
|
||||
"mips64-linux"
|
||||
"mips64el-linux"
|
||||
"mipsel-linux"
|
||||
"mipsel-netbsd"
|
||||
];
|
||||
testmmix = mseteq mmix [ "mmix-mmixware" ];
|
||||
testpower = mseteq power [
|
||||
"powerpc-netbsd"
|
||||
"powerpc-none"
|
||||
"powerpc64-linux"
|
||||
"powerpc64le-linux"
|
||||
"powerpcle-none"
|
||||
];
|
||||
testriscv = mseteq riscv [
|
||||
"riscv32-linux"
|
||||
"riscv64-linux"
|
||||
"riscv32-netbsd"
|
||||
"riscv64-netbsd"
|
||||
"riscv32-none"
|
||||
"riscv64-none"
|
||||
];
|
||||
testriscv32 = mseteq riscv32 [
|
||||
"riscv32-linux"
|
||||
"riscv32-netbsd"
|
||||
"riscv32-none"
|
||||
];
|
||||
testriscv64 = mseteq riscv64 [
|
||||
"riscv64-linux"
|
||||
"riscv64-netbsd"
|
||||
"riscv64-none"
|
||||
];
|
||||
tests390x = mseteq s390x [
|
||||
"s390x-linux"
|
||||
"s390x-none"
|
||||
];
|
||||
testx86_64 = mseteq x86_64 [
|
||||
"x86_64-linux"
|
||||
"x86_64-darwin"
|
||||
"x86_64-freebsd"
|
||||
"x86_64-genode"
|
||||
"x86_64-redox"
|
||||
"x86_64-openbsd"
|
||||
"x86_64-netbsd"
|
||||
"x86_64-cygwin"
|
||||
"x86_64-solaris"
|
||||
"x86_64-windows"
|
||||
"x86_64-none"
|
||||
];
|
||||
|
||||
testcygwin = mseteq cygwin [ "i686-cygwin" "x86_64-cygwin" ];
|
||||
testdarwin = mseteq darwin [ "x86_64-darwin" "i686-darwin" "aarch64-darwin" "armv7a-darwin" ];
|
||||
testfreebsd = mseteq freebsd [ "i686-freebsd" "x86_64-freebsd" ];
|
||||
testgenode = mseteq genode [ "aarch64-genode" "i686-genode" "x86_64-genode" ];
|
||||
testredox = mseteq redox [ "x86_64-redox" ];
|
||||
testgnu = mseteq gnu (linux /* ++ kfreebsd ++ ... */);
|
||||
testillumos = mseteq illumos [ "x86_64-solaris" ];
|
||||
testlinux = mseteq linux [ "aarch64-linux" "armv5tel-linux" "armv6l-linux" "armv7a-linux" "armv7l-linux" "i686-linux" "loongarch64-linux" "m68k-linux" "microblaze-linux" "microblazeel-linux" "mips-linux" "mips64-linux" "mips64el-linux" "mipsel-linux" "powerpc64-linux" "powerpc64le-linux" "riscv32-linux" "riscv64-linux" "s390-linux" "s390x-linux" "x86_64-linux" ];
|
||||
testnetbsd = mseteq netbsd [ "aarch64-netbsd" "armv6l-netbsd" "armv7a-netbsd" "armv7l-netbsd" "i686-netbsd" "m68k-netbsd" "mipsel-netbsd" "powerpc-netbsd" "riscv32-netbsd" "riscv64-netbsd" "x86_64-netbsd" ];
|
||||
testopenbsd = mseteq openbsd [ "i686-openbsd" "x86_64-openbsd" ];
|
||||
testwindows = mseteq windows [ "i686-cygwin" "x86_64-cygwin" "aarch64-windows" "i686-windows" "x86_64-windows" ];
|
||||
testunix = mseteq unix (linux ++ darwin ++ freebsd ++ openbsd ++ netbsd ++ illumos ++ cygwin ++ redox);
|
||||
})
|
||||
testcygwin = mseteq cygwin [
|
||||
"i686-cygwin"
|
||||
"x86_64-cygwin"
|
||||
];
|
||||
testdarwin = mseteq darwin [
|
||||
"x86_64-darwin"
|
||||
"i686-darwin"
|
||||
"aarch64-darwin"
|
||||
"armv7a-darwin"
|
||||
];
|
||||
testfreebsd = mseteq freebsd [
|
||||
"i686-freebsd"
|
||||
"x86_64-freebsd"
|
||||
];
|
||||
testgenode = mseteq genode [
|
||||
"aarch64-genode"
|
||||
"i686-genode"
|
||||
"x86_64-genode"
|
||||
];
|
||||
testredox = mseteq redox [ "x86_64-redox" ];
|
||||
testgnu = mseteq gnu (
|
||||
linux # ++ kfreebsd ++ ...
|
||||
);
|
||||
testillumos = mseteq illumos [ "x86_64-solaris" ];
|
||||
testlinux = mseteq linux [
|
||||
"aarch64-linux"
|
||||
"armv5tel-linux"
|
||||
"armv6l-linux"
|
||||
"armv7a-linux"
|
||||
"armv7l-linux"
|
||||
"i686-linux"
|
||||
"loongarch64-linux"
|
||||
"m68k-linux"
|
||||
"microblaze-linux"
|
||||
"microblazeel-linux"
|
||||
"mips-linux"
|
||||
"mips64-linux"
|
||||
"mips64el-linux"
|
||||
"mipsel-linux"
|
||||
"powerpc64-linux"
|
||||
"powerpc64le-linux"
|
||||
"riscv32-linux"
|
||||
"riscv64-linux"
|
||||
"s390-linux"
|
||||
"s390x-linux"
|
||||
"x86_64-linux"
|
||||
];
|
||||
testnetbsd = mseteq netbsd [
|
||||
"aarch64-netbsd"
|
||||
"armv6l-netbsd"
|
||||
"armv7a-netbsd"
|
||||
"armv7l-netbsd"
|
||||
"i686-netbsd"
|
||||
"m68k-netbsd"
|
||||
"mipsel-netbsd"
|
||||
"powerpc-netbsd"
|
||||
"riscv32-netbsd"
|
||||
"riscv64-netbsd"
|
||||
"x86_64-netbsd"
|
||||
];
|
||||
testopenbsd = mseteq openbsd [
|
||||
"i686-openbsd"
|
||||
"x86_64-openbsd"
|
||||
];
|
||||
testwindows = mseteq windows [
|
||||
"i686-cygwin"
|
||||
"x86_64-cygwin"
|
||||
"aarch64-windows"
|
||||
"i686-windows"
|
||||
"x86_64-windows"
|
||||
];
|
||||
testunix = mseteq unix (
|
||||
linux ++ darwin ++ freebsd ++ openbsd ++ netbsd ++ illumos ++ cygwin ++ redox
|
||||
);
|
||||
})
|
||||
|
||||
// {
|
||||
test_equals_example_x86_64-linux = {
|
||||
expr = lib.systems.equals (lib.systems.elaborate "x86_64-linux") (lib.systems.elaborate "x86_64-linux");
|
||||
expected = true;
|
||||
};
|
||||
|
||||
test_toLosslessStringMaybe_example_x86_64-linux = {
|
||||
expr = toLosslessStringMaybe (lib.systems.elaborate "x86_64-linux");
|
||||
expected = "x86_64-linux";
|
||||
};
|
||||
test_toLosslessStringMaybe_fail = {
|
||||
expr = toLosslessStringMaybe (lib.systems.elaborate "x86_64-linux" // { something = "extra"; });
|
||||
expected = null;
|
||||
};
|
||||
}
|
||||
|
||||
# Generate test cases to assert that a change in any non-function attribute makes a platform unequal
|
||||
// lib.concatMapAttrs (platformAttrName: origValue: {
|
||||
|
||||
${"test_equals_unequal_${platformAttrName}"} =
|
||||
let modified =
|
||||
assert origValue != arbitraryValue;
|
||||
lib.systems.elaborate "x86_64-linux" // { ${platformAttrName} = arbitraryValue; };
|
||||
arbitraryValue = x: "<<modified>>";
|
||||
in {
|
||||
expr = lib.systems.equals (lib.systems.elaborate "x86_64-linux") modified;
|
||||
expected = {
|
||||
# Changes in these attrs are not detectable because they're function.
|
||||
# The functions should be derived from the data, so this is not a problem.
|
||||
canExecute = null;
|
||||
emulator = null;
|
||||
emulatorAvailable = null;
|
||||
staticEmulatorAvailable = null;
|
||||
isCompatible = null;
|
||||
}?${platformAttrName};
|
||||
// {
|
||||
test_equals_example_x86_64-linux = {
|
||||
expr = lib.systems.equals (lib.systems.elaborate "x86_64-linux") (
|
||||
lib.systems.elaborate "x86_64-linux"
|
||||
);
|
||||
expected = true;
|
||||
};
|
||||
|
||||
}) (lib.systems.elaborate "x86_64-linux" /* arbitrary choice, just to get all the elaborated attrNames */)
|
||||
test_toLosslessStringMaybe_example_x86_64-linux = {
|
||||
expr = toLosslessStringMaybe (lib.systems.elaborate "x86_64-linux");
|
||||
expected = "x86_64-linux";
|
||||
};
|
||||
test_toLosslessStringMaybe_fail = {
|
||||
expr = toLosslessStringMaybe (lib.systems.elaborate "x86_64-linux" // { something = "extra"; });
|
||||
expected = null;
|
||||
};
|
||||
}
|
||||
|
||||
# Generate test cases to assert that a change in any non-function attribute makes a platform unequal
|
||||
//
|
||||
lib.concatMapAttrs
|
||||
(platformAttrName: origValue: {
|
||||
|
||||
${"test_equals_unequal_${platformAttrName}"} =
|
||||
let
|
||||
modified =
|
||||
assert origValue != arbitraryValue;
|
||||
lib.systems.elaborate "x86_64-linux" // { ${platformAttrName} = arbitraryValue; };
|
||||
arbitraryValue = x: "<<modified>>";
|
||||
in
|
||||
{
|
||||
expr = lib.systems.equals (lib.systems.elaborate "x86_64-linux") modified;
|
||||
expected =
|
||||
{
|
||||
# Changes in these attrs are not detectable because they're function.
|
||||
# The functions should be derived from the data, so this is not a problem.
|
||||
canExecute = null;
|
||||
emulator = null;
|
||||
emulatorAvailable = null;
|
||||
staticEmulatorAvailable = null;
|
||||
isCompatible = null;
|
||||
} ? ${platformAttrName};
|
||||
};
|
||||
|
||||
})
|
||||
(
|
||||
lib.systems.elaborate "x86_64-linux" # arbitrary choice, just to get all the elaborated attrNames
|
||||
)
|
||||
|
||||
)
|
||||
|
|
269
lib/trivial.nix
269
lib/trivial.nix
|
@ -11,11 +11,13 @@ let
|
|||
toBaseDigits
|
||||
version
|
||||
versionSuffix
|
||||
warn;
|
||||
warn
|
||||
;
|
||||
inherit (lib)
|
||||
isString
|
||||
;
|
||||
in {
|
||||
in
|
||||
{
|
||||
|
||||
## Simple (higher order) functions
|
||||
|
||||
|
@ -23,7 +25,6 @@ in {
|
|||
The identity function
|
||||
For when you need a function that does “nothing”.
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`x`
|
||||
|
@ -44,7 +45,6 @@ in {
|
|||
Ignores the second argument. If called with only one argument,
|
||||
constructs a function that always returns a static value.
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`x`
|
||||
|
@ -72,9 +72,7 @@ in {
|
|||
|
||||
:::
|
||||
*/
|
||||
const =
|
||||
x:
|
||||
y: x;
|
||||
const = x: y: x;
|
||||
|
||||
/**
|
||||
Pipes a value through a list of functions, left to right.
|
||||
|
@ -140,7 +138,6 @@ in {
|
|||
/**
|
||||
Concatenate two lists
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`x`
|
||||
|
@ -173,7 +170,6 @@ in {
|
|||
/**
|
||||
boolean “or”
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`x`
|
||||
|
@ -189,7 +185,6 @@ in {
|
|||
/**
|
||||
boolean “and”
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`x`
|
||||
|
@ -205,7 +200,6 @@ in {
|
|||
/**
|
||||
boolean “exclusive or”
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`x`
|
||||
|
@ -232,7 +226,6 @@ in {
|
|||
boolean values. Calling `toString` on a bool instead returns "1"
|
||||
and "" (sic!).
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`b`
|
||||
|
@ -252,7 +245,6 @@ in {
|
|||
|
||||
mergeAttrs :: attrs -> attrs -> attrs
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`x`
|
||||
|
@ -263,7 +255,6 @@ in {
|
|||
|
||||
: Right attribute set (higher precedence for equal keys)
|
||||
|
||||
|
||||
# Examples
|
||||
:::{.example}
|
||||
## `lib.trivial.mergeAttrs` usage example
|
||||
|
@ -275,14 +266,11 @@ in {
|
|||
|
||||
:::
|
||||
*/
|
||||
mergeAttrs =
|
||||
x:
|
||||
y: x // y;
|
||||
mergeAttrs = x: y: x // y;
|
||||
|
||||
/**
|
||||
Flip the order of the arguments of a binary function.
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`f`
|
||||
|
@ -314,12 +302,13 @@ in {
|
|||
|
||||
:::
|
||||
*/
|
||||
flip = f: a: b: f b a;
|
||||
flip =
|
||||
f: a: b:
|
||||
f b a;
|
||||
|
||||
/**
|
||||
Return `maybeValue` if not null, otherwise return `default`.
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`default`
|
||||
|
@ -330,7 +319,6 @@ in {
|
|||
|
||||
: 2\. Function argument
|
||||
|
||||
|
||||
# Examples
|
||||
:::{.example}
|
||||
## `lib.trivial.defaultTo` usage example
|
||||
|
@ -346,14 +334,11 @@ in {
|
|||
|
||||
:::
|
||||
*/
|
||||
defaultTo = default: maybeValue:
|
||||
if maybeValue != null then maybeValue
|
||||
else default;
|
||||
defaultTo = default: maybeValue: if maybeValue != null then maybeValue else default;
|
||||
|
||||
/**
|
||||
Apply function if the supplied argument is non-null.
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`f`
|
||||
|
@ -364,7 +349,6 @@ in {
|
|||
|
||||
: Argument to check for null before passing it to `f`
|
||||
|
||||
|
||||
# Examples
|
||||
:::{.example}
|
||||
## `lib.trivial.mapNullable` usage example
|
||||
|
@ -378,16 +362,25 @@ in {
|
|||
|
||||
:::
|
||||
*/
|
||||
mapNullable =
|
||||
f:
|
||||
a: if a == null then a else f a;
|
||||
mapNullable = f: a: if a == null then a else f a;
|
||||
|
||||
# Pull in some builtins not included elsewhere.
|
||||
inherit (builtins)
|
||||
pathExists readFile isBool
|
||||
isInt isFloat add sub lessThan
|
||||
seq deepSeq genericClosure
|
||||
bitAnd bitOr bitXor;
|
||||
pathExists
|
||||
readFile
|
||||
isBool
|
||||
isInt
|
||||
isFloat
|
||||
add
|
||||
sub
|
||||
lessThan
|
||||
seq
|
||||
deepSeq
|
||||
genericClosure
|
||||
bitAnd
|
||||
bitOr
|
||||
bitXor
|
||||
;
|
||||
|
||||
## nixpkgs version strings
|
||||
|
||||
|
@ -422,7 +415,6 @@ in {
|
|||
Whether a feature is supported in all supported releases (at the time of
|
||||
release branch-off, if applicable). See `oldestSupportedRelease`.
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`release`
|
||||
|
@ -433,15 +425,13 @@ in {
|
|||
isInOldestRelease =
|
||||
lib.warnIf (lib.oldestSupportedReleaseIsAtLeast 2411)
|
||||
"lib.isInOldestRelease is deprecated. Use lib.oldestSupportedReleaseIsAtLeast instead."
|
||||
lib.oldestSupportedReleaseIsAtLeast;
|
||||
lib.oldestSupportedReleaseIsAtLeast;
|
||||
|
||||
/**
|
||||
Alias for `isInOldestRelease` introduced in 24.11.
|
||||
Use `isInOldestRelease` in expressions outside of Nixpkgs for greater compatibility.
|
||||
*/
|
||||
oldestSupportedReleaseIsAtLeast =
|
||||
release:
|
||||
release <= lib.trivial.oldestSupportedRelease;
|
||||
*/
|
||||
oldestSupportedReleaseIsAtLeast = release: release <= lib.trivial.oldestSupportedRelease;
|
||||
|
||||
/**
|
||||
Returns the current nixpkgs release code name.
|
||||
|
@ -455,16 +445,15 @@ in {
|
|||
Returns the current nixpkgs version suffix as string.
|
||||
*/
|
||||
versionSuffix =
|
||||
let suffixFile = ../.version-suffix;
|
||||
in if pathExists suffixFile
|
||||
then lib.strings.fileContents suffixFile
|
||||
else "pre-git";
|
||||
let
|
||||
suffixFile = ../.version-suffix;
|
||||
in
|
||||
if pathExists suffixFile then lib.strings.fileContents suffixFile else "pre-git";
|
||||
|
||||
/**
|
||||
Attempts to return the the current revision of nixpkgs and
|
||||
returns the supplied default value otherwise.
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`default`
|
||||
|
@ -481,11 +470,14 @@ in {
|
|||
default:
|
||||
let
|
||||
revisionFile = "${toString ./..}/.git-revision";
|
||||
gitRepo = "${toString ./..}/.git";
|
||||
in if lib.pathIsGitRepo gitRepo
|
||||
then lib.commitIdFromGitRepo gitRepo
|
||||
else if lib.pathExists revisionFile then lib.fileContents revisionFile
|
||||
else default;
|
||||
gitRepo = "${toString ./..}/.git";
|
||||
in
|
||||
if lib.pathIsGitRepo gitRepo then
|
||||
lib.commitIdFromGitRepo gitRepo
|
||||
else if lib.pathExists revisionFile then
|
||||
lib.fileContents revisionFile
|
||||
else
|
||||
default;
|
||||
|
||||
nixpkgsVersion = warn "lib.nixpkgsVersion is a deprecated alias of lib.version." version;
|
||||
|
||||
|
@ -512,14 +504,13 @@ in {
|
|||
inPureEvalMode :: bool
|
||||
```
|
||||
*/
|
||||
inPureEvalMode = ! builtins ? currentSystem;
|
||||
inPureEvalMode = !builtins ? currentSystem;
|
||||
|
||||
## Integer operations
|
||||
|
||||
/**
|
||||
Return minimum of two numbers.
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`x`
|
||||
|
@ -535,7 +526,6 @@ in {
|
|||
/**
|
||||
Return maximum of two numbers.
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`x`
|
||||
|
@ -551,7 +541,6 @@ in {
|
|||
/**
|
||||
Integer modulus
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`base`
|
||||
|
@ -562,7 +551,6 @@ in {
|
|||
|
||||
: 2\. Function argument
|
||||
|
||||
|
||||
# Examples
|
||||
:::{.example}
|
||||
## `lib.trivial.mod` usage example
|
||||
|
@ -578,7 +566,6 @@ in {
|
|||
*/
|
||||
mod = base: int: base - (int * (builtins.div base int));
|
||||
|
||||
|
||||
## Comparisons
|
||||
|
||||
/**
|
||||
|
@ -588,7 +575,6 @@ in {
|
|||
a == b, compare a b => 0
|
||||
a > b, compare a b => 1
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`a`
|
||||
|
@ -599,12 +585,14 @@ in {
|
|||
|
||||
: 2\. Function argument
|
||||
*/
|
||||
compare = a: b:
|
||||
if a < b
|
||||
then -1
|
||||
else if a > b
|
||||
then 1
|
||||
else 0;
|
||||
compare =
|
||||
a: b:
|
||||
if a < b then
|
||||
-1
|
||||
else if a > b then
|
||||
1
|
||||
else
|
||||
0;
|
||||
|
||||
/**
|
||||
Split type into two subtypes by predicate `p`, take all elements
|
||||
|
@ -612,7 +600,6 @@ in {
|
|||
second subtype, compare elements of a single subtype with `yes`
|
||||
and `no` respectively.
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`p`
|
||||
|
@ -661,10 +648,12 @@ in {
|
|||
*/
|
||||
splitByAndCompare =
|
||||
p: yes: no: a: b:
|
||||
if p a
|
||||
then if p b then yes a b else -1
|
||||
else if p b then 1 else no a b;
|
||||
|
||||
if p a then
|
||||
if p b then yes a b else -1
|
||||
else if p b then
|
||||
1
|
||||
else
|
||||
no a b;
|
||||
|
||||
/**
|
||||
Reads a JSON file.
|
||||
|
@ -713,8 +702,7 @@ in {
|
|||
importJSON :: path -> any
|
||||
```
|
||||
*/
|
||||
importJSON = path:
|
||||
builtins.fromJSON (builtins.readFile path);
|
||||
importJSON = path: builtins.fromJSON (builtins.readFile path);
|
||||
|
||||
/**
|
||||
Reads a TOML file.
|
||||
|
@ -761,11 +749,9 @@ in {
|
|||
importTOML :: path -> any
|
||||
```
|
||||
*/
|
||||
importTOML = path:
|
||||
builtins.fromTOML (builtins.readFile path);
|
||||
importTOML = path: builtins.fromTOML (builtins.readFile path);
|
||||
|
||||
/**
|
||||
|
||||
`warn` *`message`* *`value`*
|
||||
|
||||
Print a warning before returning the second argument.
|
||||
|
@ -792,19 +778,26 @@ in {
|
|||
warn =
|
||||
# Since Nix 2.23, https://github.com/NixOS/nix/pull/10592
|
||||
builtins.warn or (
|
||||
let mustAbort = lib.elem (builtins.getEnv "NIX_ABORT_ON_WARN") ["1" "true" "yes"];
|
||||
let
|
||||
mustAbort = lib.elem (builtins.getEnv "NIX_ABORT_ON_WARN") [
|
||||
"1"
|
||||
"true"
|
||||
"yes"
|
||||
];
|
||||
in
|
||||
# Do not eta reduce v, so that we have the same strictness as `builtins.warn`.
|
||||
msg: v:
|
||||
# `builtins.warn` requires a string message, so we enforce that in our implementation, so that callers aren't accidentally incompatible with newer Nix versions.
|
||||
assert isString msg;
|
||||
if mustAbort
|
||||
then builtins.trace "[1;31mevaluation warning:[0m ${msg}" (abort "NIX_ABORT_ON_WARN=true; warnings are treated as unrecoverable errors.")
|
||||
else builtins.trace "[1;35mevaluation warning:[0m ${msg}" v
|
||||
# Do not eta reduce v, so that we have the same strictness as `builtins.warn`.
|
||||
msg: v:
|
||||
# `builtins.warn` requires a string message, so we enforce that in our implementation, so that callers aren't accidentally incompatible with newer Nix versions.
|
||||
assert isString msg;
|
||||
if mustAbort then
|
||||
builtins.trace "[1;31mevaluation warning:[0m ${msg}" (
|
||||
abort "NIX_ABORT_ON_WARN=true; warnings are treated as unrecoverable errors."
|
||||
)
|
||||
else
|
||||
builtins.trace "[1;35mevaluation warning:[0m ${msg}" v
|
||||
);
|
||||
|
||||
/**
|
||||
|
||||
`warnIf` *`condition`* *`message`* *`value`*
|
||||
|
||||
Like `warn`, but only warn when the first argument is `true`.
|
||||
|
@ -832,7 +825,6 @@ in {
|
|||
warnIf = cond: msg: if cond then warn msg else x: x;
|
||||
|
||||
/**
|
||||
|
||||
`warnIfNot` *`condition`* *`message`* *`value`*
|
||||
|
||||
Like `warnIf`, but negated: warn if the first argument is `false`.
|
||||
|
@ -870,7 +862,6 @@ in {
|
|||
Calls can be juxtaposed using function application, as `(r: r) a = a`, so
|
||||
`(r: r) (r: r) a = a`, and so forth.
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`cond`
|
||||
|
@ -904,7 +895,6 @@ in {
|
|||
/**
|
||||
Like throwIfNot, but negated (throw if the first argument is `true`).
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`cond`
|
||||
|
@ -926,7 +916,6 @@ in {
|
|||
/**
|
||||
Check if the elements in a list are valid values from a enum, returning the identity function, or throwing an error message otherwise.
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`msg`
|
||||
|
@ -960,12 +949,13 @@ in {
|
|||
|
||||
:::
|
||||
*/
|
||||
checkListOfEnum = msg: valid: given:
|
||||
checkListOfEnum =
|
||||
msg: valid: given:
|
||||
let
|
||||
unexpected = lib.subtractLists valid given;
|
||||
in
|
||||
lib.throwIfNot (unexpected == [])
|
||||
"${msg}: ${builtins.concatStringsSep ", " (builtins.map builtins.toString unexpected)} unexpected; valid ones: ${builtins.concatStringsSep ", " (builtins.map builtins.toString valid)}";
|
||||
lib.throwIfNot (unexpected == [ ])
|
||||
"${msg}: ${builtins.concatStringsSep ", " (builtins.map builtins.toString unexpected)} unexpected; valid ones: ${builtins.concatStringsSep ", " (builtins.map builtins.toString valid)}";
|
||||
|
||||
info = msg: builtins.trace "INFO: ${msg}";
|
||||
|
||||
|
@ -984,7 +974,6 @@ in {
|
|||
function of the { a, b ? foo, ... }: format, but some facilities
|
||||
like callPackage expect to be able to query expected arguments.
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`f`
|
||||
|
@ -995,11 +984,11 @@ in {
|
|||
|
||||
: 2\. Function argument
|
||||
*/
|
||||
setFunctionArgs = f: args:
|
||||
{ # TODO: Should we add call-time "type" checking like built in?
|
||||
__functor = self: f;
|
||||
__functionArgs = args;
|
||||
};
|
||||
setFunctionArgs = f: args: {
|
||||
# TODO: Should we add call-time "type" checking like built in?
|
||||
__functor = self: f;
|
||||
__functionArgs = args;
|
||||
};
|
||||
|
||||
/**
|
||||
Extract the expected function arguments from a function.
|
||||
|
@ -1008,37 +997,35 @@ in {
|
|||
has the same return type and semantics as builtins.functionArgs.
|
||||
setFunctionArgs : (a → b) → Map String Bool.
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`f`
|
||||
|
||||
: 1\. Function argument
|
||||
*/
|
||||
functionArgs = f:
|
||||
if f ? __functor
|
||||
then f.__functionArgs or (functionArgs (f.__functor f))
|
||||
else builtins.functionArgs f;
|
||||
functionArgs =
|
||||
f:
|
||||
if f ? __functor then
|
||||
f.__functionArgs or (functionArgs (f.__functor f))
|
||||
else
|
||||
builtins.functionArgs f;
|
||||
|
||||
/**
|
||||
Check whether something is a function or something
|
||||
annotated with function args.
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`f`
|
||||
|
||||
: 1\. Function argument
|
||||
*/
|
||||
isFunction = f: builtins.isFunction f ||
|
||||
(f ? __functor && isFunction (f.__functor f));
|
||||
isFunction = f: builtins.isFunction f || (f ? __functor && isFunction (f.__functor f));
|
||||
|
||||
/**
|
||||
`mirrorFunctionArgs f g` creates a new function `g'` with the same behavior as `g` (`g' x == g x`)
|
||||
but its function arguments mirroring `f` (`lib.functionArgs g' == lib.functionArgs f`).
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`f`
|
||||
|
@ -1084,21 +1071,18 @@ in {
|
|||
let
|
||||
fArgs = functionArgs f;
|
||||
in
|
||||
g:
|
||||
setFunctionArgs g fArgs;
|
||||
g: setFunctionArgs g fArgs;
|
||||
|
||||
/**
|
||||
Turns any non-callable values into constant functions.
|
||||
Returns callable values as is.
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`v`
|
||||
|
||||
: Any value
|
||||
|
||||
|
||||
# Examples
|
||||
:::{.example}
|
||||
## `lib.trivial.toFunction` usage example
|
||||
|
@ -1113,11 +1097,7 @@ in {
|
|||
|
||||
:::
|
||||
*/
|
||||
toFunction =
|
||||
v:
|
||||
if isFunction v
|
||||
then v
|
||||
else k: v;
|
||||
toFunction = v: if isFunction v then v else k: v;
|
||||
|
||||
/**
|
||||
Convert a hexadecimal string to it's integer representation.
|
||||
|
@ -1138,12 +1118,15 @@ in {
|
|||
=> 9223372036854775807
|
||||
```
|
||||
*/
|
||||
fromHexString = value:
|
||||
let
|
||||
noPrefix = lib.strings.removePrefix "0x" (lib.strings.toLower value);
|
||||
in let
|
||||
parsed = builtins.fromTOML "v=0x${noPrefix}";
|
||||
in parsed.v;
|
||||
fromHexString =
|
||||
value:
|
||||
let
|
||||
noPrefix = lib.strings.removePrefix "0x" (lib.strings.toLower value);
|
||||
in
|
||||
let
|
||||
parsed = builtins.fromTOML "v=0x${noPrefix}";
|
||||
in
|
||||
parsed.v;
|
||||
|
||||
/**
|
||||
Convert the given positive integer to a string of its hexadecimal
|
||||
|
@ -1155,20 +1138,19 @@ in {
|
|||
|
||||
toHexString 250 => "FA"
|
||||
*/
|
||||
toHexString = let
|
||||
hexDigits = {
|
||||
"10" = "A";
|
||||
"11" = "B";
|
||||
"12" = "C";
|
||||
"13" = "D";
|
||||
"14" = "E";
|
||||
"15" = "F";
|
||||
};
|
||||
toHexDigit = d:
|
||||
if d < 10
|
||||
then toString d
|
||||
else hexDigits.${toString d};
|
||||
in i: lib.concatMapStrings toHexDigit (toBaseDigits 16 i);
|
||||
toHexString =
|
||||
let
|
||||
hexDigits = {
|
||||
"10" = "A";
|
||||
"11" = "B";
|
||||
"12" = "C";
|
||||
"13" = "D";
|
||||
"14" = "E";
|
||||
"15" = "F";
|
||||
};
|
||||
toHexDigit = d: if d < 10 then toString d else hexDigits.${toString d};
|
||||
in
|
||||
i: lib.concatMapStrings toHexDigit (toBaseDigits 16 i);
|
||||
|
||||
/**
|
||||
`toBaseDigits base i` converts the positive integer i to a list of its
|
||||
|
@ -1180,7 +1162,6 @@ in {
|
|||
|
||||
toBaseDigits 16 250 => [ 15 10 ]
|
||||
|
||||
|
||||
# Inputs
|
||||
|
||||
`base`
|
||||
|
@ -1191,21 +1172,23 @@ in {
|
|||
|
||||
: 2\. Function argument
|
||||
*/
|
||||
toBaseDigits = base: i:
|
||||
toBaseDigits =
|
||||
base: i:
|
||||
let
|
||||
go = i:
|
||||
if i < base
|
||||
then [i]
|
||||
go =
|
||||
i:
|
||||
if i < base then
|
||||
[ i ]
|
||||
else
|
||||
let
|
||||
r = i - ((i / base) * base);
|
||||
q = (i - r) / base;
|
||||
in
|
||||
[r] ++ go q;
|
||||
[ r ] ++ go q;
|
||||
in
|
||||
assert (isInt base);
|
||||
assert (isInt i);
|
||||
assert (base >= 2);
|
||||
assert (i >= 0);
|
||||
lib.reverseList (go i);
|
||||
assert (isInt base);
|
||||
assert (isInt i);
|
||||
assert (base >= 2);
|
||||
assert (i >= 0);
|
||||
lib.reverseList (go i);
|
||||
}
|
||||
|
|
2066
lib/types.nix
2066
lib/types.nix
File diff suppressed because it is too large
Load diff
|
@ -1,12 +1,13 @@
|
|||
{ pkgs
|
||||
, options
|
||||
, config
|
||||
, version
|
||||
, revision
|
||||
, extraSources ? []
|
||||
, baseOptionsJSON ? null
|
||||
, warningsAreErrors ? true
|
||||
, prefix ? ../../..
|
||||
{
|
||||
pkgs,
|
||||
options,
|
||||
config,
|
||||
version,
|
||||
revision,
|
||||
extraSources ? [ ],
|
||||
baseOptionsJSON ? null,
|
||||
warningsAreErrors ? true,
|
||||
prefix ? ../../..,
|
||||
}:
|
||||
|
||||
let
|
||||
|
@ -38,43 +39,61 @@ let
|
|||
stripAnyPrefixes = flip (foldr removePrefix) prefixesToStrip;
|
||||
|
||||
optionsDoc = buildPackages.nixosOptionsDoc {
|
||||
inherit options revision baseOptionsJSON warningsAreErrors;
|
||||
transformOptions = opt: opt // {
|
||||
# Clean up declaration sites to not refer to the NixOS source tree.
|
||||
declarations = map stripAnyPrefixes opt.declarations;
|
||||
};
|
||||
inherit
|
||||
options
|
||||
revision
|
||||
baseOptionsJSON
|
||||
warningsAreErrors
|
||||
;
|
||||
transformOptions =
|
||||
opt:
|
||||
opt
|
||||
// {
|
||||
# Clean up declaration sites to not refer to the NixOS source tree.
|
||||
declarations = map stripAnyPrefixes opt.declarations;
|
||||
};
|
||||
};
|
||||
|
||||
nixos-lib = import ../../lib { };
|
||||
|
||||
testOptionsDoc = let
|
||||
testOptionsDoc =
|
||||
let
|
||||
eval = nixos-lib.evalTest {
|
||||
# Avoid evaluating a NixOS config prototype.
|
||||
config.node.type = types.deferredModule;
|
||||
options._module.args = mkOption { internal = true; };
|
||||
};
|
||||
in buildPackages.nixosOptionsDoc {
|
||||
in
|
||||
buildPackages.nixosOptionsDoc {
|
||||
inherit (eval) options;
|
||||
inherit revision;
|
||||
transformOptions = opt: opt // {
|
||||
# Clean up declaration sites to not refer to the NixOS source tree.
|
||||
declarations =
|
||||
map
|
||||
(decl:
|
||||
if hasPrefix (toString ../../..) (toString decl)
|
||||
then
|
||||
let subpath = removePrefix "/" (removePrefix (toString ../../..) (toString decl));
|
||||
in { url = "https://github.com/NixOS/nixpkgs/blob/master/${subpath}"; name = subpath; }
|
||||
else decl)
|
||||
opt.declarations;
|
||||
};
|
||||
transformOptions =
|
||||
opt:
|
||||
opt
|
||||
// {
|
||||
# Clean up declaration sites to not refer to the NixOS source tree.
|
||||
declarations = map (
|
||||
decl:
|
||||
if hasPrefix (toString ../../..) (toString decl) then
|
||||
let
|
||||
subpath = removePrefix "/" (removePrefix (toString ../../..) (toString decl));
|
||||
in
|
||||
{
|
||||
url = "https://github.com/NixOS/nixpkgs/blob/master/${subpath}";
|
||||
name = subpath;
|
||||
}
|
||||
else
|
||||
decl
|
||||
) opt.declarations;
|
||||
};
|
||||
documentType = "none";
|
||||
variablelistId = "test-options-list";
|
||||
optionIdPrefix = "test-opt-";
|
||||
};
|
||||
|
||||
testDriverMachineDocstrings = pkgs.callPackage
|
||||
../../../nixos/lib/test-driver/nixos-test-driver-docstrings.nix {};
|
||||
testDriverMachineDocstrings =
|
||||
pkgs.callPackage ../../../nixos/lib/test-driver/nixos-test-driver-docstrings.nix
|
||||
{ };
|
||||
|
||||
prepareManualFromMD = ''
|
||||
cp -r --no-preserve=all $inputs/* .
|
||||
|
@ -97,48 +116,51 @@ let
|
|||
-i ./development/writing-nixos-tests.section.md
|
||||
'';
|
||||
|
||||
in rec {
|
||||
in
|
||||
rec {
|
||||
inherit (optionsDoc) optionsJSON optionsNix optionsDocBook;
|
||||
|
||||
# Generate the NixOS manual.
|
||||
manualHTML = runCommand "nixos-manual-html"
|
||||
{ nativeBuildInputs = [ buildPackages.nixos-render-docs ];
|
||||
inputs = sourceFilesBySuffices ./. [ ".md" ];
|
||||
meta.description = "The NixOS manual in HTML format";
|
||||
allowedReferences = ["out"];
|
||||
}
|
||||
''
|
||||
# Generate the HTML manual.
|
||||
dst=$out/${common.outputPath}
|
||||
mkdir -p $dst
|
||||
manualHTML =
|
||||
runCommand "nixos-manual-html"
|
||||
{
|
||||
nativeBuildInputs = [ buildPackages.nixos-render-docs ];
|
||||
inputs = sourceFilesBySuffices ./. [ ".md" ];
|
||||
meta.description = "The NixOS manual in HTML format";
|
||||
allowedReferences = [ "out" ];
|
||||
}
|
||||
''
|
||||
# Generate the HTML manual.
|
||||
dst=$out/${common.outputPath}
|
||||
mkdir -p $dst
|
||||
|
||||
cp ${../../../doc/style.css} $dst/style.css
|
||||
cp ${../../../doc/anchor.min.js} $dst/anchor.min.js
|
||||
cp ${../../../doc/anchor-use.js} $dst/anchor-use.js
|
||||
cp ${../../../doc/style.css} $dst/style.css
|
||||
cp ${../../../doc/anchor.min.js} $dst/anchor.min.js
|
||||
cp ${../../../doc/anchor-use.js} $dst/anchor-use.js
|
||||
|
||||
cp -r ${pkgs.documentation-highlighter} $dst/highlightjs
|
||||
cp -r ${pkgs.documentation-highlighter} $dst/highlightjs
|
||||
|
||||
${prepareManualFromMD}
|
||||
${prepareManualFromMD}
|
||||
|
||||
nixos-render-docs -j $NIX_BUILD_CORES manual html \
|
||||
--manpage-urls ${manpageUrls} \
|
||||
--revision ${escapeShellArg revision} \
|
||||
--generator "nixos-render-docs ${pkgs.lib.version}" \
|
||||
--stylesheet style.css \
|
||||
--stylesheet highlightjs/mono-blue.css \
|
||||
--script ./highlightjs/highlight.pack.js \
|
||||
--script ./highlightjs/loader.js \
|
||||
--script ./anchor.min.js \
|
||||
--script ./anchor-use.js \
|
||||
--toc-depth 1 \
|
||||
--chunk-toc-depth 1 \
|
||||
./manual.md \
|
||||
$dst/${common.indexPath}
|
||||
nixos-render-docs -j $NIX_BUILD_CORES manual html \
|
||||
--manpage-urls ${manpageUrls} \
|
||||
--revision ${escapeShellArg revision} \
|
||||
--generator "nixos-render-docs ${pkgs.lib.version}" \
|
||||
--stylesheet style.css \
|
||||
--stylesheet highlightjs/mono-blue.css \
|
||||
--script ./highlightjs/highlight.pack.js \
|
||||
--script ./highlightjs/loader.js \
|
||||
--script ./anchor.min.js \
|
||||
--script ./anchor-use.js \
|
||||
--toc-depth 1 \
|
||||
--chunk-toc-depth 1 \
|
||||
./manual.md \
|
||||
$dst/${common.indexPath}
|
||||
|
||||
mkdir -p $out/nix-support
|
||||
echo "nix-build out $out" >> $out/nix-support/hydra-build-products
|
||||
echo "doc manual $dst" >> $out/nix-support/hydra-build-products
|
||||
''; # */
|
||||
mkdir -p $out/nix-support
|
||||
echo "nix-build out $out" >> $out/nix-support/hydra-build-products
|
||||
echo "doc manual $dst" >> $out/nix-support/hydra-build-products
|
||||
''; # */
|
||||
|
||||
# Alias for backward compatibility. TODO(@oxij): remove eventually.
|
||||
manual = manualHTML;
|
||||
|
@ -146,70 +168,77 @@ in rec {
|
|||
# Index page of the NixOS manual.
|
||||
manualHTMLIndex = "${manualHTML}/${common.outputPath}/${common.indexPath}";
|
||||
|
||||
manualEpub = runCommand "nixos-manual-epub"
|
||||
{ nativeBuildInputs = [ buildPackages.libxml2.bin buildPackages.libxslt.bin buildPackages.zip ];
|
||||
doc = ''
|
||||
<book xmlns="http://docbook.org/ns/docbook"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
version="5.0"
|
||||
xml:id="book-nixos-manual">
|
||||
<info>
|
||||
<title>NixOS Manual</title>
|
||||
<subtitle>Version ${pkgs.lib.version}</subtitle>
|
||||
</info>
|
||||
<chapter>
|
||||
<title>Temporarily unavailable</title>
|
||||
<para>
|
||||
The NixOS manual is currently not available in EPUB format,
|
||||
please use the <link xlink:href="https://nixos.org/nixos/manual">HTML manual</link>
|
||||
instead.
|
||||
</para>
|
||||
<para>
|
||||
If you've used the EPUB manual in the past and it has been useful to you, please
|
||||
<link xlink:href="https://github.com/NixOS/nixpkgs/issues/237234">let us know</link>.
|
||||
</para>
|
||||
</chapter>
|
||||
</book>
|
||||
manualEpub =
|
||||
runCommand "nixos-manual-epub"
|
||||
{
|
||||
nativeBuildInputs = [
|
||||
buildPackages.libxml2.bin
|
||||
buildPackages.libxslt.bin
|
||||
buildPackages.zip
|
||||
];
|
||||
doc = ''
|
||||
<book xmlns="http://docbook.org/ns/docbook"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
version="5.0"
|
||||
xml:id="book-nixos-manual">
|
||||
<info>
|
||||
<title>NixOS Manual</title>
|
||||
<subtitle>Version ${pkgs.lib.version}</subtitle>
|
||||
</info>
|
||||
<chapter>
|
||||
<title>Temporarily unavailable</title>
|
||||
<para>
|
||||
The NixOS manual is currently not available in EPUB format,
|
||||
please use the <link xlink:href="https://nixos.org/nixos/manual">HTML manual</link>
|
||||
instead.
|
||||
</para>
|
||||
<para>
|
||||
If you've used the EPUB manual in the past and it has been useful to you, please
|
||||
<link xlink:href="https://github.com/NixOS/nixpkgs/issues/237234">let us know</link>.
|
||||
</para>
|
||||
</chapter>
|
||||
</book>
|
||||
'';
|
||||
passAsFile = [ "doc" ];
|
||||
}
|
||||
''
|
||||
# Generate the epub manual.
|
||||
dst=$out/${common.outputPath}
|
||||
|
||||
xsltproc \
|
||||
--param chapter.autolabel 0 \
|
||||
--nonet --xinclude --output $dst/epub/ \
|
||||
${docbook_xsl_ns}/xml/xsl/docbook/epub/docbook.xsl \
|
||||
$docPath
|
||||
|
||||
echo "application/epub+zip" > mimetype
|
||||
manual="$dst/nixos-manual.epub"
|
||||
zip -0Xq "$manual" mimetype
|
||||
cd $dst/epub && zip -Xr9D "$manual" *
|
||||
|
||||
rm -rf $dst/epub
|
||||
|
||||
mkdir -p $out/nix-support
|
||||
echo "doc-epub manual $manual" >> $out/nix-support/hydra-build-products
|
||||
'';
|
||||
passAsFile = [ "doc" ];
|
||||
}
|
||||
''
|
||||
# Generate the epub manual.
|
||||
dst=$out/${common.outputPath}
|
||||
|
||||
xsltproc \
|
||||
--param chapter.autolabel 0 \
|
||||
--nonet --xinclude --output $dst/epub/ \
|
||||
${docbook_xsl_ns}/xml/xsl/docbook/epub/docbook.xsl \
|
||||
$docPath
|
||||
|
||||
echo "application/epub+zip" > mimetype
|
||||
manual="$dst/nixos-manual.epub"
|
||||
zip -0Xq "$manual" mimetype
|
||||
cd $dst/epub && zip -Xr9D "$manual" *
|
||||
|
||||
rm -rf $dst/epub
|
||||
|
||||
mkdir -p $out/nix-support
|
||||
echo "doc-epub manual $manual" >> $out/nix-support/hydra-build-products
|
||||
'';
|
||||
|
||||
|
||||
# Generate the `man configuration.nix` package
|
||||
nixos-configuration-reference-manpage = runCommand "nixos-configuration-reference-manpage"
|
||||
{ nativeBuildInputs = [
|
||||
buildPackages.installShellFiles
|
||||
buildPackages.nixos-render-docs
|
||||
];
|
||||
allowedReferences = ["out"];
|
||||
}
|
||||
''
|
||||
# Generate manpages.
|
||||
mkdir -p $out/share/man/man5
|
||||
nixos-render-docs -j $NIX_BUILD_CORES options manpage \
|
||||
--revision ${escapeShellArg revision} \
|
||||
${optionsJSON}/${common.outputPath}/options.json \
|
||||
$out/share/man/man5/configuration.nix.5
|
||||
'';
|
||||
nixos-configuration-reference-manpage =
|
||||
runCommand "nixos-configuration-reference-manpage"
|
||||
{
|
||||
nativeBuildInputs = [
|
||||
buildPackages.installShellFiles
|
||||
buildPackages.nixos-render-docs
|
||||
];
|
||||
allowedReferences = [ "out" ];
|
||||
}
|
||||
''
|
||||
# Generate manpages.
|
||||
mkdir -p $out/share/man/man5
|
||||
nixos-render-docs -j $NIX_BUILD_CORES options manpage \
|
||||
--revision ${escapeShellArg revision} \
|
||||
${optionsJSON}/${common.outputPath}/options.json \
|
||||
$out/share/man/man5/configuration.nix.5
|
||||
'';
|
||||
|
||||
}
|
||||
|
|
|
@ -8,40 +8,44 @@
|
|||
# as subcomponents (e.g. the container feature, or nixops if network
|
||||
# expressions are ever made modular at the top level) can just use
|
||||
# types.submodule instead of using eval-config.nix
|
||||
evalConfigArgs@
|
||||
{ # !!! system can be set modularly, would be nice to remove,
|
||||
evalConfigArgs@{
|
||||
# !!! system can be set modularly, would be nice to remove,
|
||||
# however, removing or changing this default is too much
|
||||
# of a breaking change. To set it modularly, pass `null`.
|
||||
system ? builtins.currentSystem
|
||||
, # !!! is this argument needed any more? The pkgs argument can
|
||||
system ? builtins.currentSystem,
|
||||
# !!! is this argument needed any more? The pkgs argument can
|
||||
# be set modularly anyway.
|
||||
pkgs ? null
|
||||
, # !!! what do we gain by making this configurable?
|
||||
pkgs ? null,
|
||||
# !!! what do we gain by making this configurable?
|
||||
# we can add modules that are included in specialisations, regardless
|
||||
# of inheritParentConfig.
|
||||
baseModules ? import ../modules/module-list.nix
|
||||
, # !!! See comment about args in lib/modules.nix
|
||||
extraArgs ? {}
|
||||
, # !!! See comment about args in lib/modules.nix
|
||||
specialArgs ? {}
|
||||
, modules
|
||||
, modulesLocation ? (builtins.unsafeGetAttrPos "modules" evalConfigArgs).file or null
|
||||
, # !!! See comment about check in lib/modules.nix
|
||||
check ? true
|
||||
, prefix ? []
|
||||
, lib ? import ../../lib
|
||||
, extraModules ? let e = builtins.getEnv "NIXOS_EXTRA_MODULE_PATH";
|
||||
in lib.optional (e != "") (import e)
|
||||
baseModules ? import ../modules/module-list.nix,
|
||||
# !!! See comment about args in lib/modules.nix
|
||||
extraArgs ? { },
|
||||
# !!! See comment about args in lib/modules.nix
|
||||
specialArgs ? { },
|
||||
modules,
|
||||
modulesLocation ? (builtins.unsafeGetAttrPos "modules" evalConfigArgs).file or null,
|
||||
# !!! See comment about check in lib/modules.nix
|
||||
check ? true,
|
||||
prefix ? [ ],
|
||||
lib ? import ../../lib,
|
||||
extraModules ?
|
||||
let
|
||||
e = builtins.getEnv "NIXOS_EXTRA_MODULE_PATH";
|
||||
in
|
||||
lib.optional (e != "") (import e),
|
||||
}:
|
||||
|
||||
let
|
||||
inherit (lib) optional;
|
||||
|
||||
evalModulesMinimal = (import ./default.nix {
|
||||
inherit lib;
|
||||
# Implicit use of feature is noted in implementation.
|
||||
featureFlags.minimalModules = { };
|
||||
}).evalModules;
|
||||
evalModulesMinimal =
|
||||
(import ./default.nix {
|
||||
inherit lib;
|
||||
# Implicit use of feature is noted in implementation.
|
||||
featureFlags.minimalModules = { };
|
||||
}).evalModules;
|
||||
|
||||
pkgsModule = rec {
|
||||
_file = ./eval-config.nix;
|
||||
|
@ -54,26 +58,29 @@ let
|
|||
# they way through, but has the last priority behind everything else.
|
||||
nixpkgs.system = lib.mkDefault system;
|
||||
})
|
||||
++
|
||||
(optional (pkgs != null) {
|
||||
++ (optional (pkgs != null) {
|
||||
# This should be default priority, so it conflicts with any user-defined pkgs.
|
||||
nixpkgs.pkgs = pkgs;
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
withWarnings = x:
|
||||
lib.warnIf (evalConfigArgs?extraArgs) "The extraArgs argument to eval-config.nix is deprecated. Please set config._module.args instead."
|
||||
lib.warnIf (evalConfigArgs?check) "The check argument to eval-config.nix is deprecated. Please set config._module.check instead."
|
||||
x;
|
||||
withWarnings =
|
||||
x:
|
||||
lib.warnIf (evalConfigArgs ? extraArgs)
|
||||
"The extraArgs argument to eval-config.nix is deprecated. Please set config._module.args instead."
|
||||
lib.warnIf
|
||||
(evalConfigArgs ? check)
|
||||
"The check argument to eval-config.nix is deprecated. Please set config._module.check instead."
|
||||
x;
|
||||
|
||||
legacyModules =
|
||||
lib.optional (evalConfigArgs?extraArgs) {
|
||||
lib.optional (evalConfigArgs ? extraArgs) {
|
||||
config = {
|
||||
_module.args = extraArgs;
|
||||
};
|
||||
}
|
||||
++ lib.optional (evalConfigArgs?check) {
|
||||
++ lib.optional (evalConfigArgs ? check) {
|
||||
config = {
|
||||
_module.check = lib.mkDefault check;
|
||||
};
|
||||
|
@ -89,29 +96,43 @@ let
|
|||
else
|
||||
map (lib.setDefaultModuleLocation modulesLocation) modules;
|
||||
in
|
||||
locatedModules ++ legacyModules;
|
||||
locatedModules ++ legacyModules;
|
||||
|
||||
noUserModules = evalModulesMinimal ({
|
||||
inherit prefix specialArgs;
|
||||
modules = baseModules ++ extraModules ++ [ pkgsModule modulesModule ];
|
||||
modules =
|
||||
baseModules
|
||||
++ extraModules
|
||||
++ [
|
||||
pkgsModule
|
||||
modulesModule
|
||||
];
|
||||
});
|
||||
|
||||
# Extra arguments that are useful for constructing a similar configuration.
|
||||
modulesModule = {
|
||||
config = {
|
||||
_module.args = {
|
||||
inherit noUserModules baseModules extraModules modules;
|
||||
inherit
|
||||
noUserModules
|
||||
baseModules
|
||||
extraModules
|
||||
modules
|
||||
;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
nixosWithUserModules = noUserModules.extendModules { modules = allUserModules; };
|
||||
|
||||
withExtraAttrs = configuration: configuration // {
|
||||
inherit extraArgs;
|
||||
inherit (configuration._module.args) pkgs;
|
||||
inherit lib;
|
||||
extendModules = args: withExtraAttrs (configuration.extendModules args);
|
||||
};
|
||||
withExtraAttrs =
|
||||
configuration:
|
||||
configuration
|
||||
// {
|
||||
inherit extraArgs;
|
||||
inherit (configuration._module.args) pkgs;
|
||||
inherit lib;
|
||||
extendModules = args: withExtraAttrs (configuration.extendModules args);
|
||||
};
|
||||
in
|
||||
withWarnings (withExtraAttrs nixosWithUserModules)
|
||||
|
|
|
@ -3,20 +3,22 @@
|
|||
# contents of a directory that can be populated with commands. The
|
||||
# generated image is sized to only fit its contents, with the expectation
|
||||
# that a script resizes the filesystem at boot time.
|
||||
{ pkgs
|
||||
, lib
|
||||
# List of derivations to be included
|
||||
, storePaths
|
||||
# Whether or not to compress the resulting image with zstd
|
||||
, compressImage ? false, zstd
|
||||
# Shell commands to populate the ./files directory.
|
||||
# All files in that directory are copied to the root of the FS.
|
||||
, populateImageCommands ? ""
|
||||
, volumeLabel
|
||||
, uuid ? "44444444-4444-4444-8888-888888888888"
|
||||
, btrfs-progs
|
||||
, libfaketime
|
||||
, fakeroot
|
||||
{
|
||||
pkgs,
|
||||
lib,
|
||||
# List of derivations to be included
|
||||
storePaths,
|
||||
# Whether or not to compress the resulting image with zstd
|
||||
compressImage ? false,
|
||||
zstd,
|
||||
# Shell commands to populate the ./files directory.
|
||||
# All files in that directory are copied to the root of the FS.
|
||||
populateImageCommands ? "",
|
||||
volumeLabel,
|
||||
uuid ? "44444444-4444-4444-8888-888888888888",
|
||||
btrfs-progs,
|
||||
libfaketime,
|
||||
fakeroot,
|
||||
}:
|
||||
|
||||
let
|
||||
|
@ -25,43 +27,46 @@ in
|
|||
pkgs.stdenv.mkDerivation {
|
||||
name = "btrfs-fs.img${lib.optionalString compressImage ".zst"}";
|
||||
|
||||
nativeBuildInputs = [ btrfs-progs libfaketime fakeroot ] ++ lib.optional compressImage zstd;
|
||||
nativeBuildInputs = [
|
||||
btrfs-progs
|
||||
libfaketime
|
||||
fakeroot
|
||||
] ++ lib.optional compressImage zstd;
|
||||
|
||||
buildCommand =
|
||||
''
|
||||
${if compressImage then "img=temp.img" else "img=$out"}
|
||||
buildCommand = ''
|
||||
${if compressImage then "img=temp.img" else "img=$out"}
|
||||
|
||||
set -x
|
||||
(
|
||||
mkdir -p ./files
|
||||
${populateImageCommands}
|
||||
)
|
||||
set -x
|
||||
(
|
||||
mkdir -p ./files
|
||||
${populateImageCommands}
|
||||
)
|
||||
|
||||
mkdir -p ./rootImage/nix/store
|
||||
mkdir -p ./rootImage/nix/store
|
||||
|
||||
xargs -I % cp -a --reflink=auto % -t ./rootImage/nix/store/ < ${sdClosureInfo}/store-paths
|
||||
(
|
||||
GLOBIGNORE=".:.."
|
||||
shopt -u dotglob
|
||||
xargs -I % cp -a --reflink=auto % -t ./rootImage/nix/store/ < ${sdClosureInfo}/store-paths
|
||||
(
|
||||
GLOBIGNORE=".:.."
|
||||
shopt -u dotglob
|
||||
|
||||
for f in ./files/*; do
|
||||
cp -a --reflink=auto -t ./rootImage/ "$f"
|
||||
done
|
||||
)
|
||||
for f in ./files/*; do
|
||||
cp -a --reflink=auto -t ./rootImage/ "$f"
|
||||
done
|
||||
)
|
||||
|
||||
cp ${sdClosureInfo}/registration ./rootImage/nix-path-registration
|
||||
cp ${sdClosureInfo}/registration ./rootImage/nix-path-registration
|
||||
|
||||
touch $img
|
||||
faketime -f "1970-01-01 00:00:01" fakeroot mkfs.btrfs -L ${volumeLabel} -U ${uuid} -r ./rootImage --shrink $img
|
||||
touch $img
|
||||
faketime -f "1970-01-01 00:00:01" fakeroot mkfs.btrfs -L ${volumeLabel} -U ${uuid} -r ./rootImage --shrink $img
|
||||
|
||||
if ! btrfs check $img; then
|
||||
echo "--- 'btrfs check' failed for BTRFS image ---"
|
||||
return 1
|
||||
fi
|
||||
if ! btrfs check $img; then
|
||||
echo "--- 'btrfs check' failed for BTRFS image ---"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ ${builtins.toString compressImage} ]; then
|
||||
echo "Compressing image"
|
||||
zstd -v --no-progress ./$img -o $out
|
||||
fi
|
||||
'';
|
||||
if [ ${builtins.toString compressImage} ]; then
|
||||
echo "Compressing image"
|
||||
zstd -v --no-progress ./$img -o $out
|
||||
fi
|
||||
'';
|
||||
}
|
||||
|
|
|
@ -1,115 +1,117 @@
|
|||
/* Technical details
|
||||
/*
|
||||
Technical details
|
||||
|
||||
`make-disk-image` has a bit of magic to minimize the amount of work to do in a virtual machine.
|
||||
`make-disk-image` has a bit of magic to minimize the amount of work to do in a virtual machine.
|
||||
|
||||
It relies on the [LKL (Linux Kernel Library) project](https://github.com/lkl/linux) which provides Linux kernel as userspace library.
|
||||
It relies on the [LKL (Linux Kernel Library) project](https://github.com/lkl/linux) which provides Linux kernel as userspace library.
|
||||
|
||||
The Nix-store only image only need to run LKL tools to produce an image and will never spawn a virtual machine, whereas full images will always require a virtual machine, but also use LKL.
|
||||
The Nix-store only image only need to run LKL tools to produce an image and will never spawn a virtual machine, whereas full images will always require a virtual machine, but also use LKL.
|
||||
|
||||
### Image preparation phase
|
||||
### Image preparation phase
|
||||
|
||||
Image preparation phase will produce the initial image layout in a folder:
|
||||
Image preparation phase will produce the initial image layout in a folder:
|
||||
|
||||
- devise a root folder based on `$PWD`
|
||||
- prepare the contents by copying and restoring ACLs in this root folder
|
||||
- load in the Nix store database all additional paths computed by `pkgs.closureInfo` in a temporary Nix store
|
||||
- run `nixos-install` in a temporary folder
|
||||
- transfer from the temporary store the additional paths registered to the installed NixOS
|
||||
- compute the size of the disk image based on the apparent size of the root folder
|
||||
- partition the disk image using the corresponding script according to the partition table type
|
||||
- format the partitions if needed
|
||||
- use `cptofs` (LKL tool) to copy the root folder inside the disk image
|
||||
- devise a root folder based on `$PWD`
|
||||
- prepare the contents by copying and restoring ACLs in this root folder
|
||||
- load in the Nix store database all additional paths computed by `pkgs.closureInfo` in a temporary Nix store
|
||||
- run `nixos-install` in a temporary folder
|
||||
- transfer from the temporary store the additional paths registered to the installed NixOS
|
||||
- compute the size of the disk image based on the apparent size of the root folder
|
||||
- partition the disk image using the corresponding script according to the partition table type
|
||||
- format the partitions if needed
|
||||
- use `cptofs` (LKL tool) to copy the root folder inside the disk image
|
||||
|
||||
At this step, the disk image already contains the Nix store, it now only needs to be converted to the desired format to be used.
|
||||
At this step, the disk image already contains the Nix store, it now only needs to be converted to the desired format to be used.
|
||||
|
||||
### Image conversion phase
|
||||
### Image conversion phase
|
||||
|
||||
Using `qemu-img`, the disk image is converted from a raw format to the desired format: qcow2(-compressed), vdi, vpc.
|
||||
Using `qemu-img`, the disk image is converted from a raw format to the desired format: qcow2(-compressed), vdi, vpc.
|
||||
|
||||
### Image Partitioning
|
||||
### Image Partitioning
|
||||
|
||||
#### `none`
|
||||
#### `none`
|
||||
|
||||
No partition table layout is written. The image is a bare filesystem image.
|
||||
No partition table layout is written. The image is a bare filesystem image.
|
||||
|
||||
#### `legacy`
|
||||
#### `legacy`
|
||||
|
||||
The image is partitioned using MBR. There is one primary ext4 partition starting at 1 MiB that fills the rest of the disk image.
|
||||
The image is partitioned using MBR. There is one primary ext4 partition starting at 1 MiB that fills the rest of the disk image.
|
||||
|
||||
This partition layout is unsuitable for UEFI.
|
||||
This partition layout is unsuitable for UEFI.
|
||||
|
||||
#### `legacy+gpt`
|
||||
#### `legacy+gpt`
|
||||
|
||||
This partition table type uses GPT and:
|
||||
This partition table type uses GPT and:
|
||||
|
||||
- create a "no filesystem" partition from 1MiB to 2MiB ;
|
||||
- set `bios_grub` flag on this "no filesystem" partition, which marks it as a [GRUB BIOS partition](https://www.gnu.org/software/parted/manual/html_node/set.html) ;
|
||||
- create a primary ext4 partition starting at 2MiB and extending to the full disk image ;
|
||||
- perform optimal alignments checks on each partition
|
||||
- create a "no filesystem" partition from 1MiB to 2MiB ;
|
||||
- set `bios_grub` flag on this "no filesystem" partition, which marks it as a [GRUB BIOS partition](https://www.gnu.org/software/parted/manual/html_node/set.html) ;
|
||||
- create a primary ext4 partition starting at 2MiB and extending to the full disk image ;
|
||||
- perform optimal alignments checks on each partition
|
||||
|
||||
This partition layout is unsuitable for UEFI boot, because it has no ESP (EFI System Partition) partition. It can work with CSM (Compatibility Support Module) which emulates legacy (BIOS) boot for UEFI.
|
||||
This partition layout is unsuitable for UEFI boot, because it has no ESP (EFI System Partition) partition. It can work with CSM (Compatibility Support Module) which emulates legacy (BIOS) boot for UEFI.
|
||||
|
||||
#### `efi`
|
||||
#### `efi`
|
||||
|
||||
This partition table type uses GPT and:
|
||||
This partition table type uses GPT and:
|
||||
|
||||
- creates an FAT32 ESP partition from 8MiB to specified `bootSize` parameter (256MiB by default), set it bootable ;
|
||||
- creates an primary ext4 partition starting after the boot partition and extending to the full disk image
|
||||
- creates an FAT32 ESP partition from 8MiB to specified `bootSize` parameter (256MiB by default), set it bootable ;
|
||||
- creates an primary ext4 partition starting after the boot partition and extending to the full disk image
|
||||
|
||||
#### `efixbootldr`
|
||||
#### `efixbootldr`
|
||||
|
||||
This partition table type uses GPT and:
|
||||
This partition table type uses GPT and:
|
||||
|
||||
- creates an FAT32 ESP partition from 8MiB to 100MiB, set it bootable ;
|
||||
- creates an FAT32 BOOT partition from 100MiB to specified `bootSize` parameter (256MiB by default), set `bls_boot` flag ;
|
||||
- creates an primary ext4 partition starting after the boot partition and extending to the full disk image
|
||||
- creates an FAT32 ESP partition from 8MiB to 100MiB, set it bootable ;
|
||||
- creates an FAT32 BOOT partition from 100MiB to specified `bootSize` parameter (256MiB by default), set `bls_boot` flag ;
|
||||
- creates an primary ext4 partition starting after the boot partition and extending to the full disk image
|
||||
|
||||
#### `hybrid`
|
||||
#### `hybrid`
|
||||
|
||||
This partition table type uses GPT and:
|
||||
This partition table type uses GPT and:
|
||||
|
||||
- creates a "no filesystem" partition from 0 to 1MiB, set `bios_grub` flag on it ;
|
||||
- creates an FAT32 ESP partition from 8MiB to specified `bootSize` parameter (256MiB by default), set it bootable ;
|
||||
- creates a primary ext4 partition starting after the boot one and extending to the full disk image
|
||||
- creates a "no filesystem" partition from 0 to 1MiB, set `bios_grub` flag on it ;
|
||||
- creates an FAT32 ESP partition from 8MiB to specified `bootSize` parameter (256MiB by default), set it bootable ;
|
||||
- creates a primary ext4 partition starting after the boot one and extending to the full disk image
|
||||
|
||||
This partition could be booted by a BIOS able to understand GPT layouts and recognizing the MBR at the start.
|
||||
This partition could be booted by a BIOS able to understand GPT layouts and recognizing the MBR at the start.
|
||||
|
||||
### How to run determinism analysis on results?
|
||||
### How to run determinism analysis on results?
|
||||
|
||||
Build your derivation with `--check` to rebuild it and verify it is the same.
|
||||
Build your derivation with `--check` to rebuild it and verify it is the same.
|
||||
|
||||
If it fails, you will be left with two folders with one having `.check`.
|
||||
If it fails, you will be left with two folders with one having `.check`.
|
||||
|
||||
You can use `diffoscope` to see the differences between the folders.
|
||||
You can use `diffoscope` to see the differences between the folders.
|
||||
|
||||
However, `diffoscope` is currently not able to diff two QCOW2 filesystems, thus, it is advised to use raw format.
|
||||
However, `diffoscope` is currently not able to diff two QCOW2 filesystems, thus, it is advised to use raw format.
|
||||
|
||||
Even if you use raw disks, `diffoscope` cannot diff the partition table and partitions recursively.
|
||||
Even if you use raw disks, `diffoscope` cannot diff the partition table and partitions recursively.
|
||||
|
||||
To solve this, you can run `fdisk -l $image` and generate `dd if=$image of=$image-p$i.raw skip=$start count=$sectors` for each `(start, sectors)` listed in the `fdisk` output. Now, you will have each partition as a separate file and you can compare them in pairs.
|
||||
To solve this, you can run `fdisk -l $image` and generate `dd if=$image of=$image-p$i.raw skip=$start count=$sectors` for each `(start, sectors)` listed in the `fdisk` output. Now, you will have each partition as a separate file and you can compare them in pairs.
|
||||
*/
|
||||
{ pkgs
|
||||
, lib
|
||||
{
|
||||
pkgs,
|
||||
lib,
|
||||
|
||||
, # The NixOS configuration to be installed onto the disk image.
|
||||
config
|
||||
# The NixOS configuration to be installed onto the disk image.
|
||||
config,
|
||||
|
||||
, # The size of the disk, in megabytes.
|
||||
# The size of the disk, in megabytes.
|
||||
# if "auto" size is calculated based on the contents copied to it and
|
||||
# additionalSpace is taken into account.
|
||||
diskSize ? "auto"
|
||||
diskSize ? "auto",
|
||||
|
||||
, # additional disk space to be added to the image if diskSize "auto"
|
||||
# additional disk space to be added to the image if diskSize "auto"
|
||||
# is used
|
||||
additionalSpace ? "512M"
|
||||
additionalSpace ? "512M",
|
||||
|
||||
, # size of the boot partition, is only used if partitionTableType is
|
||||
# size of the boot partition, is only used if partitionTableType is
|
||||
# either "efi" or "hybrid"
|
||||
# This will be undersized slightly, as this is actually the offset of
|
||||
# the end of the partition. Generally it will be 1MiB smaller.
|
||||
bootSize ? "256M"
|
||||
bootSize ? "256M",
|
||||
|
||||
, # The files and directories to be placed in the target file system.
|
||||
# The files and directories to be placed in the target file system.
|
||||
# This is a list of attribute sets {source, target, mode, user, group} where
|
||||
# `source' is the file system object (regular file or directory) to be
|
||||
# grafted in the file system at path `target', `mode' is a string containing
|
||||
|
@ -117,51 +119,51 @@ To solve this, you can run `fdisk -l $image` and generate `dd if=$image of=$imag
|
|||
# user and group name that will be set as owner of the files.
|
||||
# `mode', `user', and `group' are optional.
|
||||
# When setting one of `user' or `group', the other needs to be set too.
|
||||
contents ? []
|
||||
contents ? [ ],
|
||||
|
||||
, # Type of partition table to use; described in the `Image Partitioning` section above.
|
||||
partitionTableType ? "legacy"
|
||||
# Type of partition table to use; described in the `Image Partitioning` section above.
|
||||
partitionTableType ? "legacy",
|
||||
|
||||
, # Whether to invoke `switch-to-configuration boot` during image creation
|
||||
installBootLoader ? true
|
||||
# Whether to invoke `switch-to-configuration boot` during image creation
|
||||
installBootLoader ? true,
|
||||
|
||||
, # Whether to output have EFIVARS available in $out/efi-vars.fd and use it during disk creation
|
||||
touchEFIVars ? false
|
||||
# Whether to output have EFIVARS available in $out/efi-vars.fd and use it during disk creation
|
||||
touchEFIVars ? false,
|
||||
|
||||
, # OVMF firmware derivation
|
||||
OVMF ? pkgs.OVMF.fd
|
||||
# OVMF firmware derivation
|
||||
OVMF ? pkgs.OVMF.fd,
|
||||
|
||||
, # EFI firmware
|
||||
efiFirmware ? OVMF.firmware
|
||||
# EFI firmware
|
||||
efiFirmware ? OVMF.firmware,
|
||||
|
||||
, # EFI variables
|
||||
efiVariables ? OVMF.variables
|
||||
# EFI variables
|
||||
efiVariables ? OVMF.variables,
|
||||
|
||||
, # The root file system type.
|
||||
fsType ? "ext4"
|
||||
# The root file system type.
|
||||
fsType ? "ext4",
|
||||
|
||||
, # Filesystem label
|
||||
label ? if onlyNixStore then "nix-store" else "nixos"
|
||||
# Filesystem label
|
||||
label ? if onlyNixStore then "nix-store" else "nixos",
|
||||
|
||||
, # The initial NixOS configuration file to be copied to
|
||||
# The initial NixOS configuration file to be copied to
|
||||
# /etc/nixos/configuration.nix.
|
||||
configFile ? null
|
||||
configFile ? null,
|
||||
|
||||
, # Shell code executed after the VM has finished.
|
||||
postVM ? ""
|
||||
# Shell code executed after the VM has finished.
|
||||
postVM ? "",
|
||||
|
||||
, # Guest memory size
|
||||
memSize ? 1024
|
||||
# Guest memory size
|
||||
memSize ? 1024,
|
||||
|
||||
, # Copy the contents of the Nix store to the root of the image and
|
||||
# Copy the contents of the Nix store to the root of the image and
|
||||
# skip further setup. Incompatible with `contents`,
|
||||
# `installBootLoader` and `configFile`.
|
||||
onlyNixStore ? false
|
||||
onlyNixStore ? false,
|
||||
|
||||
, name ? "nixos-disk-image"
|
||||
name ? "nixos-disk-image",
|
||||
|
||||
, # Disk image format, one of qcow2, qcow2-compressed, vdi, vpc, raw.
|
||||
format ? "raw"
|
||||
# Disk image format, one of qcow2, qcow2-compressed, vdi, vpc, raw.
|
||||
format ? "raw",
|
||||
|
||||
# Whether to fix:
|
||||
# - GPT Disk Unique Identifier (diskGUID)
|
||||
|
@ -171,139 +173,181 @@ To solve this, you can run `fdisk -l $image` and generate `dd if=$image of=$imag
|
|||
# BIOS/MBR support is "best effort" at the moment.
|
||||
# Boot partitions may not be deterministic.
|
||||
# Also, to fix last time checked of the ext4 partition if fsType = ext4.
|
||||
, deterministic ? true
|
||||
deterministic ? true,
|
||||
|
||||
# GPT Partition Unique Identifier for root partition.
|
||||
, rootGPUID ? "F222513B-DED1-49FA-B591-20CE86A2FE7F"
|
||||
rootGPUID ? "F222513B-DED1-49FA-B591-20CE86A2FE7F",
|
||||
# When fsType = ext4, this is the root Filesystem Unique Identifier.
|
||||
# TODO: support other filesystems someday.
|
||||
, rootFSUID ? (if fsType == "ext4" then rootGPUID else null)
|
||||
rootFSUID ? (if fsType == "ext4" then rootGPUID else null),
|
||||
|
||||
, # Whether a nix channel based on the current source tree should be
|
||||
# Whether a nix channel based on the current source tree should be
|
||||
# made available inside the image. Useful for interactive use of nix
|
||||
# utils, but changes the hash of the image when the sources are
|
||||
# updated.
|
||||
copyChannel ? true
|
||||
copyChannel ? true,
|
||||
|
||||
, # Additional store paths to copy to the image's store.
|
||||
additionalPaths ? []
|
||||
# Additional store paths to copy to the image's store.
|
||||
additionalPaths ? [ ],
|
||||
}:
|
||||
|
||||
assert (lib.assertOneOf "partitionTableType" partitionTableType [ "legacy" "legacy+gpt" "efi" "efixbootldr" "hybrid" "none" ]);
|
||||
assert (lib.assertMsg (fsType == "ext4" && deterministic -> rootFSUID != null) "In deterministic mode with a ext4 partition, rootFSUID must be non-null, by default, it is equal to rootGPUID.");
|
||||
# We use -E offset=X below, which is only supported by e2fsprogs
|
||||
assert (lib.assertMsg (partitionTableType != "none" -> fsType == "ext4") "to produce a partition table, we need to use -E offset flag which is support only for fsType = ext4");
|
||||
assert (lib.assertMsg (touchEFIVars -> partitionTableType == "hybrid" || partitionTableType == "efi" || partitionTableType == "efixbootldr" || partitionTableType == "legacy+gpt") "EFI variables can be used only with a partition table of type: hybrid, efi, efixbootldr, or legacy+gpt.");
|
||||
# If only Nix store image, then: contents must be empty, configFile must be unset, and we should no install bootloader.
|
||||
assert (lib.assertMsg (onlyNixStore -> contents == [] && configFile == null && !installBootLoader) "In a only Nix store image, the contents must be empty, no configuration must be provided and no bootloader should be installed.");
|
||||
assert (
|
||||
lib.assertOneOf "partitionTableType" partitionTableType [
|
||||
"legacy"
|
||||
"legacy+gpt"
|
||||
"efi"
|
||||
"efixbootldr"
|
||||
"hybrid"
|
||||
"none"
|
||||
]
|
||||
);
|
||||
assert (
|
||||
lib.assertMsg (fsType == "ext4" && deterministic -> rootFSUID != null)
|
||||
"In deterministic mode with a ext4 partition, rootFSUID must be non-null, by default, it is equal to rootGPUID."
|
||||
);
|
||||
# We use -E offset=X below, which is only supported by e2fsprogs
|
||||
assert (
|
||||
lib.assertMsg (partitionTableType != "none" -> fsType == "ext4")
|
||||
"to produce a partition table, we need to use -E offset flag which is support only for fsType = ext4"
|
||||
);
|
||||
assert (
|
||||
lib.assertMsg
|
||||
(
|
||||
touchEFIVars
|
||||
->
|
||||
partitionTableType == "hybrid"
|
||||
|| partitionTableType == "efi"
|
||||
|| partitionTableType == "efixbootldr"
|
||||
|| partitionTableType == "legacy+gpt"
|
||||
)
|
||||
"EFI variables can be used only with a partition table of type: hybrid, efi, efixbootldr, or legacy+gpt."
|
||||
);
|
||||
# If only Nix store image, then: contents must be empty, configFile must be unset, and we should no install bootloader.
|
||||
assert (
|
||||
lib.assertMsg (onlyNixStore -> contents == [ ] && configFile == null && !installBootLoader)
|
||||
"In a only Nix store image, the contents must be empty, no configuration must be provided and no bootloader should be installed."
|
||||
);
|
||||
# Either both or none of {user,group} need to be set
|
||||
assert (lib.assertMsg (lib.all
|
||||
(attrs: ((attrs.user or null) == null)
|
||||
== ((attrs.group or null) == null))
|
||||
contents) "Contents of the disk image should set none of {user, group} or both at the same time.");
|
||||
assert (
|
||||
lib.assertMsg (lib.all (
|
||||
attrs: ((attrs.user or null) == null) == ((attrs.group or null) == null)
|
||||
) contents) "Contents of the disk image should set none of {user, group} or both at the same time."
|
||||
);
|
||||
|
||||
let format' = format; in let
|
||||
let
|
||||
format' = format;
|
||||
in
|
||||
let
|
||||
|
||||
format = if format' == "qcow2-compressed" then "qcow2" else format';
|
||||
|
||||
compress = lib.optionalString (format' == "qcow2-compressed") "-c";
|
||||
|
||||
filename = "nixos." + {
|
||||
qcow2 = "qcow2";
|
||||
vdi = "vdi";
|
||||
vpc = "vhd";
|
||||
raw = "img";
|
||||
}.${format} or format;
|
||||
filename =
|
||||
"nixos."
|
||||
+ {
|
||||
qcow2 = "qcow2";
|
||||
vdi = "vdi";
|
||||
vpc = "vhd";
|
||||
raw = "img";
|
||||
}
|
||||
.${format} or format;
|
||||
|
||||
rootPartition = { # switch-case
|
||||
legacy = "1";
|
||||
"legacy+gpt" = "2";
|
||||
efi = "2";
|
||||
efixbootldr = "3";
|
||||
hybrid = "3";
|
||||
}.${partitionTableType};
|
||||
rootPartition =
|
||||
{
|
||||
# switch-case
|
||||
legacy = "1";
|
||||
"legacy+gpt" = "2";
|
||||
efi = "2";
|
||||
efixbootldr = "3";
|
||||
hybrid = "3";
|
||||
}
|
||||
.${partitionTableType};
|
||||
|
||||
partitionDiskScript = { # switch-case
|
||||
legacy = ''
|
||||
parted --script $diskImage -- \
|
||||
mklabel msdos \
|
||||
mkpart primary ext4 1MiB -1
|
||||
'';
|
||||
"legacy+gpt" = ''
|
||||
parted --script $diskImage -- \
|
||||
mklabel gpt \
|
||||
mkpart no-fs 1MB 2MB \
|
||||
set 1 bios_grub on \
|
||||
align-check optimal 1 \
|
||||
mkpart primary ext4 2MB -1 \
|
||||
align-check optimal 2 \
|
||||
print
|
||||
${lib.optionalString deterministic ''
|
||||
partitionDiskScript =
|
||||
{
|
||||
# switch-case
|
||||
legacy = ''
|
||||
parted --script $diskImage -- \
|
||||
mklabel msdos \
|
||||
mkpart primary ext4 1MiB -1
|
||||
'';
|
||||
"legacy+gpt" = ''
|
||||
parted --script $diskImage -- \
|
||||
mklabel gpt \
|
||||
mkpart no-fs 1MB 2MB \
|
||||
set 1 bios_grub on \
|
||||
align-check optimal 1 \
|
||||
mkpart primary ext4 2MB -1 \
|
||||
align-check optimal 2 \
|
||||
print
|
||||
${lib.optionalString deterministic ''
|
||||
sgdisk \
|
||||
--disk-guid=97FD5997-D90B-4AA3-8D16-C1723AEA73C \
|
||||
--partition-guid=1:1C06F03B-704E-4657-B9CD-681A087A2FDC \
|
||||
--partition-guid=2:970C694F-AFD0-4B99-B750-CDB7A329AB6F \
|
||||
--partition-guid=3:${rootGPUID} \
|
||||
$diskImage
|
||||
''}
|
||||
'';
|
||||
efi = ''
|
||||
parted --script $diskImage -- \
|
||||
mklabel gpt \
|
||||
mkpart ESP fat32 8MiB ${bootSize} \
|
||||
set 1 boot on \
|
||||
mkpart primary ext4 ${bootSize} -1
|
||||
${lib.optionalString deterministic ''
|
||||
''}
|
||||
'';
|
||||
efi = ''
|
||||
parted --script $diskImage -- \
|
||||
mklabel gpt \
|
||||
mkpart ESP fat32 8MiB ${bootSize} \
|
||||
set 1 boot on \
|
||||
mkpart primary ext4 ${bootSize} -1
|
||||
${lib.optionalString deterministic ''
|
||||
sgdisk \
|
||||
--disk-guid=97FD5997-D90B-4AA3-8D16-C1723AEA73C \
|
||||
--partition-guid=1:1C06F03B-704E-4657-B9CD-681A087A2FDC \
|
||||
--partition-guid=2:${rootGPUID} \
|
||||
$diskImage
|
||||
''}
|
||||
'';
|
||||
efixbootldr = ''
|
||||
parted --script $diskImage -- \
|
||||
mklabel gpt \
|
||||
mkpart ESP fat32 8MiB 100MiB \
|
||||
set 1 boot on \
|
||||
mkpart BOOT fat32 100MiB ${bootSize} \
|
||||
set 2 bls_boot on \
|
||||
mkpart ROOT ext4 ${bootSize} -1
|
||||
${lib.optionalString deterministic ''
|
||||
''}
|
||||
'';
|
||||
efixbootldr = ''
|
||||
parted --script $diskImage -- \
|
||||
mklabel gpt \
|
||||
mkpart ESP fat32 8MiB 100MiB \
|
||||
set 1 boot on \
|
||||
mkpart BOOT fat32 100MiB ${bootSize} \
|
||||
set 2 bls_boot on \
|
||||
mkpart ROOT ext4 ${bootSize} -1
|
||||
${lib.optionalString deterministic ''
|
||||
sgdisk \
|
||||
--disk-guid=97FD5997-D90B-4AA3-8D16-C1723AEA73C \
|
||||
--partition-guid=1:1C06F03B-704E-4657-B9CD-681A087A2FDC \
|
||||
--partition-guid=2:970C694F-AFD0-4B99-B750-CDB7A329AB6F \
|
||||
--partition-guid=3:${rootGPUID} \
|
||||
$diskImage
|
||||
''}
|
||||
'';
|
||||
hybrid = ''
|
||||
parted --script $diskImage -- \
|
||||
mklabel gpt \
|
||||
mkpart ESP fat32 8MiB ${bootSize} \
|
||||
set 1 boot on \
|
||||
mkpart no-fs 0 1024KiB \
|
||||
set 2 bios_grub on \
|
||||
mkpart primary ext4 ${bootSize} -1
|
||||
${lib.optionalString deterministic ''
|
||||
''}
|
||||
'';
|
||||
hybrid = ''
|
||||
parted --script $diskImage -- \
|
||||
mklabel gpt \
|
||||
mkpart ESP fat32 8MiB ${bootSize} \
|
||||
set 1 boot on \
|
||||
mkpart no-fs 0 1024KiB \
|
||||
set 2 bios_grub on \
|
||||
mkpart primary ext4 ${bootSize} -1
|
||||
${lib.optionalString deterministic ''
|
||||
sgdisk \
|
||||
--disk-guid=97FD5997-D90B-4AA3-8D16-C1723AEA73C \
|
||||
--partition-guid=1:1C06F03B-704E-4657-B9CD-681A087A2FDC \
|
||||
--partition-guid=2:970C694F-AFD0-4B99-B750-CDB7A329AB6F \
|
||||
--partition-guid=3:${rootGPUID} \
|
||||
$diskImage
|
||||
''}
|
||||
'';
|
||||
none = "";
|
||||
}.${partitionTableType};
|
||||
''}
|
||||
'';
|
||||
none = "";
|
||||
}
|
||||
.${partitionTableType};
|
||||
|
||||
useEFIBoot = touchEFIVars;
|
||||
|
||||
nixpkgs = lib.cleanSource pkgs.path;
|
||||
|
||||
# FIXME: merge with channel.nix / make-channel.nix.
|
||||
channelSources = pkgs.runCommand "nixos-${config.system.nixos.version}" {} ''
|
||||
channelSources = pkgs.runCommand "nixos-${config.system.nixos.version}" { } ''
|
||||
mkdir -p $out
|
||||
cp -prd ${nixpkgs.outPath} $out/nixos
|
||||
chmod -R u+w $out/nixos
|
||||
|
@ -314,7 +358,9 @@ let format' = format; in let
|
|||
echo -n ${config.system.nixos.versionSuffix} > $out/nixos/.version-suffix
|
||||
'';
|
||||
|
||||
binPath = lib.makeBinPath (with pkgs; [
|
||||
binPath = lib.makeBinPath (
|
||||
with pkgs;
|
||||
[
|
||||
rsync
|
||||
util-linux
|
||||
parted
|
||||
|
@ -326,19 +372,19 @@ let format' = format; in let
|
|||
systemdMinimal
|
||||
]
|
||||
++ lib.optional deterministic gptfdisk
|
||||
++ stdenv.initialPath);
|
||||
++ stdenv.initialPath
|
||||
);
|
||||
|
||||
# I'm preserving the line below because I'm going to search for it across nixpkgs to consolidate
|
||||
# image building logic. The comment right below this now appears in 4 different places in nixpkgs :)
|
||||
# !!! should use XML.
|
||||
sources = map (x: x.source) contents;
|
||||
targets = map (x: x.target) contents;
|
||||
modes = map (x: x.mode or "''") contents;
|
||||
users = map (x: x.user or "''") contents;
|
||||
groups = map (x: x.group or "''") contents;
|
||||
modes = map (x: x.mode or "''") contents;
|
||||
users = map (x: x.user or "''") contents;
|
||||
groups = map (x: x.group or "''") contents;
|
||||
|
||||
basePaths = [ config.system.build.toplevel ]
|
||||
++ lib.optional copyChannel channelSources;
|
||||
basePaths = [ config.system.build.toplevel ] ++ lib.optional copyChannel channelSources;
|
||||
|
||||
additionalPaths' = lib.subtractLists basePaths additionalPaths;
|
||||
|
||||
|
@ -441,75 +487,96 @@ let format' = format; in let
|
|||
${if copyChannel then "--channel ${channelSources}" else "--no-channel-copy"} \
|
||||
--substituters ""
|
||||
|
||||
${lib.optionalString (additionalPaths' != []) ''
|
||||
${lib.optionalString (additionalPaths' != [ ]) ''
|
||||
nix --extra-experimental-features nix-command copy --to $root --no-check-sigs ${lib.concatStringsSep " " additionalPaths'}
|
||||
''}
|
||||
|
||||
diskImage=nixos.raw
|
||||
|
||||
${if diskSize == "auto" then ''
|
||||
${if partitionTableType == "efi" || partitionTableType == "efixbootldr" || partitionTableType == "hybrid" then ''
|
||||
# Add the GPT at the end
|
||||
gptSpace=$(( 512 * 34 * 1 ))
|
||||
# Normally we'd need to account for alignment and things, if bootSize
|
||||
# represented the actual size of the boot partition. But it instead
|
||||
# represents the offset at which it ends.
|
||||
# So we know bootSize is the reserved space in front of the partition.
|
||||
reservedSpace=$(( gptSpace + $(numfmt --from=iec '${bootSize}') ))
|
||||
'' else if partitionTableType == "legacy+gpt" then ''
|
||||
# Add the GPT at the end
|
||||
gptSpace=$(( 512 * 34 * 1 ))
|
||||
# And include the bios_grub partition; the ext4 partition starts at 2MB exactly.
|
||||
reservedSpace=$(( gptSpace + 2 * mebibyte ))
|
||||
'' else if partitionTableType == "legacy" then ''
|
||||
# Add the 1MiB aligned reserved space (includes MBR)
|
||||
reservedSpace=$(( mebibyte ))
|
||||
'' else ''
|
||||
reservedSpace=0
|
||||
''}
|
||||
additionalSpace=$(( $(numfmt --from=iec '${additionalSpace}') + reservedSpace ))
|
||||
${
|
||||
if diskSize == "auto" then
|
||||
''
|
||||
${
|
||||
if
|
||||
partitionTableType == "efi" || partitionTableType == "efixbootldr" || partitionTableType == "hybrid"
|
||||
then
|
||||
''
|
||||
# Add the GPT at the end
|
||||
gptSpace=$(( 512 * 34 * 1 ))
|
||||
# Normally we'd need to account for alignment and things, if bootSize
|
||||
# represented the actual size of the boot partition. But it instead
|
||||
# represents the offset at which it ends.
|
||||
# So we know bootSize is the reserved space in front of the partition.
|
||||
reservedSpace=$(( gptSpace + $(numfmt --from=iec '${bootSize}') ))
|
||||
''
|
||||
else if partitionTableType == "legacy+gpt" then
|
||||
''
|
||||
# Add the GPT at the end
|
||||
gptSpace=$(( 512 * 34 * 1 ))
|
||||
# And include the bios_grub partition; the ext4 partition starts at 2MB exactly.
|
||||
reservedSpace=$(( gptSpace + 2 * mebibyte ))
|
||||
''
|
||||
else if partitionTableType == "legacy" then
|
||||
''
|
||||
# Add the 1MiB aligned reserved space (includes MBR)
|
||||
reservedSpace=$(( mebibyte ))
|
||||
''
|
||||
else
|
||||
''
|
||||
reservedSpace=0
|
||||
''
|
||||
}
|
||||
additionalSpace=$(( $(numfmt --from=iec '${additionalSpace}') + reservedSpace ))
|
||||
|
||||
# Compute required space in filesystem blocks
|
||||
diskUsage=$(find . ! -type d -print0 | du --files0-from=- --apparent-size --block-size "${blockSize}" | cut -f1 | sum_lines)
|
||||
# Each inode takes space!
|
||||
numInodes=$(find . | wc -l)
|
||||
# Convert to bytes, inodes take two blocks each!
|
||||
diskUsage=$(( (diskUsage + 2 * numInodes) * ${blockSize} ))
|
||||
# Then increase the required space to account for the reserved blocks.
|
||||
fudge=$(compute_fudge $diskUsage)
|
||||
requiredFilesystemSpace=$(( diskUsage + fudge ))
|
||||
# Compute required space in filesystem blocks
|
||||
diskUsage=$(find . ! -type d -print0 | du --files0-from=- --apparent-size --block-size "${blockSize}" | cut -f1 | sum_lines)
|
||||
# Each inode takes space!
|
||||
numInodes=$(find . | wc -l)
|
||||
# Convert to bytes, inodes take two blocks each!
|
||||
diskUsage=$(( (diskUsage + 2 * numInodes) * ${blockSize} ))
|
||||
# Then increase the required space to account for the reserved blocks.
|
||||
fudge=$(compute_fudge $diskUsage)
|
||||
requiredFilesystemSpace=$(( diskUsage + fudge ))
|
||||
|
||||
diskSize=$(( requiredFilesystemSpace + additionalSpace ))
|
||||
diskSize=$(( requiredFilesystemSpace + additionalSpace ))
|
||||
|
||||
# Round up to the nearest mebibyte.
|
||||
# This ensures whole 512 bytes sector sizes in the disk image
|
||||
# and helps towards aligning partitions optimally.
|
||||
if (( diskSize % mebibyte )); then
|
||||
diskSize=$(( ( diskSize / mebibyte + 1) * mebibyte ))
|
||||
fi
|
||||
# Round up to the nearest mebibyte.
|
||||
# This ensures whole 512 bytes sector sizes in the disk image
|
||||
# and helps towards aligning partitions optimally.
|
||||
if (( diskSize % mebibyte )); then
|
||||
diskSize=$(( ( diskSize / mebibyte + 1) * mebibyte ))
|
||||
fi
|
||||
|
||||
truncate -s "$diskSize" $diskImage
|
||||
truncate -s "$diskSize" $diskImage
|
||||
|
||||
printf "Automatic disk size...\n"
|
||||
printf " Closure space use: %d bytes\n" $diskUsage
|
||||
printf " fudge: %d bytes\n" $fudge
|
||||
printf " Filesystem size needed: %d bytes\n" $requiredFilesystemSpace
|
||||
printf " Additional space: %d bytes\n" $additionalSpace
|
||||
printf " Disk image size: %d bytes\n" $diskSize
|
||||
'' else ''
|
||||
truncate -s ${toString diskSize}M $diskImage
|
||||
''}
|
||||
printf "Automatic disk size...\n"
|
||||
printf " Closure space use: %d bytes\n" $diskUsage
|
||||
printf " fudge: %d bytes\n" $fudge
|
||||
printf " Filesystem size needed: %d bytes\n" $requiredFilesystemSpace
|
||||
printf " Additional space: %d bytes\n" $additionalSpace
|
||||
printf " Disk image size: %d bytes\n" $diskSize
|
||||
''
|
||||
else
|
||||
''
|
||||
truncate -s ${toString diskSize}M $diskImage
|
||||
''
|
||||
}
|
||||
|
||||
${partitionDiskScript}
|
||||
|
||||
${if partitionTableType != "none" then ''
|
||||
# Get start & length of the root partition in sectors to $START and $SECTORS.
|
||||
eval $(partx $diskImage -o START,SECTORS --nr ${rootPartition} --pairs)
|
||||
${
|
||||
if partitionTableType != "none" then
|
||||
''
|
||||
# Get start & length of the root partition in sectors to $START and $SECTORS.
|
||||
eval $(partx $diskImage -o START,SECTORS --nr ${rootPartition} --pairs)
|
||||
|
||||
mkfs.${fsType} -b ${blockSize} -F -L ${label} $diskImage -E offset=$(sectorsToBytes $START) $(sectorsToKilobytes $SECTORS)K
|
||||
'' else ''
|
||||
mkfs.${fsType} -b ${blockSize} -F -L ${label} $diskImage
|
||||
''}
|
||||
mkfs.${fsType} -b ${blockSize} -F -L ${label} $diskImage -E offset=$(sectorsToBytes $START) $(sectorsToKilobytes $SECTORS)K
|
||||
''
|
||||
else
|
||||
''
|
||||
mkfs.${fsType} -b ${blockSize} -F -L ${label} $diskImage
|
||||
''
|
||||
}
|
||||
|
||||
echo "copying staging root to image..."
|
||||
cptofs -p ${lib.optionalString (partitionTableType != "none") "-P ${rootPartition}"} \
|
||||
|
@ -520,11 +587,16 @@ let format' = format; in let
|
|||
'';
|
||||
|
||||
moveOrConvertImage = ''
|
||||
${if format == "raw" then ''
|
||||
mv $diskImage $out/${filename}
|
||||
'' else ''
|
||||
${pkgs.qemu-utils}/bin/qemu-img convert -f raw -O ${format} ${compress} $diskImage $out/${filename}
|
||||
''}
|
||||
${
|
||||
if format == "raw" then
|
||||
''
|
||||
mv $diskImage $out/${filename}
|
||||
''
|
||||
else
|
||||
''
|
||||
${pkgs.qemu-utils}/bin/qemu-img convert -f raw -O ${format} ${compress} $diskImage $out/${filename}
|
||||
''
|
||||
}
|
||||
diskImage=$out/${filename}
|
||||
'';
|
||||
|
||||
|
@ -540,118 +612,131 @@ let format' = format; in let
|
|||
'';
|
||||
|
||||
buildImage = pkgs.vmTools.runInLinuxVM (
|
||||
pkgs.runCommand name {
|
||||
preVM = prepareImage + lib.optionalString touchEFIVars createEFIVars;
|
||||
buildInputs = with pkgs; [ util-linux e2fsprogs dosfstools ];
|
||||
postVM = moveOrConvertImage + createHydraBuildProducts + postVM;
|
||||
QEMU_OPTS =
|
||||
lib.concatStringsSep " " (lib.optional useEFIBoot "-drive if=pflash,format=raw,unit=0,readonly=on,file=${efiFirmware}"
|
||||
++ lib.optionals touchEFIVars [
|
||||
"-drive if=pflash,format=raw,unit=1,file=$efiVars"
|
||||
] ++ lib.optionals (OVMF.systemManagementModeRequired or false) [
|
||||
"-machine" "q35,smm=on"
|
||||
"-global" "driver=cfi.pflash01,property=secure,value=on"
|
||||
]
|
||||
);
|
||||
inherit memSize;
|
||||
} ''
|
||||
export PATH=${binPath}:$PATH
|
||||
pkgs.runCommand name
|
||||
{
|
||||
preVM = prepareImage + lib.optionalString touchEFIVars createEFIVars;
|
||||
buildInputs = with pkgs; [
|
||||
util-linux
|
||||
e2fsprogs
|
||||
dosfstools
|
||||
];
|
||||
postVM = moveOrConvertImage + createHydraBuildProducts + postVM;
|
||||
QEMU_OPTS = lib.concatStringsSep " " (
|
||||
lib.optional useEFIBoot "-drive if=pflash,format=raw,unit=0,readonly=on,file=${efiFirmware}"
|
||||
++ lib.optionals touchEFIVars [
|
||||
"-drive if=pflash,format=raw,unit=1,file=$efiVars"
|
||||
]
|
||||
++ lib.optionals (OVMF.systemManagementModeRequired or false) [
|
||||
"-machine"
|
||||
"q35,smm=on"
|
||||
"-global"
|
||||
"driver=cfi.pflash01,property=secure,value=on"
|
||||
]
|
||||
);
|
||||
inherit memSize;
|
||||
}
|
||||
''
|
||||
export PATH=${binPath}:$PATH
|
||||
|
||||
rootDisk=${if partitionTableType != "none" then "/dev/vda${rootPartition}" else "/dev/vda"}
|
||||
rootDisk=${if partitionTableType != "none" then "/dev/vda${rootPartition}" else "/dev/vda"}
|
||||
|
||||
# It is necessary to set root filesystem unique identifier in advance, otherwise
|
||||
# bootloader might get the wrong one and fail to boot.
|
||||
# At the end, we reset again because we want deterministic timestamps.
|
||||
${lib.optionalString (fsType == "ext4" && deterministic) ''
|
||||
tune2fs -T now ${lib.optionalString deterministic "-U ${rootFSUID}"} -c 0 -i 0 $rootDisk
|
||||
''}
|
||||
# make systemd-boot find ESP without udev
|
||||
mkdir /dev/block
|
||||
ln -s /dev/vda1 /dev/block/254:1
|
||||
# It is necessary to set root filesystem unique identifier in advance, otherwise
|
||||
# bootloader might get the wrong one and fail to boot.
|
||||
# At the end, we reset again because we want deterministic timestamps.
|
||||
${lib.optionalString (fsType == "ext4" && deterministic) ''
|
||||
tune2fs -T now ${lib.optionalString deterministic "-U ${rootFSUID}"} -c 0 -i 0 $rootDisk
|
||||
''}
|
||||
# make systemd-boot find ESP without udev
|
||||
mkdir /dev/block
|
||||
ln -s /dev/vda1 /dev/block/254:1
|
||||
|
||||
mountPoint=/mnt
|
||||
mkdir $mountPoint
|
||||
mount $rootDisk $mountPoint
|
||||
mountPoint=/mnt
|
||||
mkdir $mountPoint
|
||||
mount $rootDisk $mountPoint
|
||||
|
||||
# Create the ESP and mount it. Unlike e2fsprogs, mkfs.vfat doesn't support an
|
||||
# '-E offset=X' option, so we can't do this outside the VM.
|
||||
${lib.optionalString (partitionTableType == "efi" || partitionTableType == "hybrid") ''
|
||||
mkdir -p /mnt/boot
|
||||
mkfs.vfat -n ESP /dev/vda1
|
||||
mount /dev/vda1 /mnt/boot
|
||||
# Create the ESP and mount it. Unlike e2fsprogs, mkfs.vfat doesn't support an
|
||||
# '-E offset=X' option, so we can't do this outside the VM.
|
||||
${lib.optionalString (partitionTableType == "efi" || partitionTableType == "hybrid") ''
|
||||
mkdir -p /mnt/boot
|
||||
mkfs.vfat -n ESP /dev/vda1
|
||||
mount /dev/vda1 /mnt/boot
|
||||
|
||||
${lib.optionalString touchEFIVars "mount -t efivarfs efivarfs /sys/firmware/efi/efivars"}
|
||||
''}
|
||||
${lib.optionalString (partitionTableType == "efixbootldr") ''
|
||||
mkdir -p /mnt/{boot,efi}
|
||||
mkfs.vfat -n ESP /dev/vda1
|
||||
mkfs.vfat -n BOOT /dev/vda2
|
||||
mount /dev/vda1 /mnt/efi
|
||||
mount /dev/vda2 /mnt/boot
|
||||
${lib.optionalString touchEFIVars "mount -t efivarfs efivarfs /sys/firmware/efi/efivars"}
|
||||
''}
|
||||
${lib.optionalString (partitionTableType == "efixbootldr") ''
|
||||
mkdir -p /mnt/{boot,efi}
|
||||
mkfs.vfat -n ESP /dev/vda1
|
||||
mkfs.vfat -n BOOT /dev/vda2
|
||||
mount /dev/vda1 /mnt/efi
|
||||
mount /dev/vda2 /mnt/boot
|
||||
|
||||
${lib.optionalString touchEFIVars "mount -t efivarfs efivarfs /sys/firmware/efi/efivars"}
|
||||
''}
|
||||
${lib.optionalString touchEFIVars "mount -t efivarfs efivarfs /sys/firmware/efi/efivars"}
|
||||
''}
|
||||
|
||||
# Install a configuration.nix
|
||||
mkdir -p /mnt/etc/nixos
|
||||
${lib.optionalString (configFile != null) ''
|
||||
cp ${configFile} /mnt/etc/nixos/configuration.nix
|
||||
''}
|
||||
# Install a configuration.nix
|
||||
mkdir -p /mnt/etc/nixos
|
||||
${lib.optionalString (configFile != null) ''
|
||||
cp ${configFile} /mnt/etc/nixos/configuration.nix
|
||||
''}
|
||||
|
||||
${lib.optionalString installBootLoader ''
|
||||
# In this throwaway resource, we only have /dev/vda, but the actual VM may refer to another disk for bootloader, e.g. /dev/vdb
|
||||
# Use this option to create a symlink from vda to any arbitrary device you want.
|
||||
${lib.optionalString (config.boot.loader.grub.enable) (lib.concatMapStringsSep " " (device:
|
||||
lib.optionalString (device != "/dev/vda") ''
|
||||
mkdir -p "$(dirname ${device})"
|
||||
ln -s /dev/vda ${device}
|
||||
'') config.boot.loader.grub.devices)}
|
||||
${lib.optionalString installBootLoader ''
|
||||
# In this throwaway resource, we only have /dev/vda, but the actual VM may refer to another disk for bootloader, e.g. /dev/vdb
|
||||
# Use this option to create a symlink from vda to any arbitrary device you want.
|
||||
${lib.optionalString (config.boot.loader.grub.enable) (
|
||||
lib.concatMapStringsSep " " (
|
||||
device:
|
||||
lib.optionalString (device != "/dev/vda") ''
|
||||
mkdir -p "$(dirname ${device})"
|
||||
ln -s /dev/vda ${device}
|
||||
''
|
||||
) config.boot.loader.grub.devices
|
||||
)}
|
||||
|
||||
# Set up core system link, bootloader (sd-boot, GRUB, uboot, etc.), etc.
|
||||
# Set up core system link, bootloader (sd-boot, GRUB, uboot, etc.), etc.
|
||||
|
||||
# NOTE: systemd-boot-builder.py calls nix-env --list-generations which
|
||||
# clobbers $HOME/.nix-defexpr/channels/nixos This would cause a folder
|
||||
# /homeless-shelter to show up in the final image which in turn breaks
|
||||
# nix builds in the target image if sandboxing is turned off (through
|
||||
# __noChroot for example).
|
||||
export HOME=$TMPDIR
|
||||
NIXOS_INSTALL_BOOTLOADER=1 nixos-enter --root $mountPoint -- /nix/var/nix/profiles/system/bin/switch-to-configuration boot
|
||||
# NOTE: systemd-boot-builder.py calls nix-env --list-generations which
|
||||
# clobbers $HOME/.nix-defexpr/channels/nixos This would cause a folder
|
||||
# /homeless-shelter to show up in the final image which in turn breaks
|
||||
# nix builds in the target image if sandboxing is turned off (through
|
||||
# __noChroot for example).
|
||||
export HOME=$TMPDIR
|
||||
NIXOS_INSTALL_BOOTLOADER=1 nixos-enter --root $mountPoint -- /nix/var/nix/profiles/system/bin/switch-to-configuration boot
|
||||
|
||||
# The above scripts will generate a random machine-id and we don't want to bake a single ID into all our images
|
||||
rm -f $mountPoint/etc/machine-id
|
||||
''}
|
||||
# The above scripts will generate a random machine-id and we don't want to bake a single ID into all our images
|
||||
rm -f $mountPoint/etc/machine-id
|
||||
''}
|
||||
|
||||
# Set the ownerships of the contents. The modes are set in preVM.
|
||||
# No globbing on targets, so no need to set -f
|
||||
targets_=(${lib.concatStringsSep " " targets})
|
||||
users_=(${lib.concatStringsSep " " users})
|
||||
groups_=(${lib.concatStringsSep " " groups})
|
||||
for ((i = 0; i < ''${#targets_[@]}; i++)); do
|
||||
target="''${targets_[$i]}"
|
||||
user="''${users_[$i]}"
|
||||
group="''${groups_[$i]}"
|
||||
if [ -n "$user$group" ]; then
|
||||
# We have to nixos-enter since we need to use the user and group of the VM
|
||||
nixos-enter --root $mountPoint -- chown -R "$user:$group" "$target"
|
||||
fi
|
||||
done
|
||||
# Set the ownerships of the contents. The modes are set in preVM.
|
||||
# No globbing on targets, so no need to set -f
|
||||
targets_=(${lib.concatStringsSep " " targets})
|
||||
users_=(${lib.concatStringsSep " " users})
|
||||
groups_=(${lib.concatStringsSep " " groups})
|
||||
for ((i = 0; i < ''${#targets_[@]}; i++)); do
|
||||
target="''${targets_[$i]}"
|
||||
user="''${users_[$i]}"
|
||||
group="''${groups_[$i]}"
|
||||
if [ -n "$user$group" ]; then
|
||||
# We have to nixos-enter since we need to use the user and group of the VM
|
||||
nixos-enter --root $mountPoint -- chown -R "$user:$group" "$target"
|
||||
fi
|
||||
done
|
||||
|
||||
umount -R /mnt
|
||||
umount -R /mnt
|
||||
|
||||
# Make sure resize2fs works. Note that resize2fs has stricter criteria for resizing than a normal
|
||||
# mount, so the `-c 0` and `-i 0` don't affect it. Setting it to `now` doesn't produce deterministic
|
||||
# output, of course, but we can fix that when/if we start making images deterministic.
|
||||
# In deterministic mode, this is fixed to 1970-01-01 (UNIX timestamp 0).
|
||||
# This two-step approach is necessary otherwise `tune2fs` will want a fresher filesystem to perform
|
||||
# some changes.
|
||||
${lib.optionalString (fsType == "ext4") ''
|
||||
tune2fs -T now ${lib.optionalString deterministic "-U ${rootFSUID}"} -c 0 -i 0 $rootDisk
|
||||
${lib.optionalString deterministic "tune2fs -f -T 19700101 $rootDisk"}
|
||||
''}
|
||||
''
|
||||
# Make sure resize2fs works. Note that resize2fs has stricter criteria for resizing than a normal
|
||||
# mount, so the `-c 0` and `-i 0` don't affect it. Setting it to `now` doesn't produce deterministic
|
||||
# output, of course, but we can fix that when/if we start making images deterministic.
|
||||
# In deterministic mode, this is fixed to 1970-01-01 (UNIX timestamp 0).
|
||||
# This two-step approach is necessary otherwise `tune2fs` will want a fresher filesystem to perform
|
||||
# some changes.
|
||||
${lib.optionalString (fsType == "ext4") ''
|
||||
tune2fs -T now ${lib.optionalString deterministic "-U ${rootFSUID}"} -c 0 -i 0 $rootDisk
|
||||
${lib.optionalString deterministic "tune2fs -f -T 19700101 $rootDisk"}
|
||||
''}
|
||||
''
|
||||
);
|
||||
in
|
||||
if onlyNixStore then
|
||||
pkgs.runCommand name {}
|
||||
(prepareImage + moveOrConvertImage + createHydraBuildProducts + postVM)
|
||||
else buildImage
|
||||
if onlyNixStore then
|
||||
pkgs.runCommand name { } (prepareImage + moveOrConvertImage + createHydraBuildProducts + postVM)
|
||||
else
|
||||
buildImage
|
||||
|
|
|
@ -1,18 +1,22 @@
|
|||
{ lib, stdenv, squashfsTools, closureInfo
|
||||
{
|
||||
lib,
|
||||
stdenv,
|
||||
squashfsTools,
|
||||
closureInfo,
|
||||
|
||||
, fileName ? "squashfs"
|
||||
, # The root directory of the squashfs filesystem is filled with the
|
||||
fileName ? "squashfs",
|
||||
# The root directory of the squashfs filesystem is filled with the
|
||||
# closures of the Nix store paths listed here.
|
||||
storeContents ? []
|
||||
storeContents ? [ ],
|
||||
# Pseudo files to be added to squashfs image
|
||||
, pseudoFiles ? []
|
||||
, noStrip ? false
|
||||
, # Compression parameters.
|
||||
pseudoFiles ? [ ],
|
||||
noStrip ? false,
|
||||
# Compression parameters.
|
||||
# For zstd compression you can use "zstd -Xcompression-level 6".
|
||||
comp ? "xz -Xdict-size 100%"
|
||||
, # create hydra build product. will put image in directory instead
|
||||
comp ? "xz -Xdict-size 100%",
|
||||
# create hydra build product. will put image in directory instead
|
||||
# of directly in the store
|
||||
hydraBuildProduct ? false
|
||||
hydraBuildProduct ? false,
|
||||
}:
|
||||
|
||||
let
|
||||
|
@ -34,24 +38,28 @@ stdenv.mkDerivation {
|
|||
cp $closureInfo/registration nix-path-registration
|
||||
|
||||
imgPath="$out"
|
||||
'' + lib.optionalString hydraBuildProduct ''
|
||||
''
|
||||
+ lib.optionalString hydraBuildProduct ''
|
||||
|
||||
mkdir $out
|
||||
imgPath="$out/${fileName}.squashfs"
|
||||
'' + lib.optionalString stdenv.buildPlatform.is32bit ''
|
||||
''
|
||||
+ lib.optionalString stdenv.buildPlatform.is32bit ''
|
||||
|
||||
# 64 cores on i686 does not work
|
||||
# fails with FATAL ERROR: mangle2:: xz compress failed with error code 5
|
||||
if ((NIX_BUILD_CORES > 48)); then
|
||||
NIX_BUILD_CORES=48
|
||||
fi
|
||||
'' + ''
|
||||
''
|
||||
+ ''
|
||||
|
||||
# Generate the squashfs image.
|
||||
mksquashfs nix-path-registration $(cat $closureInfo/store-paths) $imgPath ${pseudoFilesArgs} \
|
||||
-no-hardlinks ${lib.optionalString noStrip "-no-strip"} -keep-as-directory -all-root -b 1048576 ${compFlag} \
|
||||
-processors $NIX_BUILD_CORES -root-mode 0755
|
||||
'' + lib.optionalString hydraBuildProduct ''
|
||||
''
|
||||
+ lib.optionalString hydraBuildProduct ''
|
||||
|
||||
mkdir -p $out/nix-support
|
||||
echo "file squashfs-image $out/${fileName}.squashfs" >> $out/nix-support/hydra-build-products
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -30,24 +30,39 @@ let
|
|||
|
||||
checkService = checkUnitConfig "Service" [
|
||||
(assertValueOneOf "Type" [
|
||||
"exec" "simple" "forking" "oneshot" "dbus" "notify" "notify-reload" "idle"
|
||||
"exec"
|
||||
"simple"
|
||||
"forking"
|
||||
"oneshot"
|
||||
"dbus"
|
||||
"notify"
|
||||
"notify-reload"
|
||||
"idle"
|
||||
])
|
||||
(assertValueOneOf "Restart" [
|
||||
"no" "on-success" "on-failure" "on-abnormal" "on-abort" "always"
|
||||
"no"
|
||||
"on-success"
|
||||
"on-failure"
|
||||
"on-abnormal"
|
||||
"on-abort"
|
||||
"always"
|
||||
])
|
||||
];
|
||||
|
||||
in rec {
|
||||
in
|
||||
rec {
|
||||
|
||||
unitOption = mkOptionType {
|
||||
name = "systemd option";
|
||||
merge = loc: defs:
|
||||
merge =
|
||||
loc: defs:
|
||||
let
|
||||
defs' = filterOverrides defs;
|
||||
in
|
||||
if any (def: isList def.value) defs'
|
||||
then concatMap (def: toList def.value) defs'
|
||||
else mergeEqualOption loc defs';
|
||||
if any (def: isList def.value) defs' then
|
||||
concatMap (def: toList def.value) defs'
|
||||
else
|
||||
mergeEqualOption loc defs';
|
||||
};
|
||||
|
||||
sharedOptions = {
|
||||
|
@ -76,7 +91,10 @@ in rec {
|
|||
|
||||
overrideStrategy = mkOption {
|
||||
default = "asDropinIfExists";
|
||||
type = types.enum [ "asDropinIfExists" "asDropin" ];
|
||||
type = types.enum [
|
||||
"asDropinIfExists"
|
||||
"asDropin"
|
||||
];
|
||||
description = ''
|
||||
Defines how unit configuration is provided for systemd:
|
||||
|
||||
|
@ -91,7 +109,7 @@ in rec {
|
|||
};
|
||||
|
||||
requiredBy = mkOption {
|
||||
default = [];
|
||||
default = [ ];
|
||||
type = types.listOf unitNameType;
|
||||
description = ''
|
||||
Units that require (i.e. depend on and need to go down with) this unit.
|
||||
|
@ -101,7 +119,7 @@ in rec {
|
|||
};
|
||||
|
||||
upheldBy = mkOption {
|
||||
default = [];
|
||||
default = [ ];
|
||||
type = types.listOf unitNameType;
|
||||
description = ''
|
||||
Keep this unit running as long as the listed units are running. This is a continuously
|
||||
|
@ -110,7 +128,7 @@ in rec {
|
|||
};
|
||||
|
||||
wantedBy = mkOption {
|
||||
default = [];
|
||||
default = [ ];
|
||||
type = types.listOf unitNameType;
|
||||
description = ''
|
||||
Units that want (i.e. depend on) this unit. The default method for
|
||||
|
@ -128,7 +146,7 @@ in rec {
|
|||
};
|
||||
|
||||
aliases = mkOption {
|
||||
default = [];
|
||||
default = [ ];
|
||||
type = types.listOf unitNameType;
|
||||
description = "Aliases of that unit.";
|
||||
};
|
||||
|
@ -160,13 +178,13 @@ in rec {
|
|||
};
|
||||
|
||||
documentation = mkOption {
|
||||
default = [];
|
||||
default = [ ];
|
||||
type = types.listOf types.str;
|
||||
description = "A list of URIs referencing documentation for this unit or its configuration.";
|
||||
};
|
||||
|
||||
requires = mkOption {
|
||||
default = [];
|
||||
default = [ ];
|
||||
type = types.listOf unitNameType;
|
||||
description = ''
|
||||
Start the specified units when this unit is started, and stop
|
||||
|
@ -175,7 +193,7 @@ in rec {
|
|||
};
|
||||
|
||||
wants = mkOption {
|
||||
default = [];
|
||||
default = [ ];
|
||||
type = types.listOf unitNameType;
|
||||
description = ''
|
||||
Start the specified units when this unit is started.
|
||||
|
@ -183,7 +201,7 @@ in rec {
|
|||
};
|
||||
|
||||
upholds = mkOption {
|
||||
default = [];
|
||||
default = [ ];
|
||||
type = types.listOf unitNameType;
|
||||
description = ''
|
||||
Keeps the specified running while this unit is running. A continuous version of `wants`.
|
||||
|
@ -191,7 +209,7 @@ in rec {
|
|||
};
|
||||
|
||||
after = mkOption {
|
||||
default = [];
|
||||
default = [ ];
|
||||
type = types.listOf unitNameType;
|
||||
description = ''
|
||||
If the specified units are started at the same time as
|
||||
|
@ -200,7 +218,7 @@ in rec {
|
|||
};
|
||||
|
||||
before = mkOption {
|
||||
default = [];
|
||||
default = [ ];
|
||||
type = types.listOf unitNameType;
|
||||
description = ''
|
||||
If the specified units are started at the same time as
|
||||
|
@ -209,7 +227,7 @@ in rec {
|
|||
};
|
||||
|
||||
bindsTo = mkOption {
|
||||
default = [];
|
||||
default = [ ];
|
||||
type = types.listOf unitNameType;
|
||||
description = ''
|
||||
Like ‘requires’, but in addition, if the specified units
|
||||
|
@ -218,7 +236,7 @@ in rec {
|
|||
};
|
||||
|
||||
partOf = mkOption {
|
||||
default = [];
|
||||
default = [ ];
|
||||
type = types.listOf unitNameType;
|
||||
description = ''
|
||||
If the specified units are stopped or restarted, then this
|
||||
|
@ -227,7 +245,7 @@ in rec {
|
|||
};
|
||||
|
||||
conflicts = mkOption {
|
||||
default = [];
|
||||
default = [ ];
|
||||
type = types.listOf unitNameType;
|
||||
description = ''
|
||||
If the specified units are started, then this unit is stopped
|
||||
|
@ -236,7 +254,7 @@ in rec {
|
|||
};
|
||||
|
||||
requisite = mkOption {
|
||||
default = [];
|
||||
default = [ ];
|
||||
type = types.listOf unitNameType;
|
||||
description = ''
|
||||
Similar to requires. However if the units listed are not started,
|
||||
|
@ -245,8 +263,10 @@ in rec {
|
|||
};
|
||||
|
||||
unitConfig = mkOption {
|
||||
default = {};
|
||||
example = { RequiresMountsFor = "/data"; };
|
||||
default = { };
|
||||
example = {
|
||||
RequiresMountsFor = "/data";
|
||||
};
|
||||
type = types.attrsOf unitOption;
|
||||
description = ''
|
||||
Each attribute in this set specifies an option in the
|
||||
|
@ -256,7 +276,7 @@ in rec {
|
|||
};
|
||||
|
||||
onFailure = mkOption {
|
||||
default = [];
|
||||
default = [ ];
|
||||
type = types.listOf unitNameType;
|
||||
description = ''
|
||||
A list of one or more units that are activated when
|
||||
|
@ -265,7 +285,7 @@ in rec {
|
|||
};
|
||||
|
||||
onSuccess = mkOption {
|
||||
default = [];
|
||||
default = [ ];
|
||||
type = types.listOf unitNameType;
|
||||
description = ''
|
||||
A list of one or more units that are activated when
|
||||
|
@ -274,21 +294,21 @@ in rec {
|
|||
};
|
||||
|
||||
startLimitBurst = mkOption {
|
||||
type = types.int;
|
||||
description = ''
|
||||
Configure unit start rate limiting. Units which are started
|
||||
more than startLimitBurst times within an interval time
|
||||
interval are not permitted to start any more.
|
||||
'';
|
||||
type = types.int;
|
||||
description = ''
|
||||
Configure unit start rate limiting. Units which are started
|
||||
more than startLimitBurst times within an interval time
|
||||
interval are not permitted to start any more.
|
||||
'';
|
||||
};
|
||||
|
||||
startLimitIntervalSec = mkOption {
|
||||
type = types.int;
|
||||
description = ''
|
||||
Configure unit start rate limiting. Units which are started
|
||||
more than startLimitBurst times within an interval time
|
||||
interval are not permitted to start any more.
|
||||
'';
|
||||
type = types.int;
|
||||
description = ''
|
||||
Configure unit start rate limiting. Units which are started
|
||||
more than startLimitBurst times within an interval time
|
||||
interval are not permitted to start any more.
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
|
@ -301,7 +321,7 @@ in rec {
|
|||
|
||||
options = {
|
||||
restartTriggers = mkOption {
|
||||
default = [];
|
||||
default = [ ];
|
||||
type = types.listOf types.unspecified;
|
||||
description = ''
|
||||
An arbitrary list of items such as derivations. If any item
|
||||
|
@ -311,7 +331,7 @@ in rec {
|
|||
};
|
||||
|
||||
reloadTriggers = mkOption {
|
||||
default = [];
|
||||
default = [ ];
|
||||
type = types.listOf unitOption;
|
||||
description = ''
|
||||
An arbitrary list of items such as derivations. If any item
|
||||
|
@ -324,170 +344,188 @@ in rec {
|
|||
};
|
||||
stage1CommonUnitOptions = commonUnitOptions;
|
||||
|
||||
serviceOptions = { name, config, ... }: {
|
||||
options = {
|
||||
serviceOptions =
|
||||
{ name, config, ... }:
|
||||
{
|
||||
options = {
|
||||
|
||||
environment = mkOption {
|
||||
default = {};
|
||||
type = with types; attrsOf (nullOr (oneOf [ str path package ]));
|
||||
example = { PATH = "/foo/bar/bin"; LANG = "nl_NL.UTF-8"; };
|
||||
description = "Environment variables passed to the service's processes.";
|
||||
};
|
||||
|
||||
path = mkOption {
|
||||
default = [];
|
||||
type = with types; listOf (oneOf [ package str ]);
|
||||
description = ''
|
||||
Packages added to the service's {env}`PATH`
|
||||
environment variable. Both the {file}`bin`
|
||||
and {file}`sbin` subdirectories of each
|
||||
package are added.
|
||||
'';
|
||||
};
|
||||
|
||||
serviceConfig = mkOption {
|
||||
default = {};
|
||||
example =
|
||||
{ RestartSec = 5;
|
||||
environment = mkOption {
|
||||
default = { };
|
||||
type =
|
||||
with types;
|
||||
attrsOf (
|
||||
nullOr (oneOf [
|
||||
str
|
||||
path
|
||||
package
|
||||
])
|
||||
);
|
||||
example = {
|
||||
PATH = "/foo/bar/bin";
|
||||
LANG = "nl_NL.UTF-8";
|
||||
};
|
||||
type = types.addCheck (types.attrsOf unitOption) checkService;
|
||||
description = ''
|
||||
Each attribute in this set specifies an option in the
|
||||
`[Service]` section of the unit. See
|
||||
{manpage}`systemd.service(5)` for details.
|
||||
'';
|
||||
description = "Environment variables passed to the service's processes.";
|
||||
};
|
||||
|
||||
path = mkOption {
|
||||
default = [ ];
|
||||
type =
|
||||
with types;
|
||||
listOf (oneOf [
|
||||
package
|
||||
str
|
||||
]);
|
||||
description = ''
|
||||
Packages added to the service's {env}`PATH`
|
||||
environment variable. Both the {file}`bin`
|
||||
and {file}`sbin` subdirectories of each
|
||||
package are added.
|
||||
'';
|
||||
};
|
||||
|
||||
serviceConfig = mkOption {
|
||||
default = { };
|
||||
example = {
|
||||
RestartSec = 5;
|
||||
};
|
||||
type = types.addCheck (types.attrsOf unitOption) checkService;
|
||||
description = ''
|
||||
Each attribute in this set specifies an option in the
|
||||
`[Service]` section of the unit. See
|
||||
{manpage}`systemd.service(5)` for details.
|
||||
'';
|
||||
};
|
||||
|
||||
enableStrictShellChecks = mkOption {
|
||||
type = types.bool;
|
||||
description = "Enable running shellcheck on the generated scripts for this unit.";
|
||||
# The default gets set in systemd-lib.nix because we don't have access to
|
||||
# the full NixOS config here.
|
||||
defaultText = literalExpression "config.systemd.enableStrictShellChecks";
|
||||
};
|
||||
|
||||
script = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
description = "Shell commands executed as the service's main process.";
|
||||
};
|
||||
|
||||
scriptArgs = mkOption {
|
||||
type = types.str;
|
||||
default = "";
|
||||
example = "%i";
|
||||
description = ''
|
||||
Arguments passed to the main process script.
|
||||
Can contain specifiers (`%` placeholders expanded by systemd, see {manpage}`systemd.unit(5)`).
|
||||
'';
|
||||
};
|
||||
|
||||
preStart = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
description = ''
|
||||
Shell commands executed before the service's main process
|
||||
is started.
|
||||
'';
|
||||
};
|
||||
|
||||
postStart = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
description = ''
|
||||
Shell commands executed after the service's main process
|
||||
is started.
|
||||
'';
|
||||
};
|
||||
|
||||
reload = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
description = ''
|
||||
Shell commands executed when the service's main process
|
||||
is reloaded.
|
||||
'';
|
||||
};
|
||||
|
||||
preStop = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
description = ''
|
||||
Shell commands executed to stop the service.
|
||||
'';
|
||||
};
|
||||
|
||||
postStop = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
description = ''
|
||||
Shell commands executed after the service's main process
|
||||
has exited.
|
||||
'';
|
||||
};
|
||||
|
||||
jobScripts = mkOption {
|
||||
type = with types; coercedTo path singleton (listOf path);
|
||||
internal = true;
|
||||
description = "A list of all job script derivations of this unit.";
|
||||
default = [ ];
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
enableStrictShellChecks = mkOption {
|
||||
type = types.bool;
|
||||
description = "Enable running shellcheck on the generated scripts for this unit.";
|
||||
# The default gets set in systemd-lib.nix because we don't have access to
|
||||
# the full NixOS config here.
|
||||
defaultText = literalExpression "config.systemd.enableStrictShellChecks";
|
||||
};
|
||||
|
||||
script = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
description = "Shell commands executed as the service's main process.";
|
||||
};
|
||||
|
||||
scriptArgs = mkOption {
|
||||
type = types.str;
|
||||
default = "";
|
||||
example = "%i";
|
||||
description = ''
|
||||
Arguments passed to the main process script.
|
||||
Can contain specifiers (`%` placeholders expanded by systemd, see {manpage}`systemd.unit(5)`).
|
||||
'';
|
||||
};
|
||||
|
||||
preStart = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
description = ''
|
||||
Shell commands executed before the service's main process
|
||||
is started.
|
||||
'';
|
||||
};
|
||||
|
||||
postStart = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
description = ''
|
||||
Shell commands executed after the service's main process
|
||||
is started.
|
||||
'';
|
||||
};
|
||||
|
||||
reload = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
description = ''
|
||||
Shell commands executed when the service's main process
|
||||
is reloaded.
|
||||
'';
|
||||
};
|
||||
|
||||
preStop = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
description = ''
|
||||
Shell commands executed to stop the service.
|
||||
'';
|
||||
};
|
||||
|
||||
postStop = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
description = ''
|
||||
Shell commands executed after the service's main process
|
||||
has exited.
|
||||
'';
|
||||
};
|
||||
|
||||
jobScripts = mkOption {
|
||||
type = with types; coercedTo path singleton (listOf path);
|
||||
internal = true;
|
||||
description = "A list of all job script derivations of this unit.";
|
||||
default = [];
|
||||
};
|
||||
config = mkMerge [
|
||||
(mkIf (config.preStart != "") rec {
|
||||
jobScripts = makeJobScript {
|
||||
name = "${name}-pre-start";
|
||||
text = config.preStart;
|
||||
inherit (config) enableStrictShellChecks;
|
||||
};
|
||||
serviceConfig.ExecStartPre = [ jobScripts ];
|
||||
})
|
||||
(mkIf (config.script != "") rec {
|
||||
jobScripts = makeJobScript {
|
||||
name = "${name}-start";
|
||||
text = config.script;
|
||||
inherit (config) enableStrictShellChecks;
|
||||
};
|
||||
serviceConfig.ExecStart = jobScripts + " " + config.scriptArgs;
|
||||
})
|
||||
(mkIf (config.postStart != "") rec {
|
||||
jobScripts = makeJobScript {
|
||||
name = "${name}-post-start";
|
||||
text = config.postStart;
|
||||
inherit (config) enableStrictShellChecks;
|
||||
};
|
||||
serviceConfig.ExecStartPost = [ jobScripts ];
|
||||
})
|
||||
(mkIf (config.reload != "") rec {
|
||||
jobScripts = makeJobScript {
|
||||
name = "${name}-reload";
|
||||
text = config.reload;
|
||||
inherit (config) enableStrictShellChecks;
|
||||
};
|
||||
serviceConfig.ExecReload = jobScripts;
|
||||
})
|
||||
(mkIf (config.preStop != "") rec {
|
||||
jobScripts = makeJobScript {
|
||||
name = "${name}-pre-stop";
|
||||
text = config.preStop;
|
||||
inherit (config) enableStrictShellChecks;
|
||||
};
|
||||
serviceConfig.ExecStop = jobScripts;
|
||||
})
|
||||
(mkIf (config.postStop != "") rec {
|
||||
jobScripts = makeJobScript {
|
||||
name = "${name}-post-stop";
|
||||
text = config.postStop;
|
||||
inherit (config) enableStrictShellChecks;
|
||||
};
|
||||
serviceConfig.ExecStopPost = jobScripts;
|
||||
})
|
||||
];
|
||||
|
||||
};
|
||||
|
||||
config = mkMerge [
|
||||
(mkIf (config.preStart != "") rec {
|
||||
jobScripts = makeJobScript {
|
||||
name = "${name}-pre-start";
|
||||
text = config.preStart;
|
||||
inherit (config) enableStrictShellChecks;
|
||||
};
|
||||
serviceConfig.ExecStartPre = [ jobScripts ];
|
||||
})
|
||||
(mkIf (config.script != "") rec {
|
||||
jobScripts = makeJobScript {
|
||||
name = "${name}-start";
|
||||
text = config.script;
|
||||
inherit (config) enableStrictShellChecks;
|
||||
};
|
||||
serviceConfig.ExecStart = jobScripts + " " + config.scriptArgs;
|
||||
})
|
||||
(mkIf (config.postStart != "") rec {
|
||||
jobScripts = makeJobScript {
|
||||
name = "${name}-post-start";
|
||||
text = config.postStart;
|
||||
inherit (config) enableStrictShellChecks;
|
||||
};
|
||||
serviceConfig.ExecStartPost = [ jobScripts ];
|
||||
})
|
||||
(mkIf (config.reload != "") rec {
|
||||
jobScripts = makeJobScript {
|
||||
name = "${name}-reload";
|
||||
text = config.reload;
|
||||
inherit (config) enableStrictShellChecks;
|
||||
};
|
||||
serviceConfig.ExecReload = jobScripts;
|
||||
})
|
||||
(mkIf (config.preStop != "") rec {
|
||||
jobScripts = makeJobScript {
|
||||
name = "${name}-pre-stop";
|
||||
text = config.preStop;
|
||||
inherit (config) enableStrictShellChecks;
|
||||
};
|
||||
serviceConfig.ExecStop = jobScripts;
|
||||
})
|
||||
(mkIf (config.postStop != "") rec {
|
||||
jobScripts = makeJobScript {
|
||||
name = "${name}-post-stop";
|
||||
text = config.postStop;
|
||||
inherit (config) enableStrictShellChecks;
|
||||
};
|
||||
serviceConfig.ExecStopPost = jobScripts;
|
||||
})
|
||||
];
|
||||
|
||||
};
|
||||
|
||||
stage2ServiceOptions = {
|
||||
imports = [
|
||||
stage2CommonUnitOptions
|
||||
|
@ -537,7 +575,7 @@ in rec {
|
|||
|
||||
startAt = mkOption {
|
||||
type = with types; either str (listOf str);
|
||||
default = [];
|
||||
default = [ ];
|
||||
example = "Sun 14:00:00";
|
||||
description = ''
|
||||
Automatically start this unit at the given date/time, which
|
||||
|
@ -558,14 +596,16 @@ in rec {
|
|||
];
|
||||
};
|
||||
|
||||
|
||||
socketOptions = {
|
||||
options = {
|
||||
|
||||
listenStreams = mkOption {
|
||||
default = [];
|
||||
default = [ ];
|
||||
type = types.listOf types.str;
|
||||
example = [ "0.0.0.0:993" "/run/my-socket" ];
|
||||
example = [
|
||||
"0.0.0.0:993"
|
||||
"/run/my-socket"
|
||||
];
|
||||
description = ''
|
||||
For each item in this list, a `ListenStream`
|
||||
option in the `[Socket]` section will be created.
|
||||
|
@ -573,9 +613,12 @@ in rec {
|
|||
};
|
||||
|
||||
listenDatagrams = mkOption {
|
||||
default = [];
|
||||
default = [ ];
|
||||
type = types.listOf types.str;
|
||||
example = [ "0.0.0.0:993" "/run/my-socket" ];
|
||||
example = [
|
||||
"0.0.0.0:993"
|
||||
"/run/my-socket"
|
||||
];
|
||||
description = ''
|
||||
For each item in this list, a `ListenDatagram`
|
||||
option in the `[Socket]` section will be created.
|
||||
|
@ -583,8 +626,10 @@ in rec {
|
|||
};
|
||||
|
||||
socketConfig = mkOption {
|
||||
default = {};
|
||||
example = { ListenStream = "/run/my-socket"; };
|
||||
default = { };
|
||||
example = {
|
||||
ListenStream = "/run/my-socket";
|
||||
};
|
||||
type = types.attrsOf unitOption;
|
||||
description = ''
|
||||
Each attribute in this set specifies an option in the
|
||||
|
@ -610,13 +655,15 @@ in rec {
|
|||
];
|
||||
};
|
||||
|
||||
|
||||
timerOptions = {
|
||||
options = {
|
||||
|
||||
timerConfig = mkOption {
|
||||
default = {};
|
||||
example = { OnCalendar = "Sun 14:00:00"; Unit = "foo.service"; };
|
||||
default = { };
|
||||
example = {
|
||||
OnCalendar = "Sun 14:00:00";
|
||||
Unit = "foo.service";
|
||||
};
|
||||
type = types.attrsOf unitOption;
|
||||
description = ''
|
||||
Each attribute in this set specifies an option in the
|
||||
|
@ -643,13 +690,15 @@ in rec {
|
|||
];
|
||||
};
|
||||
|
||||
|
||||
pathOptions = {
|
||||
options = {
|
||||
|
||||
pathConfig = mkOption {
|
||||
default = {};
|
||||
example = { PathChanged = "/some/path"; Unit = "changedpath.service"; };
|
||||
default = { };
|
||||
example = {
|
||||
PathChanged = "/some/path";
|
||||
Unit = "changedpath.service";
|
||||
};
|
||||
type = types.attrsOf unitOption;
|
||||
description = ''
|
||||
Each attribute in this set specifies an option in the
|
||||
|
@ -675,7 +724,6 @@ in rec {
|
|||
];
|
||||
};
|
||||
|
||||
|
||||
mountOptions = {
|
||||
options = {
|
||||
|
||||
|
@ -709,8 +757,10 @@ in rec {
|
|||
};
|
||||
|
||||
mountConfig = mkOption {
|
||||
default = {};
|
||||
example = { DirectoryMode = "0775"; };
|
||||
default = { };
|
||||
example = {
|
||||
DirectoryMode = "0775";
|
||||
};
|
||||
type = types.attrsOf unitOption;
|
||||
description = ''
|
||||
Each attribute in this set specifies an option in the
|
||||
|
@ -749,8 +799,10 @@ in rec {
|
|||
};
|
||||
|
||||
automountConfig = mkOption {
|
||||
default = {};
|
||||
example = { DirectoryMode = "0775"; };
|
||||
default = { };
|
||||
example = {
|
||||
DirectoryMode = "0775";
|
||||
};
|
||||
type = types.attrsOf unitOption;
|
||||
description = ''
|
||||
Each attribute in this set specifies an option in the
|
||||
|
@ -780,8 +832,10 @@ in rec {
|
|||
options = {
|
||||
|
||||
sliceConfig = mkOption {
|
||||
default = {};
|
||||
example = { MemoryMax = "2G"; };
|
||||
default = { };
|
||||
example = {
|
||||
MemoryMax = "2G";
|
||||
};
|
||||
type = types.attrsOf unitOption;
|
||||
description = ''
|
||||
Each attribute in this set specifies an option in the
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
{ config, lib, hostPkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
hostPkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (lib) mkOption types literalMD;
|
||||
|
||||
|
@ -11,10 +16,9 @@ let
|
|||
tesseract4 = hostPkgs.tesseract4.override { enableLanguages = [ "eng" ]; };
|
||||
};
|
||||
|
||||
|
||||
vlans = map (m: (
|
||||
m.virtualisation.vlans ++
|
||||
(lib.mapAttrsToList (_: v: v.vlan) m.virtualisation.interfaces))) (lib.attrValues config.nodes);
|
||||
vlans = map (
|
||||
m: (m.virtualisation.vlans ++ (lib.mapAttrsToList (_: v: v.vlan) m.virtualisation.interfaces))
|
||||
) (lib.attrValues config.nodes);
|
||||
vms = map (m: m.system.build.vm) (lib.attrValues config.nodes);
|
||||
|
||||
nodeHostNames =
|
||||
|
@ -23,13 +27,14 @@ let
|
|||
in
|
||||
nodesList ++ lib.optional (lib.length nodesList == 1 && !lib.elem "machine" nodesList) "machine";
|
||||
|
||||
pythonizeName = name:
|
||||
pythonizeName =
|
||||
name:
|
||||
let
|
||||
head = lib.substring 0 1 name;
|
||||
tail = lib.substring 1 (-1) name;
|
||||
in
|
||||
(if builtins.match "[A-z_]" head == null then "_" else head) +
|
||||
lib.stringAsChars (c: if builtins.match "[A-z0-9_]" c == null then "_" else c) tail;
|
||||
(if builtins.match "[A-z_]" head == null then "_" else head)
|
||||
+ lib.stringAsChars (c: if builtins.match "[A-z0-9_]" c == null then "_" else c) tail;
|
||||
|
||||
uniqueVlans = lib.unique (builtins.concatLists vlans);
|
||||
vlanNames = map (i: "vlan${toString i}: VLan;") uniqueVlans;
|
||||
|
@ -96,7 +101,12 @@ let
|
|||
--set testScript "$out/test-script" \
|
||||
--set globalTimeout "${toString config.globalTimeout}" \
|
||||
--set vlans '${toString vlans}' \
|
||||
${lib.escapeShellArgs (lib.concatMap (arg: ["--add-flags" arg]) config.extraDriverArgs)}
|
||||
${lib.escapeShellArgs (
|
||||
lib.concatMap (arg: [
|
||||
"--add-flags"
|
||||
arg
|
||||
]) config.extraDriverArgs
|
||||
)}
|
||||
'';
|
||||
|
||||
in
|
||||
|
@ -165,7 +175,7 @@ in
|
|||
They become part of [{option}`driver`](#test-opt-driver) via `wrapProgram`.
|
||||
'';
|
||||
type = types.listOf types.str;
|
||||
default = [];
|
||||
default = [ ];
|
||||
};
|
||||
|
||||
skipLint = mkOption {
|
||||
|
@ -191,8 +201,7 @@ in
|
|||
_module.args = {
|
||||
hostPkgs =
|
||||
# Comment is in nixos/modules/misc/nixpkgs.nix
|
||||
lib.mkOverride lib.modules.defaultOverridePriority
|
||||
config.hostPkgs.__splicedPackages;
|
||||
lib.mkOverride lib.modules.defaultOverridePriority config.hostPkgs.__splicedPackages;
|
||||
};
|
||||
|
||||
driver = withChecks driver;
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
# nix-build '<nixpkgs/nixos>' -A config.system.build.openstackImage --arg configuration "{ imports = [ ./nixos/maintainers/scripts/openstack/openstack-image.nix ]; }"
|
||||
|
||||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
copyChannel = true;
|
||||
in
|
||||
|
@ -16,12 +21,11 @@ in
|
|||
additionalSpace = "1024M";
|
||||
pkgs = import ../../../.. { inherit (pkgs) system; }; # ensure we use the regular qemu-kvm package
|
||||
format = "qcow2";
|
||||
configFile = pkgs.writeText "configuration.nix"
|
||||
''
|
||||
{
|
||||
imports = [ <nixpkgs/nixos/modules/virtualisation/openstack-config.nix> ];
|
||||
}
|
||||
'';
|
||||
configFile = pkgs.writeText "configuration.nix" ''
|
||||
{
|
||||
imports = [ <nixpkgs/nixos/modules/virtualisation/openstack-config.nix> ];
|
||||
}
|
||||
'';
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -1,18 +1,26 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.console;
|
||||
|
||||
makeColor = i: lib.concatMapStringsSep "," (x: "0x" + lib.substring (2*i) 2 x);
|
||||
makeColor = i: lib.concatMapStringsSep "," (x: "0x" + lib.substring (2 * i) 2 x);
|
||||
|
||||
isUnicode = lib.hasSuffix "UTF-8" (lib.toUpper config.i18n.defaultLocale);
|
||||
|
||||
optimizedKeymap = pkgs.runCommand "keymap" {
|
||||
nativeBuildInputs = [ pkgs.buildPackages.kbd ];
|
||||
LOADKEYS_KEYMAP_PATH = "${consoleEnv pkgs.kbd}/share/keymaps/**";
|
||||
preferLocalBuild = true;
|
||||
} ''
|
||||
loadkeys -b ${lib.optionalString isUnicode "-u"} "${cfg.keyMap}" > $out
|
||||
'';
|
||||
optimizedKeymap =
|
||||
pkgs.runCommand "keymap"
|
||||
{
|
||||
nativeBuildInputs = [ pkgs.buildPackages.kbd ];
|
||||
LOADKEYS_KEYMAP_PATH = "${consoleEnv pkgs.kbd}/share/keymaps/**";
|
||||
preferLocalBuild = true;
|
||||
}
|
||||
''
|
||||
loadkeys -b ${lib.optionalString isUnicode "-u"} "${cfg.keyMap}" > $out
|
||||
'';
|
||||
|
||||
# Sadly, systemd-vconsole-setup doesn't support binary keymaps.
|
||||
vconsoleConf = pkgs.writeText "vconsole.conf" ''
|
||||
|
@ -20,22 +28,24 @@ let
|
|||
${lib.optionalString (cfg.font != null) "FONT=${cfg.font}"}
|
||||
'';
|
||||
|
||||
consoleEnv = kbd: pkgs.buildEnv {
|
||||
name = "console-env";
|
||||
paths = [ kbd ] ++ cfg.packages;
|
||||
pathsToLink = [
|
||||
"/share/consolefonts"
|
||||
"/share/consoletrans"
|
||||
"/share/keymaps"
|
||||
"/share/unimaps"
|
||||
];
|
||||
};
|
||||
consoleEnv =
|
||||
kbd:
|
||||
pkgs.buildEnv {
|
||||
name = "console-env";
|
||||
paths = [ kbd ] ++ cfg.packages;
|
||||
pathsToLink = [
|
||||
"/share/consolefonts"
|
||||
"/share/consoletrans"
|
||||
"/share/keymaps"
|
||||
"/share/unimaps"
|
||||
];
|
||||
};
|
||||
in
|
||||
|
||||
{
|
||||
###### interface
|
||||
|
||||
options.console = {
|
||||
options.console = {
|
||||
enable = lib.mkEnableOption "virtual console" // {
|
||||
default = true;
|
||||
};
|
||||
|
@ -70,10 +80,22 @@ in
|
|||
type = with lib.types; listOf (strMatching "[[:xdigit:]]{6}");
|
||||
default = [ ];
|
||||
example = [
|
||||
"002b36" "dc322f" "859900" "b58900"
|
||||
"268bd2" "d33682" "2aa198" "eee8d5"
|
||||
"002b36" "cb4b16" "586e75" "657b83"
|
||||
"839496" "6c71c4" "93a1a1" "fdf6e3"
|
||||
"002b36"
|
||||
"dc322f"
|
||||
"859900"
|
||||
"b58900"
|
||||
"268bd2"
|
||||
"d33682"
|
||||
"2aa198"
|
||||
"eee8d5"
|
||||
"002b36"
|
||||
"cb4b16"
|
||||
"586e75"
|
||||
"657b83"
|
||||
"839496"
|
||||
"6c71c4"
|
||||
"93a1a1"
|
||||
"fdf6e3"
|
||||
];
|
||||
description = ''
|
||||
The 16 colors palette used by the virtual consoles.
|
||||
|
@ -112,20 +134,24 @@ in
|
|||
|
||||
};
|
||||
|
||||
|
||||
###### implementation
|
||||
|
||||
config = lib.mkMerge [
|
||||
{ console.keyMap = with config.services.xserver;
|
||||
lib.mkIf cfg.useXkbConfig
|
||||
(pkgs.runCommand "xkb-console-keymap" { preferLocalBuild = true; } ''
|
||||
{
|
||||
console.keyMap =
|
||||
with config.services.xserver;
|
||||
lib.mkIf cfg.useXkbConfig (
|
||||
pkgs.runCommand "xkb-console-keymap" { preferLocalBuild = true; } ''
|
||||
'${pkgs.buildPackages.ckbcomp}/bin/ckbcomp' \
|
||||
${lib.optionalString (config.environment.sessionVariables ? XKB_CONFIG_ROOT)
|
||||
"-I${config.environment.sessionVariables.XKB_CONFIG_ROOT}"
|
||||
${
|
||||
lib.optionalString (
|
||||
config.environment.sessionVariables ? XKB_CONFIG_ROOT
|
||||
) "-I${config.environment.sessionVariables.XKB_CONFIG_ROOT}"
|
||||
} \
|
||||
-model '${xkb.model}' -layout '${xkb.layout}' \
|
||||
-option '${xkb.options}' -variant '${xkb.variant}' > "$out"
|
||||
'');
|
||||
''
|
||||
);
|
||||
}
|
||||
|
||||
(lib.mkIf (!cfg.enable) {
|
||||
|
@ -138,83 +164,103 @@ in
|
|||
};
|
||||
})
|
||||
|
||||
(lib.mkIf cfg.enable (lib.mkMerge [
|
||||
{ environment.systemPackages = [ pkgs.kbd ];
|
||||
(lib.mkIf cfg.enable (
|
||||
lib.mkMerge [
|
||||
{
|
||||
environment.systemPackages = [ pkgs.kbd ];
|
||||
|
||||
# Let systemd-vconsole-setup.service do the work of setting up the
|
||||
# virtual consoles.
|
||||
environment.etc."vconsole.conf".source = vconsoleConf;
|
||||
# Provide kbd with additional packages.
|
||||
environment.etc.kbd.source = "${consoleEnv pkgs.kbd}/share";
|
||||
# Let systemd-vconsole-setup.service do the work of setting up the
|
||||
# virtual consoles.
|
||||
environment.etc."vconsole.conf".source = vconsoleConf;
|
||||
# Provide kbd with additional packages.
|
||||
environment.etc.kbd.source = "${consoleEnv pkgs.kbd}/share";
|
||||
|
||||
boot.initrd.preLVMCommands = lib.mkIf (!config.boot.initrd.systemd.enable) (lib.mkBefore ''
|
||||
kbd_mode ${if isUnicode then "-u" else "-a"} -C /dev/console
|
||||
printf "\033%%${if isUnicode then "G" else "@"}" >> /dev/console
|
||||
loadkmap < ${optimizedKeymap}
|
||||
boot.initrd.preLVMCommands = lib.mkIf (!config.boot.initrd.systemd.enable) (
|
||||
lib.mkBefore ''
|
||||
kbd_mode ${if isUnicode then "-u" else "-a"} -C /dev/console
|
||||
printf "\033%%${if isUnicode then "G" else "@"}" >> /dev/console
|
||||
loadkmap < ${optimizedKeymap}
|
||||
|
||||
${lib.optionalString (cfg.earlySetup && cfg.font != null) ''
|
||||
setfont -C /dev/console $extraUtils/share/consolefonts/font.psf
|
||||
''}
|
||||
'');
|
||||
${lib.optionalString (cfg.earlySetup && cfg.font != null) ''
|
||||
setfont -C /dev/console $extraUtils/share/consolefonts/font.psf
|
||||
''}
|
||||
''
|
||||
);
|
||||
|
||||
boot.initrd.systemd.contents = {
|
||||
"/etc/vconsole.conf".source = vconsoleConf;
|
||||
# Add everything if we want full console setup...
|
||||
"/etc/kbd" = lib.mkIf cfg.earlySetup { source = "${consoleEnv config.boot.initrd.systemd.package.kbd}/share"; };
|
||||
# ...but only the keymaps if we don't
|
||||
"/etc/kbd/keymaps" = lib.mkIf (!cfg.earlySetup) { source = "${consoleEnv config.boot.initrd.systemd.package.kbd}/share/keymaps"; };
|
||||
};
|
||||
boot.initrd.systemd.additionalUpstreamUnits = [
|
||||
"systemd-vconsole-setup.service"
|
||||
];
|
||||
boot.initrd.systemd.storePaths = [
|
||||
"${config.boot.initrd.systemd.package}/lib/systemd/systemd-vconsole-setup"
|
||||
"${config.boot.initrd.systemd.package.kbd}/bin/setfont"
|
||||
"${config.boot.initrd.systemd.package.kbd}/bin/loadkeys"
|
||||
"${config.boot.initrd.systemd.package.kbd.gzip}/bin/gzip" # Fonts and keyboard layouts are compressed
|
||||
] ++ lib.optionals (cfg.font != null && lib.hasPrefix builtins.storeDir cfg.font) [
|
||||
"${cfg.font}"
|
||||
] ++ lib.optionals (lib.hasPrefix builtins.storeDir cfg.keyMap) [
|
||||
"${cfg.keyMap}"
|
||||
];
|
||||
|
||||
systemd.services.reload-systemd-vconsole-setup =
|
||||
{ description = "Reset console on configuration changes";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
restartTriggers = [ vconsoleConf (consoleEnv pkgs.kbd) ];
|
||||
reloadIfChanged = true;
|
||||
serviceConfig =
|
||||
{ RemainAfterExit = true;
|
||||
ExecStart = "${pkgs.coreutils}/bin/true";
|
||||
ExecReload = "/run/current-system/systemd/bin/systemctl restart systemd-vconsole-setup";
|
||||
};
|
||||
boot.initrd.systemd.contents = {
|
||||
"/etc/vconsole.conf".source = vconsoleConf;
|
||||
# Add everything if we want full console setup...
|
||||
"/etc/kbd" = lib.mkIf cfg.earlySetup {
|
||||
source = "${consoleEnv config.boot.initrd.systemd.package.kbd}/share";
|
||||
};
|
||||
# ...but only the keymaps if we don't
|
||||
"/etc/kbd/keymaps" = lib.mkIf (!cfg.earlySetup) {
|
||||
source = "${consoleEnv config.boot.initrd.systemd.package.kbd}/share/keymaps";
|
||||
};
|
||||
};
|
||||
}
|
||||
boot.initrd.systemd.additionalUpstreamUnits = [
|
||||
"systemd-vconsole-setup.service"
|
||||
];
|
||||
boot.initrd.systemd.storePaths =
|
||||
[
|
||||
"${config.boot.initrd.systemd.package}/lib/systemd/systemd-vconsole-setup"
|
||||
"${config.boot.initrd.systemd.package.kbd}/bin/setfont"
|
||||
"${config.boot.initrd.systemd.package.kbd}/bin/loadkeys"
|
||||
"${config.boot.initrd.systemd.package.kbd.gzip}/bin/gzip" # Fonts and keyboard layouts are compressed
|
||||
]
|
||||
++ lib.optionals (cfg.font != null && lib.hasPrefix builtins.storeDir cfg.font) [
|
||||
"${cfg.font}"
|
||||
]
|
||||
++ lib.optionals (lib.hasPrefix builtins.storeDir cfg.keyMap) [
|
||||
"${cfg.keyMap}"
|
||||
];
|
||||
|
||||
(lib.mkIf (cfg.colors != []) {
|
||||
boot.kernelParams = [
|
||||
"vt.default_red=${makeColor 0 cfg.colors}"
|
||||
"vt.default_grn=${makeColor 1 cfg.colors}"
|
||||
"vt.default_blu=${makeColor 2 cfg.colors}"
|
||||
];
|
||||
})
|
||||
systemd.services.reload-systemd-vconsole-setup = {
|
||||
description = "Reset console on configuration changes";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
restartTriggers = [
|
||||
vconsoleConf
|
||||
(consoleEnv pkgs.kbd)
|
||||
];
|
||||
reloadIfChanged = true;
|
||||
serviceConfig = {
|
||||
RemainAfterExit = true;
|
||||
ExecStart = "${pkgs.coreutils}/bin/true";
|
||||
ExecReload = "/run/current-system/systemd/bin/systemctl restart systemd-vconsole-setup";
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
(lib.mkIf (cfg.earlySetup && cfg.font != null && !config.boot.initrd.systemd.enable) {
|
||||
boot.initrd.extraUtilsCommands = ''
|
||||
mkdir -p $out/share/consolefonts
|
||||
${if lib.substring 0 1 cfg.font == "/" then ''
|
||||
font="${cfg.font}"
|
||||
'' else ''
|
||||
font="$(echo ${consoleEnv pkgs.kbd}/share/consolefonts/${cfg.font}.*)"
|
||||
''}
|
||||
if [[ $font == *.gz ]]; then
|
||||
gzip -cd $font > $out/share/consolefonts/font.psf
|
||||
else
|
||||
cp -L $font $out/share/consolefonts/font.psf
|
||||
fi
|
||||
'';
|
||||
})
|
||||
]))
|
||||
(lib.mkIf (cfg.colors != [ ]) {
|
||||
boot.kernelParams = [
|
||||
"vt.default_red=${makeColor 0 cfg.colors}"
|
||||
"vt.default_grn=${makeColor 1 cfg.colors}"
|
||||
"vt.default_blu=${makeColor 2 cfg.colors}"
|
||||
];
|
||||
})
|
||||
|
||||
(lib.mkIf (cfg.earlySetup && cfg.font != null && !config.boot.initrd.systemd.enable) {
|
||||
boot.initrd.extraUtilsCommands = ''
|
||||
mkdir -p $out/share/consolefonts
|
||||
${
|
||||
if lib.substring 0 1 cfg.font == "/" then
|
||||
''
|
||||
font="${cfg.font}"
|
||||
''
|
||||
else
|
||||
''
|
||||
font="$(echo ${consoleEnv pkgs.kbd}/share/consolefonts/${cfg.font}.*)"
|
||||
''
|
||||
}
|
||||
if [[ $font == *.gz ]]; then
|
||||
gzip -cd $font > $out/share/consolefonts/font.psf
|
||||
else
|
||||
cp -L $font $out/share/consolefonts/font.psf
|
||||
fi
|
||||
'';
|
||||
})
|
||||
]
|
||||
))
|
||||
];
|
||||
|
||||
imports = [
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
{
|
||||
###### interface
|
||||
|
||||
|
@ -39,8 +44,11 @@
|
|||
|
||||
extraLocaleSettings = lib.mkOption {
|
||||
type = lib.types.attrsOf lib.types.str;
|
||||
default = {};
|
||||
example = { LC_MESSAGES = "en_US.UTF-8"; LC_TIME = "de_DE.UTF-8"; };
|
||||
default = { };
|
||||
example = {
|
||||
LC_MESSAGES = "en_US.UTF-8";
|
||||
LC_TIME = "de_DE.UTF-8";
|
||||
};
|
||||
description = ''
|
||||
A set of additional system-wide locale settings other than
|
||||
`LANG` which can be configured with
|
||||
|
@ -50,14 +58,18 @@
|
|||
|
||||
supportedLocales = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = lib.unique
|
||||
(builtins.map (l: (lib.replaceStrings [ "utf8" "utf-8" "UTF8" ] [ "UTF-8" "UTF-8" "UTF-8" ] l) + "/UTF-8") (
|
||||
[
|
||||
"C.UTF-8"
|
||||
"en_US.UTF-8"
|
||||
config.i18n.defaultLocale
|
||||
] ++ (lib.attrValues (lib.filterAttrs (n: v: n != "LANGUAGE") config.i18n.extraLocaleSettings))
|
||||
));
|
||||
default = lib.unique (
|
||||
builtins.map
|
||||
(l: (lib.replaceStrings [ "utf8" "utf-8" "UTF8" ] [ "UTF-8" "UTF-8" "UTF-8" ] l) + "/UTF-8")
|
||||
(
|
||||
[
|
||||
"C.UTF-8"
|
||||
"en_US.UTF-8"
|
||||
config.i18n.defaultLocale
|
||||
]
|
||||
++ (lib.attrValues (lib.filterAttrs (n: v: n != "LANGUAGE") config.i18n.extraLocaleSettings))
|
||||
)
|
||||
);
|
||||
defaultText = lib.literalExpression ''
|
||||
lib.unique
|
||||
(builtins.map (l: (lib.replaceStrings [ "utf8" "utf-8" "UTF8" ] [ "UTF-8" "UTF-8" "UTF-8" ] l) + "/UTF-8") (
|
||||
|
@ -68,7 +80,11 @@
|
|||
] ++ (lib.attrValues (lib.filterAttrs (n: v: n != "LANGUAGE") config.i18n.extraLocaleSettings))
|
||||
))
|
||||
'';
|
||||
example = ["en_US.UTF-8/UTF-8" "nl_NL.UTF-8/UTF-8" "nl_NL/ISO-8859-1"];
|
||||
example = [
|
||||
"en_US.UTF-8/UTF-8"
|
||||
"nl_NL.UTF-8/UTF-8"
|
||||
"nl_NL/ISO-8859-1"
|
||||
];
|
||||
description = ''
|
||||
List of locales that the system should support. The value
|
||||
`"all"` means that all locales supported by
|
||||
|
@ -81,30 +97,30 @@
|
|||
|
||||
};
|
||||
|
||||
|
||||
###### implementation
|
||||
|
||||
config = {
|
||||
|
||||
environment.systemPackages =
|
||||
# We increase the priority a little, so that plain glibc in systemPackages can't win.
|
||||
lib.optional (config.i18n.supportedLocales != []) (lib.setPrio (-1) config.i18n.glibcLocales);
|
||||
lib.optional (config.i18n.supportedLocales != [ ]) (lib.setPrio (-1) config.i18n.glibcLocales);
|
||||
|
||||
environment.sessionVariables =
|
||||
{ LANG = config.i18n.defaultLocale;
|
||||
LOCALE_ARCHIVE = "/run/current-system/sw/lib/locale/locale-archive";
|
||||
} // config.i18n.extraLocaleSettings;
|
||||
environment.sessionVariables = {
|
||||
LANG = config.i18n.defaultLocale;
|
||||
LOCALE_ARCHIVE = "/run/current-system/sw/lib/locale/locale-archive";
|
||||
} // config.i18n.extraLocaleSettings;
|
||||
|
||||
systemd.globalEnvironment = lib.mkIf (config.i18n.supportedLocales != []) {
|
||||
systemd.globalEnvironment = lib.mkIf (config.i18n.supportedLocales != [ ]) {
|
||||
LOCALE_ARCHIVE = "${config.i18n.glibcLocales}/lib/locale/locale-archive";
|
||||
};
|
||||
|
||||
# ‘/etc/locale.conf’ is used by systemd.
|
||||
environment.etc."locale.conf".source = pkgs.writeText "locale.conf"
|
||||
''
|
||||
LANG=${config.i18n.defaultLocale}
|
||||
${lib.concatStringsSep "\n" (lib.mapAttrsToList (n: v: "${n}=${v}") config.i18n.extraLocaleSettings)}
|
||||
'';
|
||||
environment.etc."locale.conf".source = pkgs.writeText "locale.conf" ''
|
||||
LANG=${config.i18n.defaultLocale}
|
||||
${lib.concatStringsSep "\n" (
|
||||
lib.mapAttrsToList (n: v: "${n}=${v}") config.i18n.extraLocaleSettings
|
||||
)}
|
||||
'';
|
||||
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,11 +1,24 @@
|
|||
# /etc files related to networking, such as /etc/services.
|
||||
{ config, lib, options, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
options,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
|
||||
cfg = config.networking;
|
||||
opt = options.networking;
|
||||
|
||||
localhostMultiple = lib.any (lib.elem "localhost") (lib.attrValues (removeAttrs cfg.hosts [ "127.0.0.1" "::1" ]));
|
||||
localhostMultiple = lib.any (lib.elem "localhost") (
|
||||
lib.attrValues (
|
||||
removeAttrs cfg.hosts [
|
||||
"127.0.0.1"
|
||||
"::1"
|
||||
]
|
||||
)
|
||||
);
|
||||
|
||||
in
|
||||
|
||||
|
@ -136,7 +149,7 @@ in
|
|||
envVars = lib.mkOption {
|
||||
type = lib.types.attrs;
|
||||
internal = true;
|
||||
default = {};
|
||||
default = { };
|
||||
description = ''
|
||||
Environment variables used for the network proxy.
|
||||
'';
|
||||
|
@ -146,50 +159,63 @@ in
|
|||
|
||||
config = {
|
||||
|
||||
assertions = [{
|
||||
assertion = !localhostMultiple;
|
||||
message = ''
|
||||
`networking.hosts` maps "localhost" to something other than "127.0.0.1"
|
||||
or "::1". This will break some applications. Please use
|
||||
`networking.extraHosts` if you really want to add such a mapping.
|
||||
'';
|
||||
}];
|
||||
assertions = [
|
||||
{
|
||||
assertion = !localhostMultiple;
|
||||
message = ''
|
||||
`networking.hosts` maps "localhost" to something other than "127.0.0.1"
|
||||
or "::1". This will break some applications. Please use
|
||||
`networking.extraHosts` if you really want to add such a mapping.
|
||||
'';
|
||||
}
|
||||
];
|
||||
|
||||
# These entries are required for "hostname -f" and to resolve both the
|
||||
# hostname and FQDN correctly:
|
||||
networking.hosts = let
|
||||
hostnames = # Note: The FQDN (canonical hostname) has to come first:
|
||||
lib.optional (cfg.hostName != "" && cfg.domain != null) "${cfg.hostName}.${cfg.domain}"
|
||||
++ lib.optional (cfg.hostName != "") cfg.hostName; # Then the hostname (without the domain)
|
||||
in {
|
||||
"127.0.0.2" = hostnames;
|
||||
} // lib.optionalAttrs cfg.enableIPv6 {
|
||||
"::1" = hostnames;
|
||||
};
|
||||
networking.hosts =
|
||||
let
|
||||
hostnames = # Note: The FQDN (canonical hostname) has to come first:
|
||||
lib.optional (cfg.hostName != "" && cfg.domain != null) "${cfg.hostName}.${cfg.domain}"
|
||||
++ lib.optional (cfg.hostName != "") cfg.hostName; # Then the hostname (without the domain)
|
||||
in
|
||||
{
|
||||
"127.0.0.2" = hostnames;
|
||||
}
|
||||
// lib.optionalAttrs cfg.enableIPv6 {
|
||||
"::1" = hostnames;
|
||||
};
|
||||
|
||||
networking.hostFiles = let
|
||||
# Note: localhostHosts has to appear first in /etc/hosts so that 127.0.0.1
|
||||
# resolves back to "localhost" (as some applications assume) instead of
|
||||
# the FQDN! By default "networking.hosts" also contains entries for the
|
||||
# FQDN so that e.g. "hostname -f" works correctly.
|
||||
localhostHosts = pkgs.writeText "localhost-hosts" ''
|
||||
127.0.0.1 localhost
|
||||
${lib.optionalString cfg.enableIPv6 "::1 localhost"}
|
||||
'';
|
||||
stringHosts =
|
||||
let
|
||||
oneToString = set: ip: ip + " " + lib.concatStringsSep " " set.${ip} + "\n";
|
||||
allToString = set: lib.concatMapStrings (oneToString set) (lib.attrNames set);
|
||||
in pkgs.writeText "string-hosts" (allToString (lib.filterAttrs (_: v: v != []) cfg.hosts));
|
||||
extraHosts = pkgs.writeText "extra-hosts" cfg.extraHosts;
|
||||
in lib.mkBefore [ localhostHosts stringHosts extraHosts ];
|
||||
networking.hostFiles =
|
||||
let
|
||||
# Note: localhostHosts has to appear first in /etc/hosts so that 127.0.0.1
|
||||
# resolves back to "localhost" (as some applications assume) instead of
|
||||
# the FQDN! By default "networking.hosts" also contains entries for the
|
||||
# FQDN so that e.g. "hostname -f" works correctly.
|
||||
localhostHosts = pkgs.writeText "localhost-hosts" ''
|
||||
127.0.0.1 localhost
|
||||
${lib.optionalString cfg.enableIPv6 "::1 localhost"}
|
||||
'';
|
||||
stringHosts =
|
||||
let
|
||||
oneToString = set: ip: ip + " " + lib.concatStringsSep " " set.${ip} + "\n";
|
||||
allToString = set: lib.concatMapStrings (oneToString set) (lib.attrNames set);
|
||||
in
|
||||
pkgs.writeText "string-hosts" (allToString (lib.filterAttrs (_: v: v != [ ]) cfg.hosts));
|
||||
extraHosts = pkgs.writeText "extra-hosts" cfg.extraHosts;
|
||||
in
|
||||
lib.mkBefore [
|
||||
localhostHosts
|
||||
stringHosts
|
||||
extraHosts
|
||||
];
|
||||
|
||||
environment.etc =
|
||||
{ # /etc/services: TCP/UDP port assignments.
|
||||
{
|
||||
# /etc/services: TCP/UDP port assignments.
|
||||
services.source = pkgs.iana-etc + "/etc/services";
|
||||
|
||||
# /etc/protocols: IP protocol numbers.
|
||||
protocols.source = pkgs.iana-etc + "/etc/protocols";
|
||||
protocols.source = pkgs.iana-etc + "/etc/protocols";
|
||||
|
||||
# /etc/hosts: Hostname-to-IP mappings.
|
||||
hosts.source = pkgs.concatText "hosts" cfg.hostFiles;
|
||||
|
@ -202,28 +228,35 @@ in
|
|||
multi on
|
||||
'';
|
||||
|
||||
} // lib.optionalAttrs (pkgs.stdenv.hostPlatform.libc == "glibc") {
|
||||
}
|
||||
// lib.optionalAttrs (pkgs.stdenv.hostPlatform.libc == "glibc") {
|
||||
# /etc/rpc: RPC program numbers.
|
||||
rpc.source = pkgs.stdenv.cc.libc.out + "/etc/rpc";
|
||||
};
|
||||
|
||||
networking.proxy.envVars =
|
||||
lib.optionalAttrs (cfg.proxy.default != null) {
|
||||
# other options already fallback to proxy.default
|
||||
no_proxy = "127.0.0.1,localhost";
|
||||
} // lib.optionalAttrs (cfg.proxy.httpProxy != null) {
|
||||
http_proxy = cfg.proxy.httpProxy;
|
||||
} // lib.optionalAttrs (cfg.proxy.httpsProxy != null) {
|
||||
https_proxy = cfg.proxy.httpsProxy;
|
||||
} // lib.optionalAttrs (cfg.proxy.rsyncProxy != null) {
|
||||
rsync_proxy = cfg.proxy.rsyncProxy;
|
||||
} // lib.optionalAttrs (cfg.proxy.ftpProxy != null) {
|
||||
ftp_proxy = cfg.proxy.ftpProxy;
|
||||
} // lib.optionalAttrs (cfg.proxy.allProxy != null) {
|
||||
all_proxy = cfg.proxy.allProxy;
|
||||
} // lib.optionalAttrs (cfg.proxy.noProxy != null) {
|
||||
no_proxy = cfg.proxy.noProxy;
|
||||
};
|
||||
networking.proxy.envVars =
|
||||
lib.optionalAttrs (cfg.proxy.default != null) {
|
||||
# other options already fallback to proxy.default
|
||||
no_proxy = "127.0.0.1,localhost";
|
||||
}
|
||||
// lib.optionalAttrs (cfg.proxy.httpProxy != null) {
|
||||
http_proxy = cfg.proxy.httpProxy;
|
||||
}
|
||||
// lib.optionalAttrs (cfg.proxy.httpsProxy != null) {
|
||||
https_proxy = cfg.proxy.httpsProxy;
|
||||
}
|
||||
// lib.optionalAttrs (cfg.proxy.rsyncProxy != null) {
|
||||
rsync_proxy = cfg.proxy.rsyncProxy;
|
||||
}
|
||||
// lib.optionalAttrs (cfg.proxy.ftpProxy != null) {
|
||||
ftp_proxy = cfg.proxy.ftpProxy;
|
||||
}
|
||||
// lib.optionalAttrs (cfg.proxy.allProxy != null) {
|
||||
all_proxy = cfg.proxy.allProxy;
|
||||
}
|
||||
// lib.optionalAttrs (cfg.proxy.noProxy != null) {
|
||||
no_proxy = cfg.proxy.noProxy;
|
||||
};
|
||||
|
||||
# Install the proxy environment variables
|
||||
environment.sessionVariables = cfg.proxy.envVars;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
See also
|
||||
- ./nix.nix
|
||||
- ./nix-flakes.nix
|
||||
*/
|
||||
*/
|
||||
{ config, lib, ... }:
|
||||
let
|
||||
inherit (lib)
|
||||
|
@ -42,13 +42,14 @@ in
|
|||
nixPath = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default =
|
||||
if cfg.channel.enable
|
||||
then [
|
||||
"nixpkgs=/nix/var/nix/profiles/per-user/root/channels/nixos"
|
||||
"nixos-config=/etc/nixos/configuration.nix"
|
||||
"/nix/var/nix/profiles/per-user/root/channels"
|
||||
]
|
||||
else [ ];
|
||||
if cfg.channel.enable then
|
||||
[
|
||||
"nixpkgs=/nix/var/nix/profiles/per-user/root/channels/nixos"
|
||||
"nixos-config=/etc/nixos/configuration.nix"
|
||||
"/nix/var/nix/profiles/per-user/root/channels"
|
||||
]
|
||||
else
|
||||
[ ];
|
||||
defaultText = ''
|
||||
if nix.channel.enable
|
||||
then [
|
||||
|
@ -78,12 +79,11 @@ in
|
|||
|
||||
config = mkIf cfg.enable {
|
||||
|
||||
environment.extraInit =
|
||||
mkIf cfg.channel.enable ''
|
||||
if [ -e "$HOME/.nix-defexpr/channels" ]; then
|
||||
export NIX_PATH="$HOME/.nix-defexpr/channels''${NIX_PATH:+:$NIX_PATH}"
|
||||
fi
|
||||
'';
|
||||
environment.extraInit = mkIf cfg.channel.enable ''
|
||||
if [ -e "$HOME/.nix-defexpr/channels" ]; then
|
||||
export NIX_PATH="$HOME/.nix-defexpr/channels''${NIX_PATH:+:$NIX_PATH}"
|
||||
fi
|
||||
'';
|
||||
|
||||
environment.extraSetup = mkIf (!cfg.channel.enable) ''
|
||||
rm --force $out/bin/nix-channel
|
||||
|
@ -99,7 +99,8 @@ in
|
|||
''f /root/.nix-channels - - - - ${config.system.defaultChannel} nixos\n''
|
||||
];
|
||||
|
||||
system.activationScripts.no-nix-channel = mkIf (!cfg.channel.enable)
|
||||
(stringAfter [ "etc" "users" ] (builtins.readFile ./nix-channel/activation-check.sh));
|
||||
system.activationScripts.no-nix-channel = mkIf (!cfg.channel.enable) (
|
||||
stringAfter [ "etc" "users" ] (builtins.readFile ./nix-channel/activation-check.sh)
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -6,8 +6,13 @@
|
|||
- ./nix-flakes.nix
|
||||
- ./nix-remote-build.nix
|
||||
- nixos/modules/services/system/nix-daemon.nix
|
||||
*/
|
||||
{ config, lib, pkgs, ... }:
|
||||
*/
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
inherit (lib)
|
||||
|
@ -61,19 +66,21 @@ let
|
|||
systemFeatures = "system-features";
|
||||
};
|
||||
|
||||
semanticConfType = with types;
|
||||
semanticConfType =
|
||||
with types;
|
||||
let
|
||||
confAtom = nullOr
|
||||
(oneOf [
|
||||
confAtom =
|
||||
nullOr (oneOf [
|
||||
bool
|
||||
int
|
||||
float
|
||||
str
|
||||
path
|
||||
package
|
||||
]) // {
|
||||
description = "Nix config atom (null, bool, int, float, str, path or package)";
|
||||
};
|
||||
])
|
||||
// {
|
||||
description = "Nix config atom (null, bool, int, float, str, path or package)";
|
||||
};
|
||||
in
|
||||
attrsOf (either confAtom (listOf confAtom));
|
||||
|
||||
|
@ -81,17 +88,28 @@ let
|
|||
assert isNixAtLeast "2.2";
|
||||
let
|
||||
|
||||
mkValueString = v:
|
||||
if v == null then ""
|
||||
else if isInt v then toString v
|
||||
else if isBool v then boolToString v
|
||||
else if isFloat v then floatToString v
|
||||
else if isList v then toString v
|
||||
else if isDerivation v then toString v
|
||||
else if builtins.isPath v then toString v
|
||||
else if isString v then v
|
||||
else if strings.isConvertibleWithToString v then toString v
|
||||
else abort "The nix conf value: ${toPretty {} v} can not be encoded";
|
||||
mkValueString =
|
||||
v:
|
||||
if v == null then
|
||||
""
|
||||
else if isInt v then
|
||||
toString v
|
||||
else if isBool v then
|
||||
boolToString v
|
||||
else if isFloat v then
|
||||
floatToString v
|
||||
else if isList v then
|
||||
toString v
|
||||
else if isDerivation v then
|
||||
toString v
|
||||
else if builtins.isPath v then
|
||||
toString v
|
||||
else if isString v then
|
||||
v
|
||||
else if strings.isConvertibleWithToString v then
|
||||
toString v
|
||||
else
|
||||
abort "The nix conf value: ${toPretty { } v} can not be encoded";
|
||||
|
||||
mkKeyValue = k: v: "${escape [ "=" ] k} = ${mkValueString v}";
|
||||
|
||||
|
@ -113,41 +131,71 @@ let
|
|||
${cfg.extraOptions}
|
||||
'';
|
||||
checkPhase = lib.optionalString cfg.checkConfig (
|
||||
if pkgs.stdenv.hostPlatform != pkgs.stdenv.buildPlatform then ''
|
||||
echo "Ignoring validation for cross-compilation"
|
||||
''
|
||||
if pkgs.stdenv.hostPlatform != pkgs.stdenv.buildPlatform then
|
||||
''
|
||||
echo "Ignoring validation for cross-compilation"
|
||||
''
|
||||
else
|
||||
let
|
||||
showCommand = if isNixAtLeast "2.20pre" then "config show" else "show-config";
|
||||
in
|
||||
''
|
||||
echo "Validating generated nix.conf"
|
||||
ln -s $out ./nix.conf
|
||||
set -e
|
||||
set +o pipefail
|
||||
NIX_CONF_DIR=$PWD \
|
||||
${cfg.package}/bin/nix ${showCommand} ${optionalString (isNixAtLeast "2.3pre") "--no-net"} \
|
||||
${optionalString (isNixAtLeast "2.4pre") "--option experimental-features nix-command"} \
|
||||
|& sed -e 's/^warning:/error:/' \
|
||||
| (! grep '${if cfg.checkAllErrors then "^error:" else "^error: unknown setting"}')
|
||||
set -o pipefail
|
||||
'');
|
||||
let
|
||||
showCommand = if isNixAtLeast "2.20pre" then "config show" else "show-config";
|
||||
in
|
||||
''
|
||||
echo "Validating generated nix.conf"
|
||||
ln -s $out ./nix.conf
|
||||
set -e
|
||||
set +o pipefail
|
||||
NIX_CONF_DIR=$PWD \
|
||||
${cfg.package}/bin/nix ${showCommand} ${optionalString (isNixAtLeast "2.3pre") "--no-net"} \
|
||||
${optionalString (isNixAtLeast "2.4pre") "--option experimental-features nix-command"} \
|
||||
|& sed -e 's/^warning:/error:/' \
|
||||
| (! grep '${if cfg.checkAllErrors then "^error:" else "^error: unknown setting"}')
|
||||
set -o pipefail
|
||||
''
|
||||
);
|
||||
};
|
||||
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
(mkRenamedOptionModuleWith { sinceRelease = 2003; from = [ "nix" "useChroot" ]; to = [ "nix" "useSandbox" ]; })
|
||||
(mkRenamedOptionModuleWith { sinceRelease = 2003; from = [ "nix" "chrootDirs" ]; to = [ "nix" "sandboxPaths" ]; })
|
||||
] ++
|
||||
mapAttrsToList
|
||||
(oldConf: newConf:
|
||||
mkRenamedOptionModuleWith {
|
||||
sinceRelease = 2205;
|
||||
from = [ "nix" oldConf ];
|
||||
to = [ "nix" "settings" newConf ];
|
||||
imports =
|
||||
[
|
||||
(mkRenamedOptionModuleWith {
|
||||
sinceRelease = 2003;
|
||||
from = [
|
||||
"nix"
|
||||
"useChroot"
|
||||
];
|
||||
to = [
|
||||
"nix"
|
||||
"useSandbox"
|
||||
];
|
||||
})
|
||||
legacyConfMappings;
|
||||
(mkRenamedOptionModuleWith {
|
||||
sinceRelease = 2003;
|
||||
from = [
|
||||
"nix"
|
||||
"chrootDirs"
|
||||
];
|
||||
to = [
|
||||
"nix"
|
||||
"sandboxPaths"
|
||||
];
|
||||
})
|
||||
]
|
||||
++ mapAttrsToList (
|
||||
oldConf: newConf:
|
||||
mkRenamedOptionModuleWith {
|
||||
sinceRelease = 2205;
|
||||
from = [
|
||||
"nix"
|
||||
oldConf
|
||||
];
|
||||
to = [
|
||||
"nix"
|
||||
"settings"
|
||||
newConf
|
||||
];
|
||||
}
|
||||
) legacyConfMappings;
|
||||
|
||||
options = {
|
||||
nix = {
|
||||
|
@ -246,7 +294,10 @@ in
|
|||
extra-sandbox-paths = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [ ];
|
||||
example = [ "/dev" "/proc" ];
|
||||
example = [
|
||||
"/dev"
|
||||
"/proc"
|
||||
];
|
||||
description = ''
|
||||
Directories from the host filesystem to be included
|
||||
in the sandbox.
|
||||
|
@ -302,7 +353,11 @@ in
|
|||
|
||||
trusted-users = mkOption {
|
||||
type = types.listOf types.str;
|
||||
example = [ "root" "alice" "@wheel" ];
|
||||
example = [
|
||||
"root"
|
||||
"alice"
|
||||
"@wheel"
|
||||
];
|
||||
description = ''
|
||||
A list of names of users that have additional rights when
|
||||
connecting to the Nix daemon, such as the ability to specify
|
||||
|
@ -316,7 +371,11 @@ in
|
|||
|
||||
system-features = mkOption {
|
||||
type = types.listOf types.str;
|
||||
example = [ "kvm" "big-parallel" "gccarch-skylake" ];
|
||||
example = [
|
||||
"kvm"
|
||||
"big-parallel"
|
||||
"gccarch-skylake"
|
||||
];
|
||||
description = ''
|
||||
The set of features supported by the machine. Derivations
|
||||
can express dependencies on system features through the
|
||||
|
@ -331,7 +390,12 @@ in
|
|||
allowed-users = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [ "*" ];
|
||||
example = [ "@wheel" "@builders" "alice" "bob" ];
|
||||
example = [
|
||||
"@wheel"
|
||||
"@builders"
|
||||
"alice"
|
||||
"bob"
|
||||
];
|
||||
description = ''
|
||||
A list of names of users (separated by whitespace) that are
|
||||
allowed to connect to the Nix daemon. As with
|
||||
|
@ -378,11 +442,18 @@ in
|
|||
trusted-users = [ "root" ];
|
||||
substituters = mkAfter [ "https://cache.nixos.org/" ];
|
||||
system-features = mkDefault (
|
||||
[ "nixos-test" "benchmark" "big-parallel" "kvm" ] ++
|
||||
optionals (pkgs.stdenv.hostPlatform ? gcc.arch) (
|
||||
[
|
||||
"nixos-test"
|
||||
"benchmark"
|
||||
"big-parallel"
|
||||
"kvm"
|
||||
]
|
||||
++ optionals (pkgs.stdenv.hostPlatform ? gcc.arch) (
|
||||
# a builder can run code for `gcc.arch` and inferior architectures
|
||||
[ "gccarch-${pkgs.stdenv.hostPlatform.gcc.arch}" ] ++
|
||||
map (x: "gccarch-${x}") (systems.architectures.inferiors.${pkgs.stdenv.hostPlatform.gcc.arch} or [])
|
||||
[ "gccarch-${pkgs.stdenv.hostPlatform.gcc.arch}" ]
|
||||
++ map (x: "gccarch-${x}") (
|
||||
systems.architectures.inferiors.${pkgs.stdenv.hostPlatform.gcc.arch} or [ ]
|
||||
)
|
||||
)
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,27 +1,35 @@
|
|||
# This module defines a global environment configuration and
|
||||
# a common configuration for all shells.
|
||||
{ config, lib, utils, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
utils,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
|
||||
cfg = config.environment;
|
||||
|
||||
exportedEnvVars =
|
||||
let
|
||||
absoluteVariables =
|
||||
lib.mapAttrs (n: lib.toList) cfg.variables;
|
||||
absoluteVariables = lib.mapAttrs (n: lib.toList) cfg.variables;
|
||||
|
||||
suffixedVariables =
|
||||
lib.flip lib.mapAttrs cfg.profileRelativeEnvVars (envVar: listSuffixes:
|
||||
lib.concatMap (profile: map (suffix: "${profile}${suffix}") listSuffixes) cfg.profiles
|
||||
);
|
||||
suffixedVariables = lib.flip lib.mapAttrs cfg.profileRelativeEnvVars (
|
||||
envVar: listSuffixes:
|
||||
lib.concatMap (profile: map (suffix: "${profile}${suffix}") listSuffixes) cfg.profiles
|
||||
);
|
||||
|
||||
allVariables =
|
||||
lib.zipAttrsWith (n: lib.concatLists) [ absoluteVariables suffixedVariables ];
|
||||
allVariables = lib.zipAttrsWith (n: lib.concatLists) [
|
||||
absoluteVariables
|
||||
suffixedVariables
|
||||
];
|
||||
|
||||
exportVariables =
|
||||
lib.mapAttrsToList (n: v: ''export ${n}="${lib.concatStringsSep ":" v}"'') allVariables;
|
||||
exportVariables = lib.mapAttrsToList (
|
||||
n: v: ''export ${n}="${lib.concatStringsSep ":" v}"''
|
||||
) allVariables;
|
||||
in
|
||||
lib.concatStringsSep "\n" exportVariables;
|
||||
lib.concatStringsSep "\n" exportVariables;
|
||||
in
|
||||
|
||||
{
|
||||
|
@ -29,8 +37,11 @@ in
|
|||
options = {
|
||||
|
||||
environment.variables = lib.mkOption {
|
||||
default = {};
|
||||
example = { EDITOR = "nvim"; VISUAL = "nvim"; };
|
||||
default = { };
|
||||
example = {
|
||||
EDITOR = "nvim";
|
||||
VISUAL = "nvim";
|
||||
};
|
||||
description = ''
|
||||
A set of environment variables used in the global environment.
|
||||
These variables will be set on shell initialisation (e.g. in /etc/profile).
|
||||
|
@ -38,14 +49,27 @@ in
|
|||
strings. The latter is concatenated, interspersed with colon
|
||||
characters.
|
||||
'';
|
||||
type = with lib.types; attrsOf (oneOf [ (listOf (oneOf [ int str path ])) int str path ]);
|
||||
apply = let
|
||||
toStr = v: if lib.isPath v then "${v}" else toString v;
|
||||
in lib.mapAttrs (n: v: if lib.isList v then lib.concatMapStringsSep ":" toStr v else toStr v);
|
||||
type =
|
||||
with lib.types;
|
||||
attrsOf (oneOf [
|
||||
(listOf (oneOf [
|
||||
int
|
||||
str
|
||||
path
|
||||
]))
|
||||
int
|
||||
str
|
||||
path
|
||||
]);
|
||||
apply =
|
||||
let
|
||||
toStr = v: if lib.isPath v then "${v}" else toString v;
|
||||
in
|
||||
lib.mapAttrs (n: v: if lib.isList v then lib.concatMapStringsSep ":" toStr v else toStr v);
|
||||
};
|
||||
|
||||
environment.profiles = lib.mkOption {
|
||||
default = [];
|
||||
default = [ ];
|
||||
description = ''
|
||||
A list of profiles used to setup the global environment.
|
||||
'';
|
||||
|
@ -54,7 +78,13 @@ in
|
|||
|
||||
environment.profileRelativeEnvVars = lib.mkOption {
|
||||
type = lib.types.attrsOf (lib.types.listOf lib.types.str);
|
||||
example = { PATH = [ "/bin" ]; MANPATH = [ "/man" "/share/man" ]; };
|
||||
example = {
|
||||
PATH = [ "/bin" ];
|
||||
MANPATH = [
|
||||
"/man"
|
||||
"/share/man"
|
||||
];
|
||||
};
|
||||
description = ''
|
||||
Attribute set of environment variable. Each attribute maps to a list
|
||||
of relative paths. Each relative path is appended to the each profile
|
||||
|
@ -106,7 +136,10 @@ in
|
|||
};
|
||||
|
||||
environment.shellAliases = lib.mkOption {
|
||||
example = { l = null; ll = "ls -l"; };
|
||||
example = {
|
||||
l = null;
|
||||
ll = "ls -l";
|
||||
};
|
||||
description = ''
|
||||
An attribute set that maps aliases (the top level attribute names in
|
||||
this option) to command strings or directly to build outputs. The
|
||||
|
@ -147,7 +180,7 @@ in
|
|||
};
|
||||
|
||||
environment.shells = lib.mkOption {
|
||||
default = [];
|
||||
default = [ ];
|
||||
example = lib.literalExpression "[ pkgs.bashInteractive pkgs.zsh ]";
|
||||
description = ''
|
||||
A list of permissible login shells for user accounts.
|
||||
|
@ -174,49 +207,46 @@ in
|
|||
environment.shellAliases = lib.mapAttrs (name: lib.mkDefault) {
|
||||
ls = "ls --color=tty";
|
||||
ll = "ls -l";
|
||||
l = "ls -alh";
|
||||
l = "ls -alh";
|
||||
};
|
||||
|
||||
environment.etc.shells.text =
|
||||
''
|
||||
${lib.concatStringsSep "\n" (map utils.toShellPath cfg.shells)}
|
||||
/bin/sh
|
||||
'';
|
||||
environment.etc.shells.text = ''
|
||||
${lib.concatStringsSep "\n" (map utils.toShellPath cfg.shells)}
|
||||
/bin/sh
|
||||
'';
|
||||
|
||||
# For resetting environment with `. /etc/set-environment` when needed
|
||||
# and discoverability (see motivation of #30418).
|
||||
environment.etc.set-environment.source = config.system.build.setEnvironment;
|
||||
|
||||
system.build.setEnvironment = pkgs.writeText "set-environment"
|
||||
''
|
||||
# DO NOT EDIT -- this file has been generated automatically.
|
||||
system.build.setEnvironment = pkgs.writeText "set-environment" ''
|
||||
# DO NOT EDIT -- this file has been generated automatically.
|
||||
|
||||
# Prevent this file from being sourced by child shells.
|
||||
export __NIXOS_SET_ENVIRONMENT_DONE=1
|
||||
# Prevent this file from being sourced by child shells.
|
||||
export __NIXOS_SET_ENVIRONMENT_DONE=1
|
||||
|
||||
${exportedEnvVars}
|
||||
${exportedEnvVars}
|
||||
|
||||
${cfg.extraInit}
|
||||
${cfg.extraInit}
|
||||
|
||||
${lib.optionalString cfg.homeBinInPath ''
|
||||
# ~/bin if it exists overrides other bin directories.
|
||||
export PATH="$HOME/bin:$PATH"
|
||||
''}
|
||||
${lib.optionalString cfg.homeBinInPath ''
|
||||
# ~/bin if it exists overrides other bin directories.
|
||||
export PATH="$HOME/bin:$PATH"
|
||||
''}
|
||||
|
||||
${lib.optionalString cfg.localBinInPath ''
|
||||
export PATH="$HOME/.local/bin:$PATH"
|
||||
''}
|
||||
'';
|
||||
${lib.optionalString cfg.localBinInPath ''
|
||||
export PATH="$HOME/.local/bin:$PATH"
|
||||
''}
|
||||
'';
|
||||
|
||||
system.activationScripts.binsh = lib.stringAfter [ "stdio" ]
|
||||
''
|
||||
# Create the required /bin/sh symlink; otherwise lots of things
|
||||
# (notably the system() function) won't work.
|
||||
mkdir -p /bin
|
||||
chmod 0755 /bin
|
||||
ln -sfn "${cfg.binsh}" /bin/.sh.tmp
|
||||
mv /bin/.sh.tmp /bin/sh # atomically replace /bin/sh
|
||||
'';
|
||||
system.activationScripts.binsh = lib.stringAfter [ "stdio" ] ''
|
||||
# Create the required /bin/sh symlink; otherwise lots of things
|
||||
# (notably the system() function) won't work.
|
||||
mkdir -p /bin
|
||||
chmod 0755 /bin
|
||||
ln -sfn "${cfg.binsh}" /bin/.sh.tmp
|
||||
mv /bin/.sh.tmp /bin/sh # atomically replace /bin/sh
|
||||
'';
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -1,196 +1,212 @@
|
|||
{ config, lib, pkgs, utils, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
utils,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
inherit (lib) mkIf mkOption types;
|
||||
|
||||
randomEncryptionCoerce = enable: { inherit enable; };
|
||||
|
||||
randomEncryptionOpts = { ... }: {
|
||||
randomEncryptionOpts =
|
||||
{ ... }:
|
||||
{
|
||||
|
||||
options = {
|
||||
options = {
|
||||
|
||||
enable = mkOption {
|
||||
default = false;
|
||||
type = types.bool;
|
||||
description = ''
|
||||
Encrypt swap device with a random key. This way you won't have a persistent swap device.
|
||||
enable = mkOption {
|
||||
default = false;
|
||||
type = types.bool;
|
||||
description = ''
|
||||
Encrypt swap device with a random key. This way you won't have a persistent swap device.
|
||||
|
||||
WARNING: Don't try to hibernate when you have at least one swap partition with
|
||||
this option enabled! We have no way to set the partition into which hibernation image
|
||||
is saved, so if your image ends up on an encrypted one you would lose it!
|
||||
WARNING: Don't try to hibernate when you have at least one swap partition with
|
||||
this option enabled! We have no way to set the partition into which hibernation image
|
||||
is saved, so if your image ends up on an encrypted one you would lose it!
|
||||
|
||||
WARNING #2: Do not use /dev/disk/by-uuid/… or /dev/disk/by-label/… as your swap device
|
||||
when using randomEncryption as the UUIDs and labels will get erased on every boot when
|
||||
the partition is encrypted. Best to use /dev/disk/by-partuuid/…
|
||||
'';
|
||||
};
|
||||
|
||||
cipher = mkOption {
|
||||
default = "aes-xts-plain64";
|
||||
example = "serpent-xts-plain64";
|
||||
type = types.str;
|
||||
description = ''
|
||||
Use specified cipher for randomEncryption.
|
||||
|
||||
Hint: Run "cryptsetup benchmark" to see which one is fastest on your machine.
|
||||
'';
|
||||
};
|
||||
|
||||
keySize = mkOption {
|
||||
default = null;
|
||||
example = "512";
|
||||
type = types.nullOr types.int;
|
||||
description = ''
|
||||
Set the encryption key size for the plain device.
|
||||
|
||||
If not specified, the amount of data to read from `source` will be
|
||||
determined by cryptsetup.
|
||||
|
||||
See `cryptsetup-open(8)` for details.
|
||||
'';
|
||||
};
|
||||
|
||||
sectorSize = mkOption {
|
||||
default = null;
|
||||
example = "4096";
|
||||
type = types.nullOr types.int;
|
||||
description = ''
|
||||
Set the sector size for the plain encrypted device type.
|
||||
|
||||
If not specified, the default sector size is determined from the
|
||||
underlying block device.
|
||||
|
||||
See `cryptsetup-open(8)` for details.
|
||||
'';
|
||||
};
|
||||
|
||||
source = mkOption {
|
||||
default = "/dev/urandom";
|
||||
example = "/dev/random";
|
||||
type = types.str;
|
||||
description = ''
|
||||
Define the source of randomness to obtain a random key for encryption.
|
||||
'';
|
||||
};
|
||||
|
||||
allowDiscards = mkOption {
|
||||
default = false;
|
||||
type = types.bool;
|
||||
description = ''
|
||||
Whether to allow TRIM requests to the underlying device. This option
|
||||
has security implications; please read the LUKS documentation before
|
||||
activating it.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
swapCfg = {config, options, ...}: {
|
||||
|
||||
options = {
|
||||
|
||||
device = mkOption {
|
||||
example = "/dev/sda3";
|
||||
type = types.nonEmptyStr;
|
||||
description = "Path of the device or swap file.";
|
||||
};
|
||||
|
||||
label = mkOption {
|
||||
example = "swap";
|
||||
type = types.str;
|
||||
description = ''
|
||||
Label of the device. Can be used instead of {var}`device`.
|
||||
'';
|
||||
};
|
||||
|
||||
size = mkOption {
|
||||
default = null;
|
||||
example = 2048;
|
||||
type = types.nullOr types.int;
|
||||
description = ''
|
||||
If this option is set, ‘device’ is interpreted as the
|
||||
path of a swapfile that will be created automatically
|
||||
with the indicated size (in megabytes).
|
||||
'';
|
||||
};
|
||||
|
||||
priority = mkOption {
|
||||
default = null;
|
||||
example = 2048;
|
||||
type = types.nullOr types.int;
|
||||
description = ''
|
||||
Specify the priority of the swap device. Priority is a value between 0 and 32767.
|
||||
Higher numbers indicate higher priority.
|
||||
null lets the kernel choose a priority, which will show up as a negative value.
|
||||
'';
|
||||
};
|
||||
|
||||
randomEncryption = mkOption {
|
||||
default = false;
|
||||
example = {
|
||||
enable = true;
|
||||
cipher = "serpent-xts-plain64";
|
||||
source = "/dev/random";
|
||||
WARNING #2: Do not use /dev/disk/by-uuid/… or /dev/disk/by-label/… as your swap device
|
||||
when using randomEncryption as the UUIDs and labels will get erased on every boot when
|
||||
the partition is encrypted. Best to use /dev/disk/by-partuuid/…
|
||||
'';
|
||||
};
|
||||
type = types.coercedTo types.bool randomEncryptionCoerce (types.submodule randomEncryptionOpts);
|
||||
description = ''
|
||||
Encrypt swap device with a random key. This way you won't have a persistent swap device.
|
||||
|
||||
HINT: run "cryptsetup benchmark" to test cipher performance on your machine.
|
||||
cipher = mkOption {
|
||||
default = "aes-xts-plain64";
|
||||
example = "serpent-xts-plain64";
|
||||
type = types.str;
|
||||
description = ''
|
||||
Use specified cipher for randomEncryption.
|
||||
|
||||
WARNING: Don't try to hibernate when you have at least one swap partition with
|
||||
this option enabled! We have no way to set the partition into which hibernation image
|
||||
is saved, so if your image ends up on an encrypted one you would lose it!
|
||||
Hint: Run "cryptsetup benchmark" to see which one is fastest on your machine.
|
||||
'';
|
||||
};
|
||||
|
||||
WARNING #2: Do not use /dev/disk/by-uuid/… or /dev/disk/by-label/… as your swap device
|
||||
when using randomEncryption as the UUIDs and labels will get erased on every boot when
|
||||
the partition is encrypted. Best to use /dev/disk/by-partuuid/…
|
||||
'';
|
||||
};
|
||||
keySize = mkOption {
|
||||
default = null;
|
||||
example = "512";
|
||||
type = types.nullOr types.int;
|
||||
description = ''
|
||||
Set the encryption key size for the plain device.
|
||||
|
||||
discardPolicy = mkOption {
|
||||
default = null;
|
||||
example = "once";
|
||||
type = types.nullOr (types.enum ["once" "pages" "both" ]);
|
||||
description = ''
|
||||
Specify the discard policy for the swap device. If "once", then the
|
||||
whole swap space is discarded at swapon invocation. If "pages",
|
||||
asynchronous discard on freed pages is performed, before returning to
|
||||
the available pages pool. With "both", both policies are activated.
|
||||
See swapon(8) for more information.
|
||||
'';
|
||||
};
|
||||
If not specified, the amount of data to read from `source` will be
|
||||
determined by cryptsetup.
|
||||
|
||||
options = mkOption {
|
||||
default = [ "defaults" ];
|
||||
example = [ "nofail" ];
|
||||
type = types.listOf types.nonEmptyStr;
|
||||
description = ''
|
||||
Options used to mount the swap.
|
||||
'';
|
||||
};
|
||||
See `cryptsetup-open(8)` for details.
|
||||
'';
|
||||
};
|
||||
|
||||
deviceName = mkOption {
|
||||
type = types.str;
|
||||
internal = true;
|
||||
};
|
||||
sectorSize = mkOption {
|
||||
default = null;
|
||||
example = "4096";
|
||||
type = types.nullOr types.int;
|
||||
description = ''
|
||||
Set the sector size for the plain encrypted device type.
|
||||
|
||||
realDevice = mkOption {
|
||||
type = types.path;
|
||||
internal = true;
|
||||
If not specified, the default sector size is determined from the
|
||||
underlying block device.
|
||||
|
||||
See `cryptsetup-open(8)` for details.
|
||||
'';
|
||||
};
|
||||
|
||||
source = mkOption {
|
||||
default = "/dev/urandom";
|
||||
example = "/dev/random";
|
||||
type = types.str;
|
||||
description = ''
|
||||
Define the source of randomness to obtain a random key for encryption.
|
||||
'';
|
||||
};
|
||||
|
||||
allowDiscards = mkOption {
|
||||
default = false;
|
||||
type = types.bool;
|
||||
description = ''
|
||||
Whether to allow TRIM requests to the underlying device. This option
|
||||
has security implications; please read the LUKS documentation before
|
||||
activating it.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
config = {
|
||||
device = mkIf options.label.isDefined
|
||||
"/dev/disk/by-label/${config.label}";
|
||||
deviceName = lib.replaceStrings ["\\"] [""] (utils.escapeSystemdPath config.device);
|
||||
realDevice = if config.randomEncryption.enable then "/dev/mapper/${config.deviceName}" else config.device;
|
||||
};
|
||||
swapCfg =
|
||||
{ config, options, ... }:
|
||||
{
|
||||
|
||||
};
|
||||
options = {
|
||||
|
||||
device = mkOption {
|
||||
example = "/dev/sda3";
|
||||
type = types.nonEmptyStr;
|
||||
description = "Path of the device or swap file.";
|
||||
};
|
||||
|
||||
label = mkOption {
|
||||
example = "swap";
|
||||
type = types.str;
|
||||
description = ''
|
||||
Label of the device. Can be used instead of {var}`device`.
|
||||
'';
|
||||
};
|
||||
|
||||
size = mkOption {
|
||||
default = null;
|
||||
example = 2048;
|
||||
type = types.nullOr types.int;
|
||||
description = ''
|
||||
If this option is set, ‘device’ is interpreted as the
|
||||
path of a swapfile that will be created automatically
|
||||
with the indicated size (in megabytes).
|
||||
'';
|
||||
};
|
||||
|
||||
priority = mkOption {
|
||||
default = null;
|
||||
example = 2048;
|
||||
type = types.nullOr types.int;
|
||||
description = ''
|
||||
Specify the priority of the swap device. Priority is a value between 0 and 32767.
|
||||
Higher numbers indicate higher priority.
|
||||
null lets the kernel choose a priority, which will show up as a negative value.
|
||||
'';
|
||||
};
|
||||
|
||||
randomEncryption = mkOption {
|
||||
default = false;
|
||||
example = {
|
||||
enable = true;
|
||||
cipher = "serpent-xts-plain64";
|
||||
source = "/dev/random";
|
||||
};
|
||||
type = types.coercedTo types.bool randomEncryptionCoerce (types.submodule randomEncryptionOpts);
|
||||
description = ''
|
||||
Encrypt swap device with a random key. This way you won't have a persistent swap device.
|
||||
|
||||
HINT: run "cryptsetup benchmark" to test cipher performance on your machine.
|
||||
|
||||
WARNING: Don't try to hibernate when you have at least one swap partition with
|
||||
this option enabled! We have no way to set the partition into which hibernation image
|
||||
is saved, so if your image ends up on an encrypted one you would lose it!
|
||||
|
||||
WARNING #2: Do not use /dev/disk/by-uuid/… or /dev/disk/by-label/… as your swap device
|
||||
when using randomEncryption as the UUIDs and labels will get erased on every boot when
|
||||
the partition is encrypted. Best to use /dev/disk/by-partuuid/…
|
||||
'';
|
||||
};
|
||||
|
||||
discardPolicy = mkOption {
|
||||
default = null;
|
||||
example = "once";
|
||||
type = types.nullOr (
|
||||
types.enum [
|
||||
"once"
|
||||
"pages"
|
||||
"both"
|
||||
]
|
||||
);
|
||||
description = ''
|
||||
Specify the discard policy for the swap device. If "once", then the
|
||||
whole swap space is discarded at swapon invocation. If "pages",
|
||||
asynchronous discard on freed pages is performed, before returning to
|
||||
the available pages pool. With "both", both policies are activated.
|
||||
See swapon(8) for more information.
|
||||
'';
|
||||
};
|
||||
|
||||
options = mkOption {
|
||||
default = [ "defaults" ];
|
||||
example = [ "nofail" ];
|
||||
type = types.listOf types.nonEmptyStr;
|
||||
description = ''
|
||||
Options used to mount the swap.
|
||||
'';
|
||||
};
|
||||
|
||||
deviceName = mkOption {
|
||||
type = types.str;
|
||||
internal = true;
|
||||
};
|
||||
|
||||
realDevice = mkOption {
|
||||
type = types.path;
|
||||
internal = true;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
config = {
|
||||
device = mkIf options.label.isDefined "/dev/disk/by-label/${config.label}";
|
||||
deviceName = lib.replaceStrings [ "\\" ] [ "" ] (utils.escapeSystemdPath config.device);
|
||||
realDevice =
|
||||
if config.randomEncryption.enable then "/dev/mapper/${config.deviceName}" else config.device;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
in
|
||||
|
||||
|
@ -201,7 +217,7 @@ in
|
|||
options = {
|
||||
|
||||
swapDevices = mkOption {
|
||||
default = [];
|
||||
default = [ ];
|
||||
example = [
|
||||
{ device = "/dev/hda7"; }
|
||||
{ device = "/var/swapfile"; }
|
||||
|
@ -224,7 +240,8 @@ in
|
|||
|
||||
config = mkIf ((lib.length config.swapDevices) != 0) {
|
||||
assertions = lib.map (sw: {
|
||||
assertion = sw.randomEncryption.enable -> builtins.match "/dev/disk/by-(uuid|label)/.*" sw.device == null;
|
||||
assertion =
|
||||
sw.randomEncryption.enable -> builtins.match "/dev/disk/by-(uuid|label)/.*" sw.device == null;
|
||||
message = ''
|
||||
You cannot use swap device "${sw.device}" with randomEncryption enabled.
|
||||
The UUIDs and labels will get erased on every boot when the partition is encrypted.
|
||||
|
@ -232,12 +249,13 @@ in
|
|||
'';
|
||||
}) config.swapDevices;
|
||||
|
||||
warnings =
|
||||
lib.concatMap (sw:
|
||||
if sw.size != null && lib.hasPrefix "/dev/" sw.device
|
||||
then [ "Setting the swap size of block device ${sw.device} has no effect" ]
|
||||
else [ ])
|
||||
config.swapDevices;
|
||||
warnings = lib.concatMap (
|
||||
sw:
|
||||
if sw.size != null && lib.hasPrefix "/dev/" sw.device then
|
||||
[ "Setting the swap size of block device ${sw.device} has no effect" ]
|
||||
else
|
||||
[ ]
|
||||
) config.swapDevices;
|
||||
|
||||
system.requiredKernelConfig = [
|
||||
(config.lib.kernelConfig.isYes "SWAP")
|
||||
|
@ -246,47 +264,62 @@ in
|
|||
# Create missing swapfiles.
|
||||
systemd.services =
|
||||
let
|
||||
createSwapDevice = sw:
|
||||
let realDevice' = utils.escapeSystemdPath sw.realDevice;
|
||||
in lib.nameValuePair "mkswap-${sw.deviceName}"
|
||||
{ description = "Initialisation of swap device ${sw.device}";
|
||||
createSwapDevice =
|
||||
sw:
|
||||
let
|
||||
realDevice' = utils.escapeSystemdPath sw.realDevice;
|
||||
in
|
||||
lib.nameValuePair "mkswap-${sw.deviceName}" {
|
||||
description = "Initialisation of swap device ${sw.device}";
|
||||
# The mkswap service fails for file-backed swap devices if the
|
||||
# loop module has not been loaded before the service runs.
|
||||
# We add an ordering constraint to run after systemd-modules-load to
|
||||
# avoid this race condition.
|
||||
after = [ "systemd-modules-load.service" ];
|
||||
wantedBy = [ "${realDevice'}.swap" ];
|
||||
before = [ "${realDevice'}.swap" "shutdown.target"];
|
||||
before = [
|
||||
"${realDevice'}.swap"
|
||||
"shutdown.target"
|
||||
];
|
||||
conflicts = [ "shutdown.target" ];
|
||||
path = [ pkgs.util-linux pkgs.e2fsprogs ]
|
||||
++ lib.optional sw.randomEncryption.enable pkgs.cryptsetup;
|
||||
path = [
|
||||
pkgs.util-linux
|
||||
pkgs.e2fsprogs
|
||||
] ++ lib.optional sw.randomEncryption.enable pkgs.cryptsetup;
|
||||
|
||||
environment.DEVICE = sw.device;
|
||||
|
||||
script =
|
||||
''
|
||||
${lib.optionalString (sw.size != null) ''
|
||||
currentSize=$(( $(stat -c "%s" "$DEVICE" 2>/dev/null || echo 0) / 1024 / 1024 ))
|
||||
if [[ ! -b "$DEVICE" && "${toString sw.size}" != "$currentSize" ]]; then
|
||||
# Disable CoW for CoW based filesystems like BTRFS.
|
||||
truncate --size 0 "$DEVICE"
|
||||
chattr +C "$DEVICE" 2>/dev/null || true
|
||||
script = ''
|
||||
${lib.optionalString (sw.size != null) ''
|
||||
currentSize=$(( $(stat -c "%s" "$DEVICE" 2>/dev/null || echo 0) / 1024 / 1024 ))
|
||||
if [[ ! -b "$DEVICE" && "${toString sw.size}" != "$currentSize" ]]; then
|
||||
# Disable CoW for CoW based filesystems like BTRFS.
|
||||
truncate --size 0 "$DEVICE"
|
||||
chattr +C "$DEVICE" 2>/dev/null || true
|
||||
|
||||
echo "Creating swap file using dd and mkswap."
|
||||
dd if=/dev/zero of="$DEVICE" bs=1M count=${toString sw.size} status=progress
|
||||
${lib.optionalString (!sw.randomEncryption.enable) "mkswap ${sw.realDevice}"}
|
||||
fi
|
||||
''}
|
||||
${lib.optionalString sw.randomEncryption.enable ''
|
||||
cryptsetup plainOpen -c ${sw.randomEncryption.cipher} -d ${sw.randomEncryption.source} \
|
||||
${lib.concatStringsSep " \\\n" (lib.flatten [
|
||||
(lib.optional (sw.randomEncryption.sectorSize != null) "--sector-size=${toString sw.randomEncryption.sectorSize}")
|
||||
(lib.optional (sw.randomEncryption.keySize != null) "--key-size=${toString sw.randomEncryption.keySize}")
|
||||
(lib.optional sw.randomEncryption.allowDiscards "--allow-discards")
|
||||
])} ${sw.device} ${sw.deviceName}
|
||||
mkswap ${sw.realDevice}
|
||||
''}
|
||||
'';
|
||||
echo "Creating swap file using dd and mkswap."
|
||||
dd if=/dev/zero of="$DEVICE" bs=1M count=${toString sw.size} status=progress
|
||||
${lib.optionalString (!sw.randomEncryption.enable) "mkswap ${sw.realDevice}"}
|
||||
fi
|
||||
''}
|
||||
${lib.optionalString sw.randomEncryption.enable ''
|
||||
cryptsetup plainOpen -c ${sw.randomEncryption.cipher} -d ${sw.randomEncryption.source} \
|
||||
${
|
||||
lib.concatStringsSep " \\\n" (
|
||||
lib.flatten [
|
||||
(lib.optional (
|
||||
sw.randomEncryption.sectorSize != null
|
||||
) "--sector-size=${toString sw.randomEncryption.sectorSize}")
|
||||
(lib.optional (
|
||||
sw.randomEncryption.keySize != null
|
||||
) "--key-size=${toString sw.randomEncryption.keySize}")
|
||||
(lib.optional sw.randomEncryption.allowDiscards "--allow-discards")
|
||||
]
|
||||
)
|
||||
} ${sw.device} ${sw.deviceName}
|
||||
mkswap ${sw.realDevice}
|
||||
''}
|
||||
'';
|
||||
|
||||
unitConfig.RequiresMountsFor = [ "${dirOf sw.device}" ];
|
||||
unitConfig.DefaultDependencies = false; # needed to prevent a cycle
|
||||
|
@ -299,7 +332,12 @@ in
|
|||
restartIfChanged = false;
|
||||
};
|
||||
|
||||
in lib.listToAttrs (lib.map createSwapDevice (lib.filter (sw: sw.size != null || sw.randomEncryption.enable) config.swapDevices));
|
||||
in
|
||||
lib.listToAttrs (
|
||||
lib.map createSwapDevice (
|
||||
lib.filter (sw: sw.size != null || sw.randomEncryption.enable) config.swapDevices
|
||||
)
|
||||
);
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
# This module defines a system-wide environment that will be
|
||||
# initialised by pam_env (that is, not only in shells).
|
||||
{ config, lib, options, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
options,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
|
||||
cfg = config.environment;
|
||||
|
@ -12,7 +18,7 @@ in
|
|||
options = {
|
||||
|
||||
environment.sessionVariables = lib.mkOption {
|
||||
default = {};
|
||||
default = { };
|
||||
description = ''
|
||||
A set of environment variables used in the global environment.
|
||||
These variables will be set by PAM early in the login process.
|
||||
|
@ -34,7 +40,13 @@ in
|
|||
|
||||
environment.profileRelativeSessionVariables = lib.mkOption {
|
||||
type = lib.types.attrsOf (lib.types.listOf lib.types.str);
|
||||
example = { PATH = [ "/bin" ]; MANPATH = [ "/man" "/share/man" ]; };
|
||||
example = {
|
||||
PATH = [ "/bin" ];
|
||||
MANPATH = [
|
||||
"/man"
|
||||
"/share/man"
|
||||
];
|
||||
};
|
||||
description = ''
|
||||
Attribute set of environment variable used in the global
|
||||
environment. These variables will be set by PAM early in the
|
||||
|
@ -58,40 +70,40 @@ in
|
|||
};
|
||||
|
||||
config = {
|
||||
environment.etc."pam/environment".text = let
|
||||
suffixedVariables =
|
||||
lib.flip lib.mapAttrs cfg.profileRelativeSessionVariables (envVar: suffixes:
|
||||
lib.flip lib.concatMap cfg.profiles (profile:
|
||||
map (suffix: "${profile}${suffix}") suffixes
|
||||
)
|
||||
environment.etc."pam/environment".text =
|
||||
let
|
||||
suffixedVariables = lib.flip lib.mapAttrs cfg.profileRelativeSessionVariables (
|
||||
envVar: suffixes:
|
||||
lib.flip lib.concatMap cfg.profiles (profile: map (suffix: "${profile}${suffix}") suffixes)
|
||||
);
|
||||
|
||||
# We're trying to use the same syntax for PAM variables and env variables.
|
||||
# That means we need to map the env variables that people might use to their
|
||||
# equivalent PAM variable.
|
||||
replaceEnvVars = lib.replaceStrings ["$HOME" "$USER"] ["@{HOME}" "@{PAM_USER}"];
|
||||
# We're trying to use the same syntax for PAM variables and env variables.
|
||||
# That means we need to map the env variables that people might use to their
|
||||
# equivalent PAM variable.
|
||||
replaceEnvVars = lib.replaceStrings [ "$HOME" "$USER" ] [ "@{HOME}" "@{PAM_USER}" ];
|
||||
|
||||
pamVariable = n: v:
|
||||
''${n} DEFAULT="${lib.concatStringsSep ":" (map replaceEnvVars (lib.toList v))}"'';
|
||||
pamVariable =
|
||||
n: v: ''${n} DEFAULT="${lib.concatStringsSep ":" (map replaceEnvVars (lib.toList v))}"'';
|
||||
|
||||
pamVariables =
|
||||
lib.concatStringsSep "\n"
|
||||
(lib.mapAttrsToList pamVariable
|
||||
(lib.zipAttrsWith (n: lib.concatLists)
|
||||
[
|
||||
# Make sure security wrappers are prioritized without polluting
|
||||
# shell environments with an extra entry. Sessions which depend on
|
||||
# pam for its environment will otherwise have eg. broken sudo. In
|
||||
# particular Gnome Shell sometimes fails to source a proper
|
||||
# environment from a shell.
|
||||
{ PATH = [ config.security.wrapperDir ]; }
|
||||
pamVariables = lib.concatStringsSep "\n" (
|
||||
lib.mapAttrsToList pamVariable (
|
||||
lib.zipAttrsWith (n: lib.concatLists) [
|
||||
# Make sure security wrappers are prioritized without polluting
|
||||
# shell environments with an extra entry. Sessions which depend on
|
||||
# pam for its environment will otherwise have eg. broken sudo. In
|
||||
# particular Gnome Shell sometimes fails to source a proper
|
||||
# environment from a shell.
|
||||
{ PATH = [ config.security.wrapperDir ]; }
|
||||
|
||||
(lib.mapAttrs (n: lib.toList) cfg.sessionVariables)
|
||||
suffixedVariables
|
||||
]));
|
||||
in ''
|
||||
${pamVariables}
|
||||
'';
|
||||
(lib.mapAttrs (n: lib.toList) cfg.sessionVariables)
|
||||
suffixedVariables
|
||||
]
|
||||
)
|
||||
);
|
||||
in
|
||||
''
|
||||
${pamVariables}
|
||||
'';
|
||||
};
|
||||
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,9 +1,14 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.xdg.mime;
|
||||
associationOptions = with lib.types; attrsOf (
|
||||
coercedTo (either (listOf str) str) (x: lib.concatStringsSep ";" (lib.toList x)) str
|
||||
);
|
||||
associationOptions =
|
||||
with lib.types;
|
||||
attrsOf (coercedTo (either (listOf str) str) (x: lib.concatStringsSep ";" (lib.toList x)) str);
|
||||
in
|
||||
|
||||
{
|
||||
|
@ -24,10 +29,13 @@ in
|
|||
|
||||
xdg.mime.addedAssociations = lib.mkOption {
|
||||
type = associationOptions;
|
||||
default = {};
|
||||
default = { };
|
||||
example = {
|
||||
"application/pdf" = "firefox.desktop";
|
||||
"text/xml" = [ "nvim.desktop" "codium.desktop" ];
|
||||
"text/xml" = [
|
||||
"nvim.desktop"
|
||||
"codium.desktop"
|
||||
];
|
||||
};
|
||||
description = ''
|
||||
Adds associations between mimetypes and applications. See the
|
||||
|
@ -38,10 +46,13 @@ in
|
|||
|
||||
xdg.mime.defaultApplications = lib.mkOption {
|
||||
type = associationOptions;
|
||||
default = {};
|
||||
default = { };
|
||||
example = {
|
||||
"application/pdf" = "firefox.desktop";
|
||||
"image/png" = [ "sxiv.desktop" "gimp.desktop" ];
|
||||
"image/png" = [
|
||||
"sxiv.desktop"
|
||||
"gimp.desktop"
|
||||
];
|
||||
};
|
||||
description = ''
|
||||
Sets the default applications for given mimetypes. See the
|
||||
|
@ -52,9 +63,12 @@ in
|
|||
|
||||
xdg.mime.removedAssociations = lib.mkOption {
|
||||
type = associationOptions;
|
||||
default = {};
|
||||
default = { };
|
||||
example = {
|
||||
"audio/mp3" = [ "mpv.desktop" "umpv.desktop" ];
|
||||
"audio/mp3" = [
|
||||
"mpv.desktop"
|
||||
"umpv.desktop"
|
||||
];
|
||||
"inode/directory" = "codium.desktop";
|
||||
};
|
||||
description = ''
|
||||
|
@ -66,17 +80,16 @@ in
|
|||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
environment.etc."xdg/mimeapps.list" = lib.mkIf (
|
||||
cfg.addedAssociations != {}
|
||||
|| cfg.defaultApplications != {}
|
||||
|| cfg.removedAssociations != {}
|
||||
) {
|
||||
text = lib.generators.toINI { } {
|
||||
"Added Associations" = cfg.addedAssociations;
|
||||
"Default Applications" = cfg.defaultApplications;
|
||||
"Removed Associations" = cfg.removedAssociations;
|
||||
};
|
||||
};
|
||||
environment.etc."xdg/mimeapps.list" =
|
||||
lib.mkIf
|
||||
(cfg.addedAssociations != { } || cfg.defaultApplications != { } || cfg.removedAssociations != { })
|
||||
{
|
||||
text = lib.generators.toINI { } {
|
||||
"Added Associations" = cfg.addedAssociations;
|
||||
"Default Applications" = cfg.defaultApplications;
|
||||
"Removed Associations" = cfg.removedAssociations;
|
||||
};
|
||||
};
|
||||
|
||||
environment.pathsToLink = [ "/share/mime" ];
|
||||
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
|
||||
|
@ -10,7 +15,10 @@ in
|
|||
{
|
||||
|
||||
imports = [
|
||||
(lib.mkRemovedOptionModule [ "zramSwap" "numDevices" ] "Using ZRAM devices as general purpose ephemeral block devices is no longer supported")
|
||||
(lib.mkRemovedOptionModule [
|
||||
"zramSwap"
|
||||
"numDevices"
|
||||
] "Using ZRAM devices as general purpose ephemeral block devices is no longer supported")
|
||||
];
|
||||
|
||||
###### interface
|
||||
|
@ -73,7 +81,16 @@ in
|
|||
algorithm = lib.mkOption {
|
||||
default = "zstd";
|
||||
example = "lz4";
|
||||
type = with lib.types; either (enum [ "842" "lzo" "lzo-rle" "lz4" "lz4hc" "zstd" ]) str;
|
||||
type =
|
||||
with lib.types;
|
||||
either (enum [
|
||||
"842"
|
||||
"lzo"
|
||||
"lzo-rle"
|
||||
"lz4"
|
||||
"lz4hc"
|
||||
"zstd"
|
||||
]) str;
|
||||
description = ''
|
||||
Compression algorithm. `lzo` has good compression,
|
||||
but is slow. `lz4` has bad compression, but is fast.
|
||||
|
@ -107,23 +124,24 @@ in
|
|||
|
||||
services.zram-generator.enable = true;
|
||||
|
||||
services.zram-generator.settings = lib.listToAttrs
|
||||
(builtins.map
|
||||
(dev: {
|
||||
name = dev;
|
||||
value =
|
||||
let
|
||||
size = "${toString cfg.memoryPercent} / 100 * ram";
|
||||
in
|
||||
{
|
||||
zram-size = if cfg.memoryMax != null then "min(${size}, ${toString cfg.memoryMax} / 1024 / 1024)" else size;
|
||||
compression-algorithm = cfg.algorithm;
|
||||
swap-priority = cfg.priority;
|
||||
} // lib.optionalAttrs (cfg.writebackDevice != null) {
|
||||
writeback-device = cfg.writebackDevice;
|
||||
};
|
||||
})
|
||||
devices);
|
||||
services.zram-generator.settings = lib.listToAttrs (
|
||||
builtins.map (dev: {
|
||||
name = dev;
|
||||
value =
|
||||
let
|
||||
size = "${toString cfg.memoryPercent} / 100 * ram";
|
||||
in
|
||||
{
|
||||
zram-size =
|
||||
if cfg.memoryMax != null then "min(${size}, ${toString cfg.memoryMax} / 1024 / 1024)" else size;
|
||||
compression-algorithm = cfg.algorithm;
|
||||
swap-priority = cfg.priority;
|
||||
}
|
||||
// lib.optionalAttrs (cfg.writebackDevice != null) {
|
||||
writeback-device = cfg.writebackDevice;
|
||||
};
|
||||
}) devices
|
||||
);
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.hardware.deviceTree;
|
||||
|
||||
|
@ -62,10 +67,12 @@ let
|
|||
};
|
||||
};
|
||||
|
||||
filterDTBs = src: if cfg.filter == null
|
||||
then src
|
||||
filterDTBs =
|
||||
src:
|
||||
if cfg.filter == null then
|
||||
src
|
||||
else
|
||||
pkgs.runCommand "dtbs-filtered" {} ''
|
||||
pkgs.runCommand "dtbs-filtered" { } ''
|
||||
mkdir -p $out
|
||||
cd ${src}
|
||||
find . -type f -name '${cfg.filter}' -print0 \
|
||||
|
@ -76,148 +83,169 @@ let
|
|||
|
||||
# Fill in `dtboFile` for each overlay if not set already.
|
||||
# Existence of one of these is guarded by assertion below
|
||||
withDTBOs = xs: lib.flip map xs (o: o // { dtboFile =
|
||||
let
|
||||
includePaths = ["${lib.getDev cfg.kernelPackage}/lib/modules/${cfg.kernelPackage.modDirVersion}/source/scripts/dtc/include-prefixes"] ++ cfg.dtboBuildExtraIncludePaths;
|
||||
extraPreprocessorFlags = cfg.dtboBuildExtraPreprocessorFlags;
|
||||
in
|
||||
if o.dtboFile == null then
|
||||
let
|
||||
dtsFile = if o.dtsFile == null then (pkgs.writeText "dts" o.dtsText) else o.dtsFile;
|
||||
in
|
||||
pkgs.deviceTree.compileDTS {
|
||||
name = "${o.name}-dtbo";
|
||||
inherit includePaths extraPreprocessorFlags dtsFile;
|
||||
withDTBOs =
|
||||
xs:
|
||||
lib.flip map xs (
|
||||
o:
|
||||
o
|
||||
// {
|
||||
dtboFile =
|
||||
let
|
||||
includePaths = [
|
||||
"${lib.getDev cfg.kernelPackage}/lib/modules/${cfg.kernelPackage.modDirVersion}/source/scripts/dtc/include-prefixes"
|
||||
] ++ cfg.dtboBuildExtraIncludePaths;
|
||||
extraPreprocessorFlags = cfg.dtboBuildExtraPreprocessorFlags;
|
||||
in
|
||||
if o.dtboFile == null then
|
||||
let
|
||||
dtsFile = if o.dtsFile == null then (pkgs.writeText "dts" o.dtsText) else o.dtsFile;
|
||||
in
|
||||
pkgs.deviceTree.compileDTS {
|
||||
name = "${o.name}-dtbo";
|
||||
inherit includePaths extraPreprocessorFlags dtsFile;
|
||||
}
|
||||
else
|
||||
o.dtboFile;
|
||||
}
|
||||
else o.dtboFile; } );
|
||||
);
|
||||
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
(lib.mkRemovedOptionModule [ "hardware" "deviceTree" "base" ] "Use hardware.deviceTree.kernelPackage instead")
|
||||
(lib.mkRemovedOptionModule [
|
||||
"hardware"
|
||||
"deviceTree"
|
||||
"base"
|
||||
] "Use hardware.deviceTree.kernelPackage instead")
|
||||
];
|
||||
|
||||
options = {
|
||||
hardware.deviceTree = {
|
||||
enable = lib.mkOption {
|
||||
default = pkgs.stdenv.hostPlatform.linux-kernel.DTB or false;
|
||||
type = lib.types.bool;
|
||||
description = ''
|
||||
Build device tree files. These are used to describe the
|
||||
non-discoverable hardware of a system.
|
||||
'';
|
||||
};
|
||||
hardware.deviceTree = {
|
||||
enable = lib.mkOption {
|
||||
default = pkgs.stdenv.hostPlatform.linux-kernel.DTB or false;
|
||||
type = lib.types.bool;
|
||||
description = ''
|
||||
Build device tree files. These are used to describe the
|
||||
non-discoverable hardware of a system.
|
||||
'';
|
||||
};
|
||||
|
||||
kernelPackage = lib.mkOption {
|
||||
default = config.boot.kernelPackages.kernel;
|
||||
defaultText = lib.literalExpression "config.boot.kernelPackages.kernel";
|
||||
example = lib.literalExpression "pkgs.linux_latest";
|
||||
type = lib.types.path;
|
||||
description = ''
|
||||
Kernel package where device tree include directory is from. Also used as default source of dtb package to apply overlays to
|
||||
'';
|
||||
};
|
||||
kernelPackage = lib.mkOption {
|
||||
default = config.boot.kernelPackages.kernel;
|
||||
defaultText = lib.literalExpression "config.boot.kernelPackages.kernel";
|
||||
example = lib.literalExpression "pkgs.linux_latest";
|
||||
type = lib.types.path;
|
||||
description = ''
|
||||
Kernel package where device tree include directory is from. Also used as default source of dtb package to apply overlays to
|
||||
'';
|
||||
};
|
||||
|
||||
dtboBuildExtraPreprocessorFlags = lib.mkOption {
|
||||
default = [];
|
||||
example = lib.literalExpression "[ \"-DMY_DTB_DEFINE\" ]";
|
||||
type = lib.types.listOf lib.types.str;
|
||||
description = ''
|
||||
Additional flags to pass to the preprocessor during dtbo compilations
|
||||
'';
|
||||
};
|
||||
dtboBuildExtraPreprocessorFlags = lib.mkOption {
|
||||
default = [ ];
|
||||
example = lib.literalExpression "[ \"-DMY_DTB_DEFINE\" ]";
|
||||
type = lib.types.listOf lib.types.str;
|
||||
description = ''
|
||||
Additional flags to pass to the preprocessor during dtbo compilations
|
||||
'';
|
||||
};
|
||||
|
||||
dtboBuildExtraIncludePaths = lib.mkOption {
|
||||
default = [];
|
||||
example = lib.literalExpression ''
|
||||
[
|
||||
./my_custom_include_dir_1
|
||||
./custom_include_dir_2
|
||||
]
|
||||
'';
|
||||
type = lib.types.listOf lib.types.path;
|
||||
description = ''
|
||||
Additional include paths that will be passed to the preprocessor when creating the final .dts to compile into .dtbo
|
||||
'';
|
||||
};
|
||||
dtboBuildExtraIncludePaths = lib.mkOption {
|
||||
default = [ ];
|
||||
example = lib.literalExpression ''
|
||||
[
|
||||
./my_custom_include_dir_1
|
||||
./custom_include_dir_2
|
||||
]
|
||||
'';
|
||||
type = lib.types.listOf lib.types.path;
|
||||
description = ''
|
||||
Additional include paths that will be passed to the preprocessor when creating the final .dts to compile into .dtbo
|
||||
'';
|
||||
};
|
||||
|
||||
dtbSource = lib.mkOption {
|
||||
default = "${cfg.kernelPackage}/dtbs";
|
||||
defaultText = lib.literalExpression "\${cfg.kernelPackage}/dtbs";
|
||||
type = lib.types.path;
|
||||
description = ''
|
||||
Path to dtb directory that overlays and other processing will be applied to. Uses
|
||||
device trees bundled with the Linux kernel by default.
|
||||
'';
|
||||
};
|
||||
dtbSource = lib.mkOption {
|
||||
default = "${cfg.kernelPackage}/dtbs";
|
||||
defaultText = lib.literalExpression "\${cfg.kernelPackage}/dtbs";
|
||||
type = lib.types.path;
|
||||
description = ''
|
||||
Path to dtb directory that overlays and other processing will be applied to. Uses
|
||||
device trees bundled with the Linux kernel by default.
|
||||
'';
|
||||
};
|
||||
|
||||
name = lib.mkOption {
|
||||
default = null;
|
||||
example = "some-dtb.dtb";
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
description = ''
|
||||
The name of an explicit dtb to be loaded, relative to the dtb base.
|
||||
Useful in extlinux scenarios if the bootloader doesn't pick the
|
||||
right .dtb file from FDTDIR.
|
||||
'';
|
||||
};
|
||||
name = lib.mkOption {
|
||||
default = null;
|
||||
example = "some-dtb.dtb";
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
description = ''
|
||||
The name of an explicit dtb to be loaded, relative to the dtb base.
|
||||
Useful in extlinux scenarios if the bootloader doesn't pick the
|
||||
right .dtb file from FDTDIR.
|
||||
'';
|
||||
};
|
||||
|
||||
filter = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = null;
|
||||
example = "*rpi*.dtb";
|
||||
description = ''
|
||||
Only include .dtb files matching glob expression.
|
||||
'';
|
||||
};
|
||||
filter = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = null;
|
||||
example = "*rpi*.dtb";
|
||||
description = ''
|
||||
Only include .dtb files matching glob expression.
|
||||
'';
|
||||
};
|
||||
|
||||
overlays = lib.mkOption {
|
||||
default = [];
|
||||
example = lib.literalExpression ''
|
||||
[
|
||||
{ name = "pps"; dtsFile = ./dts/pps.dts; }
|
||||
{ name = "spi";
|
||||
dtsText = "...";
|
||||
}
|
||||
{ name = "precompiled"; dtboFile = ./dtbos/example.dtbo; }
|
||||
]
|
||||
'';
|
||||
type = lib.types.listOf (lib.types.coercedTo lib.types.path (path: {
|
||||
overlays = lib.mkOption {
|
||||
default = [ ];
|
||||
example = lib.literalExpression ''
|
||||
[
|
||||
{ name = "pps"; dtsFile = ./dts/pps.dts; }
|
||||
{ name = "spi";
|
||||
dtsText = "...";
|
||||
}
|
||||
{ name = "precompiled"; dtboFile = ./dtbos/example.dtbo; }
|
||||
]
|
||||
'';
|
||||
type = lib.types.listOf (
|
||||
lib.types.coercedTo lib.types.path (path: {
|
||||
name = baseNameOf path;
|
||||
filter = null;
|
||||
dtboFile = path;
|
||||
}) overlayType);
|
||||
description = ''
|
||||
List of overlays to apply to base device-tree (.dtb) files.
|
||||
'';
|
||||
};
|
||||
|
||||
package = lib.mkOption {
|
||||
default = null;
|
||||
type = lib.types.nullOr lib.types.path;
|
||||
internal = true;
|
||||
description = ''
|
||||
A path containing the result of applying `overlays` to `kernelPackage`.
|
||||
'';
|
||||
};
|
||||
}) overlayType
|
||||
);
|
||||
description = ''
|
||||
List of overlays to apply to base device-tree (.dtb) files.
|
||||
'';
|
||||
};
|
||||
|
||||
package = lib.mkOption {
|
||||
default = null;
|
||||
type = lib.types.nullOr lib.types.path;
|
||||
internal = true;
|
||||
description = ''
|
||||
A path containing the result of applying `overlays` to `kernelPackage`.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf (cfg.enable) {
|
||||
|
||||
assertions = let
|
||||
invalidOverlay = o: (o.dtsFile == null) && (o.dtsText == null) && (o.dtboFile == null);
|
||||
in lib.singleton {
|
||||
assertion = lib.all (o: !invalidOverlay o) cfg.overlays;
|
||||
message = ''
|
||||
deviceTree overlay needs one of dtsFile, dtsText or dtboFile set.
|
||||
Offending overlay(s):
|
||||
${toString (map (o: o.name) (builtins.filter invalidOverlay cfg.overlays))}
|
||||
'';
|
||||
};
|
||||
assertions =
|
||||
let
|
||||
invalidOverlay = o: (o.dtsFile == null) && (o.dtsText == null) && (o.dtboFile == null);
|
||||
in
|
||||
lib.singleton {
|
||||
assertion = lib.all (o: !invalidOverlay o) cfg.overlays;
|
||||
message = ''
|
||||
deviceTree overlay needs one of dtsFile, dtsText or dtboFile set.
|
||||
Offending overlay(s):
|
||||
${toString (map (o: o.name) (builtins.filter invalidOverlay cfg.overlays))}
|
||||
'';
|
||||
};
|
||||
|
||||
hardware.deviceTree.package = if (cfg.overlays != [])
|
||||
then pkgs.deviceTree.applyOverlays filteredDTBs (withDTBOs cfg.overlays)
|
||||
else filteredDTBs;
|
||||
hardware.deviceTree.package =
|
||||
if (cfg.overlays != [ ]) then
|
||||
pkgs.deviceTree.applyOverlays filteredDTBs (withDTBOs cfg.overlays)
|
||||
else
|
||||
filteredDTBs;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.hardware.graphics;
|
||||
|
||||
|
@ -14,16 +19,35 @@ let
|
|||
in
|
||||
{
|
||||
imports = [
|
||||
(lib.mkRenamedOptionModule [ "services" "xserver" "vaapiDrivers" ] [ "hardware" "graphics" "extraPackages" ])
|
||||
(lib.mkRemovedOptionModule [ "hardware" "opengl" "s3tcSupport" ] "S3TC support is now always enabled in Mesa.")
|
||||
(lib.mkRemovedOptionModule [ "hardware" "opengl" "driSupport"] "The setting can be removed.")
|
||||
(lib.mkRenamedOptionModule
|
||||
[ "services" "xserver" "vaapiDrivers" ]
|
||||
[ "hardware" "graphics" "extraPackages" ]
|
||||
)
|
||||
(lib.mkRemovedOptionModule [
|
||||
"hardware"
|
||||
"opengl"
|
||||
"s3tcSupport"
|
||||
] "S3TC support is now always enabled in Mesa.")
|
||||
(lib.mkRemovedOptionModule [ "hardware" "opengl" "driSupport" ] "The setting can be removed.")
|
||||
|
||||
(lib.mkRenamedOptionModule [ "hardware" "opengl" "enable"] [ "hardware" "graphics" "enable" ])
|
||||
(lib.mkRenamedOptionModule [ "hardware" "opengl" "driSupport32Bit"] [ "hardware" "graphics" "enable32Bit" ])
|
||||
(lib.mkRenamedOptionModule [ "hardware" "opengl" "package"] [ "hardware" "graphics" "package" ])
|
||||
(lib.mkRenamedOptionModule [ "hardware" "opengl" "package32"] [ "hardware" "graphics" "package32" ])
|
||||
(lib.mkRenamedOptionModule [ "hardware" "opengl" "extraPackages"] [ "hardware" "graphics" "extraPackages" ])
|
||||
(lib.mkRenamedOptionModule [ "hardware" "opengl" "extraPackages32"] [ "hardware" "graphics" "extraPackages32" ])
|
||||
(lib.mkRenamedOptionModule [ "hardware" "opengl" "enable" ] [ "hardware" "graphics" "enable" ])
|
||||
(lib.mkRenamedOptionModule
|
||||
[ "hardware" "opengl" "driSupport32Bit" ]
|
||||
[ "hardware" "graphics" "enable32Bit" ]
|
||||
)
|
||||
(lib.mkRenamedOptionModule [ "hardware" "opengl" "package" ] [ "hardware" "graphics" "package" ])
|
||||
(lib.mkRenamedOptionModule
|
||||
[ "hardware" "opengl" "package32" ]
|
||||
[ "hardware" "graphics" "package32" ]
|
||||
)
|
||||
(lib.mkRenamedOptionModule
|
||||
[ "hardware" "opengl" "extraPackages" ]
|
||||
[ "hardware" "graphics" "extraPackages" ]
|
||||
)
|
||||
(lib.mkRenamedOptionModule
|
||||
[ "hardware" "opengl" "extraPackages32" ]
|
||||
[ "hardware" "graphics" "extraPackages32" ]
|
||||
)
|
||||
];
|
||||
|
||||
options.hardware.graphics = {
|
||||
|
@ -78,7 +102,7 @@ in
|
|||
:::
|
||||
'';
|
||||
type = lib.types.listOf lib.types.package;
|
||||
default = [];
|
||||
default = [ ];
|
||||
example = lib.literalExpression "with pkgs; [ intel-media-driver intel-ocl intel-vaapi-driver ]";
|
||||
};
|
||||
|
||||
|
@ -92,7 +116,7 @@ in
|
|||
:::
|
||||
'';
|
||||
type = lib.types.listOf lib.types.package;
|
||||
default = [];
|
||||
default = [ ];
|
||||
example = lib.literalExpression "with pkgs.pkgsi686Linux; [ intel-media-driver intel-vaapi-driver ]";
|
||||
};
|
||||
};
|
||||
|
@ -117,7 +141,7 @@ in
|
|||
else if cfg.enable32Bit then
|
||||
{ "L+".argument = toString driversEnv32; }
|
||||
else
|
||||
{ "r" = {}; };
|
||||
{ "r" = { }; };
|
||||
};
|
||||
|
||||
hardware.graphics.package = lib.mkDefault pkgs.mesa.drivers;
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
|
||||
cfg = config.hardware.steam-hardware;
|
||||
|
|
|
@ -92,21 +92,23 @@ in
|
|||
information, see the NVIDIA docs, on Chapter 23. Dynamic Boost on Linux
|
||||
'';
|
||||
|
||||
modesetting.enable = lib.mkEnableOption ''
|
||||
kernel modesetting when using the NVIDIA proprietary driver.
|
||||
modesetting.enable =
|
||||
lib.mkEnableOption ''
|
||||
kernel modesetting when using the NVIDIA proprietary driver.
|
||||
|
||||
Enabling this fixes screen tearing when using Optimus via PRIME (see
|
||||
{option}`hardware.nvidia.prime.sync.enable`. This is not enabled
|
||||
by default because it is not officially supported by NVIDIA and would not
|
||||
work with SLI.
|
||||
Enabling this fixes screen tearing when using Optimus via PRIME (see
|
||||
{option}`hardware.nvidia.prime.sync.enable`. This is not enabled
|
||||
by default because it is not officially supported by NVIDIA and would not
|
||||
work with SLI.
|
||||
|
||||
Enabling this and using version 545 or newer of the proprietary NVIDIA
|
||||
driver causes it to provide its own framebuffer device, which can cause
|
||||
Wayland compositors to work when they otherwise wouldn't.
|
||||
'' // {
|
||||
default = lib.versionAtLeast cfg.package.version "535";
|
||||
defaultText = lib.literalExpression "lib.versionAtLeast cfg.package.version \"535\"";
|
||||
};
|
||||
Enabling this and using version 545 or newer of the proprietary NVIDIA
|
||||
driver causes it to provide its own framebuffer device, which can cause
|
||||
Wayland compositors to work when they otherwise wouldn't.
|
||||
''
|
||||
// {
|
||||
default = lib.versionAtLeast cfg.package.version "535";
|
||||
defaultText = lib.literalExpression "lib.versionAtLeast cfg.package.version \"535\"";
|
||||
};
|
||||
|
||||
prime.nvidiaBusId = lib.mkOption {
|
||||
type = busIDType;
|
||||
|
@ -266,14 +268,16 @@ in
|
|||
'';
|
||||
};
|
||||
|
||||
gsp.enable = lib.mkEnableOption ''
|
||||
the GPU System Processor (GSP) on the video card
|
||||
'' // {
|
||||
default = useOpenModules || lib.versionAtLeast nvidia_x11.version "555";
|
||||
defaultText = lib.literalExpression ''
|
||||
config.hardware.nvidia.open == true || lib.versionAtLeast config.hardware.nvidia.package.version "555"
|
||||
'';
|
||||
};
|
||||
gsp.enable =
|
||||
lib.mkEnableOption ''
|
||||
the GPU System Processor (GSP) on the video card
|
||||
''
|
||||
// {
|
||||
default = useOpenModules || lib.versionAtLeast nvidia_x11.version "555";
|
||||
defaultText = lib.literalExpression ''
|
||||
config.hardware.nvidia.open == true || lib.versionAtLeast config.hardware.nvidia.package.version "555"
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -315,7 +319,9 @@ in
|
|||
softdep nvidia post: nvidia-uvm
|
||||
'';
|
||||
};
|
||||
systemd.tmpfiles.rules = lib.mkIf config.virtualisation.docker.enableNvidia [ "L+ /run/nvidia-docker/bin - - - - ${nvidia_x11.bin}/origBin" ];
|
||||
systemd.tmpfiles.rules = lib.mkIf config.virtualisation.docker.enableNvidia [
|
||||
"L+ /run/nvidia-docker/bin - - - - ${nvidia_x11.bin}/origBin"
|
||||
];
|
||||
services.udev.extraRules = ''
|
||||
# Create /dev/nvidia-uvm when the nvidia-uvm module is loaded.
|
||||
KERNEL=="nvidia", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidiactl c 195 255'"
|
||||
|
@ -616,7 +622,9 @@ in
|
|||
# If requested enable modesetting via kernel parameters.
|
||||
kernelParams =
|
||||
lib.optional (offloadCfg.enable || cfg.modesetting.enable) "nvidia-drm.modeset=1"
|
||||
++ lib.optional ((offloadCfg.enable || cfg.modesetting.enable) && lib.versionAtLeast nvidia_x11.version "545") "nvidia-drm.fbdev=1"
|
||||
++ lib.optional (
|
||||
(offloadCfg.enable || cfg.modesetting.enable) && lib.versionAtLeast nvidia_x11.version "545"
|
||||
) "nvidia-drm.fbdev=1"
|
||||
++ lib.optional cfg.powerManagement.enable "nvidia.NVreg_PreserveVideoMemoryAllocations=1"
|
||||
++ lib.optional useOpenModules "nvidia.NVreg_OpenRmEnableUnsupportedGpus=1"
|
||||
++ lib.optional (config.boot.kernelPackages.kernel.kernelAtLeast "6.2" && !ibtSupport) "ibt=off";
|
||||
|
@ -677,7 +685,9 @@ in
|
|||
TOPOLOGY_FILE_PATH = "${nvidia_x11.fabricmanager}/share/nvidia-fabricmanager/nvidia/nvswitch";
|
||||
DATABASE_PATH = "${nvidia_x11.fabricmanager}/share/nvidia-fabricmanager/nvidia/nvswitch";
|
||||
};
|
||||
nv-fab-conf = settingsFormat.generate "fabricmanager.conf" (fabricManagerConfDefaults // cfg.datacenter.settings);
|
||||
nv-fab-conf = settingsFormat.generate "fabricmanager.conf" (
|
||||
fabricManagerConfDefaults // cfg.datacenter.settings
|
||||
);
|
||||
in
|
||||
"${lib.getExe nvidia_x11.fabricmanager} -c ${nv-fab-conf}";
|
||||
LimitCORE = "infinity";
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
{
|
||||
options.hardware.wooting.enable = lib.mkEnableOption ''
|
||||
support for Wooting keyboards.
|
||||
|
|
|
@ -1,11 +1,17 @@
|
|||
{ config, pkgs, lib, ... }:
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
imcfg = config.i18n.inputMethod;
|
||||
cfg = imcfg.fcitx5;
|
||||
fcitx5Package =
|
||||
if cfg.plasma6Support
|
||||
then pkgs.qt6Packages.fcitx5-with-addons.override { inherit (cfg) addons; }
|
||||
else pkgs.libsForQt5.fcitx5-with-addons.override { inherit (cfg) addons; };
|
||||
if cfg.plasma6Support then
|
||||
pkgs.qt6Packages.fcitx5-with-addons.override { inherit (cfg) addons; }
|
||||
else
|
||||
pkgs.libsForQt5.fcitx5-with-addons.override { inherit (cfg) addons; };
|
||||
settingsFormat = pkgs.formats.ini { };
|
||||
in
|
||||
{
|
||||
|
@ -108,40 +114,46 @@ in
|
|||
config = lib.mkIf (imcfg.enable && imcfg.type == "fcitx5") {
|
||||
i18n.inputMethod.package = fcitx5Package;
|
||||
|
||||
i18n.inputMethod.fcitx5.addons = lib.optionals (cfg.quickPhrase != { }) [
|
||||
(pkgs.writeTextDir "share/fcitx5/data/QuickPhrase.mb"
|
||||
(lib.concatStringsSep "\n"
|
||||
(lib.mapAttrsToList (name: value: "${name} ${value}") cfg.quickPhrase)))
|
||||
] ++ lib.optionals (cfg.quickPhraseFiles != { }) [
|
||||
(pkgs.linkFarm "quickPhraseFiles" (lib.mapAttrs'
|
||||
(name: value: lib.nameValuePair ("share/fcitx5/data/quickphrase.d/${name}.mb") value)
|
||||
cfg.quickPhraseFiles))
|
||||
];
|
||||
i18n.inputMethod.fcitx5.addons =
|
||||
lib.optionals (cfg.quickPhrase != { }) [
|
||||
(pkgs.writeTextDir "share/fcitx5/data/QuickPhrase.mb" (
|
||||
lib.concatStringsSep "\n" (lib.mapAttrsToList (name: value: "${name} ${value}") cfg.quickPhrase)
|
||||
))
|
||||
]
|
||||
++ lib.optionals (cfg.quickPhraseFiles != { }) [
|
||||
(pkgs.linkFarm "quickPhraseFiles" (
|
||||
lib.mapAttrs' (
|
||||
name: value: lib.nameValuePair ("share/fcitx5/data/quickphrase.d/${name}.mb") value
|
||||
) cfg.quickPhraseFiles
|
||||
))
|
||||
];
|
||||
environment.etc =
|
||||
let
|
||||
optionalFile = p: f: v: lib.optionalAttrs (v != { }) {
|
||||
"xdg/fcitx5/${p}".text = f v;
|
||||
};
|
||||
optionalFile =
|
||||
p: f: v:
|
||||
lib.optionalAttrs (v != { }) {
|
||||
"xdg/fcitx5/${p}".text = f v;
|
||||
};
|
||||
in
|
||||
lib.attrsets.mergeAttrsList [
|
||||
(optionalFile "config" (lib.generators.toINI { }) cfg.settings.globalOptions)
|
||||
(optionalFile "profile" (lib.generators.toINI { }) cfg.settings.inputMethod)
|
||||
(lib.concatMapAttrs
|
||||
(name: value: optionalFile
|
||||
"conf/${name}.conf"
|
||||
(lib.generators.toINIWithGlobalSection { })
|
||||
value)
|
||||
cfg.settings.addons)
|
||||
(lib.concatMapAttrs (
|
||||
name: value: optionalFile "conf/${name}.conf" (lib.generators.toINIWithGlobalSection { }) value
|
||||
) cfg.settings.addons)
|
||||
];
|
||||
|
||||
environment.variables = {
|
||||
XMODIFIERS = "@im=fcitx";
|
||||
QT_PLUGIN_PATH = [ "${fcitx5Package}/${pkgs.qt6.qtbase.qtPluginPrefix}" ];
|
||||
} // lib.optionalAttrs (!cfg.waylandFrontend) {
|
||||
GTK_IM_MODULE = "fcitx";
|
||||
QT_IM_MODULE = "fcitx";
|
||||
} // lib.optionalAttrs cfg.ignoreUserConfig {
|
||||
SKIP_FCITX_USER_PATH = "1";
|
||||
};
|
||||
environment.variables =
|
||||
{
|
||||
XMODIFIERS = "@im=fcitx";
|
||||
QT_PLUGIN_PATH = [ "${fcitx5Package}/${pkgs.qt6.qtbase.qtPluginPrefix}" ];
|
||||
}
|
||||
// lib.optionalAttrs (!cfg.waylandFrontend) {
|
||||
GTK_IM_MODULE = "fcitx";
|
||||
QT_IM_MODULE = "fcitx";
|
||||
}
|
||||
// lib.optionalAttrs cfg.ignoreUserConfig {
|
||||
SKIP_FCITX_USER_PATH = "1";
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
{ config, pkgs, lib, ... }:
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
|
||||
with lib;
|
||||
|
||||
|
@ -7,9 +12,9 @@ let
|
|||
cfg = imcfg.ibus;
|
||||
ibusPackage = pkgs.ibus-with-plugins.override { plugins = cfg.engines; };
|
||||
ibusEngine = lib.types.mkOptionType {
|
||||
name = "ibus-engine";
|
||||
name = "ibus-engine";
|
||||
inherit (lib.types.package) descriptionClass merge;
|
||||
check = x: (lib.types.package.check x) && (attrByPath ["meta" "isIbusEngine"] false x);
|
||||
check = x: (lib.types.package.check x) && (attrByPath [ "meta" "isIbusEngine" ] false x);
|
||||
};
|
||||
|
||||
impanel = optionalString (cfg.panel != null) "--panel=${cfg.panel}";
|
||||
|
@ -35,15 +40,15 @@ in
|
|||
options = {
|
||||
i18n.inputMethod.ibus = {
|
||||
engines = mkOption {
|
||||
type = with types; listOf ibusEngine;
|
||||
default = [];
|
||||
type = with types; listOf ibusEngine;
|
||||
default = [ ];
|
||||
example = literalExpression "with pkgs.ibus-engines; [ mozc hangul ]";
|
||||
description =
|
||||
let
|
||||
enginesDrv = filterAttrs (const isDerivation) pkgs.ibus-engines;
|
||||
engines = concatStringsSep ", "
|
||||
(map (name: "`${name}`") (attrNames enginesDrv));
|
||||
in "Enabled IBus engines. Available engines are: ${engines}.";
|
||||
engines = concatStringsSep ", " (map (name: "`${name}`") (attrNames enginesDrv));
|
||||
in
|
||||
"Enabled IBus engines. Available engines are: ${engines}.";
|
||||
};
|
||||
panel = mkOption {
|
||||
type = with types; nullOr path;
|
||||
|
|
|
@ -1,80 +1,106 @@
|
|||
# This is an expression meant to be called from `./repart.nix`, it is NOT a
|
||||
# NixOS module that can be imported.
|
||||
|
||||
{ lib
|
||||
, stdenvNoCC
|
||||
, runCommand
|
||||
, python3
|
||||
, black
|
||||
, ruff
|
||||
, mypy
|
||||
, systemd
|
||||
, fakeroot
|
||||
{
|
||||
lib,
|
||||
stdenvNoCC,
|
||||
runCommand,
|
||||
python3,
|
||||
black,
|
||||
ruff,
|
||||
mypy,
|
||||
systemd,
|
||||
fakeroot,
|
||||
|
||||
# filesystem tools
|
||||
, dosfstools
|
||||
, mtools
|
||||
, e2fsprogs
|
||||
, squashfsTools
|
||||
, erofs-utils
|
||||
, btrfs-progs
|
||||
, xfsprogs
|
||||
dosfstools,
|
||||
mtools,
|
||||
e2fsprogs,
|
||||
squashfsTools,
|
||||
erofs-utils,
|
||||
btrfs-progs,
|
||||
xfsprogs,
|
||||
|
||||
# compression tools
|
||||
, zstd
|
||||
, xz
|
||||
zstd,
|
||||
xz,
|
||||
|
||||
# arguments
|
||||
, name
|
||||
, version
|
||||
, imageFileBasename
|
||||
, compression
|
||||
, fileSystems
|
||||
, finalPartitions
|
||||
, split
|
||||
, seed
|
||||
, definitionsDirectory
|
||||
, sectorSize
|
||||
, mkfsEnv ? {}
|
||||
, createEmpty ? true
|
||||
name,
|
||||
version,
|
||||
imageFileBasename,
|
||||
compression,
|
||||
fileSystems,
|
||||
finalPartitions,
|
||||
split,
|
||||
seed,
|
||||
definitionsDirectory,
|
||||
sectorSize,
|
||||
mkfsEnv ? { },
|
||||
createEmpty ? true,
|
||||
}:
|
||||
|
||||
let
|
||||
systemdArch = let
|
||||
inherit (stdenvNoCC) hostPlatform;
|
||||
in
|
||||
if hostPlatform.isAarch32 then "arm"
|
||||
else if hostPlatform.isAarch64 then "arm64"
|
||||
else if hostPlatform.isx86_32 then "x86"
|
||||
else if hostPlatform.isx86_64 then "x86-64"
|
||||
else if hostPlatform.isMips32 then "mips-le"
|
||||
else if hostPlatform.isMips64 then "mips64-le"
|
||||
else if hostPlatform.isPower then "ppc"
|
||||
else if hostPlatform.isPower64 then "ppc64"
|
||||
else if hostPlatform.isRiscV32 then "riscv32"
|
||||
else if hostPlatform.isRiscV64 then "riscv64"
|
||||
else if hostPlatform.isS390 then "s390"
|
||||
else if hostPlatform.isS390x then "s390x"
|
||||
else if hostPlatform.isLoongArch64 then "loongarch64"
|
||||
else if hostPlatform.isAlpha then "alpha"
|
||||
else hostPlatform.parsed.cpu.name;
|
||||
systemdArch =
|
||||
let
|
||||
inherit (stdenvNoCC) hostPlatform;
|
||||
in
|
||||
if hostPlatform.isAarch32 then
|
||||
"arm"
|
||||
else if hostPlatform.isAarch64 then
|
||||
"arm64"
|
||||
else if hostPlatform.isx86_32 then
|
||||
"x86"
|
||||
else if hostPlatform.isx86_64 then
|
||||
"x86-64"
|
||||
else if hostPlatform.isMips32 then
|
||||
"mips-le"
|
||||
else if hostPlatform.isMips64 then
|
||||
"mips64-le"
|
||||
else if hostPlatform.isPower then
|
||||
"ppc"
|
||||
else if hostPlatform.isPower64 then
|
||||
"ppc64"
|
||||
else if hostPlatform.isRiscV32 then
|
||||
"riscv32"
|
||||
else if hostPlatform.isRiscV64 then
|
||||
"riscv64"
|
||||
else if hostPlatform.isS390 then
|
||||
"s390"
|
||||
else if hostPlatform.isS390x then
|
||||
"s390x"
|
||||
else if hostPlatform.isLoongArch64 then
|
||||
"loongarch64"
|
||||
else if hostPlatform.isAlpha then
|
||||
"alpha"
|
||||
else
|
||||
hostPlatform.parsed.cpu.name;
|
||||
|
||||
amendRepartDefinitions = runCommand "amend-repart-definitions.py"
|
||||
{
|
||||
# TODO: ruff does not splice properly in nativeBuildInputs
|
||||
depsBuildBuild = [ ruff ];
|
||||
nativeBuildInputs = [ python3 black mypy ];
|
||||
} ''
|
||||
install ${./amend-repart-definitions.py} $out
|
||||
patchShebangs --build $out
|
||||
amendRepartDefinitions =
|
||||
runCommand "amend-repart-definitions.py"
|
||||
{
|
||||
# TODO: ruff does not splice properly in nativeBuildInputs
|
||||
depsBuildBuild = [ ruff ];
|
||||
nativeBuildInputs = [
|
||||
python3
|
||||
black
|
||||
mypy
|
||||
];
|
||||
}
|
||||
''
|
||||
install ${./amend-repart-definitions.py} $out
|
||||
patchShebangs --build $out
|
||||
|
||||
black --check --diff $out
|
||||
ruff check --line-length 88 $out
|
||||
mypy --strict $out
|
||||
'';
|
||||
black --check --diff $out
|
||||
ruff check --line-length 88 $out
|
||||
mypy --strict $out
|
||||
'';
|
||||
|
||||
fileSystemToolMapping = {
|
||||
"vfat" = [ dosfstools mtools ];
|
||||
"vfat" = [
|
||||
dosfstools
|
||||
mtools
|
||||
];
|
||||
"ext4" = [ e2fsprogs.bin ];
|
||||
"squashfs" = [ squashfsTools ];
|
||||
"erofs" = [ erofs-utils ];
|
||||
|
@ -84,105 +110,123 @@ let
|
|||
|
||||
fileSystemTools = builtins.concatMap (f: fileSystemToolMapping."${f}") fileSystems;
|
||||
|
||||
compressionPkg = {
|
||||
"zstd" = zstd;
|
||||
"xz" = xz;
|
||||
}."${compression.algorithm}";
|
||||
compressionPkg =
|
||||
{
|
||||
"zstd" = zstd;
|
||||
"xz" = xz;
|
||||
}
|
||||
."${compression.algorithm}";
|
||||
|
||||
compressionCommand = {
|
||||
"zstd" = "zstd --no-progress --threads=$NIX_BUILD_CORES -${toString compression.level}";
|
||||
"xz" = "xz --keep --verbose --threads=$NIX_BUILD_CORES -${toString compression.level}";
|
||||
}."${compression.algorithm}";
|
||||
compressionCommand =
|
||||
{
|
||||
"zstd" = "zstd --no-progress --threads=$NIX_BUILD_CORES -${toString compression.level}";
|
||||
"xz" = "xz --keep --verbose --threads=$NIX_BUILD_CORES -${toString compression.level}";
|
||||
}
|
||||
."${compression.algorithm}";
|
||||
in
|
||||
stdenvNoCC.mkDerivation (finalAttrs:
|
||||
(if (version != null)
|
||||
then { pname = name; inherit version; }
|
||||
else { inherit name; }
|
||||
) // {
|
||||
__structuredAttrs = true;
|
||||
stdenvNoCC.mkDerivation (
|
||||
finalAttrs:
|
||||
(
|
||||
if (version != null) then
|
||||
{
|
||||
pname = name;
|
||||
inherit version;
|
||||
}
|
||||
else
|
||||
{ inherit name; }
|
||||
)
|
||||
// {
|
||||
__structuredAttrs = true;
|
||||
|
||||
# the image will be self-contained so we can drop references
|
||||
# to the closure that was used to build it
|
||||
unsafeDiscardReferences.out = true;
|
||||
|
||||
# the image will be self-contained so we can drop references
|
||||
# to the closure that was used to build it
|
||||
unsafeDiscardReferences.out = true;
|
||||
nativeBuildInputs =
|
||||
[
|
||||
systemd
|
||||
fakeroot
|
||||
]
|
||||
++ lib.optionals (compression.enable) [
|
||||
compressionPkg
|
||||
]
|
||||
++ fileSystemTools;
|
||||
|
||||
nativeBuildInputs = [
|
||||
systemd
|
||||
fakeroot
|
||||
] ++ lib.optionals (compression.enable) [
|
||||
compressionPkg
|
||||
] ++ fileSystemTools;
|
||||
env = mkfsEnv;
|
||||
|
||||
env = mkfsEnv;
|
||||
inherit finalPartitions definitionsDirectory;
|
||||
|
||||
inherit finalPartitions definitionsDirectory;
|
||||
partitionsJSON = builtins.toJSON finalAttrs.finalPartitions;
|
||||
|
||||
partitionsJSON = builtins.toJSON finalAttrs.finalPartitions;
|
||||
# relative path to the repart definitions that are read by systemd-repart
|
||||
finalRepartDefinitions = "repart.d";
|
||||
|
||||
# relative path to the repart definitions that are read by systemd-repart
|
||||
finalRepartDefinitions = "repart.d";
|
||||
systemdRepartFlags =
|
||||
[
|
||||
"--architecture=${systemdArch}"
|
||||
"--dry-run=no"
|
||||
"--size=auto"
|
||||
"--seed=${seed}"
|
||||
"--definitions=${finalAttrs.finalRepartDefinitions}"
|
||||
"--split=${lib.boolToString split}"
|
||||
"--json=pretty"
|
||||
]
|
||||
++ lib.optionals createEmpty [
|
||||
"--empty=create"
|
||||
]
|
||||
++ lib.optionals (sectorSize != null) [
|
||||
"--sector-size=${toString sectorSize}"
|
||||
];
|
||||
|
||||
systemdRepartFlags = [
|
||||
"--architecture=${systemdArch}"
|
||||
"--dry-run=no"
|
||||
"--size=auto"
|
||||
"--seed=${seed}"
|
||||
"--definitions=${finalAttrs.finalRepartDefinitions}"
|
||||
"--split=${lib.boolToString split}"
|
||||
"--json=pretty"
|
||||
] ++ lib.optionals createEmpty [
|
||||
"--empty=create"
|
||||
] ++ lib.optionals (sectorSize != null) [
|
||||
"--sector-size=${toString sectorSize}"
|
||||
];
|
||||
dontUnpack = true;
|
||||
dontConfigure = true;
|
||||
doCheck = false;
|
||||
|
||||
dontUnpack = true;
|
||||
dontConfigure = true;
|
||||
doCheck = false;
|
||||
patchPhase = ''
|
||||
runHook prePatch
|
||||
|
||||
patchPhase = ''
|
||||
runHook prePatch
|
||||
amendedRepartDefinitionsDir=$(${amendRepartDefinitions} <(echo "$partitionsJSON") $definitionsDirectory)
|
||||
ln -vs $amendedRepartDefinitionsDir $finalRepartDefinitions
|
||||
|
||||
amendedRepartDefinitionsDir=$(${amendRepartDefinitions} <(echo "$partitionsJSON") $definitionsDirectory)
|
||||
ln -vs $amendedRepartDefinitionsDir $finalRepartDefinitions
|
||||
runHook postPatch
|
||||
'';
|
||||
|
||||
runHook postPatch
|
||||
'';
|
||||
buildPhase = ''
|
||||
runHook preBuild
|
||||
|
||||
buildPhase = ''
|
||||
runHook preBuild
|
||||
echo "Building image with systemd-repart..."
|
||||
fakeroot systemd-repart \
|
||||
''${systemdRepartFlags[@]} \
|
||||
${imageFileBasename}.raw \
|
||||
| tee repart-output.json
|
||||
|
||||
echo "Building image with systemd-repart..."
|
||||
fakeroot systemd-repart \
|
||||
''${systemdRepartFlags[@]} \
|
||||
${imageFileBasename}.raw \
|
||||
| tee repart-output.json
|
||||
runHook postBuild
|
||||
'';
|
||||
|
||||
runHook postBuild
|
||||
'';
|
||||
installPhase =
|
||||
''
|
||||
runHook preInstall
|
||||
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
mkdir -p $out
|
||||
''
|
||||
# Compression is implemented in the same derivation as opposed to in a
|
||||
# separate derivation to allow users to save disk space. Disk images are
|
||||
# already very space intensive so we want to allow users to mitigate this.
|
||||
+ lib.optionalString compression.enable ''
|
||||
for f in ${imageFileBasename}*; do
|
||||
echo "Compressing $f with ${compression.algorithm}..."
|
||||
# Keep the original file when compressing and only delete it afterwards
|
||||
${compressionCommand} $f && rm $f
|
||||
done
|
||||
''
|
||||
+ ''
|
||||
mv -v repart-output.json ${imageFileBasename}* $out
|
||||
|
||||
mkdir -p $out
|
||||
''
|
||||
# Compression is implemented in the same derivation as opposed to in a
|
||||
# separate derivation to allow users to save disk space. Disk images are
|
||||
# already very space intensive so we want to allow users to mitigate this.
|
||||
+ lib.optionalString compression.enable
|
||||
''
|
||||
for f in ${imageFileBasename}*; do
|
||||
echo "Compressing $f with ${compression.algorithm}..."
|
||||
# Keep the original file when compressing and only delete it afterwards
|
||||
${compressionCommand} $f && rm $f
|
||||
done
|
||||
'' + ''
|
||||
mv -v repart-output.json ${imageFileBasename}* $out
|
||||
runHook postInstall
|
||||
'';
|
||||
|
||||
runHook postInstall
|
||||
'';
|
||||
|
||||
passthru = {
|
||||
inherit amendRepartDefinitions;
|
||||
};
|
||||
})
|
||||
passthru = {
|
||||
inherit amendRepartDefinitions;
|
||||
};
|
||||
}
|
||||
)
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
# This module exposes options to build a disk image with a GUID Partition Table
|
||||
# (GPT). It uses systemd-repart to build the image.
|
||||
|
||||
{ config, pkgs, lib, utils, ... }:
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
utils,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.image.repart;
|
||||
|
@ -27,14 +33,16 @@ let
|
|||
};
|
||||
|
||||
contents = lib.mkOption {
|
||||
type = with lib.types; attrsOf (submodule {
|
||||
options = {
|
||||
source = lib.mkOption {
|
||||
type = types.path;
|
||||
description = "Path of the source file.";
|
||||
type =
|
||||
with lib.types;
|
||||
attrsOf (submodule {
|
||||
options = {
|
||||
source = lib.mkOption {
|
||||
type = types.path;
|
||||
description = "Path of the source file.";
|
||||
};
|
||||
};
|
||||
};
|
||||
});
|
||||
});
|
||||
default = { };
|
||||
example = lib.literalExpression ''
|
||||
{
|
||||
|
@ -48,7 +56,13 @@ let
|
|||
};
|
||||
|
||||
repartConfig = lib.mkOption {
|
||||
type = with lib.types; attrsOf (oneOf [ str int bool ]);
|
||||
type =
|
||||
with lib.types;
|
||||
attrsOf (oneOf [
|
||||
str
|
||||
int
|
||||
bool
|
||||
]);
|
||||
example = {
|
||||
Type = "home";
|
||||
SizeMinBytes = "512M";
|
||||
|
@ -63,10 +77,12 @@ let
|
|||
};
|
||||
};
|
||||
|
||||
mkfsOptionsToEnv = opts: lib.mapAttrs' (fsType: options: {
|
||||
name = "SYSTEMD_REPART_MKFS_OPTIONS_${lib.toUpper fsType}";
|
||||
value = builtins.concatStringsSep " " options;
|
||||
}) opts;
|
||||
mkfsOptionsToEnv =
|
||||
opts:
|
||||
lib.mapAttrs' (fsType: options: {
|
||||
name = "SYSTEMD_REPART_MKFS_OPTIONS_${lib.toUpper fsType}";
|
||||
value = builtins.concatStringsSep " " options;
|
||||
}) opts;
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
|
@ -113,7 +129,10 @@ in
|
|||
enable = lib.mkEnableOption "Image compression";
|
||||
|
||||
algorithm = lib.mkOption {
|
||||
type = lib.types.enum [ "zstd" "xz" ];
|
||||
type = lib.types.enum [
|
||||
"zstd"
|
||||
"xz"
|
||||
];
|
||||
default = "zstd";
|
||||
description = "Compression algorithm";
|
||||
};
|
||||
|
@ -159,7 +178,10 @@ in
|
|||
package = lib.mkPackageOption pkgs "systemd-repart" {
|
||||
# We use buildPackages so that repart images are built with the build
|
||||
# platform's systemd, allowing for cross-compiled systems to work.
|
||||
default = [ "buildPackages" "systemd" ];
|
||||
default = [
|
||||
"buildPackages"
|
||||
"systemd"
|
||||
];
|
||||
example = "pkgs.buildPackages.systemdMinimal.override { withCryptsetup = true; }";
|
||||
};
|
||||
|
||||
|
@ -196,7 +218,7 @@ in
|
|||
|
||||
mkfsOptions = lib.mkOption {
|
||||
type = with lib.types; attrsOf (listOf str);
|
||||
default = {};
|
||||
default = { };
|
||||
example = lib.literalExpression ''
|
||||
{
|
||||
vfat = [ "-S 512" "-c" ];
|
||||
|
@ -230,7 +252,8 @@ in
|
|||
|
||||
config = {
|
||||
|
||||
assertions = lib.mapAttrsToList (fileName: partitionConfig:
|
||||
assertions = lib.mapAttrsToList (
|
||||
fileName: partitionConfig:
|
||||
let
|
||||
inherit (partitionConfig) repartConfig;
|
||||
labelLength = builtins.stringLength repartConfig.Label;
|
||||
|
@ -240,51 +263,58 @@ in
|
|||
message = ''
|
||||
The partition label '${repartConfig.Label}'
|
||||
defined for '${fileName}' is ${toString labelLength} characters long,
|
||||
but the maximum label length supported by UEFI is ${toString
|
||||
GPTMaxLabelLength}.
|
||||
but the maximum label length supported by UEFI is ${toString GPTMaxLabelLength}.
|
||||
'';
|
||||
}
|
||||
) cfg.partitions;
|
||||
|
||||
warnings = lib.filter (v: v != null) (lib.mapAttrsToList (fileName: partitionConfig:
|
||||
let
|
||||
inherit (partitionConfig) repartConfig;
|
||||
suggestedMaxLabelLength = GPTMaxLabelLength - 2;
|
||||
labelLength = builtins.stringLength repartConfig.Label;
|
||||
in
|
||||
if (repartConfig ? Label && labelLength >= suggestedMaxLabelLength) then ''
|
||||
The partition label '${repartConfig.Label}'
|
||||
defined for '${fileName}' is ${toString labelLength} characters long.
|
||||
The suggested maximum label length is ${toString
|
||||
suggestedMaxLabelLength}.
|
||||
warnings = lib.filter (v: v != null) (
|
||||
lib.mapAttrsToList (
|
||||
fileName: partitionConfig:
|
||||
let
|
||||
inherit (partitionConfig) repartConfig;
|
||||
suggestedMaxLabelLength = GPTMaxLabelLength - 2;
|
||||
labelLength = builtins.stringLength repartConfig.Label;
|
||||
in
|
||||
if (repartConfig ? Label && labelLength >= suggestedMaxLabelLength) then
|
||||
''
|
||||
The partition label '${repartConfig.Label}'
|
||||
defined for '${fileName}' is ${toString labelLength} characters long.
|
||||
The suggested maximum label length is ${toString suggestedMaxLabelLength}.
|
||||
|
||||
If you use sytemd-sysupdate style A/B updates, this might
|
||||
not leave enough space to increment the version number included in
|
||||
the label in a future release. For example, if your label is
|
||||
${toString GPTMaxLabelLength} characters long (the maximum enforced by UEFI) and
|
||||
you're at version 9, you cannot increment this to 10.
|
||||
'' else null
|
||||
) cfg.partitions);
|
||||
If you use sytemd-sysupdate style A/B updates, this might
|
||||
not leave enough space to increment the version number included in
|
||||
the label in a future release. For example, if your label is
|
||||
${toString GPTMaxLabelLength} characters long (the maximum enforced by UEFI) and
|
||||
you're at version 9, you cannot increment this to 10.
|
||||
''
|
||||
else
|
||||
null
|
||||
) cfg.partitions
|
||||
);
|
||||
|
||||
image.repart =
|
||||
let
|
||||
version = config.image.repart.version;
|
||||
versionInfix = if version != null then "_${version}" else "";
|
||||
compressionSuffix = lib.optionalString cfg.compression.enable
|
||||
{
|
||||
"zstd" = ".zst";
|
||||
"xz" = ".xz";
|
||||
}."${cfg.compression.algorithm}";
|
||||
compressionSuffix =
|
||||
lib.optionalString cfg.compression.enable
|
||||
{
|
||||
"zstd" = ".zst";
|
||||
"xz" = ".xz";
|
||||
}
|
||||
."${cfg.compression.algorithm}";
|
||||
|
||||
makeClosure = paths: pkgs.closureInfo { rootPaths = paths; };
|
||||
|
||||
# Add the closure of the provided Nix store paths to cfg.partitions so
|
||||
# that amend-repart-definitions.py can read it.
|
||||
addClosure = _name: partitionConfig: partitionConfig // (
|
||||
lib.optionalAttrs
|
||||
(partitionConfig.storePaths or [ ] != [ ])
|
||||
{ closure = "${makeClosure partitionConfig.storePaths}/store-paths"; }
|
||||
);
|
||||
addClosure =
|
||||
_name: partitionConfig:
|
||||
partitionConfig
|
||||
// (lib.optionalAttrs (partitionConfig.storePaths or [ ] != [ ]) {
|
||||
closure = "${makeClosure partitionConfig.storePaths}/store-paths";
|
||||
});
|
||||
in
|
||||
{
|
||||
name = lib.mkIf (config.system.image.id != null) (lib.mkOptionDefault config.system.image.id);
|
||||
|
@ -295,10 +325,13 @@ in
|
|||
# Generally default to slightly faster than default compression
|
||||
# levels under the assumption that most of the building will be done
|
||||
# for development and release builds will be customized.
|
||||
level = lib.mkOptionDefault {
|
||||
"zstd" = 3;
|
||||
"xz" = 3;
|
||||
}."${cfg.compression.algorithm}";
|
||||
level =
|
||||
lib.mkOptionDefault
|
||||
{
|
||||
"zstd" = 3;
|
||||
"xz" = 3;
|
||||
}
|
||||
."${cfg.compression.algorithm}";
|
||||
};
|
||||
|
||||
finalPartitions = lib.mapAttrs addClosure cfg.partitions;
|
||||
|
@ -306,27 +339,37 @@ in
|
|||
|
||||
system.build.image =
|
||||
let
|
||||
fileSystems = lib.filter
|
||||
(f: f != null)
|
||||
(lib.mapAttrsToList (_n: v: v.repartConfig.Format or null) cfg.partitions);
|
||||
|
||||
fileSystems = lib.filter (f: f != null) (
|
||||
lib.mapAttrsToList (_n: v: v.repartConfig.Format or null) cfg.partitions
|
||||
);
|
||||
|
||||
format = pkgs.formats.ini { };
|
||||
|
||||
definitionsDirectory = utils.systemdUtils.lib.definitions
|
||||
"repart.d"
|
||||
format
|
||||
(lib.mapAttrs (_n: v: { Partition = v.repartConfig; }) cfg.finalPartitions);
|
||||
definitionsDirectory = utils.systemdUtils.lib.definitions "repart.d" format (
|
||||
lib.mapAttrs (_n: v: { Partition = v.repartConfig; }) cfg.finalPartitions
|
||||
);
|
||||
|
||||
mkfsEnv = mkfsOptionsToEnv cfg.mkfsOptions;
|
||||
in
|
||||
pkgs.callPackage ./repart-image.nix {
|
||||
systemd = cfg.package;
|
||||
inherit (cfg) name version imageFileBasename compression split seed sectorSize finalPartitions;
|
||||
inherit (cfg)
|
||||
name
|
||||
version
|
||||
imageFileBasename
|
||||
compression
|
||||
split
|
||||
seed
|
||||
sectorSize
|
||||
finalPartitions
|
||||
;
|
||||
inherit fileSystems definitionsDirectory mkfsEnv;
|
||||
};
|
||||
|
||||
meta.maintainers = with lib.maintainers; [ nikstur willibutz ];
|
||||
meta.maintainers = with lib.maintainers; [
|
||||
nikstur
|
||||
willibutz
|
||||
];
|
||||
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,15 +1,21 @@
|
|||
# This module contains the basic configuration for building a NixOS
|
||||
# installation CD.
|
||||
{ config, lib, options, pkgs, ... }:
|
||||
{
|
||||
imports =
|
||||
[ ./iso-image.nix
|
||||
config,
|
||||
lib,
|
||||
options,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
{
|
||||
imports = [
|
||||
./iso-image.nix
|
||||
|
||||
# Profiles of this basic installation CD.
|
||||
../../profiles/all-hardware.nix
|
||||
../../profiles/base.nix
|
||||
../../profiles/installation-device.nix
|
||||
];
|
||||
# Profiles of this basic installation CD.
|
||||
../../profiles/all-hardware.nix
|
||||
../../profiles/base.nix
|
||||
../../profiles/installation-device.nix
|
||||
];
|
||||
|
||||
# Adds terminus_font for people with HiDPI displays
|
||||
console.packages = options.console.packages.default ++ [ pkgs.terminus_font ];
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,7 +1,12 @@
|
|||
# This module creates netboot media containing the given NixOS
|
||||
# configuration.
|
||||
|
||||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
with lib;
|
||||
|
||||
|
@ -32,43 +37,50 @@ with lib;
|
|||
# here and it causes a cyclic dependency.
|
||||
boot.loader.grub.enable = false;
|
||||
|
||||
fileSystems."/" = mkImageMediaOverride
|
||||
{ fsType = "tmpfs";
|
||||
options = [ "mode=0755" ];
|
||||
};
|
||||
fileSystems."/" = mkImageMediaOverride {
|
||||
fsType = "tmpfs";
|
||||
options = [ "mode=0755" ];
|
||||
};
|
||||
|
||||
# In stage 1, mount a tmpfs on top of /nix/store (the squashfs
|
||||
# image) to make this a live CD.
|
||||
fileSystems."/nix/.ro-store" = mkImageMediaOverride
|
||||
{ fsType = "squashfs";
|
||||
device = "../nix-store.squashfs";
|
||||
options = [ "loop" ] ++ lib.optional (config.boot.kernelPackages.kernel.kernelAtLeast "6.2") "threads=multi";
|
||||
neededForBoot = true;
|
||||
fileSystems."/nix/.ro-store" = mkImageMediaOverride {
|
||||
fsType = "squashfs";
|
||||
device = "../nix-store.squashfs";
|
||||
options = [
|
||||
"loop"
|
||||
] ++ lib.optional (config.boot.kernelPackages.kernel.kernelAtLeast "6.2") "threads=multi";
|
||||
neededForBoot = true;
|
||||
};
|
||||
|
||||
fileSystems."/nix/.rw-store" = mkImageMediaOverride {
|
||||
fsType = "tmpfs";
|
||||
options = [ "mode=0755" ];
|
||||
neededForBoot = true;
|
||||
};
|
||||
|
||||
fileSystems."/nix/store" = mkImageMediaOverride {
|
||||
overlay = {
|
||||
lowerdir = [ "/nix/.ro-store" ];
|
||||
upperdir = "/nix/.rw-store/store";
|
||||
workdir = "/nix/.rw-store/work";
|
||||
};
|
||||
neededForBoot = true;
|
||||
};
|
||||
|
||||
fileSystems."/nix/.rw-store" = mkImageMediaOverride
|
||||
{ fsType = "tmpfs";
|
||||
options = [ "mode=0755" ];
|
||||
neededForBoot = true;
|
||||
};
|
||||
boot.initrd.availableKernelModules = [
|
||||
"squashfs"
|
||||
"overlay"
|
||||
];
|
||||
|
||||
fileSystems."/nix/store" = mkImageMediaOverride
|
||||
{ overlay = {
|
||||
lowerdir = [ "/nix/.ro-store" ];
|
||||
upperdir = "/nix/.rw-store/store";
|
||||
workdir = "/nix/.rw-store/work";
|
||||
};
|
||||
neededForBoot = true;
|
||||
};
|
||||
|
||||
boot.initrd.availableKernelModules = [ "squashfs" "overlay" ];
|
||||
|
||||
boot.initrd.kernelModules = [ "loop" "overlay" ];
|
||||
boot.initrd.kernelModules = [
|
||||
"loop"
|
||||
"overlay"
|
||||
];
|
||||
|
||||
# Closures to be copied to the Nix store, namely the init
|
||||
# script and the top-level system configuration directory.
|
||||
netboot.storeContents =
|
||||
[ config.system.build.toplevel ];
|
||||
netboot.storeContents = [ config.system.build.toplevel ];
|
||||
|
||||
# Create the squashfs image that contains the Nix store.
|
||||
system.build.squashfsStore = pkgs.callPackage ../../../lib/make-squashfs.nix {
|
||||
|
@ -76,17 +88,17 @@ with lib;
|
|||
comp = config.netboot.squashfsCompression;
|
||||
};
|
||||
|
||||
|
||||
# Create the initrd
|
||||
system.build.netbootRamdisk = pkgs.makeInitrdNG {
|
||||
inherit (config.boot.initrd) compressor;
|
||||
prepend = [ "${config.system.build.initialRamdisk}/initrd" ];
|
||||
|
||||
contents =
|
||||
[ { source = config.system.build.squashfsStore;
|
||||
target = "/nix-store.squashfs";
|
||||
}
|
||||
];
|
||||
contents = [
|
||||
{
|
||||
source = config.system.build.squashfsStore;
|
||||
target = "/nix-store.squashfs";
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
system.build.netbootIpxeScript = pkgs.writeTextDir "netboot.ipxe" ''
|
||||
|
@ -131,17 +143,16 @@ with lib;
|
|||
|
||||
boot.loader.timeout = 10;
|
||||
|
||||
boot.postBootCommands =
|
||||
''
|
||||
# After booting, register the contents of the Nix store
|
||||
# in the Nix database in the tmpfs.
|
||||
${config.nix.package}/bin/nix-store --load-db < /nix/store/nix-path-registration
|
||||
boot.postBootCommands = ''
|
||||
# After booting, register the contents of the Nix store
|
||||
# in the Nix database in the tmpfs.
|
||||
${config.nix.package}/bin/nix-store --load-db < /nix/store/nix-path-registration
|
||||
|
||||
# nixos-rebuild also requires a "system" profile and an
|
||||
# /etc/NIXOS tag.
|
||||
touch /etc/NIXOS
|
||||
${config.nix.package}/bin/nix-env -p /nix/var/nix/profiles/system --set /run/current-system
|
||||
'';
|
||||
# nixos-rebuild also requires a "system" profile and an
|
||||
# /etc/NIXOS tag.
|
||||
touch /etc/NIXOS
|
||||
${config.nix.package}/bin/nix-env -p /nix/var/nix/profiles/system --set /run/current-system
|
||||
'';
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
# To build, use:
|
||||
# nix-build nixos -I nixos-config=nixos/modules/installer/sd-card/sd-image-aarch64.nix -A config.system.build.sdImage
|
||||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
{
|
||||
imports = [
|
||||
|
@ -16,49 +21,55 @@
|
|||
# The serial ports listed here are:
|
||||
# - ttyS0: for Tegra (Jetson TX1)
|
||||
# - ttyAMA0: for QEMU's -machine virt
|
||||
boot.kernelParams = ["console=ttyS0,115200n8" "console=ttyAMA0,115200n8" "console=tty0"];
|
||||
boot.kernelParams = [
|
||||
"console=ttyS0,115200n8"
|
||||
"console=ttyAMA0,115200n8"
|
||||
"console=tty0"
|
||||
];
|
||||
|
||||
sdImage = {
|
||||
populateFirmwareCommands = let
|
||||
configTxt = pkgs.writeText "config.txt" ''
|
||||
[pi3]
|
||||
kernel=u-boot-rpi3.bin
|
||||
populateFirmwareCommands =
|
||||
let
|
||||
configTxt = pkgs.writeText "config.txt" ''
|
||||
[pi3]
|
||||
kernel=u-boot-rpi3.bin
|
||||
|
||||
[pi02]
|
||||
kernel=u-boot-rpi3.bin
|
||||
[pi02]
|
||||
kernel=u-boot-rpi3.bin
|
||||
|
||||
[pi4]
|
||||
kernel=u-boot-rpi4.bin
|
||||
enable_gic=1
|
||||
armstub=armstub8-gic.bin
|
||||
[pi4]
|
||||
kernel=u-boot-rpi4.bin
|
||||
enable_gic=1
|
||||
armstub=armstub8-gic.bin
|
||||
|
||||
# Otherwise the resolution will be weird in most cases, compared to
|
||||
# what the pi3 firmware does by default.
|
||||
disable_overscan=1
|
||||
# Otherwise the resolution will be weird in most cases, compared to
|
||||
# what the pi3 firmware does by default.
|
||||
disable_overscan=1
|
||||
|
||||
# Supported in newer board revisions
|
||||
arm_boost=1
|
||||
# Supported in newer board revisions
|
||||
arm_boost=1
|
||||
|
||||
[cm4]
|
||||
# Enable host mode on the 2711 built-in XHCI USB controller.
|
||||
# This line should be removed if the legacy DWC2 controller is required
|
||||
# (e.g. for USB device mode) or if USB support is not required.
|
||||
otg_mode=1
|
||||
[cm4]
|
||||
# Enable host mode on the 2711 built-in XHCI USB controller.
|
||||
# This line should be removed if the legacy DWC2 controller is required
|
||||
# (e.g. for USB device mode) or if USB support is not required.
|
||||
otg_mode=1
|
||||
|
||||
[all]
|
||||
# Boot in 64-bit mode.
|
||||
arm_64bit=1
|
||||
[all]
|
||||
# Boot in 64-bit mode.
|
||||
arm_64bit=1
|
||||
|
||||
# U-Boot needs this to work, regardless of whether UART is actually used or not.
|
||||
# Look in arch/arm/mach-bcm283x/Kconfig in the U-Boot tree to see if this is still
|
||||
# a requirement in the future.
|
||||
enable_uart=1
|
||||
# U-Boot needs this to work, regardless of whether UART is actually used or not.
|
||||
# Look in arch/arm/mach-bcm283x/Kconfig in the U-Boot tree to see if this is still
|
||||
# a requirement in the future.
|
||||
enable_uart=1
|
||||
|
||||
# Prevent the firmware from smashing the framebuffer setup done by the mainline kernel
|
||||
# when attempting to show low-voltage or overtemperature warnings.
|
||||
avoid_warnings=1
|
||||
'';
|
||||
in ''
|
||||
# Prevent the firmware from smashing the framebuffer setup done by the mainline kernel
|
||||
# when attempting to show low-voltage or overtemperature warnings.
|
||||
avoid_warnings=1
|
||||
'';
|
||||
in
|
||||
''
|
||||
(cd ${pkgs.raspberrypifw}/share/raspberrypi/boot && cp bootcode.bin fixup*.dat start*.elf $NIX_BUILD_TOP/firmware/)
|
||||
|
||||
# Add the config
|
||||
|
|
|
@ -11,24 +11,36 @@
|
|||
# The derivation for the SD image will be placed in
|
||||
# config.system.build.sdImage
|
||||
|
||||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
rootfsImage = pkgs.callPackage ../../../lib/make-ext4-fs.nix ({
|
||||
inherit (config.sdImage) storePaths;
|
||||
compressImage = config.sdImage.compressImage;
|
||||
populateImageCommands = config.sdImage.populateRootCommands;
|
||||
volumeLabel = "NIXOS_SD";
|
||||
} // optionalAttrs (config.sdImage.rootPartitionUUID != null) {
|
||||
uuid = config.sdImage.rootPartitionUUID;
|
||||
});
|
||||
rootfsImage = pkgs.callPackage ../../../lib/make-ext4-fs.nix (
|
||||
{
|
||||
inherit (config.sdImage) storePaths;
|
||||
compressImage = config.sdImage.compressImage;
|
||||
populateImageCommands = config.sdImage.populateRootCommands;
|
||||
volumeLabel = "NIXOS_SD";
|
||||
}
|
||||
// optionalAttrs (config.sdImage.rootPartitionUUID != null) {
|
||||
uuid = config.sdImage.rootPartitionUUID;
|
||||
}
|
||||
);
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
(mkRemovedOptionModule [ "sdImage" "bootPartitionID" ] "The FAT partition for SD image now only holds the Raspberry Pi firmware files. Use firmwarePartitionID to configure that partition's ID.")
|
||||
(mkRemovedOptionModule [ "sdImage" "bootSize" ] "The boot files for SD image have been moved to the main ext4 partition. The FAT partition now only holds the Raspberry Pi firmware files. Changing its size may not be required.")
|
||||
(mkRemovedOptionModule [ "sdImage" "bootPartitionID" ]
|
||||
"The FAT partition for SD image now only holds the Raspberry Pi firmware files. Use firmwarePartitionID to configure that partition's ID."
|
||||
)
|
||||
(mkRemovedOptionModule [ "sdImage" "bootSize" ]
|
||||
"The boot files for SD image have been moved to the main ext4 partition. The FAT partition now only holds the Raspberry Pi firmware files. Changing its size may not be required."
|
||||
)
|
||||
../../profiles/all-hardware.nix
|
||||
];
|
||||
|
||||
|
@ -169,7 +181,10 @@ in
|
|||
# Alternatively, this could be removed from the configuration.
|
||||
# The filesystem is not needed at runtime, it could be treated
|
||||
# as an opaque blob instead of a discrete FAT32 filesystem.
|
||||
options = [ "nofail" "noauto" ];
|
||||
options = [
|
||||
"nofail"
|
||||
"noauto"
|
||||
];
|
||||
};
|
||||
"/" = {
|
||||
device = "/dev/disk/by-label/NIXOS_SD";
|
||||
|
@ -179,122 +194,139 @@ in
|
|||
|
||||
sdImage.storePaths = [ config.system.build.toplevel ];
|
||||
|
||||
system.build.sdImage = pkgs.callPackage ({ stdenv, dosfstools, e2fsprogs,
|
||||
mtools, libfaketime, util-linux, zstd }: stdenv.mkDerivation {
|
||||
name = config.sdImage.imageName;
|
||||
system.build.sdImage = pkgs.callPackage (
|
||||
{
|
||||
stdenv,
|
||||
dosfstools,
|
||||
e2fsprogs,
|
||||
mtools,
|
||||
libfaketime,
|
||||
util-linux,
|
||||
zstd,
|
||||
}:
|
||||
stdenv.mkDerivation {
|
||||
name = config.sdImage.imageName;
|
||||
|
||||
nativeBuildInputs = [ dosfstools e2fsprogs libfaketime mtools util-linux ]
|
||||
++ lib.optional config.sdImage.compressImage zstd;
|
||||
nativeBuildInputs = [
|
||||
dosfstools
|
||||
e2fsprogs
|
||||
libfaketime
|
||||
mtools
|
||||
util-linux
|
||||
] ++ lib.optional config.sdImage.compressImage zstd;
|
||||
|
||||
inherit (config.sdImage) imageName compressImage;
|
||||
inherit (config.sdImage) imageName compressImage;
|
||||
|
||||
buildCommand = ''
|
||||
mkdir -p $out/nix-support $out/sd-image
|
||||
export img=$out/sd-image/${config.sdImage.imageName}
|
||||
buildCommand = ''
|
||||
mkdir -p $out/nix-support $out/sd-image
|
||||
export img=$out/sd-image/${config.sdImage.imageName}
|
||||
|
||||
echo "${pkgs.stdenv.buildPlatform.system}" > $out/nix-support/system
|
||||
if test -n "$compressImage"; then
|
||||
echo "file sd-image $img.zst" >> $out/nix-support/hydra-build-products
|
||||
else
|
||||
echo "file sd-image $img" >> $out/nix-support/hydra-build-products
|
||||
fi
|
||||
echo "${pkgs.stdenv.buildPlatform.system}" > $out/nix-support/system
|
||||
if test -n "$compressImage"; then
|
||||
echo "file sd-image $img.zst" >> $out/nix-support/hydra-build-products
|
||||
else
|
||||
echo "file sd-image $img" >> $out/nix-support/hydra-build-products
|
||||
fi
|
||||
|
||||
root_fs=${rootfsImage}
|
||||
${lib.optionalString config.sdImage.compressImage ''
|
||||
root_fs=./root-fs.img
|
||||
echo "Decompressing rootfs image"
|
||||
zstd -d --no-progress "${rootfsImage}" -o $root_fs
|
||||
''}
|
||||
root_fs=${rootfsImage}
|
||||
${lib.optionalString config.sdImage.compressImage ''
|
||||
root_fs=./root-fs.img
|
||||
echo "Decompressing rootfs image"
|
||||
zstd -d --no-progress "${rootfsImage}" -o $root_fs
|
||||
''}
|
||||
|
||||
# Gap in front of the first partition, in MiB
|
||||
gap=${toString config.sdImage.firmwarePartitionOffset}
|
||||
# Gap in front of the first partition, in MiB
|
||||
gap=${toString config.sdImage.firmwarePartitionOffset}
|
||||
|
||||
# Create the image file sized to fit /boot/firmware and /, plus slack for the gap.
|
||||
rootSizeBlocks=$(du -B 512 --apparent-size $root_fs | awk '{ print $1 }')
|
||||
firmwareSizeBlocks=$((${toString config.sdImage.firmwareSize} * 1024 * 1024 / 512))
|
||||
imageSize=$((rootSizeBlocks * 512 + firmwareSizeBlocks * 512 + gap * 1024 * 1024))
|
||||
truncate -s $imageSize $img
|
||||
# Create the image file sized to fit /boot/firmware and /, plus slack for the gap.
|
||||
rootSizeBlocks=$(du -B 512 --apparent-size $root_fs | awk '{ print $1 }')
|
||||
firmwareSizeBlocks=$((${toString config.sdImage.firmwareSize} * 1024 * 1024 / 512))
|
||||
imageSize=$((rootSizeBlocks * 512 + firmwareSizeBlocks * 512 + gap * 1024 * 1024))
|
||||
truncate -s $imageSize $img
|
||||
|
||||
# type=b is 'W95 FAT32', type=83 is 'Linux'.
|
||||
# The "bootable" partition is where u-boot will look file for the bootloader
|
||||
# information (dtbs, extlinux.conf file).
|
||||
sfdisk --no-reread --no-tell-kernel $img <<EOF
|
||||
label: dos
|
||||
label-id: ${config.sdImage.firmwarePartitionID}
|
||||
# type=b is 'W95 FAT32', type=83 is 'Linux'.
|
||||
# The "bootable" partition is where u-boot will look file for the bootloader
|
||||
# information (dtbs, extlinux.conf file).
|
||||
sfdisk --no-reread --no-tell-kernel $img <<EOF
|
||||
label: dos
|
||||
label-id: ${config.sdImage.firmwarePartitionID}
|
||||
|
||||
start=''${gap}M, size=$firmwareSizeBlocks, type=b
|
||||
start=$((gap + ${toString config.sdImage.firmwareSize}))M, type=83, bootable
|
||||
EOF
|
||||
start=''${gap}M, size=$firmwareSizeBlocks, type=b
|
||||
start=$((gap + ${toString config.sdImage.firmwareSize}))M, type=83, bootable
|
||||
EOF
|
||||
|
||||
# Copy the rootfs into the SD image
|
||||
eval $(partx $img -o START,SECTORS --nr 2 --pairs)
|
||||
dd conv=notrunc if=$root_fs of=$img seek=$START count=$SECTORS
|
||||
# Copy the rootfs into the SD image
|
||||
eval $(partx $img -o START,SECTORS --nr 2 --pairs)
|
||||
dd conv=notrunc if=$root_fs of=$img seek=$START count=$SECTORS
|
||||
|
||||
# Create a FAT32 /boot/firmware partition of suitable size into firmware_part.img
|
||||
eval $(partx $img -o START,SECTORS --nr 1 --pairs)
|
||||
truncate -s $((SECTORS * 512)) firmware_part.img
|
||||
# Create a FAT32 /boot/firmware partition of suitable size into firmware_part.img
|
||||
eval $(partx $img -o START,SECTORS --nr 1 --pairs)
|
||||
truncate -s $((SECTORS * 512)) firmware_part.img
|
||||
|
||||
mkfs.vfat --invariant -i ${config.sdImage.firmwarePartitionID} -n ${config.sdImage.firmwarePartitionName} firmware_part.img
|
||||
mkfs.vfat --invariant -i ${config.sdImage.firmwarePartitionID} -n ${config.sdImage.firmwarePartitionName} firmware_part.img
|
||||
|
||||
# Populate the files intended for /boot/firmware
|
||||
mkdir firmware
|
||||
${config.sdImage.populateFirmwareCommands}
|
||||
# Populate the files intended for /boot/firmware
|
||||
mkdir firmware
|
||||
${config.sdImage.populateFirmwareCommands}
|
||||
|
||||
find firmware -exec touch --date=2000-01-01 {} +
|
||||
# Copy the populated /boot/firmware into the SD image
|
||||
cd firmware
|
||||
# Force a fixed order in mcopy for better determinism, and avoid file globbing
|
||||
for d in $(find . -type d -mindepth 1 | sort); do
|
||||
faketime "2000-01-01 00:00:00" mmd -i ../firmware_part.img "::/$d"
|
||||
done
|
||||
for f in $(find . -type f | sort); do
|
||||
mcopy -pvm -i ../firmware_part.img "$f" "::/$f"
|
||||
done
|
||||
cd ..
|
||||
find firmware -exec touch --date=2000-01-01 {} +
|
||||
# Copy the populated /boot/firmware into the SD image
|
||||
cd firmware
|
||||
# Force a fixed order in mcopy for better determinism, and avoid file globbing
|
||||
for d in $(find . -type d -mindepth 1 | sort); do
|
||||
faketime "2000-01-01 00:00:00" mmd -i ../firmware_part.img "::/$d"
|
||||
done
|
||||
for f in $(find . -type f | sort); do
|
||||
mcopy -pvm -i ../firmware_part.img "$f" "::/$f"
|
||||
done
|
||||
cd ..
|
||||
|
||||
# Verify the FAT partition before copying it.
|
||||
fsck.vfat -vn firmware_part.img
|
||||
dd conv=notrunc if=firmware_part.img of=$img seek=$START count=$SECTORS
|
||||
# Verify the FAT partition before copying it.
|
||||
fsck.vfat -vn firmware_part.img
|
||||
dd conv=notrunc if=firmware_part.img of=$img seek=$START count=$SECTORS
|
||||
|
||||
${config.sdImage.postBuildCommands}
|
||||
${config.sdImage.postBuildCommands}
|
||||
|
||||
if test -n "$compressImage"; then
|
||||
zstd -T$NIX_BUILD_CORES --rm $img
|
||||
if test -n "$compressImage"; then
|
||||
zstd -T$NIX_BUILD_CORES --rm $img
|
||||
fi
|
||||
'';
|
||||
}
|
||||
) { };
|
||||
|
||||
boot.postBootCommands =
|
||||
let
|
||||
expandOnBoot = lib.optionalString config.sdImage.expandOnBoot ''
|
||||
# Figure out device names for the boot device and root filesystem.
|
||||
rootPart=$(${pkgs.util-linux}/bin/findmnt -n -o SOURCE /)
|
||||
bootDevice=$(lsblk -npo PKNAME $rootPart)
|
||||
partNum=$(lsblk -npo MAJ:MIN $rootPart | ${pkgs.gawk}/bin/awk -F: '{print $2}')
|
||||
|
||||
# Resize the root partition and the filesystem to fit the disk
|
||||
echo ",+," | sfdisk -N$partNum --no-reread $bootDevice
|
||||
${pkgs.parted}/bin/partprobe
|
||||
${pkgs.e2fsprogs}/bin/resize2fs $rootPart
|
||||
'';
|
||||
nixPathRegistrationFile = config.sdImage.nixPathRegistrationFile;
|
||||
in
|
||||
''
|
||||
# On the first boot do some maintenance tasks
|
||||
if [ -f ${nixPathRegistrationFile} ]; then
|
||||
set -euo pipefail
|
||||
set -x
|
||||
|
||||
${expandOnBoot}
|
||||
|
||||
# Register the contents of the initial Nix store
|
||||
${config.nix.package.out}/bin/nix-store --load-db < ${nixPathRegistrationFile}
|
||||
|
||||
# nixos-rebuild also requires a "system" profile and an /etc/NIXOS tag.
|
||||
touch /etc/NIXOS
|
||||
${config.nix.package.out}/bin/nix-env -p /nix/var/nix/profiles/system --set /run/current-system
|
||||
|
||||
# Prevents this from running on later boots.
|
||||
rm -f ${nixPathRegistrationFile}
|
||||
fi
|
||||
'';
|
||||
}) {};
|
||||
|
||||
boot.postBootCommands = let
|
||||
expandOnBoot = lib.optionalString config.sdImage.expandOnBoot ''
|
||||
# Figure out device names for the boot device and root filesystem.
|
||||
rootPart=$(${pkgs.util-linux}/bin/findmnt -n -o SOURCE /)
|
||||
bootDevice=$(lsblk -npo PKNAME $rootPart)
|
||||
partNum=$(lsblk -npo MAJ:MIN $rootPart | ${pkgs.gawk}/bin/awk -F: '{print $2}')
|
||||
|
||||
# Resize the root partition and the filesystem to fit the disk
|
||||
echo ",+," | sfdisk -N$partNum --no-reread $bootDevice
|
||||
${pkgs.parted}/bin/partprobe
|
||||
${pkgs.e2fsprogs}/bin/resize2fs $rootPart
|
||||
'';
|
||||
nixPathRegistrationFile = config.sdImage.nixPathRegistrationFile;
|
||||
in ''
|
||||
# On the first boot do some maintenance tasks
|
||||
if [ -f ${nixPathRegistrationFile} ]; then
|
||||
set -euo pipefail
|
||||
set -x
|
||||
|
||||
${expandOnBoot}
|
||||
|
||||
# Register the contents of the initial Nix store
|
||||
${config.nix.package.out}/bin/nix-store --load-db < ${nixPathRegistrationFile}
|
||||
|
||||
# nixos-rebuild also requires a "system" profile and an /etc/NIXOS tag.
|
||||
touch /etc/NIXOS
|
||||
${config.nix.package.out}/bin/nix-env -p /nix/var/nix/profiles/system --set /run/current-system
|
||||
|
||||
# Prevents this from running on later boots.
|
||||
rm -f ${nixPathRegistrationFile}
|
||||
fi
|
||||
'';
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,19 +1,29 @@
|
|||
# This module generates nixos-install, nixos-rebuild,
|
||||
# nixos-generate-config, etc.
|
||||
|
||||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
makeProg = args: pkgs.substituteAll (args // {
|
||||
dir = "bin";
|
||||
isExecutable = true;
|
||||
nativeBuildInputs = [
|
||||
pkgs.installShellFiles
|
||||
];
|
||||
postInstall = ''
|
||||
installManPage ${args.manPage}
|
||||
'';
|
||||
});
|
||||
makeProg =
|
||||
args:
|
||||
pkgs.substituteAll (
|
||||
args
|
||||
// {
|
||||
dir = "bin";
|
||||
isExecutable = true;
|
||||
nativeBuildInputs = [
|
||||
pkgs.installShellFiles
|
||||
];
|
||||
postInstall = ''
|
||||
installManPage ${args.manPage}
|
||||
'';
|
||||
}
|
||||
);
|
||||
|
||||
nixos-generate-config = makeProg {
|
||||
name = "nixos-generate-config";
|
||||
|
@ -33,13 +43,17 @@ let
|
|||
inherit (pkgs) runtimeShell;
|
||||
inherit (config.system.nixos) version codeName revision;
|
||||
inherit (config.system) configurationRevision;
|
||||
json = builtins.toJSON ({
|
||||
nixosVersion = config.system.nixos.version;
|
||||
} // lib.optionalAttrs (config.system.nixos.revision != null) {
|
||||
nixpkgsRevision = config.system.nixos.revision;
|
||||
} // lib.optionalAttrs (config.system.configurationRevision != null) {
|
||||
configurationRevision = config.system.configurationRevision;
|
||||
});
|
||||
json = builtins.toJSON (
|
||||
{
|
||||
nixosVersion = config.system.nixos.version;
|
||||
}
|
||||
// lib.optionalAttrs (config.system.nixos.revision != null) {
|
||||
nixpkgsRevision = config.system.nixos.revision;
|
||||
}
|
||||
// lib.optionalAttrs (config.system.configurationRevision != null) {
|
||||
configurationRevision = config.system.configurationRevision;
|
||||
}
|
||||
);
|
||||
manPage = ./manpages/nixos-version.8;
|
||||
};
|
||||
|
||||
|
@ -187,7 +201,7 @@ in
|
|||
desktopConfiguration = lib.mkOption {
|
||||
internal = true;
|
||||
type = lib.types.listOf lib.types.lines;
|
||||
default = [];
|
||||
default = [ ];
|
||||
description = ''
|
||||
Text to preseed the desktop configuration that `nixos-generate-config`
|
||||
saves to `/etc/nixos/configuration.nix`.
|
||||
|
@ -214,26 +228,46 @@ in
|
|||
'';
|
||||
};
|
||||
|
||||
imports = let
|
||||
mkToolModule = { name, package ? pkgs.${name} }: { config, ... }: {
|
||||
options.system.tools.${name}.enable = lib.mkEnableOption "${name} script" // {
|
||||
default = config.nix.enable && ! config.system.disableInstallerTools;
|
||||
defaultText = "config.nix.enable && !config.system.disableInstallerTools";
|
||||
};
|
||||
imports =
|
||||
let
|
||||
mkToolModule =
|
||||
{
|
||||
name,
|
||||
package ? pkgs.${name},
|
||||
}:
|
||||
{ config, ... }:
|
||||
{
|
||||
options.system.tools.${name}.enable = lib.mkEnableOption "${name} script" // {
|
||||
default = config.nix.enable && !config.system.disableInstallerTools;
|
||||
defaultText = "config.nix.enable && !config.system.disableInstallerTools";
|
||||
};
|
||||
|
||||
config = lib.mkIf config.system.tools.${name}.enable {
|
||||
environment.systemPackages = [ package ];
|
||||
};
|
||||
};
|
||||
in [
|
||||
(mkToolModule { name = "nixos-build-vms"; })
|
||||
(mkToolModule { name = "nixos-enter"; })
|
||||
(mkToolModule { name = "nixos-generate-config"; package = config.system.build.nixos-generate-config; })
|
||||
(mkToolModule { name = "nixos-install"; package = config.system.build.nixos-install; })
|
||||
(mkToolModule { name = "nixos-option"; })
|
||||
(mkToolModule { name = "nixos-rebuild"; package = config.system.build.nixos-rebuild; })
|
||||
(mkToolModule { name = "nixos-version"; package = nixos-version; })
|
||||
];
|
||||
config = lib.mkIf config.system.tools.${name}.enable {
|
||||
environment.systemPackages = [ package ];
|
||||
};
|
||||
};
|
||||
in
|
||||
[
|
||||
(mkToolModule { name = "nixos-build-vms"; })
|
||||
(mkToolModule { name = "nixos-enter"; })
|
||||
(mkToolModule {
|
||||
name = "nixos-generate-config";
|
||||
package = config.system.build.nixos-generate-config;
|
||||
})
|
||||
(mkToolModule {
|
||||
name = "nixos-install";
|
||||
package = config.system.build.nixos-install;
|
||||
})
|
||||
(mkToolModule { name = "nixos-option"; })
|
||||
(mkToolModule {
|
||||
name = "nixos-rebuild";
|
||||
package = config.system.build.nixos-rebuild;
|
||||
})
|
||||
(mkToolModule {
|
||||
name = "nixos-version";
|
||||
package = nixos-version;
|
||||
})
|
||||
];
|
||||
|
||||
config = {
|
||||
documentation.man.man-db.skipPackages = [ nixos-version ];
|
||||
|
|
|
@ -1,4 +1,16 @@
|
|||
{ config, options, lib, pkgs, utils, modules, baseModules, extraModules, modulesPath, specialArgs, ... }:
|
||||
{
|
||||
config,
|
||||
options,
|
||||
lib,
|
||||
pkgs,
|
||||
utils,
|
||||
modules,
|
||||
baseModules,
|
||||
extraModules,
|
||||
modulesPath,
|
||||
specialArgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
inherit (lib)
|
||||
|
@ -31,25 +43,26 @@ let
|
|||
cfg = config.documentation;
|
||||
allOpts = options;
|
||||
|
||||
canCacheDocs = m:
|
||||
canCacheDocs =
|
||||
m:
|
||||
let
|
||||
f = import m;
|
||||
instance = f (mapAttrs (n: _: abort "evaluating ${n} for `meta` failed") (functionArgs f));
|
||||
in
|
||||
cfg.nixos.options.splitBuild
|
||||
&& isPath m
|
||||
&& isFunction f
|
||||
&& instance ? options
|
||||
&& instance.meta.buildDocsInSandbox or true;
|
||||
cfg.nixos.options.splitBuild
|
||||
&& isPath m
|
||||
&& isFunction f
|
||||
&& instance ? options
|
||||
&& instance.meta.buildDocsInSandbox or true;
|
||||
|
||||
docModules =
|
||||
let
|
||||
p = partition canCacheDocs (baseModules ++ cfg.nixos.extraModules);
|
||||
in
|
||||
{
|
||||
lazy = p.right;
|
||||
eager = p.wrong ++ optionals cfg.nixos.includeAllModules (extraModules ++ modules);
|
||||
};
|
||||
{
|
||||
lazy = p.right;
|
||||
eager = p.wrong ++ optionals cfg.nixos.includeAllModules (extraModules ++ modules);
|
||||
};
|
||||
|
||||
manual = import ../../doc/manual rec {
|
||||
inherit pkgs config;
|
||||
|
@ -59,9 +72,11 @@ let
|
|||
options =
|
||||
let
|
||||
scrubbedEval = evalModules {
|
||||
modules = [ {
|
||||
_module.check = false;
|
||||
} ] ++ docModules.eager;
|
||||
modules = [
|
||||
{
|
||||
_module.check = false;
|
||||
}
|
||||
] ++ docModules.eager;
|
||||
class = "nixos";
|
||||
specialArgs = specialArgs // {
|
||||
pkgs = scrubDerivations "pkgs" pkgs;
|
||||
|
@ -71,33 +86,37 @@ let
|
|||
inherit modulesPath utils;
|
||||
};
|
||||
};
|
||||
scrubDerivations = namePrefix: pkgSet: mapAttrs
|
||||
(name: value:
|
||||
scrubDerivations =
|
||||
namePrefix: pkgSet:
|
||||
mapAttrs (
|
||||
name: value:
|
||||
let
|
||||
wholeName = "${namePrefix}.${name}";
|
||||
guard = warn "Attempt to evaluate package ${wholeName} in option documentation; this is not supported and will eventually be an error. Use `mkPackageOption{,MD}` or `literalExpression` instead.";
|
||||
in if isAttrs value then
|
||||
in
|
||||
if isAttrs value then
|
||||
scrubDerivations wholeName value
|
||||
// optionalAttrs (isDerivation value) {
|
||||
outPath = guard "\${${wholeName}}";
|
||||
drvPath = guard value.drvPath;
|
||||
}
|
||||
else value
|
||||
)
|
||||
pkgSet;
|
||||
in scrubbedEval.options;
|
||||
else
|
||||
value
|
||||
) pkgSet;
|
||||
in
|
||||
scrubbedEval.options;
|
||||
|
||||
baseOptionsJSON =
|
||||
let
|
||||
filter =
|
||||
builtins.filterSource
|
||||
(n: t:
|
||||
cleanSourceFilter n t
|
||||
&& (t == "directory" -> baseNameOf n != "tests")
|
||||
&& (t == "file" -> hasSuffix ".nix" n)
|
||||
);
|
||||
filter = builtins.filterSource (
|
||||
n: t:
|
||||
cleanSourceFilter n t
|
||||
&& (t == "directory" -> baseNameOf n != "tests")
|
||||
&& (t == "file" -> hasSuffix ".nix" n)
|
||||
);
|
||||
in
|
||||
pkgs.runCommand "lazy-options.json" {
|
||||
pkgs.runCommand "lazy-options.json"
|
||||
{
|
||||
libPath = filter (pkgs.path + "/lib");
|
||||
pkgsLibPath = filter (pkgs.path + "/pkgs/pkgs-lib");
|
||||
nixosPath = filter (pkgs.path + "/nixos");
|
||||
|
@ -107,7 +126,8 @@ let
|
|||
+ concatMapStringsSep " " (p: ''"${removePrefix "${modulesPath}/" (toString p)}"'') docModules.lazy
|
||||
+ " ]";
|
||||
passAsFile = [ "modules" ];
|
||||
} ''
|
||||
}
|
||||
''
|
||||
export NIX_STORE_DIR=$TMPDIR/store
|
||||
export NIX_STATE_DIR=$TMPDIR/state
|
||||
${pkgs.buildPackages.nix}/bin/nix-instantiate \
|
||||
|
@ -139,36 +159,37 @@ let
|
|||
inherit (cfg.nixos.options) warningsAreErrors;
|
||||
};
|
||||
|
||||
|
||||
nixos-help = let
|
||||
helpScript = pkgs.writeShellScriptBin "nixos-help" ''
|
||||
# Finds first executable browser in a colon-separated list.
|
||||
# (see how xdg-open defines BROWSER)
|
||||
browser="$(
|
||||
IFS=: ; for b in $BROWSER; do
|
||||
[ -n "$(type -P "$b" || true)" ] && echo "$b" && break
|
||||
done
|
||||
)"
|
||||
if [ -z "$browser" ]; then
|
||||
browser="$(type -P xdg-open || true)"
|
||||
nixos-help =
|
||||
let
|
||||
helpScript = pkgs.writeShellScriptBin "nixos-help" ''
|
||||
# Finds first executable browser in a colon-separated list.
|
||||
# (see how xdg-open defines BROWSER)
|
||||
browser="$(
|
||||
IFS=: ; for b in $BROWSER; do
|
||||
[ -n "$(type -P "$b" || true)" ] && echo "$b" && break
|
||||
done
|
||||
)"
|
||||
if [ -z "$browser" ]; then
|
||||
browser="${pkgs.w3m-nographics}/bin/w3m"
|
||||
browser="$(type -P xdg-open || true)"
|
||||
if [ -z "$browser" ]; then
|
||||
browser="${pkgs.w3m-nographics}/bin/w3m"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
exec "$browser" ${manual.manualHTMLIndex}
|
||||
'';
|
||||
exec "$browser" ${manual.manualHTMLIndex}
|
||||
'';
|
||||
|
||||
desktopItem = pkgs.makeDesktopItem {
|
||||
name = "nixos-manual";
|
||||
desktopName = "NixOS Manual";
|
||||
genericName = "System Manual";
|
||||
comment = "View NixOS documentation in a web browser";
|
||||
icon = "nix-snowflake";
|
||||
exec = "nixos-help";
|
||||
categories = ["System"];
|
||||
};
|
||||
desktopItem = pkgs.makeDesktopItem {
|
||||
name = "nixos-manual";
|
||||
desktopName = "NixOS Manual";
|
||||
genericName = "System Manual";
|
||||
comment = "View NixOS documentation in a web browser";
|
||||
icon = "nix-snowflake";
|
||||
exec = "nixos-help";
|
||||
categories = [ "System" ];
|
||||
};
|
||||
|
||||
in pkgs.symlinkJoin {
|
||||
in
|
||||
pkgs.symlinkJoin {
|
||||
name = "nixos-help";
|
||||
paths = [
|
||||
helpScript
|
||||
|
@ -187,11 +208,14 @@ in
|
|||
../config/system-path.nix
|
||||
../system/etc/etc.nix
|
||||
(mkRenamedOptionModule [ "programs" "info" "enable" ] [ "documentation" "info" "enable" ])
|
||||
(mkRenamedOptionModule [ "programs" "man" "enable" ] [ "documentation" "man" "enable" ])
|
||||
(mkRenamedOptionModule [ "programs" "man" "enable" ] [ "documentation" "man" "enable" ])
|
||||
(mkRenamedOptionModule [ "services" "nixosManual" "enable" ] [ "documentation" "nixos" "enable" ])
|
||||
(mkRemovedOptionModule
|
||||
[ "documentation" "nixos" "options" "allowDocBook" ]
|
||||
"DocBook option documentation is no longer supported")
|
||||
(mkRemovedOptionModule [
|
||||
"documentation"
|
||||
"nixos"
|
||||
"options"
|
||||
"allowDocBook"
|
||||
] "DocBook option documentation is no longer supported")
|
||||
];
|
||||
|
||||
options = {
|
||||
|
@ -280,7 +304,7 @@ in
|
|||
|
||||
nixos.extraModules = mkOption {
|
||||
type = types.listOf types.raw;
|
||||
default = [];
|
||||
default = [ ];
|
||||
description = ''
|
||||
Modules for which to show options even when not imported.
|
||||
'';
|
||||
|
@ -374,9 +398,13 @@ in
|
|||
(mkIf cfg.nixos.enable {
|
||||
system.build.manual = manual;
|
||||
|
||||
environment.systemPackages = []
|
||||
environment.systemPackages =
|
||||
[ ]
|
||||
++ optional cfg.man.enable manual.nixos-configuration-reference-manpage
|
||||
++ optionals cfg.doc.enable [ manual.manualHTML nixos-help ];
|
||||
++ optionals cfg.doc.enable [
|
||||
manual.manualHTML
|
||||
nixos-help
|
||||
];
|
||||
})
|
||||
|
||||
]);
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
# enabled in the initrd. Its primary use is in the NixOS installation
|
||||
# CDs.
|
||||
|
||||
{ pkgs, lib,... }:
|
||||
{ pkgs, lib, ... }:
|
||||
let
|
||||
platform = pkgs.stdenv.hostPlatform;
|
||||
in
|
||||
|
@ -12,26 +12,61 @@ in
|
|||
# The initrd has to contain any module that might be necessary for
|
||||
# supporting the most important parts of HW like drives.
|
||||
boot.initrd.availableKernelModules =
|
||||
[ # SATA/PATA support.
|
||||
[
|
||||
# SATA/PATA support.
|
||||
"ahci"
|
||||
|
||||
"ata_piix"
|
||||
|
||||
"sata_inic162x" "sata_nv" "sata_promise" "sata_qstor"
|
||||
"sata_sil" "sata_sil24" "sata_sis" "sata_svw" "sata_sx4"
|
||||
"sata_uli" "sata_via" "sata_vsc"
|
||||
"sata_inic162x"
|
||||
"sata_nv"
|
||||
"sata_promise"
|
||||
"sata_qstor"
|
||||
"sata_sil"
|
||||
"sata_sil24"
|
||||
"sata_sis"
|
||||
"sata_svw"
|
||||
"sata_sx4"
|
||||
"sata_uli"
|
||||
"sata_via"
|
||||
"sata_vsc"
|
||||
|
||||
"pata_ali" "pata_amd" "pata_artop" "pata_atiixp" "pata_efar"
|
||||
"pata_hpt366" "pata_hpt37x" "pata_hpt3x2n" "pata_hpt3x3"
|
||||
"pata_it8213" "pata_it821x" "pata_jmicron" "pata_marvell"
|
||||
"pata_mpiix" "pata_netcell" "pata_ns87410" "pata_oldpiix"
|
||||
"pata_pcmcia" "pata_pdc2027x" "pata_qdi" "pata_rz1000"
|
||||
"pata_serverworks" "pata_sil680" "pata_sis"
|
||||
"pata_sl82c105" "pata_triflex" "pata_via"
|
||||
"pata_ali"
|
||||
"pata_amd"
|
||||
"pata_artop"
|
||||
"pata_atiixp"
|
||||
"pata_efar"
|
||||
"pata_hpt366"
|
||||
"pata_hpt37x"
|
||||
"pata_hpt3x2n"
|
||||
"pata_hpt3x3"
|
||||
"pata_it8213"
|
||||
"pata_it821x"
|
||||
"pata_jmicron"
|
||||
"pata_marvell"
|
||||
"pata_mpiix"
|
||||
"pata_netcell"
|
||||
"pata_ns87410"
|
||||
"pata_oldpiix"
|
||||
"pata_pcmcia"
|
||||
"pata_pdc2027x"
|
||||
"pata_qdi"
|
||||
"pata_rz1000"
|
||||
"pata_serverworks"
|
||||
"pata_sil680"
|
||||
"pata_sis"
|
||||
"pata_sl82c105"
|
||||
"pata_triflex"
|
||||
"pata_via"
|
||||
"pata_winbond"
|
||||
|
||||
# SCSI support (incomplete).
|
||||
"3w-9xxx" "3w-xxxx" "aic79xx" "aic7xxx" "arcmsr" "hpsa"
|
||||
"3w-9xxx"
|
||||
"3w-xxxx"
|
||||
"aic79xx"
|
||||
"aic7xxx"
|
||||
"arcmsr"
|
||||
"hpsa"
|
||||
|
||||
# USB support, especially for booting from USB CD-ROM
|
||||
# drives.
|
||||
|
@ -44,20 +79,33 @@ in
|
|||
"nvme"
|
||||
|
||||
# Firewire support. Not tested.
|
||||
"ohci1394" "sbp2"
|
||||
"ohci1394"
|
||||
"sbp2"
|
||||
|
||||
# Virtio (QEMU, KVM etc.) support.
|
||||
"virtio_net" "virtio_pci" "virtio_mmio" "virtio_blk" "virtio_scsi" "virtio_balloon" "virtio_console"
|
||||
"virtio_net"
|
||||
"virtio_pci"
|
||||
"virtio_mmio"
|
||||
"virtio_blk"
|
||||
"virtio_scsi"
|
||||
"virtio_balloon"
|
||||
"virtio_console"
|
||||
|
||||
# VMware support.
|
||||
"mptspi" "vmxnet3" "vsock"
|
||||
] ++ lib.optional platform.isx86 "vmw_balloon"
|
||||
"mptspi"
|
||||
"vmxnet3"
|
||||
"vsock"
|
||||
]
|
||||
++ lib.optional platform.isx86 "vmw_balloon"
|
||||
++ lib.optionals (pkgs.stdenv.hostPlatform.isi686 || pkgs.stdenv.hostPlatform.isx86_64) [
|
||||
"vmw_vmci" "vmwgfx" "vmw_vsock_vmci_transport"
|
||||
"vmw_vmci"
|
||||
"vmwgfx"
|
||||
"vmw_vsock_vmci_transport"
|
||||
|
||||
# Hyper-V support.
|
||||
"hv_storvsc"
|
||||
] ++ lib.optionals pkgs.stdenv.hostPlatform.isAarch [
|
||||
]
|
||||
++ lib.optionals pkgs.stdenv.hostPlatform.isAarch [
|
||||
# Allwinner support
|
||||
# Required for early KMS
|
||||
"sun4i-drm"
|
||||
|
@ -68,7 +116,8 @@ in
|
|||
|
||||
# Broadcom
|
||||
"vc4"
|
||||
] ++ lib.optionals pkgs.stdenv.hostPlatform.isAarch64 [
|
||||
]
|
||||
++ lib.optionals pkgs.stdenv.hostPlatform.isAarch64 [
|
||||
# Most of the following falls into two categories:
|
||||
# - early KMS / early display
|
||||
# - early storage (e.g. USB) support
|
||||
|
@ -111,7 +160,6 @@ in
|
|||
# Include lots of firmware.
|
||||
hardware.enableRedistributableFirmware = true;
|
||||
|
||||
imports =
|
||||
[ ../hardware/network/zydas-zd1211.nix ];
|
||||
imports = [ ../hardware/network/zydas-zd1211.nix ];
|
||||
|
||||
}
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
# This module defines the software packages included in the "minimal"
|
||||
# installation CD. It might be useful elsewhere.
|
||||
|
||||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
{
|
||||
# Include some utilities that are useful for installing or repairing
|
||||
|
@ -43,9 +48,14 @@
|
|||
];
|
||||
|
||||
# Include support for various filesystems and tools to create / manipulate them.
|
||||
boot.supportedFilesystems =
|
||||
[ "btrfs" "cifs" "f2fs" "ntfs" "vfat" "xfs" ] ++
|
||||
lib.optional (lib.meta.availableOn pkgs.stdenv.hostPlatform config.boot.zfs.package) "zfs";
|
||||
boot.supportedFilesystems = [
|
||||
"btrfs"
|
||||
"cifs"
|
||||
"f2fs"
|
||||
"ntfs"
|
||||
"vfat"
|
||||
"xfs"
|
||||
] ++ lib.optional (lib.meta.availableOn pkgs.stdenv.hostPlatform config.boot.zfs.package) "zfs";
|
||||
|
||||
# Configure host id for ZFS to work
|
||||
networking.hostId = lib.mkDefault "8425e349";
|
||||
|
|
|
@ -1,23 +1,28 @@
|
|||
# Provide a basic configuration for installation devices like CDs.
|
||||
{ config, pkgs, lib, ... }:
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
|
||||
with lib;
|
||||
|
||||
{
|
||||
imports =
|
||||
[ # Enable devices which are usually scanned, because we don't know the
|
||||
# target system.
|
||||
../installer/scan/detected.nix
|
||||
../installer/scan/not-detected.nix
|
||||
imports = [
|
||||
# Enable devices which are usually scanned, because we don't know the
|
||||
# target system.
|
||||
../installer/scan/detected.nix
|
||||
../installer/scan/not-detected.nix
|
||||
|
||||
# Allow "nixos-rebuild" to work properly by providing
|
||||
# /etc/nixos/configuration.nix.
|
||||
./clone-config.nix
|
||||
# Allow "nixos-rebuild" to work properly by providing
|
||||
# /etc/nixos/configuration.nix.
|
||||
./clone-config.nix
|
||||
|
||||
# Include a copy of Nixpkgs so that nixos-install works out of
|
||||
# the box.
|
||||
../installer/cd-dvd/channel.nix
|
||||
];
|
||||
# Include a copy of Nixpkgs so that nixos-install works out of
|
||||
# the box.
|
||||
../installer/cd-dvd/channel.nix
|
||||
];
|
||||
|
||||
config = {
|
||||
system.nixos.variant_id = lib.mkDefault "installer";
|
||||
|
@ -31,7 +36,11 @@ with lib;
|
|||
# Use less privileged nixos user
|
||||
users.users.nixos = {
|
||||
isNormalUser = true;
|
||||
extraGroups = [ "wheel" "networkmanager" "video" ];
|
||||
extraGroups = [
|
||||
"wheel"
|
||||
"networkmanager"
|
||||
"video"
|
||||
];
|
||||
# Allow the graphical user to login without password
|
||||
initialHashedPassword = "";
|
||||
};
|
||||
|
@ -52,21 +61,23 @@ with lib;
|
|||
services.getty.autologinUser = "nixos";
|
||||
|
||||
# Some more help text.
|
||||
services.getty.helpLine = ''
|
||||
The "nixos" and "root" accounts have empty passwords.
|
||||
services.getty.helpLine =
|
||||
''
|
||||
The "nixos" and "root" accounts have empty passwords.
|
||||
|
||||
To log in over ssh you must set a password for either "nixos" or "root"
|
||||
with `passwd` (prefix with `sudo` for "root"), or add your public key to
|
||||
/home/nixos/.ssh/authorized_keys or /root/.ssh/authorized_keys.
|
||||
To log in over ssh you must set a password for either "nixos" or "root"
|
||||
with `passwd` (prefix with `sudo` for "root"), or add your public key to
|
||||
/home/nixos/.ssh/authorized_keys or /root/.ssh/authorized_keys.
|
||||
|
||||
If you need a wireless connection, type
|
||||
`sudo systemctl start wpa_supplicant` and configure a
|
||||
network using `wpa_cli`. See the NixOS manual for details.
|
||||
'' + optionalString config.services.xserver.enable ''
|
||||
If you need a wireless connection, type
|
||||
`sudo systemctl start wpa_supplicant` and configure a
|
||||
network using `wpa_cli`. See the NixOS manual for details.
|
||||
''
|
||||
+ optionalString config.services.xserver.enable ''
|
||||
|
||||
Type `sudo systemctl start display-manager' to
|
||||
start the graphical user interface.
|
||||
'';
|
||||
Type `sudo systemctl start display-manager' to
|
||||
start the graphical user interface.
|
||||
'';
|
||||
|
||||
# We run sshd by default. Login is only possible after adding a
|
||||
# password via "passwd" or by adding a ssh key to ~/.ssh/authorized_keys.
|
||||
|
@ -81,7 +92,7 @@ with lib;
|
|||
# Enable wpa_supplicant, but don't start it by default.
|
||||
networking.wireless.enable = mkDefault true;
|
||||
networking.wireless.userControlled.enable = true;
|
||||
systemd.services.wpa_supplicant.wantedBy = mkOverride 50 [];
|
||||
systemd.services.wpa_supplicant.wantedBy = mkOverride 50 [ ];
|
||||
|
||||
# Tell the Nix evaluator to garbage collect more aggressively.
|
||||
# This is desirable in memory-constrained environments that don't
|
||||
|
@ -97,15 +108,14 @@ with lib;
|
|||
|
||||
# To speed up installation a little bit, include the complete
|
||||
# stdenv in the Nix store on the CD.
|
||||
system.extraDependencies = with pkgs;
|
||||
[
|
||||
stdenv
|
||||
stdenvNoCC # for runCommand
|
||||
busybox
|
||||
jq # for closureInfo
|
||||
# For boot.initrd.systemd
|
||||
makeInitrdNGTool
|
||||
];
|
||||
system.extraDependencies = with pkgs; [
|
||||
stdenv
|
||||
stdenvNoCC # for runCommand
|
||||
busybox
|
||||
jq # for closureInfo
|
||||
# For boot.initrd.systemd
|
||||
makeInitrdNGTool
|
||||
];
|
||||
|
||||
boot.swraid.enable = true;
|
||||
# remove warning about unset mail
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
|
||||
|
@ -7,13 +12,13 @@ let
|
|||
cfg = config.programs.fish;
|
||||
|
||||
fishAbbrs = lib.concatStringsSep "\n" (
|
||||
lib.mapAttrsToList (k: v: "abbr -ag ${k} ${lib.escapeShellArg v}")
|
||||
cfg.shellAbbrs
|
||||
lib.mapAttrsToList (k: v: "abbr -ag ${k} ${lib.escapeShellArg v}") cfg.shellAbbrs
|
||||
);
|
||||
|
||||
fishAliases = lib.concatStringsSep "\n" (
|
||||
lib.mapAttrsToList (k: v: "alias ${k} ${lib.escapeShellArg v}")
|
||||
(lib.filterAttrs (k: v: v != null) cfg.shellAliases)
|
||||
lib.mapAttrsToList (k: v: "alias ${k} ${lib.escapeShellArg v}") (
|
||||
lib.filterAttrs (k: v: v != null) cfg.shellAliases
|
||||
)
|
||||
);
|
||||
|
||||
envShellInit = pkgs.writeText "shellInit" cfge.shellInit;
|
||||
|
@ -22,17 +27,19 @@ let
|
|||
|
||||
envInteractiveShellInit = pkgs.writeText "interactiveShellInit" cfge.interactiveShellInit;
|
||||
|
||||
sourceEnv = file:
|
||||
if cfg.useBabelfish then
|
||||
"source /etc/fish/${file}.fish"
|
||||
else
|
||||
''
|
||||
set fish_function_path ${pkgs.fishPlugins.foreign-env}/share/fish/vendor_functions.d $fish_function_path
|
||||
fenv source /etc/fish/foreign-env/${file} > /dev/null
|
||||
set -e fish_function_path[1]
|
||||
'';
|
||||
sourceEnv =
|
||||
file:
|
||||
if cfg.useBabelfish then
|
||||
"source /etc/fish/${file}.fish"
|
||||
else
|
||||
''
|
||||
set fish_function_path ${pkgs.fishPlugins.foreign-env}/share/fish/vendor_functions.d $fish_function_path
|
||||
fenv source /etc/fish/foreign-env/${file} > /dev/null
|
||||
set -e fish_function_path[1]
|
||||
'';
|
||||
|
||||
babelfishTranslate = path: name:
|
||||
babelfishTranslate =
|
||||
path: name:
|
||||
pkgs.runCommandLocal "${name}.fish" {
|
||||
nativeBuildInputs = [ pkgs.babelfish ];
|
||||
} "babelfish < ${path} > $out;";
|
||||
|
@ -89,7 +96,7 @@ in
|
|||
};
|
||||
|
||||
shellAbbrs = lib.mkOption {
|
||||
default = {};
|
||||
default = { };
|
||||
example = {
|
||||
gco = "git checkout";
|
||||
npu = "nix-prefetch-url";
|
||||
|
@ -101,7 +108,7 @@ in
|
|||
};
|
||||
|
||||
shellAliases = lib.mkOption {
|
||||
default = {};
|
||||
default = { };
|
||||
description = ''
|
||||
Set of aliases for fish shell, which overrides {option}`environment.shellAliases`.
|
||||
See {option}`environment.shellAliases` for an option format description.
|
||||
|
@ -153,16 +160,16 @@ in
|
|||
documentation.man.generateCaches = lib.mkDefault true;
|
||||
|
||||
environment = lib.mkMerge [
|
||||
(lib.mkIf cfg.useBabelfish
|
||||
{
|
||||
etc."fish/setEnvironment.fish".source = babelfishTranslate config.system.build.setEnvironment "setEnvironment";
|
||||
(lib.mkIf cfg.useBabelfish {
|
||||
etc."fish/setEnvironment.fish".source =
|
||||
babelfishTranslate config.system.build.setEnvironment "setEnvironment";
|
||||
etc."fish/shellInit.fish".source = babelfishTranslate envShellInit "shellInit";
|
||||
etc."fish/loginShellInit.fish".source = babelfishTranslate envLoginShellInit "loginShellInit";
|
||||
etc."fish/interactiveShellInit.fish".source = babelfishTranslate envInteractiveShellInit "interactiveShellInit";
|
||||
})
|
||||
etc."fish/interactiveShellInit.fish".source =
|
||||
babelfishTranslate envInteractiveShellInit "interactiveShellInit";
|
||||
})
|
||||
|
||||
(lib.mkIf (!cfg.useBabelfish)
|
||||
{
|
||||
(lib.mkIf (!cfg.useBabelfish) {
|
||||
etc."fish/foreign-env/shellInit".source = envShellInit;
|
||||
etc."fish/foreign-env/loginShellInit".source = envLoginShellInit;
|
||||
etc."fish/foreign-env/interactiveShellInit".source = envInteractiveShellInit;
|
||||
|
@ -170,108 +177,119 @@ in
|
|||
|
||||
{
|
||||
etc."fish/nixos-env-preinit.fish".text =
|
||||
if cfg.useBabelfish
|
||||
then ''
|
||||
# source the NixOS environment config
|
||||
if [ -z "$__NIXOS_SET_ENVIRONMENT_DONE" ]
|
||||
source /etc/fish/setEnvironment.fish
|
||||
end
|
||||
''
|
||||
else ''
|
||||
# This happens before $__fish_datadir/config.fish sets fish_function_path, so it is currently
|
||||
# unset. We set it and then completely erase it, leaving its configuration to $__fish_datadir/config.fish
|
||||
set fish_function_path ${pkgs.fishPlugins.foreign-env}/share/fish/vendor_functions.d $__fish_datadir/functions
|
||||
if cfg.useBabelfish then
|
||||
''
|
||||
# source the NixOS environment config
|
||||
if [ -z "$__NIXOS_SET_ENVIRONMENT_DONE" ]
|
||||
source /etc/fish/setEnvironment.fish
|
||||
end
|
||||
''
|
||||
else
|
||||
''
|
||||
# This happens before $__fish_datadir/config.fish sets fish_function_path, so it is currently
|
||||
# unset. We set it and then completely erase it, leaving its configuration to $__fish_datadir/config.fish
|
||||
set fish_function_path ${pkgs.fishPlugins.foreign-env}/share/fish/vendor_functions.d $__fish_datadir/functions
|
||||
|
||||
# source the NixOS environment config
|
||||
if [ -z "$__NIXOS_SET_ENVIRONMENT_DONE" ]
|
||||
fenv source ${config.system.build.setEnvironment}
|
||||
end
|
||||
# source the NixOS environment config
|
||||
if [ -z "$__NIXOS_SET_ENVIRONMENT_DONE" ]
|
||||
fenv source ${config.system.build.setEnvironment}
|
||||
end
|
||||
|
||||
# clear fish_function_path so that it will be correctly set when we return to $__fish_datadir/config.fish
|
||||
set -e fish_function_path
|
||||
'';
|
||||
# clear fish_function_path so that it will be correctly set when we return to $__fish_datadir/config.fish
|
||||
set -e fish_function_path
|
||||
'';
|
||||
}
|
||||
|
||||
{
|
||||
etc."fish/config.fish".text = ''
|
||||
# /etc/fish/config.fish: DO NOT EDIT -- this file has been generated automatically.
|
||||
# /etc/fish/config.fish: DO NOT EDIT -- this file has been generated automatically.
|
||||
|
||||
# if we haven't sourced the general config, do it
|
||||
if not set -q __fish_nixos_general_config_sourced
|
||||
${sourceEnv "shellInit"}
|
||||
# if we haven't sourced the general config, do it
|
||||
if not set -q __fish_nixos_general_config_sourced
|
||||
${sourceEnv "shellInit"}
|
||||
|
||||
${cfg.shellInit}
|
||||
${cfg.shellInit}
|
||||
|
||||
# and leave a note so we don't source this config section again from
|
||||
# this very shell (children will source the general config anew)
|
||||
set -g __fish_nixos_general_config_sourced 1
|
||||
end
|
||||
# and leave a note so we don't source this config section again from
|
||||
# this very shell (children will source the general config anew)
|
||||
set -g __fish_nixos_general_config_sourced 1
|
||||
end
|
||||
|
||||
# if we haven't sourced the login config, do it
|
||||
status is-login; and not set -q __fish_nixos_login_config_sourced
|
||||
and begin
|
||||
${sourceEnv "loginShellInit"}
|
||||
# if we haven't sourced the login config, do it
|
||||
status is-login; and not set -q __fish_nixos_login_config_sourced
|
||||
and begin
|
||||
${sourceEnv "loginShellInit"}
|
||||
|
||||
${cfg.loginShellInit}
|
||||
${cfg.loginShellInit}
|
||||
|
||||
# and leave a note so we don't source this config section again from
|
||||
# this very shell (children will source the general config anew)
|
||||
set -g __fish_nixos_login_config_sourced 1
|
||||
end
|
||||
# and leave a note so we don't source this config section again from
|
||||
# this very shell (children will source the general config anew)
|
||||
set -g __fish_nixos_login_config_sourced 1
|
||||
end
|
||||
|
||||
# if we haven't sourced the interactive config, do it
|
||||
status is-interactive; and not set -q __fish_nixos_interactive_config_sourced
|
||||
and begin
|
||||
${fishAbbrs}
|
||||
${fishAliases}
|
||||
# if we haven't sourced the interactive config, do it
|
||||
status is-interactive; and not set -q __fish_nixos_interactive_config_sourced
|
||||
and begin
|
||||
${fishAbbrs}
|
||||
${fishAliases}
|
||||
|
||||
${sourceEnv "interactiveShellInit"}
|
||||
${sourceEnv "interactiveShellInit"}
|
||||
|
||||
${cfg.promptInit}
|
||||
${cfg.interactiveShellInit}
|
||||
${cfg.promptInit}
|
||||
${cfg.interactiveShellInit}
|
||||
|
||||
# and leave a note so we don't source this config section again from
|
||||
# this very shell (children will source the general config anew,
|
||||
# allowing configuration changes in, e.g, aliases, to propagate)
|
||||
set -g __fish_nixos_interactive_config_sourced 1
|
||||
end
|
||||
'';
|
||||
# and leave a note so we don't source this config section again from
|
||||
# this very shell (children will source the general config anew,
|
||||
# allowing configuration changes in, e.g, aliases, to propagate)
|
||||
set -g __fish_nixos_interactive_config_sourced 1
|
||||
end
|
||||
'';
|
||||
}
|
||||
|
||||
{
|
||||
etc."fish/generated_completions".source =
|
||||
let
|
||||
patchedGenerator = pkgs.stdenv.mkDerivation {
|
||||
name = "fish_patched-completion-generator";
|
||||
srcs = [
|
||||
"${cfg.package}/share/fish/tools/create_manpage_completions.py"
|
||||
"${cfg.package}/share/fish/tools/deroff.py"
|
||||
];
|
||||
unpackCmd = "cp $curSrc $(basename $curSrc)";
|
||||
sourceRoot = ".";
|
||||
patches = [ ./fish_completion-generator.patch ]; # to prevent collisions of identical completion files
|
||||
dontBuild = true;
|
||||
installPhase = ''
|
||||
mkdir -p $out
|
||||
cp * $out/
|
||||
'';
|
||||
preferLocalBuild = true;
|
||||
allowSubstitutes = false;
|
||||
};
|
||||
generateCompletions = package: pkgs.runCommandLocal
|
||||
( with lib.strings; let
|
||||
storeLength = stringLength storeDir + 34; # Nix' StorePath::HashLen + 2 for the separating slash and dash
|
||||
pathName = substring storeLength (stringLength package - storeLength) package;
|
||||
in (package.name or pathName) + "_fish-completions")
|
||||
( { inherit package; } //
|
||||
lib.optionalAttrs (package ? meta.priority) { meta.priority = package.meta.priority; })
|
||||
''
|
||||
mkdir -p $out
|
||||
if [ -d $package/share/man ]; then
|
||||
find $package/share/man -type f | xargs ${pkgs.python3.pythonOnBuildForHost.interpreter} ${patchedGenerator}/create_manpage_completions.py --directory $out >/dev/null
|
||||
fi
|
||||
'';
|
||||
in
|
||||
let
|
||||
patchedGenerator = pkgs.stdenv.mkDerivation {
|
||||
name = "fish_patched-completion-generator";
|
||||
srcs = [
|
||||
"${cfg.package}/share/fish/tools/create_manpage_completions.py"
|
||||
"${cfg.package}/share/fish/tools/deroff.py"
|
||||
];
|
||||
unpackCmd = "cp $curSrc $(basename $curSrc)";
|
||||
sourceRoot = ".";
|
||||
patches = [ ./fish_completion-generator.patch ]; # to prevent collisions of identical completion files
|
||||
dontBuild = true;
|
||||
installPhase = ''
|
||||
mkdir -p $out
|
||||
cp * $out/
|
||||
'';
|
||||
preferLocalBuild = true;
|
||||
allowSubstitutes = false;
|
||||
};
|
||||
generateCompletions =
|
||||
package:
|
||||
pkgs.runCommandLocal
|
||||
(
|
||||
with lib.strings;
|
||||
let
|
||||
storeLength = stringLength storeDir + 34; # Nix' StorePath::HashLen + 2 for the separating slash and dash
|
||||
pathName = substring storeLength (stringLength package - storeLength) package;
|
||||
in
|
||||
(package.name or pathName) + "_fish-completions"
|
||||
)
|
||||
(
|
||||
{
|
||||
inherit package;
|
||||
}
|
||||
// lib.optionalAttrs (package ? meta.priority) { meta.priority = package.meta.priority; }
|
||||
)
|
||||
''
|
||||
mkdir -p $out
|
||||
if [ -d $package/share/man ]; then
|
||||
find $package/share/man -type f | xargs ${pkgs.python3.pythonOnBuildForHost.interpreter} ${patchedGenerator}/create_manpage_completions.py --directory $out >/dev/null
|
||||
fi
|
||||
'';
|
||||
in
|
||||
pkgs.buildEnv {
|
||||
name = "system_fish-completions";
|
||||
ignoreCollisions = true;
|
||||
|
@ -281,10 +299,11 @@ in
|
|||
|
||||
# include programs that bring their own completions
|
||||
{
|
||||
pathsToLink = []
|
||||
++ lib.optional cfg.vendor.config.enable "/share/fish/vendor_conf.d"
|
||||
++ lib.optional cfg.vendor.completions.enable "/share/fish/vendor_completions.d"
|
||||
++ lib.optional cfg.vendor.functions.enable "/share/fish/vendor_functions.d";
|
||||
pathsToLink =
|
||||
[ ]
|
||||
++ lib.optional cfg.vendor.config.enable "/share/fish/vendor_conf.d"
|
||||
++ lib.optional cfg.vendor.completions.enable "/share/fish/vendor_completions.d"
|
||||
++ lib.optional cfg.vendor.functions.enable "/share/fish/vendor_functions.d";
|
||||
}
|
||||
|
||||
{ systemPackages = [ cfg.package ]; }
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
{ pkgs, config, lib, ... }:
|
||||
{
|
||||
pkgs,
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.programs.fzf;
|
||||
|
@ -16,19 +21,24 @@ in
|
|||
|
||||
programs = {
|
||||
# load after programs.bash.completion.enable
|
||||
bash.promptPluginInit = lib.mkAfter (lib.optionalString cfg.fuzzyCompletion ''
|
||||
source ${pkgs.fzf}/share/fzf/completion.bash
|
||||
'' + lib.optionalString cfg.keybindings ''
|
||||
source ${pkgs.fzf}/share/fzf/key-bindings.bash
|
||||
'');
|
||||
bash.promptPluginInit = lib.mkAfter (
|
||||
lib.optionalString cfg.fuzzyCompletion ''
|
||||
source ${pkgs.fzf}/share/fzf/completion.bash
|
||||
''
|
||||
+ lib.optionalString cfg.keybindings ''
|
||||
source ${pkgs.fzf}/share/fzf/key-bindings.bash
|
||||
''
|
||||
);
|
||||
|
||||
zsh = {
|
||||
interactiveShellInit = lib.optionalString (!config.programs.zsh.ohMyZsh.enable)
|
||||
(lib.optionalString cfg.fuzzyCompletion ''
|
||||
source ${pkgs.fzf}/share/fzf/completion.zsh
|
||||
'' + lib.optionalString cfg.keybindings ''
|
||||
source ${pkgs.fzf}/share/fzf/key-bindings.zsh
|
||||
'');
|
||||
interactiveShellInit = lib.optionalString (!config.programs.zsh.ohMyZsh.enable) (
|
||||
lib.optionalString cfg.fuzzyCompletion ''
|
||||
source ${pkgs.fzf}/share/fzf/completion.zsh
|
||||
''
|
||||
+ lib.optionalString cfg.keybindings ''
|
||||
source ${pkgs.fzf}/share/fzf/key-bindings.zsh
|
||||
''
|
||||
);
|
||||
|
||||
ohMyZsh.plugins = lib.mkIf config.programs.zsh.ohMyZsh.enable [ "fzf" ];
|
||||
};
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.programs.git;
|
||||
|
@ -19,27 +24,43 @@ in
|
|||
let
|
||||
gitini = attrsOf (attrsOf anything);
|
||||
in
|
||||
either gitini (listOf gitini) // {
|
||||
merge = loc: defs:
|
||||
either gitini (listOf gitini)
|
||||
// {
|
||||
merge =
|
||||
loc: defs:
|
||||
let
|
||||
config = builtins.foldl'
|
||||
(acc: { value, ... }@x: acc // (if builtins.isList value then {
|
||||
ordered = acc.ordered ++ value;
|
||||
} else {
|
||||
unordered = acc.unordered ++ [ x ];
|
||||
}))
|
||||
{
|
||||
ordered = [ ];
|
||||
unordered = [ ];
|
||||
}
|
||||
defs;
|
||||
config =
|
||||
builtins.foldl'
|
||||
(
|
||||
acc:
|
||||
{ value, ... }@x:
|
||||
acc
|
||||
// (
|
||||
if builtins.isList value then
|
||||
{
|
||||
ordered = acc.ordered ++ value;
|
||||
}
|
||||
else
|
||||
{
|
||||
unordered = acc.unordered ++ [ x ];
|
||||
}
|
||||
)
|
||||
)
|
||||
{
|
||||
ordered = [ ];
|
||||
unordered = [ ];
|
||||
}
|
||||
defs;
|
||||
in
|
||||
[ (gitini.merge loc config.unordered) ] ++ config.ordered;
|
||||
};
|
||||
default = [ ];
|
||||
example = {
|
||||
init.defaultBranch = "main";
|
||||
url."https://github.com/".insteadOf = [ "gh:" "github:" ];
|
||||
url."https://github.com/".insteadOf = [
|
||||
"gh:"
|
||||
"github:"
|
||||
];
|
||||
};
|
||||
description = ''
|
||||
Configuration to write to /etc/gitconfig. A list can also be
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
{
|
||||
meta.maintainers = [ lib.maintainers.league ];
|
||||
|
@ -23,6 +28,6 @@
|
|||
config = lib.mkIf config.programs.gphoto2.enable {
|
||||
services.udev.packages = [ pkgs.libgphoto2 ];
|
||||
environment.systemPackages = [ pkgs.gphoto2 ];
|
||||
users.groups.camera = {};
|
||||
users.groups.camera = { };
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,8 +1,14 @@
|
|||
{ config, pkgs, lib, ... }:
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.programs.iotop;
|
||||
in {
|
||||
in
|
||||
{
|
||||
options = {
|
||||
programs.iotop.enable = lib.mkEnableOption "iotop + setcap wrapper";
|
||||
};
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
{ config, pkgs, lib, ... }:
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
|
||||
{
|
||||
# interface
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.programs.nano;
|
||||
|
@ -37,11 +42,13 @@ in
|
|||
|
||||
config = lib.mkIf cfg.enable {
|
||||
environment = {
|
||||
etc.nanorc.text = (lib.optionalString cfg.syntaxHighlight ''
|
||||
# load syntax highlighting files
|
||||
include "${cfg.package}/share/nano/*.nanorc"
|
||||
include "${cfg.package}/share/nano/extra/*.nanorc"
|
||||
'') + cfg.nanorc;
|
||||
etc.nanorc.text =
|
||||
(lib.optionalString cfg.syntaxHighlight ''
|
||||
# load syntax highlighting files
|
||||
include "${cfg.package}/share/nano/*.nanorc"
|
||||
include "${cfg.package}/share/nano/extra/*.nanorc"
|
||||
'')
|
||||
+ cfg.nanorc;
|
||||
systemPackages = [ cfg.package ];
|
||||
pathsToLink = [ "/share/nano" ];
|
||||
};
|
||||
|
|
|
@ -14,7 +14,7 @@ in
|
|||
programs.qgroundcontrol = {
|
||||
enable = lib.mkEnableOption "qgroundcontrol";
|
||||
|
||||
package = lib.mkPackageOption pkgs "qgroundcontrol" {};
|
||||
package = lib.mkPackageOption pkgs "qgroundcontrol" { };
|
||||
|
||||
blacklistModemManagerFromTTYUSB = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
# Configuration for the pwdutils suite of tools: passwd, useradd, etc.
|
||||
{ config, lib, utils, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
utils,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.security.loginDefs;
|
||||
in
|
||||
|
@ -35,27 +41,37 @@ in
|
|||
'';
|
||||
type = lib.types.submodule {
|
||||
freeformType = (pkgs.formats.keyValue { }).type;
|
||||
/* There are three different sources for user/group id ranges, each of which gets
|
||||
used by different programs:
|
||||
- The login.defs file, used by the useradd, groupadd and newusers commands
|
||||
- The update-users-groups.pl file, used by NixOS in the activation phase to
|
||||
decide on which ids to use for declaratively defined users without a static
|
||||
id
|
||||
- Systemd compile time options -Dsystem-uid-max= and -Dsystem-gid-max=, used
|
||||
by systemd for features like ConditionUser=@system and systemd-sysusers
|
||||
*/
|
||||
/*
|
||||
There are three different sources for user/group id ranges, each of which gets
|
||||
used by different programs:
|
||||
- The login.defs file, used by the useradd, groupadd and newusers commands
|
||||
- The update-users-groups.pl file, used by NixOS in the activation phase to
|
||||
decide on which ids to use for declaratively defined users without a static
|
||||
id
|
||||
- Systemd compile time options -Dsystem-uid-max= and -Dsystem-gid-max=, used
|
||||
by systemd for features like ConditionUser=@system and systemd-sysusers
|
||||
*/
|
||||
options = {
|
||||
DEFAULT_HOME = lib.mkOption {
|
||||
description = "Indicate if login is allowed if we can't cd to the home directory.";
|
||||
default = "yes";
|
||||
type = lib.types.enum [ "yes" "no" ];
|
||||
type = lib.types.enum [
|
||||
"yes"
|
||||
"no"
|
||||
];
|
||||
};
|
||||
|
||||
ENCRYPT_METHOD = lib.mkOption {
|
||||
description = "This defines the system default encryption algorithm for encrypting passwords.";
|
||||
# The default crypt() method, keep in sync with the PAM default
|
||||
default = "YESCRYPT";
|
||||
type = lib.types.enum [ "YESCRYPT" "SHA512" "SHA256" "MD5" "DES"];
|
||||
type = lib.types.enum [
|
||||
"YESCRYPT"
|
||||
"SHA512"
|
||||
"SHA256"
|
||||
"MD5"
|
||||
"DES"
|
||||
];
|
||||
};
|
||||
|
||||
SYS_UID_MIN = lib.mkOption {
|
||||
|
@ -180,7 +196,8 @@ in
|
|||
|
||||
security.loginDefs.settings.CHFN_RESTRICT = lib.mkIf (cfg.chfnRestrict != null) cfg.chfnRestrict;
|
||||
|
||||
environment.systemPackages = lib.optional config.users.mutableUsers cfg.package
|
||||
environment.systemPackages =
|
||||
lib.optional config.users.mutableUsers cfg.package
|
||||
++ lib.optional (lib.types.shellPackage.check config.users.defaultUserShell) config.users.defaultUserShell
|
||||
++ lib.optional (cfg.chfnRestrict != null) pkgs.util-linux;
|
||||
|
||||
|
@ -191,7 +208,8 @@ in
|
|||
toKeyValue = lib.generators.toKeyValue {
|
||||
mkKeyValue = lib.generators.mkKeyValueDefault { } " ";
|
||||
};
|
||||
in {
|
||||
in
|
||||
{
|
||||
# /etc/login.defs: global configuration for pwdutils.
|
||||
# You cannot login without it!
|
||||
"login.defs".source = pkgs.writeText "login.defs" (toKeyValue cfg.settings);
|
||||
|
@ -241,17 +259,17 @@ in
|
|||
inherit source;
|
||||
};
|
||||
in
|
||||
{
|
||||
su = mkSetuidRoot "${cfg.package.su}/bin/su";
|
||||
sg = mkSetuidRoot "${cfg.package.out}/bin/sg";
|
||||
newgrp = mkSetuidRoot "${cfg.package.out}/bin/newgrp";
|
||||
newuidmap = mkSetuidRoot "${cfg.package.out}/bin/newuidmap";
|
||||
newgidmap = mkSetuidRoot "${cfg.package.out}/bin/newgidmap";
|
||||
}
|
||||
// lib.optionalAttrs config.users.mutableUsers {
|
||||
chsh = mkSetuidRoot "${cfg.package.out}/bin/chsh";
|
||||
passwd = mkSetuidRoot "${cfg.package.out}/bin/passwd";
|
||||
};
|
||||
{
|
||||
su = mkSetuidRoot "${cfg.package.su}/bin/su";
|
||||
sg = mkSetuidRoot "${cfg.package.out}/bin/sg";
|
||||
newgrp = mkSetuidRoot "${cfg.package.out}/bin/newgrp";
|
||||
newuidmap = mkSetuidRoot "${cfg.package.out}/bin/newuidmap";
|
||||
newgidmap = mkSetuidRoot "${cfg.package.out}/bin/newgidmap";
|
||||
}
|
||||
// lib.optionalAttrs config.users.mutableUsers {
|
||||
chsh = mkSetuidRoot "${cfg.package.out}/bin/chsh";
|
||||
passwd = mkSetuidRoot "${cfg.package.out}/bin/passwd";
|
||||
};
|
||||
})
|
||||
];
|
||||
}
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.programs.steam;
|
||||
|
@ -6,9 +11,12 @@ let
|
|||
|
||||
extraCompatPaths = lib.makeSearchPathOutput "steamcompattool" "" cfg.extraCompatPackages;
|
||||
|
||||
steam-gamescope = let
|
||||
exports = builtins.attrValues (builtins.mapAttrs (n: v: "export ${n}=${v}") cfg.gamescopeSession.env);
|
||||
in
|
||||
steam-gamescope =
|
||||
let
|
||||
exports = builtins.attrValues (
|
||||
builtins.mapAttrs (n: v: "export ${n}=${v}") cfg.gamescopeSession.env
|
||||
);
|
||||
in
|
||||
pkgs.writeShellScriptBin "steam-gamescope" ''
|
||||
${builtins.concatStringsSep "\n" exports}
|
||||
gamescope --steam ${builtins.toString cfg.gamescopeSession.args} -- steam -tenfoot -pipewire-dmabuf
|
||||
|
@ -21,8 +29,12 @@ let
|
|||
Comment=A digital distribution platform
|
||||
Exec=${steam-gamescope}/bin/steam-gamescope
|
||||
Type=Application
|
||||
'').overrideAttrs (_: { passthru.providedSessions = [ "steam" ]; });
|
||||
in {
|
||||
'').overrideAttrs
|
||||
(_: {
|
||||
passthru.providedSessions = [ "steam" ];
|
||||
});
|
||||
in
|
||||
{
|
||||
options.programs.steam = {
|
||||
enable = lib.mkEnableOption "steam";
|
||||
|
||||
|
@ -42,27 +54,40 @@ in {
|
|||
];
|
||||
}
|
||||
'';
|
||||
apply = steam: steam.override (prev: {
|
||||
extraEnv = (lib.optionalAttrs (cfg.extraCompatPackages != [ ]) {
|
||||
STEAM_EXTRA_COMPAT_TOOLS_PATHS = extraCompatPaths;
|
||||
}) // (lib.optionalAttrs cfg.extest.enable {
|
||||
LD_PRELOAD = "${pkgs.pkgsi686Linux.extest}/lib/libextest.so";
|
||||
}) // (prev.extraEnv or {});
|
||||
extraLibraries = pkgs: let
|
||||
prevLibs = if prev ? extraLibraries then prev.extraLibraries pkgs else [ ];
|
||||
additionalLibs = with config.hardware.graphics;
|
||||
if pkgs.stdenv.hostPlatform.is64bit
|
||||
then [ package ] ++ extraPackages
|
||||
else [ package32 ] ++ extraPackages32;
|
||||
in prevLibs ++ additionalLibs;
|
||||
extraPkgs = p: (cfg.extraPackages ++ lib.optionals (prev ? extraPkgs) (prev.extraPkgs p));
|
||||
} // lib.optionalAttrs (cfg.gamescopeSession.enable && gamescopeCfg.capSysNice)
|
||||
{
|
||||
buildFHSEnv = pkgs.buildFHSEnv.override {
|
||||
# use the setuid wrapped bubblewrap
|
||||
bubblewrap = "${config.security.wrapperDir}/..";
|
||||
};
|
||||
});
|
||||
apply =
|
||||
steam:
|
||||
steam.override (
|
||||
prev:
|
||||
{
|
||||
extraEnv =
|
||||
(lib.optionalAttrs (cfg.extraCompatPackages != [ ]) {
|
||||
STEAM_EXTRA_COMPAT_TOOLS_PATHS = extraCompatPaths;
|
||||
})
|
||||
// (lib.optionalAttrs cfg.extest.enable {
|
||||
LD_PRELOAD = "${pkgs.pkgsi686Linux.extest}/lib/libextest.so";
|
||||
})
|
||||
// (prev.extraEnv or { });
|
||||
extraLibraries =
|
||||
pkgs:
|
||||
let
|
||||
prevLibs = if prev ? extraLibraries then prev.extraLibraries pkgs else [ ];
|
||||
additionalLibs =
|
||||
with config.hardware.graphics;
|
||||
if pkgs.stdenv.hostPlatform.is64bit then
|
||||
[ package ] ++ extraPackages
|
||||
else
|
||||
[ package32 ] ++ extraPackages32;
|
||||
in
|
||||
prevLibs ++ additionalLibs;
|
||||
extraPkgs = p: (cfg.extraPackages ++ lib.optionals (prev ? extraPkgs) (prev.extraPkgs p));
|
||||
}
|
||||
// lib.optionalAttrs (cfg.gamescopeSession.enable && gamescopeCfg.capSysNice) {
|
||||
buildFHSEnv = pkgs.buildFHSEnv.override {
|
||||
# use the setuid wrapped bubblewrap
|
||||
bubblewrap = "${config.security.wrapperDir}/..";
|
||||
};
|
||||
}
|
||||
);
|
||||
description = ''
|
||||
The Steam package to use. Additional libraries are added from the system
|
||||
configuration to ensure graphics work properly.
|
||||
|
@ -141,7 +166,7 @@ in {
|
|||
|
||||
gamescopeSession = lib.mkOption {
|
||||
description = "Run a GameScope driven Steam session from your display-manager";
|
||||
default = {};
|
||||
default = { };
|
||||
type = lib.types.submodule {
|
||||
options = {
|
||||
enable = lib.mkEnableOption "GameScope Session";
|
||||
|
@ -176,7 +201,8 @@ in {
|
|||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
hardware.graphics = { # this fixes the "glXChooseVisual failed" bug, context: https://github.com/NixOS/nixpkgs/issues/47932
|
||||
hardware.graphics = {
|
||||
# this fixes the "glXChooseVisual failed" bug, context: https://github.com/NixOS/nixpkgs/issues/47932
|
||||
enable = true;
|
||||
enable32Bit = true;
|
||||
};
|
||||
|
@ -194,7 +220,9 @@ in {
|
|||
programs.steam.extraPackages = cfg.fontPackages;
|
||||
|
||||
programs.gamescope.enable = lib.mkDefault cfg.gamescopeSession.enable;
|
||||
services.displayManager.sessionPackages = lib.mkIf cfg.gamescopeSession.enable [ gamescopeSessionFile ];
|
||||
services.displayManager.sessionPackages = lib.mkIf cfg.gamescopeSession.enable [
|
||||
gamescopeSessionFile
|
||||
];
|
||||
|
||||
# enable 32bit pulseaudio/pipewire support if needed
|
||||
hardware.pulseaudio.support32Bit = config.hardware.pulseaudio.enable;
|
||||
|
@ -202,11 +230,15 @@ in {
|
|||
|
||||
hardware.steam-hardware.enable = true;
|
||||
|
||||
environment.systemPackages = [
|
||||
cfg.package
|
||||
cfg.package.run
|
||||
] ++ lib.optional cfg.gamescopeSession.enable steam-gamescope
|
||||
++ lib.optional cfg.protontricks.enable (cfg.protontricks.package.override { inherit extraCompatPaths; });
|
||||
environment.systemPackages =
|
||||
[
|
||||
cfg.package
|
||||
cfg.package.run
|
||||
]
|
||||
++ lib.optional cfg.gamescopeSession.enable steam-gamescope
|
||||
++ lib.optional cfg.protontricks.enable (
|
||||
cfg.protontricks.package.override { inherit extraCompatPaths; }
|
||||
);
|
||||
|
||||
networking.firewall = lib.mkMerge [
|
||||
(lib.mkIf (cfg.remotePlay.openFirewall || cfg.localNetworkGameTransfers.openFirewall) {
|
||||
|
@ -215,7 +247,12 @@ in {
|
|||
|
||||
(lib.mkIf cfg.remotePlay.openFirewall {
|
||||
allowedTCPPorts = [ 27036 ];
|
||||
allowedUDPPortRanges = [ { from = 27031; to = 27035; } ];
|
||||
allowedUDPPortRanges = [
|
||||
{
|
||||
from = 27031;
|
||||
to = 27035;
|
||||
}
|
||||
];
|
||||
})
|
||||
|
||||
(lib.mkIf cfg.dedicatedServer.openFirewall {
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.programs.sway;
|
||||
|
@ -14,33 +19,43 @@ in
|
|||
<https://github.com/swaywm/sway/wiki> and
|
||||
"man 5 sway" for more information'';
|
||||
|
||||
package = lib.mkPackageOption pkgs "sway" {
|
||||
nullable = true;
|
||||
extraDescription = ''
|
||||
If the package is not overridable with `extraSessionCommands`, `extraOptions`,
|
||||
`withBaseWrapper`, `withGtkWrapper`, `enableXWayland` and `isNixOS`,
|
||||
then the module options {option}`wrapperFeatures`, {option}`extraSessionCommands`,
|
||||
{option}`extraOptions` and {option}`xwayland` will have no effect.
|
||||
package =
|
||||
lib.mkPackageOption pkgs "sway" {
|
||||
nullable = true;
|
||||
extraDescription = ''
|
||||
If the package is not overridable with `extraSessionCommands`, `extraOptions`,
|
||||
`withBaseWrapper`, `withGtkWrapper`, `enableXWayland` and `isNixOS`,
|
||||
then the module options {option}`wrapperFeatures`, {option}`extraSessionCommands`,
|
||||
{option}`extraOptions` and {option}`xwayland` will have no effect.
|
||||
|
||||
Set to `null` to not add any Sway package to your path.
|
||||
This should be done if you want to use the Home Manager Sway module to install Sway.
|
||||
'';
|
||||
} // {
|
||||
apply = p: if p == null then null else
|
||||
wayland-lib.genFinalPackage p {
|
||||
extraSessionCommands = cfg.extraSessionCommands;
|
||||
extraOptions = cfg.extraOptions;
|
||||
withBaseWrapper = cfg.wrapperFeatures.base;
|
||||
withGtkWrapper = cfg.wrapperFeatures.gtk;
|
||||
enableXWayland = cfg.xwayland.enable;
|
||||
isNixOS = true;
|
||||
};
|
||||
};
|
||||
Set to `null` to not add any Sway package to your path.
|
||||
This should be done if you want to use the Home Manager Sway module to install Sway.
|
||||
'';
|
||||
}
|
||||
// {
|
||||
apply =
|
||||
p:
|
||||
if p == null then
|
||||
null
|
||||
else
|
||||
wayland-lib.genFinalPackage p {
|
||||
extraSessionCommands = cfg.extraSessionCommands;
|
||||
extraOptions = cfg.extraOptions;
|
||||
withBaseWrapper = cfg.wrapperFeatures.base;
|
||||
withGtkWrapper = cfg.wrapperFeatures.gtk;
|
||||
enableXWayland = cfg.xwayland.enable;
|
||||
isNixOS = true;
|
||||
};
|
||||
};
|
||||
|
||||
wrapperFeatures = {
|
||||
base = lib.mkEnableOption ''
|
||||
the base wrapper to execute extra session commands and prepend a
|
||||
dbus-run-session to the sway command'' // { default = true; };
|
||||
base =
|
||||
lib.mkEnableOption ''
|
||||
the base wrapper to execute extra session commands and prepend a
|
||||
dbus-run-session to the sway command''
|
||||
// {
|
||||
default = true;
|
||||
};
|
||||
gtk = lib.mkEnableOption ''
|
||||
the wrapGAppsHook wrapper to execute sway with required environment
|
||||
variables for GTK applications'';
|
||||
|
@ -69,7 +84,7 @@ in
|
|||
|
||||
extraOptions = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [];
|
||||
default = [ ];
|
||||
example = [
|
||||
"--verbose"
|
||||
"--debug"
|
||||
|
@ -81,12 +96,22 @@ in
|
|||
'';
|
||||
};
|
||||
|
||||
xwayland.enable = lib.mkEnableOption "XWayland" // { default = true; };
|
||||
xwayland.enable = lib.mkEnableOption "XWayland" // {
|
||||
default = true;
|
||||
};
|
||||
|
||||
extraPackages = lib.mkOption {
|
||||
type = with lib.types; listOf package;
|
||||
# Packages used in default config
|
||||
default = with pkgs; [ brightnessctl foot grim pulseaudio swayidle swaylock wmenu ];
|
||||
default = with pkgs; [
|
||||
brightnessctl
|
||||
foot
|
||||
grim
|
||||
pulseaudio
|
||||
swayidle
|
||||
swaylock
|
||||
wmenu
|
||||
];
|
||||
defaultText = lib.literalExpression ''
|
||||
with pkgs; [ brightnessctl foot grim pulseaudio swayidle swaylock wmenu ];
|
||||
'';
|
||||
|
@ -102,69 +127,76 @@ in
|
|||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable (lib.mkMerge [
|
||||
{
|
||||
assertions = [
|
||||
{
|
||||
assertion = cfg.extraSessionCommands != "" -> cfg.wrapperFeatures.base;
|
||||
message = ''
|
||||
The extraSessionCommands for Sway will not be run if wrapperFeatures.base is disabled.
|
||||
'';
|
||||
}
|
||||
];
|
||||
config = lib.mkIf cfg.enable (
|
||||
lib.mkMerge [
|
||||
{
|
||||
assertions = [
|
||||
{
|
||||
assertion = cfg.extraSessionCommands != "" -> cfg.wrapperFeatures.base;
|
||||
message = ''
|
||||
The extraSessionCommands for Sway will not be run if wrapperFeatures.base is disabled.
|
||||
'';
|
||||
}
|
||||
];
|
||||
|
||||
environment = {
|
||||
systemPackages = lib.optional (cfg.package != null) cfg.package ++ cfg.extraPackages;
|
||||
environment = {
|
||||
systemPackages = lib.optional (cfg.package != null) cfg.package ++ cfg.extraPackages;
|
||||
|
||||
# Needed for the default wallpaper:
|
||||
pathsToLink = lib.optional (cfg.package != null) "/share/backgrounds/sway";
|
||||
# Needed for the default wallpaper:
|
||||
pathsToLink = lib.optional (cfg.package != null) "/share/backgrounds/sway";
|
||||
|
||||
etc = {
|
||||
"sway/config.d/nixos.conf".source = pkgs.writeText "nixos.conf" ''
|
||||
# Import the most important environment variables into the D-Bus and systemd
|
||||
# user environments (e.g. required for screen sharing and Pinentry prompts):
|
||||
exec dbus-update-activation-environment --systemd DISPLAY WAYLAND_DISPLAY SWAYSOCK XDG_CURRENT_DESKTOP
|
||||
# enable systemd-integration
|
||||
exec "systemctl --user import-environment {,WAYLAND_}DISPLAY SWAYSOCK; systemctl --user start sway-session.target"
|
||||
exec swaymsg -t subscribe '["shutdown"]' && systemctl --user stop sway-session.target
|
||||
'';
|
||||
} // lib.optionalAttrs (cfg.package != null) {
|
||||
"sway/config".source = lib.mkOptionDefault "${cfg.package}/etc/sway/config";
|
||||
etc =
|
||||
{
|
||||
"sway/config.d/nixos.conf".source = pkgs.writeText "nixos.conf" ''
|
||||
# Import the most important environment variables into the D-Bus and systemd
|
||||
# user environments (e.g. required for screen sharing and Pinentry prompts):
|
||||
exec dbus-update-activation-environment --systemd DISPLAY WAYLAND_DISPLAY SWAYSOCK XDG_CURRENT_DESKTOP
|
||||
# enable systemd-integration
|
||||
exec "systemctl --user import-environment {,WAYLAND_}DISPLAY SWAYSOCK; systemctl --user start sway-session.target"
|
||||
exec swaymsg -t subscribe '["shutdown"]' && systemctl --user stop sway-session.target
|
||||
'';
|
||||
}
|
||||
// lib.optionalAttrs (cfg.package != null) {
|
||||
"sway/config".source = lib.mkOptionDefault "${cfg.package}/etc/sway/config";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
systemd.user.targets.sway-session = {
|
||||
description = "sway compositor session";
|
||||
documentation = [ "man:systemd.special(7)" ];
|
||||
bindsTo = [ "graphical-session.target" ];
|
||||
wants = [ "graphical-session-pre.target" ];
|
||||
after = [ "graphical-session-pre.target" ];
|
||||
};
|
||||
systemd.user.targets.sway-session = {
|
||||
description = "sway compositor session";
|
||||
documentation = [ "man:systemd.special(7)" ];
|
||||
bindsTo = [ "graphical-session.target" ];
|
||||
wants = [ "graphical-session-pre.target" ];
|
||||
after = [ "graphical-session-pre.target" ];
|
||||
};
|
||||
|
||||
# To make a Sway session available if a display manager like SDDM is enabled:
|
||||
services.displayManager.sessionPackages = lib.optional (cfg.package != null) cfg.package;
|
||||
# To make a Sway session available if a display manager like SDDM is enabled:
|
||||
services.displayManager.sessionPackages = lib.optional (cfg.package != null) cfg.package;
|
||||
|
||||
# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1050913
|
||||
# https://github.com/emersion/xdg-desktop-portal-wlr/blob/master/contrib/wlroots-portals.conf
|
||||
# https://github.com/emersion/xdg-desktop-portal-wlr/pull/315
|
||||
xdg.portal.config.sway = {
|
||||
# Use xdg-desktop-portal-gtk for every portal interface...
|
||||
default = [ "gtk" ];
|
||||
# ... except for the ScreenCast, Screenshot and Secret
|
||||
"org.freedesktop.impl.portal.ScreenCast" = "wlr";
|
||||
"org.freedesktop.impl.portal.Screenshot" = "wlr";
|
||||
# ignore inhibit bc gtk portal always returns as success,
|
||||
# despite sway/the wlr portal not having an implementation,
|
||||
# stopping firefox from using wayland idle-inhibit
|
||||
"org.freedesktop.impl.portal.Inhibit" = "none";
|
||||
};
|
||||
}
|
||||
# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1050913
|
||||
# https://github.com/emersion/xdg-desktop-portal-wlr/blob/master/contrib/wlroots-portals.conf
|
||||
# https://github.com/emersion/xdg-desktop-portal-wlr/pull/315
|
||||
xdg.portal.config.sway = {
|
||||
# Use xdg-desktop-portal-gtk for every portal interface...
|
||||
default = [ "gtk" ];
|
||||
# ... except for the ScreenCast, Screenshot and Secret
|
||||
"org.freedesktop.impl.portal.ScreenCast" = "wlr";
|
||||
"org.freedesktop.impl.portal.Screenshot" = "wlr";
|
||||
# ignore inhibit bc gtk portal always returns as success,
|
||||
# despite sway/the wlr portal not having an implementation,
|
||||
# stopping firefox from using wayland idle-inhibit
|
||||
"org.freedesktop.impl.portal.Inhibit" = "none";
|
||||
};
|
||||
}
|
||||
|
||||
(import ./wayland-session.nix {
|
||||
inherit lib pkgs;
|
||||
enableXWayland = cfg.xwayland.enable;
|
||||
})
|
||||
]);
|
||||
(import ./wayland-session.nix {
|
||||
inherit lib pkgs;
|
||||
enableXWayland = cfg.xwayland.enable;
|
||||
})
|
||||
]
|
||||
);
|
||||
|
||||
meta.maintainers = with lib.maintainers; [ primeos colemickens ];
|
||||
meta.maintainers = with lib.maintainers; [
|
||||
primeos
|
||||
colemickens
|
||||
];
|
||||
}
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
# This module defines global configuration for the xonsh.
|
||||
|
||||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
# This module defines global configuration for the zshell.
|
||||
|
||||
{ config, lib, options, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
options,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
|
||||
|
@ -10,8 +16,9 @@ let
|
|||
opt = options.programs.zsh;
|
||||
|
||||
zshAliases = builtins.concatStringsSep "\n" (
|
||||
lib.mapAttrsToList (k: v: "alias -- ${k}=${lib.escapeShellArg v}")
|
||||
(lib.filterAttrs (k: v: v != null) cfg.shellAliases)
|
||||
lib.mapAttrsToList (k: v: "alias -- ${k}=${lib.escapeShellArg v}") (
|
||||
lib.filterAttrs (k: v: v != null) cfg.shellAliases
|
||||
)
|
||||
);
|
||||
|
||||
zshStartupNotes = ''
|
||||
|
@ -121,7 +128,10 @@ in
|
|||
"SHARE_HISTORY"
|
||||
"HIST_FCNTL_LOCK"
|
||||
];
|
||||
example = [ "EXTENDED_HISTORY" "RM_STAR_WAIT" ];
|
||||
example = [
|
||||
"EXTENDED_HISTORY"
|
||||
"RM_STAR_WAIT"
|
||||
];
|
||||
description = ''
|
||||
Configure zsh options. See
|
||||
{manpage}`zshoptions(1)`.
|
||||
|
@ -173,144 +183,141 @@ in
|
|||
|
||||
programs.zsh.shellAliases = builtins.mapAttrs (name: lib.mkDefault) cfge.shellAliases;
|
||||
|
||||
environment.etc.zshenv.text =
|
||||
''
|
||||
# /etc/zshenv: DO NOT EDIT -- this file has been generated automatically.
|
||||
# This file is read for all shells.
|
||||
environment.etc.zshenv.text = ''
|
||||
# /etc/zshenv: DO NOT EDIT -- this file has been generated automatically.
|
||||
# This file is read for all shells.
|
||||
|
||||
# Only execute this file once per shell.
|
||||
if [ -n "''${__ETC_ZSHENV_SOURCED-}" ]; then return; fi
|
||||
__ETC_ZSHENV_SOURCED=1
|
||||
# Only execute this file once per shell.
|
||||
if [ -n "''${__ETC_ZSHENV_SOURCED-}" ]; then return; fi
|
||||
__ETC_ZSHENV_SOURCED=1
|
||||
|
||||
if [ -z "''${__NIXOS_SET_ENVIRONMENT_DONE-}" ]; then
|
||||
. ${config.system.build.setEnvironment}
|
||||
fi
|
||||
if [ -z "''${__NIXOS_SET_ENVIRONMENT_DONE-}" ]; then
|
||||
. ${config.system.build.setEnvironment}
|
||||
fi
|
||||
|
||||
HELPDIR="${pkgs.zsh}/share/zsh/$ZSH_VERSION/help"
|
||||
HELPDIR="${pkgs.zsh}/share/zsh/$ZSH_VERSION/help"
|
||||
|
||||
# Tell zsh how to find installed completions.
|
||||
for p in ''${(z)NIX_PROFILES}; do
|
||||
fpath=($p/share/zsh/site-functions $p/share/zsh/$ZSH_VERSION/functions $p/share/zsh/vendor-completions $fpath)
|
||||
done
|
||||
# Tell zsh how to find installed completions.
|
||||
for p in ''${(z)NIX_PROFILES}; do
|
||||
fpath=($p/share/zsh/site-functions $p/share/zsh/$ZSH_VERSION/functions $p/share/zsh/vendor-completions $fpath)
|
||||
done
|
||||
|
||||
# Setup custom shell init stuff.
|
||||
${cfge.shellInit}
|
||||
# Setup custom shell init stuff.
|
||||
${cfge.shellInit}
|
||||
|
||||
${cfg.shellInit}
|
||||
${cfg.shellInit}
|
||||
|
||||
# Read system-wide modifications.
|
||||
if test -f /etc/zshenv.local; then
|
||||
. /etc/zshenv.local
|
||||
fi
|
||||
'';
|
||||
# Read system-wide modifications.
|
||||
if test -f /etc/zshenv.local; then
|
||||
. /etc/zshenv.local
|
||||
fi
|
||||
'';
|
||||
|
||||
environment.etc.zprofile.text =
|
||||
''
|
||||
# /etc/zprofile: DO NOT EDIT -- this file has been generated automatically.
|
||||
# This file is read for login shells.
|
||||
#
|
||||
${zshStartupNotes}
|
||||
environment.etc.zprofile.text = ''
|
||||
# /etc/zprofile: DO NOT EDIT -- this file has been generated automatically.
|
||||
# This file is read for login shells.
|
||||
#
|
||||
${zshStartupNotes}
|
||||
|
||||
# Only execute this file once per shell.
|
||||
if [ -n "''${__ETC_ZPROFILE_SOURCED-}" ]; then return; fi
|
||||
__ETC_ZPROFILE_SOURCED=1
|
||||
# Only execute this file once per shell.
|
||||
if [ -n "''${__ETC_ZPROFILE_SOURCED-}" ]; then return; fi
|
||||
__ETC_ZPROFILE_SOURCED=1
|
||||
|
||||
# Setup custom login shell init stuff.
|
||||
${cfge.loginShellInit}
|
||||
# Setup custom login shell init stuff.
|
||||
${cfge.loginShellInit}
|
||||
|
||||
${cfg.loginShellInit}
|
||||
${cfg.loginShellInit}
|
||||
|
||||
# Read system-wide modifications.
|
||||
if test -f /etc/zprofile.local; then
|
||||
. /etc/zprofile.local
|
||||
fi
|
||||
'';
|
||||
# Read system-wide modifications.
|
||||
if test -f /etc/zprofile.local; then
|
||||
. /etc/zprofile.local
|
||||
fi
|
||||
'';
|
||||
|
||||
environment.etc.zshrc.text =
|
||||
''
|
||||
# /etc/zshrc: DO NOT EDIT -- this file has been generated automatically.
|
||||
# This file is read for interactive shells.
|
||||
#
|
||||
${zshStartupNotes}
|
||||
environment.etc.zshrc.text = ''
|
||||
# /etc/zshrc: DO NOT EDIT -- this file has been generated automatically.
|
||||
# This file is read for interactive shells.
|
||||
#
|
||||
${zshStartupNotes}
|
||||
|
||||
# Only execute this file once per shell.
|
||||
if [ -n "$__ETC_ZSHRC_SOURCED" -o -n "$NOSYSZSHRC" ]; then return; fi
|
||||
__ETC_ZSHRC_SOURCED=1
|
||||
# Only execute this file once per shell.
|
||||
if [ -n "$__ETC_ZSHRC_SOURCED" -o -n "$NOSYSZSHRC" ]; then return; fi
|
||||
__ETC_ZSHRC_SOURCED=1
|
||||
|
||||
${lib.optionalString (cfg.setOptions != []) ''
|
||||
# Set zsh options.
|
||||
setopt ${builtins.concatStringsSep " " cfg.setOptions}
|
||||
''}
|
||||
${lib.optionalString (cfg.setOptions != [ ]) ''
|
||||
# Set zsh options.
|
||||
setopt ${builtins.concatStringsSep " " cfg.setOptions}
|
||||
''}
|
||||
|
||||
# Alternative method of determining short and full hostname.
|
||||
HOST=${config.networking.fqdnOrHostName}
|
||||
# Alternative method of determining short and full hostname.
|
||||
HOST=${config.networking.fqdnOrHostName}
|
||||
|
||||
# Setup command line history.
|
||||
# Don't export these, otherwise other shells (bash) will try to use same HISTFILE.
|
||||
SAVEHIST=${builtins.toString cfg.histSize}
|
||||
HISTSIZE=${builtins.toString cfg.histSize}
|
||||
HISTFILE=${cfg.histFile}
|
||||
# Setup command line history.
|
||||
# Don't export these, otherwise other shells (bash) will try to use same HISTFILE.
|
||||
SAVEHIST=${builtins.toString cfg.histSize}
|
||||
HISTSIZE=${builtins.toString cfg.histSize}
|
||||
HISTFILE=${cfg.histFile}
|
||||
|
||||
# Configure sane keyboard defaults.
|
||||
. /etc/zinputrc
|
||||
# Configure sane keyboard defaults.
|
||||
. /etc/zinputrc
|
||||
|
||||
${lib.optionalString cfg.enableGlobalCompInit ''
|
||||
# Enable autocompletion.
|
||||
autoload -U compinit && compinit
|
||||
''}
|
||||
${lib.optionalString cfg.enableGlobalCompInit ''
|
||||
# Enable autocompletion.
|
||||
autoload -U compinit && compinit
|
||||
''}
|
||||
|
||||
${lib.optionalString cfg.enableBashCompletion ''
|
||||
# Enable compatibility with bash's completion system.
|
||||
autoload -U bashcompinit && bashcompinit
|
||||
''}
|
||||
${lib.optionalString cfg.enableBashCompletion ''
|
||||
# Enable compatibility with bash's completion system.
|
||||
autoload -U bashcompinit && bashcompinit
|
||||
''}
|
||||
|
||||
# Setup custom interactive shell init stuff.
|
||||
${cfge.interactiveShellInit}
|
||||
# Setup custom interactive shell init stuff.
|
||||
${cfge.interactiveShellInit}
|
||||
|
||||
${cfg.interactiveShellInit}
|
||||
${cfg.interactiveShellInit}
|
||||
|
||||
${lib.optionalString cfg.enableLsColors ''
|
||||
# Extra colors for directory listings.
|
||||
eval "$(${pkgs.coreutils}/bin/dircolors -b)"
|
||||
''}
|
||||
${lib.optionalString cfg.enableLsColors ''
|
||||
# Extra colors for directory listings.
|
||||
eval "$(${pkgs.coreutils}/bin/dircolors -b)"
|
||||
''}
|
||||
|
||||
# Setup aliases.
|
||||
${zshAliases}
|
||||
# Setup aliases.
|
||||
${zshAliases}
|
||||
|
||||
# Setup prompt.
|
||||
${cfg.promptInit}
|
||||
# Setup prompt.
|
||||
${cfg.promptInit}
|
||||
|
||||
# Disable some features to support TRAMP.
|
||||
if [ "$TERM" = dumb ]; then
|
||||
unsetopt zle prompt_cr prompt_subst
|
||||
unset RPS1 RPROMPT
|
||||
PS1='$ '
|
||||
PROMPT='$ '
|
||||
fi
|
||||
# Disable some features to support TRAMP.
|
||||
if [ "$TERM" = dumb ]; then
|
||||
unsetopt zle prompt_cr prompt_subst
|
||||
unset RPS1 RPROMPT
|
||||
PS1='$ '
|
||||
PROMPT='$ '
|
||||
fi
|
||||
|
||||
# Read system-wide modifications.
|
||||
if test -f /etc/zshrc.local; then
|
||||
. /etc/zshrc.local
|
||||
fi
|
||||
'';
|
||||
# Read system-wide modifications.
|
||||
if test -f /etc/zshrc.local; then
|
||||
. /etc/zshrc.local
|
||||
fi
|
||||
'';
|
||||
|
||||
# Bug in nix flakes:
|
||||
# If we use `.source` here the path is garbage collected also we point to it with a symlink
|
||||
# see https://github.com/NixOS/nixpkgs/issues/132732
|
||||
environment.etc.zinputrc.text = builtins.readFile ./zinputrc;
|
||||
|
||||
environment.systemPackages = [ pkgs.zsh ]
|
||||
++ lib.optional cfg.enableCompletion pkgs.nix-zsh-completions;
|
||||
environment.systemPackages = [
|
||||
pkgs.zsh
|
||||
] ++ lib.optional cfg.enableCompletion pkgs.nix-zsh-completions;
|
||||
|
||||
environment.pathsToLink = lib.optional cfg.enableCompletion "/share/zsh";
|
||||
|
||||
#users.defaultUserShell = lib.mkDefault "/run/current-system/sw/bin/zsh";
|
||||
|
||||
environment.shells =
|
||||
[
|
||||
"/run/current-system/sw/bin/zsh"
|
||||
"${pkgs.zsh}/bin/zsh"
|
||||
];
|
||||
environment.shells = [
|
||||
"/run/current-system/sw/bin/zsh"
|
||||
"${pkgs.zsh}/bin/zsh"
|
||||
];
|
||||
|
||||
};
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,21 +1,31 @@
|
|||
lib:
|
||||
|
||||
{ cert, groups, services }:
|
||||
{
|
||||
cert,
|
||||
groups,
|
||||
services,
|
||||
}:
|
||||
let
|
||||
catSep = builtins.concatStringsSep;
|
||||
|
||||
svcGroups = svc:
|
||||
svcGroups =
|
||||
svc:
|
||||
(lib.optional (svc.serviceConfig ? Group) svc.serviceConfig.Group)
|
||||
++ (svc.serviceConfig.SupplementaryGroups or [ ]);
|
||||
in
|
||||
{
|
||||
assertion = builtins.all (svc:
|
||||
assertion = builtins.all (
|
||||
svc:
|
||||
svc.serviceConfig.User or "root" == "root"
|
||||
|| builtins.elem svc.serviceConfig.User groups.${cert.group}.members
|
||||
|| builtins.elem cert.group (svcGroups svc)
|
||||
) services;
|
||||
|
||||
message = "Certificate ${cert.domain} (group=${cert.group}) must be readable by service(s) ${
|
||||
catSep ", " (map (svc: "${svc.name} (user=${svc.serviceConfig.User} groups=${catSep " " (svcGroups svc)})") services)
|
||||
catSep ", " (
|
||||
map (
|
||||
svc: "${svc.name} (user=${svc.serviceConfig.User} groups=${catSep " " (svcGroups svc)})"
|
||||
) services
|
||||
)
|
||||
}";
|
||||
}
|
||||
|
|
|
@ -1,23 +1,45 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
inherit (builtins) attrNames head map match readFile;
|
||||
inherit (builtins)
|
||||
attrNames
|
||||
head
|
||||
map
|
||||
match
|
||||
readFile
|
||||
;
|
||||
inherit (lib) types;
|
||||
inherit (config.environment) etc;
|
||||
cfg = config.security.apparmor;
|
||||
mkDisableOption = name: mkEnableOption name // {
|
||||
default = true;
|
||||
example = false;
|
||||
};
|
||||
mkDisableOption =
|
||||
name:
|
||||
mkEnableOption name
|
||||
// {
|
||||
default = true;
|
||||
example = false;
|
||||
};
|
||||
enabledPolicies = filterAttrs (n: p: p.enable) cfg.policies;
|
||||
in
|
||||
|
||||
{
|
||||
imports = [
|
||||
(mkRemovedOptionModule [ "security" "apparmor" "confineSUIDApplications" ] "Please use the new options: `security.apparmor.policies.<policy>.enable'.")
|
||||
(mkRemovedOptionModule [ "security" "apparmor" "profiles" ] "Please use the new option: `security.apparmor.policies'.")
|
||||
(mkRemovedOptionModule [
|
||||
"security"
|
||||
"apparmor"
|
||||
"confineSUIDApplications"
|
||||
] "Please use the new options: `security.apparmor.policies.<policy>.enable'.")
|
||||
(mkRemovedOptionModule [
|
||||
"security"
|
||||
"apparmor"
|
||||
"profiles"
|
||||
] "Please use the new option: `security.apparmor.policies'.")
|
||||
apparmor/includes.nix
|
||||
apparmor/profiles.nix
|
||||
];
|
||||
|
@ -45,22 +67,27 @@ in
|
|||
description = ''
|
||||
AppArmor policies.
|
||||
'';
|
||||
type = types.attrsOf (types.submodule ({ name, config, ... }: {
|
||||
options = {
|
||||
enable = mkDisableOption "loading of the profile into the kernel";
|
||||
enforce = mkDisableOption "enforcing of the policy or only complain in the logs";
|
||||
profile = mkOption {
|
||||
description = "The policy of the profile.";
|
||||
type = types.lines;
|
||||
apply = pkgs.writeText name;
|
||||
};
|
||||
};
|
||||
}));
|
||||
default = {};
|
||||
type = types.attrsOf (
|
||||
types.submodule (
|
||||
{ name, config, ... }:
|
||||
{
|
||||
options = {
|
||||
enable = mkDisableOption "loading of the profile into the kernel";
|
||||
enforce = mkDisableOption "enforcing of the policy or only complain in the logs";
|
||||
profile = mkOption {
|
||||
description = "The policy of the profile.";
|
||||
type = types.lines;
|
||||
apply = pkgs.writeText name;
|
||||
};
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
default = { };
|
||||
};
|
||||
includes = mkOption {
|
||||
type = types.attrsOf types.lines;
|
||||
default = {};
|
||||
default = { };
|
||||
description = ''
|
||||
List of paths to be added to AppArmor's searched paths
|
||||
when resolving `include` directives.
|
||||
|
@ -69,7 +96,7 @@ in
|
|||
};
|
||||
packages = mkOption {
|
||||
type = types.listOf types.package;
|
||||
default = [];
|
||||
default = [ ];
|
||||
description = "List of packages to be added to AppArmor's include path";
|
||||
};
|
||||
enableCache = mkEnableOption ''
|
||||
|
@ -93,13 +120,12 @@ in
|
|||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
assertions = map (policy:
|
||||
{ assertion = match ".*/.*" policy == null;
|
||||
message = "`security.apparmor.policies.\"${policy}\"' must not contain a slash.";
|
||||
# Because, for instance, aa-remove-unknown uses profiles_names_list() in rc.apparmor.functions
|
||||
# which does not recurse into sub-directories.
|
||||
}
|
||||
) (attrNames cfg.policies);
|
||||
assertions = map (policy: {
|
||||
assertion = match ".*/.*" policy == null;
|
||||
message = "`security.apparmor.policies.\"${policy}\"' must not contain a slash.";
|
||||
# Because, for instance, aa-remove-unknown uses profiles_names_list() in rc.apparmor.functions
|
||||
# which does not recurse into sub-directories.
|
||||
}) (attrNames cfg.policies);
|
||||
|
||||
environment.systemPackages = [
|
||||
pkgs.apparmor-utils
|
||||
|
@ -108,67 +134,81 @@ in
|
|||
environment.etc."apparmor.d".source = pkgs.linkFarm "apparmor.d" (
|
||||
# It's important to put only enabledPolicies here and not all cfg.policies
|
||||
# because aa-remove-unknown reads profiles from all /etc/apparmor.d/*
|
||||
mapAttrsToList (name: p: { inherit name; path = p.profile; }) enabledPolicies ++
|
||||
mapAttrsToList (name: path: { inherit name path; }) cfg.includes
|
||||
mapAttrsToList (name: p: {
|
||||
inherit name;
|
||||
path = p.profile;
|
||||
}) enabledPolicies
|
||||
++ mapAttrsToList (name: path: { inherit name path; }) cfg.includes
|
||||
);
|
||||
environment.etc."apparmor/parser.conf".text = ''
|
||||
environment.etc."apparmor/parser.conf".text =
|
||||
''
|
||||
${if cfg.enableCache then "write-cache" else "skip-cache"}
|
||||
cache-loc /var/cache/apparmor
|
||||
Include /etc/apparmor.d
|
||||
'' +
|
||||
concatMapStrings (p: "Include ${p}/etc/apparmor.d\n") cfg.packages;
|
||||
''
|
||||
+ concatMapStrings (p: "Include ${p}/etc/apparmor.d\n") cfg.packages;
|
||||
# For aa-logprof
|
||||
environment.etc."apparmor/apparmor.conf".text = ''
|
||||
'';
|
||||
environment.etc."apparmor/apparmor.conf".text = '''';
|
||||
# For aa-logprof
|
||||
environment.etc."apparmor/severity.db".source = pkgs.apparmor-utils + "/etc/apparmor/severity.db";
|
||||
environment.etc."apparmor/logprof.conf".source = pkgs.runCommand "logprof.conf" {
|
||||
header = ''
|
||||
[settings]
|
||||
# /etc/apparmor.d/ is read-only on NixOS
|
||||
profiledir = /var/cache/apparmor/logprof
|
||||
inactive_profiledir = /etc/apparmor.d/disable
|
||||
# Use: journalctl -b --since today --grep audit: | aa-logprof
|
||||
logfiles = /dev/stdin
|
||||
environment.etc."apparmor/logprof.conf".source =
|
||||
pkgs.runCommand "logprof.conf"
|
||||
{
|
||||
header = ''
|
||||
[settings]
|
||||
# /etc/apparmor.d/ is read-only on NixOS
|
||||
profiledir = /var/cache/apparmor/logprof
|
||||
inactive_profiledir = /etc/apparmor.d/disable
|
||||
# Use: journalctl -b --since today --grep audit: | aa-logprof
|
||||
logfiles = /dev/stdin
|
||||
|
||||
parser = ${pkgs.apparmor-parser}/bin/apparmor_parser
|
||||
ldd = ${pkgs.glibc.bin}/bin/ldd
|
||||
logger = ${pkgs.util-linux}/bin/logger
|
||||
parser = ${pkgs.apparmor-parser}/bin/apparmor_parser
|
||||
ldd = ${pkgs.glibc.bin}/bin/ldd
|
||||
logger = ${pkgs.util-linux}/bin/logger
|
||||
|
||||
# customize how file ownership permissions are presented
|
||||
# 0 - off
|
||||
# 1 - default of what ever mode the log reported
|
||||
# 2 - force the new permissions to be user
|
||||
# 3 - force all perms on the rule to be user
|
||||
default_owner_prompt = 1
|
||||
# customize how file ownership permissions are presented
|
||||
# 0 - off
|
||||
# 1 - default of what ever mode the log reported
|
||||
# 2 - force the new permissions to be user
|
||||
# 3 - force all perms on the rule to be user
|
||||
default_owner_prompt = 1
|
||||
|
||||
custom_includes = /etc/apparmor.d ${concatMapStringsSep " " (p: "${p}/etc/apparmor.d") cfg.packages}
|
||||
custom_includes = /etc/apparmor.d ${
|
||||
concatMapStringsSep " " (p: "${p}/etc/apparmor.d") cfg.packages
|
||||
}
|
||||
|
||||
[qualifiers]
|
||||
${pkgs.runtimeShell} = icnu
|
||||
${pkgs.bashInteractive}/bin/sh = icnu
|
||||
${pkgs.bashInteractive}/bin/bash = icnu
|
||||
${config.users.defaultUserShell} = icnu
|
||||
'';
|
||||
footer = "${pkgs.apparmor-utils}/etc/apparmor/logprof.conf";
|
||||
passAsFile = [ "header" ];
|
||||
} ''
|
||||
cp $headerPath $out
|
||||
sed '1,/\[qualifiers\]/d' $footer >> $out
|
||||
'';
|
||||
[qualifiers]
|
||||
${pkgs.runtimeShell} = icnu
|
||||
${pkgs.bashInteractive}/bin/sh = icnu
|
||||
${pkgs.bashInteractive}/bin/bash = icnu
|
||||
${config.users.defaultUserShell} = icnu
|
||||
'';
|
||||
footer = "${pkgs.apparmor-utils}/etc/apparmor/logprof.conf";
|
||||
passAsFile = [ "header" ];
|
||||
}
|
||||
''
|
||||
cp $headerPath $out
|
||||
sed '1,/\[qualifiers\]/d' $footer >> $out
|
||||
'';
|
||||
|
||||
boot.kernelParams = [ "apparmor=1" "security=apparmor" ];
|
||||
boot.kernelParams = [
|
||||
"apparmor=1"
|
||||
"security=apparmor"
|
||||
];
|
||||
|
||||
systemd.services.apparmor = {
|
||||
after = [
|
||||
"local-fs.target"
|
||||
"systemd-journald-audit.socket"
|
||||
];
|
||||
before = [ "sysinit.target" "shutdown.target" ];
|
||||
before = [
|
||||
"sysinit.target"
|
||||
"shutdown.target"
|
||||
];
|
||||
conflicts = [ "shutdown.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
unitConfig = {
|
||||
Description="Load AppArmor policies";
|
||||
Description = "Load AppArmor policies";
|
||||
DefaultDependencies = "no";
|
||||
ConditionSecurity = "apparmor";
|
||||
};
|
||||
|
@ -179,37 +219,48 @@ in
|
|||
etc."apparmor/parser.conf".source
|
||||
etc."apparmor.d".source
|
||||
];
|
||||
serviceConfig = let
|
||||
killUnconfinedConfinables = pkgs.writeShellScript "apparmor-kill" ''
|
||||
set -eu
|
||||
${pkgs.apparmor-bin-utils}/bin/aa-status --json |
|
||||
${pkgs.jq}/bin/jq --raw-output '.processes | .[] | .[] | select (.status == "unconfined") | .pid' |
|
||||
xargs --verbose --no-run-if-empty --delimiter='\n' \
|
||||
kill
|
||||
'';
|
||||
commonOpts = p: "--verbose --show-cache ${optionalString (!p.enforce) "--complain "}${p.profile}";
|
||||
in {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = "yes";
|
||||
ExecStartPre = "${pkgs.apparmor-utils}/bin/aa-teardown";
|
||||
ExecStart = mapAttrsToList (n: p: "${pkgs.apparmor-parser}/bin/apparmor_parser --add ${commonOpts p}") enabledPolicies;
|
||||
ExecStartPost = optional cfg.killUnconfinedConfinables killUnconfinedConfinables;
|
||||
ExecReload =
|
||||
# Add or replace into the kernel profiles in enabledPolicies
|
||||
# (because AppArmor can do that without stopping the processes already confined).
|
||||
mapAttrsToList (n: p: "${pkgs.apparmor-parser}/bin/apparmor_parser --replace ${commonOpts p}") enabledPolicies ++
|
||||
# Remove from the kernel any profile whose name is not
|
||||
# one of the names within the content of the profiles in enabledPolicies
|
||||
# (indirectly read from /etc/apparmor.d/*, without recursing into sub-directory).
|
||||
# Note that this does not remove profiles dynamically generated by libvirt.
|
||||
[ "${pkgs.apparmor-utils}/bin/aa-remove-unknown" ] ++
|
||||
# Optionally kill the processes which are unconfined but now have a profile loaded
|
||||
# (because AppArmor can only start to confine new processes).
|
||||
optional cfg.killUnconfinedConfinables killUnconfinedConfinables;
|
||||
ExecStop = "${pkgs.apparmor-utils}/bin/aa-teardown";
|
||||
CacheDirectory = [ "apparmor" "apparmor/logprof" ];
|
||||
CacheDirectoryMode = "0700";
|
||||
};
|
||||
serviceConfig =
|
||||
let
|
||||
killUnconfinedConfinables = pkgs.writeShellScript "apparmor-kill" ''
|
||||
set -eu
|
||||
${pkgs.apparmor-bin-utils}/bin/aa-status --json |
|
||||
${pkgs.jq}/bin/jq --raw-output '.processes | .[] | .[] | select (.status == "unconfined") | .pid' |
|
||||
xargs --verbose --no-run-if-empty --delimiter='\n' \
|
||||
kill
|
||||
'';
|
||||
commonOpts = p: "--verbose --show-cache ${optionalString (!p.enforce) "--complain "}${p.profile}";
|
||||
in
|
||||
{
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = "yes";
|
||||
ExecStartPre = "${pkgs.apparmor-utils}/bin/aa-teardown";
|
||||
ExecStart = mapAttrsToList (
|
||||
n: p: "${pkgs.apparmor-parser}/bin/apparmor_parser --add ${commonOpts p}"
|
||||
) enabledPolicies;
|
||||
ExecStartPost = optional cfg.killUnconfinedConfinables killUnconfinedConfinables;
|
||||
ExecReload =
|
||||
# Add or replace into the kernel profiles in enabledPolicies
|
||||
# (because AppArmor can do that without stopping the processes already confined).
|
||||
mapAttrsToList (
|
||||
n: p: "${pkgs.apparmor-parser}/bin/apparmor_parser --replace ${commonOpts p}"
|
||||
) enabledPolicies
|
||||
++
|
||||
# Remove from the kernel any profile whose name is not
|
||||
# one of the names within the content of the profiles in enabledPolicies
|
||||
# (indirectly read from /etc/apparmor.d/*, without recursing into sub-directory).
|
||||
# Note that this does not remove profiles dynamically generated by libvirt.
|
||||
[ "${pkgs.apparmor-utils}/bin/aa-remove-unknown" ]
|
||||
++
|
||||
# Optionally kill the processes which are unconfined but now have a profile loaded
|
||||
# (because AppArmor can only start to confine new processes).
|
||||
optional cfg.killUnconfinedConfinables killUnconfinedConfinables;
|
||||
ExecStop = "${pkgs.apparmor-utils}/bin/aa-teardown";
|
||||
CacheDirectory = [
|
||||
"apparmor"
|
||||
"apparmor/logprof"
|
||||
];
|
||||
CacheDirectoryMode = "0700";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -4,12 +4,10 @@
|
|||
pkgs,
|
||||
...
|
||||
}:
|
||||
with lib; let
|
||||
with lib;
|
||||
let
|
||||
cfg = config.security.ipa;
|
||||
pyBool = x:
|
||||
if x
|
||||
then "True"
|
||||
else "False";
|
||||
pyBool = x: if x then "True" else "False";
|
||||
|
||||
ldapConf = pkgs.writeText "ldap.conf" ''
|
||||
# Turning this off breaks GSSAPI used with krb5 when rdns = false
|
||||
|
@ -21,14 +19,16 @@ with lib; let
|
|||
'';
|
||||
nssDb =
|
||||
pkgs.runCommand "ipa-nssdb"
|
||||
{
|
||||
nativeBuildInputs = [pkgs.nss.tools];
|
||||
} ''
|
||||
mkdir -p $out
|
||||
certutil -d $out -N --empty-password
|
||||
certutil -d $out -A --empty-password -n "${cfg.realm} IPA CA" -t CT,C,C -i ${cfg.certificate}
|
||||
'';
|
||||
in {
|
||||
{
|
||||
nativeBuildInputs = [ pkgs.nss.tools ];
|
||||
}
|
||||
''
|
||||
mkdir -p $out
|
||||
certutil -d $out -N --empty-password
|
||||
certutil -d $out -A --empty-password -n "${cfg.realm} IPA CA" -t CT,C,C -i ${cfg.certificate}
|
||||
'';
|
||||
in
|
||||
{
|
||||
options = {
|
||||
security.ipa = {
|
||||
enable = mkEnableOption "FreeIPA domain integration";
|
||||
|
@ -88,8 +88,11 @@ in {
|
|||
ipaHostname = mkOption {
|
||||
type = types.str;
|
||||
example = "myworkstation.example.com";
|
||||
default = if config.networking.domain != null then config.networking.fqdn
|
||||
else "${config.networking.hostName}.${cfg.domain}";
|
||||
default =
|
||||
if config.networking.domain != null then
|
||||
config.networking.fqdn
|
||||
else
|
||||
"${config.networking.hostName}.${cfg.domain}";
|
||||
defaultText = literalExpression ''
|
||||
if config.networking.domain != null then config.networking.fqdn
|
||||
else "''${networking.hostName}.''${security.ipa.domain}"
|
||||
|
@ -99,7 +102,7 @@ in {
|
|||
|
||||
ifpAllowedUids = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = ["root"];
|
||||
default = [ "root" ];
|
||||
description = "A list of users allowed to access the ifp dbus interface.";
|
||||
};
|
||||
|
||||
|
@ -138,7 +141,10 @@ in {
|
|||
}
|
||||
];
|
||||
|
||||
environment.systemPackages = with pkgs; [krb5Full freeipa];
|
||||
environment.systemPackages = with pkgs; [
|
||||
krb5Full
|
||||
freeipa
|
||||
];
|
||||
|
||||
environment.etc = {
|
||||
"ipa/default.conf".text = ''
|
||||
|
@ -195,7 +201,10 @@ in {
|
|||
|
||||
systemd.services."ipa-activation" = {
|
||||
wantedBy = [ "sysinit.target" ];
|
||||
before = [ "sysinit.target" "shutdown.target" ];
|
||||
before = [
|
||||
"sysinit.target"
|
||||
"shutdown.target"
|
||||
];
|
||||
conflicts = [ "shutdown.target" ];
|
||||
unitConfig.DefaultDependencies = false;
|
||||
serviceConfig.Type = "oneshot";
|
||||
|
@ -234,8 +243,7 @@ in {
|
|||
|
||||
cache_credentials = ${pyBool cfg.cacheCredentials}
|
||||
krb5_store_password_if_offline = ${pyBool cfg.offlinePasswords}
|
||||
${optionalString ((toLower cfg.domain) != (toLower cfg.realm))
|
||||
"krb5_realm = ${cfg.realm}"}
|
||||
${optionalString ((toLower cfg.domain) != (toLower cfg.realm)) "krb5_realm = ${cfg.realm}"}
|
||||
|
||||
dyndns_update = ${pyBool cfg.dyndns.enable}
|
||||
dyndns_iface = ${cfg.dyndns.interface}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,7 +1,12 @@
|
|||
# A module for ‘rtkit’, a DBus system service that hands out realtime
|
||||
# scheduling priority to processes that ask for it.
|
||||
|
||||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
with lib;
|
||||
|
||||
|
@ -22,7 +27,6 @@ with lib;
|
|||
|
||||
};
|
||||
|
||||
|
||||
config = mkIf config.security.rtkit.enable {
|
||||
|
||||
security.polkit.enable = true;
|
||||
|
@ -34,13 +38,12 @@ with lib;
|
|||
|
||||
services.dbus.packages = [ pkgs.rtkit ];
|
||||
|
||||
users.users.rtkit =
|
||||
{
|
||||
isSystemUser = true;
|
||||
group = "rtkit";
|
||||
description = "RealtimeKit daemon";
|
||||
};
|
||||
users.groups.rtkit = {};
|
||||
users.users.rtkit = {
|
||||
isSystemUser = true;
|
||||
group = "rtkit";
|
||||
description = "RealtimeKit daemon";
|
||||
};
|
||||
users.groups.rtkit = { };
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
|
||||
inherit (config.security) wrapperDir wrappers;
|
||||
|
@ -9,26 +14,30 @@ let
|
|||
# musl is security-focused and generally more minimal, so it's a better choice here.
|
||||
# The dynamic linker is still a fairly complex piece of code, and the wrappers are
|
||||
# quite small, so linking it statically is more appropriate.
|
||||
securityWrapper = sourceProg : pkgs.pkgsStatic.callPackage ./wrapper.nix {
|
||||
inherit sourceProg;
|
||||
securityWrapper =
|
||||
sourceProg:
|
||||
pkgs.pkgsStatic.callPackage ./wrapper.nix {
|
||||
inherit sourceProg;
|
||||
|
||||
# glibc definitions of insecure environment variables
|
||||
#
|
||||
# We extract the single header file we need into its own derivation,
|
||||
# so that we don't have to pull full glibc sources to build wrappers.
|
||||
#
|
||||
# They're taken from pkgs.glibc so that we don't have to keep as close
|
||||
# an eye on glibc changes. Not every relevant variable is in this header,
|
||||
# so we maintain a slightly stricter list in wrapper.c itself as well.
|
||||
unsecvars = lib.overrideDerivation (pkgs.srcOnly pkgs.glibc)
|
||||
({ name, ... }: {
|
||||
name = "${name}-unsecvars";
|
||||
installPhase = ''
|
||||
mkdir $out
|
||||
cp sysdeps/generic/unsecvars.h $out
|
||||
'';
|
||||
});
|
||||
};
|
||||
# glibc definitions of insecure environment variables
|
||||
#
|
||||
# We extract the single header file we need into its own derivation,
|
||||
# so that we don't have to pull full glibc sources to build wrappers.
|
||||
#
|
||||
# They're taken from pkgs.glibc so that we don't have to keep as close
|
||||
# an eye on glibc changes. Not every relevant variable is in this header,
|
||||
# so we maintain a slightly stricter list in wrapper.c itself as well.
|
||||
unsecvars = lib.overrideDerivation (pkgs.srcOnly pkgs.glibc) (
|
||||
{ name, ... }:
|
||||
{
|
||||
name = "${name}-unsecvars";
|
||||
installPhase = ''
|
||||
mkdir $out
|
||||
cp sysdeps/generic/unsecvars.h $out
|
||||
'';
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
fileModeType =
|
||||
let
|
||||
|
@ -37,40 +46,41 @@ let
|
|||
numeric = "[-+=]?[0-7]{0,4}";
|
||||
mode = "((${symbolic})(,${symbolic})*)|(${numeric})";
|
||||
in
|
||||
lib.types.strMatching mode
|
||||
// { description = "file mode string"; };
|
||||
lib.types.strMatching mode // { description = "file mode string"; };
|
||||
|
||||
wrapperType = lib.types.submodule ({ name, config, ... }: {
|
||||
options.source = lib.mkOption
|
||||
{ type = lib.types.path;
|
||||
wrapperType = lib.types.submodule (
|
||||
{ name, config, ... }:
|
||||
{
|
||||
options.source = lib.mkOption {
|
||||
type = lib.types.path;
|
||||
description = "The absolute path to the program to be wrapped.";
|
||||
};
|
||||
options.program = lib.mkOption
|
||||
{ type = with lib.types; nullOr str;
|
||||
options.program = lib.mkOption {
|
||||
type = with lib.types; nullOr str;
|
||||
default = name;
|
||||
description = ''
|
||||
The name of the wrapper program. Defaults to the attribute name.
|
||||
'';
|
||||
};
|
||||
options.owner = lib.mkOption
|
||||
{ type = lib.types.str;
|
||||
options.owner = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "The owner of the wrapper program.";
|
||||
};
|
||||
options.group = lib.mkOption
|
||||
{ type = lib.types.str;
|
||||
options.group = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "The group of the wrapper program.";
|
||||
};
|
||||
options.permissions = lib.mkOption
|
||||
{ type = fileModeType;
|
||||
default = "u+rx,g+x,o+x";
|
||||
options.permissions = lib.mkOption {
|
||||
type = fileModeType;
|
||||
default = "u+rx,g+x,o+x";
|
||||
example = "a+rx";
|
||||
description = ''
|
||||
The permissions of the wrapper program. The format is that of a
|
||||
symbolic or numeric file mode understood by {command}`chmod`.
|
||||
'';
|
||||
};
|
||||
options.capabilities = lib.mkOption
|
||||
{ type = lib.types.commas;
|
||||
options.capabilities = lib.mkOption {
|
||||
type = lib.types.commas;
|
||||
default = "";
|
||||
description = ''
|
||||
A comma-separated list of capability clauses to be given to the
|
||||
|
@ -89,27 +99,29 @@ let
|
|||
:::
|
||||
'';
|
||||
};
|
||||
options.setuid = lib.mkOption
|
||||
{ type = lib.types.bool;
|
||||
options.setuid = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
description = "Whether to add the setuid bit the wrapper program.";
|
||||
};
|
||||
options.setgid = lib.mkOption
|
||||
{ type = lib.types.bool;
|
||||
options.setgid = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
description = "Whether to add the setgid bit the wrapper program.";
|
||||
};
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
###### Activation script for the setcap wrappers
|
||||
mkSetcapProgram =
|
||||
{ program
|
||||
, capabilities
|
||||
, source
|
||||
, owner
|
||||
, group
|
||||
, permissions
|
||||
, ...
|
||||
{
|
||||
program,
|
||||
capabilities,
|
||||
source,
|
||||
owner,
|
||||
group,
|
||||
permissions,
|
||||
...
|
||||
}:
|
||||
''
|
||||
cp ${securityWrapper source}/bin/security-wrapper "$wrapperDir/${program}"
|
||||
|
@ -129,14 +141,15 @@ let
|
|||
|
||||
###### Activation script for the setuid wrappers
|
||||
mkSetuidProgram =
|
||||
{ program
|
||||
, source
|
||||
, owner
|
||||
, group
|
||||
, setuid
|
||||
, setgid
|
||||
, permissions
|
||||
, ...
|
||||
{
|
||||
program,
|
||||
source,
|
||||
owner,
|
||||
group,
|
||||
setuid,
|
||||
setgid,
|
||||
permissions,
|
||||
...
|
||||
}:
|
||||
''
|
||||
cp ${securityWrapper source}/bin/security-wrapper "$wrapperDir/${program}"
|
||||
|
@ -148,13 +161,9 @@ let
|
|||
chmod "u${if setuid then "+" else "-"}s,g${if setgid then "+" else "-"}s,${permissions}" "$wrapperDir/${program}"
|
||||
'';
|
||||
|
||||
mkWrappedPrograms =
|
||||
builtins.map
|
||||
(opts:
|
||||
if opts.capabilities != ""
|
||||
then mkSetcapProgram opts
|
||||
else mkSetuidProgram opts
|
||||
) (lib.attrValues wrappers);
|
||||
mkWrappedPrograms = builtins.map (
|
||||
opts: if opts.capabilities != "" then mkSetcapProgram opts else mkSetuidProgram opts
|
||||
) (lib.attrValues wrappers);
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
|
@ -171,35 +180,34 @@ in
|
|||
|
||||
security.wrappers = lib.mkOption {
|
||||
type = lib.types.attrsOf wrapperType;
|
||||
default = {};
|
||||
example = lib.literalExpression
|
||||
''
|
||||
{
|
||||
# a setuid root program
|
||||
doas =
|
||||
{ setuid = true;
|
||||
owner = "root";
|
||||
group = "root";
|
||||
source = "''${pkgs.doas}/bin/doas";
|
||||
};
|
||||
default = { };
|
||||
example = lib.literalExpression ''
|
||||
{
|
||||
# a setuid root program
|
||||
doas =
|
||||
{ setuid = true;
|
||||
owner = "root";
|
||||
group = "root";
|
||||
source = "''${pkgs.doas}/bin/doas";
|
||||
};
|
||||
|
||||
# a setgid program
|
||||
locate =
|
||||
{ setgid = true;
|
||||
owner = "root";
|
||||
group = "mlocate";
|
||||
source = "''${pkgs.locate}/bin/locate";
|
||||
};
|
||||
# a setgid program
|
||||
locate =
|
||||
{ setgid = true;
|
||||
owner = "root";
|
||||
group = "mlocate";
|
||||
source = "''${pkgs.locate}/bin/locate";
|
||||
};
|
||||
|
||||
# a program with the CAP_NET_RAW capability
|
||||
ping =
|
||||
{ owner = "root";
|
||||
group = "root";
|
||||
capabilities = "cap_net_raw+ep";
|
||||
source = "''${pkgs.iputils.out}/bin/ping";
|
||||
};
|
||||
}
|
||||
'';
|
||||
# a program with the CAP_NET_RAW capability
|
||||
ping =
|
||||
{ owner = "root";
|
||||
group = "root";
|
||||
capabilities = "cap_net_raw+ep";
|
||||
source = "''${pkgs.iputils.out}/bin/ping";
|
||||
};
|
||||
}
|
||||
'';
|
||||
description = ''
|
||||
This option effectively allows adding setuid/setgid bits, capabilities,
|
||||
changing file ownership and permissions of a program without directly
|
||||
|
@ -220,9 +228,9 @@ in
|
|||
};
|
||||
|
||||
security.wrapperDir = lib.mkOption {
|
||||
type = lib.types.path;
|
||||
default = "/run/wrappers/bin";
|
||||
internal = true;
|
||||
type = lib.types.path;
|
||||
default = "/run/wrappers/bin";
|
||||
internal = true;
|
||||
description = ''
|
||||
This option defines the path to the wrapper programs. It
|
||||
should not be overridden.
|
||||
|
@ -233,29 +241,28 @@ in
|
|||
###### implementation
|
||||
config = lib.mkIf config.security.enableWrappers {
|
||||
|
||||
assertions = lib.mapAttrsToList
|
||||
(name: opts:
|
||||
{ assertion = opts.setuid || opts.setgid -> opts.capabilities == "";
|
||||
message = ''
|
||||
The security.wrappers.${name} wrapper is not valid:
|
||||
setuid/setgid and capabilities are mutually exclusive.
|
||||
'';
|
||||
}
|
||||
) wrappers;
|
||||
assertions = lib.mapAttrsToList (name: opts: {
|
||||
assertion = opts.setuid || opts.setgid -> opts.capabilities == "";
|
||||
message = ''
|
||||
The security.wrappers.${name} wrapper is not valid:
|
||||
setuid/setgid and capabilities are mutually exclusive.
|
||||
'';
|
||||
}) wrappers;
|
||||
|
||||
security.wrappers =
|
||||
let
|
||||
mkSetuidRoot = source:
|
||||
{ setuid = true;
|
||||
owner = "root";
|
||||
group = "root";
|
||||
inherit source;
|
||||
};
|
||||
mkSetuidRoot = source: {
|
||||
setuid = true;
|
||||
owner = "root";
|
||||
group = "root";
|
||||
inherit source;
|
||||
};
|
||||
in
|
||||
{ # These are mount related wrappers that require the +s permission.
|
||||
fusermount = mkSetuidRoot "${lib.getBin pkgs.fuse}/bin/fusermount";
|
||||
{
|
||||
# These are mount related wrappers that require the +s permission.
|
||||
fusermount = mkSetuidRoot "${lib.getBin pkgs.fuse}/bin/fusermount";
|
||||
fusermount3 = mkSetuidRoot "${lib.getBin pkgs.fuse3}/bin/fusermount3";
|
||||
mount = mkSetuidRoot "${lib.getBin pkgs.util-linux}/bin/mount";
|
||||
mount = mkSetuidRoot "${lib.getBin pkgs.util-linux}/bin/mount";
|
||||
umount = mkSetuidRoot "${lib.getBin pkgs.util-linux}/bin/umount";
|
||||
};
|
||||
|
||||
|
@ -266,33 +273,45 @@ in
|
|||
export PATH="${wrapperDir}:$PATH"
|
||||
'';
|
||||
|
||||
security.apparmor.includes = lib.mapAttrs' (wrapName: wrap: lib.nameValuePair
|
||||
"nixos/security.wrappers/${wrapName}" ''
|
||||
include "${pkgs.apparmorRulesFromClosure { name="security.wrappers.${wrapName}"; } [
|
||||
(securityWrapper wrap.source)
|
||||
]}"
|
||||
mrpx ${wrap.source},
|
||||
'') wrappers;
|
||||
security.apparmor.includes = lib.mapAttrs' (
|
||||
wrapName: wrap:
|
||||
lib.nameValuePair "nixos/security.wrappers/${wrapName}" ''
|
||||
include "${
|
||||
pkgs.apparmorRulesFromClosure { name = "security.wrappers.${wrapName}"; } [
|
||||
(securityWrapper wrap.source)
|
||||
]
|
||||
}"
|
||||
mrpx ${wrap.source},
|
||||
''
|
||||
) wrappers;
|
||||
|
||||
systemd.mounts = [{
|
||||
where = parentWrapperDir;
|
||||
what = "tmpfs";
|
||||
type = "tmpfs";
|
||||
options = lib.concatStringsSep "," ([
|
||||
"nodev"
|
||||
"mode=755"
|
||||
"size=${config.security.wrapperDirSize}"
|
||||
]);
|
||||
}];
|
||||
systemd.mounts = [
|
||||
{
|
||||
where = parentWrapperDir;
|
||||
what = "tmpfs";
|
||||
type = "tmpfs";
|
||||
options = lib.concatStringsSep "," ([
|
||||
"nodev"
|
||||
"mode=755"
|
||||
"size=${config.security.wrapperDirSize}"
|
||||
]);
|
||||
}
|
||||
];
|
||||
|
||||
systemd.services.suid-sgid-wrappers = {
|
||||
description = "Create SUID/SGID Wrappers";
|
||||
wantedBy = [ "sysinit.target" ];
|
||||
before = [ "sysinit.target" "shutdown.target" ];
|
||||
before = [
|
||||
"sysinit.target"
|
||||
"shutdown.target"
|
||||
];
|
||||
conflicts = [ "shutdown.target" ];
|
||||
after = [ "systemd-sysusers.service" ];
|
||||
unitConfig.DefaultDependencies = false;
|
||||
unitConfig.RequiresMountsFor = [ "/nix/store" "/run/wrappers" ];
|
||||
unitConfig.RequiresMountsFor = [
|
||||
"/nix/store"
|
||||
"/run/wrappers"
|
||||
];
|
||||
serviceConfig.Type = "oneshot";
|
||||
script = ''
|
||||
chmod 755 "${parentWrapperDir}"
|
||||
|
@ -321,17 +340,15 @@ in
|
|||
};
|
||||
|
||||
###### wrappers consistency checks
|
||||
system.checks = lib.singleton (pkgs.runCommandLocal
|
||||
"ensure-all-wrappers-paths-exist" { }
|
||||
''
|
||||
system.checks = lib.singleton (
|
||||
pkgs.runCommandLocal "ensure-all-wrappers-paths-exist" { } ''
|
||||
# make sure we produce output
|
||||
mkdir -p $out
|
||||
|
||||
echo -n "Checking that Nix store paths of all wrapped programs exist... "
|
||||
|
||||
declare -A wrappers
|
||||
${lib.concatStringsSep "\n" (lib.mapAttrsToList (n: v:
|
||||
"wrappers['${n}']='${v.source}'") wrappers)}
|
||||
${lib.concatStringsSep "\n" (lib.mapAttrsToList (n: v: "wrappers['${n}']='${v.source}'") wrappers)}
|
||||
|
||||
for name in "''${!wrappers[@]}"; do
|
||||
path="''${wrappers[$name]}"
|
||||
|
@ -346,6 +363,7 @@ in
|
|||
done
|
||||
|
||||
echo "OK"
|
||||
'');
|
||||
''
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,9 +1,16 @@
|
|||
# ALSA sound support.
|
||||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
{
|
||||
imports = [
|
||||
(lib.mkRemovedOptionModule [ "sound" ] "The option was heavily overloaded and can be removed from most configurations.")
|
||||
(lib.mkRemovedOptionModule [
|
||||
"sound"
|
||||
] "The option was heavily overloaded and can be removed from most configurations.")
|
||||
];
|
||||
|
||||
options.hardware.alsa.enablePersistence = lib.mkOption {
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
with lib;
|
||||
|
||||
|
@ -45,7 +50,9 @@ in
|
|||
ExecStart =
|
||||
let
|
||||
# these values are null by default but should not appear in the final config
|
||||
filteredSettings = filterAttrs (n: v: !((n == "tls-cert" || n == "tls-key") && v == null)) cfg.settings;
|
||||
filteredSettings = filterAttrs (
|
||||
n: v: !((n == "tls-cert" || n == "tls-key") && v == null)
|
||||
) cfg.settings;
|
||||
in
|
||||
"${pkgs.gonic}/bin/gonic -config-path ${settingsFormat.generate "gonic" filteredSettings}";
|
||||
DynamicUser = true;
|
||||
|
@ -58,17 +65,23 @@ in
|
|||
BindPaths = [
|
||||
cfg.settings.playlists-path
|
||||
];
|
||||
BindReadOnlyPaths = [
|
||||
# gonic can access scrobbling services
|
||||
"-/etc/resolv.conf"
|
||||
"-/etc/ssl/certs/ca-certificates.crt"
|
||||
builtins.storeDir
|
||||
cfg.settings.podcast-path
|
||||
] ++ cfg.settings.music-path
|
||||
++ lib.optional (cfg.settings.tls-cert != null) cfg.settings.tls-cert
|
||||
++ lib.optional (cfg.settings.tls-key != null) cfg.settings.tls-key;
|
||||
BindReadOnlyPaths =
|
||||
[
|
||||
# gonic can access scrobbling services
|
||||
"-/etc/resolv.conf"
|
||||
"-/etc/ssl/certs/ca-certificates.crt"
|
||||
builtins.storeDir
|
||||
cfg.settings.podcast-path
|
||||
]
|
||||
++ cfg.settings.music-path
|
||||
++ lib.optional (cfg.settings.tls-cert != null) cfg.settings.tls-cert
|
||||
++ lib.optional (cfg.settings.tls-key != null) cfg.settings.tls-key;
|
||||
CapabilityBoundingSet = "";
|
||||
RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ];
|
||||
RestrictAddressFamilies = [
|
||||
"AF_UNIX"
|
||||
"AF_INET"
|
||||
"AF_INET6"
|
||||
];
|
||||
RestrictNamespaces = true;
|
||||
PrivateDevices = true;
|
||||
PrivateUsers = true;
|
||||
|
@ -79,7 +92,10 @@ in
|
|||
ProtectKernelModules = true;
|
||||
ProtectKernelTunables = true;
|
||||
SystemCallArchitectures = "native";
|
||||
SystemCallFilter = [ "@system-service" "~@privileged" ];
|
||||
SystemCallFilter = [
|
||||
"@system-service"
|
||||
"~@privileged"
|
||||
];
|
||||
RestrictRealtime = true;
|
||||
LockPersonality = true;
|
||||
MemoryDenyWriteExecute = true;
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
|
||||
name = "mpd";
|
||||
|
@ -7,13 +12,17 @@ let
|
|||
gid = config.ids.gids.mpd;
|
||||
cfg = config.services.mpd;
|
||||
|
||||
credentialsPlaceholder = (creds:
|
||||
credentialsPlaceholder = (
|
||||
creds:
|
||||
let
|
||||
placeholders = (lib.imap0
|
||||
(i: c: ''password "{{password-${toString i}}}@${lib.concatStringsSep "," c.permissions}"'')
|
||||
creds);
|
||||
placeholders = (
|
||||
lib.imap0 (
|
||||
i: c: ''password "{{password-${toString i}}}@${lib.concatStringsSep "," c.permissions}"''
|
||||
) creds
|
||||
);
|
||||
in
|
||||
lib.concatStringsSep "\n" placeholders);
|
||||
lib.concatStringsSep "\n" placeholders
|
||||
);
|
||||
|
||||
mpdConf = pkgs.writeText "mpd.conf" ''
|
||||
# This file was automatically generated by NixOS. Edit mpd's configuration
|
||||
|
@ -28,8 +37,10 @@ let
|
|||
state_file "${cfg.dataDir}/state"
|
||||
sticker_file "${cfg.dataDir}/sticker.sql"
|
||||
|
||||
${lib.optionalString (cfg.network.listenAddress != "any") ''bind_to_address "${cfg.network.listenAddress}"''}
|
||||
${lib.optionalString (cfg.network.port != 6600) ''port "${toString cfg.network.port}"''}
|
||||
${lib.optionalString (
|
||||
cfg.network.listenAddress != "any"
|
||||
) ''bind_to_address "${cfg.network.listenAddress}"''}
|
||||
${lib.optionalString (cfg.network.port != 6600) ''port "${toString cfg.network.port}"''}
|
||||
${lib.optionalString (cfg.fluidsynth) ''
|
||||
decoder {
|
||||
plugin "fluidsynth"
|
||||
|
@ -37,12 +48,13 @@ let
|
|||
}
|
||||
''}
|
||||
|
||||
${lib.optionalString (cfg.credentials != []) (credentialsPlaceholder cfg.credentials)}
|
||||
${lib.optionalString (cfg.credentials != [ ]) (credentialsPlaceholder cfg.credentials)}
|
||||
|
||||
${cfg.extraConfig}
|
||||
'';
|
||||
|
||||
in {
|
||||
in
|
||||
{
|
||||
|
||||
###### interface
|
||||
|
||||
|
@ -160,33 +172,53 @@ in {
|
|||
};
|
||||
|
||||
credentials = lib.mkOption {
|
||||
type = lib.types.listOf (lib.types.submodule {
|
||||
options = {
|
||||
passwordFile = lib.mkOption {
|
||||
type = lib.types.path;
|
||||
description = ''
|
||||
Path to file containing the password.
|
||||
'';
|
||||
type = lib.types.listOf (
|
||||
lib.types.submodule {
|
||||
options = {
|
||||
passwordFile = lib.mkOption {
|
||||
type = lib.types.path;
|
||||
description = ''
|
||||
Path to file containing the password.
|
||||
'';
|
||||
};
|
||||
permissions =
|
||||
let
|
||||
perms = [
|
||||
"read"
|
||||
"add"
|
||||
"control"
|
||||
"admin"
|
||||
];
|
||||
in
|
||||
lib.mkOption {
|
||||
type = lib.types.listOf (lib.types.enum perms);
|
||||
default = [ "read" ];
|
||||
description = ''
|
||||
List of permissions that are granted with this password.
|
||||
Permissions can be "${lib.concatStringsSep "\", \"" perms}".
|
||||
'';
|
||||
};
|
||||
};
|
||||
permissions = let
|
||||
perms = ["read" "add" "control" "admin"];
|
||||
in lib.mkOption {
|
||||
type = lib.types.listOf (lib.types.enum perms);
|
||||
default = [ "read" ];
|
||||
description = ''
|
||||
List of permissions that are granted with this password.
|
||||
Permissions can be "${lib.concatStringsSep "\", \"" perms}".
|
||||
'';
|
||||
};
|
||||
};
|
||||
});
|
||||
}
|
||||
);
|
||||
description = ''
|
||||
Credentials and permissions for accessing the mpd server.
|
||||
'';
|
||||
default = [];
|
||||
default = [ ];
|
||||
example = [
|
||||
{passwordFile = "/var/lib/secrets/mpd_readonly_password"; permissions = [ "read" ];}
|
||||
{passwordFile = "/var/lib/secrets/mpd_admin_password"; permissions = ["read" "add" "control" "admin"];}
|
||||
{
|
||||
passwordFile = "/var/lib/secrets/mpd_readonly_password";
|
||||
permissions = [ "read" ];
|
||||
}
|
||||
{
|
||||
passwordFile = "/var/lib/secrets/mpd_admin_password";
|
||||
permissions = [
|
||||
"read"
|
||||
"add"
|
||||
"control"
|
||||
"admin"
|
||||
];
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
|
@ -201,7 +233,6 @@ in {
|
|||
|
||||
};
|
||||
|
||||
|
||||
###### implementation
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
|
@ -212,10 +243,15 @@ in {
|
|||
systemd.sockets.mpd = lib.mkIf cfg.startWhenNeeded {
|
||||
wantedBy = [ "sockets.target" ];
|
||||
listenStreams = [
|
||||
"" # Note: this is needed to override the upstream unit
|
||||
(if pkgs.lib.hasPrefix "/" cfg.network.listenAddress
|
||||
then cfg.network.listenAddress
|
||||
else "${lib.optionalString (cfg.network.listenAddress != "any") "${cfg.network.listenAddress}:"}${toString cfg.network.port}")
|
||||
"" # Note: this is needed to override the upstream unit
|
||||
(
|
||||
if pkgs.lib.hasPrefix "/" cfg.network.listenAddress then
|
||||
cfg.network.listenAddress
|
||||
else
|
||||
"${
|
||||
lib.optionalString (cfg.network.listenAddress != "any") "${cfg.network.listenAddress}:"
|
||||
}${toString cfg.network.port}"
|
||||
)
|
||||
];
|
||||
};
|
||||
|
||||
|
@ -226,23 +262,36 @@ in {
|
|||
''
|
||||
set -euo pipefail
|
||||
install -m 600 ${mpdConf} /run/mpd/mpd.conf
|
||||
'' + lib.optionalString (cfg.credentials != [])
|
||||
(lib.concatStringsSep "\n"
|
||||
(lib.imap0
|
||||
(i: c: ''${pkgs.replace-secret}/bin/replace-secret '{{password-${toString i}}}' '${c.passwordFile}' /run/mpd/mpd.conf'')
|
||||
cfg.credentials));
|
||||
''
|
||||
+ lib.optionalString (cfg.credentials != [ ]) (
|
||||
lib.concatStringsSep "\n" (
|
||||
lib.imap0 (
|
||||
i: c:
|
||||
''${pkgs.replace-secret}/bin/replace-secret '{{password-${toString i}}}' '${c.passwordFile}' /run/mpd/mpd.conf''
|
||||
) cfg.credentials
|
||||
)
|
||||
);
|
||||
|
||||
serviceConfig =
|
||||
{
|
||||
User = "${cfg.user}";
|
||||
# Note: the first "" overrides the ExecStart from the upstream unit
|
||||
ExecStart = [ "" "${pkgs.mpd}/bin/mpd --systemd /run/mpd/mpd.conf" ];
|
||||
RuntimeDirectory = "mpd";
|
||||
StateDirectory = []
|
||||
++ lib.optionals (cfg.dataDir == "/var/lib/${name}") [ name ]
|
||||
++ lib.optionals (cfg.playlistDirectory == "/var/lib/${name}/playlists") [ name "${name}/playlists" ]
|
||||
++ lib.optionals (cfg.musicDirectory == "/var/lib/${name}/music") [ name "${name}/music" ];
|
||||
};
|
||||
serviceConfig = {
|
||||
User = "${cfg.user}";
|
||||
# Note: the first "" overrides the ExecStart from the upstream unit
|
||||
ExecStart = [
|
||||
""
|
||||
"${pkgs.mpd}/bin/mpd --systemd /run/mpd/mpd.conf"
|
||||
];
|
||||
RuntimeDirectory = "mpd";
|
||||
StateDirectory =
|
||||
[ ]
|
||||
++ lib.optionals (cfg.dataDir == "/var/lib/${name}") [ name ]
|
||||
++ lib.optionals (cfg.playlistDirectory == "/var/lib/${name}/playlists") [
|
||||
name
|
||||
"${name}/playlists"
|
||||
]
|
||||
++ lib.optionals (cfg.musicDirectory == "/var/lib/${name}/music") [
|
||||
name
|
||||
"${name}/music"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
users.users = lib.optionalAttrs (cfg.user == name) {
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,9 +1,23 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.services.sanoid;
|
||||
|
||||
datasetSettingsType = with lib.types;
|
||||
(attrsOf (nullOr (oneOf [ str int bool (listOf str) ]))) // {
|
||||
datasetSettingsType =
|
||||
with lib.types;
|
||||
(attrsOf (
|
||||
nullOr (oneOf [
|
||||
str
|
||||
int
|
||||
bool
|
||||
(listOf str)
|
||||
])
|
||||
))
|
||||
// {
|
||||
description = "dataset/template options";
|
||||
};
|
||||
|
||||
|
@ -48,10 +62,13 @@ let
|
|||
datasetOptions = rec {
|
||||
use_template = lib.mkOption {
|
||||
description = "Names of the templates to use for this dataset.";
|
||||
type = lib.types.listOf (lib.types.str // {
|
||||
check = (lib.types.enum (lib.attrNames cfg.templates)).check;
|
||||
description = "configured template name";
|
||||
});
|
||||
type = lib.types.listOf (
|
||||
lib.types.str
|
||||
// {
|
||||
check = (lib.types.enum (lib.attrNames cfg.templates)).check;
|
||||
description = "configured template name";
|
||||
}
|
||||
);
|
||||
default = [ ];
|
||||
};
|
||||
useTemplate = use_template;
|
||||
|
@ -63,7 +80,12 @@ let
|
|||
recursively in an atomic way without the possibility to
|
||||
override settings for child datasets.
|
||||
'';
|
||||
type = with lib.types; oneOf [ bool (enum [ "zfs" ]) ];
|
||||
type =
|
||||
with lib.types;
|
||||
oneOf [
|
||||
bool
|
||||
(enum [ "zfs" ])
|
||||
];
|
||||
default = false;
|
||||
};
|
||||
|
||||
|
@ -80,26 +102,32 @@ let
|
|||
|
||||
# Function to build "zfs allow" and "zfs unallow" commands for the
|
||||
# filesystems we've delegated permissions to.
|
||||
buildAllowCommand = zfsAction: permissions: dataset: lib.escapeShellArgs [
|
||||
# Here we explicitly use the booted system to guarantee the stable API needed by ZFS
|
||||
"-+/run/booted-system/sw/bin/zfs"
|
||||
zfsAction
|
||||
"sanoid"
|
||||
(lib.concatStringsSep "," permissions)
|
||||
dataset
|
||||
];
|
||||
buildAllowCommand =
|
||||
zfsAction: permissions: dataset:
|
||||
lib.escapeShellArgs [
|
||||
# Here we explicitly use the booted system to guarantee the stable API needed by ZFS
|
||||
"-+/run/booted-system/sw/bin/zfs"
|
||||
zfsAction
|
||||
"sanoid"
|
||||
(lib.concatStringsSep "," permissions)
|
||||
dataset
|
||||
];
|
||||
|
||||
configFile =
|
||||
let
|
||||
mkValueString = v:
|
||||
if lib.isList v then lib.concatStringsSep "," v
|
||||
else lib.generators.mkValueStringDefault { } v;
|
||||
mkValueString =
|
||||
v: if lib.isList v then lib.concatStringsSep "," v else lib.generators.mkValueStringDefault { } v;
|
||||
|
||||
mkKeyValue = k: v:
|
||||
if v == null then ""
|
||||
else if k == "processChildrenOnly" then ""
|
||||
else if k == "useTemplate" then ""
|
||||
else lib.generators.mkKeyValueDefault { inherit mkValueString; } "=" k v;
|
||||
mkKeyValue =
|
||||
k: v:
|
||||
if v == null then
|
||||
""
|
||||
else if k == "processChildrenOnly" then
|
||||
""
|
||||
else if k == "useTemplate" then
|
||||
""
|
||||
else
|
||||
lib.generators.mkKeyValueDefault { inherit mkValueString; } "=" k v;
|
||||
in
|
||||
lib.generators.toINI { inherit mkKeyValue; } cfg.settings;
|
||||
|
||||
|
@ -111,7 +139,7 @@ in
|
|||
options.services.sanoid = {
|
||||
enable = lib.mkEnableOption "Sanoid ZFS snapshotting service";
|
||||
|
||||
package = lib.mkPackageOption pkgs "sanoid" {};
|
||||
package = lib.mkPackageOption pkgs "sanoid" { };
|
||||
|
||||
interval = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
|
@ -126,21 +154,32 @@ in
|
|||
};
|
||||
|
||||
datasets = lib.mkOption {
|
||||
type = lib.types.attrsOf (lib.types.submodule ({ config, options, ... }: {
|
||||
freeformType = datasetSettingsType;
|
||||
options = commonOptions // datasetOptions;
|
||||
config.use_template = lib.modules.mkAliasAndWrapDefsWithPriority lib.id (options.useTemplate or { });
|
||||
config.process_children_only = lib.modules.mkAliasAndWrapDefsWithPriority lib.id (options.processChildrenOnly or { });
|
||||
}));
|
||||
type = lib.types.attrsOf (
|
||||
lib.types.submodule (
|
||||
{ config, options, ... }:
|
||||
{
|
||||
freeformType = datasetSettingsType;
|
||||
options = commonOptions // datasetOptions;
|
||||
config.use_template = lib.modules.mkAliasAndWrapDefsWithPriority lib.id (
|
||||
options.useTemplate or { }
|
||||
);
|
||||
config.process_children_only = lib.modules.mkAliasAndWrapDefsWithPriority lib.id (
|
||||
options.processChildrenOnly or { }
|
||||
);
|
||||
}
|
||||
)
|
||||
);
|
||||
default = { };
|
||||
description = "Datasets to snapshot.";
|
||||
};
|
||||
|
||||
templates = lib.mkOption {
|
||||
type = lib.types.attrsOf (lib.types.submodule {
|
||||
freeformType = datasetSettingsType;
|
||||
options = commonOptions;
|
||||
});
|
||||
type = lib.types.attrsOf (
|
||||
lib.types.submodule {
|
||||
freeformType = datasetSettingsType;
|
||||
options = commonOptions;
|
||||
}
|
||||
);
|
||||
default = { };
|
||||
description = "Templates for datasets.";
|
||||
};
|
||||
|
@ -157,7 +196,11 @@ in
|
|||
extraArgs = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [ ];
|
||||
example = [ "--verbose" "--readonly" "--debug" ];
|
||||
example = [
|
||||
"--verbose"
|
||||
"--readonly"
|
||||
"--debug"
|
||||
];
|
||||
description = ''
|
||||
Extra arguments to pass to sanoid. See
|
||||
<https://github.com/jimsalterjrs/sanoid/#sanoid-command-line-options>
|
||||
|
@ -177,14 +220,29 @@ in
|
|||
systemd.services.sanoid = {
|
||||
description = "Sanoid snapshot service";
|
||||
serviceConfig = {
|
||||
ExecStartPre = (map (buildAllowCommand "allow" [ "snapshot" "mount" "destroy" ]) datasets);
|
||||
ExecStopPost = (map (buildAllowCommand "unallow" [ "snapshot" "mount" "destroy" ]) datasets);
|
||||
ExecStart = lib.escapeShellArgs ([
|
||||
"${cfg.package}/bin/sanoid"
|
||||
"--cron"
|
||||
"--configdir"
|
||||
(pkgs.writeTextDir "sanoid.conf" configFile)
|
||||
] ++ cfg.extraArgs);
|
||||
ExecStartPre = (
|
||||
map (buildAllowCommand "allow" [
|
||||
"snapshot"
|
||||
"mount"
|
||||
"destroy"
|
||||
]) datasets
|
||||
);
|
||||
ExecStopPost = (
|
||||
map (buildAllowCommand "unallow" [
|
||||
"snapshot"
|
||||
"mount"
|
||||
"destroy"
|
||||
]) datasets
|
||||
);
|
||||
ExecStart = lib.escapeShellArgs (
|
||||
[
|
||||
"${cfg.package}/bin/sanoid"
|
||||
"--cron"
|
||||
"--configdir"
|
||||
(pkgs.writeTextDir "sanoid.conf" configFile)
|
||||
]
|
||||
++ cfg.extraArgs
|
||||
);
|
||||
User = "sanoid";
|
||||
Group = "sanoid";
|
||||
DynamicUser = true;
|
||||
|
|
|
@ -1,54 +1,69 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.services.syncoid;
|
||||
|
||||
# Extract local dasaset names (so no datasets containing "@")
|
||||
localDatasetName = d: lib.optionals (d != null) (
|
||||
let m = builtins.match "([^/@]+[^@]*)" d; in
|
||||
lib.optionals (m != null) m
|
||||
);
|
||||
localDatasetName =
|
||||
d:
|
||||
lib.optionals (d != null) (
|
||||
let
|
||||
m = builtins.match "([^/@]+[^@]*)" d;
|
||||
in
|
||||
lib.optionals (m != null) m
|
||||
);
|
||||
|
||||
# Escape as required by: https://www.freedesktop.org/software/systemd/man/systemd.unit.html
|
||||
escapeUnitName = name:
|
||||
lib.concatMapStrings (s: if lib.isList s then "-" else s)
|
||||
(builtins.split "[^a-zA-Z0-9_.\\-]+" name);
|
||||
escapeUnitName =
|
||||
name:
|
||||
lib.concatMapStrings (s: if lib.isList s then "-" else s) (
|
||||
builtins.split "[^a-zA-Z0-9_.\\-]+" name
|
||||
);
|
||||
|
||||
# Function to build "zfs allow" commands for the filesystems we've delegated
|
||||
# permissions to. It also checks if the target dataset exists before
|
||||
# delegating permissions, if it doesn't exist we delegate it to the parent
|
||||
# dataset (if it exists). This should solve the case of provisoning new
|
||||
# datasets.
|
||||
buildAllowCommand = permissions: dataset: (
|
||||
"-+${pkgs.writeShellScript "zfs-allow-${dataset}" ''
|
||||
# Here we explicitly use the booted system to guarantee the stable API needed by ZFS
|
||||
buildAllowCommand =
|
||||
permissions: dataset:
|
||||
(
|
||||
"-+${pkgs.writeShellScript "zfs-allow-${dataset}" ''
|
||||
# Here we explicitly use the booted system to guarantee the stable API needed by ZFS
|
||||
|
||||
# Run a ZFS list on the dataset to check if it exists
|
||||
if ${lib.escapeShellArgs [
|
||||
"/run/booted-system/sw/bin/zfs"
|
||||
"list"
|
||||
dataset
|
||||
]} 2> /dev/null; then
|
||||
${lib.escapeShellArgs [
|
||||
"/run/booted-system/sw/bin/zfs"
|
||||
"allow"
|
||||
cfg.user
|
||||
(lib.concatStringsSep "," permissions)
|
||||
dataset
|
||||
]}
|
||||
${lib.optionalString ((builtins.dirOf dataset) != ".") ''
|
||||
else
|
||||
# Run a ZFS list on the dataset to check if it exists
|
||||
if ${
|
||||
lib.escapeShellArgs [
|
||||
"/run/booted-system/sw/bin/zfs"
|
||||
"list"
|
||||
dataset
|
||||
]
|
||||
} 2> /dev/null; then
|
||||
${lib.escapeShellArgs [
|
||||
"/run/booted-system/sw/bin/zfs"
|
||||
"allow"
|
||||
cfg.user
|
||||
(lib.concatStringsSep "," permissions)
|
||||
# Remove the last part of the path
|
||||
(builtins.dirOf dataset)
|
||||
dataset
|
||||
]}
|
||||
''}
|
||||
fi
|
||||
''}"
|
||||
);
|
||||
${lib.optionalString ((builtins.dirOf dataset) != ".") ''
|
||||
else
|
||||
${lib.escapeShellArgs [
|
||||
"/run/booted-system/sw/bin/zfs"
|
||||
"allow"
|
||||
cfg.user
|
||||
(lib.concatStringsSep "," permissions)
|
||||
# Remove the last part of the path
|
||||
(builtins.dirOf dataset)
|
||||
]}
|
||||
''}
|
||||
fi
|
||||
''}"
|
||||
);
|
||||
|
||||
# Function to build "zfs unallow" commands for the filesystems we've
|
||||
# delegated permissions to. Here we unallow both the target but also
|
||||
|
@ -56,26 +71,30 @@ let
|
|||
# knowing if the allow command did execute on the parent dataset or
|
||||
# not in the pre-hook. We can't run the same if in the post hook
|
||||
# since the dataset should have been created at this point.
|
||||
buildUnallowCommand = permissions: dataset: (
|
||||
"-+${pkgs.writeShellScript "zfs-unallow-${dataset}" ''
|
||||
# Here we explicitly use the booted system to guarantee the stable API needed by ZFS
|
||||
${lib.escapeShellArgs [
|
||||
"/run/booted-system/sw/bin/zfs"
|
||||
"unallow"
|
||||
cfg.user
|
||||
(lib.concatStringsSep "," permissions)
|
||||
dataset
|
||||
]}
|
||||
${lib.optionalString ((builtins.dirOf dataset) != ".") (lib.escapeShellArgs [
|
||||
"/run/booted-system/sw/bin/zfs"
|
||||
"unallow"
|
||||
cfg.user
|
||||
(lib.concatStringsSep "," permissions)
|
||||
# Remove the last part of the path
|
||||
(builtins.dirOf dataset)
|
||||
])}
|
||||
''}"
|
||||
);
|
||||
buildUnallowCommand =
|
||||
permissions: dataset:
|
||||
(
|
||||
"-+${pkgs.writeShellScript "zfs-unallow-${dataset}" ''
|
||||
# Here we explicitly use the booted system to guarantee the stable API needed by ZFS
|
||||
${lib.escapeShellArgs [
|
||||
"/run/booted-system/sw/bin/zfs"
|
||||
"unallow"
|
||||
cfg.user
|
||||
(lib.concatStringsSep "," permissions)
|
||||
dataset
|
||||
]}
|
||||
${lib.optionalString ((builtins.dirOf dataset) != ".") (
|
||||
lib.escapeShellArgs [
|
||||
"/run/booted-system/sw/bin/zfs"
|
||||
"unallow"
|
||||
cfg.user
|
||||
(lib.concatStringsSep "," permissions)
|
||||
# Remove the last part of the path
|
||||
(builtins.dirOf dataset)
|
||||
]
|
||||
)}
|
||||
''}"
|
||||
);
|
||||
in
|
||||
{
|
||||
|
||||
|
@ -84,7 +103,7 @@ in
|
|||
options.services.syncoid = {
|
||||
enable = lib.mkEnableOption "Syncoid ZFS synchronization service";
|
||||
|
||||
package = lib.mkPackageOption pkgs "sanoid" {};
|
||||
package = lib.mkPackageOption pkgs "sanoid" { };
|
||||
|
||||
interval = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
|
@ -131,7 +150,14 @@ in
|
|||
localSourceAllow = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
# Permissions snapshot and destroy are in case --no-sync-snap is not used
|
||||
default = [ "bookmark" "hold" "send" "snapshot" "destroy" "mount" ];
|
||||
default = [
|
||||
"bookmark"
|
||||
"hold"
|
||||
"send"
|
||||
"snapshot"
|
||||
"destroy"
|
||||
"mount"
|
||||
];
|
||||
description = ''
|
||||
Permissions granted for the {option}`services.syncoid.user` user
|
||||
for local source datasets. See
|
||||
|
@ -142,8 +168,21 @@ in
|
|||
|
||||
localTargetAllow = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [ "change-key" "compression" "create" "mount" "mountpoint" "receive" "rollback" ];
|
||||
example = [ "create" "mount" "receive" "rollback" ];
|
||||
default = [
|
||||
"change-key"
|
||||
"compression"
|
||||
"create"
|
||||
"mount"
|
||||
"mountpoint"
|
||||
"receive"
|
||||
"rollback"
|
||||
];
|
||||
example = [
|
||||
"create"
|
||||
"mount"
|
||||
"receive"
|
||||
"rollback"
|
||||
];
|
||||
description = ''
|
||||
Permissions granted for the {option}`services.syncoid.user` user
|
||||
for local target datasets. See
|
||||
|
@ -176,111 +215,116 @@ in
|
|||
};
|
||||
|
||||
commands = lib.mkOption {
|
||||
type = lib.types.attrsOf (lib.types.submodule ({ name, ... }: {
|
||||
options = {
|
||||
source = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
example = "pool/dataset";
|
||||
description = ''
|
||||
Source ZFS dataset. Can be either local or remote. Defaults to
|
||||
the attribute name.
|
||||
'';
|
||||
};
|
||||
type = lib.types.attrsOf (
|
||||
lib.types.submodule (
|
||||
{ name, ... }:
|
||||
{
|
||||
options = {
|
||||
source = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
example = "pool/dataset";
|
||||
description = ''
|
||||
Source ZFS dataset. Can be either local or remote. Defaults to
|
||||
the attribute name.
|
||||
'';
|
||||
};
|
||||
|
||||
target = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
example = "user@server:pool/dataset";
|
||||
description = ''
|
||||
Target ZFS dataset. Can be either local
|
||||
(«pool/dataset») or remote
|
||||
(«user@server:pool/dataset»).
|
||||
'';
|
||||
};
|
||||
target = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
example = "user@server:pool/dataset";
|
||||
description = ''
|
||||
Target ZFS dataset. Can be either local
|
||||
(«pool/dataset») or remote
|
||||
(«user@server:pool/dataset»).
|
||||
'';
|
||||
};
|
||||
|
||||
recursive = lib.mkEnableOption ''the transfer of child datasets'';
|
||||
recursive = lib.mkEnableOption ''the transfer of child datasets'';
|
||||
|
||||
sshKey = lib.mkOption {
|
||||
type = with lib.types; nullOr (coercedTo path toString str);
|
||||
description = ''
|
||||
SSH private key file to use to login to the remote system.
|
||||
Defaults to {option}`services.syncoid.sshKey` option.
|
||||
'';
|
||||
};
|
||||
sshKey = lib.mkOption {
|
||||
type = with lib.types; nullOr (coercedTo path toString str);
|
||||
description = ''
|
||||
SSH private key file to use to login to the remote system.
|
||||
Defaults to {option}`services.syncoid.sshKey` option.
|
||||
'';
|
||||
};
|
||||
|
||||
localSourceAllow = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
description = ''
|
||||
Permissions granted for the {option}`services.syncoid.user` user
|
||||
for local source datasets. See
|
||||
<https://openzfs.github.io/openzfs-docs/man/8/zfs-allow.8.html>
|
||||
for available permissions.
|
||||
Defaults to {option}`services.syncoid.localSourceAllow` option.
|
||||
'';
|
||||
};
|
||||
localSourceAllow = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
description = ''
|
||||
Permissions granted for the {option}`services.syncoid.user` user
|
||||
for local source datasets. See
|
||||
<https://openzfs.github.io/openzfs-docs/man/8/zfs-allow.8.html>
|
||||
for available permissions.
|
||||
Defaults to {option}`services.syncoid.localSourceAllow` option.
|
||||
'';
|
||||
};
|
||||
|
||||
localTargetAllow = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
description = ''
|
||||
Permissions granted for the {option}`services.syncoid.user` user
|
||||
for local target datasets. See
|
||||
<https://openzfs.github.io/openzfs-docs/man/8/zfs-allow.8.html>
|
||||
for available permissions.
|
||||
Make sure to include the `change-key` permission if you send raw encrypted datasets,
|
||||
the `compression` permission if you send raw compressed datasets, and so on.
|
||||
For remote target datasets you'll have to set your remote user permissions by yourself.
|
||||
'';
|
||||
};
|
||||
localTargetAllow = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
description = ''
|
||||
Permissions granted for the {option}`services.syncoid.user` user
|
||||
for local target datasets. See
|
||||
<https://openzfs.github.io/openzfs-docs/man/8/zfs-allow.8.html>
|
||||
for available permissions.
|
||||
Make sure to include the `change-key` permission if you send raw encrypted datasets,
|
||||
the `compression` permission if you send raw compressed datasets, and so on.
|
||||
For remote target datasets you'll have to set your remote user permissions by yourself.
|
||||
'';
|
||||
};
|
||||
|
||||
sendOptions = lib.mkOption {
|
||||
type = lib.types.separatedString " ";
|
||||
default = "";
|
||||
example = "Lc e";
|
||||
description = ''
|
||||
Advanced options to pass to zfs send. Options are specified
|
||||
without their leading dashes and separated by spaces.
|
||||
'';
|
||||
};
|
||||
sendOptions = lib.mkOption {
|
||||
type = lib.types.separatedString " ";
|
||||
default = "";
|
||||
example = "Lc e";
|
||||
description = ''
|
||||
Advanced options to pass to zfs send. Options are specified
|
||||
without their leading dashes and separated by spaces.
|
||||
'';
|
||||
};
|
||||
|
||||
recvOptions = lib.mkOption {
|
||||
type = lib.types.separatedString " ";
|
||||
default = "";
|
||||
example = "ux recordsize o compression=lz4";
|
||||
description = ''
|
||||
Advanced options to pass to zfs recv. Options are specified
|
||||
without their leading dashes and separated by spaces.
|
||||
'';
|
||||
};
|
||||
recvOptions = lib.mkOption {
|
||||
type = lib.types.separatedString " ";
|
||||
default = "";
|
||||
example = "ux recordsize o compression=lz4";
|
||||
description = ''
|
||||
Advanced options to pass to zfs recv. Options are specified
|
||||
without their leading dashes and separated by spaces.
|
||||
'';
|
||||
};
|
||||
|
||||
useCommonArgs = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
Whether to add the configured common arguments to this command.
|
||||
'';
|
||||
};
|
||||
useCommonArgs = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
Whether to add the configured common arguments to this command.
|
||||
'';
|
||||
};
|
||||
|
||||
service = lib.mkOption {
|
||||
type = lib.types.attrs;
|
||||
default = { };
|
||||
description = ''
|
||||
Systemd configuration specific to this syncoid service.
|
||||
'';
|
||||
};
|
||||
service = lib.mkOption {
|
||||
type = lib.types.attrs;
|
||||
default = { };
|
||||
description = ''
|
||||
Systemd configuration specific to this syncoid service.
|
||||
'';
|
||||
};
|
||||
|
||||
extraArgs = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [ ];
|
||||
example = [ "--sshport 2222" ];
|
||||
description = "Extra syncoid arguments for this command.";
|
||||
};
|
||||
};
|
||||
config = {
|
||||
source = lib.mkDefault name;
|
||||
sshKey = lib.mkDefault cfg.sshKey;
|
||||
localSourceAllow = lib.mkDefault cfg.localSourceAllow;
|
||||
localTargetAllow = lib.mkDefault cfg.localTargetAllow;
|
||||
};
|
||||
}));
|
||||
extraArgs = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [ ];
|
||||
example = [ "--sshport 2222" ];
|
||||
description = "Extra syncoid arguments for this command.";
|
||||
};
|
||||
};
|
||||
config = {
|
||||
source = lib.mkDefault name;
|
||||
sshKey = lib.mkDefault cfg.sshKey;
|
||||
localSourceAllow = lib.mkDefault cfg.localSourceAllow;
|
||||
localTargetAllow = lib.mkDefault cfg.localTargetAllow;
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
default = { };
|
||||
example = lib.literalExpression ''
|
||||
{
|
||||
|
@ -310,9 +354,10 @@ in
|
|||
};
|
||||
};
|
||||
|
||||
systemd.services = lib.mapAttrs'
|
||||
(name: c:
|
||||
lib.nameValuePair "syncoid-${escapeUnitName name}" (lib.mkMerge [
|
||||
systemd.services = lib.mapAttrs' (
|
||||
name: c:
|
||||
lib.nameValuePair "syncoid-${escapeUnitName name}" (
|
||||
lib.mkMerge [
|
||||
{
|
||||
description = "Syncoid ZFS synchronization from ${c.source} to ${c.target}";
|
||||
after = [ "zfs.target" ];
|
||||
|
@ -321,25 +366,30 @@ in
|
|||
path = [ "/run/booted-system/sw/bin/" ];
|
||||
serviceConfig = {
|
||||
ExecStartPre =
|
||||
(map (buildAllowCommand c.localSourceAllow) (localDatasetName c.source)) ++
|
||||
(map (buildAllowCommand c.localTargetAllow) (localDatasetName c.target));
|
||||
(map (buildAllowCommand c.localSourceAllow) (localDatasetName c.source))
|
||||
++ (map (buildAllowCommand c.localTargetAllow) (localDatasetName c.target));
|
||||
ExecStopPost =
|
||||
(map (buildUnallowCommand c.localSourceAllow) (localDatasetName c.source)) ++
|
||||
(map (buildUnallowCommand c.localTargetAllow) (localDatasetName c.target));
|
||||
ExecStart = lib.escapeShellArgs ([ "${cfg.package}/bin/syncoid" ]
|
||||
(map (buildUnallowCommand c.localSourceAllow) (localDatasetName c.source))
|
||||
++ (map (buildUnallowCommand c.localTargetAllow) (localDatasetName c.target));
|
||||
ExecStart = lib.escapeShellArgs (
|
||||
[ "${cfg.package}/bin/syncoid" ]
|
||||
++ lib.optionals c.useCommonArgs cfg.commonArgs
|
||||
++ lib.optional c.recursive "-r"
|
||||
++ lib.optionals (c.sshKey != null) [ "--sshkey" c.sshKey ]
|
||||
++ lib.optionals (c.sshKey != null) [
|
||||
"--sshkey"
|
||||
c.sshKey
|
||||
]
|
||||
++ c.extraArgs
|
||||
++ [
|
||||
"--sendoptions"
|
||||
c.sendOptions
|
||||
"--recvoptions"
|
||||
c.recvOptions
|
||||
"--no-privilege-elevation"
|
||||
c.source
|
||||
c.target
|
||||
]);
|
||||
"--sendoptions"
|
||||
c.sendOptions
|
||||
"--recvoptions"
|
||||
c.recvOptions
|
||||
"--no-privilege-elevation"
|
||||
c.source
|
||||
c.target
|
||||
]
|
||||
);
|
||||
User = cfg.user;
|
||||
Group = cfg.group;
|
||||
StateDirectory = [ "syncoid" ];
|
||||
|
@ -372,14 +422,23 @@ in
|
|||
ProtectKernelTunables = true;
|
||||
ProtectSystem = "strict";
|
||||
RemoveIPC = true;
|
||||
RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ];
|
||||
RestrictAddressFamilies = [
|
||||
"AF_UNIX"
|
||||
"AF_INET"
|
||||
"AF_INET6"
|
||||
];
|
||||
RestrictNamespaces = true;
|
||||
RestrictRealtime = true;
|
||||
RestrictSUIDSGID = true;
|
||||
RootDirectory = "/run/syncoid/${escapeUnitName name}";
|
||||
RootDirectoryStartOnly = true;
|
||||
BindPaths = [ "/dev/zfs" ];
|
||||
BindReadOnlyPaths = [ builtins.storeDir "/etc" "/run" "/bin/sh" ];
|
||||
BindReadOnlyPaths = [
|
||||
builtins.storeDir
|
||||
"/etc"
|
||||
"/run"
|
||||
"/bin/sh"
|
||||
];
|
||||
# Avoid useless mounting of RootDirectory= in the own RootDirectory= of ExecStart='s mount namespace.
|
||||
InaccessiblePaths = [ "-+/run/syncoid/${escapeUnitName name}" ];
|
||||
MountAPIVFS = true;
|
||||
|
@ -409,9 +468,13 @@ in
|
|||
}
|
||||
cfg.service
|
||||
c.service
|
||||
]))
|
||||
cfg.commands;
|
||||
]
|
||||
)
|
||||
) cfg.commands;
|
||||
};
|
||||
|
||||
meta.maintainers = with lib.maintainers; [ julm lopsided98 ];
|
||||
meta.maintainers = with lib.maintainers; [
|
||||
julm
|
||||
lopsided98
|
||||
];
|
||||
}
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
{ config, options, pkgs, lib, ... }:
|
||||
{
|
||||
config,
|
||||
options,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
version = "1.10.1";
|
||||
cfg = config.services.kubernetes.addons.dns;
|
||||
|
@ -7,7 +13,8 @@ let
|
|||
health = 10054;
|
||||
metrics = 10055;
|
||||
};
|
||||
in {
|
||||
in
|
||||
{
|
||||
options.services.kubernetes.addons.dns = {
|
||||
enable = lib.mkEnableOption "kubernetes dns addon";
|
||||
|
||||
|
@ -15,11 +22,11 @@ in {
|
|||
description = "Dns addon clusterIP";
|
||||
|
||||
# this default is also what kubernetes users
|
||||
default = (
|
||||
lib.concatStringsSep "." (
|
||||
lib.take 3 (lib.splitString "." config.services.kubernetes.apiserver.serviceClusterIpRange
|
||||
default =
|
||||
(lib.concatStringsSep "." (
|
||||
lib.take 3 (lib.splitString "." config.services.kubernetes.apiserver.serviceClusterIpRange)
|
||||
))
|
||||
) + ".254";
|
||||
+ ".254";
|
||||
defaultText = lib.literalMD ''
|
||||
The `x.y.z.254` IP of
|
||||
`config.${options.services.kubernetes.apiserver.serviceClusterIpRange}`.
|
||||
|
@ -48,7 +55,10 @@ in {
|
|||
See: <https://github.com/kubernetes/kubernetes/blob/master/cluster/addons/addon-manager/README.md>.
|
||||
'';
|
||||
default = "Reconcile";
|
||||
type = lib.types.enum [ "Reconcile" "EnsureExists" ];
|
||||
type = lib.types.enum [
|
||||
"Reconcile"
|
||||
"EnsureExists"
|
||||
];
|
||||
};
|
||||
|
||||
coredns = lib.mkOption {
|
||||
|
@ -106,8 +116,9 @@ in {
|
|||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
services.kubernetes.kubelet.seedDockerImages =
|
||||
lib.singleton (pkgs.dockerTools.pullImage cfg.coredns);
|
||||
services.kubernetes.kubelet.seedDockerImages = lib.singleton (
|
||||
pkgs.dockerTools.pullImage cfg.coredns
|
||||
);
|
||||
|
||||
services.kubernetes.addonManager.bootstrapAddons = {
|
||||
coredns-cr = {
|
||||
|
@ -125,8 +136,16 @@ in {
|
|||
rules = [
|
||||
{
|
||||
apiGroups = [ "" ];
|
||||
resources = [ "endpoints" "services" "pods" "namespaces" ];
|
||||
verbs = [ "list" "watch" ];
|
||||
resources = [
|
||||
"endpoints"
|
||||
"services"
|
||||
"pods"
|
||||
"namespaces"
|
||||
];
|
||||
verbs = [
|
||||
"list"
|
||||
"watch"
|
||||
];
|
||||
}
|
||||
{
|
||||
apiGroups = [ "" ];
|
||||
|
@ -136,7 +155,10 @@ in {
|
|||
{
|
||||
apiGroups = [ "discovery.k8s.io" ];
|
||||
resources = [ "endpointslices" ];
|
||||
verbs = [ "list" "watch" ];
|
||||
verbs = [
|
||||
"list"
|
||||
"watch"
|
||||
];
|
||||
}
|
||||
];
|
||||
};
|
||||
|
@ -219,10 +241,14 @@ in {
|
|||
spec = {
|
||||
replicas = cfg.replicas;
|
||||
selector = {
|
||||
matchLabels = { k8s-app = "kube-dns"; };
|
||||
matchLabels = {
|
||||
k8s-app = "kube-dns";
|
||||
};
|
||||
};
|
||||
strategy = {
|
||||
rollingUpdate = { maxUnavailable = 1; };
|
||||
rollingUpdate = {
|
||||
maxUnavailable = 1;
|
||||
};
|
||||
type = "RollingUpdate";
|
||||
};
|
||||
template = {
|
||||
|
@ -234,7 +260,10 @@ in {
|
|||
spec = {
|
||||
containers = [
|
||||
{
|
||||
args = [ "-conf" "/etc/coredns/Corefile" ];
|
||||
args = [
|
||||
"-conf"
|
||||
"/etc/coredns/Corefile"
|
||||
];
|
||||
image = with cfg.coredns; "${imageName}:${finalImageTag}";
|
||||
imagePullPolicy = "Never";
|
||||
livenessProbe = {
|
||||
|
@ -358,7 +387,9 @@ in {
|
|||
protocol = "TCP";
|
||||
}
|
||||
];
|
||||
selector = { k8s-app = "kube-dns"; };
|
||||
selector = {
|
||||
k8s-app = "kube-dns";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
{ config, lib, options, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
options,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
with lib;
|
||||
|
||||
|
@ -9,22 +15,40 @@ let
|
|||
|
||||
isRBACEnabled = elem "RBAC" cfg.authorizationMode;
|
||||
|
||||
apiserverServiceIP = (concatStringsSep "." (
|
||||
take 3 (splitString "." cfg.serviceClusterIpRange
|
||||
)) + ".1");
|
||||
apiserverServiceIP = (
|
||||
concatStringsSep "." (take 3 (splitString "." cfg.serviceClusterIpRange)) + ".1"
|
||||
);
|
||||
in
|
||||
{
|
||||
|
||||
imports = [
|
||||
(mkRenamedOptionModule [ "services" "kubernetes" "apiserver" "admissionControl" ] [ "services" "kubernetes" "apiserver" "enableAdmissionPlugins" ])
|
||||
(mkRenamedOptionModule [ "services" "kubernetes" "apiserver" "address" ] ["services" "kubernetes" "apiserver" "bindAddress"])
|
||||
(mkRenamedOptionModule
|
||||
[ "services" "kubernetes" "apiserver" "admissionControl" ]
|
||||
[ "services" "kubernetes" "apiserver" "enableAdmissionPlugins" ]
|
||||
)
|
||||
(mkRenamedOptionModule
|
||||
[ "services" "kubernetes" "apiserver" "address" ]
|
||||
[ "services" "kubernetes" "apiserver" "bindAddress" ]
|
||||
)
|
||||
(mkRemovedOptionModule [ "services" "kubernetes" "apiserver" "insecureBindAddress" ] "")
|
||||
(mkRemovedOptionModule [ "services" "kubernetes" "apiserver" "insecurePort" ] "")
|
||||
(mkRemovedOptionModule [ "services" "kubernetes" "apiserver" "publicAddress" ] "")
|
||||
(mkRenamedOptionModule [ "services" "kubernetes" "etcd" "servers" ] [ "services" "kubernetes" "apiserver" "etcd" "servers" ])
|
||||
(mkRenamedOptionModule [ "services" "kubernetes" "etcd" "keyFile" ] [ "services" "kubernetes" "apiserver" "etcd" "keyFile" ])
|
||||
(mkRenamedOptionModule [ "services" "kubernetes" "etcd" "certFile" ] [ "services" "kubernetes" "apiserver" "etcd" "certFile" ])
|
||||
(mkRenamedOptionModule [ "services" "kubernetes" "etcd" "caFile" ] [ "services" "kubernetes" "apiserver" "etcd" "caFile" ])
|
||||
(mkRenamedOptionModule
|
||||
[ "services" "kubernetes" "etcd" "servers" ]
|
||||
[ "services" "kubernetes" "apiserver" "etcd" "servers" ]
|
||||
)
|
||||
(mkRenamedOptionModule
|
||||
[ "services" "kubernetes" "etcd" "keyFile" ]
|
||||
[ "services" "kubernetes" "apiserver" "etcd" "keyFile" ]
|
||||
)
|
||||
(mkRenamedOptionModule
|
||||
[ "services" "kubernetes" "etcd" "certFile" ]
|
||||
[ "services" "kubernetes" "apiserver" "etcd" "certFile" ]
|
||||
)
|
||||
(mkRenamedOptionModule
|
||||
[ "services" "kubernetes" "etcd" "caFile" ]
|
||||
[ "services" "kubernetes" "apiserver" "etcd" "caFile" ]
|
||||
)
|
||||
];
|
||||
|
||||
###### interface
|
||||
|
@ -51,8 +75,18 @@ in
|
|||
Kubernetes apiserver authorization mode (AlwaysAllow/AlwaysDeny/ABAC/Webhook/RBAC/Node). See
|
||||
<https://kubernetes.io/docs/reference/access-authn-authz/authorization/>
|
||||
'';
|
||||
default = ["RBAC" "Node"]; # Enabling RBAC by default, although kubernetes default is AllowAllow
|
||||
type = listOf (enum ["AlwaysAllow" "AlwaysDeny" "ABAC" "Webhook" "RBAC" "Node"]);
|
||||
default = [
|
||||
"RBAC"
|
||||
"Node"
|
||||
]; # Enabling RBAC by default, although kubernetes default is AllowAllow
|
||||
type = listOf (enum [
|
||||
"AlwaysAllow"
|
||||
"AlwaysDeny"
|
||||
"ABAC"
|
||||
"Webhook"
|
||||
"RBAC"
|
||||
"Node"
|
||||
]);
|
||||
};
|
||||
|
||||
authorizationPolicy = mkOption {
|
||||
|
@ -60,7 +94,7 @@ in
|
|||
Kubernetes apiserver authorization policy file. See
|
||||
<https://kubernetes.io/docs/reference/access-authn-authz/authorization/>
|
||||
'';
|
||||
default = [];
|
||||
default = [ ];
|
||||
type = listOf attrs;
|
||||
};
|
||||
|
||||
|
@ -95,7 +129,7 @@ in
|
|||
Kubernetes admission control plugins to disable. See
|
||||
<https://kubernetes.io/docs/admin/admission-controllers/>
|
||||
'';
|
||||
default = [];
|
||||
default = [ ];
|
||||
type = listOf str;
|
||||
};
|
||||
|
||||
|
@ -107,14 +141,24 @@ in
|
|||
<https://kubernetes.io/docs/admin/admission-controllers/>
|
||||
'';
|
||||
default = [
|
||||
"NamespaceLifecycle" "LimitRanger" "ServiceAccount"
|
||||
"ResourceQuota" "DefaultStorageClass" "DefaultTolerationSeconds"
|
||||
"NamespaceLifecycle"
|
||||
"LimitRanger"
|
||||
"ServiceAccount"
|
||||
"ResourceQuota"
|
||||
"DefaultStorageClass"
|
||||
"DefaultTolerationSeconds"
|
||||
"NodeRestriction"
|
||||
];
|
||||
example = [
|
||||
"NamespaceLifecycle" "NamespaceExists" "LimitRanger"
|
||||
"SecurityContextDeny" "ServiceAccount" "ResourceQuota"
|
||||
"PodSecurityPolicy" "NodeRestriction" "DefaultStorageClass"
|
||||
"NamespaceLifecycle"
|
||||
"NamespaceExists"
|
||||
"LimitRanger"
|
||||
"SecurityContextDeny"
|
||||
"ServiceAccount"
|
||||
"ResourceQuota"
|
||||
"PodSecurityPolicy"
|
||||
"NodeRestriction"
|
||||
"DefaultStorageClass"
|
||||
];
|
||||
type = listOf str;
|
||||
};
|
||||
|
@ -122,7 +166,7 @@ in
|
|||
etcd = {
|
||||
servers = mkOption {
|
||||
description = "List of etcd servers.";
|
||||
default = ["http://127.0.0.1:2379"];
|
||||
default = [ "http://127.0.0.1:2379" ];
|
||||
type = types.listOf types.str;
|
||||
};
|
||||
|
||||
|
@ -154,7 +198,7 @@ in
|
|||
|
||||
extraSANs = mkOption {
|
||||
description = "Extra x509 Subject Alternative Names to be added to the kubernetes apiserver tls cert.";
|
||||
default = [];
|
||||
default = [ ];
|
||||
type = listOf str;
|
||||
};
|
||||
|
||||
|
@ -217,7 +261,10 @@ in
|
|||
Kubernetes apiserver storage backend.
|
||||
'';
|
||||
default = "etcd3";
|
||||
type = enum ["etcd2" "etcd3"];
|
||||
type = enum [
|
||||
"etcd2"
|
||||
"etcd3"
|
||||
];
|
||||
};
|
||||
|
||||
securePort = mkOption {
|
||||
|
@ -312,135 +359,139 @@ in
|
|||
|
||||
};
|
||||
|
||||
|
||||
###### implementation
|
||||
config = mkMerge [
|
||||
|
||||
(mkIf cfg.enable {
|
||||
systemd.services.kube-apiserver = {
|
||||
description = "Kubernetes APIServer Service";
|
||||
wantedBy = [ "kubernetes.target" ];
|
||||
after = [ "network.target" ];
|
||||
serviceConfig = {
|
||||
Slice = "kubernetes.slice";
|
||||
ExecStart = ''
|
||||
${top.package}/bin/kube-apiserver \
|
||||
--allow-privileged=${boolToString cfg.allowPrivileged} \
|
||||
--authorization-mode=${concatStringsSep "," cfg.authorizationMode} \
|
||||
${optionalString (elem "ABAC" cfg.authorizationMode)
|
||||
"--authorization-policy-file=${
|
||||
pkgs.writeText "kube-auth-policy.jsonl"
|
||||
(concatMapStringsSep "\n" (l: builtins.toJSON l) cfg.authorizationPolicy)
|
||||
}"
|
||||
} \
|
||||
${optionalString (elem "Webhook" cfg.authorizationMode)
|
||||
"--authorization-webhook-config-file=${cfg.webhookConfig}"
|
||||
} \
|
||||
--bind-address=${cfg.bindAddress} \
|
||||
${optionalString (cfg.advertiseAddress != null)
|
||||
"--advertise-address=${cfg.advertiseAddress}"} \
|
||||
${optionalString (cfg.clientCaFile != null)
|
||||
"--client-ca-file=${cfg.clientCaFile}"} \
|
||||
--disable-admission-plugins=${concatStringsSep "," cfg.disableAdmissionPlugins} \
|
||||
--enable-admission-plugins=${concatStringsSep "," cfg.enableAdmissionPlugins} \
|
||||
--etcd-servers=${concatStringsSep "," cfg.etcd.servers} \
|
||||
${optionalString (cfg.etcd.caFile != null)
|
||||
"--etcd-cafile=${cfg.etcd.caFile}"} \
|
||||
${optionalString (cfg.etcd.certFile != null)
|
||||
"--etcd-certfile=${cfg.etcd.certFile}"} \
|
||||
${optionalString (cfg.etcd.keyFile != null)
|
||||
"--etcd-keyfile=${cfg.etcd.keyFile}"} \
|
||||
${optionalString (cfg.featureGates != {})
|
||||
"--feature-gates=${(concatStringsSep "," (builtins.attrValues (mapAttrs (n: v: "${n}=${trivial.boolToString v}") cfg.featureGates)))}"} \
|
||||
${optionalString (cfg.basicAuthFile != null)
|
||||
"--basic-auth-file=${cfg.basicAuthFile}"} \
|
||||
${optionalString (cfg.kubeletClientCaFile != null)
|
||||
"--kubelet-certificate-authority=${cfg.kubeletClientCaFile}"} \
|
||||
${optionalString (cfg.kubeletClientCertFile != null)
|
||||
"--kubelet-client-certificate=${cfg.kubeletClientCertFile}"} \
|
||||
${optionalString (cfg.kubeletClientKeyFile != null)
|
||||
"--kubelet-client-key=${cfg.kubeletClientKeyFile}"} \
|
||||
${optionalString (cfg.preferredAddressTypes != null)
|
||||
"--kubelet-preferred-address-types=${cfg.preferredAddressTypes}"} \
|
||||
${optionalString (cfg.proxyClientCertFile != null)
|
||||
"--proxy-client-cert-file=${cfg.proxyClientCertFile}"} \
|
||||
${optionalString (cfg.proxyClientKeyFile != null)
|
||||
"--proxy-client-key-file=${cfg.proxyClientKeyFile}"} \
|
||||
${optionalString (cfg.runtimeConfig != "")
|
||||
"--runtime-config=${cfg.runtimeConfig}"} \
|
||||
--secure-port=${toString cfg.securePort} \
|
||||
--api-audiences=${toString cfg.apiAudiences} \
|
||||
--service-account-issuer=${toString cfg.serviceAccountIssuer} \
|
||||
--service-account-signing-key-file=${cfg.serviceAccountSigningKeyFile} \
|
||||
--service-account-key-file=${cfg.serviceAccountKeyFile} \
|
||||
--service-cluster-ip-range=${cfg.serviceClusterIpRange} \
|
||||
--storage-backend=${cfg.storageBackend} \
|
||||
${optionalString (cfg.tlsCertFile != null)
|
||||
"--tls-cert-file=${cfg.tlsCertFile}"} \
|
||||
${optionalString (cfg.tlsKeyFile != null)
|
||||
"--tls-private-key-file=${cfg.tlsKeyFile}"} \
|
||||
${optionalString (cfg.tokenAuthFile != null)
|
||||
"--token-auth-file=${cfg.tokenAuthFile}"} \
|
||||
${optionalString (cfg.verbosity != null) "--v=${toString cfg.verbosity}"} \
|
||||
${cfg.extraOpts}
|
||||
'';
|
||||
WorkingDirectory = top.dataDir;
|
||||
User = "kubernetes";
|
||||
Group = "kubernetes";
|
||||
AmbientCapabilities = "cap_net_bind_service";
|
||||
Restart = "on-failure";
|
||||
RestartSec = 5;
|
||||
};
|
||||
|
||||
unitConfig = {
|
||||
StartLimitIntervalSec = 0;
|
||||
};
|
||||
systemd.services.kube-apiserver = {
|
||||
description = "Kubernetes APIServer Service";
|
||||
wantedBy = [ "kubernetes.target" ];
|
||||
after = [ "network.target" ];
|
||||
serviceConfig = {
|
||||
Slice = "kubernetes.slice";
|
||||
ExecStart = ''
|
||||
${top.package}/bin/kube-apiserver \
|
||||
--allow-privileged=${boolToString cfg.allowPrivileged} \
|
||||
--authorization-mode=${concatStringsSep "," cfg.authorizationMode} \
|
||||
${optionalString (elem "ABAC" cfg.authorizationMode) "--authorization-policy-file=${pkgs.writeText "kube-auth-policy.jsonl" (concatMapStringsSep "\n" (l: builtins.toJSON l) cfg.authorizationPolicy)}"} \
|
||||
${optionalString (elem "Webhook" cfg.authorizationMode) "--authorization-webhook-config-file=${cfg.webhookConfig}"} \
|
||||
--bind-address=${cfg.bindAddress} \
|
||||
${optionalString (cfg.advertiseAddress != null) "--advertise-address=${cfg.advertiseAddress}"} \
|
||||
${optionalString (cfg.clientCaFile != null) "--client-ca-file=${cfg.clientCaFile}"} \
|
||||
--disable-admission-plugins=${concatStringsSep "," cfg.disableAdmissionPlugins} \
|
||||
--enable-admission-plugins=${concatStringsSep "," cfg.enableAdmissionPlugins} \
|
||||
--etcd-servers=${concatStringsSep "," cfg.etcd.servers} \
|
||||
${optionalString (cfg.etcd.caFile != null) "--etcd-cafile=${cfg.etcd.caFile}"} \
|
||||
${optionalString (cfg.etcd.certFile != null) "--etcd-certfile=${cfg.etcd.certFile}"} \
|
||||
${optionalString (cfg.etcd.keyFile != null) "--etcd-keyfile=${cfg.etcd.keyFile}"} \
|
||||
${
|
||||
optionalString (cfg.featureGates != { })
|
||||
"--feature-gates=${
|
||||
(concatStringsSep "," (
|
||||
builtins.attrValues (mapAttrs (n: v: "${n}=${trivial.boolToString v}") cfg.featureGates)
|
||||
))
|
||||
}"
|
||||
} \
|
||||
${optionalString (cfg.basicAuthFile != null) "--basic-auth-file=${cfg.basicAuthFile}"} \
|
||||
${
|
||||
optionalString (
|
||||
cfg.kubeletClientCaFile != null
|
||||
) "--kubelet-certificate-authority=${cfg.kubeletClientCaFile}"
|
||||
} \
|
||||
${
|
||||
optionalString (
|
||||
cfg.kubeletClientCertFile != null
|
||||
) "--kubelet-client-certificate=${cfg.kubeletClientCertFile}"
|
||||
} \
|
||||
${
|
||||
optionalString (cfg.kubeletClientKeyFile != null) "--kubelet-client-key=${cfg.kubeletClientKeyFile}"
|
||||
} \
|
||||
${
|
||||
optionalString (
|
||||
cfg.preferredAddressTypes != null
|
||||
) "--kubelet-preferred-address-types=${cfg.preferredAddressTypes}"
|
||||
} \
|
||||
${
|
||||
optionalString (
|
||||
cfg.proxyClientCertFile != null
|
||||
) "--proxy-client-cert-file=${cfg.proxyClientCertFile}"
|
||||
} \
|
||||
${
|
||||
optionalString (cfg.proxyClientKeyFile != null) "--proxy-client-key-file=${cfg.proxyClientKeyFile}"
|
||||
} \
|
||||
${optionalString (cfg.runtimeConfig != "") "--runtime-config=${cfg.runtimeConfig}"} \
|
||||
--secure-port=${toString cfg.securePort} \
|
||||
--api-audiences=${toString cfg.apiAudiences} \
|
||||
--service-account-issuer=${toString cfg.serviceAccountIssuer} \
|
||||
--service-account-signing-key-file=${cfg.serviceAccountSigningKeyFile} \
|
||||
--service-account-key-file=${cfg.serviceAccountKeyFile} \
|
||||
--service-cluster-ip-range=${cfg.serviceClusterIpRange} \
|
||||
--storage-backend=${cfg.storageBackend} \
|
||||
${optionalString (cfg.tlsCertFile != null) "--tls-cert-file=${cfg.tlsCertFile}"} \
|
||||
${optionalString (cfg.tlsKeyFile != null) "--tls-private-key-file=${cfg.tlsKeyFile}"} \
|
||||
${optionalString (cfg.tokenAuthFile != null) "--token-auth-file=${cfg.tokenAuthFile}"} \
|
||||
${optionalString (cfg.verbosity != null) "--v=${toString cfg.verbosity}"} \
|
||||
${cfg.extraOpts}
|
||||
'';
|
||||
WorkingDirectory = top.dataDir;
|
||||
User = "kubernetes";
|
||||
Group = "kubernetes";
|
||||
AmbientCapabilities = "cap_net_bind_service";
|
||||
Restart = "on-failure";
|
||||
RestartSec = 5;
|
||||
};
|
||||
|
||||
services.etcd = {
|
||||
clientCertAuth = mkDefault true;
|
||||
peerClientCertAuth = mkDefault true;
|
||||
listenClientUrls = mkDefault ["https://0.0.0.0:2379"];
|
||||
listenPeerUrls = mkDefault ["https://0.0.0.0:2380"];
|
||||
advertiseClientUrls = mkDefault ["https://${top.masterAddress}:2379"];
|
||||
initialCluster = mkDefault ["${top.masterAddress}=https://${top.masterAddress}:2380"];
|
||||
name = mkDefault top.masterAddress;
|
||||
initialAdvertisePeerUrls = mkDefault ["https://${top.masterAddress}:2380"];
|
||||
unitConfig = {
|
||||
StartLimitIntervalSec = 0;
|
||||
};
|
||||
};
|
||||
|
||||
services.kubernetes.addonManager.bootstrapAddons = mkIf isRBACEnabled {
|
||||
services.etcd = {
|
||||
clientCertAuth = mkDefault true;
|
||||
peerClientCertAuth = mkDefault true;
|
||||
listenClientUrls = mkDefault [ "https://0.0.0.0:2379" ];
|
||||
listenPeerUrls = mkDefault [ "https://0.0.0.0:2380" ];
|
||||
advertiseClientUrls = mkDefault [ "https://${top.masterAddress}:2379" ];
|
||||
initialCluster = mkDefault [ "${top.masterAddress}=https://${top.masterAddress}:2380" ];
|
||||
name = mkDefault top.masterAddress;
|
||||
initialAdvertisePeerUrls = mkDefault [ "https://${top.masterAddress}:2380" ];
|
||||
};
|
||||
|
||||
apiserver-kubelet-api-admin-crb = {
|
||||
apiVersion = "rbac.authorization.k8s.io/v1";
|
||||
kind = "ClusterRoleBinding";
|
||||
metadata = {
|
||||
name = "system:kube-apiserver:kubelet-api-admin";
|
||||
};
|
||||
roleRef = {
|
||||
apiGroup = "rbac.authorization.k8s.io";
|
||||
kind = "ClusterRole";
|
||||
name = "system:kubelet-api-admin";
|
||||
};
|
||||
subjects = [{
|
||||
services.kubernetes.addonManager.bootstrapAddons = mkIf isRBACEnabled {
|
||||
|
||||
apiserver-kubelet-api-admin-crb = {
|
||||
apiVersion = "rbac.authorization.k8s.io/v1";
|
||||
kind = "ClusterRoleBinding";
|
||||
metadata = {
|
||||
name = "system:kube-apiserver:kubelet-api-admin";
|
||||
};
|
||||
roleRef = {
|
||||
apiGroup = "rbac.authorization.k8s.io";
|
||||
kind = "ClusterRole";
|
||||
name = "system:kubelet-api-admin";
|
||||
};
|
||||
subjects = [
|
||||
{
|
||||
kind = "User";
|
||||
name = "system:kube-apiserver";
|
||||
}];
|
||||
};
|
||||
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
services.kubernetes.pki.certs = with top.lib; {
|
||||
apiServer = mkCert {
|
||||
name = "kube-apiserver";
|
||||
CN = "kubernetes";
|
||||
hosts = [
|
||||
"kubernetes.default.svc"
|
||||
"kubernetes.default.svc.${top.addons.dns.clusterDomain}"
|
||||
cfg.advertiseAddress
|
||||
top.masterAddress
|
||||
apiserverServiceIP
|
||||
"127.0.0.1"
|
||||
] ++ cfg.extraSANs;
|
||||
"kubernetes.default.svc"
|
||||
"kubernetes.default.svc.${top.addons.dns.clusterDomain}"
|
||||
cfg.advertiseAddress
|
||||
top.masterAddress
|
||||
apiserverServiceIP
|
||||
"127.0.0.1"
|
||||
] ++ cfg.extraSANs;
|
||||
action = "systemctl restart kube-apiserver.service";
|
||||
};
|
||||
apiserverProxyClient = mkCert {
|
||||
|
@ -470,11 +521,11 @@ in
|
|||
name = "etcd";
|
||||
CN = top.masterAddress;
|
||||
hosts = [
|
||||
"etcd.local"
|
||||
"etcd.${top.addons.dns.clusterDomain}"
|
||||
top.masterAddress
|
||||
cfg.advertiseAddress
|
||||
];
|
||||
"etcd.local"
|
||||
"etcd.${top.addons.dns.clusterDomain}"
|
||||
top.masterAddress
|
||||
cfg.advertiseAddress
|
||||
];
|
||||
privateKeyOwner = "etcd";
|
||||
action = "systemctl restart etcd.service";
|
||||
};
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
{ config, lib, options, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
options,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
with lib;
|
||||
|
||||
|
@ -9,7 +15,10 @@ let
|
|||
in
|
||||
{
|
||||
imports = [
|
||||
(mkRenamedOptionModule [ "services" "kubernetes" "proxy" "address" ] ["services" "kubernetes" "proxy" "bindAddress"])
|
||||
(mkRenamedOptionModule
|
||||
[ "services" "kubernetes" "proxy" "address" ]
|
||||
[ "services" "kubernetes" "proxy" "bindAddress" ]
|
||||
)
|
||||
];
|
||||
|
||||
###### interface
|
||||
|
@ -62,16 +71,24 @@ in
|
|||
description = "Kubernetes Proxy Service";
|
||||
wantedBy = [ "kubernetes.target" ];
|
||||
after = [ "kube-apiserver.service" ];
|
||||
path = with pkgs; [ iptables conntrack-tools ];
|
||||
path = with pkgs; [
|
||||
iptables
|
||||
conntrack-tools
|
||||
];
|
||||
serviceConfig = {
|
||||
Slice = "kubernetes.slice";
|
||||
ExecStart = ''
|
||||
${top.package}/bin/kube-proxy \
|
||||
--bind-address=${cfg.bindAddress} \
|
||||
${optionalString (top.clusterCidr!=null)
|
||||
"--cluster-cidr=${top.clusterCidr}"} \
|
||||
${optionalString (cfg.featureGates != {})
|
||||
"--feature-gates=${concatStringsSep "," (builtins.attrValues (mapAttrs (n: v: "${n}=${trivial.boolToString v}") cfg.featureGates))}"} \
|
||||
${optionalString (top.clusterCidr != null) "--cluster-cidr=${top.clusterCidr}"} \
|
||||
${
|
||||
optionalString (cfg.featureGates != { })
|
||||
"--feature-gates=${
|
||||
concatStringsSep "," (
|
||||
builtins.attrValues (mapAttrs (n: v: "${n}=${trivial.boolToString v}") cfg.featureGates)
|
||||
)
|
||||
}"
|
||||
} \
|
||||
--hostname-override=${cfg.hostname} \
|
||||
--kubeconfig=${top.lib.mkKubeConfig "kube-proxy" cfg.kubeconfig} \
|
||||
${optionalString (cfg.verbosity != null) "--v=${toString cfg.verbosity}"} \
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
{ config, lib, options, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
options,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
|
||||
cfg = config.services.slurm;
|
||||
|
@ -7,51 +13,51 @@ let
|
|||
|
||||
defaultUser = "slurm";
|
||||
|
||||
configFile = pkgs.writeTextDir "slurm.conf"
|
||||
''
|
||||
ClusterName=${cfg.clusterName}
|
||||
StateSaveLocation=${cfg.stateSaveLocation}
|
||||
SlurmUser=${cfg.user}
|
||||
${lib.optionalString (cfg.controlMachine != null) "controlMachine=${cfg.controlMachine}"}
|
||||
${lib.optionalString (cfg.controlAddr != null) "controlAddr=${cfg.controlAddr}"}
|
||||
${toString (map (x: "NodeName=${x}\n") cfg.nodeName)}
|
||||
${toString (map (x: "PartitionName=${x}\n") cfg.partitionName)}
|
||||
PlugStackConfig=${plugStackConfig}/plugstack.conf
|
||||
ProctrackType=${cfg.procTrackType}
|
||||
${cfg.extraConfig}
|
||||
'';
|
||||
configFile = pkgs.writeTextDir "slurm.conf" ''
|
||||
ClusterName=${cfg.clusterName}
|
||||
StateSaveLocation=${cfg.stateSaveLocation}
|
||||
SlurmUser=${cfg.user}
|
||||
${lib.optionalString (cfg.controlMachine != null) "controlMachine=${cfg.controlMachine}"}
|
||||
${lib.optionalString (cfg.controlAddr != null) "controlAddr=${cfg.controlAddr}"}
|
||||
${toString (map (x: "NodeName=${x}\n") cfg.nodeName)}
|
||||
${toString (map (x: "PartitionName=${x}\n") cfg.partitionName)}
|
||||
PlugStackConfig=${plugStackConfig}/plugstack.conf
|
||||
ProctrackType=${cfg.procTrackType}
|
||||
${cfg.extraConfig}
|
||||
'';
|
||||
|
||||
plugStackConfig = pkgs.writeTextDir "plugstack.conf"
|
||||
''
|
||||
${lib.optionalString cfg.enableSrunX11 "optional ${pkgs.slurm-spank-x11}/lib/x11.so"}
|
||||
${cfg.extraPlugstackConfig}
|
||||
'';
|
||||
plugStackConfig = pkgs.writeTextDir "plugstack.conf" ''
|
||||
${lib.optionalString cfg.enableSrunX11 "optional ${pkgs.slurm-spank-x11}/lib/x11.so"}
|
||||
${cfg.extraPlugstackConfig}
|
||||
'';
|
||||
|
||||
cgroupConfig = pkgs.writeTextDir "cgroup.conf"
|
||||
''
|
||||
${cfg.extraCgroupConfig}
|
||||
'';
|
||||
cgroupConfig = pkgs.writeTextDir "cgroup.conf" ''
|
||||
${cfg.extraCgroupConfig}
|
||||
'';
|
||||
|
||||
mpiConf = pkgs.writeTextDir "mpi.conf"
|
||||
''
|
||||
PMIxCliTmpDirBase=${cfg.mpi.PmixCliTmpDirBase}
|
||||
${cfg.mpi.extraMpiConfig}
|
||||
'';
|
||||
mpiConf = pkgs.writeTextDir "mpi.conf" ''
|
||||
PMIxCliTmpDirBase=${cfg.mpi.PmixCliTmpDirBase}
|
||||
${cfg.mpi.extraMpiConfig}
|
||||
'';
|
||||
|
||||
slurmdbdConf = pkgs.writeText "slurmdbd.conf"
|
||||
''
|
||||
DbdHost=${cfg.dbdserver.dbdHost}
|
||||
SlurmUser=${cfg.user}
|
||||
StorageType=accounting_storage/mysql
|
||||
StorageUser=${cfg.dbdserver.storageUser}
|
||||
${cfg.dbdserver.extraConfig}
|
||||
'';
|
||||
slurmdbdConf = pkgs.writeText "slurmdbd.conf" ''
|
||||
DbdHost=${cfg.dbdserver.dbdHost}
|
||||
SlurmUser=${cfg.user}
|
||||
StorageType=accounting_storage/mysql
|
||||
StorageUser=${cfg.dbdserver.storageUser}
|
||||
${cfg.dbdserver.extraConfig}
|
||||
'';
|
||||
|
||||
# slurm expects some additional config files to be
|
||||
# in the same directory as slurm.conf
|
||||
etcSlurm = pkgs.symlinkJoin {
|
||||
name = "etc-slurm";
|
||||
paths = [ configFile cgroupConfig plugStackConfig mpiConf ] ++ cfg.extraConfigPaths;
|
||||
paths = [
|
||||
configFile
|
||||
cgroupConfig
|
||||
plugStackConfig
|
||||
mpiConf
|
||||
] ++ cfg.extraConfigPaths;
|
||||
};
|
||||
in
|
||||
|
||||
|
@ -134,11 +140,13 @@ in
|
|||
'';
|
||||
};
|
||||
|
||||
package = lib.mkPackageOption pkgs "slurm" {
|
||||
example = "slurm-full";
|
||||
} // {
|
||||
default = pkgs.slurm.override { enableX11 = ! cfg.enableSrunX11; };
|
||||
};
|
||||
package =
|
||||
lib.mkPackageOption pkgs "slurm" {
|
||||
example = "slurm-full";
|
||||
}
|
||||
// {
|
||||
default = pkgs.slurm.override { enableX11 = !cfg.enableSrunX11; };
|
||||
};
|
||||
|
||||
controlMachine = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
|
@ -173,7 +181,7 @@ in
|
|||
|
||||
nodeName = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [];
|
||||
default = [ ];
|
||||
example = lib.literalExpression ''[ "linux[1-32] CPUs=1 State=UNKNOWN" ];'';
|
||||
description = ''
|
||||
Name that SLURM uses to refer to a node (or base partition for BlueGene
|
||||
|
@ -184,7 +192,7 @@ in
|
|||
|
||||
partitionName = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [];
|
||||
default = [ ];
|
||||
example = lib.literalExpression ''[ "debug Nodes=linux[1-32] Default=YES MaxTime=INFINITE State=UP" ];'';
|
||||
description = ''
|
||||
Name by which the partition may be referenced. Note that now you have
|
||||
|
@ -285,7 +293,7 @@ in
|
|||
|
||||
extraConfigPaths = lib.mkOption {
|
||||
type = with lib.types; listOf path;
|
||||
default = [];
|
||||
default = [ ];
|
||||
description = ''
|
||||
Slurm expects config files for plugins in the same path
|
||||
as `slurm.conf`. Add extra nix store
|
||||
|
@ -354,107 +362,132 @@ in
|
|||
'';
|
||||
};
|
||||
|
||||
in lib.mkIf ( cfg.enableStools ||
|
||||
cfg.client.enable ||
|
||||
cfg.server.enable ||
|
||||
cfg.dbdserver.enable ) {
|
||||
in
|
||||
lib.mkIf (cfg.enableStools || cfg.client.enable || cfg.server.enable || cfg.dbdserver.enable) {
|
||||
|
||||
environment.systemPackages = [ wrappedSlurm ];
|
||||
environment.systemPackages = [ wrappedSlurm ];
|
||||
|
||||
services.munge.enable = lib.mkDefault true;
|
||||
services.munge.enable = lib.mkDefault true;
|
||||
|
||||
# use a static uid as default to ensure it is the same on all nodes
|
||||
users.users.slurm = lib.mkIf (cfg.user == defaultUser) {
|
||||
name = defaultUser;
|
||||
group = "slurm";
|
||||
uid = config.ids.uids.slurm;
|
||||
};
|
||||
# use a static uid as default to ensure it is the same on all nodes
|
||||
users.users.slurm = lib.mkIf (cfg.user == defaultUser) {
|
||||
name = defaultUser;
|
||||
group = "slurm";
|
||||
uid = config.ids.uids.slurm;
|
||||
};
|
||||
|
||||
users.groups.slurm.gid = config.ids.uids.slurm;
|
||||
users.groups.slurm.gid = config.ids.uids.slurm;
|
||||
|
||||
systemd.services.slurmd = lib.mkIf (cfg.client.enable) {
|
||||
path = with pkgs; [ wrappedSlurm coreutils ]
|
||||
++ lib.optional cfg.enableSrunX11 slurm-spank-x11;
|
||||
systemd.services.slurmd = lib.mkIf (cfg.client.enable) {
|
||||
path =
|
||||
with pkgs;
|
||||
[
|
||||
wrappedSlurm
|
||||
coreutils
|
||||
]
|
||||
++ lib.optional cfg.enableSrunX11 slurm-spank-x11;
|
||||
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [
|
||||
"systemd-tmpfiles-clean.service"
|
||||
"munge.service"
|
||||
"network-online.target"
|
||||
"remote-fs.target"
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [
|
||||
"systemd-tmpfiles-clean.service"
|
||||
"munge.service"
|
||||
"network-online.target"
|
||||
"remote-fs.target"
|
||||
];
|
||||
wants = [ "network-online.target" ];
|
||||
|
||||
serviceConfig = {
|
||||
Type = "forking";
|
||||
KillMode = "process";
|
||||
ExecStart = "${wrappedSlurm}/bin/slurmd";
|
||||
PIDFile = "/run/slurmd.pid";
|
||||
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
|
||||
LimitMEMLOCK = "infinity";
|
||||
Delegate = "Yes";
|
||||
};
|
||||
};
|
||||
|
||||
systemd.tmpfiles.rules = lib.optionals cfg.client.enable [
|
||||
"d /var/spool/slurmd 755 root root -"
|
||||
"d ${cfg.mpi.PmixCliTmpDirBase} 755 root root -"
|
||||
];
|
||||
wants = [ "network-online.target" ];
|
||||
|
||||
serviceConfig = {
|
||||
Type = "forking";
|
||||
KillMode = "process";
|
||||
ExecStart = "${wrappedSlurm}/bin/slurmd";
|
||||
PIDFile = "/run/slurmd.pid";
|
||||
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
|
||||
LimitMEMLOCK = "infinity";
|
||||
Delegate="Yes";
|
||||
};
|
||||
};
|
||||
services.openssh.settings.X11Forwarding = lib.mkIf cfg.client.enable (lib.mkDefault true);
|
||||
|
||||
systemd.tmpfiles.rules = lib.optionals cfg.client.enable [
|
||||
"d /var/spool/slurmd 755 root root -"
|
||||
"d ${cfg.mpi.PmixCliTmpDirBase} 755 root root -"
|
||||
];
|
||||
systemd.services.slurmctld = lib.mkIf (cfg.server.enable) {
|
||||
path =
|
||||
with pkgs;
|
||||
[
|
||||
wrappedSlurm
|
||||
munge
|
||||
coreutils
|
||||
]
|
||||
++ lib.optional cfg.enableSrunX11 slurm-spank-x11;
|
||||
|
||||
services.openssh.settings.X11Forwarding = lib.mkIf cfg.client.enable (lib.mkDefault true);
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [
|
||||
"network.target"
|
||||
"munged.service"
|
||||
];
|
||||
requires = [ "munged.service" ];
|
||||
|
||||
systemd.services.slurmctld = lib.mkIf (cfg.server.enable) {
|
||||
path = with pkgs; [ wrappedSlurm munge coreutils ]
|
||||
++ lib.optional cfg.enableSrunX11 slurm-spank-x11;
|
||||
serviceConfig = {
|
||||
Type = "forking";
|
||||
ExecStart = "${wrappedSlurm}/bin/slurmctld";
|
||||
PIDFile = "/run/slurmctld.pid";
|
||||
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
|
||||
};
|
||||
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "network.target" "munged.service" ];
|
||||
requires = [ "munged.service" ];
|
||||
|
||||
serviceConfig = {
|
||||
Type = "forking";
|
||||
ExecStart = "${wrappedSlurm}/bin/slurmctld";
|
||||
PIDFile = "/run/slurmctld.pid";
|
||||
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
|
||||
preStart = ''
|
||||
mkdir -p ${cfg.stateSaveLocation}
|
||||
chown -R ${cfg.user}:slurm ${cfg.stateSaveLocation}
|
||||
'';
|
||||
};
|
||||
|
||||
preStart = ''
|
||||
mkdir -p ${cfg.stateSaveLocation}
|
||||
chown -R ${cfg.user}:slurm ${cfg.stateSaveLocation}
|
||||
'';
|
||||
systemd.services.slurmdbd =
|
||||
let
|
||||
# slurm strips the last component off the path
|
||||
configPath = "$RUNTIME_DIRECTORY/slurmdbd.conf";
|
||||
in
|
||||
lib.mkIf (cfg.dbdserver.enable) {
|
||||
path = with pkgs; [
|
||||
wrappedSlurm
|
||||
munge
|
||||
coreutils
|
||||
];
|
||||
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [
|
||||
"network.target"
|
||||
"munged.service"
|
||||
"mysql.service"
|
||||
];
|
||||
requires = [
|
||||
"munged.service"
|
||||
"mysql.service"
|
||||
];
|
||||
|
||||
preStart = ''
|
||||
install -m 600 -o ${cfg.user} -T ${slurmdbdConf} ${configPath}
|
||||
${lib.optionalString (cfg.dbdserver.storagePassFile != null) ''
|
||||
echo "StoragePass=$(cat ${cfg.dbdserver.storagePassFile})" \
|
||||
>> ${configPath}
|
||||
''}
|
||||
'';
|
||||
|
||||
script = ''
|
||||
export SLURM_CONF=${configPath}
|
||||
exec ${cfg.package}/bin/slurmdbd -D
|
||||
'';
|
||||
|
||||
serviceConfig = {
|
||||
RuntimeDirectory = "slurmdbd";
|
||||
Type = "simple";
|
||||
PIDFile = "/run/slurmdbd.pid";
|
||||
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
systemd.services.slurmdbd = let
|
||||
# slurm strips the last component off the path
|
||||
configPath = "$RUNTIME_DIRECTORY/slurmdbd.conf";
|
||||
in lib.mkIf (cfg.dbdserver.enable) {
|
||||
path = with pkgs; [ wrappedSlurm munge coreutils ];
|
||||
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "network.target" "munged.service" "mysql.service" ];
|
||||
requires = [ "munged.service" "mysql.service" ];
|
||||
|
||||
preStart = ''
|
||||
install -m 600 -o ${cfg.user} -T ${slurmdbdConf} ${configPath}
|
||||
${lib.optionalString (cfg.dbdserver.storagePassFile != null) ''
|
||||
echo "StoragePass=$(cat ${cfg.dbdserver.storagePassFile})" \
|
||||
>> ${configPath}
|
||||
''}
|
||||
'';
|
||||
|
||||
script = ''
|
||||
export SLURM_CONF=${configPath}
|
||||
exec ${cfg.package}/bin/slurmdbd -D
|
||||
'';
|
||||
|
||||
serviceConfig = {
|
||||
RuntimeDirectory = "slurmdbd";
|
||||
Type = "simple";
|
||||
PIDFile = "/run/slurmdbd.pid";
|
||||
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue