mirror of
https://github.com/NixOS/nixpkgs.git
synced 2025-06-10 03:23:29 +03:00
Merge remote-tracking branch 'upstream/staging-next' into staging
This commit is contained in:
commit
e52d633a63
1744 changed files with 988732 additions and 514579 deletions
|
@ -248,3 +248,6 @@ da9a092c34cef6947d7aee2b134f61df45171631
|
||||||
# python-packages: sort with keep-sorted
|
# python-packages: sort with keep-sorted
|
||||||
fd14c067813572afc03ddbf7cdedc3eab5a59954
|
fd14c067813572afc03ddbf7cdedc3eab5a59954
|
||||||
783add849cbca228a36ffdf407e5d380dc2fe6c4
|
783add849cbca228a36ffdf407e5d380dc2fe6c4
|
||||||
|
|
||||||
|
# treewide format of all Nix files
|
||||||
|
374e6bcc403e02a35e07b650463c01a52b13a7c8 # !autorebase nix-shell --run treefmt
|
||||||
|
|
|
@ -20,7 +20,7 @@ jobs:
|
||||||
lib
|
lib
|
||||||
maintainers
|
maintainers
|
||||||
|
|
||||||
- uses: cachix/install-nix-action@02a151ada4993995686f9ed4f1be7cfbb229e56f # v31
|
- uses: cachix/install-nix-action@d1ca217b388ee87b2507a9a93bf01368bde7cec2 # v31
|
||||||
with:
|
with:
|
||||||
extra_nix_config: sandbox = true
|
extra_nix_config: sandbox = true
|
||||||
|
|
||||||
|
|
84
.github/workflows/check-nix-format.yml
vendored
84
.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.
|
||||||
# NOTE: Formatting with the RFC-style nixfmt command is not yet stable. See
|
# See https://github.com/NixOS/rfcs/pull/166.
|
||||||
# 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.
|
|
||||||
|
|
||||||
name: Check that Nix files are formatted
|
name: Check that Nix files are formatted
|
||||||
|
|
||||||
|
@ -20,80 +17,27 @@ jobs:
|
||||||
name: nixfmt-check
|
name: nixfmt-check
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
needs: get-merge-commit
|
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:
|
steps:
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
ref: ${{ needs.get-merge-commit.outputs.mergedSha }}
|
ref: ${{ needs.get-merge-commit.outputs.mergedSha }}
|
||||||
# Fetches the merge commit and its parents
|
|
||||||
fetch-depth: 2
|
|
||||||
|
|
||||||
- name: Checking out target branch
|
- uses: cachix/install-nix-action@d1ca217b388ee87b2507a9a93bf01368bde7cec2 # v31
|
||||||
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:
|
with:
|
||||||
extra_nix_config: sandbox = true
|
extra_nix_config: sandbox = true
|
||||||
nix_path: nixpkgs=${{ env.url }}
|
|
||||||
|
|
||||||
- name: Install nixfmt
|
- name: Check that Nix files are formatted
|
||||||
run: "nix-env -f '<nixpkgs>' -iAP nixfmt-rfc-style"
|
|
||||||
|
|
||||||
- name: Check that Nix files are formatted according to the RFC style
|
|
||||||
run: |
|
run: |
|
||||||
unformattedFiles=()
|
# Note that it's fine to run this on untrusted code because:
|
||||||
|
# - There's no secrets accessible here
|
||||||
# TODO: Make this more parallel
|
# - The build is sandboxed
|
||||||
|
if ! nix-build ci -A fmt.check; then
|
||||||
# Loop through all Nix files touched by the PR
|
echo "Some Nix files are not properly formatted"
|
||||||
while readarray -d '' -n 2 entry && (( ${#entry[@]} != 0 )); do
|
echo "Please format them by going to the Nixpkgs root directory and running one of:"
|
||||||
type=${entry[0]}
|
echo " nix-shell --run treefmt"
|
||||||
file=${entry[1]}
|
echo " nix develop --command treefmt"
|
||||||
case $type in
|
echo " nix fmt"
|
||||||
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
|
|
||||||
echo "Make sure your branch is up to date with master; rebase if not."
|
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"
|
echo "If you're having trouble, please ping @NixOS/nix-formatting"
|
||||||
exit 1
|
exit 1
|
||||||
|
|
2
.github/workflows/check-nixf-tidy.yml
vendored
2
.github/workflows/check-nixf-tidy.yml
vendored
|
@ -34,7 +34,7 @@ jobs:
|
||||||
rev=$(jq -r .rev ci/pinned-nixpkgs.json)
|
rev=$(jq -r .rev ci/pinned-nixpkgs.json)
|
||||||
echo "url=https://github.com/NixOS/nixpkgs/archive/$rev.tar.gz" >> "$GITHUB_ENV"
|
echo "url=https://github.com/NixOS/nixpkgs/archive/$rev.tar.gz" >> "$GITHUB_ENV"
|
||||||
|
|
||||||
- uses: cachix/install-nix-action@02a151ada4993995686f9ed4f1be7cfbb229e56f # v31
|
- uses: cachix/install-nix-action@d1ca217b388ee87b2507a9a93bf01368bde7cec2 # v31
|
||||||
with:
|
with:
|
||||||
extra_nix_config: sandbox = true
|
extra_nix_config: sandbox = true
|
||||||
nix_path: nixpkgs=${{ env.url }}
|
nix_path: nixpkgs=${{ env.url }}
|
||||||
|
|
2
.github/workflows/check-shell.yml
vendored
2
.github/workflows/check-shell.yml
vendored
|
@ -27,7 +27,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
ref: refs/pull/${{ github.event.pull_request.number }}/merge
|
ref: refs/pull/${{ github.event.pull_request.number }}/merge
|
||||||
|
|
||||||
- uses: cachix/install-nix-action@02a151ada4993995686f9ed4f1be7cfbb229e56f # v31
|
- uses: cachix/install-nix-action@d1ca217b388ee87b2507a9a93bf01368bde7cec2 # v31
|
||||||
|
|
||||||
- name: Build shell
|
- name: Build shell
|
||||||
run: nix-build shell.nix
|
run: nix-build shell.nix
|
||||||
|
|
4
.github/workflows/codeowners-v2.yml
vendored
4
.github/workflows/codeowners-v2.yml
vendored
|
@ -45,7 +45,7 @@ jobs:
|
||||||
needs: get-merge-commit
|
needs: get-merge-commit
|
||||||
if: github.repository_owner == 'NixOS' && needs.get-merge-commit.outputs.mergedSha
|
if: github.repository_owner == 'NixOS' && needs.get-merge-commit.outputs.mergedSha
|
||||||
steps:
|
steps:
|
||||||
- uses: cachix/install-nix-action@02a151ada4993995686f9ed4f1be7cfbb229e56f # v31
|
- uses: cachix/install-nix-action@d1ca217b388ee87b2507a9a93bf01368bde7cec2 # v31
|
||||||
|
|
||||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||||
with:
|
with:
|
||||||
|
@ -90,7 +90,7 @@ jobs:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
if: github.repository_owner == 'NixOS'
|
if: github.repository_owner == 'NixOS'
|
||||||
steps:
|
steps:
|
||||||
- uses: cachix/install-nix-action@02a151ada4993995686f9ed4f1be7cfbb229e56f # v31
|
- uses: cachix/install-nix-action@d1ca217b388ee87b2507a9a93bf01368bde7cec2 # v31
|
||||||
|
|
||||||
# Important: Because we use pull_request_target, this checks out the base branch of the PR, not the PR head.
|
# Important: Because we use pull_request_target, this checks out the base branch of the PR, not the PR head.
|
||||||
# This is intentional, because we need to request the review of owners as declared in the base branch.
|
# This is intentional, because we need to request the review of owners as declared in the base branch.
|
||||||
|
|
2
.github/workflows/editorconfig-v2.yml
vendored
2
.github/workflows/editorconfig-v2.yml
vendored
|
@ -32,7 +32,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
ref: ${{ needs.get-merge-commit.outputs.mergedSha }}
|
ref: ${{ needs.get-merge-commit.outputs.mergedSha }}
|
||||||
|
|
||||||
- uses: cachix/install-nix-action@02a151ada4993995686f9ed4f1be7cfbb229e56f # v31
|
- uses: cachix/install-nix-action@d1ca217b388ee87b2507a9a93bf01368bde7cec2 # v31
|
||||||
with:
|
with:
|
||||||
# nixpkgs commit is pinned so that it doesn't break
|
# nixpkgs commit is pinned so that it doesn't break
|
||||||
# editorconfig-checker 2.4.0
|
# editorconfig-checker 2.4.0
|
||||||
|
|
2
.github/workflows/eval-lib-tests.yml
vendored
2
.github/workflows/eval-lib-tests.yml
vendored
|
@ -22,7 +22,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
ref: ${{ needs.get-merge-commit.outputs.mergedSha }}
|
ref: ${{ needs.get-merge-commit.outputs.mergedSha }}
|
||||||
|
|
||||||
- uses: cachix/install-nix-action@02a151ada4993995686f9ed4f1be7cfbb229e56f # v31
|
- uses: cachix/install-nix-action@d1ca217b388ee87b2507a9a93bf01368bde7cec2 # v31
|
||||||
with:
|
with:
|
||||||
extra_nix_config: sandbox = true
|
extra_nix_config: sandbox = true
|
||||||
|
|
||||||
|
|
10
.github/workflows/eval.yml
vendored
10
.github/workflows/eval.yml
vendored
|
@ -43,7 +43,7 @@ jobs:
|
||||||
echo "targetSha=$targetSha" >> "$GITHUB_OUTPUT"
|
echo "targetSha=$targetSha" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
- name: Install Nix
|
- name: Install Nix
|
||||||
uses: cachix/install-nix-action@02a151ada4993995686f9ed4f1be7cfbb229e56f # v31
|
uses: cachix/install-nix-action@d1ca217b388ee87b2507a9a93bf01368bde7cec2 # v31
|
||||||
with:
|
with:
|
||||||
extra_nix_config: sandbox = true
|
extra_nix_config: sandbox = true
|
||||||
|
|
||||||
|
@ -71,7 +71,7 @@ jobs:
|
||||||
path: nixpkgs
|
path: nixpkgs
|
||||||
|
|
||||||
- name: Install Nix
|
- name: Install Nix
|
||||||
uses: cachix/install-nix-action@02a151ada4993995686f9ed4f1be7cfbb229e56f # v31
|
uses: cachix/install-nix-action@d1ca217b388ee87b2507a9a93bf01368bde7cec2 # v31
|
||||||
with:
|
with:
|
||||||
extra_nix_config: sandbox = true
|
extra_nix_config: sandbox = true
|
||||||
|
|
||||||
|
@ -108,7 +108,7 @@ jobs:
|
||||||
path: nixpkgs
|
path: nixpkgs
|
||||||
|
|
||||||
- name: Install Nix
|
- name: Install Nix
|
||||||
uses: cachix/install-nix-action@02a151ada4993995686f9ed4f1be7cfbb229e56f # v31
|
uses: cachix/install-nix-action@d1ca217b388ee87b2507a9a93bf01368bde7cec2 # v31
|
||||||
with:
|
with:
|
||||||
extra_nix_config: sandbox = true
|
extra_nix_config: sandbox = true
|
||||||
|
|
||||||
|
@ -149,7 +149,7 @@ jobs:
|
||||||
path: nixpkgs
|
path: nixpkgs
|
||||||
|
|
||||||
- name: Install Nix
|
- name: Install Nix
|
||||||
uses: cachix/install-nix-action@02a151ada4993995686f9ed4f1be7cfbb229e56f # v31
|
uses: cachix/install-nix-action@d1ca217b388ee87b2507a9a93bf01368bde7cec2 # v31
|
||||||
with:
|
with:
|
||||||
extra_nix_config: sandbox = true
|
extra_nix_config: sandbox = true
|
||||||
|
|
||||||
|
@ -254,7 +254,7 @@ jobs:
|
||||||
path: comparison
|
path: comparison
|
||||||
|
|
||||||
- name: Install Nix
|
- name: Install Nix
|
||||||
uses: cachix/install-nix-action@02a151ada4993995686f9ed4f1be7cfbb229e56f # v31
|
uses: cachix/install-nix-action@d1ca217b388ee87b2507a9a93bf01368bde7cec2 # v31
|
||||||
|
|
||||||
# Important: This workflow job runs with extra permissions,
|
# Important: This workflow job runs with extra permissions,
|
||||||
# so we need to make sure to not run untrusted code from PRs
|
# so we need to make sure to not run untrusted code from PRs
|
||||||
|
|
2
.github/workflows/keep-sorted.yml
vendored
2
.github/workflows/keep-sorted.yml
vendored
|
@ -27,7 +27,7 @@ jobs:
|
||||||
rev=$(jq -r .rev ci/pinned-nixpkgs.json)
|
rev=$(jq -r .rev ci/pinned-nixpkgs.json)
|
||||||
echo "url=https://github.com/NixOS/nixpkgs/archive/$rev.tar.gz" >> "$GITHUB_ENV"
|
echo "url=https://github.com/NixOS/nixpkgs/archive/$rev.tar.gz" >> "$GITHUB_ENV"
|
||||||
|
|
||||||
- uses: cachix/install-nix-action@08dcb3a5e62fa31e2da3d490afc4176ef55ecd72 # v30
|
- uses: cachix/install-nix-action@d1ca217b388ee87b2507a9a93bf01368bde7cec2 # v31
|
||||||
with:
|
with:
|
||||||
extra_nix_config: sandbox = true
|
extra_nix_config: sandbox = true
|
||||||
nix_path: nixpkgs=${{ env.url }}
|
nix_path: nixpkgs=${{ env.url }}
|
||||||
|
|
2
.github/workflows/manual-nixos-v2.yml
vendored
2
.github/workflows/manual-nixos-v2.yml
vendored
|
@ -33,7 +33,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
ref: refs/pull/${{ github.event.pull_request.number }}/merge
|
ref: refs/pull/${{ github.event.pull_request.number }}/merge
|
||||||
|
|
||||||
- uses: cachix/install-nix-action@02a151ada4993995686f9ed4f1be7cfbb229e56f # v31
|
- uses: cachix/install-nix-action@d1ca217b388ee87b2507a9a93bf01368bde7cec2 # v31
|
||||||
with:
|
with:
|
||||||
extra_nix_config: sandbox = true
|
extra_nix_config: sandbox = true
|
||||||
|
|
||||||
|
|
2
.github/workflows/manual-nixpkgs-v2.yml
vendored
2
.github/workflows/manual-nixpkgs-v2.yml
vendored
|
@ -20,7 +20,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
ref: refs/pull/${{ github.event.pull_request.number }}/merge
|
ref: refs/pull/${{ github.event.pull_request.number }}/merge
|
||||||
|
|
||||||
- uses: cachix/install-nix-action@02a151ada4993995686f9ed4f1be7cfbb229e56f # v31
|
- uses: cachix/install-nix-action@d1ca217b388ee87b2507a9a93bf01368bde7cec2 # v31
|
||||||
with:
|
with:
|
||||||
extra_nix_config: sandbox = true
|
extra_nix_config: sandbox = true
|
||||||
|
|
||||||
|
|
2
.github/workflows/nix-parse-v2.yml
vendored
2
.github/workflows/nix-parse-v2.yml
vendored
|
@ -32,7 +32,7 @@ jobs:
|
||||||
ref: ${{ needs.get-merge-commit.outputs.mergedSha }}
|
ref: ${{ needs.get-merge-commit.outputs.mergedSha }}
|
||||||
if: ${{ env.CHANGED_FILES && env.CHANGED_FILES != '' }}
|
if: ${{ env.CHANGED_FILES && env.CHANGED_FILES != '' }}
|
||||||
|
|
||||||
- uses: cachix/install-nix-action@02a151ada4993995686f9ed4f1be7cfbb229e56f # v31
|
- uses: cachix/install-nix-action@d1ca217b388ee87b2507a9a93bf01368bde7cec2 # v31
|
||||||
with:
|
with:
|
||||||
extra_nix_config: sandbox = true
|
extra_nix_config: sandbox = true
|
||||||
nix_path: nixpkgs=channel:nixpkgs-unstable
|
nix_path: nixpkgs=channel:nixpkgs-unstable
|
||||||
|
|
2
.github/workflows/nixpkgs-vet.yml
vendored
2
.github/workflows/nixpkgs-vet.yml
vendored
|
@ -43,7 +43,7 @@ jobs:
|
||||||
git worktree add "$target" "$(git rev-parse HEAD^1)"
|
git worktree add "$target" "$(git rev-parse HEAD^1)"
|
||||||
echo "target=$target" >> "$GITHUB_ENV"
|
echo "target=$target" >> "$GITHUB_ENV"
|
||||||
|
|
||||||
- uses: cachix/install-nix-action@02a151ada4993995686f9ed4f1be7cfbb229e56f # v31
|
- uses: cachix/install-nix-action@d1ca217b388ee87b2507a9a93bf01368bde7cec2 # v31
|
||||||
|
|
||||||
- name: Fetching the pinned tool
|
- name: Fetching the pinned tool
|
||||||
# Update the pinned version using ci/nixpkgs-vet/update-pinned-tool.sh
|
# Update the pinned version using ci/nixpkgs-vet/update-pinned-tool.sh
|
||||||
|
|
|
@ -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`.
|
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
|
### Syntax
|
||||||
|
|
||||||
- Set up [editorconfig](https://editorconfig.org/) for your editor, such that [the settings](./.editorconfig) are automatically applied.
|
- 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).
|
- 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
|
- Functions should list their expected arguments as precisely as possible. That is, write
|
||||||
|
|
||||||
```nix
|
```nix
|
||||||
|
|
|
@ -21,9 +21,52 @@ let
|
||||||
config = { };
|
config = { };
|
||||||
overlays = [ ];
|
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
|
in
|
||||||
{
|
{
|
||||||
inherit pkgs;
|
inherit pkgs fmt;
|
||||||
requestReviews = pkgs.callPackage ./request-reviews { };
|
requestReviews = pkgs.callPackage ./request-reviews { };
|
||||||
codeownersValidator = pkgs.callPackage ./codeowners-validator { };
|
codeownersValidator = pkgs.callPackage ./codeowners-validator { };
|
||||||
eval = pkgs.callPackage ./eval { };
|
eval = pkgs.callPackage ./eval { };
|
||||||
|
|
89
flake.nix
89
flake.nix
|
@ -3,17 +3,21 @@
|
||||||
{
|
{
|
||||||
description = "A collection of packages for the Nix package manager";
|
description = "A collection of packages for the Nix package manager";
|
||||||
|
|
||||||
outputs = { self }:
|
outputs =
|
||||||
|
{ self }:
|
||||||
let
|
let
|
||||||
libVersionInfoOverlay = import ./lib/flake-version-info.nix self;
|
libVersionInfoOverlay = import ./lib/flake-version-info.nix self;
|
||||||
lib = (import ./lib).extend libVersionInfoOverlay;
|
lib = (import ./lib).extend libVersionInfoOverlay;
|
||||||
|
|
||||||
forAllSystems = lib.genAttrs lib.systems.flakeExposed;
|
forAllSystems = lib.genAttrs lib.systems.flakeExposed;
|
||||||
|
|
||||||
jobs = forAllSystems (system: import ./pkgs/top-level/release.nix {
|
jobs = forAllSystems (
|
||||||
|
system:
|
||||||
|
import ./pkgs/top-level/release.nix {
|
||||||
nixpkgs = self;
|
nixpkgs = self;
|
||||||
inherit system;
|
inherit system;
|
||||||
});
|
}
|
||||||
|
);
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
@ -26,7 +30,8 @@
|
||||||
*/
|
*/
|
||||||
# DON'T USE lib.extend TO ADD NEW FUNCTIONALITY.
|
# DON'T USE lib.extend TO ADD NEW FUNCTIONALITY.
|
||||||
# THIS WAS A MISTAKE. See the warning in lib/default.nix.
|
# THIS WAS A MISTAKE. See the warning in lib/default.nix.
|
||||||
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).
|
Other NixOS-provided functionality, such as [`runTest`](https://nixos.org/manual/nixos/unstable/#sec-call-nixos-test-outside-nixos).
|
||||||
|
@ -56,7 +61,8 @@
|
||||||
- `system`: Legacy alias for `nixpkgs.hostPlatform`, but this is already set in the generated `hardware-configuration.nix`, included by `configuration.nix`.
|
- `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.
|
- `pkgs`: Legacy alias for `nixpkgs.pkgs`; use `nixpkgs.pkgs` and `nixosModules.readOnlyPkgs` instead.
|
||||||
*/
|
*/
|
||||||
nixosSystem = args:
|
nixosSystem =
|
||||||
|
args:
|
||||||
import ./nixos/lib/eval-config.nix (
|
import ./nixos/lib/eval-config.nix (
|
||||||
{
|
{
|
||||||
lib = final;
|
lib = final;
|
||||||
|
@ -72,17 +78,31 @@
|
||||||
#
|
#
|
||||||
# See: failed attempt to make pkgs.path not copy when using flakes:
|
# See: failed attempt to make pkgs.path not copy when using flakes:
|
||||||
# https://github.com/NixOS/nixpkgs/pull/153594#issuecomment-1023287913
|
# https://github.com/NixOS/nixpkgs/pull/153594#issuecomment-1023287913
|
||||||
({ config, pkgs, lib, ... }: {
|
(
|
||||||
|
{
|
||||||
|
config,
|
||||||
|
pkgs,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
{
|
||||||
config.nixpkgs.flake.source = self.outPath;
|
config.nixpkgs.flake.source = self.outPath;
|
||||||
})
|
}
|
||||||
|
)
|
||||||
];
|
];
|
||||||
} // builtins.removeAttrs args [ "modules" ]
|
}
|
||||||
|
// builtins.removeAttrs args [ "modules" ]
|
||||||
|
);
|
||||||
|
}
|
||||||
);
|
);
|
||||||
});
|
|
||||||
|
|
||||||
checks = forAllSystems (system: {
|
checks = forAllSystems (
|
||||||
|
system:
|
||||||
|
{
|
||||||
tarball = jobs.${system}.tarball;
|
tarball = jobs.${system}.tarball;
|
||||||
} // lib.optionalAttrs
|
}
|
||||||
|
//
|
||||||
|
lib.optionalAttrs
|
||||||
(
|
(
|
||||||
self.legacyPackages.${system}.stdenv.hostPlatform.isLinux
|
self.legacyPackages.${system}.stdenv.hostPlatform.isLinux
|
||||||
# Exclude power64 due to "libressl is not available on the requested hostPlatform" with hostPlatform being power64
|
# Exclude power64 due to "libressl is not available on the requested hostPlatform" with hostPlatform being power64
|
||||||
|
@ -92,34 +112,45 @@
|
||||||
# Test that ensures that the nixosSystem function can accept a lib argument
|
# 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
|
# 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>`
|
# alternatives include: `import` a file, or put a custom library in an option or in `_module.args.<libname>`
|
||||||
nixosSystemAcceptsLib = (self.lib.nixosSystem {
|
nixosSystemAcceptsLib =
|
||||||
|
(self.lib.nixosSystem {
|
||||||
pkgs = self.legacyPackages.${system};
|
pkgs = self.legacyPackages.${system};
|
||||||
lib = self.lib.extend (final: prev: {
|
lib = self.lib.extend (
|
||||||
|
final: prev: {
|
||||||
ifThisFunctionIsMissingTheTestFails = final.id;
|
ifThisFunctionIsMissingTheTestFails = final.id;
|
||||||
});
|
}
|
||||||
|
);
|
||||||
modules = [
|
modules = [
|
||||||
./nixos/modules/profiles/minimal.nix
|
./nixos/modules/profiles/minimal.nix
|
||||||
({ lib, ... }: lib.ifThisFunctionIsMissingTheTestFails {
|
(
|
||||||
|
{ lib, ... }:
|
||||||
|
lib.ifThisFunctionIsMissingTheTestFails {
|
||||||
# Define a minimal config without eval warnings
|
# Define a minimal config without eval warnings
|
||||||
nixpkgs.hostPlatform = "x86_64-linux";
|
nixpkgs.hostPlatform = "x86_64-linux";
|
||||||
boot.loader.grub.enable = false;
|
boot.loader.grub.enable = false;
|
||||||
fileSystems."/".device = "nodev";
|
fileSystems."/".device = "nodev";
|
||||||
# See https://search.nixos.org/options?show=system.stateVersion&query=stateversion
|
# See https://search.nixos.org/options?show=system.stateVersion&query=stateversion
|
||||||
system.stateVersion = lib.trivial.release; # DON'T do this in real configs!
|
system.stateVersion = lib.trivial.release; # DON'T do this in real configs!
|
||||||
})
|
}
|
||||||
|
)
|
||||||
];
|
];
|
||||||
}).config.system.build.toplevel;
|
}).config.system.build.toplevel;
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
htmlDocs = {
|
htmlDocs = {
|
||||||
nixpkgsManual = builtins.mapAttrs (_: jobSet: jobSet.manual) jobs;
|
nixpkgsManual = builtins.mapAttrs (_: jobSet: jobSet.manual) jobs;
|
||||||
nixosManual = (import ./nixos/release-small.nix {
|
nixosManual =
|
||||||
|
(import ./nixos/release-small.nix {
|
||||||
nixpkgs = self;
|
nixpkgs = self;
|
||||||
}).nixos.manual;
|
}).nixos.manual;
|
||||||
};
|
};
|
||||||
|
|
||||||
devShells = forAllSystems (system:
|
devShells = forAllSystems (
|
||||||
{ } // lib.optionalAttrs
|
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"
|
# 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"
|
system != "armv6l-linux"
|
||||||
|
@ -129,9 +160,14 @@
|
||||||
&& !self.legacyPackages.${system}.stdenv.hostPlatform.isFreeBSD
|
&& !self.legacyPackages.${system}.stdenv.hostPlatform.isFreeBSD
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
/** A shell to get tooling for Nixpkgs development. See nixpkgs/shell.nix. */
|
/**
|
||||||
|
A shell to get tooling for Nixpkgs development. See nixpkgs/shell.nix.
|
||||||
|
*/
|
||||||
default = import ./shell.nix { inherit system; };
|
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.
|
A nested structure of [packages](https://nix.dev/manual/nix/latest/glossary#package-attribute-set) and other values.
|
||||||
|
@ -152,10 +188,13 @@
|
||||||
evaluation. Evaluating the attribute value tends to require a significant
|
evaluation. Evaluating the attribute value tends to require a significant
|
||||||
amount of computation, even considering lazy evaluation.
|
amount of computation, even considering lazy evaluation.
|
||||||
*/
|
*/
|
||||||
legacyPackages = forAllSystems (system:
|
legacyPackages = forAllSystems (
|
||||||
(import ./. { inherit system; }).extend (final: prev: {
|
system:
|
||||||
|
(import ./. { inherit system; }).extend (
|
||||||
|
final: prev: {
|
||||||
lib = prev.lib.extend libVersionInfoOverlay;
|
lib = prev.lib.extend libVersionInfoOverlay;
|
||||||
})
|
}
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
445
lib/attrsets.nix
445
lib/attrsets.nix
|
@ -5,14 +5,42 @@
|
||||||
|
|
||||||
let
|
let
|
||||||
inherit (builtins) head length;
|
inherit (builtins) head length;
|
||||||
inherit (lib.trivial) oldestSupportedReleaseIsAtLeast mergeAttrs warn warnIf;
|
inherit (lib.trivial)
|
||||||
inherit (lib.strings) concatStringsSep concatMapStringsSep escapeNixIdentifier sanitizeDerivationName;
|
oldestSupportedReleaseIsAtLeast
|
||||||
inherit (lib.lists) filter foldr foldl' concatMap elemAt all partition groupBy take foldl;
|
mergeAttrs
|
||||||
|
warn
|
||||||
|
warnIf
|
||||||
|
;
|
||||||
|
inherit (lib.strings)
|
||||||
|
concatStringsSep
|
||||||
|
concatMapStringsSep
|
||||||
|
escapeNixIdentifier
|
||||||
|
sanitizeDerivationName
|
||||||
|
;
|
||||||
|
inherit (lib.lists)
|
||||||
|
filter
|
||||||
|
foldr
|
||||||
|
foldl'
|
||||||
|
concatMap
|
||||||
|
elemAt
|
||||||
|
all
|
||||||
|
partition
|
||||||
|
groupBy
|
||||||
|
take
|
||||||
|
foldl
|
||||||
|
;
|
||||||
in
|
in
|
||||||
|
|
||||||
rec {
|
rec {
|
||||||
inherit (builtins) attrNames listToAttrs hasAttr isAttrs getAttr removeAttrs intersectAttrs;
|
inherit (builtins)
|
||||||
|
attrNames
|
||||||
|
listToAttrs
|
||||||
|
hasAttr
|
||||||
|
isAttrs
|
||||||
|
getAttr
|
||||||
|
removeAttrs
|
||||||
|
intersectAttrs
|
||||||
|
;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Return an attribute from nested attribute sets.
|
Return an attribute from nested attribute sets.
|
||||||
|
@ -25,7 +53,6 @@ rec {
|
||||||
(x.${f p}."example.com" or 6) == attrByPath [ (f p) "example.com" ] 6 x
|
(x.${f p}."example.com" or 6) == attrByPath [ (f p) "example.com" ] 6 x
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`attrPath`
|
`attrPath`
|
||||||
|
@ -63,19 +90,20 @@ rec {
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
attrByPath =
|
attrByPath =
|
||||||
attrPath:
|
attrPath: default: set:
|
||||||
default:
|
|
||||||
set:
|
|
||||||
let
|
let
|
||||||
lenAttrPath = length attrPath;
|
lenAttrPath = length attrPath;
|
||||||
attrByPath' = n: s: (
|
attrByPath' =
|
||||||
if n == lenAttrPath then s
|
n: s:
|
||||||
else (
|
(
|
||||||
|
if n == lenAttrPath then
|
||||||
|
s
|
||||||
|
else
|
||||||
|
(
|
||||||
let
|
let
|
||||||
attr = elemAt attrPath n;
|
attr = elemAt attrPath n;
|
||||||
in
|
in
|
||||||
if s ? ${attr} then attrByPath' (n + 1) s.${attr}
|
if s ? ${attr} then attrByPath' (n + 1) s.${attr} else default
|
||||||
else default
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
in
|
in
|
||||||
|
@ -97,7 +125,6 @@ rec {
|
||||||
hasAttrByPath [] x == true
|
hasAttrByPath [] x == true
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`attrPath`
|
`attrPath`
|
||||||
|
@ -131,17 +158,18 @@ rec {
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
hasAttrByPath =
|
hasAttrByPath =
|
||||||
attrPath:
|
attrPath: e:
|
||||||
e:
|
|
||||||
let
|
let
|
||||||
lenAttrPath = length attrPath;
|
lenAttrPath = length attrPath;
|
||||||
hasAttrByPath' = n: s: (
|
hasAttrByPath' =
|
||||||
n == lenAttrPath || (
|
n: s:
|
||||||
|
(
|
||||||
|
n == lenAttrPath
|
||||||
|
|| (
|
||||||
let
|
let
|
||||||
attr = elemAt attrPath n;
|
attr = elemAt attrPath n;
|
||||||
in
|
in
|
||||||
if s ? ${attr} then hasAttrByPath' (n + 1) s.${attr}
|
if s ? ${attr} then hasAttrByPath' (n + 1) s.${attr} else false
|
||||||
else false
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
in
|
in
|
||||||
|
@ -164,7 +192,6 @@ rec {
|
||||||
hasAttrByPath (attrsets.longestValidPathPrefix p x) x == true
|
hasAttrByPath (attrsets.longestValidPathPrefix p x) x == true
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`attrPath`
|
`attrPath`
|
||||||
|
@ -200,8 +227,7 @@ rec {
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
longestValidPathPrefix =
|
longestValidPathPrefix =
|
||||||
attrPath:
|
attrPath: v:
|
||||||
v:
|
|
||||||
let
|
let
|
||||||
lenAttrPath = length attrPath;
|
lenAttrPath = length attrPath;
|
||||||
getPrefixForSetAtIndex =
|
getPrefixForSetAtIndex =
|
||||||
|
@ -221,8 +247,7 @@ rec {
|
||||||
attr = elemAt attrPath remainingPathIndex;
|
attr = elemAt attrPath remainingPathIndex;
|
||||||
in
|
in
|
||||||
if remainingSet ? ${attr} then
|
if remainingSet ? ${attr} then
|
||||||
getPrefixForSetAtIndex
|
getPrefixForSetAtIndex remainingSet.${attr} # advance from the set to the attribute value
|
||||||
remainingSet.${attr} # advance from the set to the attribute value
|
|
||||||
(remainingPathIndex + 1) # advance the path
|
(remainingPathIndex + 1) # advance the path
|
||||||
else
|
else
|
||||||
# The attribute doesn't exist, so we return the prefix up to the
|
# The attribute doesn't exist, so we return the prefix up to the
|
||||||
|
@ -234,7 +259,6 @@ rec {
|
||||||
/**
|
/**
|
||||||
Create a new attribute set with `value` set at the nested attribute location specified in `attrPath`.
|
Create a new attribute set with `value` set at the nested attribute location specified in `attrPath`.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`attrPath`
|
`attrPath`
|
||||||
|
@ -263,15 +287,12 @@ rec {
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
setAttrByPath =
|
setAttrByPath =
|
||||||
attrPath:
|
attrPath: value:
|
||||||
value:
|
|
||||||
let
|
let
|
||||||
len = length attrPath;
|
len = length attrPath;
|
||||||
atDepth = n:
|
atDepth = n: if n == len then value else { ${elemAt attrPath n} = atDepth (n + 1); };
|
||||||
if n == len
|
in
|
||||||
then value
|
atDepth 0;
|
||||||
else { ${elemAt attrPath n} = atDepth (n + 1); };
|
|
||||||
in atDepth 0;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Like `attrByPath`, but without a default value. If it doesn't find the
|
Like `attrByPath`, but without a default value. If it doesn't find the
|
||||||
|
@ -285,7 +306,6 @@ rec {
|
||||||
x.${f p}."example.com" == getAttrByPath [ (f p) "example.com" ] x
|
x.${f p}."example.com" == getAttrByPath [ (f p) "example.com" ] x
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`attrPath`
|
`attrPath`
|
||||||
|
@ -317,14 +337,12 @@ rec {
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
getAttrFromPath =
|
getAttrFromPath =
|
||||||
attrPath:
|
attrPath: set:
|
||||||
set:
|
|
||||||
attrByPath attrPath (abort ("cannot find attribute '" + concatStringsSep "." attrPath + "'")) set;
|
attrByPath attrPath (abort ("cannot find attribute '" + concatStringsSep "." attrPath + "'")) set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Map each attribute in the given set and merge them into a new attribute set.
|
Map each attribute in the given set and merge them into a new attribute set.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`f`
|
`f`
|
||||||
|
@ -357,12 +375,7 @@ rec {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
concatMapAttrs = f: v:
|
concatMapAttrs = f: v: foldl' mergeAttrs { } (attrValues (mapAttrs f v));
|
||||||
foldl' mergeAttrs { }
|
|
||||||
(attrValues
|
|
||||||
(mapAttrs f v)
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Update or set specific paths of an attribute set.
|
Update or set specific paths of an attribute set.
|
||||||
|
@ -420,13 +433,15 @@ rec {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
updateManyAttrsByPath = let
|
updateManyAttrsByPath =
|
||||||
|
let
|
||||||
# When recursing into attributes, instead of updating the `path` of each
|
# When recursing into attributes, instead of updating the `path` of each
|
||||||
# update using `tail`, which needs to allocate an entirely new list,
|
# update using `tail`, which needs to allocate an entirely new list,
|
||||||
# we just pass a prefix length to use and make sure to only look at the
|
# we just pass a prefix length to use and make sure to only look at the
|
||||||
# path without the prefix length, so that we can reuse the original list
|
# path without the prefix length, so that we can reuse the original list
|
||||||
# entries.
|
# entries.
|
||||||
go = prefixLength: hasValue: value: updates:
|
go =
|
||||||
|
prefixLength: hasValue: value: updates:
|
||||||
let
|
let
|
||||||
# Splits updates into ones on this level (split.right)
|
# Splits updates into ones on this level (split.right)
|
||||||
# And ones on levels further down (split.wrong)
|
# And ones on levels further down (split.wrong)
|
||||||
|
@ -438,51 +453,58 @@ rec {
|
||||||
# Applies only nested modification to the input value
|
# Applies only nested modification to the input value
|
||||||
withNestedMods =
|
withNestedMods =
|
||||||
# Return the value directly if we don't have any nested modifications
|
# Return the value directly if we don't have any nested modifications
|
||||||
if split.wrong == [] then
|
if split.wrong == [ ] then
|
||||||
if hasValue then value
|
if hasValue then
|
||||||
|
value
|
||||||
else
|
else
|
||||||
# Throw an error if there is no value. This `head` call here is
|
# Throw an error if there is no value. This `head` call here is
|
||||||
# safe, but only in this branch since `go` could only be called
|
# safe, but only in this branch since `go` could only be called
|
||||||
# with `hasValue == false` for nested updates, in which case
|
# with `hasValue == false` for nested updates, in which case
|
||||||
# it's also always called with at least one update
|
# it's also always called with at least one update
|
||||||
let updatePath = (head split.right).path; in
|
let
|
||||||
throw
|
updatePath = (head split.right).path;
|
||||||
( "updateManyAttrsByPath: Path '${showAttrPath updatePath}' does "
|
in
|
||||||
|
throw (
|
||||||
|
"updateManyAttrsByPath: Path '${showAttrPath updatePath}' does "
|
||||||
+ "not exist in the given value, but the first update to this "
|
+ "not exist in the given value, but the first update to this "
|
||||||
+ "path tries to access the existing value.")
|
+ "path tries to access the existing value."
|
||||||
|
)
|
||||||
else
|
else
|
||||||
# If there are nested modifications, try to apply them to the value
|
# If there are nested modifications, try to apply them to the value
|
||||||
if ! hasValue then
|
if !hasValue then
|
||||||
# But if we don't have a value, just use an empty attribute set
|
# But if we don't have a value, just use an empty attribute set
|
||||||
# as the value, but simplify the code a bit
|
# as the value, but simplify the code a bit
|
||||||
mapAttrs (name: go (prefixLength + 1) false null) nested
|
mapAttrs (name: go (prefixLength + 1) false null) nested
|
||||||
else if isAttrs value then
|
else if isAttrs value then
|
||||||
# If we do have a value and it's an attribute set, override it
|
# If we do have a value and it's an attribute set, override it
|
||||||
# with the nested modifications
|
# with the nested modifications
|
||||||
value //
|
value // mapAttrs (name: go (prefixLength + 1) (value ? ${name}) value.${name}) nested
|
||||||
mapAttrs (name: go (prefixLength + 1) (value ? ${name}) value.${name}) nested
|
|
||||||
else
|
else
|
||||||
# However if it's not an attribute set, we can't apply the nested
|
# However if it's not an attribute set, we can't apply the nested
|
||||||
# modifications, throw an error
|
# modifications, throw an error
|
||||||
let updatePath = (head split.wrong).path; in
|
let
|
||||||
throw
|
updatePath = (head split.wrong).path;
|
||||||
( "updateManyAttrsByPath: Path '${showAttrPath updatePath}' needs to "
|
in
|
||||||
|
throw (
|
||||||
|
"updateManyAttrsByPath: Path '${showAttrPath updatePath}' needs to "
|
||||||
+ "be updated, but path '${showAttrPath (take prefixLength updatePath)}' "
|
+ "be updated, but path '${showAttrPath (take prefixLength updatePath)}' "
|
||||||
+ "of the given value is not an attribute set, so we can't "
|
+ "of the given value is not an attribute set, so we can't "
|
||||||
+ "update an attribute inside of it.");
|
+ "update an attribute inside of it."
|
||||||
|
);
|
||||||
|
|
||||||
# We get the final result by applying all the updates on this level
|
# We get the final result by applying all the updates on this level
|
||||||
# after having applied all the nested updates
|
# after having applied all the nested updates
|
||||||
# We use foldl instead of foldl' so that in case of multiple updates,
|
# We use foldl instead of foldl' so that in case of multiple updates,
|
||||||
# intermediate values aren't evaluated if not needed
|
# intermediate values aren't evaluated if not needed
|
||||||
in foldl (acc: el: el.update acc) withNestedMods split.right;
|
in
|
||||||
|
foldl (acc: el: el.update acc) withNestedMods split.right;
|
||||||
|
|
||||||
in updates: value: go 0 true value updates;
|
in
|
||||||
|
updates: value: go 0 true value updates;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Return the specified attributes from a set.
|
Return the specified attributes from a set.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`nameList`
|
`nameList`
|
||||||
|
@ -510,10 +532,7 @@ rec {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
attrVals =
|
attrVals = nameList: set: map (x: set.${x}) nameList;
|
||||||
nameList:
|
|
||||||
set: map (x: set.${x}) nameList;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Return the values of all attributes in the given set, sorted by
|
Return the values of all attributes in the given set, sorted by
|
||||||
|
@ -538,12 +557,10 @@ rec {
|
||||||
*/
|
*/
|
||||||
attrValues = builtins.attrValues;
|
attrValues = builtins.attrValues;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Given a set of attribute names, return the set of the corresponding
|
Given a set of attribute names, return the set of the corresponding
|
||||||
attributes from the given set.
|
attributes from the given set.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`names`
|
`names`
|
||||||
|
@ -571,9 +588,7 @@ rec {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
getAttrs =
|
getAttrs = names: attrs: genAttrs names (name: attrs.${name});
|
||||||
names:
|
|
||||||
attrs: genAttrs names (name: attrs.${name});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Collect each attribute named `attr` from a list of attribute
|
Collect each attribute named `attr` from a list of attribute
|
||||||
|
@ -608,12 +623,10 @@ rec {
|
||||||
*/
|
*/
|
||||||
catAttrs = builtins.catAttrs;
|
catAttrs = builtins.catAttrs;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Filter an attribute set by removing all attributes for which the
|
Filter an attribute set by removing all attributes for which the
|
||||||
given predicate return false.
|
given predicate return false.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`pred`
|
`pred`
|
||||||
|
@ -641,16 +654,12 @@ rec {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
filterAttrs =
|
filterAttrs = pred: set: removeAttrs set (filter (name: !pred name set.${name}) (attrNames set));
|
||||||
pred:
|
|
||||||
set:
|
|
||||||
removeAttrs set (filter (name: ! pred name set.${name}) (attrNames set));
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Filter an attribute set recursively by removing all attributes for
|
Filter an attribute set recursively by removing all attributes for
|
||||||
which the given predicate return false.
|
which the given predicate return false.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`pred`
|
`pred`
|
||||||
|
@ -679,17 +688,19 @@ rec {
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
filterAttrsRecursive =
|
filterAttrsRecursive =
|
||||||
pred:
|
pred: set:
|
||||||
set:
|
|
||||||
listToAttrs (
|
listToAttrs (
|
||||||
concatMap (name:
|
concatMap (
|
||||||
let v = set.${name}; in
|
name:
|
||||||
if pred name v then [
|
let
|
||||||
(nameValuePair name (
|
v = set.${name};
|
||||||
if isAttrs v then filterAttrsRecursive pred v
|
in
|
||||||
else v
|
if pred name v then
|
||||||
))
|
[
|
||||||
] else []
|
(nameValuePair name (if isAttrs v then filterAttrsRecursive pred v else v))
|
||||||
|
]
|
||||||
|
else
|
||||||
|
[ ]
|
||||||
) (attrNames set)
|
) (attrNames set)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -703,7 +714,6 @@ rec {
|
||||||
There is a completely different function `lib.foldAttrs`
|
There is a completely different function `lib.foldAttrs`
|
||||||
which has nothing to do with this function, despite the similar name.
|
which has nothing to do with this function, despite the similar name.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`f`
|
`f`
|
||||||
|
@ -772,16 +782,13 @@ rec {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
foldlAttrs = f: init: set:
|
foldlAttrs =
|
||||||
foldl'
|
f: init: set:
|
||||||
(acc: name: f acc name set.${name})
|
foldl' (acc: name: f acc name set.${name}) init (attrNames set);
|
||||||
init
|
|
||||||
(attrNames set);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Apply fold functions to values grouped by key.
|
Apply fold functions to values grouped by key.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`op`
|
`op`
|
||||||
|
@ -814,22 +821,16 @@ rec {
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
foldAttrs =
|
foldAttrs =
|
||||||
op:
|
op: nul: list_of_attrs:
|
||||||
nul:
|
foldr (
|
||||||
list_of_attrs:
|
n: a: foldr (name: o: o // { ${name} = op n.${name} (a.${name} or nul); }) a (attrNames n)
|
||||||
foldr (n: a:
|
) { } list_of_attrs;
|
||||||
foldr (name: o:
|
|
||||||
o // { ${name} = op n.${name} (a.${name} or nul); }
|
|
||||||
) a (attrNames n)
|
|
||||||
) {} list_of_attrs;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Recursively collect sets that verify a given predicate named `pred`
|
Recursively collect sets that verify a given predicate named `pred`
|
||||||
from the set `attrs`. The recursion is stopped when the predicate is
|
from the set `attrs`. The recursion is stopped when the predicate is
|
||||||
verified.
|
verified.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`pred`
|
`pred`
|
||||||
|
@ -862,19 +863,17 @@ rec {
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
collect =
|
collect =
|
||||||
pred:
|
pred: attrs:
|
||||||
attrs:
|
|
||||||
if pred attrs then
|
if pred attrs then
|
||||||
[ attrs ]
|
[ attrs ]
|
||||||
else if isAttrs attrs then
|
else if isAttrs attrs then
|
||||||
concatMap (collect pred) (attrValues attrs)
|
concatMap (collect pred) (attrValues attrs)
|
||||||
else
|
else
|
||||||
[];
|
[ ];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Return the cartesian product of attribute set value combinations.
|
Return the cartesian product of attribute set value combinations.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`attrsOfLists`
|
`attrsOfLists`
|
||||||
|
@ -905,12 +904,12 @@ rec {
|
||||||
*/
|
*/
|
||||||
cartesianProduct =
|
cartesianProduct =
|
||||||
attrsOfLists:
|
attrsOfLists:
|
||||||
foldl' (listOfAttrs: attrName:
|
foldl' (
|
||||||
concatMap (attrs:
|
listOfAttrs: attrName:
|
||||||
map (listValue: attrs // { ${attrName} = listValue; }) attrsOfLists.${attrName}
|
concatMap (
|
||||||
|
attrs: map (listValue: attrs // { ${attrName} = listValue; }) attrsOfLists.${attrName}
|
||||||
) listOfAttrs
|
) listOfAttrs
|
||||||
) [{}] (attrNames attrsOfLists);
|
) [ { } ] (attrNames attrsOfLists);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Return the result of function f applied to the cartesian product of attribute set value combinations.
|
Return the result of function f applied to the cartesian product of attribute set value combinations.
|
||||||
|
@ -942,14 +941,12 @@ rec {
|
||||||
```
|
```
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
*/
|
*/
|
||||||
mapCartesianProduct = f: attrsOfLists: map f (cartesianProduct attrsOfLists);
|
mapCartesianProduct = f: attrsOfLists: map f (cartesianProduct attrsOfLists);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Utility function that creates a `{name, value}` pair as expected by `builtins.listToAttrs`.
|
Utility function that creates a `{name, value}` pair as expected by `builtins.listToAttrs`.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`name`
|
`name`
|
||||||
|
@ -977,11 +974,7 @@ rec {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
nameValuePair =
|
nameValuePair = name: value: { inherit name value; };
|
||||||
name:
|
|
||||||
value:
|
|
||||||
{ inherit name value; };
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Apply a function to each element in an attribute set, creating a new attribute set.
|
Apply a function to each element in an attribute set, creating a new attribute set.
|
||||||
|
@ -1016,13 +1009,11 @@ rec {
|
||||||
*/
|
*/
|
||||||
mapAttrs = builtins.mapAttrs;
|
mapAttrs = builtins.mapAttrs;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Like `mapAttrs`, but allows the name of each attribute to be
|
Like `mapAttrs`, but allows the name of each attribute to be
|
||||||
changed in addition to the value. The applied function should
|
changed in addition to the value. The applied function should
|
||||||
return both the new name and value as a `nameValuePair`.
|
return both the new name and value as a `nameValuePair`.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`f`
|
`f`
|
||||||
|
@ -1051,11 +1042,7 @@ rec {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
mapAttrs' =
|
mapAttrs' = f: set: listToAttrs (map (attr: f attr set.${attr}) (attrNames set));
|
||||||
f:
|
|
||||||
set:
|
|
||||||
listToAttrs (map (attr: f attr set.${attr}) (attrNames set));
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Call a function for each attribute in the given set and return
|
Call a function for each attribute in the given set and return
|
||||||
|
@ -1089,10 +1076,7 @@ rec {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
mapAttrsToList =
|
mapAttrsToList = f: attrs: map (name: f name attrs.${name}) (attrNames attrs);
|
||||||
f:
|
|
||||||
attrs:
|
|
||||||
map (name: f name attrs.${name}) (attrNames attrs);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Deconstruct an attrset to a list of name-value pairs as expected by [`builtins.listToAttrs`](https://nixos.org/manual/nix/stable/language/builtins.html#builtins-listToAttrs).
|
Deconstruct an attrset to a list of name-value pairs as expected by [`builtins.listToAttrs`](https://nixos.org/manual/nix/stable/language/builtins.html#builtins-listToAttrs).
|
||||||
|
@ -1139,7 +1123,6 @@ rec {
|
||||||
*/
|
*/
|
||||||
attrsToList = mapAttrsToList nameValuePair;
|
attrsToList = mapAttrsToList nameValuePair;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Like `mapAttrs`, except that it recursively applies itself to the *leaf* attributes of a potentially-nested attribute set:
|
Like `mapAttrs`, except that it recursively applies itself to the *leaf* attributes of a potentially-nested attribute set:
|
||||||
the second argument of the function will never be an attrset.
|
the second argument of the function will never be an attrset.
|
||||||
|
@ -1165,11 +1148,7 @@ rec {
|
||||||
mapAttrsRecursive :: ([String] -> a -> b) -> AttrSet -> AttrSet
|
mapAttrsRecursive :: ([String] -> a -> b) -> AttrSet -> AttrSet
|
||||||
```
|
```
|
||||||
*/
|
*/
|
||||||
mapAttrsRecursive =
|
mapAttrsRecursive = f: set: mapAttrsRecursiveCond (as: true) f set;
|
||||||
f:
|
|
||||||
set:
|
|
||||||
mapAttrsRecursiveCond (as: true) f set;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Like `mapAttrsRecursive`, but it takes an additional predicate that tells it whether to recurse into an attribute set.
|
Like `mapAttrsRecursive`, but it takes an additional predicate that tells it whether to recurse into an attribute set.
|
||||||
|
@ -1195,25 +1174,21 @@ rec {
|
||||||
```
|
```
|
||||||
*/
|
*/
|
||||||
mapAttrsRecursiveCond =
|
mapAttrsRecursiveCond =
|
||||||
cond:
|
cond: f: set:
|
||||||
f:
|
|
||||||
set:
|
|
||||||
let
|
let
|
||||||
recurse = path:
|
recurse =
|
||||||
mapAttrs
|
path:
|
||||||
(name: value:
|
mapAttrs (
|
||||||
if isAttrs value && cond value
|
name: value:
|
||||||
then recurse (path ++ [ name ]) value
|
if isAttrs value && cond value then recurse (path ++ [ name ]) value else f (path ++ [ name ]) value
|
||||||
else f (path ++ [ name ]) value);
|
);
|
||||||
in
|
in
|
||||||
recurse [ ] set;
|
recurse [ ] set;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Generate an attribute set by mapping a function over a list of
|
Generate an attribute set by mapping a function over a list of
|
||||||
attribute names.
|
attribute names.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`names`
|
`names`
|
||||||
|
@ -1241,17 +1216,12 @@ rec {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
genAttrs =
|
genAttrs = names: f: listToAttrs (map (n: nameValuePair n (f n)) names);
|
||||||
names:
|
|
||||||
f:
|
|
||||||
listToAttrs (map (n: nameValuePair n (f n)) names);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Check whether the argument is a derivation. Any set with
|
Check whether the argument is a derivation. Any set with
|
||||||
`{ type = "derivation"; }` counts as a derivation.
|
`{ type = "derivation"; }` counts as a derivation.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`value`
|
`value`
|
||||||
|
@ -1278,13 +1248,11 @@ rec {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
isDerivation =
|
isDerivation = value: value.type or null == "derivation";
|
||||||
value: value.type or null == "derivation";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Converts a store path to a fake derivation.
|
Converts a store path to a fake derivation.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`path`
|
`path`
|
||||||
|
@ -1301,22 +1269,21 @@ rec {
|
||||||
path:
|
path:
|
||||||
let
|
let
|
||||||
path' = builtins.storePath path;
|
path' = builtins.storePath path;
|
||||||
res =
|
res = {
|
||||||
{ type = "derivation";
|
type = "derivation";
|
||||||
name = sanitizeDerivationName (builtins.substring 33 (-1) (baseNameOf path'));
|
name = sanitizeDerivationName (builtins.substring 33 (-1) (baseNameOf path'));
|
||||||
outPath = path';
|
outPath = path';
|
||||||
outputs = [ "out" ];
|
outputs = [ "out" ];
|
||||||
out = res;
|
out = res;
|
||||||
outputName = "out";
|
outputName = "out";
|
||||||
};
|
};
|
||||||
in res;
|
in
|
||||||
|
res;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
If `cond` is true, return the attribute set `as`,
|
If `cond` is true, return the attribute set `as`,
|
||||||
otherwise an empty attribute set.
|
otherwise an empty attribute set.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`cond`
|
`cond`
|
||||||
|
@ -1346,17 +1313,12 @@ rec {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
optionalAttrs =
|
optionalAttrs = cond: as: if cond then as else { };
|
||||||
cond:
|
|
||||||
as:
|
|
||||||
if cond then as else {};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Merge sets of attributes and use the function `f` to merge attributes
|
Merge sets of attributes and use the function `f` to merge attributes
|
||||||
values.
|
values.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`names`
|
`names`
|
||||||
|
@ -1389,14 +1351,13 @@ rec {
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
zipAttrsWithNames =
|
zipAttrsWithNames =
|
||||||
names:
|
names: f: sets:
|
||||||
f:
|
listToAttrs (
|
||||||
sets:
|
map (name: {
|
||||||
listToAttrs (map (name: {
|
|
||||||
inherit name;
|
inherit name;
|
||||||
value = f name (catAttrs name sets);
|
value = f name (catAttrs name sets);
|
||||||
}) names);
|
}) names
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Merge sets of attributes and use the function f to merge attribute values.
|
Merge sets of attributes and use the function f to merge attribute values.
|
||||||
|
@ -1427,7 +1388,6 @@ rec {
|
||||||
zipAttrsWith =
|
zipAttrsWith =
|
||||||
builtins.zipAttrsWith or (f: sets: zipAttrsWithNames (concatMap attrNames sets) f sets);
|
builtins.zipAttrsWith or (f: sets: zipAttrsWithNames (concatMap attrNames sets) f sets);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Merge sets of attributes and combine each attribute value in to a list.
|
Merge sets of attributes and combine each attribute value in to a list.
|
||||||
|
|
||||||
|
@ -1458,7 +1418,6 @@ rec {
|
||||||
The result is the same as `foldl mergeAttrs { }`, but the performance is better for large inputs.
|
The result is the same as `foldl mergeAttrs { }`, but the performance is better for large inputs.
|
||||||
For n list elements, each with an attribute set containing m unique attributes, the complexity of this operation is O(nm log n).
|
For n list elements, each with an attribute set containing m unique attributes, the complexity of this operation is O(nm log n).
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`list`
|
`list`
|
||||||
|
@ -1484,17 +1443,18 @@ rec {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
mergeAttrsList = list:
|
mergeAttrsList =
|
||||||
|
list:
|
||||||
let
|
let
|
||||||
# `binaryMerge start end` merges the elements at indices `index` of `list` such that `start <= index < end`
|
# `binaryMerge start end` merges the elements at indices `index` of `list` such that `start <= index < end`
|
||||||
# Type: Int -> Int -> Attrs
|
# Type: Int -> Int -> Attrs
|
||||||
binaryMerge = start: end:
|
binaryMerge =
|
||||||
|
start: end:
|
||||||
# assert start < end; # Invariant
|
# assert start < end; # Invariant
|
||||||
if end - start >= 2 then
|
if end - start >= 2 then
|
||||||
# If there's at least 2 elements, split the range in two, recurse on each part and merge the result
|
# If there's at least 2 elements, split the range in two, recurse on each part and merge the result
|
||||||
# The invariant is satisfied because each half will have at least 1 element
|
# The invariant is satisfied because each half will have at least 1 element
|
||||||
binaryMerge start (start + (end - start) / 2)
|
binaryMerge start (start + (end - start) / 2) // binaryMerge (start + (end - start) / 2) end
|
||||||
// binaryMerge (start + (end - start) / 2) end
|
|
||||||
else
|
else
|
||||||
# Otherwise there will be exactly 1 element due to the invariant, in which case we just return it directly
|
# Otherwise there will be exactly 1 element due to the invariant, in which case we just return it directly
|
||||||
elemAt list start;
|
elemAt list start;
|
||||||
|
@ -1505,7 +1465,6 @@ rec {
|
||||||
else
|
else
|
||||||
binaryMerge 0 (length list);
|
binaryMerge 0 (length list);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Does the same as the update operator '//' except that attributes are
|
Does the same as the update operator '//' except that attributes are
|
||||||
merged until the given predicate is verified. The predicate should
|
merged until the given predicate is verified. The predicate should
|
||||||
|
@ -1514,7 +1473,6 @@ rec {
|
||||||
the predicate is satisfied, the value of the first attribute set is
|
the predicate is satisfied, the value of the first attribute set is
|
||||||
replaced by the value of the second attribute set.
|
replaced by the value of the second attribute set.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`pred`
|
`pred`
|
||||||
|
@ -1563,20 +1521,22 @@ rec {
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
recursiveUpdateUntil =
|
recursiveUpdateUntil =
|
||||||
pred:
|
pred: lhs: rhs:
|
||||||
lhs:
|
let
|
||||||
rhs:
|
f =
|
||||||
let f = attrPath:
|
attrPath:
|
||||||
zipAttrsWith (n: values:
|
zipAttrsWith (
|
||||||
let here = attrPath ++ [n]; in
|
n: values:
|
||||||
if length values == 1
|
let
|
||||||
|| pred here (elemAt values 1) (head values) then
|
here = attrPath ++ [ n ];
|
||||||
|
in
|
||||||
|
if length values == 1 || pred here (elemAt values 1) (head values) then
|
||||||
head values
|
head values
|
||||||
else
|
else
|
||||||
f here values
|
f here values
|
||||||
);
|
);
|
||||||
in f [] [rhs lhs];
|
in
|
||||||
|
f [ ] [ rhs lhs ];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
A recursive variant of the update operator ‘//’. The recursion
|
A recursive variant of the update operator ‘//’. The recursion
|
||||||
|
@ -1584,7 +1544,6 @@ rec {
|
||||||
in which case the right hand side value takes precedence over the
|
in which case the right hand side value takes precedence over the
|
||||||
left hand side value.
|
left hand side value.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`lhs`
|
`lhs`
|
||||||
|
@ -1622,17 +1581,17 @@ rec {
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
recursiveUpdate =
|
recursiveUpdate =
|
||||||
lhs:
|
lhs: rhs:
|
||||||
rhs:
|
recursiveUpdateUntil (
|
||||||
recursiveUpdateUntil (path: lhs: rhs: !(isAttrs lhs && isAttrs rhs)) lhs rhs;
|
path: lhs: rhs:
|
||||||
|
!(isAttrs lhs && isAttrs rhs)
|
||||||
|
) lhs rhs;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Recurse into every attribute set of the first argument and check that:
|
Recurse into every attribute set of the first argument and check that:
|
||||||
- Each attribute path also exists in the second argument.
|
- Each attribute path also exists in the second argument.
|
||||||
- If the attribute's value is not a nested attribute set, it must have the same value in the right argument.
|
- If the attribute's value is not a nested attribute set, it must have the same value in the right argument.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`pattern`
|
`pattern`
|
||||||
|
@ -1661,30 +1620,27 @@ rec {
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
matchAttrs =
|
matchAttrs =
|
||||||
pattern:
|
pattern: attrs:
|
||||||
attrs:
|
|
||||||
assert isAttrs pattern;
|
assert isAttrs pattern;
|
||||||
all
|
all (
|
||||||
( # Compare equality between `pattern` & `attrs`.
|
# Compare equality between `pattern` & `attrs`.
|
||||||
attr:
|
attr:
|
||||||
# Missing attr, not equal.
|
# Missing attr, not equal.
|
||||||
attrs ? ${attr} && (
|
attrs ? ${attr}
|
||||||
|
&& (
|
||||||
let
|
let
|
||||||
lhs = pattern.${attr};
|
lhs = pattern.${attr};
|
||||||
rhs = attrs.${attr};
|
rhs = attrs.${attr};
|
||||||
in
|
in
|
||||||
# If attrset check recursively
|
# If attrset check recursively
|
||||||
if isAttrs lhs then isAttrs rhs && matchAttrs lhs rhs
|
if isAttrs lhs then isAttrs rhs && matchAttrs lhs rhs else lhs == rhs
|
||||||
else lhs == rhs
|
|
||||||
)
|
)
|
||||||
)
|
) (attrNames pattern);
|
||||||
(attrNames pattern);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Override only the attributes that are already present in the old set
|
Override only the attributes that are already present in the old set
|
||||||
useful for deep-overriding.
|
useful for deep-overriding.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`old`
|
`old`
|
||||||
|
@ -1716,11 +1672,7 @@ rec {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
overrideExisting =
|
overrideExisting = old: new: mapAttrs (name: value: new.${name} or value) old;
|
||||||
old:
|
|
||||||
new:
|
|
||||||
mapAttrs (name: value: new.${name} or value) old;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Turns a list of strings into a human-readable description of those
|
Turns a list of strings into a human-readable description of those
|
||||||
|
@ -1728,7 +1680,6 @@ rec {
|
||||||
not intended to be machine-readable.
|
not intended to be machine-readable.
|
||||||
Create a new attribute set with `value` set at the nested attribute location specified in `attrPath`.
|
Create a new attribute set with `value` set at the nested attribute location specified in `attrPath`.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`path`
|
`path`
|
||||||
|
@ -1756,16 +1707,13 @@ rec {
|
||||||
*/
|
*/
|
||||||
showAttrPath =
|
showAttrPath =
|
||||||
path:
|
path:
|
||||||
if path == [] then "<root attribute path>"
|
if path == [ ] then "<root attribute path>" else concatMapStringsSep "." escapeNixIdentifier path;
|
||||||
else concatMapStringsSep "." escapeNixIdentifier path;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Get a package output.
|
Get a package output.
|
||||||
If no output is found, fallback to `.out` and then to the default.
|
If no output is found, fallback to `.out` and then to the default.
|
||||||
The function is idempotent: `getOutput "b" (getOutput "a" p) == getOutput "a" p`.
|
The function is idempotent: `getOutput "b" (getOutput "a" p) == getOutput "a" p`.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`output`
|
`output`
|
||||||
|
@ -1793,10 +1741,9 @@ rec {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
getOutput = output: pkg:
|
getOutput =
|
||||||
if ! pkg ? outputSpecified || ! pkg.outputSpecified
|
output: pkg:
|
||||||
then pkg.${output} or pkg.out or pkg
|
if !pkg ? outputSpecified || !pkg.outputSpecified then pkg.${output} or pkg.out or pkg else pkg;
|
||||||
else pkg;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Get the first of the `outputs` provided by the package, or the default.
|
Get the first of the `outputs` provided by the package, or the default.
|
||||||
|
@ -1836,10 +1783,7 @@ rec {
|
||||||
outputs = builtins.filter (name: hasAttr name pkg) candidates;
|
outputs = builtins.filter (name: hasAttr name pkg) candidates;
|
||||||
output = builtins.head outputs;
|
output = builtins.head outputs;
|
||||||
in
|
in
|
||||||
if pkg.outputSpecified or false || outputs == [ ] then
|
if pkg.outputSpecified or false || outputs == [ ] then pkg else pkg.${output};
|
||||||
pkg
|
|
||||||
else
|
|
||||||
pkg.${output};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Get a package's `bin` output.
|
Get a package's `bin` output.
|
||||||
|
@ -1870,7 +1814,6 @@ rec {
|
||||||
*/
|
*/
|
||||||
getBin = getOutput "bin";
|
getBin = getOutput "bin";
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Get a package's `lib` output.
|
Get a package's `lib` output.
|
||||||
If the output does not exist, fallback to `.out` and then to the default.
|
If the output does not exist, fallback to `.out` and then to the default.
|
||||||
|
@ -1927,8 +1870,11 @@ rec {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
getStatic = getFirstOutput [ "static" "lib" "out" ];
|
getStatic = getFirstOutput [
|
||||||
|
"static"
|
||||||
|
"lib"
|
||||||
|
"out"
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Get a package's `dev` output.
|
Get a package's `dev` output.
|
||||||
|
@ -1986,8 +1932,11 @@ rec {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
getInclude = getFirstOutput [ "include" "dev" "out" ];
|
getInclude = getFirstOutput [
|
||||||
|
"include"
|
||||||
|
"dev"
|
||||||
|
"out"
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Get a package's `man` output.
|
Get a package's `man` output.
|
||||||
|
@ -2042,7 +1991,6 @@ rec {
|
||||||
This function only affects a single attribute set; it does not
|
This function only affects a single attribute set; it does not
|
||||||
apply itself recursively for nested attribute sets.
|
apply itself recursively for nested attribute sets.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`attrs`
|
`attrs`
|
||||||
|
@ -2070,14 +2018,11 @@ rec {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
recurseIntoAttrs =
|
recurseIntoAttrs = attrs: attrs // { recurseForDerivations = true; };
|
||||||
attrs:
|
|
||||||
attrs // { recurseForDerivations = true; };
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Undo the effect of recurseIntoAttrs.
|
Undo the effect of recurseIntoAttrs.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`attrs`
|
`attrs`
|
||||||
|
@ -2090,9 +2035,7 @@ rec {
|
||||||
dontRecurseIntoAttrs :: AttrSet -> AttrSet
|
dontRecurseIntoAttrs :: AttrSet -> AttrSet
|
||||||
```
|
```
|
||||||
*/
|
*/
|
||||||
dontRecurseIntoAttrs =
|
dontRecurseIntoAttrs = attrs: attrs // { recurseForDerivations = false; };
|
||||||
attrs:
|
|
||||||
attrs // { recurseForDerivations = false; };
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
`unionOfDisjoint x y` is equal to `x // y // z` where the
|
`unionOfDisjoint x y` is equal to `x // y // z` where the
|
||||||
|
@ -2100,7 +2043,6 @@ rec {
|
||||||
`y`, and all values `assert` with an error message. This
|
`y`, and all values `assert` with an error message. This
|
||||||
operator is commutative, unlike (//).
|
operator is commutative, unlike (//).
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`x`
|
`x`
|
||||||
|
@ -2117,25 +2059,26 @@ rec {
|
||||||
unionOfDisjoint :: AttrSet -> AttrSet -> AttrSet
|
unionOfDisjoint :: AttrSet -> AttrSet -> AttrSet
|
||||||
```
|
```
|
||||||
*/
|
*/
|
||||||
unionOfDisjoint = x: y:
|
unionOfDisjoint =
|
||||||
|
x: y:
|
||||||
let
|
let
|
||||||
intersection = builtins.intersectAttrs x y;
|
intersection = builtins.intersectAttrs x y;
|
||||||
collisions = lib.concatStringsSep " " (builtins.attrNames intersection);
|
collisions = lib.concatStringsSep " " (builtins.attrNames intersection);
|
||||||
mask = builtins.mapAttrs (name: value: builtins.throw
|
mask = builtins.mapAttrs (
|
||||||
"unionOfDisjoint: collision on ${name}; complete list: ${collisions}")
|
name: value: builtins.throw "unionOfDisjoint: collision on ${name}; complete list: ${collisions}"
|
||||||
intersection;
|
) intersection;
|
||||||
in
|
in
|
||||||
(x // y) // mask;
|
(x // y) // mask;
|
||||||
|
|
||||||
# DEPRECATED
|
# DEPRECATED
|
||||||
zipWithNames = warn
|
zipWithNames = warn "lib.zipWithNames is a deprecated alias of lib.zipAttrsWithNames." zipAttrsWithNames;
|
||||||
"lib.zipWithNames is a deprecated alias of lib.zipAttrsWithNames." zipAttrsWithNames;
|
|
||||||
|
|
||||||
# DEPRECATED
|
# DEPRECATED
|
||||||
zip = warn
|
zip = warn "lib.zip is a deprecated alias of lib.zipAttrsWith." zipAttrsWith;
|
||||||
"lib.zip is a deprecated alias of lib.zipAttrsWith." zipAttrsWith;
|
|
||||||
|
|
||||||
# DEPRECATED
|
# DEPRECATED
|
||||||
cartesianProductOfSets = warnIf (oldestSupportedReleaseIsAtLeast 2405)
|
cartesianProductOfSets =
|
||||||
"lib.cartesianProductOfSets is a deprecated alias of lib.cartesianProduct." cartesianProduct;
|
warnIf (oldestSupportedReleaseIsAtLeast 2405)
|
||||||
|
"lib.cartesianProductOfSets is a deprecated alias of lib.cartesianProduct."
|
||||||
|
cartesianProduct;
|
||||||
}
|
}
|
||||||
|
|
580
lib/default.nix
580
lib/default.nix
|
@ -1,15 +1,17 @@
|
||||||
/* Library of low-level helper functions for nix expressions.
|
/*
|
||||||
*
|
Library of low-level helper functions for nix expressions.
|
||||||
* Please implement (mostly) exhaustive unit tests
|
|
||||||
* for new functions in `./tests.nix`.
|
Please implement (mostly) exhaustive unit tests
|
||||||
*/
|
for new functions in `./tests.nix`.
|
||||||
|
*/
|
||||||
let
|
let
|
||||||
|
|
||||||
# A copy of `lib.makeExtensible'` in order to document `extend`.
|
# A copy of `lib.makeExtensible'` in order to document `extend`.
|
||||||
# It has been leading to some trouble, so we have to document it specially.
|
# It has been leading to some trouble, so we have to document it specially.
|
||||||
makeExtensible' =
|
makeExtensible' =
|
||||||
rattrs:
|
rattrs:
|
||||||
let self = rattrs self // {
|
let
|
||||||
|
self = rattrs self // {
|
||||||
/**
|
/**
|
||||||
Patch the Nixpkgs library
|
Patch the Nixpkgs library
|
||||||
|
|
||||||
|
@ -37,11 +39,15 @@ let
|
||||||
*/
|
*/
|
||||||
extend = f: lib.makeExtensible (lib.extends f rattrs);
|
extend = f: lib.makeExtensible (lib.extends f rattrs);
|
||||||
};
|
};
|
||||||
in self;
|
in
|
||||||
|
self;
|
||||||
|
|
||||||
lib = makeExtensible' (self: let
|
lib = makeExtensible' (
|
||||||
|
self:
|
||||||
|
let
|
||||||
callLibs = file: import file { lib = self; };
|
callLibs = file: import file { lib = self; };
|
||||||
in {
|
in
|
||||||
|
{
|
||||||
|
|
||||||
# often used, or depending on very little
|
# often used, or depending on very little
|
||||||
trivial = callLibs ./trivial.nix;
|
trivial = callLibs ./trivial.nix;
|
||||||
|
@ -101,112 +107,460 @@ let
|
||||||
|
|
||||||
# TODO: For consistency, all builtins should also be available from a sub-library;
|
# TODO: For consistency, all builtins should also be available from a sub-library;
|
||||||
# these are the only ones that are currently not
|
# these are the only ones that are currently not
|
||||||
inherit (builtins) addErrorContext isPath trace typeOf unsafeGetAttrPos;
|
inherit (builtins)
|
||||||
inherit (self.trivial) id const pipe concat or and xor bitAnd bitOr bitXor
|
addErrorContext
|
||||||
bitNot boolToString mergeAttrs flip defaultTo mapNullable inNixShell isFloat min max
|
isPath
|
||||||
importJSON importTOML warn warnIf warnIfNot throwIf throwIfNot checkListOfEnum
|
trace
|
||||||
info showWarnings nixpkgsVersion version isInOldestRelease oldestSupportedReleaseIsAtLeast
|
typeOf
|
||||||
mod compare splitByAndCompare seq deepSeq lessThan add sub
|
unsafeGetAttrPos
|
||||||
functionArgs setFunctionArgs isFunction toFunction mirrorFunctionArgs
|
;
|
||||||
fromHexString toHexString toBaseDigits inPureEvalMode isBool isInt pathExists
|
inherit (self.trivial)
|
||||||
genericClosure readFile;
|
id
|
||||||
inherit (self.fixedPoints) fix fix' converge extends composeExtensions
|
const
|
||||||
composeManyExtensions makeExtensible makeExtensibleWithCustomName
|
pipe
|
||||||
toExtension;
|
concat
|
||||||
inherit (self.attrsets) attrByPath hasAttrByPath setAttrByPath
|
or
|
||||||
getAttrFromPath attrVals attrNames attrValues getAttrs catAttrs filterAttrs
|
and
|
||||||
filterAttrsRecursive foldlAttrs foldAttrs collect nameValuePair mapAttrs
|
xor
|
||||||
mapAttrs' mapAttrsToList attrsToList concatMapAttrs mapAttrsRecursive
|
bitAnd
|
||||||
mapAttrsRecursiveCond genAttrs isDerivation toDerivation optionalAttrs
|
bitOr
|
||||||
zipAttrsWithNames zipAttrsWith zipAttrs recursiveUpdateUntil
|
bitXor
|
||||||
recursiveUpdate matchAttrs mergeAttrsList overrideExisting showAttrPath getOutput getFirstOutput
|
bitNot
|
||||||
getBin getLib getStatic getDev getInclude getMan chooseDevOutputs zipWithNames zip
|
boolToString
|
||||||
recurseIntoAttrs dontRecurseIntoAttrs cartesianProduct cartesianProductOfSets
|
mergeAttrs
|
||||||
mapCartesianProduct updateManyAttrsByPath listToAttrs hasAttr getAttr isAttrs intersectAttrs removeAttrs;
|
flip
|
||||||
inherit (self.lists) singleton forEach map foldr fold foldl foldl' imap0 imap1
|
defaultTo
|
||||||
filter ifilter0 concatMap flatten remove findSingle findFirst any all count
|
mapNullable
|
||||||
optional optionals toList range replicate partition zipListsWith zipLists
|
inNixShell
|
||||||
reverseList listDfs toposort sort sortOn naturalSort compareLists
|
isFloat
|
||||||
take drop dropEnd sublist last init
|
min
|
||||||
crossLists unique allUnique intersectLists
|
max
|
||||||
subtractLists mutuallyExclusive groupBy groupBy' concatLists genList
|
importJSON
|
||||||
length head tail elem elemAt isList;
|
importTOML
|
||||||
inherit (self.strings) concatStrings concatMapStrings concatImapStrings
|
warn
|
||||||
stringLength substring isString replaceStrings
|
warnIf
|
||||||
intersperse concatStringsSep concatMapStringsSep concatMapAttrsStringSep
|
warnIfNot
|
||||||
concatImapStringsSep concatLines makeSearchPath makeSearchPathOutput
|
throwIf
|
||||||
makeLibraryPath makeIncludePath makeBinPath optionalString
|
throwIfNot
|
||||||
hasInfix hasPrefix hasSuffix stringToCharacters stringAsChars escape
|
checkListOfEnum
|
||||||
escapeShellArg escapeShellArgs
|
info
|
||||||
isStorePath isStringLike
|
showWarnings
|
||||||
isValidPosixName toShellVar toShellVars trim trimWith
|
nixpkgsVersion
|
||||||
escapeRegex escapeURL escapeXML replaceChars lowerChars
|
version
|
||||||
upperChars toLower toUpper toSentenceCase addContextFrom splitString
|
isInOldestRelease
|
||||||
removePrefix removeSuffix versionOlder versionAtLeast
|
oldestSupportedReleaseIsAtLeast
|
||||||
getName getVersion match split
|
mod
|
||||||
cmakeOptionType cmakeBool cmakeFeature
|
compare
|
||||||
mesonOption mesonBool mesonEnable
|
splitByAndCompare
|
||||||
nameFromURL enableFeature enableFeatureAs withFeature
|
seq
|
||||||
withFeatureAs fixedWidthString fixedWidthNumber
|
deepSeq
|
||||||
toInt toIntBase10 readPathsFromFile fileContents;
|
lessThan
|
||||||
inherit (self.stringsWithDeps) textClosureList textClosureMap
|
add
|
||||||
noDepEntry fullDepEntry packEntry stringAfter;
|
sub
|
||||||
inherit (self.customisation) overrideDerivation makeOverridable
|
functionArgs
|
||||||
callPackageWith callPackagesWith extendDerivation hydraJob
|
setFunctionArgs
|
||||||
makeScope makeScopeWithSplicing makeScopeWithSplicing'
|
isFunction
|
||||||
extendMkDerivation;
|
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
|
||||||
|
concatMapAttrsStringSep
|
||||||
|
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
|
||||||
|
toSentenceCase
|
||||||
|
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.derivations) lazyDerivation optionalDrvAttr warnOnInstantiate;
|
||||||
inherit (self.generators) mkLuaInline;
|
inherit (self.generators) mkLuaInline;
|
||||||
inherit (self.meta) addMetaAttrs dontDistribute setName updateName
|
inherit (self.meta)
|
||||||
appendToName mapDerivationAttrset setPrio lowPrio lowPrioSet hiPrio
|
addMetaAttrs
|
||||||
hiPrioSet licensesSpdx getLicenseFromSpdxId getLicenseFromSpdxIdOr
|
dontDistribute
|
||||||
getExe getExe';
|
setName
|
||||||
inherit (self.filesystem) pathType pathIsDirectory pathIsRegularFile
|
updateName
|
||||||
packagesFromDirectoryRecursive;
|
appendToName
|
||||||
inherit (self.sources) cleanSourceFilter
|
mapDerivationAttrset
|
||||||
cleanSource sourceByRegex sourceFilesBySuffices
|
setPrio
|
||||||
commitIdFromGitRepo cleanSourceWith pathHasContext
|
lowPrio
|
||||||
canCleanSource pathIsGitRepo;
|
lowPrioSet
|
||||||
inherit (self.modules) evalModules setDefaultModuleLocation
|
hiPrio
|
||||||
unifyModuleSyntax applyModuleArgsIfFunction mergeModules
|
hiPrioSet
|
||||||
mergeModules' mergeOptionDecls mergeDefinitions
|
licensesSpdx
|
||||||
pushDownProperties dischargeProperties filterOverrides
|
getLicenseFromSpdxId
|
||||||
sortProperties fixupOptionType mkIf mkAssert mkMerge mkOverride
|
getLicenseFromSpdxIdOr
|
||||||
mkOptionDefault mkDefault mkImageMediaOverride mkForce mkVMOverride
|
getExe
|
||||||
mkFixStrictness mkOrder mkBefore mkAfter mkAliasDefinitions
|
getExe'
|
||||||
mkAliasAndWrapDefinitions fixMergeModules mkRemovedOptionModule
|
;
|
||||||
mkRenamedOptionModule mkRenamedOptionModuleWith
|
inherit (self.filesystem)
|
||||||
mkMergedOptionModule mkChangedOptionModule
|
pathType
|
||||||
mkAliasOptionModule mkDerivedConfig doRename
|
pathIsDirectory
|
||||||
mkAliasOptionModuleMD;
|
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;
|
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
|
inherit (self.options)
|
||||||
mergeDefaultOption mergeOneOption mergeEqualOption mergeUniqueOption
|
isOption
|
||||||
getValues getFiles
|
mkEnableOption
|
||||||
optionAttrSetToDocList optionAttrSetToDocList'
|
mkSinkUndeclaredOptions
|
||||||
scrubOptionValue literalExpression literalExample
|
mergeDefaultOption
|
||||||
showOption showOptionWithDefLocs showFiles
|
mergeOneOption
|
||||||
unknownModule mkOption mkPackageOption mkPackageOptionMD
|
mergeEqualOption
|
||||||
literalMD;
|
mergeUniqueOption
|
||||||
inherit (self.types) isType setType defaultTypeMerge defaultFunctor
|
getValues
|
||||||
isOptionType mkOptionType;
|
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)
|
inherit (self.asserts)
|
||||||
assertMsg assertOneOf;
|
assertMsg
|
||||||
inherit (self.debug) traceIf traceVal traceValFn
|
assertOneOf
|
||||||
traceSeq traceSeqN traceValSeq
|
;
|
||||||
traceValSeqFn traceValSeqN traceValSeqNFn traceFnSeqN
|
inherit (self.debug)
|
||||||
runTests testAllTrue;
|
traceIf
|
||||||
inherit (self.misc) maybeEnv defaultMergeArg defaultMerge foldArgs
|
traceVal
|
||||||
maybeAttrNullable maybeAttr ifEnable checkFlag getValue
|
traceValFn
|
||||||
checkReqs uniqList uniqListExt condConcat lazyGenericClosure
|
traceSeq
|
||||||
innerModifySumArgs modifySumArgs innerClosePropagation
|
traceSeqN
|
||||||
closePropagation mapAttrsFlatten nvs setAttr setAttrMerge
|
traceValSeq
|
||||||
mergeAttrsWithFunc mergeAttrsConcatenateValues
|
traceValSeqFn
|
||||||
mergeAttrsNoOverride mergeAttrByFunc mergeAttrsByFuncDefaults
|
traceValSeqN
|
||||||
mergeAttrsByFuncDefaultsClean mergeAttrBy
|
traceValSeqNFn
|
||||||
fakeHash fakeSha256 fakeSha512
|
traceFnSeqN
|
||||||
nixType imap;
|
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)
|
inherit (self.versions)
|
||||||
splitVersion;
|
splitVersion
|
||||||
});
|
;
|
||||||
in lib
|
}
|
||||||
|
);
|
||||||
|
in
|
||||||
|
lib
|
||||||
|
|
|
@ -35,153 +35,212 @@ let
|
||||||
inherit (lib.attrsets) removeAttrs mapAttrsToList;
|
inherit (lib.attrsets) removeAttrs mapAttrsToList;
|
||||||
|
|
||||||
# returns default if env var is not set
|
# returns default if env var is not set
|
||||||
maybeEnv = name: default:
|
maybeEnv =
|
||||||
let value = builtins.getEnv name; in
|
name: default:
|
||||||
|
let
|
||||||
|
value = builtins.getEnv name;
|
||||||
|
in
|
||||||
if value == "" then default else value;
|
if value == "" then default else value;
|
||||||
|
|
||||||
defaultMergeArg = x : y: if builtins.isAttrs y then
|
defaultMergeArg = x: y: if builtins.isAttrs y then y else (y x);
|
||||||
y
|
|
||||||
else
|
|
||||||
(y x);
|
|
||||||
defaultMerge = x: y: x // (defaultMergeArg x y);
|
defaultMerge = x: y: x // (defaultMergeArg x y);
|
||||||
foldArgs = merger: f: init: x:
|
foldArgs =
|
||||||
let arg = (merger init (defaultMergeArg init x));
|
merger: f: init: x:
|
||||||
|
let
|
||||||
|
arg = (merger init (defaultMergeArg init x));
|
||||||
# now add the function with composed args already applied to the final attrs
|
# now add the function with composed args already applied to the final attrs
|
||||||
base = (setAttrMerge "passthru" {} (f arg)
|
base = (
|
||||||
( z: z // {
|
setAttrMerge "passthru" { } (f arg) (
|
||||||
|
z:
|
||||||
|
z
|
||||||
|
// {
|
||||||
function = foldArgs merger f arg;
|
function = foldArgs merger f arg;
|
||||||
args = (attrByPath ["passthru" "args"] {} z) // x;
|
args = (attrByPath [ "passthru" "args" ] { } z) // x;
|
||||||
} ));
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
withStdOverrides = base // {
|
withStdOverrides = base // {
|
||||||
override = base.passthru.function;
|
override = base.passthru.function;
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
withStdOverrides;
|
withStdOverrides;
|
||||||
|
|
||||||
|
|
||||||
# shortcut for attrByPath ["name"] default attrs
|
# shortcut for attrByPath ["name"] default attrs
|
||||||
maybeAttrNullable = maybeAttr;
|
maybeAttrNullable = maybeAttr;
|
||||||
|
|
||||||
# shortcut for attrByPath ["name"] default attrs
|
# 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
|
# Return the second argument if the first one is true or the empty version
|
||||||
# of the second argument.
|
# of the second argument.
|
||||||
ifEnable = cond: val:
|
ifEnable =
|
||||||
if cond then val
|
cond: val:
|
||||||
else if builtins.isList val then []
|
if cond then
|
||||||
else if builtins.isAttrs val then {}
|
val
|
||||||
|
else if builtins.isList val then
|
||||||
|
[ ]
|
||||||
|
else if builtins.isAttrs val then
|
||||||
|
{ }
|
||||||
# else if builtins.isString val then ""
|
# else if builtins.isString val then ""
|
||||||
else if val == true || val == false then false
|
else if val == true || val == false then
|
||||||
else null;
|
false
|
||||||
|
else
|
||||||
|
null;
|
||||||
|
|
||||||
# Return true only if there is an attribute and it is true.
|
# Return true only if there is an attribute and it is true.
|
||||||
checkFlag = attrSet: name:
|
checkFlag =
|
||||||
if name == "true" then true else
|
attrSet: name:
|
||||||
if name == "false" then false else
|
if name == "true" then
|
||||||
if (elem name (attrByPath ["flags"] [] attrSet)) then true else
|
true
|
||||||
attrByPath [name] false attrSet ;
|
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
|
# Input : attrSet, [ [name default] ... ], name
|
||||||
# Output : its value or default.
|
# Output : its value or default.
|
||||||
getValue = attrSet: argList: name:
|
getValue =
|
||||||
( attrByPath [name] (if checkFlag attrSet name then true else
|
attrSet: argList: name:
|
||||||
if argList == [] then null else
|
(attrByPath [ name ] (
|
||||||
let x = builtins.head argList; in
|
if checkFlag attrSet name then
|
||||||
if (head x) == name then
|
true
|
||||||
(head (tail x))
|
else if argList == [ ] then
|
||||||
else (getValue attrSet
|
null
|
||||||
(tail argList) name)) attrSet );
|
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..] ... ]
|
# Input : attrSet, [[name default] ...], [ [flagname reqs..] ... ]
|
||||||
# Output : are reqs satisfied? It's asserted.
|
# Output : are reqs satisfied? It's asserted.
|
||||||
checkReqs = attrSet: argList: condList:
|
checkReqs =
|
||||||
|
attrSet: argList: condList:
|
||||||
|
(foldr and true (
|
||||||
|
map (
|
||||||
|
x:
|
||||||
|
let
|
||||||
|
name = (head x);
|
||||||
|
in
|
||||||
|
|
||||||
(
|
(
|
||||||
foldr and true
|
(checkFlag attrSet name)
|
||||||
(map (x: let name = (head x); in
|
-> (foldr and true (
|
||||||
|
map (
|
||||||
((checkFlag attrSet name) ->
|
y:
|
||||||
(foldr and true
|
let
|
||||||
(map (y: let val=(getValue attrSet argList y); in
|
val = (getValue attrSet argList y);
|
||||||
(val!=null) && (val!=false))
|
in
|
||||||
(tail x))))) condList));
|
(val != null) && (val != false)
|
||||||
|
) (tail x)
|
||||||
|
))
|
||||||
|
)
|
||||||
|
) condList
|
||||||
|
));
|
||||||
|
|
||||||
# This function has O(n^2) performance.
|
# This function has O(n^2) performance.
|
||||||
uniqList = { inputList, acc ? [] }:
|
uniqList =
|
||||||
let go = xs: acc:
|
{
|
||||||
if xs == []
|
inputList,
|
||||||
then []
|
acc ? [ ],
|
||||||
else let x = head xs;
|
}:
|
||||||
y = if elem x acc then [] else [x];
|
let
|
||||||
in y ++ go (tail xs) (y ++ acc);
|
go =
|
||||||
in go inputList acc;
|
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,
|
uniqListExt =
|
||||||
outputList ? [],
|
{
|
||||||
|
inputList,
|
||||||
|
outputList ? [ ],
|
||||||
getter ? (x: x),
|
getter ? (x: x),
|
||||||
compare ? (x: y: x==y) }:
|
compare ? (x: y: x == y),
|
||||||
if inputList == [] then outputList else
|
}:
|
||||||
let x = head inputList;
|
if inputList == [ ] then
|
||||||
|
outputList
|
||||||
|
else
|
||||||
|
let
|
||||||
|
x = head inputList;
|
||||||
isX = y: (compare (getter y) (getter x));
|
isX = y: (compare (getter y) (getter x));
|
||||||
newOutputList = outputList ++
|
newOutputList = outputList ++ (if any isX outputList then [ ] else [ x ]);
|
||||||
(if any isX outputList then [] else [x]);
|
in
|
||||||
in uniqListExt { outputList = newOutputList;
|
uniqListExt {
|
||||||
|
outputList = newOutputList;
|
||||||
inputList = (tail inputList);
|
inputList = (tail inputList);
|
||||||
inherit getter compare;
|
inherit getter compare;
|
||||||
};
|
};
|
||||||
|
|
||||||
condConcat = name: list: checker:
|
condConcat =
|
||||||
if list == [] then name else
|
name: list: checker:
|
||||||
if checker (head list) then
|
if list == [ ] then
|
||||||
condConcat
|
name
|
||||||
(name + (head (tail list)))
|
else if checker (head list) then
|
||||||
(tail (tail list))
|
condConcat (name + (head (tail list))) (tail (tail list)) checker
|
||||||
checker
|
else
|
||||||
else condConcat
|
condConcat name (tail (tail list)) checker;
|
||||||
name (tail (tail list)) checker;
|
|
||||||
|
|
||||||
lazyGenericClosure = {startSet, operator}:
|
lazyGenericClosure =
|
||||||
|
{ startSet, operator }:
|
||||||
let
|
let
|
||||||
work = list: doneKeys: result:
|
work =
|
||||||
if list == [] then
|
list: doneKeys: result:
|
||||||
|
if list == [ ] then
|
||||||
result
|
result
|
||||||
else
|
else
|
||||||
let x = head list; key = x.key; in
|
let
|
||||||
|
x = head list;
|
||||||
|
key = x.key;
|
||||||
|
in
|
||||||
if elem key doneKeys then
|
if elem key doneKeys then
|
||||||
work (tail list) doneKeys result
|
work (tail list) doneKeys result
|
||||||
else
|
else
|
||||||
work (tail list ++ operator x) ([key] ++ doneKeys) ([x] ++ result);
|
work (tail list ++ operator x) ([ key ] ++ doneKeys) ([ x ] ++ result);
|
||||||
in
|
in
|
||||||
work startSet [] [];
|
work startSet [ ] [ ];
|
||||||
|
|
||||||
innerModifySumArgs = f: x: a: b: if b == null then (f a b) // x else
|
innerModifySumArgs =
|
||||||
innerModifySumArgs f x (a // b);
|
f: x: a: b:
|
||||||
modifySumArgs = f: x: innerModifySumArgs f x {};
|
if b == null then (f a b) // x else innerModifySumArgs f x (a // b);
|
||||||
|
modifySumArgs = f: x: innerModifySumArgs f x { };
|
||||||
|
|
||||||
|
innerClosePropagation =
|
||||||
innerClosePropagation = acc: xs:
|
acc: xs:
|
||||||
if xs == []
|
if xs == [ ] then
|
||||||
then acc
|
acc
|
||||||
else let y = head xs;
|
else
|
||||||
|
let
|
||||||
|
y = head xs;
|
||||||
ys = tail xs;
|
ys = tail xs;
|
||||||
in if ! isAttrs y
|
in
|
||||||
then innerClosePropagation acc ys
|
if !isAttrs y then
|
||||||
else let acc' = [y] ++ acc;
|
innerClosePropagation acc ys
|
||||||
in innerClosePropagation
|
else
|
||||||
acc'
|
let
|
||||||
(uniqList { inputList = (maybeAttrNullable "propagatedBuildInputs" [] y)
|
acc' = [ y ] ++ acc;
|
||||||
++ (maybeAttrNullable "propagatedNativeBuildInputs" [] y)
|
in
|
||||||
|
innerClosePropagation acc' (uniqList {
|
||||||
|
inputList =
|
||||||
|
(maybeAttrNullable "propagatedBuildInputs" [ ] y)
|
||||||
|
++ (maybeAttrNullable "propagatedNativeBuildInputs" [ ] y)
|
||||||
++ ys;
|
++ ys;
|
||||||
acc = acc';
|
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
|
# 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
|
# Using a list of derivations, it generates the full closure of the propagatedXXXBuildInputs
|
||||||
|
@ -189,28 +248,35 @@ let
|
||||||
# attribute of each derivation.
|
# attribute of each derivation.
|
||||||
# On some benchmarks, it performs up to 15 times faster than closePropagation.
|
# On some benchmarks, it performs up to 15 times faster than closePropagation.
|
||||||
# See https://github.com/NixOS/nixpkgs/pull/194391 for details.
|
# See https://github.com/NixOS/nixpkgs/pull/194391 for details.
|
||||||
closePropagationFast = list:
|
closePropagationFast =
|
||||||
builtins.map (x: x.val) (builtins.genericClosure {
|
list:
|
||||||
|
builtins.map (x: x.val) (
|
||||||
|
builtins.genericClosure {
|
||||||
startSet = builtins.map (x: {
|
startSet = builtins.map (x: {
|
||||||
key = x.outPath;
|
key = x.outPath;
|
||||||
val = x;
|
val = x;
|
||||||
}) (builtins.filter (x: x != null) list);
|
}) (builtins.filter (x: x != null) list);
|
||||||
operator = item:
|
operator =
|
||||||
|
item:
|
||||||
if !builtins.isAttrs item.val then
|
if !builtins.isAttrs item.val then
|
||||||
[ ]
|
[ ]
|
||||||
else
|
else
|
||||||
builtins.concatMap (x:
|
builtins.concatMap (
|
||||||
if x != null then [{
|
x:
|
||||||
|
if x != null then
|
||||||
|
[
|
||||||
|
{
|
||||||
key = x.outPath;
|
key = x.outPath;
|
||||||
val = x;
|
val = x;
|
||||||
}] else
|
}
|
||||||
[ ]) ((item.val.propagatedBuildInputs or [ ])
|
]
|
||||||
++ (item.val.propagatedNativeBuildInputs or [ ]));
|
else
|
||||||
});
|
[ ]
|
||||||
|
) ((item.val.propagatedBuildInputs or [ ]) ++ (item.val.propagatedNativeBuildInputs or [ ]));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
closePropagation = if builtins ? genericClosure
|
closePropagation = if builtins ? genericClosure then closePropagationFast else closePropagationSlow;
|
||||||
then closePropagationFast
|
|
||||||
else closePropagationSlow;
|
|
||||||
|
|
||||||
# calls a function (f attr value ) for each record item. returns a list
|
# 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;
|
mapAttrsFlatten = warn "lib.misc.mapAttrsFlatten is deprecated, please use lib.attrsets.mapAttrsToList instead." mapAttrsToList;
|
||||||
|
@ -218,26 +284,29 @@ let
|
||||||
# attribute set containing one attribute
|
# attribute set containing one attribute
|
||||||
nvs = name: value: listToAttrs [ (nameValuePair name value) ];
|
nvs = name: value: listToAttrs [ (nameValuePair name value) ];
|
||||||
# adds / replaces an attribute of an attribute set
|
# 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 (similar to mergeAttrsWithFunc but only merges the values of a particular name)
|
||||||
# setAttrMerge "a" [] { a = [2];} (x: x ++ [3]) -> { a = [2 3]; }
|
# setAttrMerge "a" [] { a = [2];} (x: x ++ [3]) -> { a = [2 3]; }
|
||||||
# setAttrMerge "a" [] { } (x: x ++ [3]) -> { a = [ 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));
|
setAttr attrs name (f (maybeAttr name default attrs));
|
||||||
|
|
||||||
# Using f = a: b = b the result is similar to //
|
# Using f = a: b = b the result is similar to //
|
||||||
# merge attributes with custom function handling the case that the attribute
|
# merge attributes with custom function handling the case that the attribute
|
||||||
# exists in both sets
|
# exists in both sets
|
||||||
mergeAttrsWithFunc = f: set1: set2:
|
mergeAttrsWithFunc =
|
||||||
foldr (n: set: if set ? ${n}
|
f: set1: set2:
|
||||||
then setAttr set n (f set.${n} set2.${n})
|
foldr (n: set: if set ? ${n} then setAttr set n (f set.${n} set2.${n}) else set) (set2 // set1) (
|
||||||
else set )
|
attrNames set2
|
||||||
(set2 // set1) (attrNames set2);
|
);
|
||||||
|
|
||||||
# merging two attribute set concatenating the values of same attribute names
|
# merging two attribute set concatenating the values of same attribute names
|
||||||
# eg { a = 7; } { a = [ 2 3 ]; } becomes { a = [ 7 2 3 ]; }
|
# 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
|
# merges attributes using //, if a name exists in both attributes
|
||||||
# an error will be triggered unless its listed in mergeLists
|
# 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?
|
# 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
|
# in these cases the first buildPhase will override the second one
|
||||||
# ! deprecated, use mergeAttrByFunc instead
|
# ! deprecated, use mergeAttrByFunc instead
|
||||||
mergeAttrsNoOverride = { mergeLists ? ["buildInputs" "propagatedBuildInputs"],
|
mergeAttrsNoOverride =
|
||||||
overrideSnd ? [ "buildPhase" ]
|
{
|
||||||
}: attrs1: attrs2:
|
mergeLists ? [
|
||||||
foldr (n: set:
|
"buildInputs"
|
||||||
setAttr set n ( if set ? ${n}
|
"propagatedBuildInputs"
|
||||||
then # merge
|
],
|
||||||
if elem n mergeLists # attribute contains list, merge them by concatenating
|
overrideSnd ? [ "buildPhase" ],
|
||||||
then attrs2.${n} ++ attrs1.${n}
|
}:
|
||||||
else if elem n overrideSnd
|
attrs1: attrs2:
|
||||||
then attrs1.${n}
|
foldr (
|
||||||
else throw "error mergeAttrsNoOverride, attribute ${n} given in both attributes - no merge func defined"
|
n: set:
|
||||||
else attrs2.${n} # add attribute not existing in attr1
|
setAttr set n (
|
||||||
)) attrs1 (attrNames attrs2);
|
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:
|
# example usage:
|
||||||
# mergeAttrByFunc {
|
# mergeAttrByFunc {
|
||||||
|
@ -272,48 +352,82 @@ let
|
||||||
# { mergeAttrsBy = [...]; buildInputs = [ a b c d ]; }
|
# { mergeAttrsBy = [...]; buildInputs = [ a b c d ]; }
|
||||||
# is used by defaultOverridableDelayableArgs and can be used when composing using
|
# is used by defaultOverridableDelayableArgs and can be used when composing using
|
||||||
# foldArgs, composedArgsAndFun or applyAndFun. Example: composableDerivation in all-packages.nix
|
# foldArgs, composedArgsAndFun or applyAndFun. Example: composableDerivation in all-packages.nix
|
||||||
mergeAttrByFunc = x: y:
|
mergeAttrByFunc =
|
||||||
|
x: y:
|
||||||
let
|
let
|
||||||
mergeAttrBy2 = { mergeAttrBy = mergeAttrs; }
|
mergeAttrBy2 =
|
||||||
// (maybeAttr "mergeAttrBy" {} x)
|
{ mergeAttrBy = mergeAttrs; } // (maybeAttr "mergeAttrBy" { } x) // (maybeAttr "mergeAttrBy" { } y);
|
||||||
// (maybeAttr "mergeAttrBy" {} y); in
|
in
|
||||||
foldr mergeAttrs {} [
|
foldr mergeAttrs { } [
|
||||||
x y
|
x
|
||||||
(mapAttrs ( a: v: # merge special names using given functions
|
y
|
||||||
if x ? ${a}
|
(mapAttrs
|
||||||
then if y ? ${a}
|
(
|
||||||
then v x.${a} y.${a} # both have attr, use merge func
|
a: v: # merge special names using given functions
|
||||||
else x.${a} # only x has attr
|
if x ? ${a} then
|
||||||
else y.${a} # only y has attr)
|
if y ? ${a} then
|
||||||
) (removeAttrs mergeAttrBy2
|
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
|
# don't merge attrs which are neither in x nor y
|
||||||
(filter (a: ! x ? ${a} && ! y ? ${a})
|
(filter (a: !x ? ${a} && !y ? ${a}) (attrNames mergeAttrBy2))
|
||||||
(attrNames mergeAttrBy2))
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
];
|
];
|
||||||
mergeAttrsByFuncDefaults = foldl mergeAttrByFunc { inherit mergeAttrBy; };
|
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)
|
# sane defaults (same name as attr name so that inherit can be used)
|
||||||
mergeAttrBy = # { buildInputs = concatList; [...]; passthru = mergeAttr; [..]; }
|
mergeAttrBy = # { buildInputs = concatList; [...]; passthru = mergeAttr; [..]; }
|
||||||
listToAttrs (map (n: nameValuePair n concat)
|
listToAttrs (
|
||||||
[ "nativeBuildInputs" "buildInputs" "propagatedBuildInputs" "configureFlags" "prePhases" "postAll" "patches" ])
|
map (n: nameValuePair n concat) [
|
||||||
// listToAttrs (map (n: nameValuePair n mergeAttrs) [ "passthru" "meta" "cfg" "flags" ])
|
"nativeBuildInputs"
|
||||||
// listToAttrs (map (n: nameValuePair n (a: b: "${a}\n${b}") ) [ "preConfigure" "postInstall" ])
|
"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:
|
nixType =
|
||||||
|
x:
|
||||||
if isAttrs x then
|
if isAttrs x then
|
||||||
if x ? outPath then "derivation"
|
if x ? outPath then "derivation" else "attrs"
|
||||||
else "attrs"
|
else if isFunction x then
|
||||||
else if isFunction x then "function"
|
"function"
|
||||||
else if isList x then "list"
|
else if isList x then
|
||||||
else if x == true then "bool"
|
"list"
|
||||||
else if x == false then "bool"
|
else if x == true then
|
||||||
else if x == null then "null"
|
"bool"
|
||||||
else if isInt x then "int"
|
else if x == false then
|
||||||
else "string";
|
"bool"
|
||||||
|
else if x == null then
|
||||||
|
"null"
|
||||||
|
else if isInt x then
|
||||||
|
"int"
|
||||||
|
else
|
||||||
|
"string";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
# Deprecated
|
# Deprecated
|
||||||
|
|
|
@ -59,23 +59,26 @@ in
|
||||||
pathType =
|
pathType =
|
||||||
builtins.readFileType or
|
builtins.readFileType or
|
||||||
# Nix <2.14 compatibility shim
|
# Nix <2.14 compatibility shim
|
||||||
(path:
|
(
|
||||||
if ! pathExists path
|
path:
|
||||||
|
if
|
||||||
|
!pathExists path
|
||||||
# Fail irrecoverably to mimic the historic behavior of this function and
|
# Fail irrecoverably to mimic the historic behavior of this function and
|
||||||
# the new builtins.readFileType
|
# 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
|
# The filesystem root is the only path where `dirOf / == /` and
|
||||||
# `baseNameOf /` is not valid. We can detect this and directly return
|
# `baseNameOf /` is not valid. We can detect this and directly return
|
||||||
# "directory", since we know the filesystem root can't be anything else.
|
# "directory", since we know the filesystem root can't be anything else.
|
||||||
else if dirOf path == path
|
else if dirOf path == path then
|
||||||
then "directory"
|
"directory"
|
||||||
else (readDir (dirOf path)).${baseNameOf path}
|
else
|
||||||
|
(readDir (dirOf path)).${baseNameOf path}
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Whether a path exists and is a directory.
|
Whether a path exists and is a directory.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`path`
|
`path`
|
||||||
|
@ -105,13 +108,11 @@ in
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
pathIsDirectory = path:
|
pathIsDirectory = path: pathExists path && pathType path == "directory";
|
||||||
pathExists path && pathType path == "directory";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Whether a path exists and is a regular file, meaning not a symlink or any other special file type.
|
Whether a path exists and is a regular file, meaning not a symlink or any other special file type.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`path`
|
`path`
|
||||||
|
@ -141,15 +142,13 @@ in
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
pathIsRegularFile = path:
|
pathIsRegularFile = path: pathExists path && pathType path == "regular";
|
||||||
pathExists path && pathType path == "regular";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
A map of all haskell packages defined in the given path,
|
A map of all haskell packages defined in the given path,
|
||||||
identified by having a cabal file with the same name as the
|
identified by having a cabal file with the same name as the
|
||||||
directory itself.
|
directory itself.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`root`
|
`root`
|
||||||
|
@ -164,25 +163,25 @@ in
|
||||||
*/
|
*/
|
||||||
haskellPathsInDir =
|
haskellPathsInDir =
|
||||||
root:
|
root:
|
||||||
let # Files in the root
|
let
|
||||||
|
# Files in the root
|
||||||
root-files = builtins.attrNames (builtins.readDir root);
|
root-files = builtins.attrNames (builtins.readDir root);
|
||||||
# Files with their full paths
|
# Files with their full paths
|
||||||
root-files-with-paths =
|
root-files-with-paths = map (file: {
|
||||||
map (file:
|
name = file;
|
||||||
{ name = file; value = root + "/${file}"; }
|
value = root + "/${file}";
|
||||||
) root-files;
|
}) root-files;
|
||||||
# Subdirectories of the root with a cabal file.
|
# Subdirectories of the root with a cabal file.
|
||||||
cabal-subdirs =
|
cabal-subdirs = builtins.filter (
|
||||||
builtins.filter ({ name, value }:
|
{ name, value }: builtins.pathExists (value + "/${name}.cabal")
|
||||||
builtins.pathExists (value + "/${name}.cabal")
|
|
||||||
) root-files-with-paths;
|
) root-files-with-paths;
|
||||||
in builtins.listToAttrs cabal-subdirs;
|
in
|
||||||
|
builtins.listToAttrs cabal-subdirs;
|
||||||
/**
|
/**
|
||||||
Find the first directory containing a file matching 'pattern'
|
Find the first directory containing a file matching 'pattern'
|
||||||
upward from a given 'file'.
|
upward from a given 'file'.
|
||||||
Returns 'null' if no directories contain a file matching 'pattern'.
|
Returns 'null' if no directories contain a file matching 'pattern'.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`pattern`
|
`pattern`
|
||||||
|
@ -200,30 +199,33 @@ in
|
||||||
```
|
```
|
||||||
*/
|
*/
|
||||||
locateDominatingFile =
|
locateDominatingFile =
|
||||||
pattern:
|
pattern: file:
|
||||||
file:
|
let
|
||||||
let go = path:
|
go =
|
||||||
let files = builtins.attrNames (builtins.readDir path);
|
path:
|
||||||
matches = builtins.filter (match: match != null)
|
let
|
||||||
(map (builtins.match pattern) files);
|
files = builtins.attrNames (builtins.readDir path);
|
||||||
|
matches = builtins.filter (match: match != null) (map (builtins.match pattern) files);
|
||||||
in
|
in
|
||||||
if builtins.length matches != 0
|
if builtins.length matches != 0 then
|
||||||
then { inherit path matches; }
|
{ inherit path matches; }
|
||||||
else if path == /.
|
else if path == /. then
|
||||||
then null
|
null
|
||||||
else go (dirOf path);
|
else
|
||||||
|
go (dirOf path);
|
||||||
parent = dirOf file;
|
parent = dirOf file;
|
||||||
isDir =
|
isDir =
|
||||||
let base = baseNameOf file;
|
let
|
||||||
|
base = baseNameOf file;
|
||||||
type = (builtins.readDir parent).${base} or null;
|
type = (builtins.readDir parent).${base} or null;
|
||||||
in file == /. || type == "directory";
|
in
|
||||||
in go (if isDir then file else parent);
|
file == /. || type == "directory";
|
||||||
|
in
|
||||||
|
go (if isDir then file else parent);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Given a directory, return a flattened list of all files within it recursively.
|
Given a directory, return a flattened list of all files within it recursively.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`dir`
|
`dir`
|
||||||
|
@ -238,12 +240,15 @@ in
|
||||||
*/
|
*/
|
||||||
listFilesRecursive =
|
listFilesRecursive =
|
||||||
dir:
|
dir:
|
||||||
lib.flatten (lib.mapAttrsToList (name: type:
|
lib.flatten (
|
||||||
|
lib.mapAttrsToList (
|
||||||
|
name: type:
|
||||||
if type == "directory" then
|
if type == "directory" then
|
||||||
lib.filesystem.listFilesRecursive (dir + "/${name}")
|
lib.filesystem.listFilesRecursive (dir + "/${name}")
|
||||||
else
|
else
|
||||||
dir + "/${name}"
|
dir + "/${name}"
|
||||||
) (builtins.readDir dir));
|
) (builtins.readDir dir)
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Transform a directory tree containing package files suitable for
|
Transform a directory tree containing package files suitable for
|
||||||
|
@ -373,27 +378,47 @@ in
|
||||||
*/
|
*/
|
||||||
packagesFromDirectoryRecursive =
|
packagesFromDirectoryRecursive =
|
||||||
let
|
let
|
||||||
inherit (lib) concatMapAttrs id makeScope recurseIntoAttrs removeSuffix;
|
inherit (lib)
|
||||||
|
concatMapAttrs
|
||||||
|
id
|
||||||
|
makeScope
|
||||||
|
recurseIntoAttrs
|
||||||
|
removeSuffix
|
||||||
|
;
|
||||||
inherit (lib.path) append;
|
inherit (lib.path) append;
|
||||||
|
|
||||||
# Generate an attrset corresponding to a given directory.
|
# Generate an attrset corresponding to a given directory.
|
||||||
# This function is outside `packagesFromDirectoryRecursive`'s lambda expression,
|
# This function is outside `packagesFromDirectoryRecursive`'s lambda expression,
|
||||||
# to prevent accidentally using its parameters.
|
# to prevent accidentally using its parameters.
|
||||||
processDir = { callPackage, directory, ... }@args:
|
processDir =
|
||||||
concatMapAttrs (name: type:
|
{ callPackage, directory, ... }@args:
|
||||||
|
concatMapAttrs (
|
||||||
|
name: type:
|
||||||
# for each directory entry
|
# for each directory entry
|
||||||
let path = append directory name; in
|
let
|
||||||
if type == "directory" then {
|
path = append directory name;
|
||||||
|
in
|
||||||
|
if type == "directory" then
|
||||||
|
{
|
||||||
# recurse into directories
|
# recurse into directories
|
||||||
"${name}" = packagesFromDirectoryRecursive (args // {
|
"${name}" = packagesFromDirectoryRecursive (
|
||||||
|
args
|
||||||
|
// {
|
||||||
directory = path;
|
directory = path;
|
||||||
});
|
}
|
||||||
} else if type == "regular" && hasSuffix ".nix" name then {
|
);
|
||||||
|
}
|
||||||
|
else if type == "regular" && hasSuffix ".nix" name then
|
||||||
|
{
|
||||||
# call .nix files
|
# call .nix files
|
||||||
"${removeSuffix ".nix" name}" = callPackage path {};
|
"${removeSuffix ".nix" name}" = callPackage path { };
|
||||||
} else if type == "regular" then {
|
}
|
||||||
|
else if type == "regular" then
|
||||||
|
{
|
||||||
# ignore non-nix files
|
# ignore non-nix files
|
||||||
} else throw ''
|
}
|
||||||
|
else
|
||||||
|
throw ''
|
||||||
lib.filesystem.packagesFromDirectoryRecursive: Unsupported file type ${type} at path ${toString path}
|
lib.filesystem.packagesFromDirectoryRecursive: Unsupported file type ${type} at path ${toString path}
|
||||||
''
|
''
|
||||||
) (builtins.readDir directory);
|
) (builtins.readDir directory);
|
||||||
|
@ -408,20 +433,25 @@ in
|
||||||
in
|
in
|
||||||
if pathExists defaultPath then
|
if pathExists defaultPath then
|
||||||
# if `${directory}/package.nix` exists, call it directly
|
# if `${directory}/package.nix` exists, call it directly
|
||||||
callPackage defaultPath {}
|
callPackage defaultPath { }
|
||||||
else if args ? newScope then
|
else if args ? newScope then
|
||||||
# Create a new scope and mark it `recurseForDerivations`.
|
# Create a new scope and mark it `recurseForDerivations`.
|
||||||
# This lets the packages refer to each other.
|
# This lets the packages refer to each other.
|
||||||
# See:
|
# See:
|
||||||
# [lib.makeScope](https://nixos.org/manual/nixpkgs/unstable/#function-library-lib.customisation.makeScope) and
|
# [lib.makeScope](https://nixos.org/manual/nixpkgs/unstable/#function-library-lib.customisation.makeScope) and
|
||||||
# [lib.recurseIntoAttrs](https://nixos.org/manual/nixpkgs/unstable/#function-library-lib.customisation.makeScope)
|
# [lib.recurseIntoAttrs](https://nixos.org/manual/nixpkgs/unstable/#function-library-lib.customisation.makeScope)
|
||||||
recurseIntoAttrs (makeScope newScope (self:
|
recurseIntoAttrs (
|
||||||
|
makeScope newScope (
|
||||||
|
self:
|
||||||
# generate the attrset representing the directory, using the new scope's `callPackage` and `newScope`
|
# generate the attrset representing the directory, using the new scope's `callPackage` and `newScope`
|
||||||
processDir (args // {
|
processDir (
|
||||||
|
args
|
||||||
|
// {
|
||||||
inherit (self) callPackage newScope;
|
inherit (self) callPackage newScope;
|
||||||
})
|
}
|
||||||
))
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
else
|
else
|
||||||
processDir args
|
processDir args;
|
||||||
;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,7 +74,8 @@ let
|
||||||
;
|
;
|
||||||
|
|
||||||
## -- HELPER FUNCTIONS & DEFAULTS --
|
## -- HELPER FUNCTIONS & DEFAULTS --
|
||||||
in rec {
|
in
|
||||||
|
rec {
|
||||||
/**
|
/**
|
||||||
Convert a value to a sensible default string representation.
|
Convert a value to a sensible default string representation.
|
||||||
The builtin `toString` function has some strange defaults,
|
The builtin `toString` function has some strange defaults,
|
||||||
|
@ -88,32 +89,44 @@ in rec {
|
||||||
`v`
|
`v`
|
||||||
: 2\. Function argument
|
: 2\. Function argument
|
||||||
*/
|
*/
|
||||||
mkValueStringDefault = {}: v:
|
mkValueStringDefault =
|
||||||
let err = t: v: abort
|
{ }:
|
||||||
("generators.mkValueStringDefault: " +
|
v:
|
||||||
"${t} not supported: ${toPretty {} v}");
|
let
|
||||||
in if isInt v then toString v
|
err = t: v: abort ("generators.mkValueStringDefault: " + "${t} not supported: ${toPretty { } v}");
|
||||||
|
in
|
||||||
|
if isInt v then
|
||||||
|
toString v
|
||||||
# convert derivations to store paths
|
# 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
|
# 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
|
# 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
|
# here it returns to "", which is even less of a good default
|
||||||
else if false == v then "false"
|
else if false == v then
|
||||||
else if null == v then "null"
|
"false"
|
||||||
|
else if null == v then
|
||||||
|
"null"
|
||||||
# if you have lists you probably want to replace this
|
# 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
|
# 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
|
# 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,
|
# Floats currently can't be converted to precise strings,
|
||||||
# condition warning on nix version once this isn't a problem anymore
|
# condition warning on nix version once this isn't a problem anymore
|
||||||
# See https://github.com/NixOS/nix/pull/3480
|
# See https://github.com/NixOS/nix/pull/3480
|
||||||
else if isFloat v then floatToString v
|
else if isFloat v then
|
||||||
else err "this value is" (toString v);
|
floatToString v
|
||||||
|
else
|
||||||
|
err "this value is" (toString v);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Generate a line of key k and value v, separated by
|
Generate a line of key k and value v, separated by
|
||||||
|
@ -145,15 +158,15 @@ in rec {
|
||||||
|
|
||||||
: 4\. Function argument
|
: 4\. Function argument
|
||||||
*/
|
*/
|
||||||
mkKeyValueDefault = {
|
mkKeyValueDefault =
|
||||||
mkValueString ? mkValueStringDefault {}
|
{
|
||||||
}: sep: k: v:
|
mkValueString ? mkValueStringDefault { },
|
||||||
"${escape [sep] k}${sep}${mkValueString v}";
|
}:
|
||||||
|
sep: k: v:
|
||||||
|
"${escape [ sep ] k}${sep}${mkValueString v}";
|
||||||
|
|
||||||
## -- FILE FORMAT GENERATORS --
|
## -- FILE FORMAT GENERATORS --
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Generate a key-value-style config file from an attrset.
|
Generate a key-value-style config file from an attrset.
|
||||||
|
|
||||||
|
@ -169,19 +182,22 @@ in rec {
|
||||||
|
|
||||||
: indent (optional, default: `""`)
|
: indent (optional, default: `""`)
|
||||||
: Initial indentation level
|
: Initial indentation level
|
||||||
|
|
||||||
*/
|
*/
|
||||||
toKeyValue = {
|
toKeyValue =
|
||||||
mkKeyValue ? mkKeyValueDefault {} "=",
|
{
|
||||||
|
mkKeyValue ? mkKeyValueDefault { } "=",
|
||||||
listsAsDuplicateKeys ? false,
|
listsAsDuplicateKeys ? false,
|
||||||
indent ? ""
|
indent ? "",
|
||||||
}:
|
}:
|
||||||
let mkLine = k: v: indent + mkKeyValue k v + "\n";
|
let
|
||||||
mkLines = if listsAsDuplicateKeys
|
mkLine = k: v: indent + mkKeyValue k v + "\n";
|
||||||
then k: v: map (mkLine k) (if isList v then v else [v])
|
mkLines =
|
||||||
else k: v: [ (mkLine k v) ];
|
if listsAsDuplicateKeys then
|
||||||
in attrs: concatStrings (concatLists (mapAttrsToList mkLines attrs));
|
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
|
Generate an INI-style config file from an
|
||||||
|
@ -225,19 +241,24 @@ in rec {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
toINI = {
|
toINI =
|
||||||
|
{
|
||||||
mkSectionName ? (name: escape [ "[" "]" ] name),
|
mkSectionName ? (name: escape [ "[" "]" ] name),
|
||||||
mkKeyValue ? mkKeyValueDefault {} "=",
|
mkKeyValue ? mkKeyValueDefault { } "=",
|
||||||
listsAsDuplicateKeys ? false
|
listsAsDuplicateKeys ? false,
|
||||||
}: attrsOfAttrs:
|
}:
|
||||||
|
attrsOfAttrs:
|
||||||
let
|
let
|
||||||
# map function to string for each key val
|
# map function to string for each key val
|
||||||
mapAttrsToStringsSep = sep: mapFn: attrs:
|
mapAttrsToStringsSep =
|
||||||
concatStringsSep sep
|
sep: mapFn: attrs:
|
||||||
(mapAttrsToList mapFn attrs);
|
concatStringsSep sep (mapAttrsToList mapFn attrs);
|
||||||
mkSection = sectName: sectValues: ''
|
mkSection =
|
||||||
|
sectName: sectValues:
|
||||||
|
''
|
||||||
[${mkSectionName sectName}]
|
[${mkSectionName sectName}]
|
||||||
'' + toKeyValue { inherit mkKeyValue listsAsDuplicateKeys; } sectValues;
|
''
|
||||||
|
+ toKeyValue { inherit mkKeyValue listsAsDuplicateKeys; } sectValues;
|
||||||
in
|
in
|
||||||
# map input to ini sections
|
# map input to ini sections
|
||||||
mapAttrsToStringsSep "\n" mkSection attrsOfAttrs;
|
mapAttrsToStringsSep "\n" mkSection attrsOfAttrs;
|
||||||
|
@ -303,15 +324,22 @@ in rec {
|
||||||
`generators.toINI` directly, which only takes
|
`generators.toINI` directly, which only takes
|
||||||
the part in `sections`.
|
the part in `sections`.
|
||||||
*/
|
*/
|
||||||
toINIWithGlobalSection = {
|
toINIWithGlobalSection =
|
||||||
|
{
|
||||||
mkSectionName ? (name: escape [ "[" "]" ] name),
|
mkSectionName ? (name: escape [ "[" "]" ] name),
|
||||||
mkKeyValue ? mkKeyValueDefault {} "=",
|
mkKeyValue ? mkKeyValueDefault { } "=",
|
||||||
listsAsDuplicateKeys ? false
|
listsAsDuplicateKeys ? false,
|
||||||
}: { globalSection, sections ? {} }:
|
}:
|
||||||
( if globalSection == {}
|
{
|
||||||
then ""
|
globalSection,
|
||||||
else (toKeyValue { inherit mkKeyValue listsAsDuplicateKeys; } globalSection)
|
sections ? { },
|
||||||
+ "\n")
|
}:
|
||||||
|
(
|
||||||
|
if globalSection == { } then
|
||||||
|
""
|
||||||
|
else
|
||||||
|
(toKeyValue { inherit mkKeyValue listsAsDuplicateKeys; } globalSection) + "\n"
|
||||||
|
)
|
||||||
+ (toINI { inherit mkSectionName mkKeyValue listsAsDuplicateKeys; } sections);
|
+ (toINI { inherit mkSectionName mkKeyValue listsAsDuplicateKeys; } sections);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -349,46 +377,53 @@ in rec {
|
||||||
|
|
||||||
: Key-value pairs to be converted to a git-config file.
|
: Key-value pairs to be converted to a git-config file.
|
||||||
See: https://git-scm.com/docs/git-config#_variables for possible values.
|
See: https://git-scm.com/docs/git-config#_variables for possible values.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
toGitINI = attrs:
|
toGitINI =
|
||||||
|
attrs:
|
||||||
let
|
let
|
||||||
mkSectionName = name:
|
mkSectionName =
|
||||||
|
name:
|
||||||
let
|
let
|
||||||
containsQuote = hasInfix ''"'' name;
|
containsQuote = hasInfix ''"'' name;
|
||||||
sections = splitString "." name;
|
sections = splitString "." name;
|
||||||
section = head sections;
|
section = head sections;
|
||||||
subsections = tail sections;
|
subsections = tail sections;
|
||||||
subsection = concatStringsSep "." subsections;
|
subsection = concatStringsSep "." subsections;
|
||||||
in if containsQuote || subsections == [ ] then
|
in
|
||||||
name
|
if containsQuote || subsections == [ ] then name else ''${section} "${subsection}"'';
|
||||||
else
|
|
||||||
''${section} "${subsection}"'';
|
|
||||||
|
|
||||||
mkValueString = v:
|
mkValueString =
|
||||||
|
v:
|
||||||
let
|
let
|
||||||
escapedV = ''
|
escapedV = ''"${replaceStrings [ "\n" " " ''"'' "\\" ] [ "\\n" "\\t" ''\"'' "\\\\" ] v}"'';
|
||||||
"${
|
in
|
||||||
replaceStrings [ "\n" " " ''"'' "\\" ] [ "\\n" "\\t" ''\"'' "\\\\" ] v
|
mkValueStringDefault { } (if isString v then escapedV else v);
|
||||||
}"'';
|
|
||||||
in mkValueStringDefault { } (if isString v then escapedV else v);
|
|
||||||
|
|
||||||
# generation for multiple ini values
|
# generation for multiple ini values
|
||||||
mkKeyValue = k: v:
|
mkKeyValue =
|
||||||
let mkKeyValue = mkKeyValueDefault { inherit mkValueString; } " = " k;
|
k: v:
|
||||||
in concatStringsSep "\n" (map (kv: "\t" + mkKeyValue kv) (toList 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
|
# converts { a.b.c = 5; } to { "a.b".c = 5; } for toINI
|
||||||
gitFlattenAttrs = let
|
gitFlattenAttrs =
|
||||||
recurse = path: value:
|
let
|
||||||
|
recurse =
|
||||||
|
path: value:
|
||||||
if isAttrs value && !isDerivation value then
|
if isAttrs value && !isDerivation value then
|
||||||
mapAttrsToList (name: value: recurse ([ name ] ++ path) value) value
|
mapAttrsToList (name: value: recurse ([ name ] ++ path) value) value
|
||||||
else if length path > 1 then {
|
else if length path > 1 then
|
||||||
|
{
|
||||||
${concatStringsSep "." (reverseList (tail path))}.${head path} = value;
|
${concatStringsSep "." (reverseList (tail path))}.${head path} = value;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
${head path} = value;
|
${head path} = value;
|
||||||
};
|
};
|
||||||
in attrs: foldl recursiveUpdate { } (flatten (recurse [ ] attrs));
|
in
|
||||||
|
attrs: foldl recursiveUpdate { } (flatten (recurse [ ] attrs));
|
||||||
|
|
||||||
toINI_ = toINI { inherit mkKeyValue mkSectionName; };
|
toINI_ = toINI { inherit mkKeyValue mkSectionName; };
|
||||||
in
|
in
|
||||||
|
@ -427,7 +462,7 @@ in rec {
|
||||||
withRecursion =
|
withRecursion =
|
||||||
{
|
{
|
||||||
depthLimit,
|
depthLimit,
|
||||||
throwOnDepthLimit ? true
|
throwOnDepthLimit ? true,
|
||||||
}:
|
}:
|
||||||
assert isInt depthLimit;
|
assert isInt depthLimit;
|
||||||
let
|
let
|
||||||
|
@ -437,23 +472,27 @@ in rec {
|
||||||
"__toString"
|
"__toString"
|
||||||
"__pretty"
|
"__pretty"
|
||||||
];
|
];
|
||||||
stepIntoAttr = evalNext: name:
|
stepIntoAttr = evalNext: name: if elem name specialAttrs then id else evalNext;
|
||||||
if elem name specialAttrs
|
transform =
|
||||||
then id
|
depth:
|
||||||
else evalNext;
|
|
||||||
transform = depth:
|
|
||||||
if depthLimit != null && depth > depthLimit then
|
if depthLimit != null && depth > depthLimit then
|
||||||
if throwOnDepthLimit
|
if throwOnDepthLimit then
|
||||||
then throw "Exceeded maximum eval-depth limit of ${toString depthLimit} while trying to evaluate with `generators.withRecursion'!"
|
throw "Exceeded maximum eval-depth limit of ${toString depthLimit} while trying to evaluate with `generators.withRecursion'!"
|
||||||
else const "<unevaluated>"
|
else
|
||||||
else id;
|
const "<unevaluated>"
|
||||||
mapAny = depth: v:
|
else
|
||||||
|
id;
|
||||||
|
mapAny =
|
||||||
|
depth: v:
|
||||||
let
|
let
|
||||||
evalNext = x: mapAny (depth + 1) (transform (depth + 1) x);
|
evalNext = x: mapAny (depth + 1) (transform (depth + 1) x);
|
||||||
in
|
in
|
||||||
if isAttrs v then mapAttrs (stepIntoAttr evalNext) v
|
if isAttrs v then
|
||||||
else if isList v then map evalNext v
|
mapAttrs (stepIntoAttr evalNext) v
|
||||||
else transform (depth + 1) v;
|
else if isList v then
|
||||||
|
map evalNext v
|
||||||
|
else
|
||||||
|
transform (depth + 1) v;
|
||||||
in
|
in
|
||||||
mapAny 0;
|
mapAny 0;
|
||||||
|
|
||||||
|
@ -481,68 +520,96 @@ in rec {
|
||||||
Value
|
Value
|
||||||
: The value to be pretty printed
|
: The value to be pretty printed
|
||||||
*/
|
*/
|
||||||
toPretty = {
|
toPretty =
|
||||||
|
{
|
||||||
allowPrettyValues ? false,
|
allowPrettyValues ? false,
|
||||||
multiline ? true,
|
multiline ? true,
|
||||||
indent ? ""
|
indent ? "",
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
go = indent: v:
|
go =
|
||||||
let introSpace = if multiline then "\n${indent} " else " ";
|
indent: v:
|
||||||
|
let
|
||||||
|
introSpace = if multiline then "\n${indent} " else " ";
|
||||||
outroSpace = if multiline then "\n${indent}" else " ";
|
outroSpace = if multiline then "\n${indent}" else " ";
|
||||||
in if isInt v then toString v
|
in
|
||||||
|
if isInt v then
|
||||||
|
toString v
|
||||||
# toString loses precision on floats, so we use toJSON instead. This isn't perfect
|
# 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
|
# as the resulting string may not parse back as a float (e.g. 42, 1e-06), but for
|
||||||
# pretty-printing purposes this is acceptable.
|
# pretty-printing purposes this is acceptable.
|
||||||
else if isFloat v then builtins.toJSON v
|
else if isFloat v then
|
||||||
|
builtins.toJSON v
|
||||||
else if isString v then
|
else if isString v then
|
||||||
let
|
let
|
||||||
lines = filter (v: ! isList v) (split "\n" v);
|
lines = filter (v: !isList v) (split "\n" v);
|
||||||
escapeSingleline = escape [ "\\" "\"" "\${" ];
|
escapeSingleline = escape [
|
||||||
|
"\\"
|
||||||
|
"\""
|
||||||
|
"\${"
|
||||||
|
];
|
||||||
escapeMultiline = replaceStrings [ "\${" "''" ] [ "''\${" "'''" ];
|
escapeMultiline = replaceStrings [ "\${" "''" ] [ "''\${" "'''" ];
|
||||||
singlelineResult = "\"" + concatStringsSep "\\n" (map escapeSingleline lines) + "\"";
|
singlelineResult = "\"" + concatStringsSep "\\n" (map escapeSingleline lines) + "\"";
|
||||||
multilineResult = let
|
multilineResult =
|
||||||
|
let
|
||||||
escapedLines = map escapeMultiline lines;
|
escapedLines = map escapeMultiline lines;
|
||||||
# The last line gets a special treatment: if it's empty, '' is on its own line at the "outer"
|
# 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.
|
# indentation level. Otherwise, '' is appended to the last line.
|
||||||
lastLine = last escapedLines;
|
lastLine = last escapedLines;
|
||||||
in "''" + introSpace + concatStringsSep introSpace (init escapedLines)
|
in
|
||||||
+ (if lastLine == "" then outroSpace else introSpace + lastLine) + "''";
|
"''"
|
||||||
|
+ introSpace
|
||||||
|
+ concatStringsSep introSpace (init escapedLines)
|
||||||
|
+ (if lastLine == "" then outroSpace else introSpace + lastLine)
|
||||||
|
+ "''";
|
||||||
in
|
in
|
||||||
if multiline && length lines > 1 then multilineResult else singlelineResult
|
if multiline && length lines > 1 then multilineResult else singlelineResult
|
||||||
else if true == v then "true"
|
else if true == v then
|
||||||
else if false == v then "false"
|
"true"
|
||||||
else if null == v then "null"
|
else if false == v then
|
||||||
else if isPath v then toString v
|
"false"
|
||||||
|
else if null == v then
|
||||||
|
"null"
|
||||||
|
else if isPath v then
|
||||||
|
toString v
|
||||||
else if isList v then
|
else if isList v then
|
||||||
if v == [] then "[ ]"
|
if v == [ ] then
|
||||||
else "[" + introSpace
|
"[ ]"
|
||||||
+ concatMapStringsSep introSpace (go (indent + " ")) v
|
else
|
||||||
+ outroSpace + "]"
|
"[" + introSpace + concatMapStringsSep introSpace (go (indent + " ")) v + outroSpace + "]"
|
||||||
else if isFunction v then
|
else if isFunction v then
|
||||||
let fna = functionArgs v;
|
let
|
||||||
showFnas = concatStringsSep ", " (mapAttrsToList
|
fna = functionArgs v;
|
||||||
(name: hasDefVal: if hasDefVal then name + "?" else name)
|
showFnas = concatStringsSep ", " (
|
||||||
fna);
|
mapAttrsToList (name: hasDefVal: if hasDefVal then name + "?" else name) fna
|
||||||
in if fna == {} then "<function>"
|
);
|
||||||
else "<function, args: {${showFnas}}>"
|
in
|
||||||
|
if fna == { } then "<function>" else "<function, args: {${showFnas}}>"
|
||||||
else if isAttrs v then
|
else if isAttrs v then
|
||||||
# apply pretty values if allowed
|
# apply pretty values if allowed
|
||||||
if allowPrettyValues && v ? __pretty && v ? val
|
if allowPrettyValues && v ? __pretty && v ? val then
|
||||||
then v.__pretty v.val
|
v.__pretty v.val
|
||||||
else if v == {} then "{ }"
|
else if v == { } then
|
||||||
|
"{ }"
|
||||||
else if v ? type && v.type == "derivation" then
|
else if v ? type && v.type == "derivation" then
|
||||||
"<derivation ${v.name or "???"}>"
|
"<derivation ${v.name or "???"}>"
|
||||||
else "{" + introSpace
|
else
|
||||||
+ concatStringsSep introSpace (mapAttrsToList
|
"{"
|
||||||
(name: value:
|
+ introSpace
|
||||||
|
+ concatStringsSep introSpace (
|
||||||
|
mapAttrsToList (
|
||||||
|
name: value:
|
||||||
"${escapeNixIdentifier name} = ${
|
"${escapeNixIdentifier name} = ${
|
||||||
addErrorContext "while evaluating an attribute `${name}`"
|
addErrorContext "while evaluating an attribute `${name}`" (go (indent + " ") value)
|
||||||
(go (indent + " ") value)
|
};"
|
||||||
};") v)
|
) v
|
||||||
+ outroSpace + "}"
|
)
|
||||||
else abort "generators.toPretty: should never happen (v = ${v})";
|
+ outroSpace
|
||||||
in go indent;
|
+ "}"
|
||||||
|
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).
|
Translate a simple Nix expression to [Plist notation](https://en.wikipedia.org/wiki/Property_list).
|
||||||
|
@ -557,18 +624,31 @@ in rec {
|
||||||
Value
|
Value
|
||||||
: The value to be converted to Plist
|
: The value to be converted to Plist
|
||||||
*/
|
*/
|
||||||
toPlist = {
|
toPlist =
|
||||||
escape ? false
|
{
|
||||||
}: v: let
|
escape ? false,
|
||||||
expr = ind: x:
|
}:
|
||||||
if x == null then "" else
|
v:
|
||||||
if isBool x then bool ind x else
|
let
|
||||||
if isInt x then int ind x else
|
expr =
|
||||||
if isString x then str ind x else
|
ind: x:
|
||||||
if isList x then list ind x else
|
if x == null then
|
||||||
if isAttrs x then attrs ind x else
|
""
|
||||||
if isPath x then str ind (toString x) else
|
else if isBool x then
|
||||||
if isFloat x then float ind x else
|
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})";
|
abort "generators.toPlist: should never happen (v = ${v})";
|
||||||
|
|
||||||
literal = ind: x: ind + x;
|
literal = ind: x: ind + x;
|
||||||
|
@ -585,28 +665,44 @@ in rec {
|
||||||
|
|
||||||
item = ind: concatMapStringsSep "\n" (indent ind);
|
item = ind: concatMapStringsSep "\n" (indent ind);
|
||||||
|
|
||||||
list = ind: x: concatStringsSep "\n" [
|
list =
|
||||||
|
ind: x:
|
||||||
|
concatStringsSep "\n" [
|
||||||
(literal ind "<array>")
|
(literal ind "<array>")
|
||||||
(item ind x)
|
(item ind x)
|
||||||
(literal ind "</array>")
|
(literal ind "</array>")
|
||||||
];
|
];
|
||||||
|
|
||||||
attrs = ind: x: concatStringsSep "\n" [
|
attrs =
|
||||||
|
ind: x:
|
||||||
|
concatStringsSep "\n" [
|
||||||
(literal ind "<dict>")
|
(literal ind "<dict>")
|
||||||
(attr ind x)
|
(attr ind x)
|
||||||
(literal ind "</dict>")
|
(literal ind "</dict>")
|
||||||
];
|
];
|
||||||
|
|
||||||
attr = let attrFilter = name: value: name != "_module" && value != null;
|
attr =
|
||||||
in ind: x: concatStringsSep "\n" (flatten (mapAttrsToList
|
let
|
||||||
(name: value: optionals (attrFilter name value) [
|
attrFilter = name: value: name != "_module" && value != null;
|
||||||
|
in
|
||||||
|
ind: x:
|
||||||
|
concatStringsSep "\n" (
|
||||||
|
flatten (
|
||||||
|
mapAttrsToList (
|
||||||
|
name: value:
|
||||||
|
optionals (attrFilter name value) [
|
||||||
(key "\t${ind}" name)
|
(key "\t${ind}" name)
|
||||||
(expr "\t${ind}" value)
|
(expr "\t${ind}" value)
|
||||||
]) x));
|
]
|
||||||
|
) x
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
in
|
in
|
||||||
# TODO: As discussed in #356502, deprecated functionality should be removed sometime after 25.11.
|
# TODO: As discussed in #356502, deprecated functionality should be removed sometime after 25.11.
|
||||||
lib.warnIf (!escape && lib.oldestSupportedReleaseIsAtLeast 2505) "Using `lib.generators.toPlist` without `escape = true` is deprecated" ''
|
lib.warnIf (!escape && lib.oldestSupportedReleaseIsAtLeast 2505)
|
||||||
|
"Using `lib.generators.toPlist` without `escape = true` is deprecated"
|
||||||
|
''
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?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">
|
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
|
@ -629,13 +725,14 @@ in rec {
|
||||||
|
|
||||||
: The value to be converted to Dhall
|
: The value to be converted to Dhall
|
||||||
*/
|
*/
|
||||||
toDhall = { }@args: v:
|
toDhall =
|
||||||
let concatItems = concatStringsSep ", ";
|
{ }@args:
|
||||||
in if isAttrs v then
|
v:
|
||||||
"{ ${
|
let
|
||||||
concatItems (mapAttrsToList
|
concatItems = concatStringsSep ", ";
|
||||||
(key: value: "${key} = ${toDhall args value}") v)
|
in
|
||||||
} }"
|
if isAttrs v then
|
||||||
|
"{ ${concatItems (mapAttrsToList (key: value: "${key} = ${toDhall args value}") v)} }"
|
||||||
else if isList v then
|
else if isList v then
|
||||||
"[ ${concatItems (map (toDhall args) v)} ]"
|
"[ ${concatItems (map (toDhall args) v)} ]"
|
||||||
else if isInt v then
|
else if isInt v then
|
||||||
|
@ -663,7 +760,6 @@ in rec {
|
||||||
|
|
||||||
Regardless of multiline parameter there is no trailing newline.
|
Regardless of multiline parameter there is no trailing newline.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
Structured function argument
|
Structured function argument
|
||||||
|
@ -711,11 +807,13 @@ in rec {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
toLua = {
|
toLua =
|
||||||
|
{
|
||||||
multiline ? true,
|
multiline ? true,
|
||||||
indent ? "",
|
indent ? "",
|
||||||
asBindings ? false,
|
asBindings ? false,
|
||||||
}@args: v:
|
}@args:
|
||||||
|
v:
|
||||||
let
|
let
|
||||||
innerIndent = "${indent} ";
|
innerIndent = "${indent} ";
|
||||||
introSpace = if multiline then "\n${innerIndent}" else " ";
|
introSpace = if multiline then "\n${innerIndent}" else " ";
|
||||||
|
@ -725,13 +823,16 @@ in rec {
|
||||||
asBindings = false;
|
asBindings = false;
|
||||||
};
|
};
|
||||||
concatItems = concatStringsSep ",${introSpace}";
|
concatItems = concatStringsSep ",${introSpace}";
|
||||||
isLuaInline = { _type ? null, ... }: _type == "lua-inline";
|
isLuaInline =
|
||||||
|
{
|
||||||
|
_type ? null,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
_type == "lua-inline";
|
||||||
|
|
||||||
generatedBindings =
|
generatedBindings =
|
||||||
assert assertMsg (badVarNames == []) "Bad Lua var names: ${toPretty {} badVarNames}";
|
assert assertMsg (badVarNames == [ ]) "Bad Lua var names: ${toPretty { } badVarNames}";
|
||||||
concatStrings (
|
concatStrings (mapAttrsToList (key: value: "${indent}${key} = ${toLua innerArgs value}\n") v);
|
||||||
mapAttrsToList (key: value: "${indent}${key} = ${toLua innerArgs value}\n") v
|
|
||||||
);
|
|
||||||
|
|
||||||
# https://en.wikibooks.org/wiki/Lua_Programming/variable#Variable_names
|
# https://en.wikibooks.org/wiki/Lua_Programming/variable#Variable_names
|
||||||
matchVarName = match "[[:alpha:]_][[:alnum:]_]*(\\.[[:alpha:]_][[:alnum:]_]*)*";
|
matchVarName = match "[[:alpha:]_][[:alnum:]_]*(\\.[[:alpha:]_][[:alnum:]_]*)*";
|
||||||
|
@ -746,8 +847,12 @@ in rec {
|
||||||
else if isPath v || isDerivation v then
|
else if isPath v || isDerivation v then
|
||||||
toJSON "${v}"
|
toJSON "${v}"
|
||||||
else if isList v then
|
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
|
else if isAttrs v then
|
||||||
(
|
(
|
||||||
if isLuaInline v then
|
if isLuaInline v then
|
||||||
|
@ -755,9 +860,9 @@ in rec {
|
||||||
else if v == { } then
|
else if v == { } then
|
||||||
"{}"
|
"{}"
|
||||||
else
|
else
|
||||||
"{${introSpace}${concatItems (
|
"{${introSpace}${
|
||||||
mapAttrsToList (key: value: "[${toJSON key}] = ${toLua innerArgs value}") v
|
concatItems (mapAttrsToList (key: value: "[${toJSON key}] = ${toLua innerArgs value}") v)
|
||||||
)}${outroSpace}}"
|
}${outroSpace}}"
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
abort "generators.toLua: type ${typeOf v} is unsupported";
|
abort "generators.toLua: type ${typeOf v} is unsupported";
|
||||||
|
@ -765,7 +870,6 @@ in rec {
|
||||||
/**
|
/**
|
||||||
Mark string as Lua expression to be inlined when processed by toLua.
|
Mark string as Lua expression to be inlined when processed by toLua.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`expr`
|
`expr`
|
||||||
|
@ -778,8 +882,12 @@ in rec {
|
||||||
mkLuaInline :: String -> AttrSet
|
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.
|
Generates JSON from an arbitrary (non-function) value.
|
||||||
For more information see the documentation of the builtin.
|
For more information see the documentation of the builtin.
|
||||||
|
@ -794,7 +902,7 @@ in rec {
|
||||||
|
|
||||||
: The value to be converted to JSON
|
: 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
|
YAML has been a strict superset of JSON since 1.2, so we
|
||||||
|
@ -812,5 +920,5 @@ in rec {
|
||||||
|
|
||||||
: The value to be converted to YAML
|
: The value to be converted to YAML
|
||||||
*/
|
*/
|
||||||
toYAML = {}: lib.strings.toJSON;
|
toYAML = { }: lib.strings.toJSON;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,9 @@
|
||||||
let
|
let
|
||||||
inherit (lib) optionalAttrs;
|
inherit (lib) optionalAttrs;
|
||||||
|
|
||||||
mkLicense = lname: {
|
mkLicense =
|
||||||
|
lname:
|
||||||
|
{
|
||||||
shortName ? lname,
|
shortName ? lname,
|
||||||
# Most of our licenses are Free, explicitly declare unfree additions as such!
|
# Most of our licenses are Free, explicitly declare unfree additions as such!
|
||||||
free ? true,
|
free ? true,
|
||||||
|
@ -10,20 +12,30 @@ let
|
||||||
spdxId ? null,
|
spdxId ? null,
|
||||||
url ? null,
|
url ? null,
|
||||||
fullName ? null,
|
fullName ? null,
|
||||||
redistributable ? free
|
redistributable ? free,
|
||||||
}@attrs: {
|
}@attrs:
|
||||||
inherit shortName free deprecated redistributable;
|
{
|
||||||
} // optionalAttrs (attrs ? spdxId) {
|
inherit
|
||||||
|
shortName
|
||||||
|
free
|
||||||
|
deprecated
|
||||||
|
redistributable
|
||||||
|
;
|
||||||
|
}
|
||||||
|
// optionalAttrs (attrs ? spdxId) {
|
||||||
inherit spdxId;
|
inherit spdxId;
|
||||||
url = "https://spdx.org/licenses/${spdxId}.html";
|
url = "https://spdx.org/licenses/${spdxId}.html";
|
||||||
} // optionalAttrs (attrs ? url) {
|
}
|
||||||
|
// optionalAttrs (attrs ? url) {
|
||||||
inherit url;
|
inherit url;
|
||||||
} // optionalAttrs (attrs ? fullName) {
|
}
|
||||||
|
// optionalAttrs (attrs ? fullName) {
|
||||||
inherit fullName;
|
inherit fullName;
|
||||||
};
|
};
|
||||||
|
|
||||||
in
|
in
|
||||||
lib.mapAttrs mkLicense ({
|
lib.mapAttrs mkLicense (
|
||||||
|
{
|
||||||
/**
|
/**
|
||||||
License identifiers from spdx.org where possible.
|
License identifiers from spdx.org where possible.
|
||||||
If you cannot find your license here, then look for a similar license or
|
If you cannot find your license here, then look for a similar license or
|
||||||
|
@ -1409,7 +1421,8 @@ lib.mapAttrs mkLicense ({
|
||||||
fullName = "Zope Public License 2.1";
|
fullName = "Zope Public License 2.1";
|
||||||
};
|
};
|
||||||
|
|
||||||
} // {
|
}
|
||||||
|
// {
|
||||||
# TODO: remove legacy aliases
|
# TODO: remove legacy aliases
|
||||||
apsl10 = {
|
apsl10 = {
|
||||||
# deprecated for consistency with `apple-psl20`; use `apple-psl10`
|
# deprecated for consistency with `apple-psl20`; use `apple-psl10`
|
||||||
|
@ -1448,4 +1461,5 @@ lib.mapAttrs mkLicense ({
|
||||||
fullName = "GNU Lesser General Public License v3.0";
|
fullName = "GNU Lesser General Public License v3.0";
|
||||||
deprecated = true;
|
deprecated = true;
|
||||||
};
|
};
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
354
lib/lists.nix
354
lib/lists.nix
|
@ -4,13 +4,30 @@
|
||||||
{ lib }:
|
{ lib }:
|
||||||
let
|
let
|
||||||
inherit (lib.strings) toInt;
|
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.attrsets) mapAttrs;
|
||||||
inherit (lib) max;
|
inherit (lib) max;
|
||||||
in
|
in
|
||||||
rec {
|
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
|
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.
|
Apply the function to each element in the list.
|
||||||
|
@ -82,7 +99,6 @@ rec {
|
||||||
`list` with `nul` as the starting value, i.e.,
|
`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))`.
|
`foldr op nul [x_1 x_2 ... x_n] == op x_1 (op x_2 ... (op x_n nul))`.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`op`
|
`op`
|
||||||
|
@ -119,14 +135,13 @@ rec {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
foldr = op: nul: list:
|
foldr =
|
||||||
|
op: nul: list:
|
||||||
let
|
let
|
||||||
len = length list;
|
len = length list;
|
||||||
fold' = n:
|
fold' = n: if n == len then nul else op (elemAt list n) (fold' (n + 1));
|
||||||
if n == len
|
in
|
||||||
then nul
|
fold' 0;
|
||||||
else op (elemAt list n) (fold' (n + 1));
|
|
||||||
in fold' 0;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
`fold` is an alias of `foldr` for historic reasons
|
`fold` is an alias of `foldr` for historic reasons
|
||||||
|
@ -134,7 +149,6 @@ rec {
|
||||||
# FIXME(Profpatsch): deprecate?
|
# FIXME(Profpatsch): deprecate?
|
||||||
fold = foldr;
|
fold = foldr;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
“left fold”, like `foldr`, but from the left:
|
“left fold”, like `foldr`, but from the left:
|
||||||
|
|
||||||
|
@ -176,13 +190,12 @@ rec {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
foldl = op: nul: list:
|
foldl =
|
||||||
|
op: nul: list:
|
||||||
let
|
let
|
||||||
foldl' = n:
|
foldl' = n: if n == -1 then nul else op (foldl' (n - 1)) (elemAt list n);
|
||||||
if n == -1
|
in
|
||||||
then nul
|
foldl' (length list - 1);
|
||||||
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,
|
Reduce a list by applying a binary operator from left to right,
|
||||||
|
@ -261,13 +274,11 @@ rec {
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
foldl' =
|
foldl' =
|
||||||
op:
|
op: acc:
|
||||||
acc:
|
|
||||||
# The builtin `foldl'` is a bit lazier than one might expect.
|
# The builtin `foldl'` is a bit lazier than one might expect.
|
||||||
# See https://github.com/NixOS/nix/pull/7158.
|
# See https://github.com/NixOS/nix/pull/7158.
|
||||||
# In particular, the initial accumulator value is not forced before the first iteration starts.
|
# In particular, the initial accumulator value is not forced before the first iteration starts.
|
||||||
builtins.seq acc
|
builtins.seq acc (builtins.foldl' op acc);
|
||||||
(builtins.foldl' op acc);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Map with index starting from 0
|
Map with index starting from 0
|
||||||
|
@ -304,7 +315,6 @@ rec {
|
||||||
/**
|
/**
|
||||||
Map with index starting from 1
|
Map with index starting from 1
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`f`
|
`f`
|
||||||
|
@ -374,12 +384,9 @@ rec {
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
ifilter0 =
|
ifilter0 =
|
||||||
ipred:
|
ipred: input:
|
||||||
input:
|
|
||||||
map (idx: elemAt input idx) (
|
map (idx: elemAt input idx) (
|
||||||
filter (idx: ipred idx (elemAt input idx)) (
|
filter (idx: ipred idx (elemAt input idx)) (genList (x: x) (length input))
|
||||||
genList (x: x) (length input)
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -408,14 +415,12 @@ rec {
|
||||||
Flatten the argument into a single list; that is, nested lists are
|
Flatten the argument into a single list; that is, nested lists are
|
||||||
spliced into the top-level lists.
|
spliced into the top-level lists.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`x`
|
`x`
|
||||||
|
|
||||||
: 1\. Function argument
|
: 1\. Function argument
|
||||||
|
|
||||||
|
|
||||||
# Examples
|
# Examples
|
||||||
:::{.example}
|
:::{.example}
|
||||||
## `lib.lists.flatten` usage example
|
## `lib.lists.flatten` usage example
|
||||||
|
@ -429,15 +434,11 @@ rec {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
flatten = x:
|
flatten = x: if isList x then concatMap (y: flatten y) x else [ x ];
|
||||||
if isList x
|
|
||||||
then concatMap (y: flatten y) x
|
|
||||||
else [x];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Remove elements equal to 'e' from a list. Useful for buildInputs.
|
Remove elements equal to 'e' from a list. Useful for buildInputs.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`e`
|
`e`
|
||||||
|
@ -465,8 +466,7 @@ rec {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
remove =
|
remove = e: filter (x: x != e);
|
||||||
e: filter (x: x != e);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Find the sole element in the list matching the specified
|
Find the sole element in the list matching the specified
|
||||||
|
@ -475,7 +475,6 @@ rec {
|
||||||
Returns `default` if no such element exists, or
|
Returns `default` if no such element exists, or
|
||||||
`multiple` if there are multiple matching elements.
|
`multiple` if there are multiple matching elements.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`pred`
|
`pred`
|
||||||
|
@ -516,14 +515,17 @@ rec {
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
findSingle =
|
findSingle =
|
||||||
pred:
|
pred: default: multiple: list:
|
||||||
default:
|
let
|
||||||
multiple:
|
found = filter pred list;
|
||||||
list:
|
len = length found;
|
||||||
let found = filter pred list; len = length found;
|
in
|
||||||
in if len == 0 then default
|
if len == 0 then
|
||||||
else if len != 1 then multiple
|
default
|
||||||
else head found;
|
else if len != 1 then
|
||||||
|
multiple
|
||||||
|
else
|
||||||
|
head found;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Find the first index in the list matching the specified
|
Find the first index in the list matching the specified
|
||||||
|
@ -563,9 +565,7 @@ rec {
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
findFirstIndex =
|
findFirstIndex =
|
||||||
pred:
|
pred: default: list:
|
||||||
default:
|
|
||||||
list:
|
|
||||||
let
|
let
|
||||||
# A naive recursive implementation would be much simpler, but
|
# A naive recursive implementation would be much simpler, but
|
||||||
# would also overflow the evaluator stack. We use `foldl'` as a workaround
|
# 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
|
# - 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
|
# 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
|
if index < 0 then
|
||||||
# No match yet before the current index, we need to check the element
|
# No match yet before the current index, we need to check the element
|
||||||
if pred el then
|
if pred el then
|
||||||
# We have a match! Turn it into the actual index to prevent future iterations from modifying it
|
# We have a match! Turn it into the actual index to prevent future iterations from modifying it
|
||||||
- index - 1
|
-index - 1
|
||||||
else
|
else
|
||||||
# Still no match, update the index to the next element (we're counting down, so minus one)
|
# Still no match, update the index to the next element (we're counting down, so minus one)
|
||||||
index - 1
|
index - 1
|
||||||
|
@ -594,10 +595,7 @@ rec {
|
||||||
index
|
index
|
||||||
) (-1) list;
|
) (-1) list;
|
||||||
in
|
in
|
||||||
if resultIndex < 0 then
|
if resultIndex < 0 then default else resultIndex;
|
||||||
default
|
|
||||||
else
|
|
||||||
resultIndex;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Find the first element in the list matching the specified
|
Find the first element in the list matching the specified
|
||||||
|
@ -637,16 +635,11 @@ rec {
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
findFirst =
|
findFirst =
|
||||||
pred:
|
pred: default: list:
|
||||||
default:
|
|
||||||
list:
|
|
||||||
let
|
let
|
||||||
index = findFirstIndex pred null list;
|
index = findFirstIndex pred null list;
|
||||||
in
|
in
|
||||||
if index == null then
|
if index == null then default else elemAt list index;
|
||||||
default
|
|
||||||
else
|
|
||||||
elemAt list index;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Return true if function `pred` returns true for at least one
|
Return true if function `pred` returns true for at least one
|
||||||
|
@ -745,8 +738,7 @@ rec {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
count =
|
count = pred: foldl' (c: x: if pred x then c + 1 else c) 0;
|
||||||
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
|
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.
|
Return a list or an empty list, depending on a boolean value.
|
||||||
|
@ -816,10 +808,7 @@ rec {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
optionals =
|
optionals = cond: elems: if cond then elems else [ ];
|
||||||
cond:
|
|
||||||
elems: if cond then elems else [];
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
If argument is a list, return it; else, wrap it in a singleton
|
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`.
|
Return a list of integers from `first` up to and including `last`.
|
||||||
|
@ -879,13 +868,7 @@ rec {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
range =
|
range = first: last: if first > last then [ ] else genList (n: first + n) (last - first + 1);
|
||||||
first:
|
|
||||||
last:
|
|
||||||
if first > last then
|
|
||||||
[]
|
|
||||||
else
|
|
||||||
genList (n: first + n) (last - first + 1);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Return a list with `n` copies of an element.
|
Return a list with `n` copies of an element.
|
||||||
|
@ -977,7 +960,6 @@ rec {
|
||||||
|
|
||||||
: 4\. Function argument
|
: 4\. Function argument
|
||||||
|
|
||||||
|
|
||||||
# Examples
|
# Examples
|
||||||
:::{.example}
|
:::{.example}
|
||||||
## `lib.lists.groupBy'` usage 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 (
|
groupBy =
|
||||||
pred: foldl' (r: e:
|
builtins.groupBy or (
|
||||||
|
pred:
|
||||||
|
foldl' (
|
||||||
|
r: e:
|
||||||
let
|
let
|
||||||
key = pred e;
|
key = pred e;
|
||||||
in
|
in
|
||||||
r // { ${key} = (r.${key} or []) ++ [e]; }
|
r // { ${key} = (r.${key} or [ ]) ++ [ e ]; }
|
||||||
) {});
|
) { }
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Merges two lists of the same size together. If the sizes aren't the same
|
Merges two lists of the same size together. If the sizes aren't the same
|
||||||
|
@ -1049,11 +1037,8 @@ rec {
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
zipListsWith =
|
zipListsWith =
|
||||||
f:
|
f: fst: snd:
|
||||||
fst:
|
genList (n: f (elemAt fst n) (elemAt snd n)) (min (length fst) (length snd));
|
||||||
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
|
Merges two lists of the same size together. If the sizes aren't the same
|
||||||
|
@ -1114,8 +1099,12 @@ rec {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
reverseList = xs:
|
reverseList =
|
||||||
let l = length xs; in genList (n: elemAt xs (l - n - 1)) l;
|
xs:
|
||||||
|
let
|
||||||
|
l = length xs;
|
||||||
|
in
|
||||||
|
genList (n: elemAt xs (l - n - 1)) l;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Depth-First Search (DFS) for lists `list != []`.
|
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
|
`before a b == true` means that `b` depends on `a` (there's an
|
||||||
edge from `b` to `a`).
|
edge from `b` to `a`).
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`stopOnCycles`
|
`stopOnCycles`
|
||||||
|
@ -1138,7 +1126,6 @@ rec {
|
||||||
|
|
||||||
: 3\. Function argument
|
: 3\. Function argument
|
||||||
|
|
||||||
|
|
||||||
# Examples
|
# Examples
|
||||||
:::{.example}
|
:::{.example}
|
||||||
## `lib.lists.listDfs` usage example
|
## `lib.lists.listDfs` usage example
|
||||||
|
@ -1159,22 +1146,32 @@ rec {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
listDfs = stopOnCycles: before: list:
|
listDfs =
|
||||||
|
stopOnCycles: before: list:
|
||||||
let
|
let
|
||||||
dfs' = us: visited: rest:
|
dfs' =
|
||||||
|
us: visited: rest:
|
||||||
let
|
let
|
||||||
c = filter (x: before x us) visited;
|
c = filter (x: before x us) visited;
|
||||||
b = partition (x: before x us) rest;
|
b = partition (x: before x us) rest;
|
||||||
in if stopOnCycles && (length c > 0)
|
in
|
||||||
then { cycle = us; loops = c; inherit visited rest; }
|
if stopOnCycles && (length c > 0) then
|
||||||
else if length b.right == 0
|
{
|
||||||
then # nothing is before us
|
cycle = us;
|
||||||
{ minimal = us; inherit visited rest; }
|
loops = c;
|
||||||
else # grab the first one before us and continue
|
inherit visited rest;
|
||||||
dfs' (head b.right)
|
}
|
||||||
([ us ] ++ visited)
|
else if length b.right == 0 then
|
||||||
(tail b.right ++ b.wrong);
|
# nothing is before us
|
||||||
in dfs' (head list) [] (tail list);
|
{
|
||||||
|
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
|
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`
|
`before a b == true` means that `b` should be after `a`
|
||||||
in the result.
|
in the result.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`before`
|
`before`
|
||||||
|
@ -1195,7 +1191,6 @@ rec {
|
||||||
|
|
||||||
: 2\. Function argument
|
: 2\. Function argument
|
||||||
|
|
||||||
|
|
||||||
# Examples
|
# Examples
|
||||||
:::{.example}
|
:::{.example}
|
||||||
## `lib.lists.toposort` usage example
|
## `lib.lists.toposort` usage example
|
||||||
|
@ -1216,23 +1211,27 @@ rec {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
toposort = before: list:
|
toposort =
|
||||||
|
before: list:
|
||||||
let
|
let
|
||||||
dfsthis = listDfs true before list;
|
dfsthis = listDfs true before list;
|
||||||
toporest = toposort before (dfsthis.visited ++ dfsthis.rest);
|
toporest = toposort before (dfsthis.visited ++ dfsthis.rest);
|
||||||
in
|
in
|
||||||
if length list < 2
|
if length list < 2 then
|
||||||
then # finish
|
# finish
|
||||||
{ result = list; }
|
{ result = list; }
|
||||||
else if dfsthis ? cycle
|
else if dfsthis ? cycle then
|
||||||
then # there's a cycle, starting from the current vertex, return it
|
# there's a cycle, starting from the current vertex, return it
|
||||||
{ cycle = reverseList ([ dfsthis.cycle ] ++ dfsthis.visited);
|
{
|
||||||
inherit (dfsthis) loops; }
|
cycle = reverseList ([ dfsthis.cycle ] ++ dfsthis.visited);
|
||||||
else if toporest ? cycle
|
inherit (dfsthis) loops;
|
||||||
then # there's a cycle somewhere else in the graph, return it
|
}
|
||||||
|
else if toporest ? cycle then
|
||||||
|
# there's a cycle somewhere else in the graph, return it
|
||||||
toporest
|
toporest
|
||||||
# Slow, but short. Can be made a bit faster with an explicit stack.
|
# Slow, but short. Can be made a bit faster with an explicit stack.
|
||||||
else # there are no cycles
|
else
|
||||||
|
# there are no cycles
|
||||||
{ result = [ dfsthis.minimal ] ++ toporest.result; };
|
{ result = [ dfsthis.minimal ] ++ toporest.result; };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1289,7 +1288,6 @@ rec {
|
||||||
sortOn f == sort (p: q: f p < f q)
|
sortOn f == sort (p: q: f p < f q)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`f`
|
`f`
|
||||||
|
@ -1317,18 +1315,22 @@ rec {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
sortOn = f: list:
|
sortOn =
|
||||||
|
f: list:
|
||||||
let
|
let
|
||||||
# Heterogenous list as pair may be ugly, but requires minimal allocations.
|
# 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
|
in
|
||||||
map
|
map (x: builtins.elemAt x 1) (
|
||||||
(x: builtins.elemAt x 1)
|
sort
|
||||||
(sort
|
|
||||||
# Compare the first element of the pairs
|
# Compare the first element of the pairs
|
||||||
# Do not factor out the `<`, to avoid calls in hot code; duplicate instead.
|
# Do not factor out the `<`, to avoid calls in hot code; duplicate instead.
|
||||||
(a: b: head a < head b)
|
(a: b: head a < head b)
|
||||||
pairs);
|
pairs
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Compare two lists element-by-element with a comparison function `cmp`.
|
Compare two lists element-by-element with a comparison function `cmp`.
|
||||||
|
@ -1360,7 +1362,6 @@ rec {
|
||||||
|
|
||||||
: The second list
|
: The second list
|
||||||
|
|
||||||
|
|
||||||
# Examples
|
# Examples
|
||||||
:::{.example}
|
:::{.example}
|
||||||
## `lib.lists.compareLists` usage examples
|
## `lib.lists.compareLists` usage examples
|
||||||
|
@ -1378,30 +1379,28 @@ rec {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
compareLists = cmp: a: b:
|
compareLists =
|
||||||
if a == []
|
cmp: a: b:
|
||||||
then if b == []
|
if a == [ ] then
|
||||||
then 0
|
if b == [ ] then 0 else -1
|
||||||
else -1
|
else if b == [ ] then
|
||||||
else if b == []
|
1
|
||||||
then 1
|
else
|
||||||
else let rel = cmp (head a) (head b); in
|
let
|
||||||
if rel == 0
|
rel = cmp (head a) (head b);
|
||||||
then compareLists cmp (tail a) (tail b)
|
in
|
||||||
else rel;
|
if rel == 0 then compareLists cmp (tail a) (tail b) else rel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Sort list using "Natural sorting".
|
Sort list using "Natural sorting".
|
||||||
Numeric portions of strings are sorted in numeric order.
|
Numeric portions of strings are sorted in numeric order.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`lst`
|
`lst`
|
||||||
|
|
||||||
: 1\. Function argument
|
: 1\. Function argument
|
||||||
|
|
||||||
|
|
||||||
# Examples
|
# Examples
|
||||||
:::{.example}
|
:::{.example}
|
||||||
## `lib.lists.naturalSort` usage example
|
## `lib.lists.naturalSort` usage example
|
||||||
|
@ -1417,10 +1416,14 @@ rec {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
naturalSort = lst:
|
naturalSort =
|
||||||
|
lst:
|
||||||
let
|
let
|
||||||
vectorise = s: map (x: if isList x then toInt (head x) else x) (builtins.split "(0|[1-9][0-9]*)" s);
|
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;
|
less = a: b: (compareLists compare (head a) (head b)) < 0;
|
||||||
in
|
in
|
||||||
map (x: elemAt x 1) (sort less prepared);
|
map (x: elemAt x 1) (sort less prepared);
|
||||||
|
@ -1428,7 +1431,6 @@ rec {
|
||||||
/**
|
/**
|
||||||
Return the first (at most) N elements of a list.
|
Return the first (at most) N elements of a list.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`count`
|
`count`
|
||||||
|
@ -1458,13 +1460,11 @@ rec {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
take =
|
take = count: sublist 0 count;
|
||||||
count: sublist 0 count;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Remove the first (at most) N elements of a list.
|
Remove the first (at most) N elements of a list.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`count`
|
`count`
|
||||||
|
@ -1494,14 +1494,11 @@ rec {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
drop =
|
drop = count: list: sublist count (length list) list;
|
||||||
count:
|
|
||||||
list: sublist count (length list) list;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Remove the last (at most) N elements of a list.
|
Remove the last (at most) N elements of a list.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`count`
|
`count`
|
||||||
|
@ -1530,18 +1527,12 @@ rec {
|
||||||
=> [ ]
|
=> [ ]
|
||||||
```
|
```
|
||||||
:::
|
:::
|
||||||
|
|
||||||
*/
|
*/
|
||||||
dropEnd =
|
dropEnd = n: xs: take (max 0 (length xs - n)) xs;
|
||||||
n: xs:
|
|
||||||
take
|
|
||||||
(max 0 (length xs - n))
|
|
||||||
xs;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Whether the first list is a prefix of the second list.
|
Whether the first list is a prefix of the second list.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`list1`
|
`list1`
|
||||||
|
@ -1571,10 +1562,7 @@ rec {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
hasPrefix =
|
hasPrefix = list1: list2: take (length list1) list2 == list1;
|
||||||
list1:
|
|
||||||
list2:
|
|
||||||
take (length list1) list2 == list1;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Remove the first list as a prefix from the second list.
|
Remove the first list as a prefix from the second list.
|
||||||
|
@ -1610,8 +1598,7 @@ rec {
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
removePrefix =
|
removePrefix =
|
||||||
list1:
|
list1: list2:
|
||||||
list2:
|
|
||||||
if hasPrefix list1 list2 then
|
if hasPrefix list1 list2 then
|
||||||
drop (length list1) list2
|
drop (length list1) list2
|
||||||
else
|
else
|
||||||
|
@ -1655,20 +1642,22 @@ rec {
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
sublist =
|
sublist =
|
||||||
start:
|
start: count: list:
|
||||||
count:
|
let
|
||||||
list:
|
len = length list;
|
||||||
let len = length list; in
|
in
|
||||||
genList
|
genList (n: elemAt list (n + start)) (
|
||||||
(n: elemAt list (n + start))
|
if start >= len then
|
||||||
(if start >= len then 0
|
0
|
||||||
else if start + count > len then len - start
|
else if start + count > len then
|
||||||
else count);
|
len - start
|
||||||
|
else
|
||||||
|
count
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
The common prefix of two lists.
|
The common prefix of two lists.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`list1`
|
`list1`
|
||||||
|
@ -1701,8 +1690,7 @@ rec {
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
commonPrefix =
|
commonPrefix =
|
||||||
list1:
|
list1: list2:
|
||||||
list2:
|
|
||||||
let
|
let
|
||||||
# Zip the lists together into a list of booleans whether each element matches
|
# Zip the lists together into a list of booleans whether each element matches
|
||||||
matchings = zipListsWith (fst: snd: fst != snd) list1 list2;
|
matchings = zipListsWith (fst: snd: fst != snd) list1 list2;
|
||||||
|
@ -1719,7 +1707,6 @@ rec {
|
||||||
|
|
||||||
This function throws an error if the list is empty.
|
This function throws an error if the list is empty.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`list`
|
`list`
|
||||||
|
@ -1743,8 +1730,9 @@ rec {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
last = list:
|
last =
|
||||||
assert lib.assertMsg (list != []) "lists.last: list must not be empty!";
|
list:
|
||||||
|
assert lib.assertMsg (list != [ ]) "lists.last: list must not be empty!";
|
||||||
elemAt list (length list - 1);
|
elemAt list (length list - 1);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1752,7 +1740,6 @@ rec {
|
||||||
|
|
||||||
This function throws an error if the list is empty.
|
This function throws an error if the list is empty.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`list`
|
`list`
|
||||||
|
@ -1776,15 +1763,14 @@ rec {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
init = list:
|
init =
|
||||||
assert lib.assertMsg (list != []) "lists.init: list must not be empty!";
|
list:
|
||||||
|
assert lib.assertMsg (list != [ ]) "lists.init: list must not be empty!";
|
||||||
take (length list - 1) list;
|
take (length list - 1) list;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Return the image of the cross product of some lists by a function.
|
Return the image of the cross product of some lists by a function.
|
||||||
|
|
||||||
|
|
||||||
# Examples
|
# Examples
|
||||||
:::{.example}
|
:::{.example}
|
||||||
## `lib.lists.crossLists` usage example
|
## `lib.lists.crossLists` usage example
|
||||||
|
@ -1814,13 +1800,11 @@ rec {
|
||||||
|
|
||||||
nix-repl> lib.mapCartesianProduct ({x,y}: x+y) { x = [1 2]; y = [3 4]; }
|
nix-repl> lib.mapCartesianProduct ({x,y}: x+y) { x = [1 2]; y = [3 4]; }
|
||||||
[ 4 5 5 6 ]
|
[ 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.
|
Remove duplicate elements from the `list`. O(n^2) complexity.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`list`
|
`list`
|
||||||
|
@ -1844,12 +1828,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.
|
Check if list contains only unique elements. O(n^2) complexity.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`list`
|
`list`
|
||||||
|
@ -1877,7 +1860,6 @@ rec {
|
||||||
*/
|
*/
|
||||||
allUnique = list: (length (unique list) == length list);
|
allUnique = list: (length (unique list) == length list);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Intersects list 'list1' and another list (`list2`).
|
Intersects list 'list1' and another list (`list2`).
|
||||||
|
|
||||||
|
@ -1893,7 +1875,6 @@ rec {
|
||||||
|
|
||||||
: Second list
|
: Second list
|
||||||
|
|
||||||
|
|
||||||
# Examples
|
# Examples
|
||||||
:::{.example}
|
:::{.example}
|
||||||
## `lib.lists.intersectLists` usage example
|
## `lib.lists.intersectLists` usage example
|
||||||
|
@ -1922,7 +1903,6 @@ rec {
|
||||||
|
|
||||||
: Second list
|
: Second list
|
||||||
|
|
||||||
|
|
||||||
# Examples
|
# Examples
|
||||||
:::{.example}
|
:::{.example}
|
||||||
## `lib.lists.subtractLists` usage example
|
## `lib.lists.subtractLists` usage example
|
||||||
|
|
102
lib/meta.nix
102
lib/meta.nix
|
@ -6,14 +6,20 @@
|
||||||
{ lib }:
|
{ lib }:
|
||||||
|
|
||||||
let
|
let
|
||||||
inherit (lib) matchAttrs any all isDerivation getBin assertMsg;
|
inherit (lib)
|
||||||
|
matchAttrs
|
||||||
|
any
|
||||||
|
all
|
||||||
|
isDerivation
|
||||||
|
getBin
|
||||||
|
assertMsg
|
||||||
|
;
|
||||||
inherit (lib.attrsets) mapAttrs' filterAttrs;
|
inherit (lib.attrsets) mapAttrs' filterAttrs;
|
||||||
inherit (builtins) isString match typeOf;
|
inherit (builtins) isString match typeOf;
|
||||||
|
|
||||||
in
|
in
|
||||||
rec {
|
rec {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Add to or override the meta attributes of the given
|
Add to or override the meta attributes of the given
|
||||||
derivation.
|
derivation.
|
||||||
|
@ -28,7 +34,6 @@ rec {
|
||||||
|
|
||||||
: 2\. Function argument
|
: 2\. Function argument
|
||||||
|
|
||||||
|
|
||||||
# Examples
|
# Examples
|
||||||
:::{.example}
|
:::{.example}
|
||||||
## `lib.meta.addMetaAttrs` usage example
|
## `lib.meta.addMetaAttrs` usage example
|
||||||
|
@ -39,9 +44,7 @@ rec {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
addMetaAttrs = newAttrs: drv:
|
addMetaAttrs = newAttrs: drv: drv // { meta = (drv.meta or { }) // newAttrs; };
|
||||||
drv // { meta = (drv.meta or {}) // newAttrs; };
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Disable Hydra builds of given derivation.
|
Disable Hydra builds of given derivation.
|
||||||
|
@ -52,8 +55,7 @@ rec {
|
||||||
|
|
||||||
: 1\. Function argument
|
: 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).
|
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
|
: 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.
|
Like `setName`, but takes the previous name as an argument.
|
||||||
|
@ -88,7 +89,6 @@ rec {
|
||||||
|
|
||||||
: 2\. Function argument
|
: 2\. Function argument
|
||||||
|
|
||||||
|
|
||||||
# Examples
|
# Examples
|
||||||
:::{.example}
|
:::{.example}
|
||||||
## `lib.meta.updateName` usage 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
|
Append a suffix to the name of a package (before the version
|
||||||
|
@ -112,14 +111,19 @@ rec {
|
||||||
|
|
||||||
: 1\. Function argument
|
: 1\. Function argument
|
||||||
*/
|
*/
|
||||||
appendToName = suffix: updateName (name:
|
appendToName =
|
||||||
let x = builtins.parseDrvName name; in "${x.name}-${suffix}-${x.version}");
|
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.
|
Apply a function to each derivation and only to derivations in an attrset.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`f`
|
`f`
|
||||||
|
@ -130,7 +134,8 @@ rec {
|
||||||
|
|
||||||
: 2\. Function argument
|
: 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).
|
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).
|
||||||
|
@ -159,7 +164,6 @@ rec {
|
||||||
`drv`
|
`drv`
|
||||||
|
|
||||||
: 1\. Function argument
|
: 1\. Function argument
|
||||||
|
|
||||||
*/
|
*/
|
||||||
lowPrio = setPrio 10;
|
lowPrio = setPrio 10;
|
||||||
|
|
||||||
|
@ -174,7 +178,6 @@ rec {
|
||||||
*/
|
*/
|
||||||
lowPrioSet = set: mapDerivationAttrset lowPrio set;
|
lowPrioSet = set: mapDerivationAttrset lowPrio set;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Increase the nix-env priority of the package, i.e., this
|
Increase the nix-env priority of the package, i.e., this
|
||||||
version/variant of the package will be preferred.
|
version/variant of the package will be preferred.
|
||||||
|
@ -198,7 +201,6 @@ rec {
|
||||||
*/
|
*/
|
||||||
hiPrioSet = set: mapDerivationAttrset hiPrio set;
|
hiPrioSet = set: mapDerivationAttrset hiPrio set;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Check to see if a platform is matched by the given `meta.platforms`
|
Check to see if a platform is matched by the given `meta.platforms`
|
||||||
element.
|
element.
|
||||||
|
@ -214,7 +216,6 @@ rec {
|
||||||
We can inject these into a pattern for the whole of a structured platform,
|
We can inject these into a pattern for the whole of a structured platform,
|
||||||
and then match that.
|
and then match that.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`platform`
|
`platform`
|
||||||
|
@ -225,7 +226,6 @@ rec {
|
||||||
|
|
||||||
: 2\. Function argument
|
: 2\. Function argument
|
||||||
|
|
||||||
|
|
||||||
# Examples
|
# Examples
|
||||||
:::{.example}
|
:::{.example}
|
||||||
## `lib.meta.platformMatch` usage example
|
## `lib.meta.platformMatch` usage example
|
||||||
|
@ -237,7 +237,9 @@ rec {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
platformMatch = platform: elem: (
|
platformMatch =
|
||||||
|
platform: elem:
|
||||||
|
(
|
||||||
# Check with simple string comparison if elem was a string.
|
# Check with simple string comparison if elem was a string.
|
||||||
#
|
#
|
||||||
# The majority of comparisons done with this function will be against meta.platforms
|
# The majority of comparisons done with this function will be against meta.platforms
|
||||||
|
@ -245,11 +247,12 @@ rec {
|
||||||
#
|
#
|
||||||
# Avoiding an attrset allocation results in significant performance gains (~2-30) across the board in OfBorg
|
# Avoiding an attrset allocation results in significant performance gains (~2-30) across the board in OfBorg
|
||||||
# because this is a hot path for nixpkgs.
|
# because this is a hot path for nixpkgs.
|
||||||
if isString elem then platform ? system && elem == platform.system
|
if isString elem then
|
||||||
else matchAttrs (
|
platform ? system && elem == platform.system
|
||||||
|
else
|
||||||
|
matchAttrs (
|
||||||
# Normalize platform attrset.
|
# Normalize platform attrset.
|
||||||
if elem ? parsed then elem
|
if elem ? parsed then elem else { parsed = elem; }
|
||||||
else { parsed = elem; }
|
|
||||||
) platform
|
) platform
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -263,7 +266,6 @@ rec {
|
||||||
|
|
||||||
2. None of `meta.badPlatforms` pattern matches the given platform.
|
2. None of `meta.badPlatforms` pattern matches the given platform.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`platform`
|
`platform`
|
||||||
|
@ -274,7 +276,6 @@ rec {
|
||||||
|
|
||||||
: 2\. Function argument
|
: 2\. Function argument
|
||||||
|
|
||||||
|
|
||||||
# Examples
|
# Examples
|
||||||
:::{.example}
|
:::{.example}
|
||||||
## `lib.meta.availableOn` usage example
|
## `lib.meta.availableOn` usage example
|
||||||
|
@ -286,9 +287,10 @@ rec {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
availableOn = platform: pkg:
|
availableOn =
|
||||||
((!pkg?meta.platforms) || any (platformMatch platform) pkg.meta.platforms) &&
|
platform: pkg:
|
||||||
all (elem: !platformMatch platform elem) (pkg.meta.badPlatforms or []);
|
((!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.
|
Mapping of SPDX ID to the attributes in lib.licenses.
|
||||||
|
@ -309,13 +311,10 @@ rec {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
licensesSpdx =
|
licensesSpdx = mapAttrs' (_key: license: {
|
||||||
mapAttrs'
|
|
||||||
(_key: license: {
|
|
||||||
name = license.spdxId;
|
name = license.spdxId;
|
||||||
value = license;
|
value = license;
|
||||||
})
|
}) (filterAttrs (_key: license: license ? spdxId) lib.licenses);
|
||||||
(filterAttrs (_key: license: license ? spdxId) lib.licenses);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Get the corresponding attribute in lib.licenses from the SPDX ID
|
Get the corresponding attribute in lib.licenses from the SPDX ID
|
||||||
|
@ -349,8 +348,9 @@ rec {
|
||||||
getLicenseFromSpdxId =
|
getLicenseFromSpdxId =
|
||||||
licstr:
|
licstr:
|
||||||
getLicenseFromSpdxIdOr licstr (
|
getLicenseFromSpdxIdOr licstr (
|
||||||
lib.warn "getLicenseFromSpdxId: No license matches the given SPDX ID: ${licstr}"
|
lib.warn "getLicenseFromSpdxId: No license matches the given SPDX ID: ${licstr}" {
|
||||||
{ shortName = licstr; }
|
shortName = licstr;
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -398,13 +398,12 @@ rec {
|
||||||
name = lib.toLower name;
|
name = lib.toLower name;
|
||||||
inherit value;
|
inherit value;
|
||||||
}) licensesSpdx;
|
}) licensesSpdx;
|
||||||
in licstr: default:
|
in
|
||||||
lowercaseLicenses.${ lib.toLower licstr } or default;
|
licstr: default: lowercaseLicenses.${lib.toLower licstr} or default;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Get the path to the main program of a package based on meta.mainProgram
|
Get the path to the main program of a package based on meta.mainProgram
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`x`
|
`x`
|
||||||
|
@ -430,17 +429,23 @@ rec {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
getExe = x: getExe' x (x.meta.mainProgram or (
|
getExe =
|
||||||
|
x:
|
||||||
|
getExe' x (
|
||||||
|
x.meta.mainProgram or (
|
||||||
# This could be turned into an error when 23.05 is at end of life
|
# 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.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
|
lib.getName
|
||||||
x
|
x
|
||||||
));
|
)
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Get the path of a program of a derivation.
|
Get the path of a program of a derivation.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`x`
|
`x`
|
||||||
|
@ -470,7 +475,8 @@ rec {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
getExe' = x: y:
|
getExe' =
|
||||||
|
x: y:
|
||||||
assert assertMsg (isDerivation x)
|
assert assertMsg (isDerivation x)
|
||||||
"lib.meta.getExe': The first argument is of type ${typeOf x}, but it should be a derivation instead.";
|
"lib.meta.getExe': The first argument is of type ${typeOf x}, but it should be a derivation instead.";
|
||||||
assert assertMsg (isString y)
|
assert assertMsg (isString y)
|
||||||
|
|
1059
lib/modules.nix
1059
lib/modules.nix
File diff suppressed because it is too large
Load diff
283
lib/options.nix
283
lib/options.nix
|
@ -71,7 +71,6 @@ rec {
|
||||||
```
|
```
|
||||||
isOption :: a -> Bool
|
isOption :: a -> Bool
|
||||||
```
|
```
|
||||||
|
|
||||||
*/
|
*/
|
||||||
isOption = lib.isType "option";
|
isOption = lib.isType "option";
|
||||||
|
|
||||||
|
@ -91,7 +90,6 @@ rec {
|
||||||
: Can be any nix value that evaluates.
|
: Can be any nix value that evaluates.
|
||||||
: Usage with `lib.literalMD` or `lib.literalExpression` is supported
|
: Usage with `lib.literalMD` or `lib.literalExpression` is supported
|
||||||
|
|
||||||
|
|
||||||
`example`
|
`example`
|
||||||
: Optional example value used in the manual.
|
: Optional example value used in the manual.
|
||||||
: Can be any nix value that evaluates.
|
: Can be any nix value that evaluates.
|
||||||
|
@ -144,7 +142,7 @@ rec {
|
||||||
internal ? null,
|
internal ? null,
|
||||||
visible ? null,
|
visible ? null,
|
||||||
readOnly ? null,
|
readOnly ? null,
|
||||||
} @ attrs:
|
}@attrs:
|
||||||
attrs // { _type = "option"; };
|
attrs // { _type = "option"; };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -179,7 +177,9 @@ rec {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
mkEnableOption = name: mkOption {
|
mkEnableOption =
|
||||||
|
name:
|
||||||
|
mkOption {
|
||||||
default = false;
|
default = false;
|
||||||
example = true;
|
example = true;
|
||||||
description = "Whether to enable ${name}.";
|
description = "Whether to enable ${name}.";
|
||||||
|
@ -220,7 +220,6 @@ rec {
|
||||||
If you want users to be able to set no package, pass `nullable = true`.
|
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.
|
In this mode a `default = null` will not be interpreted as no default and is interpreted literally.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`pkgs`
|
`pkgs`
|
||||||
|
@ -264,39 +263,33 @@ rec {
|
||||||
mkPackageOption pkgs "hello" { }
|
mkPackageOption pkgs "hello" { }
|
||||||
=> { ...; default = pkgs.hello; defaultText = literalExpression "pkgs.hello"; description = "The hello package to use."; type = package; }
|
=> { ...; default = pkgs.hello; defaultText = literalExpression "pkgs.hello"; description = "The hello package to use."; type = package; }
|
||||||
|
|
||||||
|
|
||||||
mkPackageOption pkgs "GHC" {
|
mkPackageOption pkgs "GHC" {
|
||||||
default = [ "ghc" ];
|
default = [ "ghc" ];
|
||||||
example = "pkgs.haskell.packages.ghc92.ghc.withPackages (hkgs: [ hkgs.primes ])";
|
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; }
|
=> { ...; 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; }
|
||||||
|
|
||||||
|
|
||||||
mkPackageOption pkgs [ "python3Packages" "pytorch" ] {
|
mkPackageOption pkgs [ "python3Packages" "pytorch" ] {
|
||||||
extraDescription = "This is an example and doesn't actually do anything.";
|
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; }
|
=> { ...; 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; }
|
||||||
|
|
||||||
|
|
||||||
mkPackageOption pkgs "nushell" {
|
mkPackageOption pkgs "nushell" {
|
||||||
nullable = true;
|
nullable = true;
|
||||||
}
|
}
|
||||||
=> { ...; default = pkgs.nushell; defaultText = literalExpression "pkgs.nushell"; description = "The nushell package to use."; type = nullOr package; }
|
=> { ...; default = pkgs.nushell; defaultText = literalExpression "pkgs.nushell"; description = "The nushell package to use."; type = nullOr package; }
|
||||||
|
|
||||||
|
|
||||||
mkPackageOption pkgs "coreutils" {
|
mkPackageOption pkgs "coreutils" {
|
||||||
default = null;
|
default = null;
|
||||||
}
|
}
|
||||||
=> { ...; description = "The coreutils package to use."; type = package; }
|
=> { ...; description = "The coreutils package to use."; type = package; }
|
||||||
|
|
||||||
|
|
||||||
mkPackageOption pkgs "dbus" {
|
mkPackageOption pkgs "dbus" {
|
||||||
nullable = true;
|
nullable = true;
|
||||||
default = null;
|
default = null;
|
||||||
}
|
}
|
||||||
=> { ...; default = null; description = "The dbus package to use."; type = nullOr package; }
|
=> { ...; default = null; description = "The dbus package to use."; type = nullOr package; }
|
||||||
|
|
||||||
|
|
||||||
mkPackageOption pkgs.javaPackages "OpenJFX" {
|
mkPackageOption pkgs.javaPackages "OpenJFX" {
|
||||||
default = "openjfx20";
|
default = "openjfx20";
|
||||||
pkgsText = "pkgs.javaPackages";
|
pkgsText = "pkgs.javaPackages";
|
||||||
|
@ -306,35 +299,44 @@ rec {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
mkPackageOption = pkgs:
|
mkPackageOption =
|
||||||
name:
|
pkgs: name:
|
||||||
{
|
{
|
||||||
nullable ? false,
|
nullable ? false,
|
||||||
default ? name,
|
default ? name,
|
||||||
example ? null,
|
example ? null,
|
||||||
extraDescription ? "",
|
extraDescription ? "",
|
||||||
pkgsText ? "pkgs"
|
pkgsText ? "pkgs",
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
name' = if isList name then last name else name;
|
name' = if isList name then last name else name;
|
||||||
default' = if isList default then default else [ default ];
|
default' = if isList default then default else [ default ];
|
||||||
defaultText = concatStringsSep "." default';
|
defaultText = concatStringsSep "." default';
|
||||||
defaultValue = attrByPath default'
|
defaultValue = attrByPath default' (throw "${defaultText} cannot be found in ${pkgsText}") pkgs;
|
||||||
(throw "${defaultText} cannot be found in ${pkgsText}") pkgs;
|
defaults =
|
||||||
defaults = if default != null then {
|
if default != null then
|
||||||
|
{
|
||||||
default = defaultValue;
|
default = defaultValue;
|
||||||
defaultText = literalExpression ("${pkgsText}." + defaultText);
|
defaultText = literalExpression ("${pkgsText}." + defaultText);
|
||||||
} else optionalAttrs nullable {
|
}
|
||||||
|
else
|
||||||
|
optionalAttrs nullable {
|
||||||
default = null;
|
default = null;
|
||||||
};
|
};
|
||||||
in mkOption (defaults // {
|
in
|
||||||
description = "The ${name'} package to use."
|
mkOption (
|
||||||
+ (if extraDescription == "" then "" else " ") + extraDescription;
|
defaults
|
||||||
|
// {
|
||||||
|
description =
|
||||||
|
"The ${name'} package to use." + (if extraDescription == "" then "" else " ") + extraDescription;
|
||||||
type = with lib.types; (if nullable then nullOr else lib.id) package;
|
type = with lib.types; (if nullable then nullOr else lib.id) package;
|
||||||
} // optionalAttrs (example != null) {
|
}
|
||||||
example = literalExpression
|
// optionalAttrs (example != null) {
|
||||||
(if isList example then "${pkgsText}." + concatStringsSep "." example else example);
|
example = literalExpression (
|
||||||
});
|
if isList example then "${pkgsText}." + concatStringsSep "." example else example
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Deprecated alias of mkPackageOption, to be removed in 25.05.
|
Deprecated alias of mkPackageOption, to be removed in 25.05.
|
||||||
|
@ -350,14 +352,16 @@ rec {
|
||||||
without having to implement similar features as long as the
|
without having to implement similar features as long as the
|
||||||
values of the options are not accessed.
|
values of the options are not accessed.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`attrs`
|
`attrs`
|
||||||
|
|
||||||
: Attribute set whose attributes override the argument to `mkOption`.
|
: Attribute set whose attributes override the argument to `mkOption`.
|
||||||
*/
|
*/
|
||||||
mkSinkUndeclaredOptions = attrs: mkOption ({
|
mkSinkUndeclaredOptions =
|
||||||
|
attrs:
|
||||||
|
mkOption (
|
||||||
|
{
|
||||||
internal = true;
|
internal = true;
|
||||||
visible = false;
|
visible = false;
|
||||||
default = false;
|
default = false;
|
||||||
|
@ -368,7 +372,9 @@ rec {
|
||||||
merge = loc: defs: false;
|
merge = loc: defs: false;
|
||||||
};
|
};
|
||||||
apply = x: throw "Option value is not readable because the option is not declared.";
|
apply = x: throw "Option value is not readable because the option is not declared.";
|
||||||
} // attrs);
|
}
|
||||||
|
// attrs
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
A merge function that merges multiple definitions of an option into a single value
|
A merge function that merges multiple definitions of an option into a single value
|
||||||
|
@ -413,18 +419,28 @@ rec {
|
||||||
- If all definitions are attribute sets, they are merged. (`lib.mergeAttrs`)
|
- If all definitions are attribute sets, they are merged. (`lib.mergeAttrs`)
|
||||||
- If all definitions are functions, the first function is applied to the result of the second function. (`f -> x: f x`)
|
- If all definitions are functions, the first function is applied to the result of the second function. (`f -> x: f x`)
|
||||||
- Otherwise, an error is thrown.
|
- Otherwise, an error is thrown.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
mergeDefaultOption = loc: defs:
|
mergeDefaultOption =
|
||||||
let list = getValues defs; in
|
loc: defs:
|
||||||
if length list == 1 then head list
|
let
|
||||||
else if all isFunction list then x: mergeDefaultOption loc (map (f: f x) list)
|
list = getValues defs;
|
||||||
else if all isList list then concatLists list
|
in
|
||||||
else if all isAttrs list then foldl' lib.mergeAttrs {} list
|
if length list == 1 then
|
||||||
else if all isBool list then foldl' lib.or false list
|
head list
|
||||||
else if all isString list then lib.concatStrings list
|
else if all isFunction list then
|
||||||
else if all isInt list && all (x: x == head list) list then head list
|
x: mergeDefaultOption loc (map (f: f x) list)
|
||||||
else throw "Cannot merge definitions of `${showOption loc}'. Definition values:${showDefs defs}";
|
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.
|
Require a single definition.
|
||||||
|
@ -438,7 +454,6 @@ rec {
|
||||||
|
|
||||||
NOTE: When the type is not checked completely by check, pass a merge function for further checking (of sub-attributes, etc).
|
NOTE: When the type is not checked completely by check, pass a merge function for further checking (of sub-attributes, etc).
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`loc`
|
`loc`
|
||||||
|
@ -449,16 +464,18 @@ rec {
|
||||||
|
|
||||||
: 3\. Function argument
|
: 3\. Function argument
|
||||||
*/
|
*/
|
||||||
mergeUniqueOption = args@{
|
mergeUniqueOption =
|
||||||
|
args@{
|
||||||
message,
|
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
|
# 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)
|
# - 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 attribute values to be checked, or list items
|
||||||
# - if you want coercedTo-like behavior to work
|
# - if you want coercedTo-like behavior to work
|
||||||
merge ? loc: defs: (head defs).value }:
|
merge ? loc: defs: (head defs).value,
|
||||||
|
}:
|
||||||
loc: defs:
|
loc: defs:
|
||||||
if length defs == 1
|
if length defs == 1 then
|
||||||
then merge loc defs
|
merge loc defs
|
||||||
else
|
else
|
||||||
assert length defs > 1;
|
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}";
|
throw "The option `${showOption loc}' is defined multiple times while it's expected to be unique.\n${message}\nDefinition values:${showDefs defs}\n${prioritySuggestion}";
|
||||||
|
@ -466,7 +483,6 @@ rec {
|
||||||
/**
|
/**
|
||||||
"Merge" option definitions by checking that they all have the same value.
|
"Merge" option definitions by checking that they all have the same value.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`loc`
|
`loc`
|
||||||
|
@ -477,17 +493,28 @@ rec {
|
||||||
|
|
||||||
: 2\. Function argument
|
: 2\. Function argument
|
||||||
*/
|
*/
|
||||||
mergeEqualOption = loc: defs:
|
mergeEqualOption =
|
||||||
if defs == [] then abort "This case should never happen."
|
loc: defs:
|
||||||
|
if defs == [ ] then
|
||||||
|
abort "This case should never happen."
|
||||||
# Return early if we only have one element
|
# Return early if we only have one element
|
||||||
# This also makes it work for functions, because the foldl' below would try
|
# 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
|
# to compare the first element with itself, which is false for functions
|
||||||
else if length defs == 1 then (head defs).value
|
else if length defs == 1 then
|
||||||
else (foldl' (first: def:
|
(head defs).value
|
||||||
if def.value != first.value then
|
|
||||||
throw "The option `${showOption loc}' has conflicting definition values:${showDefs [ first def ]}\n${prioritySuggestion}"
|
|
||||||
else
|
else
|
||||||
first) (head defs) (tail defs)).value;
|
(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.
|
||||||
|
@ -535,48 +562,50 @@ rec {
|
||||||
|
|
||||||
# Generate documentation template from the list of option declaration like
|
# Generate documentation template from the list of option declaration like
|
||||||
# the set generated with filterOptionSets.
|
# the set generated with filterOptionSets.
|
||||||
optionAttrSetToDocList = optionAttrSetToDocList' [];
|
optionAttrSetToDocList = optionAttrSetToDocList' [ ];
|
||||||
|
|
||||||
optionAttrSetToDocList' = _: options:
|
optionAttrSetToDocList' =
|
||||||
concatMap (opt:
|
_: options:
|
||||||
|
concatMap (
|
||||||
|
opt:
|
||||||
let
|
let
|
||||||
name = showOption opt.loc;
|
name = showOption opt.loc;
|
||||||
docOption = {
|
docOption =
|
||||||
|
{
|
||||||
loc = opt.loc;
|
loc = opt.loc;
|
||||||
inherit name;
|
inherit name;
|
||||||
description = opt.description or null;
|
description = opt.description or null;
|
||||||
declarations = filter (x: x != unknownModule) opt.declarations;
|
declarations = filter (x: x != unknownModule) opt.declarations;
|
||||||
internal = opt.internal or false;
|
internal = opt.internal or false;
|
||||||
visible =
|
visible = if (opt ? visible && opt.visible == "shallow") then true else opt.visible or true;
|
||||||
if (opt?visible && opt.visible == "shallow")
|
|
||||||
then true
|
|
||||||
else opt.visible or true;
|
|
||||||
readOnly = opt.readOnly or false;
|
readOnly = opt.readOnly or false;
|
||||||
type = opt.type.description or "unspecified";
|
type = opt.type.description or "unspecified";
|
||||||
}
|
}
|
||||||
// optionalAttrs (opt ? example) {
|
// optionalAttrs (opt ? example) {
|
||||||
example =
|
example = builtins.addErrorContext "while evaluating the example of option `${name}`" (
|
||||||
builtins.addErrorContext "while evaluating the example of option `${name}`" (
|
|
||||||
renderOptionValue opt.example
|
renderOptionValue opt.example
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// optionalAttrs (opt ? defaultText || opt ? default) {
|
// optionalAttrs (opt ? defaultText || opt ? default) {
|
||||||
default =
|
default = builtins.addErrorContext "while evaluating the ${
|
||||||
builtins.addErrorContext "while evaluating the ${if opt?defaultText then "defaultText" else "default value"} of option `${name}`" (
|
if opt ? defaultText then "defaultText" else "default value"
|
||||||
renderOptionValue (opt.defaultText or opt.default)
|
} of option `${name}`" (renderOptionValue (opt.defaultText or opt.default));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
// optionalAttrs (opt ? relatedPackages && opt.relatedPackages != null) { inherit (opt) relatedPackages; };
|
// optionalAttrs (opt ? relatedPackages && opt.relatedPackages != null) {
|
||||||
|
inherit (opt) relatedPackages;
|
||||||
|
};
|
||||||
|
|
||||||
subOptions =
|
subOptions =
|
||||||
let ss = opt.type.getSubOptions opt.loc;
|
let
|
||||||
in if ss != {} then optionAttrSetToDocList' opt.loc ss else [];
|
ss = opt.type.getSubOptions opt.loc;
|
||||||
|
in
|
||||||
|
if ss != { } then optionAttrSetToDocList' opt.loc ss else [ ];
|
||||||
subOptionsVisible = docOption.visible && opt.visible or null != "shallow";
|
subOptionsVisible = docOption.visible && opt.visible or null != "shallow";
|
||||||
in
|
in
|
||||||
# To find infinite recursion in NixOS option docs:
|
# To find infinite recursion in NixOS option docs:
|
||||||
# builtins.trace opt.loc
|
# builtins.trace opt.loc
|
||||||
[ docOption ] ++ optionals subOptionsVisible subOptions) (collect isOption options);
|
[ docOption ] ++ optionals subOptionsVisible subOptions
|
||||||
|
) (collect isOption options);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
This function recursively removes all derivation attributes from
|
This function recursively removes all derivation attributes from
|
||||||
|
@ -590,39 +619,49 @@ rec {
|
||||||
This function was made obsolete by renderOptionValue and is kept for
|
This function was made obsolete by renderOptionValue and is kept for
|
||||||
compatibility with out-of-tree code.
|
compatibility with out-of-tree code.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`x`
|
`x`
|
||||||
|
|
||||||
: 1\. Function argument
|
: 1\. Function argument
|
||||||
*/
|
*/
|
||||||
scrubOptionValue = x:
|
scrubOptionValue =
|
||||||
|
x:
|
||||||
if isDerivation x then
|
if isDerivation x then
|
||||||
{ type = "derivation"; drvPath = x.name; outPath = x.name; name = x.name; }
|
{
|
||||||
else if isList x then map scrubOptionValue x
|
type = "derivation";
|
||||||
else if isAttrs x then mapAttrs (n: v: scrubOptionValue v) (removeAttrs x ["_args"])
|
drvPath = x.name;
|
||||||
else x;
|
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
|
Ensures that the given option value (default or example) is a `_type`d string
|
||||||
by rendering Nix values to `literalExpression`s.
|
by rendering Nix values to `literalExpression`s.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`v`
|
`v`
|
||||||
|
|
||||||
: 1\. Function argument
|
: 1\. Function argument
|
||||||
*/
|
*/
|
||||||
renderOptionValue = v:
|
renderOptionValue =
|
||||||
if v ? _type && v ? text then v
|
v:
|
||||||
else literalExpression (lib.generators.toPretty {
|
if v ? _type && v ? text then
|
||||||
|
v
|
||||||
|
else
|
||||||
|
literalExpression (
|
||||||
|
lib.generators.toPretty {
|
||||||
multiline = true;
|
multiline = true;
|
||||||
allowPrettyValues = true;
|
allowPrettyValues = true;
|
||||||
} v);
|
} v
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
For use in the `defaultText` and `example` option attributes. Causes the
|
For use in the `defaultText` and `example` option attributes. Causes the
|
||||||
|
@ -630,16 +669,21 @@ rec {
|
||||||
is necessary for complex values, e.g. functions, or values that depend on
|
is necessary for complex values, e.g. functions, or values that depend on
|
||||||
other values or packages.
|
other values or packages.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`text`
|
`text`
|
||||||
|
|
||||||
: 1\. Function argument
|
: 1\. Function argument
|
||||||
*/
|
*/
|
||||||
literalExpression = text:
|
literalExpression =
|
||||||
if ! isString text then throw "literalExpression expects a string."
|
text:
|
||||||
else { _type = "literalExpression"; inherit 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;
|
literalExample = lib.warn "lib.literalExample is deprecated, use lib.literalExpression instead, or use lib.literalMD for a non-Nix description." literalExpression;
|
||||||
|
|
||||||
|
@ -648,16 +692,21 @@ rec {
|
||||||
given MD text to be inserted verbatim in the documentation, for when
|
given MD text to be inserted verbatim in the documentation, for when
|
||||||
a `literalExpression` would be too hard to read.
|
a `literalExpression` would be too hard to read.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`text`
|
`text`
|
||||||
|
|
||||||
: 1\. Function argument
|
: 1\. Function argument
|
||||||
*/
|
*/
|
||||||
literalMD = text:
|
literalMD =
|
||||||
if ! isString text then throw "literalMD expects a string."
|
text:
|
||||||
else { _type = "literalMD"; inherit text; };
|
if !isString text then
|
||||||
|
throw "literalMD expects a string."
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_type = "literalMD";
|
||||||
|
inherit text;
|
||||||
|
};
|
||||||
|
|
||||||
# Helper functions.
|
# Helper functions.
|
||||||
|
|
||||||
|
@ -665,14 +714,12 @@ rec {
|
||||||
Convert an option, described as a list of the option parts to a
|
Convert an option, described as a list of the option parts to a
|
||||||
human-readable version.
|
human-readable version.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`parts`
|
`parts`
|
||||||
|
|
||||||
: 1\. Function argument
|
: 1\. Function argument
|
||||||
|
|
||||||
|
|
||||||
# Examples
|
# Examples
|
||||||
:::{.example}
|
:::{.example}
|
||||||
## `showOption` usage example
|
## `showOption` usage example
|
||||||
|
@ -690,35 +737,52 @@ rec {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
showOption = parts: let
|
showOption =
|
||||||
|
parts:
|
||||||
|
let
|
||||||
# If the part is a named placeholder of the form "<...>" don't escape it.
|
# If the part is a named placeholder of the form "<...>" don't escape it.
|
||||||
# It may cause misleading escaping if somebody uses literally "<...>" in their option names.
|
# It may cause misleading escaping if somebody uses literally "<...>" in their option names.
|
||||||
# This is the trade-off to allow for placeholders in option names.
|
# This is the trade-off to allow for placeholders in option names.
|
||||||
isNamedPlaceholder = builtins.match "<(.*)>";
|
isNamedPlaceholder = builtins.match "<(.*)>";
|
||||||
escapeOptionPart = part:
|
escapeOptionPart =
|
||||||
if part == "*" || isNamedPlaceholder part != null
|
part:
|
||||||
then part
|
if part == "*" || isNamedPlaceholder part != null then
|
||||||
else lib.strings.escapeNixIdentifier part;
|
part
|
||||||
in (concatStringsSep ".") (map escapeOptionPart parts);
|
else
|
||||||
|
lib.strings.escapeNixIdentifier part;
|
||||||
|
in
|
||||||
|
(concatStringsSep ".") (map escapeOptionPart parts);
|
||||||
showFiles = files: concatStringsSep " and " (map (f: "`${f}'") files);
|
showFiles = files: concatStringsSep " and " (map (f: "`${f}'") files);
|
||||||
|
|
||||||
showDefs = defs: concatMapStrings (def:
|
showDefs =
|
||||||
|
defs:
|
||||||
|
concatMapStrings (
|
||||||
|
def:
|
||||||
let
|
let
|
||||||
# Pretty print the value for display, if successful
|
# Pretty print the value for display, if successful
|
||||||
prettyEval = builtins.tryEval
|
prettyEval = builtins.tryEval (
|
||||||
(lib.generators.toPretty { }
|
lib.generators.toPretty { } (
|
||||||
(lib.generators.withRecursion { depthLimit = 10; throwOnDepthLimit = false; } def.value));
|
lib.generators.withRecursion {
|
||||||
|
depthLimit = 10;
|
||||||
|
throwOnDepthLimit = false;
|
||||||
|
} def.value
|
||||||
|
)
|
||||||
|
);
|
||||||
# Split it into its lines
|
# Split it into its lines
|
||||||
lines = filter (v: ! isList v) (builtins.split "\n" prettyEval.value);
|
lines = filter (v: !isList v) (builtins.split "\n" prettyEval.value);
|
||||||
# Only display the first 5 lines, and indent them for better visibility
|
# Only display the first 5 lines, and indent them for better visibility
|
||||||
value = concatStringsSep "\n " (take 5 lines ++ optional (length lines > 5) "...");
|
value = concatStringsSep "\n " (take 5 lines ++ optional (length lines > 5) "...");
|
||||||
result =
|
result =
|
||||||
# Don't print any value if evaluating the value strictly fails
|
# Don't print any value if evaluating the value strictly fails
|
||||||
if ! prettyEval.success then ""
|
if !prettyEval.success then
|
||||||
|
""
|
||||||
# Put it on a new line if it consists of multiple
|
# Put it on a new line if it consists of multiple
|
||||||
else if length lines > 1 then ":\n " + value
|
else if length lines > 1 then
|
||||||
else ": " + value;
|
":\n " + value
|
||||||
in "\n- In `${def.file}'${result}"
|
else
|
||||||
|
": " + value;
|
||||||
|
in
|
||||||
|
"\n- In `${def.file}'${result}"
|
||||||
) defs;
|
) defs;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -733,7 +797,6 @@ rec {
|
||||||
:::{.example}
|
:::{.example}
|
||||||
## `lib.options.showOptionWithDefLocs` usage example
|
## `lib.options.showOptionWithDefLocs` usage example
|
||||||
|
|
||||||
|
|
||||||
```nix
|
```nix
|
||||||
showOptionWithDefLocs { loc = ["x" "y" ]; files = [ "foo.nix" "bar.nix" ]; }
|
showOptionWithDefLocs { loc = ["x" "y" ]; files = [ "foo.nix" "bar.nix" ]; }
|
||||||
"x.y, with values defined in:\n - foo.nix\n - bar.nix\n"
|
"x.y, with values defined in:\n - foo.nix\n - bar.nix\n"
|
||||||
|
|
|
@ -125,30 +125,58 @@ rec {
|
||||||
- Ordering the dependent phases of `system.userActivationScripts`
|
- Ordering the dependent phases of `system.userActivationScripts`
|
||||||
|
|
||||||
For further examples see: [NixOS activation script](https://nixos.org/manual/nixos/stable/#sec-activation-script)
|
For further examples see: [NixOS activation script](https://nixos.org/manual/nixos/stable/#sec-activation-script)
|
||||||
|
|
||||||
*/
|
*/
|
||||||
textClosureList = predefined: arg:
|
textClosureList =
|
||||||
|
predefined: arg:
|
||||||
let
|
let
|
||||||
f = done: todo:
|
f =
|
||||||
if todo == [] then {result = []; inherit done;}
|
done: todo:
|
||||||
|
if todo == [ ] then
|
||||||
|
{
|
||||||
|
result = [ ];
|
||||||
|
inherit done;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
let entry = head todo; in
|
let
|
||||||
|
entry = head todo;
|
||||||
|
in
|
||||||
if isAttrs entry then
|
if isAttrs entry then
|
||||||
let x = f done entry.deps;
|
let
|
||||||
|
x = f done entry.deps;
|
||||||
y = f x.done (tail todo);
|
y = f x.done (tail todo);
|
||||||
in { result = x.result ++ [entry.text] ++ y.result;
|
in
|
||||||
|
{
|
||||||
|
result = x.result ++ [ entry.text ] ++ y.result;
|
||||||
done = y.done;
|
done = y.done;
|
||||||
}
|
}
|
||||||
else if done ? ${entry} then f done (tail todo)
|
else if done ? ${entry} then
|
||||||
else f (done // listToAttrs [{name = entry; value = 1;}]) ([predefined.${entry}] ++ tail todo);
|
f done (tail todo)
|
||||||
in (f {} arg).result;
|
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));
|
concatStringsSep "\n" (map f (textClosureList predefined names));
|
||||||
|
|
||||||
noDepEntry = text: {inherit text; deps = [];};
|
noDepEntry = text: {
|
||||||
fullDepEntry = text: deps: {inherit text deps;};
|
inherit text;
|
||||||
packEntry = deps: {inherit deps; text="";};
|
deps = [ ];
|
||||||
|
};
|
||||||
|
fullDepEntry = text: deps: { inherit text deps; };
|
||||||
|
packEntry = deps: {
|
||||||
|
inherit deps;
|
||||||
|
text = "";
|
||||||
|
};
|
||||||
|
|
||||||
stringAfter = deps: text: { inherit text deps; };
|
stringAfter = deps: text: { inherit text deps; };
|
||||||
|
|
||||||
|
|
558
lib/strings.nix
558
lib/strings.nix
File diff suppressed because it is too large
Load diff
|
@ -7,41 +7,328 @@ rec {
|
||||||
# Spec: https://gitlab.com/x86-psABIs/x86-64-ABI/
|
# Spec: https://gitlab.com/x86-psABIs/x86-64-ABI/
|
||||||
default = [ ];
|
default = [ ];
|
||||||
x86-64 = [ ];
|
x86-64 = [ ];
|
||||||
x86-64-v2 = [ "sse3" "ssse3" "sse4_1" "sse4_2" ];
|
x86-64-v2 = [
|
||||||
x86-64-v3 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "avx" "avx2" "fma" ];
|
"sse3"
|
||||||
x86-64-v4 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "avx" "avx2" "avx512" "fma" ];
|
"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
|
# x86_64 Intel
|
||||||
nehalem = [ "sse3" "ssse3" "sse4_1" "sse4_2" ];
|
nehalem = [
|
||||||
westmere = [ "sse3" "ssse3" "sse4_1" "sse4_2" ];
|
"sse3"
|
||||||
silvermont = [ "sse3" "ssse3" "sse4_1" "sse4_2" ];
|
"ssse3"
|
||||||
sandybridge = [ "sse3" "ssse3" "sse4_1" "sse4_2" "avx" ];
|
"sse4_1"
|
||||||
ivybridge = [ "sse3" "ssse3" "sse4_1" "sse4_2" "avx" ];
|
"sse4_2"
|
||||||
haswell = [ "sse3" "ssse3" "sse4_1" "sse4_2" "avx" "avx2" "fma" ];
|
];
|
||||||
broadwell = [ "sse3" "ssse3" "sse4_1" "sse4_2" "avx" "avx2" "fma" ];
|
westmere = [
|
||||||
skylake = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "fma" ];
|
"sse3"
|
||||||
skylake-avx512 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "avx512" "fma" ];
|
"ssse3"
|
||||||
cannonlake = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "avx512" "fma" ];
|
"sse4_1"
|
||||||
icelake-client = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "avx512" "fma" ];
|
"sse4_2"
|
||||||
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" ];
|
silvermont = [
|
||||||
cooperlake = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "avx512" "fma" ];
|
"sse3"
|
||||||
tigerlake = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "avx512" "fma" ];
|
"ssse3"
|
||||||
alderlake = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "fma" ];
|
"sse4_1"
|
||||||
sapphirerapids = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "avx512" "fma" ];
|
"sse4_2"
|
||||||
emeraldrapids = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "avx512" "fma" ];
|
];
|
||||||
sierraforest = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "fma" ];
|
sandybridge = [
|
||||||
|
"sse3"
|
||||||
|
"ssse3"
|
||||||
|
"sse4_1"
|
||||||
|
"sse4_2"
|
||||||
|
"avx"
|
||||||
|
];
|
||||||
|
ivybridge = [
|
||||||
|
"sse3"
|
||||||
|
"ssse3"
|
||||||
|
"sse4_1"
|
||||||
|
"sse4_2"
|
||||||
|
"avx"
|
||||||
|
];
|
||||||
|
haswell = [
|
||||||
|
"sse3"
|
||||||
|
"ssse3"
|
||||||
|
"sse4_1"
|
||||||
|
"sse4_2"
|
||||||
|
"avx"
|
||||||
|
"avx2"
|
||||||
|
"fma"
|
||||||
|
];
|
||||||
|
broadwell = [
|
||||||
|
"sse3"
|
||||||
|
"ssse3"
|
||||||
|
"sse4_1"
|
||||||
|
"sse4_2"
|
||||||
|
"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"
|
||||||
|
];
|
||||||
|
sierraforest = [
|
||||||
|
"sse3"
|
||||||
|
"ssse3"
|
||||||
|
"sse4_1"
|
||||||
|
"sse4_2"
|
||||||
|
"aes"
|
||||||
|
"avx"
|
||||||
|
"avx2"
|
||||||
|
"fma"
|
||||||
|
];
|
||||||
# x86_64 AMD
|
# x86_64 AMD
|
||||||
btver1 = [ "sse3" "ssse3" "sse4_1" "sse4_2" ];
|
btver1 = [
|
||||||
btver2 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" ];
|
"sse3"
|
||||||
bdver1 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "sse4a" "aes" "avx" "fma" "fma4" ];
|
"ssse3"
|
||||||
bdver2 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "sse4a" "aes" "avx" "fma" "fma4" ];
|
"sse4_1"
|
||||||
bdver3 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "sse4a" "aes" "avx" "fma" "fma4" ];
|
"sse4_2"
|
||||||
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" ];
|
btver2 = [
|
||||||
znver2 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "sse4a" "aes" "avx" "avx2" "fma" ];
|
"sse3"
|
||||||
znver3 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "sse4a" "aes" "avx" "avx2" "fma" ];
|
"ssse3"
|
||||||
znver4 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "sse4a" "aes" "avx" "avx2" "avx512" "fma" ];
|
"sse4_1"
|
||||||
znver5 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "sse4a" "aes" "avx" "avx2" "avx512" "fma" ];
|
"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"
|
||||||
|
];
|
||||||
|
znver5 = [
|
||||||
|
"sse3"
|
||||||
|
"ssse3"
|
||||||
|
"sse4_1"
|
||||||
|
"sse4_2"
|
||||||
|
"sse4a"
|
||||||
|
"aes"
|
||||||
|
"avx"
|
||||||
|
"avx2"
|
||||||
|
"avx512"
|
||||||
|
"fma"
|
||||||
|
];
|
||||||
# other
|
# other
|
||||||
armv5te = [ ];
|
armv5te = [ ];
|
||||||
armv6 = [ ];
|
armv6 = [ ];
|
||||||
|
@ -67,11 +354,25 @@ rec {
|
||||||
sandybridge = [ "westmere" ] ++ inferiors.westmere;
|
sandybridge = [ "westmere" ] ++ inferiors.westmere;
|
||||||
ivybridge = [ "sandybridge" ] ++ inferiors.sandybridge;
|
ivybridge = [ "sandybridge" ] ++ inferiors.sandybridge;
|
||||||
|
|
||||||
haswell = lib.unique ([ "ivybridge" "x86-64-v3" ] ++ inferiors.ivybridge ++ inferiors.x86-64-v3);
|
haswell = lib.unique (
|
||||||
|
[
|
||||||
|
"ivybridge"
|
||||||
|
"x86-64-v3"
|
||||||
|
]
|
||||||
|
++ inferiors.ivybridge
|
||||||
|
++ inferiors.x86-64-v3
|
||||||
|
);
|
||||||
broadwell = [ "haswell" ] ++ inferiors.haswell;
|
broadwell = [ "haswell" ] ++ inferiors.haswell;
|
||||||
skylake = [ "broadwell" ] ++ inferiors.broadwell;
|
skylake = [ "broadwell" ] ++ inferiors.broadwell;
|
||||||
|
|
||||||
skylake-avx512 = lib.unique ([ "skylake" "x86-64-v4" ] ++ inferiors.skylake ++ inferiors.x86-64-v4);
|
skylake-avx512 = lib.unique (
|
||||||
|
[
|
||||||
|
"skylake"
|
||||||
|
"x86-64-v4"
|
||||||
|
]
|
||||||
|
++ inferiors.skylake
|
||||||
|
++ inferiors.x86-64-v4
|
||||||
|
);
|
||||||
cannonlake = [ "skylake-avx512" ] ++ inferiors.skylake-avx512;
|
cannonlake = [ "skylake-avx512" ] ++ inferiors.skylake-avx512;
|
||||||
icelake-client = [ "cannonlake" ] ++ inferiors.cannonlake;
|
icelake-client = [ "cannonlake" ] ++ inferiors.cannonlake;
|
||||||
icelake-server = [ "icelake-client" ] ++ inferiors.icelake-client;
|
icelake-server = [ "icelake-client" ] ++ inferiors.icelake-client;
|
||||||
|
@ -115,7 +416,14 @@ rec {
|
||||||
znver1 = [ "skylake" ] ++ inferiors.skylake; # Includes haswell and x86-64-v3
|
znver1 = [ "skylake" ] ++ inferiors.skylake; # Includes haswell and x86-64-v3
|
||||||
znver2 = [ "znver1" ] ++ inferiors.znver1;
|
znver2 = [ "znver1" ] ++ inferiors.znver1;
|
||||||
znver3 = [ "znver2" ] ++ inferiors.znver2;
|
znver3 = [ "znver2" ] ++ inferiors.znver2;
|
||||||
znver4 = lib.unique ([ "znver3" "x86-64-v4" ] ++ inferiors.znver3 ++ inferiors.x86-64-v4);
|
znver4 = lib.unique (
|
||||||
|
[
|
||||||
|
"znver3"
|
||||||
|
"x86-64-v4"
|
||||||
|
]
|
||||||
|
++ inferiors.znver3
|
||||||
|
++ inferiors.x86-64-v4
|
||||||
|
);
|
||||||
znver5 = [ "znver4" ] ++ inferiors.znver4;
|
znver5 = [ "znver4" ] ++ inferiors.znver4;
|
||||||
|
|
||||||
# other
|
# other
|
||||||
|
@ -127,9 +435,11 @@ rec {
|
||||||
loongson2f = [ ];
|
loongson2f = [ ];
|
||||||
};
|
};
|
||||||
|
|
||||||
predicates = let
|
predicates =
|
||||||
featureSupport = feature: x: builtins.elem feature features.${x} or [];
|
let
|
||||||
in {
|
featureSupport = feature: x: builtins.elem feature features.${x} or [ ];
|
||||||
|
in
|
||||||
|
{
|
||||||
sse3Support = featureSupport "sse3";
|
sse3Support = featureSupport "sse3";
|
||||||
ssse3Support = featureSupport "ssse3";
|
ssse3Support = featureSupport "ssse3";
|
||||||
sse4_1Support = featureSupport "sse4_1";
|
sse4_1Support = featureSupport "sse4_1";
|
||||||
|
|
|
@ -42,8 +42,10 @@ let
|
||||||
both arguments have been `elaborate`-d.
|
both arguments have been `elaborate`-d.
|
||||||
*/
|
*/
|
||||||
equals =
|
equals =
|
||||||
let removeFunctions = a: filterAttrs (_: v: !isFunction v) a;
|
let
|
||||||
in a: b: removeFunctions a == removeFunctions b;
|
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
|
List of all Nix system doubles the nixpkgs flake will expose the package set
|
||||||
|
@ -57,8 +59,8 @@ let
|
||||||
|
|
||||||
# Turn localSystem or crossSystem, which could be system-string or attrset, into
|
# Turn localSystem or crossSystem, which could be system-string or attrset, into
|
||||||
# attrset.
|
# attrset.
|
||||||
systemToAttrs = systemOrArgs:
|
systemToAttrs =
|
||||||
if isAttrs systemOrArgs then systemOrArgs else { system = systemOrArgs; };
|
systemOrArgs: if isAttrs systemOrArgs then systemOrArgs else { system = systemOrArgs; };
|
||||||
|
|
||||||
# Elaborate a `localSystem` or `crossSystem` so that it contains everything
|
# Elaborate a `localSystem` or `crossSystem` so that it contains everything
|
||||||
# necessary.
|
# necessary.
|
||||||
|
@ -66,17 +68,23 @@ let
|
||||||
# `parsed` is inferred from args, both because there are two options with one
|
# `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
|
# clearly preferred, and to prevent cycles. A simpler fixed point where the RHS
|
||||||
# always just used `final.*` would fail on both counts.
|
# always just used `final.*` would fail on both counts.
|
||||||
elaborate = systemOrArgs: let
|
elaborate =
|
||||||
|
systemOrArgs:
|
||||||
|
let
|
||||||
allArgs = systemToAttrs systemOrArgs;
|
allArgs = systemToAttrs systemOrArgs;
|
||||||
|
|
||||||
# Those two will always be derived from "config", if given, so they should NOT
|
# Those two will always be derived from "config", if given, so they should NOT
|
||||||
# be overridden further down with "// args".
|
# be overridden further down with "// args".
|
||||||
args = builtins.removeAttrs allArgs [ "parsed" "system" ];
|
args = builtins.removeAttrs allArgs [
|
||||||
|
"parsed"
|
||||||
|
"system"
|
||||||
|
];
|
||||||
|
|
||||||
# TODO: deprecate args.rustc in favour of args.rust after 23.05 is EOL.
|
# TODO: deprecate args.rustc in favour of args.rust after 23.05 is EOL.
|
||||||
rust = args.rust or args.rustc or {};
|
rust = args.rust or args.rustc or { };
|
||||||
|
|
||||||
final = {
|
final =
|
||||||
|
{
|
||||||
# Prefer to parse `config` as it is strictly more informative.
|
# Prefer to parse `config` as it is strictly more informative.
|
||||||
parsed = parse.mkSystemFromString (args.config or allArgs.system);
|
parsed = parse.mkSystemFromString (args.config or allArgs.system);
|
||||||
# This can be losslessly-extracted from `parsed` iff parsing succeeds.
|
# This can be losslessly-extracted from `parsed` iff parsing succeeds.
|
||||||
|
@ -85,33 +93,55 @@ let
|
||||||
# because of -mingw32.
|
# because of -mingw32.
|
||||||
config = parse.tripleFromSystem final.parsed;
|
config = parse.tripleFromSystem final.parsed;
|
||||||
# Determine whether we can execute binaries built for the provided platform.
|
# Determine whether we can execute binaries built for the provided platform.
|
||||||
canExecute = platform:
|
canExecute =
|
||||||
final.isAndroid == platform.isAndroid &&
|
platform:
|
||||||
parse.isCompatible final.parsed.cpu platform.parsed.cpu
|
final.isAndroid == platform.isAndroid
|
||||||
|
&& parse.isCompatible final.parsed.cpu platform.parsed.cpu
|
||||||
&& final.parsed.kernel == platform.parsed.kernel;
|
&& 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";
|
isCompatible =
|
||||||
|
_:
|
||||||
|
throw "2022-05-23: isCompatible has been removed in favor of canExecute, refer to the 22.11 changelog for details";
|
||||||
# Derived meta-data
|
# Derived meta-data
|
||||||
useLLVM = final.isFreeBSD || final.isOpenBSD;
|
useLLVM = final.isFreeBSD || final.isOpenBSD;
|
||||||
|
|
||||||
libc =
|
libc =
|
||||||
/**/ if final.isDarwin then "libSystem"
|
if final.isDarwin then
|
||||||
else if final.isMinGW then "msvcrt"
|
"libSystem"
|
||||||
else if final.isWasi then "wasilibc"
|
else if final.isMinGW then
|
||||||
else if final.isWasm && !final.isWasi then null
|
"msvcrt"
|
||||||
else if final.isRedox then "relibc"
|
else if final.isWasi then
|
||||||
else if final.isMusl then "musl"
|
"wasilibc"
|
||||||
else if final.isUClibc then "uclibc"
|
else if final.isWasm && !final.isWasi then
|
||||||
else if final.isAndroid then "bionic"
|
null
|
||||||
else if final.isLLVMLibc then "llvm"
|
else if final.isRedox then
|
||||||
else if final.isLinux /* default */ then "glibc"
|
"relibc"
|
||||||
else if final.isFreeBSD then "fblibc"
|
else if final.isMusl then
|
||||||
else if final.isOpenBSD then "oblibc"
|
"musl"
|
||||||
else if final.isNetBSD then "nblibc"
|
else if final.isUClibc then
|
||||||
else if final.isAvr then "avrlibc"
|
"uclibc"
|
||||||
else if final.isGhcjs then null
|
else if final.isAndroid then
|
||||||
else if final.isNone then "newlib"
|
"bionic"
|
||||||
|
else if final.isLLVMLibc then
|
||||||
|
"llvm"
|
||||||
|
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
|
# TODO(@Ericson2314) think more about other operating systems
|
||||||
else "native/impure";
|
else
|
||||||
|
"native/impure";
|
||||||
# Choose what linker we wish to use by default. Someday we might also
|
# Choose what linker we wish to use by default. Someday we might also
|
||||||
# choose the C compiler, runtime library, C++ standard library, etc. in
|
# choose the C compiler, runtime library, C++ standard library, etc. in
|
||||||
# this way, nice and orthogonally, and deprecate `useLLVM`. But due to
|
# this way, nice and orthogonally, and deprecate `useLLVM`. But due to
|
||||||
|
@ -119,34 +149,36 @@ let
|
||||||
# independently, so we are just doing `linker` and keeping `useLLVM` for
|
# independently, so we are just doing `linker` and keeping `useLLVM` for
|
||||||
# now.
|
# now.
|
||||||
linker =
|
linker =
|
||||||
/**/ if final.useLLVM or false then "lld"
|
if final.useLLVM or false then
|
||||||
else if final.isDarwin then "cctools"
|
"lld"
|
||||||
|
else if final.isDarwin then
|
||||||
|
"cctools"
|
||||||
# "bfd" and "gold" both come from GNU binutils. The existence of Gold
|
# "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
|
# is why we use the more obscure "bfd" and not "binutils" for this
|
||||||
# choice.
|
# choice.
|
||||||
else "bfd";
|
else
|
||||||
|
"bfd";
|
||||||
# The standard lib directory name that non-nixpkgs binaries distributed
|
# The standard lib directory name that non-nixpkgs binaries distributed
|
||||||
# for this platform normally assume.
|
# for this platform normally assume.
|
||||||
libDir = if final.isLinux then
|
libDir =
|
||||||
if final.isx86_64 || final.isMips64 || final.isPower64
|
if final.isLinux then
|
||||||
then "lib64"
|
if final.isx86_64 || final.isMips64 || final.isPower64 then "lib64" else "lib"
|
||||||
else "lib"
|
else
|
||||||
else null;
|
null;
|
||||||
extensions = optionalAttrs final.hasSharedLibraries {
|
extensions =
|
||||||
|
optionalAttrs final.hasSharedLibraries {
|
||||||
sharedLibrary =
|
sharedLibrary =
|
||||||
if final.isDarwin then ".dylib"
|
if final.isDarwin then
|
||||||
else if final.isWindows then ".dll"
|
".dylib"
|
||||||
else ".so";
|
else if final.isWindows then
|
||||||
} // {
|
".dll"
|
||||||
staticLibrary =
|
else
|
||||||
/**/ if final.isWindows then ".lib"
|
".so";
|
||||||
else ".a";
|
}
|
||||||
library =
|
// {
|
||||||
/**/ if final.isStatic then final.extensions.staticLibrary
|
staticLibrary = if final.isWindows then ".lib" else ".a";
|
||||||
else final.extensions.sharedLibrary;
|
library = if final.isStatic then final.extensions.staticLibrary else final.extensions.sharedLibrary;
|
||||||
executable =
|
executable = if final.isWindows then ".exe" else "";
|
||||||
/**/ if final.isWindows then ".exe"
|
|
||||||
else "";
|
|
||||||
};
|
};
|
||||||
# Misc boolean options
|
# Misc boolean options
|
||||||
useAndroidPrebuilt = false;
|
useAndroidPrebuilt = false;
|
||||||
|
@ -155,7 +187,8 @@ let
|
||||||
# Output from uname
|
# Output from uname
|
||||||
uname = {
|
uname = {
|
||||||
# uname -s
|
# uname -s
|
||||||
system = {
|
system =
|
||||||
|
{
|
||||||
linux = "Linux";
|
linux = "Linux";
|
||||||
windows = "Windows";
|
windows = "Windows";
|
||||||
darwin = "Darwin";
|
darwin = "Darwin";
|
||||||
|
@ -165,17 +198,19 @@ let
|
||||||
wasi = "Wasi";
|
wasi = "Wasi";
|
||||||
redox = "Redox";
|
redox = "Redox";
|
||||||
genode = "Genode";
|
genode = "Genode";
|
||||||
}.${final.parsed.kernel.name} or null;
|
}
|
||||||
|
.${final.parsed.kernel.name} or null;
|
||||||
|
|
||||||
# uname -m
|
# uname -m
|
||||||
processor =
|
processor =
|
||||||
if final.isPower64
|
if final.isPower64 then
|
||||||
then "ppc64${optionalString final.isLittleEndian "le"}"
|
"ppc64${optionalString final.isLittleEndian "le"}"
|
||||||
else if final.isPower
|
else if final.isPower then
|
||||||
then "ppc${optionalString final.isLittleEndian "le"}"
|
"ppc${optionalString final.isLittleEndian "le"}"
|
||||||
else if final.isMips64
|
else if final.isMips64 then
|
||||||
then "mips64" # endianness is *not* included on mips64
|
"mips64" # endianness is *not* included on mips64
|
||||||
else final.parsed.cpu.name;
|
else
|
||||||
|
final.parsed.cpu.name;
|
||||||
|
|
||||||
# uname -r
|
# uname -r
|
||||||
release = null;
|
release = null;
|
||||||
|
@ -187,12 +222,23 @@ let
|
||||||
# will still build on/for those platforms with --enable-shared, but simply
|
# 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,
|
# omit any `.so` build products such as libgcc_s.so. When that happens,
|
||||||
# it causes hard-to-troubleshoot build failures.
|
# it causes hard-to-troubleshoot build failures.
|
||||||
hasSharedLibraries = with final;
|
hasSharedLibraries =
|
||||||
(isAndroid || isGnu || isMusl # Linux (allows multiple libcs)
|
with final;
|
||||||
|| isDarwin || isSunOS || isOpenBSD || isFreeBSD || isNetBSD # BSDs
|
(
|
||||||
|| isCygwin || isMinGW || isWindows # Windows
|
isAndroid
|
||||||
|
|| isGnu
|
||||||
|
|| isMusl # Linux (allows multiple libcs)
|
||||||
|
|| isDarwin
|
||||||
|
|| isSunOS
|
||||||
|
|| isOpenBSD
|
||||||
|
|| isFreeBSD
|
||||||
|
|| isNetBSD # BSDs
|
||||||
|
|| isCygwin
|
||||||
|
|| isMinGW
|
||||||
|
|| isWindows # Windows
|
||||||
|| isWasm # WASM
|
|| isWasm # WASM
|
||||||
) && !isStatic;
|
)
|
||||||
|
&& !isStatic;
|
||||||
|
|
||||||
# The difference between `isStatic` and `hasSharedLibraries` is mainly the
|
# The difference between `isStatic` and `hasSharedLibraries` is mainly the
|
||||||
# addition of the `staticMarker` (see make-derivation.nix). Some
|
# addition of the `staticMarker` (see make-derivation.nix). Some
|
||||||
|
@ -204,70 +250,110 @@ let
|
||||||
|
|
||||||
# Just a guess, based on `system`
|
# Just a guess, based on `system`
|
||||||
inherit
|
inherit
|
||||||
({
|
(
|
||||||
linux-kernel = args.linux-kernel or {};
|
{
|
||||||
gcc = args.gcc or {};
|
linux-kernel = args.linux-kernel or { };
|
||||||
} // platforms.select final)
|
gcc = args.gcc or { };
|
||||||
linux-kernel gcc;
|
}
|
||||||
|
// platforms.select final
|
||||||
|
)
|
||||||
|
linux-kernel
|
||||||
|
gcc
|
||||||
|
;
|
||||||
|
|
||||||
# TODO: remove after 23.05 is EOL, with an error pointing to the rust.* attrs.
|
# TODO: remove after 23.05 is EOL, with an error pointing to the rust.* attrs.
|
||||||
rustc = args.rustc or {};
|
rustc = args.rustc or { };
|
||||||
|
|
||||||
linuxArch =
|
linuxArch =
|
||||||
if final.isAarch32 then "arm"
|
if final.isAarch32 then
|
||||||
else if final.isAarch64 then "arm64"
|
"arm"
|
||||||
else if final.isx86_32 then "i386"
|
else if final.isAarch64 then
|
||||||
else if final.isx86_64 then "x86_64"
|
"arm64"
|
||||||
|
else if final.isx86_32 then
|
||||||
|
"i386"
|
||||||
|
else if final.isx86_64 then
|
||||||
|
"x86_64"
|
||||||
# linux kernel does not distinguish microblaze/microblazeel
|
# linux kernel does not distinguish microblaze/microblazeel
|
||||||
else if final.isMicroBlaze then "microblaze"
|
else if final.isMicroBlaze then
|
||||||
else if final.isMips32 then "mips"
|
"microblaze"
|
||||||
else if final.isMips64 then "mips" # linux kernel does not distinguish mips32/mips64
|
else if final.isMips32 then
|
||||||
else if final.isPower then "powerpc"
|
"mips"
|
||||||
else if final.isRiscV then "riscv"
|
else if final.isMips64 then
|
||||||
else if final.isS390 then "s390"
|
"mips" # linux kernel does not distinguish mips32/mips64
|
||||||
else if final.isLoongArch64 then "loongarch"
|
else if final.isPower then
|
||||||
else final.parsed.cpu.name;
|
"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
|
# https://source.denx.de/u-boot/u-boot/-/blob/9bfb567e5f1bfe7de8eb41f8c6d00f49d2b9a426/common/image.c#L81-106
|
||||||
ubootArch =
|
ubootArch =
|
||||||
if final.isx86_32 then "x86" # not i386
|
if final.isx86_32 then
|
||||||
else if final.isMips64 then "mips64" # uboot *does* distinguish between mips32/mips64
|
"x86" # not i386
|
||||||
else final.linuxArch; # other cases appear to agree with linuxArch
|
else if final.isMips64 then
|
||||||
|
"mips64" # uboot *does* distinguish between mips32/mips64
|
||||||
|
else
|
||||||
|
final.linuxArch; # other cases appear to agree with linuxArch
|
||||||
|
|
||||||
qemuArch =
|
qemuArch =
|
||||||
if final.isAarch32 then "arm"
|
if final.isAarch32 then
|
||||||
else if final.isS390 && !final.isS390x then null
|
"arm"
|
||||||
else if final.isx86_64 then "x86_64"
|
else if final.isS390 && !final.isS390x then
|
||||||
else if final.isx86 then "i386"
|
null
|
||||||
else if final.isMips64n32 then "mipsn32${optionalString final.isLittleEndian "el"}"
|
else if final.isx86_64 then
|
||||||
else if final.isMips64 then "mips64${optionalString final.isLittleEndian "el"}"
|
"x86_64"
|
||||||
else final.uname.processor;
|
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.
|
# Name used by UEFI for architectures.
|
||||||
efiArch =
|
efiArch =
|
||||||
if final.isx86_32 then "ia32"
|
if final.isx86_32 then
|
||||||
else if final.isx86_64 then "x64"
|
"ia32"
|
||||||
else if final.isAarch32 then "arm"
|
else if final.isx86_64 then
|
||||||
else if final.isAarch64 then "aa64"
|
"x64"
|
||||||
else final.parsed.cpu.name;
|
else if final.isAarch32 then
|
||||||
|
"arm"
|
||||||
|
else if final.isAarch64 then
|
||||||
|
"aa64"
|
||||||
|
else
|
||||||
|
final.parsed.cpu.name;
|
||||||
|
|
||||||
darwinArch = {
|
darwinArch =
|
||||||
|
{
|
||||||
armv7a = "armv7";
|
armv7a = "armv7";
|
||||||
aarch64 = "arm64";
|
aarch64 = "arm64";
|
||||||
}.${final.parsed.cpu.name} or final.parsed.cpu.name;
|
}
|
||||||
|
.${final.parsed.cpu.name} or final.parsed.cpu.name;
|
||||||
|
|
||||||
darwinPlatform =
|
darwinPlatform =
|
||||||
if final.isMacOS then "macos"
|
if final.isMacOS then
|
||||||
else if final.isiOS then "ios"
|
"macos"
|
||||||
else null;
|
else if final.isiOS then
|
||||||
|
"ios"
|
||||||
|
else
|
||||||
|
null;
|
||||||
# The canonical name for this attribute is darwinSdkVersion, but some
|
# The canonical name for this attribute is darwinSdkVersion, but some
|
||||||
# platforms define the old name "sdkVer".
|
# platforms define the old name "sdkVer".
|
||||||
darwinSdkVersion = final.sdkVer or "11.3";
|
darwinSdkVersion = final.sdkVer or "11.3";
|
||||||
darwinMinVersion = final.darwinSdkVersion;
|
darwinMinVersion = final.darwinSdkVersion;
|
||||||
darwinMinVersionVariable =
|
darwinMinVersionVariable =
|
||||||
if final.isMacOS then "MACOSX_DEPLOYMENT_TARGET"
|
if final.isMacOS then
|
||||||
else if final.isiOS then "IPHONEOS_DEPLOYMENT_TARGET"
|
"MACOSX_DEPLOYMENT_TARGET"
|
||||||
else null;
|
else if final.isiOS then
|
||||||
|
"IPHONEOS_DEPLOYMENT_TARGET"
|
||||||
|
else
|
||||||
|
null;
|
||||||
|
|
||||||
# Remove before 25.05
|
# Remove before 25.05
|
||||||
androidSdkVersion =
|
androidSdkVersion =
|
||||||
|
@ -284,68 +370,86 @@ let
|
||||||
args.androidNdkVersion
|
args.androidNdkVersion
|
||||||
else
|
else
|
||||||
null;
|
null;
|
||||||
} // (
|
}
|
||||||
|
// (
|
||||||
let
|
let
|
||||||
selectEmulator = pkgs:
|
selectEmulator =
|
||||||
|
pkgs:
|
||||||
let
|
let
|
||||||
wine = (pkgs.winePackagesFor "wine${toString final.parsed.cpu.bits}").minimal;
|
wine = (pkgs.winePackagesFor "wine${toString final.parsed.cpu.bits}").minimal;
|
||||||
in
|
in
|
||||||
# Note: we guarantee that the return value is either `null` or a path
|
# Note: we guarantee that the return value is either `null` or a path
|
||||||
# to an emulator program. That is, if an emulator requires additional
|
# to an emulator program. That is, if an emulator requires additional
|
||||||
# arguments, a wrapper should be used.
|
# arguments, a wrapper should be used.
|
||||||
if pkgs.stdenv.hostPlatform.canExecute final
|
if pkgs.stdenv.hostPlatform.canExecute final then
|
||||||
then lib.getExe (pkgs.writeShellScriptBin "exec" ''exec "$@"'')
|
lib.getExe (pkgs.writeShellScriptBin "exec" ''exec "$@"'')
|
||||||
else if final.isWindows
|
else if final.isWindows then
|
||||||
then "${wine}/bin/wine${optionalString (final.parsed.cpu.bits == 64) "64"}"
|
"${wine}/bin/wine${optionalString (final.parsed.cpu.bits == 64) "64"}"
|
||||||
else if final.isLinux && pkgs.stdenv.hostPlatform.isLinux && final.qemuArch != null
|
else if final.isLinux && pkgs.stdenv.hostPlatform.isLinux && final.qemuArch != null then
|
||||||
then "${pkgs.qemu-user}/bin/qemu-${final.qemuArch}"
|
"${pkgs.qemu-user}/bin/qemu-${final.qemuArch}"
|
||||||
else if final.isWasi
|
else if final.isWasi then
|
||||||
then "${pkgs.wasmtime}/bin/wasmtime"
|
"${pkgs.wasmtime}/bin/wasmtime"
|
||||||
else if final.isMmix
|
else if final.isMmix then
|
||||||
then "${pkgs.mmixware}/bin/mmix"
|
"${pkgs.mmixware}/bin/mmix"
|
||||||
else null;
|
else
|
||||||
in {
|
null;
|
||||||
|
in
|
||||||
|
{
|
||||||
emulatorAvailable = pkgs: (selectEmulator pkgs) != null;
|
emulatorAvailable = pkgs: (selectEmulator pkgs) != null;
|
||||||
|
|
||||||
# whether final.emulator pkgs.pkgsStatic works
|
# whether final.emulator pkgs.pkgsStatic works
|
||||||
staticEmulatorAvailable = pkgs: final.emulatorAvailable pkgs
|
staticEmulatorAvailable =
|
||||||
&& (final.isLinux || final.isWasi || final.isMmix);
|
pkgs: final.emulatorAvailable pkgs && (final.isLinux || final.isWasi || final.isMmix);
|
||||||
|
|
||||||
emulator = pkgs:
|
emulator =
|
||||||
if (final.emulatorAvailable pkgs)
|
pkgs:
|
||||||
then selectEmulator pkgs
|
if (final.emulatorAvailable pkgs) then
|
||||||
else throw "Don't know how to run ${final.config} executables.";
|
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.parsed) inspect.predicates
|
||||||
// mapAttrs (n: v: v final.gcc.arch or "default") architectures.predicates
|
// mapAttrs (n: v: v final.gcc.arch or "default") architectures.predicates
|
||||||
// args // {
|
// args
|
||||||
|
// {
|
||||||
rust = rust // {
|
rust = rust // {
|
||||||
# Once args.rustc.platform.target-family is deprecated and
|
# Once args.rustc.platform.target-family is deprecated and
|
||||||
# removed, there will no longer be any need to modify any
|
# removed, there will no longer be any need to modify any
|
||||||
# values from args.rust.platform, so we can drop all the
|
# values from args.rust.platform, so we can drop all the
|
||||||
# "args ? rust" etc. checks, and merge args.rust.platform in
|
# "args ? rust" etc. checks, and merge args.rust.platform in
|
||||||
# /after/.
|
# /after/.
|
||||||
platform = rust.platform or {} // {
|
platform = rust.platform or { } // {
|
||||||
# https://doc.rust-lang.org/reference/conditional-compilation.html#target_arch
|
# https://doc.rust-lang.org/reference/conditional-compilation.html#target_arch
|
||||||
arch =
|
arch =
|
||||||
/**/ if rust ? platform then rust.platform.arch
|
if rust ? platform then
|
||||||
else if final.isAarch32 then "arm"
|
rust.platform.arch
|
||||||
else if final.isMips64 then "mips64" # never add "el" suffix
|
else if final.isAarch32 then
|
||||||
else if final.isPower64 then "powerpc64" # never add "le" suffix
|
"arm"
|
||||||
else final.parsed.cpu.name;
|
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
|
# https://doc.rust-lang.org/reference/conditional-compilation.html#target_os
|
||||||
os =
|
os =
|
||||||
/**/ if rust ? platform then rust.platform.os or "none"
|
if rust ? platform then
|
||||||
else if final.isDarwin then "macos"
|
rust.platform.os or "none"
|
||||||
else if final.isWasm && !final.isWasi then "unknown" # Needed for {wasm32,wasm64}-unknown-unknown.
|
else if final.isDarwin then
|
||||||
else final.parsed.kernel.name;
|
"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
|
# https://doc.rust-lang.org/reference/conditional-compilation.html#target_family
|
||||||
target-family =
|
target-family =
|
||||||
/**/ if args ? rust.platform.target-family then args.rust.platform.target-family
|
if args ? rust.platform.target-family then
|
||||||
else if args ? rustc.platform.target-family
|
args.rust.platform.target-family
|
||||||
then
|
else if args ? rustc.platform.target-family then
|
||||||
(
|
(
|
||||||
# Since https://github.com/rust-lang/rust/pull/84072
|
# Since https://github.com/rust-lang/rust/pull/84072
|
||||||
# `target-family` is a list instead of single value.
|
# `target-family` is a list instead of single value.
|
||||||
|
@ -354,82 +458,88 @@ let
|
||||||
in
|
in
|
||||||
if isList f then f else [ f ]
|
if isList f then f else [ f ]
|
||||||
)
|
)
|
||||||
else optional final.isUnix "unix"
|
else
|
||||||
++ optional final.isWindows "windows"
|
optional final.isUnix "unix" ++ optional final.isWindows "windows" ++ optional final.isWasm "wasm";
|
||||||
++ optional final.isWasm "wasm";
|
|
||||||
|
|
||||||
# https://doc.rust-lang.org/reference/conditional-compilation.html#target_vendor
|
# https://doc.rust-lang.org/reference/conditional-compilation.html#target_vendor
|
||||||
vendor = let
|
vendor =
|
||||||
|
let
|
||||||
inherit (final.parsed) vendor;
|
inherit (final.parsed) vendor;
|
||||||
in rust.platform.vendor or {
|
in
|
||||||
|
rust.platform.vendor or {
|
||||||
"w64" = "pc";
|
"w64" = "pc";
|
||||||
}.${vendor.name} or vendor.name;
|
}
|
||||||
|
.${vendor.name} or vendor.name;
|
||||||
};
|
};
|
||||||
|
|
||||||
# The name of the rust target, even if it is custom. Adjustments are
|
# The name of the rust target, even if it is custom. Adjustments are
|
||||||
# because rust has slightly different naming conventions than we do.
|
# because rust has slightly different naming conventions than we do.
|
||||||
rustcTarget = let
|
rustcTarget =
|
||||||
|
let
|
||||||
inherit (final.parsed) cpu kernel abi;
|
inherit (final.parsed) cpu kernel abi;
|
||||||
cpu_ = rust.platform.arch or {
|
cpu_ =
|
||||||
|
rust.platform.arch or {
|
||||||
"armv7a" = "armv7";
|
"armv7a" = "armv7";
|
||||||
"armv7l" = "armv7";
|
"armv7l" = "armv7";
|
||||||
"armv6l" = "arm";
|
"armv6l" = "arm";
|
||||||
"armv5tel" = "armv5te";
|
"armv5tel" = "armv5te";
|
||||||
"riscv32" = "riscv32gc";
|
"riscv32" = "riscv32gc";
|
||||||
"riscv64" = "riscv64gc";
|
"riscv64" = "riscv64gc";
|
||||||
}.${cpu.name} or cpu.name;
|
}
|
||||||
|
.${cpu.name} or cpu.name;
|
||||||
vendor_ = final.rust.platform.vendor;
|
vendor_ = final.rust.platform.vendor;
|
||||||
# TODO: deprecate args.rustc in favour of args.rust after 23.05 is EOL.
|
# TODO: deprecate args.rustc in favour of args.rust after 23.05 is EOL.
|
||||||
in
|
in
|
||||||
args.rust.rustcTarget or
|
args.rust.rustcTarget or args.rustc.config or (
|
||||||
args.rustc.config or (
|
|
||||||
# Rust uses `wasm32-wasip?` rather than `wasm32-unknown-wasi`.
|
# Rust uses `wasm32-wasip?` rather than `wasm32-unknown-wasi`.
|
||||||
# We cannot know which subversion does the user want, and
|
# We cannot know which subversion does the user want, and
|
||||||
# currently use WASI 0.1 as default for compatibility. Custom
|
# currently use WASI 0.1 as default for compatibility. Custom
|
||||||
# users can set `rust.rustcTarget` to override it.
|
# users can set `rust.rustcTarget` to override it.
|
||||||
if final.isWasi
|
if final.isWasi then
|
||||||
then "${cpu_}-wasip1"
|
"${cpu_}-wasip1"
|
||||||
else "${cpu_}-${vendor_}-${kernel.name}${optionalString (abi.name != "unknown") "-${abi.name}"}"
|
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
|
# The name of the rust target if it is standard, or the json file
|
||||||
# containing the custom target spec.
|
# containing the custom target spec.
|
||||||
rustcTargetSpec = rust.rustcTargetSpec or (
|
rustcTargetSpec =
|
||||||
/**/ if rust ? platform
|
rust.rustcTargetSpec or (
|
||||||
then builtins.toFile (final.rust.rustcTarget + ".json") (toJSON rust.platform)
|
if rust ? platform then
|
||||||
else final.rust.rustcTarget);
|
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
|
# The name of the rust target if it is standard, or the
|
||||||
# basename of the file containing the custom target spec,
|
# basename of the file containing the custom target spec,
|
||||||
# without the .json extension.
|
# without the .json extension.
|
||||||
#
|
#
|
||||||
# This is the name used by Cargo for target subdirectories.
|
# This is the name used by Cargo for target subdirectories.
|
||||||
cargoShortTarget =
|
cargoShortTarget = removeSuffix ".json" (baseNameOf "${final.rust.rustcTargetSpec}");
|
||||||
removeSuffix ".json" (baseNameOf "${final.rust.rustcTargetSpec}");
|
|
||||||
|
|
||||||
# When used as part of an environment variable name, triples are
|
# When used as part of an environment variable name, triples are
|
||||||
# uppercased and have all hyphens replaced by underscores:
|
# uppercased and have all hyphens replaced by underscores:
|
||||||
#
|
#
|
||||||
# https://github.com/rust-lang/cargo/pull/9169
|
# https://github.com/rust-lang/cargo/pull/9169
|
||||||
# https://github.com/rust-lang/cargo/issues/8285#issuecomment-634202431
|
# https://github.com/rust-lang/cargo/issues/8285#issuecomment-634202431
|
||||||
cargoEnvVarTarget =
|
cargoEnvVarTarget = replaceStrings [ "-" ] [ "_" ] (toUpper final.rust.cargoShortTarget);
|
||||||
replaceStrings ["-"] ["_"]
|
|
||||||
(toUpper final.rust.cargoShortTarget);
|
|
||||||
|
|
||||||
# True if the target is no_std
|
# True if the target is no_std
|
||||||
# https://github.com/rust-lang/rust/blob/2e44c17c12cec45b6a682b1e53a04ac5b5fcc9d2/src/bootstrap/config.rs#L415-L421
|
# https://github.com/rust-lang/rust/blob/2e44c17c12cec45b6a682b1e53a04ac5b5fcc9d2/src/bootstrap/config.rs#L415-L421
|
||||||
isNoStdTarget =
|
isNoStdTarget = any (t: hasInfix t final.rust.rustcTarget) [
|
||||||
any (t: hasInfix t final.rust.rustcTarget) ["-none" "nvptx" "switch" "-uefi"];
|
"-none"
|
||||||
|
"nvptx"
|
||||||
|
"switch"
|
||||||
|
"-uefi"
|
||||||
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
in assert final.useAndroidPrebuilt -> final.isAndroid;
|
in
|
||||||
assert foldl
|
assert final.useAndroidPrebuilt -> final.isAndroid;
|
||||||
(pass: { assertion, message }:
|
assert foldl (pass: { assertion, message }: if assertion final then pass else throw message) true (
|
||||||
if assertion final
|
final.parsed.abi.assertions or [ ]
|
||||||
then pass
|
);
|
||||||
else throw message)
|
|
||||||
true
|
|
||||||
(final.parsed.abi.assertions or []);
|
|
||||||
final;
|
final;
|
||||||
|
|
||||||
in
|
in
|
||||||
|
|
|
@ -7,16 +7,24 @@ let
|
||||||
|
|
||||||
all = [
|
all = [
|
||||||
# Cygwin
|
# Cygwin
|
||||||
"i686-cygwin" "x86_64-cygwin"
|
"i686-cygwin"
|
||||||
|
"x86_64-cygwin"
|
||||||
|
|
||||||
# Darwin
|
# Darwin
|
||||||
"x86_64-darwin" "i686-darwin" "aarch64-darwin" "armv7a-darwin"
|
"x86_64-darwin"
|
||||||
|
"i686-darwin"
|
||||||
|
"aarch64-darwin"
|
||||||
|
"armv7a-darwin"
|
||||||
|
|
||||||
# FreeBSD
|
# FreeBSD
|
||||||
"i686-freebsd" "x86_64-freebsd" "aarch64-freebsd"
|
"i686-freebsd"
|
||||||
|
"x86_64-freebsd"
|
||||||
|
"aarch64-freebsd"
|
||||||
|
|
||||||
# Genode
|
# Genode
|
||||||
"aarch64-genode" "i686-genode" "x86_64-genode"
|
"aarch64-genode"
|
||||||
|
"i686-genode"
|
||||||
|
"x86_64-genode"
|
||||||
|
|
||||||
# illumos
|
# illumos
|
||||||
"x86_64-solaris"
|
"x86_64-solaris"
|
||||||
|
@ -25,47 +33,94 @@ let
|
||||||
"javascript-ghcjs"
|
"javascript-ghcjs"
|
||||||
|
|
||||||
# Linux
|
# Linux
|
||||||
"aarch64-linux" "armv5tel-linux" "armv6l-linux" "armv7a-linux"
|
"aarch64-linux"
|
||||||
"armv7l-linux" "i686-linux" "loongarch64-linux" "m68k-linux" "microblaze-linux"
|
"armv5tel-linux"
|
||||||
"microblazeel-linux" "mips-linux" "mips64-linux" "mips64el-linux"
|
"armv6l-linux"
|
||||||
"mipsel-linux" "powerpc64-linux" "powerpc64le-linux" "riscv32-linux"
|
"armv7a-linux"
|
||||||
"riscv64-linux" "s390-linux" "s390x-linux" "x86_64-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
|
# MMIXware
|
||||||
"mmix-mmixware"
|
"mmix-mmixware"
|
||||||
|
|
||||||
# NetBSD
|
# NetBSD
|
||||||
"aarch64-netbsd" "armv6l-netbsd" "armv7a-netbsd" "armv7l-netbsd"
|
"aarch64-netbsd"
|
||||||
"i686-netbsd" "m68k-netbsd" "mipsel-netbsd" "powerpc-netbsd"
|
"armv6l-netbsd"
|
||||||
"riscv32-netbsd" "riscv64-netbsd" "x86_64-netbsd"
|
"armv7a-netbsd"
|
||||||
|
"armv7l-netbsd"
|
||||||
|
"i686-netbsd"
|
||||||
|
"m68k-netbsd"
|
||||||
|
"mipsel-netbsd"
|
||||||
|
"powerpc-netbsd"
|
||||||
|
"riscv32-netbsd"
|
||||||
|
"riscv64-netbsd"
|
||||||
|
"x86_64-netbsd"
|
||||||
|
|
||||||
# none
|
# none
|
||||||
"aarch64_be-none" "aarch64-none" "arm-none" "armv6l-none" "avr-none" "i686-none"
|
"aarch64_be-none"
|
||||||
"microblaze-none" "microblazeel-none" "mips-none" "mips64-none" "msp430-none" "or1k-none" "m68k-none"
|
"aarch64-none"
|
||||||
"powerpc-none" "powerpcle-none" "riscv32-none" "riscv64-none" "rx-none"
|
"arm-none"
|
||||||
"s390-none" "s390x-none" "vc4-none" "x86_64-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
|
# OpenBSD
|
||||||
"i686-openbsd" "x86_64-openbsd"
|
"i686-openbsd"
|
||||||
|
"x86_64-openbsd"
|
||||||
|
|
||||||
# Redox
|
# Redox
|
||||||
"x86_64-redox"
|
"x86_64-redox"
|
||||||
|
|
||||||
# WASI
|
# WASI
|
||||||
"wasm64-wasi" "wasm32-wasi"
|
"wasm64-wasi"
|
||||||
|
"wasm32-wasi"
|
||||||
|
|
||||||
# Windows
|
# Windows
|
||||||
"aarch64-windows" "x86_64-windows" "i686-windows"
|
"aarch64-windows"
|
||||||
|
"x86_64-windows"
|
||||||
|
"i686-windows"
|
||||||
];
|
];
|
||||||
|
|
||||||
allParsed = map parse.mkSystemFromString all;
|
allParsed = map parse.mkSystemFromString all;
|
||||||
|
|
||||||
filterDoubles = f: map parse.doubleFromSystem (lists.filter f allParsed);
|
filterDoubles = f: map parse.doubleFromSystem (lists.filter f allParsed);
|
||||||
|
|
||||||
in {
|
in
|
||||||
|
{
|
||||||
inherit all;
|
inherit all;
|
||||||
|
|
||||||
none = [];
|
none = [ ];
|
||||||
|
|
||||||
arm = filterDoubles predicates.isAarch32;
|
arm = filterDoubles predicates.isAarch32;
|
||||||
armv7 = filterDoubles predicates.isArmv7;
|
armv7 = filterDoubles predicates.isArmv7;
|
||||||
|
@ -97,13 +152,35 @@ in {
|
||||||
darwin = filterDoubles predicates.isDarwin;
|
darwin = filterDoubles predicates.isDarwin;
|
||||||
freebsd = filterDoubles predicates.isFreeBSD;
|
freebsd = filterDoubles predicates.isFreeBSD;
|
||||||
# Should be better, but MinGW is unclear.
|
# Should be better, but MinGW is unclear.
|
||||||
gnu = filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.gnu; })
|
gnu =
|
||||||
++ filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.gnueabi; })
|
filterDoubles (matchAttrs {
|
||||||
++ filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.gnueabihf; })
|
kernel = parse.kernels.linux;
|
||||||
++ filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.gnuabin32; })
|
abi = parse.abis.gnu;
|
||||||
++ filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.gnuabi64; })
|
})
|
||||||
++ filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.gnuabielfv1; })
|
++ filterDoubles (matchAttrs {
|
||||||
++ filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.gnuabielfv2; });
|
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;
|
illumos = filterDoubles predicates.isSunOS;
|
||||||
linux = filterDoubles predicates.isLinux;
|
linux = filterDoubles predicates.isLinux;
|
||||||
netbsd = filterDoubles predicates.isNetBSD;
|
netbsd = filterDoubles predicates.isNetBSD;
|
||||||
|
|
|
@ -26,7 +26,9 @@ rec {
|
||||||
};
|
};
|
||||||
ppc64-musl = {
|
ppc64-musl = {
|
||||||
config = "powerpc64-unknown-linux-musl";
|
config = "powerpc64-unknown-linux-musl";
|
||||||
gcc = { abi = "elfv2"; };
|
gcc = {
|
||||||
|
abi = "elfv2";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
sheevaplug = {
|
sheevaplug = {
|
||||||
|
@ -95,16 +97,28 @@ rec {
|
||||||
} // platforms.fuloong2f_n32;
|
} // platforms.fuloong2f_n32;
|
||||||
|
|
||||||
# can execute on 32bit chip
|
# can execute on 32bit chip
|
||||||
mips-linux-gnu = { config = "mips-unknown-linux-gnu"; } // platforms.gcc_mips32r2_o32;
|
mips-linux-gnu = {
|
||||||
mipsel-linux-gnu = { config = "mipsel-unknown-linux-gnu"; } // platforms.gcc_mips32r2_o32;
|
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
|
# 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;
|
mips64-linux-gnuabin32 = {
|
||||||
mips64el-linux-gnuabin32 = { config = "mips64el-unknown-linux-gnuabin32"; } // platforms.gcc_mips64r2_n32;
|
config = "mips64-unknown-linux-gnuabin32";
|
||||||
|
} // platforms.gcc_mips64r2_n32;
|
||||||
|
mips64el-linux-gnuabin32 = {
|
||||||
|
config = "mips64el-unknown-linux-gnuabin32";
|
||||||
|
} // platforms.gcc_mips64r2_n32;
|
||||||
|
|
||||||
# 64bit pointers
|
# 64bit pointers
|
||||||
mips64-linux-gnuabi64 = { config = "mips64-unknown-linux-gnuabi64"; } // platforms.gcc_mips64r2_64;
|
mips64-linux-gnuabi64 = {
|
||||||
mips64el-linux-gnuabi64 = { config = "mips64el-unknown-linux-gnuabi64"; } // platforms.gcc_mips64r2_64;
|
config = "mips64-unknown-linux-gnuabi64";
|
||||||
|
} // platforms.gcc_mips64r2_64;
|
||||||
|
mips64el-linux-gnuabi64 = {
|
||||||
|
config = "mips64el-unknown-linux-gnuabi64";
|
||||||
|
} // platforms.gcc_mips64r2_64;
|
||||||
|
|
||||||
muslpi = raspberryPi // {
|
muslpi = raspberryPi // {
|
||||||
config = "armv6l-unknown-linux-musleabihf";
|
config = "armv6l-unknown-linux-musleabihf";
|
||||||
|
@ -114,12 +128,20 @@ rec {
|
||||||
config = "aarch64-unknown-linux-musl";
|
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
|
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"; };
|
musl64 = {
|
||||||
musl32 = { config = "i686-unknown-linux-musl"; };
|
config = "x86_64-unknown-linux-musl";
|
||||||
|
};
|
||||||
|
musl32 = {
|
||||||
|
config = "i686-unknown-linux-musl";
|
||||||
|
};
|
||||||
|
|
||||||
riscv64 = riscv "64";
|
riscv64 = riscv "64";
|
||||||
riscv32 = riscv "32";
|
riscv32 = riscv "32";
|
||||||
|
@ -294,13 +316,13 @@ rec {
|
||||||
aarch64-darwin = {
|
aarch64-darwin = {
|
||||||
config = "aarch64-apple-darwin";
|
config = "aarch64-apple-darwin";
|
||||||
xcodePlatform = "MacOSX";
|
xcodePlatform = "MacOSX";
|
||||||
platform = {};
|
platform = { };
|
||||||
};
|
};
|
||||||
|
|
||||||
x86_64-darwin = {
|
x86_64-darwin = {
|
||||||
config = "x86_64-apple-darwin";
|
config = "x86_64-apple-darwin";
|
||||||
xcodePlatform = "MacOSX";
|
xcodePlatform = "MacOSX";
|
||||||
platform = {};
|
platform = { };
|
||||||
};
|
};
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|
|
@ -38,124 +38,429 @@ rec {
|
||||||
# `lib.attrsets.matchAttrs`, which requires a match on *all* attributes of
|
# `lib.attrsets.matchAttrs`, which requires a match on *all* attributes of
|
||||||
# the product.
|
# the product.
|
||||||
|
|
||||||
isi686 = { cpu = cpuTypes.i686; };
|
isi686 = {
|
||||||
isx86_32 = { cpu = { family = "x86"; bits = 32; }; };
|
cpu = cpuTypes.i686;
|
||||||
isx86_64 = { cpu = { family = "x86"; bits = 64; }; };
|
};
|
||||||
isPower = { cpu = { family = "power"; }; };
|
isx86_32 = {
|
||||||
isPower64 = { cpu = { family = "power"; bits = 64; }; };
|
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,
|
# 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
|
# so it sometimes causes issues in certain packages that makes the wrong
|
||||||
# assumption on the used ABI.
|
# assumption on the used ABI.
|
||||||
isAbiElfv2 = [
|
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"; }; };
|
isx86 = {
|
||||||
isAarch32 = { cpu = { family = "arm"; bits = 32; }; };
|
cpu = {
|
||||||
isArmv7 = map ({ arch, ... }: { cpu = { inherit arch; }; })
|
family = "x86";
|
||||||
(filter (cpu: hasPrefix "armv7" cpu.arch or "")
|
};
|
||||||
(attrValues cpuTypes));
|
};
|
||||||
isAarch64 = { cpu = { family = "arm"; bits = 64; }; };
|
isAarch32 = {
|
||||||
isAarch = { cpu = { family = "arm"; }; };
|
cpu = {
|
||||||
isMicroBlaze = { cpu = { family = "microblaze"; }; };
|
family = "arm";
|
||||||
isMips = { cpu = { family = "mips"; }; };
|
bits = 32;
|
||||||
isMips32 = { cpu = { family = "mips"; bits = 32; }; };
|
};
|
||||||
isMips64 = { cpu = { family = "mips"; bits = 64; }; };
|
};
|
||||||
isMips64n32 = { cpu = { family = "mips"; bits = 64; }; abi = { abi = "n32"; }; };
|
isArmv7 = map (
|
||||||
isMips64n64 = { cpu = { family = "mips"; bits = 64; }; abi = { abi = "64"; }; };
|
{ arch, ... }:
|
||||||
isMmix = { cpu = { family = "mmix"; }; };
|
{
|
||||||
isRiscV = { cpu = { family = "riscv"; }; };
|
cpu = { inherit arch; };
|
||||||
isRiscV32 = { cpu = { family = "riscv"; bits = 32; }; };
|
}
|
||||||
isRiscV64 = { cpu = { family = "riscv"; bits = 64; }; };
|
) (filter (cpu: hasPrefix "armv7" cpu.arch or "") (attrValues cpuTypes));
|
||||||
isRx = { cpu = { family = "rx"; }; };
|
isAarch64 = {
|
||||||
isSparc = { cpu = { family = "sparc"; }; };
|
cpu = {
|
||||||
isSparc64 = { cpu = { family = "sparc"; bits = 64; }; };
|
family = "arm";
|
||||||
isWasm = { cpu = { family = "wasm"; }; };
|
bits = 64;
|
||||||
isMsp430 = { cpu = { family = "msp430"; }; };
|
};
|
||||||
isVc4 = { cpu = { family = "vc4"; }; };
|
};
|
||||||
isAvr = { cpu = { family = "avr"; }; };
|
isAarch = {
|
||||||
isAlpha = { cpu = { family = "alpha"; }; };
|
cpu = {
|
||||||
isOr1k = { cpu = { family = "or1k"; }; };
|
family = "arm";
|
||||||
isM68k = { cpu = { family = "m68k"; }; };
|
};
|
||||||
isS390 = { cpu = { family = "s390"; }; };
|
};
|
||||||
isS390x = { cpu = { family = "s390"; bits = 64; }; };
|
isMicroBlaze = {
|
||||||
isLoongArch64 = { cpu = { family = "loongarch"; bits = 64; }; };
|
cpu = {
|
||||||
isJavaScript = { cpu = cpuTypes.javascript; };
|
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; }; };
|
is32bit = {
|
||||||
is64bit = { cpu = { bits = 64; }; };
|
cpu = {
|
||||||
isILP32 = [ { cpu = { family = "wasm"; bits = 32; }; } ] ++
|
bits = 32;
|
||||||
map (a: { abi = { abi = a; }; }) [ "n32" "ilp32" "x32" ];
|
};
|
||||||
isBigEndian = { cpu = { significantByte = significantBytes.bigEndian; }; };
|
};
|
||||||
isLittleEndian = { cpu = { significantByte = significantBytes.littleEndian; }; };
|
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; }; }; };
|
isBSD = {
|
||||||
isDarwin = { kernel = { families = { inherit (kernelFamilies) darwin; }; }; };
|
kernel = {
|
||||||
isUnix = [ isBSD isDarwin isLinux isSunOS isCygwin isRedox ];
|
families = { inherit (kernelFamilies) bsd; };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
isDarwin = {
|
||||||
|
kernel = {
|
||||||
|
families = { inherit (kernelFamilies) darwin; };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
isUnix = [
|
||||||
|
isBSD
|
||||||
|
isDarwin
|
||||||
|
isLinux
|
||||||
|
isSunOS
|
||||||
|
isCygwin
|
||||||
|
isRedox
|
||||||
|
];
|
||||||
|
|
||||||
isMacOS = { kernel = kernels.macos; };
|
isMacOS = {
|
||||||
isiOS = { kernel = kernels.ios; };
|
kernel = kernels.macos;
|
||||||
isLinux = { kernel = kernels.linux; };
|
};
|
||||||
isSunOS = { kernel = kernels.solaris; };
|
isiOS = {
|
||||||
isFreeBSD = { kernel = { name = "freebsd"; }; };
|
kernel = kernels.ios;
|
||||||
isNetBSD = { kernel = kernels.netbsd; };
|
};
|
||||||
isOpenBSD = { kernel = kernels.openbsd; };
|
isLinux = {
|
||||||
isWindows = { kernel = kernels.windows; };
|
kernel = kernels.linux;
|
||||||
isCygwin = { kernel = kernels.windows; abi = abis.cygnus; };
|
};
|
||||||
isMinGW = { kernel = kernels.windows; abi = abis.gnu; };
|
isSunOS = {
|
||||||
isWasi = { kernel = kernels.wasi; };
|
kernel = kernels.solaris;
|
||||||
isRedox = { kernel = kernels.redox; };
|
};
|
||||||
isGhcjs = { kernel = kernels.ghcjs; };
|
isFreeBSD = {
|
||||||
isGenode = { kernel = kernels.genode; };
|
kernel = {
|
||||||
isNone = { kernel = kernels.none; };
|
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; } ];
|
isAndroid = [
|
||||||
isGnu = with abis; map (a: { abi = a; }) [ gnuabi64 gnuabin32 gnu gnueabi gnueabihf gnuabielfv1 gnuabielfv2 ];
|
{ abi = abis.android; }
|
||||||
isMusl = with abis; map (a: { abi = a; }) [ musl musleabi musleabihf muslabin32 muslabi64 ];
|
{ abi = abis.androideabi; }
|
||||||
isUClibc = with abis; map (a: { abi = a; }) [ uclibc uclibceabi uclibceabihf ];
|
];
|
||||||
|
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
|
||||||
|
];
|
||||||
isLLVMLibc = [ { abi = abis.llvm; } ];
|
isLLVMLibc = [ { abi = abis.llvm; } ];
|
||||||
|
|
||||||
isEfi = [
|
isEfi = [
|
||||||
{ cpu = { family = "arm"; version = "6"; }; }
|
{
|
||||||
{ cpu = { family = "arm"; version = "7"; }; }
|
cpu = {
|
||||||
{ cpu = { family = "arm"; version = "8"; }; }
|
family = "arm";
|
||||||
{ cpu = { family = "riscv"; }; }
|
version = "6";
|
||||||
{ cpu = { family = "x86"; }; }
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
cpu = {
|
||||||
|
family = "arm";
|
||||||
|
version = "7";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
cpu = {
|
||||||
|
family = "arm";
|
||||||
|
version = "8";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
cpu = {
|
||||||
|
family = "riscv";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
cpu = {
|
||||||
|
family = "x86";
|
||||||
|
};
|
||||||
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
isElf = { kernel.execFormat = execFormats.elf; };
|
isElf = {
|
||||||
isMacho = { kernel.execFormat = execFormats.macho; };
|
kernel.execFormat = execFormats.elf;
|
||||||
|
};
|
||||||
|
isMacho = {
|
||||||
|
kernel.execFormat = execFormats.macho;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
# given two patterns, return a pattern which is their logical AND.
|
# given two patterns, return a pattern which is their logical AND.
|
||||||
# Since a pattern is a list-of-disjuncts, this needs to
|
# Since a pattern is a list-of-disjuncts, this needs to
|
||||||
patternLogicalAnd = pat1_: pat2_:
|
patternLogicalAnd =
|
||||||
|
pat1_: pat2_:
|
||||||
let
|
let
|
||||||
# patterns can be either a list or a (bare) singleton; turn
|
# patterns can be either a list or a (bare) singleton; turn
|
||||||
# them into singletons for uniform handling
|
# them into singletons for uniform handling
|
||||||
pat1 = toList pat1_;
|
pat1 = toList pat1_;
|
||||||
pat2 = toList pat2_;
|
pat2 = toList pat2_;
|
||||||
in
|
in
|
||||||
concatMap (attr1:
|
concatMap (
|
||||||
map (attr2:
|
attr1:
|
||||||
recursiveUpdateUntil
|
map (
|
||||||
(path: subattr1: subattr2:
|
attr2:
|
||||||
if (builtins.intersectAttrs subattr1 subattr2) == {} || subattr1 == subattr2
|
recursiveUpdateUntil (
|
||||||
then true
|
path: subattr1: subattr2:
|
||||||
else throw ''
|
if (builtins.intersectAttrs subattr1 subattr2) == { } || subattr1 == subattr2 then
|
||||||
|
true
|
||||||
|
else
|
||||||
|
throw ''
|
||||||
pattern conflict at path ${toString path}:
|
pattern conflict at path ${toString path}:
|
||||||
${toJSON subattr1}
|
${toJSON subattr1}
|
||||||
${toJSON subattr2}
|
${toJSON subattr2}
|
||||||
'')
|
''
|
||||||
attr1
|
) attr1 attr2
|
||||||
attr2
|
) pat2
|
||||||
)
|
) pat1;
|
||||||
pat2)
|
|
||||||
pat1;
|
|
||||||
|
|
||||||
matchAnyAttrs = patterns:
|
matchAnyAttrs =
|
||||||
if isList patterns then attrs: any (pattern: matchAttrs pattern attrs) patterns
|
patterns:
|
||||||
else matchAttrs patterns;
|
if isList patterns then
|
||||||
|
attrs: any (pattern: matchAttrs pattern attrs) patterns
|
||||||
|
else
|
||||||
|
matchAttrs patterns;
|
||||||
|
|
||||||
predicates = mapAttrs (_: matchAnyAttrs) patterns;
|
predicates = mapAttrs (_: matchAnyAttrs) patterns;
|
||||||
|
|
||||||
|
@ -164,7 +469,9 @@ rec {
|
||||||
# that `lib.meta.availableOn` can distinguish them from the patterns which
|
# that `lib.meta.availableOn` can distinguish them from the patterns which
|
||||||
# apply only to the `parsed` field.
|
# apply only to the `parsed` field.
|
||||||
|
|
||||||
platformPatterns = mapAttrs (_: p: { parsed = {}; } // p) {
|
platformPatterns = mapAttrs (_: p: { parsed = { }; } // p) {
|
||||||
isStatic = { isStatic = true; };
|
isStatic = {
|
||||||
|
isStatic = true;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,19 +55,23 @@ let
|
||||||
types
|
types
|
||||||
;
|
;
|
||||||
|
|
||||||
setTypes = type:
|
setTypes =
|
||||||
mapAttrs (name: value:
|
type:
|
||||||
|
mapAttrs (
|
||||||
|
name: value:
|
||||||
assert type.check value;
|
assert type.check value;
|
||||||
setType type.name ({ inherit name; } // value));
|
setType type.name ({ inherit name; } // value)
|
||||||
|
);
|
||||||
|
|
||||||
# gnu-config will ignore the portion of a triple matching the
|
# gnu-config will ignore the portion of a triple matching the
|
||||||
# regex `e?abi.*$` when determining the validity of a triple. In
|
# regex `e?abi.*$` when determining the validity of a triple. In
|
||||||
# other words, `i386-linuxabichickenlips` is a valid triple.
|
# other words, `i386-linuxabichickenlips` is a valid triple.
|
||||||
removeAbiSuffix = x:
|
removeAbiSuffix =
|
||||||
let found = match "(.*)e?abi.*" x;
|
x:
|
||||||
in if found == null
|
let
|
||||||
then x
|
found = match "(.*)e?abi.*" x;
|
||||||
else elemAt found 0;
|
in
|
||||||
|
if found == null then x else elemAt found 0;
|
||||||
|
|
||||||
in
|
in
|
||||||
|
|
||||||
|
@ -84,14 +88,20 @@ rec {
|
||||||
types.significantByte = enum (attrValues significantBytes);
|
types.significantByte = enum (attrValues significantBytes);
|
||||||
|
|
||||||
significantBytes = setTypes types.openSignificantByte {
|
significantBytes = setTypes types.openSignificantByte {
|
||||||
bigEndian = {};
|
bigEndian = { };
|
||||||
littleEndian = {};
|
littleEndian = { };
|
||||||
};
|
};
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
|
|
||||||
# Reasonable power of 2
|
# Reasonable power of 2
|
||||||
types.bitWidth = enum [ 8 16 32 64 128 ];
|
types.bitWidth = enum [
|
||||||
|
8
|
||||||
|
16
|
||||||
|
32
|
||||||
|
64
|
||||||
|
128
|
||||||
|
];
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
|
|
||||||
|
@ -99,87 +109,307 @@ rec {
|
||||||
name = "cpu-type";
|
name = "cpu-type";
|
||||||
description = "instruction set architecture name and information";
|
description = "instruction set architecture name and information";
|
||||||
merge = mergeOneOption;
|
merge = mergeOneOption;
|
||||||
check = x: types.bitWidth.check x.bits
|
check =
|
||||||
&& (if 8 < x.bits
|
x:
|
||||||
then types.significantByte.check x.significantByte
|
types.bitWidth.check x.bits
|
||||||
else !(x ? significantByte));
|
&& (if 8 < x.bits then types.significantByte.check x.significantByte else !(x ? significantByte));
|
||||||
};
|
};
|
||||||
|
|
||||||
types.cpuType = enum (attrValues cpuTypes);
|
types.cpuType = enum (attrValues cpuTypes);
|
||||||
|
|
||||||
cpuTypes = let inherit (significantBytes) bigEndian littleEndian; in setTypes types.openCpuType {
|
cpuTypes =
|
||||||
arm = { bits = 32; significantByte = littleEndian; family = "arm"; };
|
let
|
||||||
armv5tel = { bits = 32; significantByte = littleEndian; family = "arm"; version = "5"; arch = "armv5t"; };
|
inherit (significantBytes) bigEndian littleEndian;
|
||||||
armv6m = { bits = 32; significantByte = littleEndian; family = "arm"; version = "6"; arch = "armv6-m"; };
|
in
|
||||||
armv6l = { bits = 32; significantByte = littleEndian; family = "arm"; version = "6"; arch = "armv6"; };
|
setTypes types.openCpuType {
|
||||||
armv7a = { bits = 32; significantByte = littleEndian; family = "arm"; version = "7"; arch = "armv7-a"; };
|
arm = {
|
||||||
armv7r = { bits = 32; significantByte = littleEndian; family = "arm"; version = "7"; arch = "armv7-r"; };
|
bits = 32;
|
||||||
armv7m = { bits = 32; significantByte = littleEndian; family = "arm"; version = "7"; arch = "armv7-m"; };
|
significantByte = littleEndian;
|
||||||
armv7l = { bits = 32; significantByte = littleEndian; family = "arm"; version = "7"; arch = "armv7"; };
|
family = "arm";
|
||||||
armv8a = { bits = 32; significantByte = littleEndian; family = "arm"; version = "8"; arch = "armv8-a"; };
|
};
|
||||||
armv8r = { bits = 32; significantByte = littleEndian; family = "arm"; version = "8"; arch = "armv8-a"; };
|
armv5tel = {
|
||||||
armv8m = { bits = 32; significantByte = littleEndian; family = "arm"; version = "8"; arch = "armv8-m"; };
|
bits = 32;
|
||||||
aarch64 = { bits = 64; significantByte = littleEndian; family = "arm"; version = "8"; arch = "armv8-a"; };
|
significantByte = littleEndian;
|
||||||
aarch64_be = { bits = 64; significantByte = bigEndian; family = "arm"; version = "8"; arch = "armv8-a"; };
|
family = "arm";
|
||||||
|
version = "5";
|
||||||
|
arch = "armv5t";
|
||||||
|
};
|
||||||
|
armv6m = {
|
||||||
|
bits = 32;
|
||||||
|
significantByte = littleEndian;
|
||||||
|
family = "arm";
|
||||||
|
version = "6";
|
||||||
|
arch = "armv6-m";
|
||||||
|
};
|
||||||
|
armv6l = {
|
||||||
|
bits = 32;
|
||||||
|
significantByte = littleEndian;
|
||||||
|
family = "arm";
|
||||||
|
version = "6";
|
||||||
|
arch = "armv6";
|
||||||
|
};
|
||||||
|
armv7a = {
|
||||||
|
bits = 32;
|
||||||
|
significantByte = littleEndian;
|
||||||
|
family = "arm";
|
||||||
|
version = "7";
|
||||||
|
arch = "armv7-a";
|
||||||
|
};
|
||||||
|
armv7r = {
|
||||||
|
bits = 32;
|
||||||
|
significantByte = littleEndian;
|
||||||
|
family = "arm";
|
||||||
|
version = "7";
|
||||||
|
arch = "armv7-r";
|
||||||
|
};
|
||||||
|
armv7m = {
|
||||||
|
bits = 32;
|
||||||
|
significantByte = littleEndian;
|
||||||
|
family = "arm";
|
||||||
|
version = "7";
|
||||||
|
arch = "armv7-m";
|
||||||
|
};
|
||||||
|
armv7l = {
|
||||||
|
bits = 32;
|
||||||
|
significantByte = littleEndian;
|
||||||
|
family = "arm";
|
||||||
|
version = "7";
|
||||||
|
arch = "armv7";
|
||||||
|
};
|
||||||
|
armv8a = {
|
||||||
|
bits = 32;
|
||||||
|
significantByte = littleEndian;
|
||||||
|
family = "arm";
|
||||||
|
version = "8";
|
||||||
|
arch = "armv8-a";
|
||||||
|
};
|
||||||
|
armv8r = {
|
||||||
|
bits = 32;
|
||||||
|
significantByte = littleEndian;
|
||||||
|
family = "arm";
|
||||||
|
version = "8";
|
||||||
|
arch = "armv8-a";
|
||||||
|
};
|
||||||
|
armv8m = {
|
||||||
|
bits = 32;
|
||||||
|
significantByte = littleEndian;
|
||||||
|
family = "arm";
|
||||||
|
version = "8";
|
||||||
|
arch = "armv8-m";
|
||||||
|
};
|
||||||
|
aarch64 = {
|
||||||
|
bits = 64;
|
||||||
|
significantByte = littleEndian;
|
||||||
|
family = "arm";
|
||||||
|
version = "8";
|
||||||
|
arch = "armv8-a";
|
||||||
|
};
|
||||||
|
aarch64_be = {
|
||||||
|
bits = 64;
|
||||||
|
significantByte = bigEndian;
|
||||||
|
family = "arm";
|
||||||
|
version = "8";
|
||||||
|
arch = "armv8-a";
|
||||||
|
};
|
||||||
|
|
||||||
i386 = { bits = 32; significantByte = littleEndian; family = "x86"; arch = "i386"; };
|
i386 = {
|
||||||
i486 = { bits = 32; significantByte = littleEndian; family = "x86"; arch = "i486"; };
|
bits = 32;
|
||||||
i586 = { bits = 32; significantByte = littleEndian; family = "x86"; arch = "i586"; };
|
significantByte = littleEndian;
|
||||||
i686 = { bits = 32; significantByte = littleEndian; family = "x86"; arch = "i686"; };
|
family = "x86";
|
||||||
x86_64 = { bits = 64; significantByte = littleEndian; family = "x86"; arch = "x86-64"; };
|
arch = "i386";
|
||||||
|
};
|
||||||
|
i486 = {
|
||||||
|
bits = 32;
|
||||||
|
significantByte = littleEndian;
|
||||||
|
family = "x86";
|
||||||
|
arch = "i486";
|
||||||
|
};
|
||||||
|
i586 = {
|
||||||
|
bits = 32;
|
||||||
|
significantByte = littleEndian;
|
||||||
|
family = "x86";
|
||||||
|
arch = "i586";
|
||||||
|
};
|
||||||
|
i686 = {
|
||||||
|
bits = 32;
|
||||||
|
significantByte = littleEndian;
|
||||||
|
family = "x86";
|
||||||
|
arch = "i686";
|
||||||
|
};
|
||||||
|
x86_64 = {
|
||||||
|
bits = 64;
|
||||||
|
significantByte = littleEndian;
|
||||||
|
family = "x86";
|
||||||
|
arch = "x86-64";
|
||||||
|
};
|
||||||
|
|
||||||
microblaze = { bits = 32; significantByte = bigEndian; family = "microblaze"; };
|
microblaze = {
|
||||||
microblazeel = { bits = 32; significantByte = littleEndian; family = "microblaze"; };
|
bits = 32;
|
||||||
|
significantByte = bigEndian;
|
||||||
|
family = "microblaze";
|
||||||
|
};
|
||||||
|
microblazeel = {
|
||||||
|
bits = 32;
|
||||||
|
significantByte = littleEndian;
|
||||||
|
family = "microblaze";
|
||||||
|
};
|
||||||
|
|
||||||
mips = { bits = 32; significantByte = bigEndian; family = "mips"; };
|
mips = {
|
||||||
mipsel = { bits = 32; significantByte = littleEndian; family = "mips"; };
|
bits = 32;
|
||||||
mips64 = { bits = 64; significantByte = bigEndian; family = "mips"; };
|
significantByte = bigEndian;
|
||||||
mips64el = { bits = 64; significantByte = littleEndian; family = "mips"; };
|
family = "mips";
|
||||||
|
};
|
||||||
|
mipsel = {
|
||||||
|
bits = 32;
|
||||||
|
significantByte = littleEndian;
|
||||||
|
family = "mips";
|
||||||
|
};
|
||||||
|
mips64 = {
|
||||||
|
bits = 64;
|
||||||
|
significantByte = bigEndian;
|
||||||
|
family = "mips";
|
||||||
|
};
|
||||||
|
mips64el = {
|
||||||
|
bits = 64;
|
||||||
|
significantByte = littleEndian;
|
||||||
|
family = "mips";
|
||||||
|
};
|
||||||
|
|
||||||
mmix = { bits = 64; significantByte = bigEndian; family = "mmix"; };
|
mmix = {
|
||||||
|
bits = 64;
|
||||||
|
significantByte = bigEndian;
|
||||||
|
family = "mmix";
|
||||||
|
};
|
||||||
|
|
||||||
m68k = { bits = 32; significantByte = bigEndian; family = "m68k"; };
|
m68k = {
|
||||||
|
bits = 32;
|
||||||
|
significantByte = bigEndian;
|
||||||
|
family = "m68k";
|
||||||
|
};
|
||||||
|
|
||||||
powerpc = { bits = 32; significantByte = bigEndian; family = "power"; };
|
powerpc = {
|
||||||
powerpc64 = { bits = 64; significantByte = bigEndian; family = "power"; };
|
bits = 32;
|
||||||
powerpc64le = { bits = 64; significantByte = littleEndian; family = "power"; };
|
significantByte = bigEndian;
|
||||||
powerpcle = { bits = 32; significantByte = littleEndian; family = "power"; };
|
family = "power";
|
||||||
|
};
|
||||||
|
powerpc64 = {
|
||||||
|
bits = 64;
|
||||||
|
significantByte = bigEndian;
|
||||||
|
family = "power";
|
||||||
|
};
|
||||||
|
powerpc64le = {
|
||||||
|
bits = 64;
|
||||||
|
significantByte = littleEndian;
|
||||||
|
family = "power";
|
||||||
|
};
|
||||||
|
powerpcle = {
|
||||||
|
bits = 32;
|
||||||
|
significantByte = littleEndian;
|
||||||
|
family = "power";
|
||||||
|
};
|
||||||
|
|
||||||
riscv32 = { bits = 32; significantByte = littleEndian; family = "riscv"; };
|
riscv32 = {
|
||||||
riscv64 = { bits = 64; significantByte = littleEndian; family = "riscv"; };
|
bits = 32;
|
||||||
|
significantByte = littleEndian;
|
||||||
|
family = "riscv";
|
||||||
|
};
|
||||||
|
riscv64 = {
|
||||||
|
bits = 64;
|
||||||
|
significantByte = littleEndian;
|
||||||
|
family = "riscv";
|
||||||
|
};
|
||||||
|
|
||||||
s390 = { bits = 32; significantByte = bigEndian; family = "s390"; };
|
s390 = {
|
||||||
s390x = { bits = 64; significantByte = bigEndian; family = "s390"; };
|
bits = 32;
|
||||||
|
significantByte = bigEndian;
|
||||||
|
family = "s390";
|
||||||
|
};
|
||||||
|
s390x = {
|
||||||
|
bits = 64;
|
||||||
|
significantByte = bigEndian;
|
||||||
|
family = "s390";
|
||||||
|
};
|
||||||
|
|
||||||
sparc = { bits = 32; significantByte = bigEndian; family = "sparc"; };
|
sparc = {
|
||||||
sparc64 = { bits = 64; significantByte = bigEndian; family = "sparc"; };
|
bits = 32;
|
||||||
|
significantByte = bigEndian;
|
||||||
|
family = "sparc";
|
||||||
|
};
|
||||||
|
sparc64 = {
|
||||||
|
bits = 64;
|
||||||
|
significantByte = bigEndian;
|
||||||
|
family = "sparc";
|
||||||
|
};
|
||||||
|
|
||||||
wasm32 = { bits = 32; significantByte = littleEndian; family = "wasm"; };
|
wasm32 = {
|
||||||
wasm64 = { bits = 64; significantByte = littleEndian; family = "wasm"; };
|
bits = 32;
|
||||||
|
significantByte = littleEndian;
|
||||||
|
family = "wasm";
|
||||||
|
};
|
||||||
|
wasm64 = {
|
||||||
|
bits = 64;
|
||||||
|
significantByte = littleEndian;
|
||||||
|
family = "wasm";
|
||||||
|
};
|
||||||
|
|
||||||
alpha = { bits = 64; significantByte = littleEndian; family = "alpha"; };
|
alpha = {
|
||||||
|
bits = 64;
|
||||||
|
significantByte = littleEndian;
|
||||||
|
family = "alpha";
|
||||||
|
};
|
||||||
|
|
||||||
rx = { bits = 32; significantByte = littleEndian; family = "rx"; };
|
rx = {
|
||||||
msp430 = { bits = 16; significantByte = littleEndian; family = "msp430"; };
|
bits = 32;
|
||||||
avr = { bits = 8; family = "avr"; };
|
significantByte = littleEndian;
|
||||||
|
family = "rx";
|
||||||
|
};
|
||||||
|
msp430 = {
|
||||||
|
bits = 16;
|
||||||
|
significantByte = littleEndian;
|
||||||
|
family = "msp430";
|
||||||
|
};
|
||||||
|
avr = {
|
||||||
|
bits = 8;
|
||||||
|
family = "avr";
|
||||||
|
};
|
||||||
|
|
||||||
vc4 = { bits = 32; significantByte = littleEndian; family = "vc4"; };
|
vc4 = {
|
||||||
|
bits = 32;
|
||||||
|
significantByte = littleEndian;
|
||||||
|
family = "vc4";
|
||||||
|
};
|
||||||
|
|
||||||
or1k = { bits = 32; significantByte = bigEndian; family = "or1k"; };
|
or1k = {
|
||||||
|
bits = 32;
|
||||||
|
significantByte = bigEndian;
|
||||||
|
family = "or1k";
|
||||||
|
};
|
||||||
|
|
||||||
loongarch64 = { bits = 64; significantByte = littleEndian; family = "loongarch"; };
|
loongarch64 = {
|
||||||
|
bits = 64;
|
||||||
|
significantByte = littleEndian;
|
||||||
|
family = "loongarch";
|
||||||
|
};
|
||||||
|
|
||||||
javascript = { bits = 32; significantByte = littleEndian; family = "javascript"; };
|
javascript = {
|
||||||
|
bits = 32;
|
||||||
|
significantByte = littleEndian;
|
||||||
|
family = "javascript";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
# GNU build systems assume that older NetBSD architectures are using a.out.
|
# GNU build systems assume that older NetBSD architectures are using a.out.
|
||||||
gnuNetBSDDefaultExecFormat = cpu:
|
gnuNetBSDDefaultExecFormat =
|
||||||
if (cpu.family == "arm" && cpu.bits == 32) ||
|
cpu:
|
||||||
(cpu.family == "sparc" && cpu.bits == 32) ||
|
if
|
||||||
(cpu.family == "m68k" && cpu.bits == 32) ||
|
(cpu.family == "arm" && cpu.bits == 32)
|
||||||
(cpu.family == "x86" && cpu.bits == 32)
|
|| (cpu.family == "sparc" && cpu.bits == 32)
|
||||||
then execFormats.aout
|
|| (cpu.family == "m68k" && cpu.bits == 32)
|
||||||
else execFormats.elf;
|
|| (cpu.family == "x86" && cpu.bits == 32)
|
||||||
|
then
|
||||||
|
execFormats.aout
|
||||||
|
else
|
||||||
|
execFormats.elf;
|
||||||
|
|
||||||
# Determine when two CPUs are compatible with each other. That is,
|
# Determine when two CPUs are compatible with each other. That is,
|
||||||
# can code built for system B run on system A? For that to happen,
|
# can code built for system B run on system A? For that to happen,
|
||||||
|
@ -197,7 +427,10 @@ rec {
|
||||||
# Note: Since 22.11 the archs of a mode switching CPU are no longer considered
|
# Note: Since 22.11 the archs of a mode switching CPU are no longer considered
|
||||||
# pairwise compatible. Mode switching implies that binaries built for A
|
# pairwise compatible. Mode switching implies that binaries built for A
|
||||||
# and B respectively can't be executed at the same time.
|
# and B respectively can't be executed at the same time.
|
||||||
isCompatible = with cpuTypes; a: b: any id [
|
isCompatible =
|
||||||
|
with cpuTypes;
|
||||||
|
a: b:
|
||||||
|
any id [
|
||||||
# x86
|
# x86
|
||||||
(b == i386 && isCompatible a i486)
|
(b == i386 && isCompatible a i486)
|
||||||
(b == i486 && isCompatible a i586)
|
(b == i486 && isCompatible a i586)
|
||||||
|
@ -259,16 +492,16 @@ rec {
|
||||||
types.vendor = enum (attrValues vendors);
|
types.vendor = enum (attrValues vendors);
|
||||||
|
|
||||||
vendors = setTypes types.openVendor {
|
vendors = setTypes types.openVendor {
|
||||||
apple = {};
|
apple = { };
|
||||||
pc = {};
|
pc = { };
|
||||||
knuth = {};
|
knuth = { };
|
||||||
|
|
||||||
# Actually matters, unlocking some MinGW-w64-specific options in GCC. See
|
# Actually matters, unlocking some MinGW-w64-specific options in GCC. See
|
||||||
# bottom of https://sourceforge.net/p/mingw-w64/wiki2/Unicode%20apps/
|
# bottom of https://sourceforge.net/p/mingw-w64/wiki2/Unicode%20apps/
|
||||||
w64 = {};
|
w64 = { };
|
||||||
|
|
||||||
none = {};
|
none = { };
|
||||||
unknown = {};
|
unknown = { };
|
||||||
};
|
};
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
|
@ -282,13 +515,13 @@ rec {
|
||||||
types.execFormat = enum (attrValues execFormats);
|
types.execFormat = enum (attrValues execFormats);
|
||||||
|
|
||||||
execFormats = setTypes types.openExecFormat {
|
execFormats = setTypes types.openExecFormat {
|
||||||
aout = {}; # a.out
|
aout = { }; # a.out
|
||||||
elf = {};
|
elf = { };
|
||||||
macho = {};
|
macho = { };
|
||||||
pe = {};
|
pe = { };
|
||||||
wasm = {};
|
wasm = { };
|
||||||
|
|
||||||
unknown = {};
|
unknown = { };
|
||||||
};
|
};
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
|
@ -302,8 +535,8 @@ rec {
|
||||||
types.kernelFamily = enum (attrValues kernelFamilies);
|
types.kernelFamily = enum (attrValues kernelFamilies);
|
||||||
|
|
||||||
kernelFamilies = setTypes types.openKernelFamily {
|
kernelFamilies = setTypes types.openKernelFamily {
|
||||||
bsd = {};
|
bsd = { };
|
||||||
darwin = {};
|
darwin = { };
|
||||||
};
|
};
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
|
@ -312,33 +545,87 @@ rec {
|
||||||
name = "kernel";
|
name = "kernel";
|
||||||
description = "kernel name and information";
|
description = "kernel name and information";
|
||||||
merge = mergeOneOption;
|
merge = mergeOneOption;
|
||||||
check = x: types.execFormat.check x.execFormat
|
check =
|
||||||
&& all types.kernelFamily.check (attrValues x.families);
|
x: types.execFormat.check x.execFormat && all types.kernelFamily.check (attrValues x.families);
|
||||||
};
|
};
|
||||||
|
|
||||||
types.kernel = enum (attrValues kernels);
|
types.kernel = enum (attrValues kernels);
|
||||||
|
|
||||||
kernels = let
|
kernels =
|
||||||
inherit (execFormats) elf pe wasm unknown macho;
|
let
|
||||||
|
inherit (execFormats)
|
||||||
|
elf
|
||||||
|
pe
|
||||||
|
wasm
|
||||||
|
unknown
|
||||||
|
macho
|
||||||
|
;
|
||||||
inherit (kernelFamilies) bsd darwin;
|
inherit (kernelFamilies) bsd darwin;
|
||||||
in setTypes types.openKernel {
|
in
|
||||||
|
setTypes types.openKernel {
|
||||||
# TODO(@Ericson2314): Don't want to mass-rebuild yet to keeping 'darwin' as
|
# TODO(@Ericson2314): Don't want to mass-rebuild yet to keeping 'darwin' as
|
||||||
# the normalized name for macOS.
|
# the normalized name for macOS.
|
||||||
macos = { execFormat = macho; families = { inherit darwin; }; name = "darwin"; };
|
macos = {
|
||||||
ios = { execFormat = macho; families = { inherit darwin; }; };
|
execFormat = macho;
|
||||||
freebsd = { execFormat = elf; families = { inherit bsd; }; name = "freebsd"; };
|
families = { inherit darwin; };
|
||||||
linux = { execFormat = elf; families = { }; };
|
name = "darwin";
|
||||||
netbsd = { execFormat = elf; families = { inherit bsd; }; };
|
};
|
||||||
none = { execFormat = unknown; families = { }; };
|
ios = {
|
||||||
openbsd = { execFormat = elf; families = { inherit bsd; }; };
|
execFormat = macho;
|
||||||
solaris = { execFormat = elf; families = { }; };
|
families = { inherit darwin; };
|
||||||
wasi = { execFormat = wasm; families = { }; };
|
};
|
||||||
redox = { execFormat = elf; families = { }; };
|
freebsd = {
|
||||||
windows = { execFormat = pe; families = { }; };
|
execFormat = elf;
|
||||||
ghcjs = { execFormat = unknown; families = { }; };
|
families = { inherit bsd; };
|
||||||
genode = { execFormat = elf; families = { }; };
|
name = "freebsd";
|
||||||
mmixware = { execFormat = unknown; families = { }; };
|
};
|
||||||
} // { # aliases
|
linux = {
|
||||||
|
execFormat = elf;
|
||||||
|
families = { };
|
||||||
|
};
|
||||||
|
netbsd = {
|
||||||
|
execFormat = elf;
|
||||||
|
families = { inherit bsd; };
|
||||||
|
};
|
||||||
|
none = {
|
||||||
|
execFormat = unknown;
|
||||||
|
families = { };
|
||||||
|
};
|
||||||
|
openbsd = {
|
||||||
|
execFormat = elf;
|
||||||
|
families = { inherit bsd; };
|
||||||
|
};
|
||||||
|
solaris = {
|
||||||
|
execFormat = elf;
|
||||||
|
families = { };
|
||||||
|
};
|
||||||
|
wasi = {
|
||||||
|
execFormat = wasm;
|
||||||
|
families = { };
|
||||||
|
};
|
||||||
|
redox = {
|
||||||
|
execFormat = elf;
|
||||||
|
families = { };
|
||||||
|
};
|
||||||
|
windows = {
|
||||||
|
execFormat = pe;
|
||||||
|
families = { };
|
||||||
|
};
|
||||||
|
ghcjs = {
|
||||||
|
execFormat = unknown;
|
||||||
|
families = { };
|
||||||
|
};
|
||||||
|
genode = {
|
||||||
|
execFormat = elf;
|
||||||
|
families = { };
|
||||||
|
};
|
||||||
|
mmixware = {
|
||||||
|
execFormat = unknown;
|
||||||
|
families = { };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// {
|
||||||
|
# aliases
|
||||||
# 'darwin' is the kernel for all of them. We choose macOS by default.
|
# 'darwin' is the kernel for all of them. We choose macOS by default.
|
||||||
darwin = kernels.macos;
|
darwin = kernels.macos;
|
||||||
watchos = kernels.ios;
|
watchos = kernels.ios;
|
||||||
|
@ -357,22 +644,27 @@ rec {
|
||||||
types.abi = enum (attrValues abis);
|
types.abi = enum (attrValues abis);
|
||||||
|
|
||||||
abis = setTypes types.openAbi {
|
abis = setTypes types.openAbi {
|
||||||
cygnus = {};
|
cygnus = { };
|
||||||
msvc = {};
|
msvc = { };
|
||||||
|
|
||||||
# Note: eabi is specific to ARM and PowerPC.
|
# Note: eabi is specific to ARM and PowerPC.
|
||||||
# On PowerPC, this corresponds to PPCEABI.
|
# On PowerPC, this corresponds to PPCEABI.
|
||||||
# On ARM, this corresponds to ARMEABI.
|
# On ARM, this corresponds to ARMEABI.
|
||||||
eabi = { float = "soft"; };
|
eabi = {
|
||||||
eabihf = { float = "hard"; };
|
float = "soft";
|
||||||
|
};
|
||||||
|
eabihf = {
|
||||||
|
float = "hard";
|
||||||
|
};
|
||||||
|
|
||||||
# Other architectures should use ELF in embedded situations.
|
# Other architectures should use ELF in embedded situations.
|
||||||
elf = {};
|
elf = { };
|
||||||
|
|
||||||
androideabi = {};
|
androideabi = { };
|
||||||
android = {
|
android = {
|
||||||
assertions = [
|
assertions = [
|
||||||
{ assertion = platform: !platform.isAarch32;
|
{
|
||||||
|
assertion = platform: !platform.isAarch32;
|
||||||
message = ''
|
message = ''
|
||||||
The "android" ABI is not for 32-bit ARM. Use "androideabi" instead.
|
The "android" ABI is not for 32-bit ARM. Use "androideabi" instead.
|
||||||
'';
|
'';
|
||||||
|
@ -380,46 +672,72 @@ rec {
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
gnueabi = { float = "soft"; };
|
gnueabi = {
|
||||||
gnueabihf = { float = "hard"; };
|
float = "soft";
|
||||||
|
};
|
||||||
|
gnueabihf = {
|
||||||
|
float = "hard";
|
||||||
|
};
|
||||||
gnu = {
|
gnu = {
|
||||||
assertions = [
|
assertions = [
|
||||||
{ assertion = platform: !platform.isAarch32;
|
{
|
||||||
|
assertion = platform: !platform.isAarch32;
|
||||||
message = ''
|
message = ''
|
||||||
The "gnu" ABI is ambiguous on 32-bit ARM. Use "gnueabi" or "gnueabihf" instead.
|
The "gnu" ABI is ambiguous on 32-bit ARM. Use "gnueabi" or "gnueabihf" instead.
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
{ assertion = platform: !(platform.isPower64 && platform.isBigEndian);
|
{
|
||||||
|
assertion = platform: !(platform.isPower64 && platform.isBigEndian);
|
||||||
message = ''
|
message = ''
|
||||||
The "gnu" ABI is ambiguous on big-endian 64-bit PowerPC. Use "gnuabielfv2" or "gnuabielfv1" instead.
|
The "gnu" ABI is ambiguous on big-endian 64-bit PowerPC. Use "gnuabielfv2" or "gnuabielfv1" instead.
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
gnuabi64 = { abi = "64"; };
|
gnuabi64 = {
|
||||||
muslabi64 = { abi = "64"; };
|
abi = "64";
|
||||||
|
};
|
||||||
|
muslabi64 = {
|
||||||
|
abi = "64";
|
||||||
|
};
|
||||||
|
|
||||||
# NOTE: abi=n32 requires a 64-bit MIPS chip! That is not a typo.
|
# NOTE: abi=n32 requires a 64-bit MIPS chip! That is not a typo.
|
||||||
# It is basically the 64-bit abi with 32-bit pointers. Details:
|
# It is basically the 64-bit abi with 32-bit pointers. Details:
|
||||||
# https://www.linux-mips.org/pub/linux/mips/doc/ABI/MIPS-N32-ABI-Handbook.pdf
|
# https://www.linux-mips.org/pub/linux/mips/doc/ABI/MIPS-N32-ABI-Handbook.pdf
|
||||||
gnuabin32 = { abi = "n32"; };
|
gnuabin32 = {
|
||||||
muslabin32 = { abi = "n32"; };
|
abi = "n32";
|
||||||
|
};
|
||||||
|
muslabin32 = {
|
||||||
|
abi = "n32";
|
||||||
|
};
|
||||||
|
|
||||||
gnuabielfv2 = { abi = "elfv2"; };
|
gnuabielfv2 = {
|
||||||
gnuabielfv1 = { abi = "elfv1"; };
|
abi = "elfv2";
|
||||||
|
};
|
||||||
|
gnuabielfv1 = {
|
||||||
|
abi = "elfv1";
|
||||||
|
};
|
||||||
|
|
||||||
musleabi = { float = "soft"; };
|
musleabi = {
|
||||||
musleabihf = { float = "hard"; };
|
float = "soft";
|
||||||
musl = {};
|
};
|
||||||
|
musleabihf = {
|
||||||
|
float = "hard";
|
||||||
|
};
|
||||||
|
musl = { };
|
||||||
|
|
||||||
uclibceabi = { float = "soft"; };
|
uclibceabi = {
|
||||||
uclibceabihf = { float = "hard"; };
|
float = "soft";
|
||||||
uclibc = {};
|
};
|
||||||
|
uclibceabihf = {
|
||||||
|
float = "hard";
|
||||||
|
};
|
||||||
|
uclibc = { };
|
||||||
|
|
||||||
# LLVM libc
|
# LLVM libc
|
||||||
llvm = {};
|
llvm = { };
|
||||||
|
|
||||||
unknown = {};
|
unknown = { };
|
||||||
};
|
};
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
|
@ -428,7 +746,13 @@ rec {
|
||||||
name = "system";
|
name = "system";
|
||||||
description = "fully parsed representation of llvm- or nix-style platform tuple";
|
description = "fully parsed representation of llvm- or nix-style platform tuple";
|
||||||
merge = mergeOneOption;
|
merge = mergeOneOption;
|
||||||
check = { cpu, vendor, kernel, abi }:
|
check =
|
||||||
|
{
|
||||||
|
cpu,
|
||||||
|
vendor,
|
||||||
|
kernel,
|
||||||
|
abi,
|
||||||
|
}:
|
||||||
types.cpuType.check cpu
|
types.cpuType.check cpu
|
||||||
&& types.vendor.check vendor
|
&& types.vendor.check vendor
|
||||||
&& types.kernel.check kernel
|
&& types.kernel.check kernel
|
||||||
|
@ -437,65 +761,121 @@ rec {
|
||||||
|
|
||||||
isSystem = isType "system";
|
isSystem = isType "system";
|
||||||
|
|
||||||
mkSystem = components:
|
mkSystem =
|
||||||
|
components:
|
||||||
assert types.parsedPlatform.check components;
|
assert types.parsedPlatform.check components;
|
||||||
setType "system" components;
|
setType "system" components;
|
||||||
|
|
||||||
mkSkeletonFromList = l: {
|
mkSkeletonFromList =
|
||||||
"1" = if elemAt l 0 == "avr"
|
l:
|
||||||
then { cpu = elemAt l 0; kernel = "none"; abi = "unknown"; }
|
{
|
||||||
else throw "system string '${lib.concatStringsSep "-" l}' with 1 component is ambiguous";
|
"1" =
|
||||||
|
if elemAt l 0 == "avr" then
|
||||||
|
{
|
||||||
|
cpu = elemAt l 0;
|
||||||
|
kernel = "none";
|
||||||
|
abi = "unknown";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw "system string '${lib.concatStringsSep "-" l}' with 1 component is ambiguous";
|
||||||
"2" = # We only do 2-part hacks for things Nix already supports
|
"2" = # We only do 2-part hacks for things Nix already supports
|
||||||
if elemAt l 1 == "cygwin"
|
if elemAt l 1 == "cygwin" then
|
||||||
then { cpu = elemAt l 0; kernel = "windows"; abi = "cygnus"; }
|
{
|
||||||
|
cpu = elemAt l 0;
|
||||||
|
kernel = "windows";
|
||||||
|
abi = "cygnus";
|
||||||
|
}
|
||||||
# MSVC ought to be the default ABI so this case isn't needed. But then it
|
# MSVC ought to be the default ABI so this case isn't needed. But then it
|
||||||
# becomes difficult to handle the gnu* variants for Aarch32 correctly for
|
# becomes difficult to handle the gnu* variants for Aarch32 correctly for
|
||||||
# minGW. So it's easier to make gnu* the default for the MinGW, but
|
# minGW. So it's easier to make gnu* the default for the MinGW, but
|
||||||
# hack-in MSVC for the non-MinGW case right here.
|
# hack-in MSVC for the non-MinGW case right here.
|
||||||
else if elemAt l 1 == "windows"
|
else if elemAt l 1 == "windows" then
|
||||||
then { cpu = elemAt l 0; kernel = "windows"; abi = "msvc"; }
|
{
|
||||||
else if (elemAt l 1) == "elf"
|
cpu = elemAt l 0;
|
||||||
then { cpu = elemAt l 0; vendor = "unknown"; kernel = "none"; abi = elemAt l 1; }
|
kernel = "windows";
|
||||||
else { cpu = elemAt l 0; kernel = elemAt l 1; };
|
abi = "msvc";
|
||||||
|
}
|
||||||
|
else if (elemAt l 1) == "elf" then
|
||||||
|
{
|
||||||
|
cpu = elemAt l 0;
|
||||||
|
vendor = "unknown";
|
||||||
|
kernel = "none";
|
||||||
|
abi = elemAt l 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cpu = elemAt l 0;
|
||||||
|
kernel = elemAt l 1;
|
||||||
|
};
|
||||||
"3" =
|
"3" =
|
||||||
# cpu-kernel-environment
|
# cpu-kernel-environment
|
||||||
if elemAt l 1 == "linux" ||
|
if
|
||||||
elem (elemAt l 2) ["eabi" "eabihf" "elf" "gnu"]
|
elemAt l 1 == "linux"
|
||||||
then {
|
|| elem (elemAt l 2) [
|
||||||
|
"eabi"
|
||||||
|
"eabihf"
|
||||||
|
"elf"
|
||||||
|
"gnu"
|
||||||
|
]
|
||||||
|
then
|
||||||
|
{
|
||||||
cpu = elemAt l 0;
|
cpu = elemAt l 0;
|
||||||
kernel = elemAt l 1;
|
kernel = elemAt l 1;
|
||||||
abi = elemAt l 2;
|
abi = elemAt l 2;
|
||||||
vendor = "unknown";
|
vendor = "unknown";
|
||||||
}
|
}
|
||||||
# cpu-vendor-os
|
# cpu-vendor-os
|
||||||
else if elemAt l 1 == "apple" ||
|
else if
|
||||||
elem (elemAt l 2) [ "redox" "mmixware" "ghcjs" "mingw32" ] ||
|
elemAt l 1 == "apple"
|
||||||
hasPrefix "freebsd" (elemAt l 2) ||
|
|| elem (elemAt l 2) [
|
||||||
hasPrefix "netbsd" (elemAt l 2) ||
|
"redox"
|
||||||
hasPrefix "openbsd" (elemAt l 2) ||
|
"mmixware"
|
||||||
hasPrefix "genode" (elemAt l 2) ||
|
"ghcjs"
|
||||||
hasPrefix "wasm32" (elemAt l 0)
|
"mingw32"
|
||||||
then {
|
]
|
||||||
|
|| hasPrefix "freebsd" (elemAt l 2)
|
||||||
|
|| hasPrefix "netbsd" (elemAt l 2)
|
||||||
|
|| hasPrefix "openbsd" (elemAt l 2)
|
||||||
|
|| hasPrefix "genode" (elemAt l 2)
|
||||||
|
|| hasPrefix "wasm32" (elemAt l 0)
|
||||||
|
then
|
||||||
|
{
|
||||||
cpu = elemAt l 0;
|
cpu = elemAt l 0;
|
||||||
vendor = elemAt l 1;
|
vendor = elemAt l 1;
|
||||||
kernel = if elemAt l 2 == "mingw32"
|
kernel =
|
||||||
then "windows" # autotools breaks on -gnu for window
|
if elemAt l 2 == "mingw32" then
|
||||||
else elemAt l 2;
|
"windows" # autotools breaks on -gnu for window
|
||||||
|
else
|
||||||
|
elemAt l 2;
|
||||||
}
|
}
|
||||||
else throw "system string '${lib.concatStringsSep "-" l}' with 3 components is ambiguous";
|
else
|
||||||
"4" = { cpu = elemAt l 0; vendor = elemAt l 1; kernel = elemAt l 2; abi = elemAt l 3; };
|
throw "system string '${lib.concatStringsSep "-" l}' with 3 components is ambiguous";
|
||||||
}.${toString (length l)}
|
"4" = {
|
||||||
|
cpu = elemAt l 0;
|
||||||
|
vendor = elemAt l 1;
|
||||||
|
kernel = elemAt l 2;
|
||||||
|
abi = elemAt l 3;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
.${toString (length l)}
|
||||||
or (throw "system string '${lib.concatStringsSep "-" l}' has invalid number of hyphen-separated components");
|
or (throw "system string '${lib.concatStringsSep "-" l}' has invalid number of hyphen-separated components");
|
||||||
|
|
||||||
# This should revert the job done by config.guess from the gcc compiler.
|
# This should revert the job done by config.guess from the gcc compiler.
|
||||||
mkSystemFromSkeleton = { cpu
|
mkSystemFromSkeleton =
|
||||||
, # Optional, but fallback too complex for here.
|
{
|
||||||
|
cpu,
|
||||||
|
# Optional, but fallback too complex for here.
|
||||||
# Inferred below instead.
|
# Inferred below instead.
|
||||||
vendor ? assert false; null
|
vendor ?
|
||||||
, kernel
|
assert false;
|
||||||
, # Also inferred below
|
null,
|
||||||
abi ? assert false; null
|
kernel,
|
||||||
} @ args: let
|
# Also inferred below
|
||||||
|
abi ?
|
||||||
|
assert false;
|
||||||
|
null,
|
||||||
|
}@args:
|
||||||
|
let
|
||||||
getCpu = name: cpuTypes.${name} or (throw "Unknown CPU type: ${name}");
|
getCpu = name: cpuTypes.${name} or (throw "Unknown CPU type: ${name}");
|
||||||
getVendor = name: vendors.${name} or (throw "Unknown vendor: ${name}");
|
getVendor = name: vendors.${name} or (throw "Unknown vendor: ${name}");
|
||||||
getKernel = name: kernels.${name} or (throw "Unknown kernel: ${name}");
|
getKernel = name: kernels.${name} or (throw "Unknown kernel: ${name}");
|
||||||
|
@ -504,45 +884,73 @@ rec {
|
||||||
parsed = {
|
parsed = {
|
||||||
cpu = getCpu args.cpu;
|
cpu = getCpu args.cpu;
|
||||||
vendor =
|
vendor =
|
||||||
/**/ if args ? vendor then getVendor args.vendor
|
if args ? vendor then
|
||||||
else if isDarwin parsed then vendors.apple
|
getVendor args.vendor
|
||||||
else if isWindows parsed then vendors.pc
|
else if isDarwin parsed then
|
||||||
else vendors.unknown;
|
vendors.apple
|
||||||
kernel = if hasPrefix "darwin" args.kernel then getKernel "darwin"
|
else if isWindows parsed then
|
||||||
else if hasPrefix "netbsd" args.kernel then getKernel "netbsd"
|
vendors.pc
|
||||||
else getKernel (removeAbiSuffix args.kernel);
|
else
|
||||||
|
vendors.unknown;
|
||||||
|
kernel =
|
||||||
|
if hasPrefix "darwin" args.kernel then
|
||||||
|
getKernel "darwin"
|
||||||
|
else if hasPrefix "netbsd" args.kernel then
|
||||||
|
getKernel "netbsd"
|
||||||
|
else
|
||||||
|
getKernel (removeAbiSuffix args.kernel);
|
||||||
abi =
|
abi =
|
||||||
/**/ if args ? abi then getAbi args.abi
|
if args ? abi then
|
||||||
|
getAbi args.abi
|
||||||
else if isLinux parsed || isWindows parsed then
|
else if isLinux parsed || isWindows parsed then
|
||||||
if isAarch32 parsed then
|
if isAarch32 parsed then
|
||||||
if versionAtLeast (parsed.cpu.version or "0") "6"
|
if versionAtLeast (parsed.cpu.version or "0") "6" then abis.gnueabihf else abis.gnueabi
|
||||||
then abis.gnueabihf
|
|
||||||
else abis.gnueabi
|
|
||||||
# Default ppc64 BE to ELFv2
|
# Default ppc64 BE to ELFv2
|
||||||
else if isPower64 parsed && isBigEndian parsed then abis.gnuabielfv2
|
else if isPower64 parsed && isBigEndian parsed then
|
||||||
else abis.gnu
|
abis.gnuabielfv2
|
||||||
else abis.unknown;
|
else
|
||||||
|
abis.gnu
|
||||||
|
else
|
||||||
|
abis.unknown;
|
||||||
};
|
};
|
||||||
|
|
||||||
in mkSystem parsed;
|
in
|
||||||
|
mkSystem parsed;
|
||||||
|
|
||||||
mkSystemFromString = s: mkSystemFromSkeleton (mkSkeletonFromList (splitString "-" s));
|
mkSystemFromString = s: mkSystemFromSkeleton (mkSkeletonFromList (splitString "-" s));
|
||||||
|
|
||||||
kernelName = kernel:
|
kernelName = kernel: kernel.name + toString (kernel.version or "");
|
||||||
kernel.name + toString (kernel.version or "");
|
|
||||||
|
|
||||||
doubleFromSystem = { cpu, kernel, abi, ... }:
|
doubleFromSystem =
|
||||||
/**/ if abi == abis.cygnus then "${cpu.name}-cygwin"
|
{
|
||||||
else if kernel.families ? darwin then "${cpu.name}-darwin"
|
cpu,
|
||||||
else "${cpu.name}-${kernelName kernel}";
|
kernel,
|
||||||
|
abi,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
if abi == abis.cygnus then
|
||||||
|
"${cpu.name}-cygwin"
|
||||||
|
else if kernel.families ? darwin then
|
||||||
|
"${cpu.name}-darwin"
|
||||||
|
else
|
||||||
|
"${cpu.name}-${kernelName kernel}";
|
||||||
|
|
||||||
tripleFromSystem = { cpu, vendor, kernel, abi, ... } @ sys: assert isSystem sys; let
|
tripleFromSystem =
|
||||||
optExecFormat =
|
{
|
||||||
optionalString (kernel.name == "netbsd" &&
|
cpu,
|
||||||
gnuNetBSDDefaultExecFormat cpu != kernel.execFormat)
|
vendor,
|
||||||
kernel.execFormat.name;
|
kernel,
|
||||||
|
abi,
|
||||||
|
...
|
||||||
|
}@sys:
|
||||||
|
assert isSystem sys;
|
||||||
|
let
|
||||||
|
optExecFormat = optionalString (
|
||||||
|
kernel.name == "netbsd" && gnuNetBSDDefaultExecFormat cpu != kernel.execFormat
|
||||||
|
) kernel.execFormat.name;
|
||||||
optAbi = optionalString (abi != abis.unknown) "-${abi.name}";
|
optAbi = optionalString (abi != abis.unknown) "-${abi.name}";
|
||||||
in "${cpu.name}-${vendor.name}-${kernelName kernel}${optExecFormat}${optAbi}";
|
in
|
||||||
|
"${cpu.name}-${vendor.name}-${kernelName kernel}${optExecFormat}${optAbi}";
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
|
|
||||||
|
|
2155
lib/tests/misc.nix
2155
lib/tests/misc.nix
File diff suppressed because it is too large
Load diff
|
@ -1,7 +1,8 @@
|
||||||
{ lib, ... }:
|
{ lib, ... }:
|
||||||
let
|
let
|
||||||
inherit (builtins)
|
inherit (builtins)
|
||||||
storeDir;
|
storeDir
|
||||||
|
;
|
||||||
inherit (lib)
|
inherit (lib)
|
||||||
types
|
types
|
||||||
mkOption
|
mkOption
|
||||||
|
|
|
@ -1,24 +1,32 @@
|
||||||
{ # 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
|
# Don't test properties of pkgs.lib, but rather the lib in the parent directory
|
||||||
system ? builtins.currentSystem,
|
system ? builtins.currentSystem,
|
||||||
pkgs ? import ../.. { inherit system; } // { lib = throw "pkgs.lib accessed, but the lib tests should use nixpkgs' lib path directly!"; },
|
pkgs ? import ../.. { inherit system; } // {
|
||||||
|
lib = throw "pkgs.lib accessed, but the lib tests should use nixpkgs' lib path directly!";
|
||||||
|
},
|
||||||
# For testing someone may edit impure.nix to return cross pkgs, use `pkgsBuildBuild` directly so everything here works.
|
# For testing someone may edit impure.nix to return cross pkgs, use `pkgsBuildBuild` directly so everything here works.
|
||||||
pkgsBB ? pkgs.pkgsBuildBuild,
|
pkgsBB ? pkgs.pkgsBuildBuild,
|
||||||
nix ? pkgs-nixVersions.stable,
|
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 { pkgs = pkgsBB; },
|
pkgs-nixVersions ? import ./nix-for-tests.nix { pkgs = pkgsBB; },
|
||||||
}:
|
}:
|
||||||
|
|
||||||
let
|
let
|
||||||
lib = import ../.;
|
lib = import ../.;
|
||||||
testWithNix = nix:
|
testWithNix =
|
||||||
|
nix:
|
||||||
import ./test-with-nix.nix {
|
import ./test-with-nix.nix {
|
||||||
inherit lib nix;
|
inherit lib nix;
|
||||||
pkgs = pkgsBB;
|
pkgs = pkgsBB;
|
||||||
};
|
};
|
||||||
|
|
||||||
in
|
in
|
||||||
pkgsBB.symlinkJoin {
|
pkgsBB.symlinkJoin {
|
||||||
name = "nixpkgs-lib-tests";
|
name = "nixpkgs-lib-tests";
|
||||||
paths = map testWithNix nixVersions;
|
paths = map testWithNix nixVersions;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,53 +20,207 @@ let
|
||||||
NOTE: This property is not guaranteed when `sys` was elaborated by a different
|
NOTE: This property is not guaranteed when `sys` was elaborated by a different
|
||||||
version of Nixpkgs.
|
version of Nixpkgs.
|
||||||
*/
|
*/
|
||||||
toLosslessStringMaybe = sys:
|
toLosslessStringMaybe =
|
||||||
if lib.isString sys then sys
|
sys:
|
||||||
else if lib.systems.equals sys (lib.systems.elaborate sys.system) then sys.system
|
if lib.isString sys then
|
||||||
else null;
|
sys
|
||||||
|
else if lib.systems.equals sys (lib.systems.elaborate sys.system) then
|
||||||
|
sys.system
|
||||||
|
else
|
||||||
|
null;
|
||||||
|
|
||||||
in
|
in
|
||||||
lib.runTests (
|
lib.runTests (
|
||||||
# We assert that the new algorithmic way of generating these lists matches the
|
# We assert that the new algorithmic way of generating these lists matches the
|
||||||
# way they were hard-coded before.
|
# way they were hard-coded before.
|
||||||
#
|
#
|
||||||
# One might think "if we exhaustively test, what's the point of procedurally
|
# 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
|
# 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
|
# tests as new platforms become supported, and then just give the diff a quick
|
||||||
# sanity check before committing :).
|
# sanity check before committing :).
|
||||||
|
|
||||||
(with lib.systems.doubles; {
|
(with lib.systems.doubles; {
|
||||||
testall = mseteq all (linux ++ darwin ++ freebsd ++ openbsd ++ netbsd ++ illumos ++ wasi ++ windows ++ embedded ++ mmix ++ js ++ genode ++ redox);
|
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" ];
|
testarm = mseteq arm [
|
||||||
testarmv7 = mseteq armv7 [ "armv7a-darwin" "armv7a-linux" "armv7l-linux" "armv7a-netbsd" "armv7l-netbsd" ];
|
"armv5tel-linux"
|
||||||
testi686 = mseteq i686 [ "i686-linux" "i686-freebsd" "i686-genode" "i686-netbsd" "i686-openbsd" "i686-cygwin" "i686-windows" "i686-none" "i686-darwin" ];
|
"armv6l-linux"
|
||||||
testmips = mseteq mips [ "mips-none" "mips64-none" "mips-linux" "mips64-linux" "mips64el-linux" "mipsel-linux" "mipsel-netbsd" ];
|
"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" ];
|
testmmix = mseteq mmix [ "mmix-mmixware" ];
|
||||||
testpower = mseteq power [ "powerpc-netbsd" "powerpc-none" "powerpc64-linux" "powerpc64le-linux" "powerpcle-none" ];
|
testpower = mseteq power [
|
||||||
testriscv = mseteq riscv [ "riscv32-linux" "riscv64-linux" "riscv32-netbsd" "riscv64-netbsd" "riscv32-none" "riscv64-none" ];
|
"powerpc-netbsd"
|
||||||
testriscv32 = mseteq riscv32 [ "riscv32-linux" "riscv32-netbsd" "riscv32-none" ];
|
"powerpc-none"
|
||||||
testriscv64 = mseteq riscv64 [ "riscv64-linux" "riscv64-netbsd" "riscv64-none" ];
|
"powerpc64-linux"
|
||||||
tests390x = mseteq s390x [ "s390x-linux" "s390x-none" ];
|
"powerpc64le-linux"
|
||||||
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" ];
|
"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" ];
|
testcygwin = mseteq cygwin [
|
||||||
testdarwin = mseteq darwin [ "x86_64-darwin" "i686-darwin" "aarch64-darwin" "armv7a-darwin" ];
|
"i686-cygwin"
|
||||||
testfreebsd = mseteq freebsd [ "aarch64-freebsd" "i686-freebsd" "x86_64-freebsd" ];
|
"x86_64-cygwin"
|
||||||
testgenode = mseteq genode [ "aarch64-genode" "i686-genode" "x86_64-genode" ];
|
];
|
||||||
|
testdarwin = mseteq darwin [
|
||||||
|
"x86_64-darwin"
|
||||||
|
"i686-darwin"
|
||||||
|
"aarch64-darwin"
|
||||||
|
"armv7a-darwin"
|
||||||
|
];
|
||||||
|
testfreebsd = mseteq freebsd [
|
||||||
|
"aarch64-freebsd"
|
||||||
|
"i686-freebsd"
|
||||||
|
"x86_64-freebsd"
|
||||||
|
];
|
||||||
|
testgenode = mseteq genode [
|
||||||
|
"aarch64-genode"
|
||||||
|
"i686-genode"
|
||||||
|
"x86_64-genode"
|
||||||
|
];
|
||||||
testredox = mseteq redox [ "x86_64-redox" ];
|
testredox = mseteq redox [ "x86_64-redox" ];
|
||||||
testgnu = mseteq gnu (linux /* ++ kfreebsd ++ ... */);
|
testgnu = mseteq gnu (
|
||||||
|
linux # ++ kfreebsd ++ ...
|
||||||
|
);
|
||||||
testillumos = mseteq illumos [ "x86_64-solaris" ];
|
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" ];
|
testlinux = mseteq 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" ];
|
"aarch64-linux"
|
||||||
testopenbsd = mseteq openbsd [ "i686-openbsd" "x86_64-openbsd" ];
|
"armv5tel-linux"
|
||||||
testwindows = mseteq windows [ "i686-cygwin" "x86_64-cygwin" "aarch64-windows" "i686-windows" "x86_64-windows" ];
|
"armv6l-linux"
|
||||||
testunix = mseteq unix (linux ++ darwin ++ freebsd ++ openbsd ++ netbsd ++ illumos ++ cygwin ++ redox);
|
"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 = {
|
test_equals_example_x86_64-linux = {
|
||||||
expr = lib.systems.equals (lib.systems.elaborate "x86_64-linux") (lib.systems.elaborate "x86_64-linux");
|
expr = lib.systems.equals (lib.systems.elaborate "x86_64-linux") (
|
||||||
|
lib.systems.elaborate "x86_64-linux"
|
||||||
|
);
|
||||||
expected = true;
|
expected = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -79,30 +233,47 @@ lib.runTests (
|
||||||
expected = null;
|
expected = null;
|
||||||
};
|
};
|
||||||
test_elaborate_config_over_system = {
|
test_elaborate_config_over_system = {
|
||||||
expr = (lib.systems.elaborate { config = "i686-unknown-linux-gnu"; system = "x86_64-linux"; }).system;
|
expr =
|
||||||
|
(lib.systems.elaborate {
|
||||||
|
config = "i686-unknown-linux-gnu";
|
||||||
|
system = "x86_64-linux";
|
||||||
|
}).system;
|
||||||
expected = "i686-linux";
|
expected = "i686-linux";
|
||||||
};
|
};
|
||||||
test_elaborate_config_over_parsed = {
|
test_elaborate_config_over_parsed = {
|
||||||
expr = (lib.systems.elaborate { config = "i686-unknown-linux-gnu"; parsed = (lib.systems.elaborate "x86_64-linux").parsed; }).parsed.cpu.arch;
|
expr =
|
||||||
|
(lib.systems.elaborate {
|
||||||
|
config = "i686-unknown-linux-gnu";
|
||||||
|
parsed = (lib.systems.elaborate "x86_64-linux").parsed;
|
||||||
|
}).parsed.cpu.arch;
|
||||||
expected = "i686";
|
expected = "i686";
|
||||||
};
|
};
|
||||||
test_elaborate_system_over_parsed = {
|
test_elaborate_system_over_parsed = {
|
||||||
expr = (lib.systems.elaborate { system = "i686-linux"; parsed = (lib.systems.elaborate "x86_64-linux").parsed; }).parsed.cpu.arch;
|
expr =
|
||||||
|
(lib.systems.elaborate {
|
||||||
|
system = "i686-linux";
|
||||||
|
parsed = (lib.systems.elaborate "x86_64-linux").parsed;
|
||||||
|
}).parsed.cpu.arch;
|
||||||
expected = "i686";
|
expected = "i686";
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
# Generate test cases to assert that a change in any non-function attribute makes a platform unequal
|
# Generate test cases to assert that a change in any non-function attribute makes a platform unequal
|
||||||
// lib.concatMapAttrs (platformAttrName: origValue: {
|
//
|
||||||
|
lib.concatMapAttrs
|
||||||
|
(platformAttrName: origValue: {
|
||||||
|
|
||||||
${"test_equals_unequal_${platformAttrName}"} =
|
${"test_equals_unequal_${platformAttrName}"} =
|
||||||
let modified =
|
let
|
||||||
|
modified =
|
||||||
assert origValue != arbitraryValue;
|
assert origValue != arbitraryValue;
|
||||||
lib.systems.elaborate "x86_64-linux" // { ${platformAttrName} = arbitraryValue; };
|
lib.systems.elaborate "x86_64-linux" // { ${platformAttrName} = arbitraryValue; };
|
||||||
arbitraryValue = x: "<<modified>>";
|
arbitraryValue = x: "<<modified>>";
|
||||||
in {
|
in
|
||||||
|
{
|
||||||
expr = lib.systems.equals (lib.systems.elaborate "x86_64-linux") modified;
|
expr = lib.systems.equals (lib.systems.elaborate "x86_64-linux") modified;
|
||||||
expected = {
|
expected =
|
||||||
|
{
|
||||||
# Changes in these attrs are not detectable because they're function.
|
# 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.
|
# The functions should be derived from the data, so this is not a problem.
|
||||||
canExecute = null;
|
canExecute = null;
|
||||||
|
@ -110,9 +281,12 @@ lib.runTests (
|
||||||
emulatorAvailable = null;
|
emulatorAvailable = null;
|
||||||
staticEmulatorAvailable = null;
|
staticEmulatorAvailable = null;
|
||||||
isCompatible = null;
|
isCompatible = null;
|
||||||
}?${platformAttrName};
|
} ? ${platformAttrName};
|
||||||
};
|
};
|
||||||
|
|
||||||
}) (lib.systems.elaborate "x86_64-linux" /* arbitrary choice, just to get all the elaborated attrNames */)
|
})
|
||||||
|
(
|
||||||
|
lib.systems.elaborate "x86_64-linux" # arbitrary choice, just to get all the elaborated attrNames
|
||||||
|
)
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
215
lib/trivial.nix
215
lib/trivial.nix
|
@ -11,11 +11,13 @@ let
|
||||||
toBaseDigits
|
toBaseDigits
|
||||||
version
|
version
|
||||||
versionSuffix
|
versionSuffix
|
||||||
warn;
|
warn
|
||||||
|
;
|
||||||
inherit (lib)
|
inherit (lib)
|
||||||
isString
|
isString
|
||||||
;
|
;
|
||||||
in {
|
in
|
||||||
|
{
|
||||||
|
|
||||||
## Simple (higher order) functions
|
## Simple (higher order) functions
|
||||||
|
|
||||||
|
@ -23,7 +25,6 @@ in {
|
||||||
The identity function
|
The identity function
|
||||||
For when you need a function that does “nothing”.
|
For when you need a function that does “nothing”.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`x`
|
`x`
|
||||||
|
@ -44,7 +45,6 @@ in {
|
||||||
Ignores the second argument. If called with only one argument,
|
Ignores the second argument. If called with only one argument,
|
||||||
constructs a function that always returns a static value.
|
constructs a function that always returns a static value.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`x`
|
`x`
|
||||||
|
@ -72,9 +72,7 @@ in {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
const =
|
const = x: y: x;
|
||||||
x:
|
|
||||||
y: x;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Pipes a value through a list of functions, left to right.
|
Pipes a value through a list of functions, left to right.
|
||||||
|
@ -140,7 +138,6 @@ in {
|
||||||
/**
|
/**
|
||||||
Concatenate two lists
|
Concatenate two lists
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`x`
|
`x`
|
||||||
|
@ -173,7 +170,6 @@ in {
|
||||||
/**
|
/**
|
||||||
boolean “or”
|
boolean “or”
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`x`
|
`x`
|
||||||
|
@ -189,7 +185,6 @@ in {
|
||||||
/**
|
/**
|
||||||
boolean “and”
|
boolean “and”
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`x`
|
`x`
|
||||||
|
@ -205,7 +200,6 @@ in {
|
||||||
/**
|
/**
|
||||||
boolean “exclusive or”
|
boolean “exclusive or”
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`x`
|
`x`
|
||||||
|
@ -232,7 +226,6 @@ in {
|
||||||
boolean values. Calling `toString` on a bool instead returns "1"
|
boolean values. Calling `toString` on a bool instead returns "1"
|
||||||
and "" (sic!).
|
and "" (sic!).
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`b`
|
`b`
|
||||||
|
@ -252,7 +245,6 @@ in {
|
||||||
|
|
||||||
mergeAttrs :: attrs -> attrs -> attrs
|
mergeAttrs :: attrs -> attrs -> attrs
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`x`
|
`x`
|
||||||
|
@ -263,7 +255,6 @@ in {
|
||||||
|
|
||||||
: Right attribute set (higher precedence for equal keys)
|
: Right attribute set (higher precedence for equal keys)
|
||||||
|
|
||||||
|
|
||||||
# Examples
|
# Examples
|
||||||
:::{.example}
|
:::{.example}
|
||||||
## `lib.trivial.mergeAttrs` usage example
|
## `lib.trivial.mergeAttrs` usage example
|
||||||
|
@ -275,14 +266,11 @@ in {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
mergeAttrs =
|
mergeAttrs = x: y: x // y;
|
||||||
x:
|
|
||||||
y: x // y;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Flip the order of the arguments of a binary function.
|
Flip the order of the arguments of a binary function.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`f`
|
`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`.
|
Return `maybeValue` if not null, otherwise return `default`.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`default`
|
`default`
|
||||||
|
@ -330,7 +319,6 @@ in {
|
||||||
|
|
||||||
: 2\. Function argument
|
: 2\. Function argument
|
||||||
|
|
||||||
|
|
||||||
# Examples
|
# Examples
|
||||||
:::{.example}
|
:::{.example}
|
||||||
## `lib.trivial.defaultTo` usage example
|
## `lib.trivial.defaultTo` usage example
|
||||||
|
@ -346,14 +334,11 @@ in {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
defaultTo = default: maybeValue:
|
defaultTo = default: maybeValue: if maybeValue != null then maybeValue else default;
|
||||||
if maybeValue != null then maybeValue
|
|
||||||
else default;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Apply function if the supplied argument is non-null.
|
Apply function if the supplied argument is non-null.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`f`
|
`f`
|
||||||
|
@ -364,7 +349,6 @@ in {
|
||||||
|
|
||||||
: Argument to check for null before passing it to `f`
|
: Argument to check for null before passing it to `f`
|
||||||
|
|
||||||
|
|
||||||
# Examples
|
# Examples
|
||||||
:::{.example}
|
:::{.example}
|
||||||
## `lib.trivial.mapNullable` usage example
|
## `lib.trivial.mapNullable` usage example
|
||||||
|
@ -378,16 +362,25 @@ in {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
mapNullable =
|
mapNullable = f: a: if a == null then a else f a;
|
||||||
f:
|
|
||||||
a: if a == null then a else f a;
|
|
||||||
|
|
||||||
# Pull in some builtins not included elsewhere.
|
# Pull in some builtins not included elsewhere.
|
||||||
inherit (builtins)
|
inherit (builtins)
|
||||||
pathExists readFile isBool
|
pathExists
|
||||||
isInt isFloat add sub lessThan
|
readFile
|
||||||
seq deepSeq genericClosure
|
isBool
|
||||||
bitAnd bitOr bitXor;
|
isInt
|
||||||
|
isFloat
|
||||||
|
add
|
||||||
|
sub
|
||||||
|
lessThan
|
||||||
|
seq
|
||||||
|
deepSeq
|
||||||
|
genericClosure
|
||||||
|
bitAnd
|
||||||
|
bitOr
|
||||||
|
bitXor
|
||||||
|
;
|
||||||
|
|
||||||
## nixpkgs version strings
|
## nixpkgs version strings
|
||||||
|
|
||||||
|
@ -422,7 +415,6 @@ in {
|
||||||
Whether a feature is supported in all supported releases (at the time of
|
Whether a feature is supported in all supported releases (at the time of
|
||||||
release branch-off, if applicable). See `oldestSupportedRelease`.
|
release branch-off, if applicable). See `oldestSupportedRelease`.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`release`
|
`release`
|
||||||
|
@ -439,9 +431,7 @@ in {
|
||||||
Alias for `isInOldestRelease` introduced in 24.11.
|
Alias for `isInOldestRelease` introduced in 24.11.
|
||||||
Use `isInOldestRelease` in expressions outside of Nixpkgs for greater compatibility.
|
Use `isInOldestRelease` in expressions outside of Nixpkgs for greater compatibility.
|
||||||
*/
|
*/
|
||||||
oldestSupportedReleaseIsAtLeast =
|
oldestSupportedReleaseIsAtLeast = release: release <= lib.trivial.oldestSupportedRelease;
|
||||||
release:
|
|
||||||
release <= lib.trivial.oldestSupportedRelease;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Returns the current nixpkgs release code name.
|
Returns the current nixpkgs release code name.
|
||||||
|
@ -455,16 +445,15 @@ in {
|
||||||
Returns the current nixpkgs version suffix as string.
|
Returns the current nixpkgs version suffix as string.
|
||||||
*/
|
*/
|
||||||
versionSuffix =
|
versionSuffix =
|
||||||
let suffixFile = ../.version-suffix;
|
let
|
||||||
in if pathExists suffixFile
|
suffixFile = ../.version-suffix;
|
||||||
then lib.strings.fileContents suffixFile
|
in
|
||||||
else "pre-git";
|
if pathExists suffixFile then lib.strings.fileContents suffixFile else "pre-git";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Attempts to return the the current revision of nixpkgs and
|
Attempts to return the the current revision of nixpkgs and
|
||||||
returns the supplied default value otherwise.
|
returns the supplied default value otherwise.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`default`
|
`default`
|
||||||
|
@ -482,10 +471,13 @@ in {
|
||||||
let
|
let
|
||||||
revisionFile = "${toString ./..}/.git-revision";
|
revisionFile = "${toString ./..}/.git-revision";
|
||||||
gitRepo = "${toString ./..}/.git";
|
gitRepo = "${toString ./..}/.git";
|
||||||
in if lib.pathIsGitRepo gitRepo
|
in
|
||||||
then lib.commitIdFromGitRepo gitRepo
|
if lib.pathIsGitRepo gitRepo then
|
||||||
else if lib.pathExists revisionFile then lib.fileContents revisionFile
|
lib.commitIdFromGitRepo gitRepo
|
||||||
else default;
|
else if lib.pathExists revisionFile then
|
||||||
|
lib.fileContents revisionFile
|
||||||
|
else
|
||||||
|
default;
|
||||||
|
|
||||||
nixpkgsVersion = warn "lib.nixpkgsVersion is a deprecated alias of lib.version." version;
|
nixpkgsVersion = warn "lib.nixpkgsVersion is a deprecated alias of lib.version." version;
|
||||||
|
|
||||||
|
@ -512,14 +504,13 @@ in {
|
||||||
inPureEvalMode :: bool
|
inPureEvalMode :: bool
|
||||||
```
|
```
|
||||||
*/
|
*/
|
||||||
inPureEvalMode = ! builtins ? currentSystem;
|
inPureEvalMode = !builtins ? currentSystem;
|
||||||
|
|
||||||
## Integer operations
|
## Integer operations
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Return minimum of two numbers.
|
Return minimum of two numbers.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`x`
|
`x`
|
||||||
|
@ -535,7 +526,6 @@ in {
|
||||||
/**
|
/**
|
||||||
Return maximum of two numbers.
|
Return maximum of two numbers.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`x`
|
`x`
|
||||||
|
@ -551,7 +541,6 @@ in {
|
||||||
/**
|
/**
|
||||||
Integer modulus
|
Integer modulus
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`base`
|
`base`
|
||||||
|
@ -562,7 +551,6 @@ in {
|
||||||
|
|
||||||
: 2\. Function argument
|
: 2\. Function argument
|
||||||
|
|
||||||
|
|
||||||
# Examples
|
# Examples
|
||||||
:::{.example}
|
:::{.example}
|
||||||
## `lib.trivial.mod` usage example
|
## `lib.trivial.mod` usage example
|
||||||
|
@ -578,7 +566,6 @@ in {
|
||||||
*/
|
*/
|
||||||
mod = base: int: base - (int * (builtins.div base int));
|
mod = base: int: base - (int * (builtins.div base int));
|
||||||
|
|
||||||
|
|
||||||
## Comparisons
|
## Comparisons
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -588,7 +575,6 @@ in {
|
||||||
a == b, compare a b => 0
|
a == b, compare a b => 0
|
||||||
a > b, compare a b => 1
|
a > b, compare a b => 1
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`a`
|
`a`
|
||||||
|
@ -599,12 +585,14 @@ in {
|
||||||
|
|
||||||
: 2\. Function argument
|
: 2\. Function argument
|
||||||
*/
|
*/
|
||||||
compare = a: b:
|
compare =
|
||||||
if a < b
|
a: b:
|
||||||
then -1
|
if a < b then
|
||||||
else if a > b
|
-1
|
||||||
then 1
|
else if a > b then
|
||||||
else 0;
|
1
|
||||||
|
else
|
||||||
|
0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Split type into two subtypes by predicate `p`, take all elements
|
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`
|
second subtype, compare elements of a single subtype with `yes`
|
||||||
and `no` respectively.
|
and `no` respectively.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`p`
|
`p`
|
||||||
|
@ -661,10 +648,12 @@ in {
|
||||||
*/
|
*/
|
||||||
splitByAndCompare =
|
splitByAndCompare =
|
||||||
p: yes: no: a: b:
|
p: yes: no: a: b:
|
||||||
if p a
|
if p a then
|
||||||
then if p b then yes a b else -1
|
if p b then yes a b else -1
|
||||||
else if p b then 1 else no a b;
|
else if p b then
|
||||||
|
1
|
||||||
|
else
|
||||||
|
no a b;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Reads a JSON file.
|
Reads a JSON file.
|
||||||
|
@ -713,8 +702,7 @@ in {
|
||||||
importJSON :: path -> any
|
importJSON :: path -> any
|
||||||
```
|
```
|
||||||
*/
|
*/
|
||||||
importJSON = path:
|
importJSON = path: builtins.fromJSON (builtins.readFile path);
|
||||||
builtins.fromJSON (builtins.readFile path);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Reads a TOML file.
|
Reads a TOML file.
|
||||||
|
@ -761,11 +749,9 @@ in {
|
||||||
importTOML :: path -> any
|
importTOML :: path -> any
|
||||||
```
|
```
|
||||||
*/
|
*/
|
||||||
importTOML = path:
|
importTOML = path: builtins.fromTOML (builtins.readFile path);
|
||||||
builtins.fromTOML (builtins.readFile path);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
||||||
`warn` *`message`* *`value`*
|
`warn` *`message`* *`value`*
|
||||||
|
|
||||||
Print a warning before returning the second argument.
|
Print a warning before returning the second argument.
|
||||||
|
@ -792,19 +778,26 @@ in {
|
||||||
warn =
|
warn =
|
||||||
# Since Nix 2.23, https://github.com/NixOS/nix/pull/10592
|
# Since Nix 2.23, https://github.com/NixOS/nix/pull/10592
|
||||||
builtins.warn or (
|
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
|
in
|
||||||
# Do not eta reduce v, so that we have the same strictness as `builtins.warn`.
|
# Do not eta reduce v, so that we have the same strictness as `builtins.warn`.
|
||||||
msg: v:
|
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.
|
# `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;
|
assert isString msg;
|
||||||
if mustAbort
|
if mustAbort then
|
||||||
then builtins.trace "[1;31mevaluation warning:[0m ${msg}" (abort "NIX_ABORT_ON_WARN=true; warnings are treated as unrecoverable errors.")
|
builtins.trace "[1;31mevaluation warning:[0m ${msg}" (
|
||||||
else builtins.trace "[1;35mevaluation warning:[0m ${msg}" v
|
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`*
|
`warnIf` *`condition`* *`message`* *`value`*
|
||||||
|
|
||||||
Like `warn`, but only warn when the first argument is `true`.
|
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;
|
warnIf = cond: msg: if cond then warn msg else x: x;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
||||||
`warnIfNot` *`condition`* *`message`* *`value`*
|
`warnIfNot` *`condition`* *`message`* *`value`*
|
||||||
|
|
||||||
Like `warnIf`, but negated: warn if the first argument is `false`.
|
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
|
Calls can be juxtaposed using function application, as `(r: r) a = a`, so
|
||||||
`(r: r) (r: r) a = a`, and so forth.
|
`(r: r) (r: r) a = a`, and so forth.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`cond`
|
`cond`
|
||||||
|
@ -904,7 +895,6 @@ in {
|
||||||
/**
|
/**
|
||||||
Like throwIfNot, but negated (throw if the first argument is `true`).
|
Like throwIfNot, but negated (throw if the first argument is `true`).
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`cond`
|
`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.
|
Check if the elements in a list are valid values from a enum, returning the identity function, or throwing an error message otherwise.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`msg`
|
`msg`
|
||||||
|
@ -960,11 +949,12 @@ in {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
checkListOfEnum = msg: valid: given:
|
checkListOfEnum =
|
||||||
|
msg: valid: given:
|
||||||
let
|
let
|
||||||
unexpected = lib.subtractLists valid given;
|
unexpected = lib.subtractLists valid given;
|
||||||
in
|
in
|
||||||
lib.throwIfNot (unexpected == [])
|
lib.throwIfNot (unexpected == [ ])
|
||||||
"${msg}: ${builtins.concatStringsSep ", " (builtins.map builtins.toString unexpected)} unexpected; valid ones: ${builtins.concatStringsSep ", " (builtins.map builtins.toString valid)}";
|
"${msg}: ${builtins.concatStringsSep ", " (builtins.map builtins.toString unexpected)} unexpected; valid ones: ${builtins.concatStringsSep ", " (builtins.map builtins.toString valid)}";
|
||||||
|
|
||||||
info = msg: builtins.trace "INFO: ${msg}";
|
info = msg: builtins.trace "INFO: ${msg}";
|
||||||
|
@ -984,7 +974,6 @@ in {
|
||||||
function of the { a, b ? foo, ... }: format, but some facilities
|
function of the { a, b ? foo, ... }: format, but some facilities
|
||||||
like callPackage expect to be able to query expected arguments.
|
like callPackage expect to be able to query expected arguments.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`f`
|
`f`
|
||||||
|
@ -995,8 +984,8 @@ in {
|
||||||
|
|
||||||
: 2\. Function argument
|
: 2\. Function argument
|
||||||
*/
|
*/
|
||||||
setFunctionArgs = f: args:
|
setFunctionArgs = f: args: {
|
||||||
{ # TODO: Should we add call-time "type" checking like built in?
|
# TODO: Should we add call-time "type" checking like built in?
|
||||||
__functor = self: f;
|
__functor = self: f;
|
||||||
__functionArgs = args;
|
__functionArgs = args;
|
||||||
};
|
};
|
||||||
|
@ -1008,37 +997,35 @@ in {
|
||||||
has the same return type and semantics as builtins.functionArgs.
|
has the same return type and semantics as builtins.functionArgs.
|
||||||
setFunctionArgs : (a → b) → Map String Bool.
|
setFunctionArgs : (a → b) → Map String Bool.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`f`
|
`f`
|
||||||
|
|
||||||
: 1\. Function argument
|
: 1\. Function argument
|
||||||
*/
|
*/
|
||||||
functionArgs = f:
|
functionArgs =
|
||||||
if f ? __functor
|
f:
|
||||||
then f.__functionArgs or (functionArgs (f.__functor f))
|
if f ? __functor then
|
||||||
else builtins.functionArgs f;
|
f.__functionArgs or (functionArgs (f.__functor f))
|
||||||
|
else
|
||||||
|
builtins.functionArgs f;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Check whether something is a function or something
|
Check whether something is a function or something
|
||||||
annotated with function args.
|
annotated with function args.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`f`
|
`f`
|
||||||
|
|
||||||
: 1\. Function argument
|
: 1\. Function argument
|
||||||
*/
|
*/
|
||||||
isFunction = f: builtins.isFunction f ||
|
isFunction = f: builtins.isFunction f || (f ? __functor && isFunction (f.__functor 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`)
|
`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`).
|
but its function arguments mirroring `f` (`lib.functionArgs g' == lib.functionArgs f`).
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`f`
|
`f`
|
||||||
|
@ -1084,21 +1071,18 @@ in {
|
||||||
let
|
let
|
||||||
fArgs = functionArgs f;
|
fArgs = functionArgs f;
|
||||||
in
|
in
|
||||||
g:
|
g: setFunctionArgs g fArgs;
|
||||||
setFunctionArgs g fArgs;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Turns any non-callable values into constant functions.
|
Turns any non-callable values into constant functions.
|
||||||
Returns callable values as is.
|
Returns callable values as is.
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`v`
|
`v`
|
||||||
|
|
||||||
: Any value
|
: Any value
|
||||||
|
|
||||||
|
|
||||||
# Examples
|
# Examples
|
||||||
:::{.example}
|
:::{.example}
|
||||||
## `lib.trivial.toFunction` usage example
|
## `lib.trivial.toFunction` usage example
|
||||||
|
@ -1113,11 +1097,7 @@ in {
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
toFunction =
|
toFunction = v: if isFunction v then v else k: v;
|
||||||
v:
|
|
||||||
if isFunction v
|
|
||||||
then v
|
|
||||||
else k: v;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Convert a hexadecimal string to it's integer representation.
|
Convert a hexadecimal string to it's integer representation.
|
||||||
|
@ -1138,12 +1118,15 @@ in {
|
||||||
=> 9223372036854775807
|
=> 9223372036854775807
|
||||||
```
|
```
|
||||||
*/
|
*/
|
||||||
fromHexString = value:
|
fromHexString =
|
||||||
|
value:
|
||||||
let
|
let
|
||||||
noPrefix = lib.strings.removePrefix "0x" (lib.strings.toLower value);
|
noPrefix = lib.strings.removePrefix "0x" (lib.strings.toLower value);
|
||||||
in let
|
in
|
||||||
|
let
|
||||||
parsed = builtins.fromTOML "v=0x${noPrefix}";
|
parsed = builtins.fromTOML "v=0x${noPrefix}";
|
||||||
in parsed.v;
|
in
|
||||||
|
parsed.v;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Convert the given positive integer to a string of its hexadecimal
|
Convert the given positive integer to a string of its hexadecimal
|
||||||
|
@ -1155,7 +1138,8 @@ in {
|
||||||
|
|
||||||
toHexString 250 => "FA"
|
toHexString 250 => "FA"
|
||||||
*/
|
*/
|
||||||
toHexString = let
|
toHexString =
|
||||||
|
let
|
||||||
hexDigits = {
|
hexDigits = {
|
||||||
"10" = "A";
|
"10" = "A";
|
||||||
"11" = "B";
|
"11" = "B";
|
||||||
|
@ -1164,11 +1148,9 @@ in {
|
||||||
"14" = "E";
|
"14" = "E";
|
||||||
"15" = "F";
|
"15" = "F";
|
||||||
};
|
};
|
||||||
toHexDigit = d:
|
toHexDigit = d: if d < 10 then toString d else hexDigits.${toString d};
|
||||||
if d < 10
|
in
|
||||||
then toString d
|
i: lib.concatMapStrings toHexDigit (toBaseDigits 16 i);
|
||||||
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
|
`toBaseDigits base i` converts the positive integer i to a list of its
|
||||||
|
@ -1180,7 +1162,6 @@ in {
|
||||||
|
|
||||||
toBaseDigits 16 250 => [ 15 10 ]
|
toBaseDigits 16 250 => [ 15 10 ]
|
||||||
|
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
|
||||||
`base`
|
`base`
|
||||||
|
@ -1191,17 +1172,19 @@ in {
|
||||||
|
|
||||||
: 2\. Function argument
|
: 2\. Function argument
|
||||||
*/
|
*/
|
||||||
toBaseDigits = base: i:
|
toBaseDigits =
|
||||||
|
base: i:
|
||||||
let
|
let
|
||||||
go = i:
|
go =
|
||||||
if i < base
|
i:
|
||||||
then [i]
|
if i < base then
|
||||||
|
[ i ]
|
||||||
else
|
else
|
||||||
let
|
let
|
||||||
r = i - ((i / base) * base);
|
r = i - ((i / base) * base);
|
||||||
q = (i - r) / base;
|
q = (i - r) / base;
|
||||||
in
|
in
|
||||||
[r] ++ go q;
|
[ r ] ++ go q;
|
||||||
in
|
in
|
||||||
assert (isInt base);
|
assert (isInt base);
|
||||||
assert (isInt i);
|
assert (isInt i);
|
||||||
|
|
937
lib/types.nix
937
lib/types.nix
File diff suppressed because it is too large
Load diff
|
@ -22765,6 +22765,13 @@
|
||||||
github = "srounce";
|
github = "srounce";
|
||||||
githubId = 60792;
|
githubId = 60792;
|
||||||
};
|
};
|
||||||
|
srp = {
|
||||||
|
name = "Seraphim Pardee";
|
||||||
|
matrix = "@xsrp:matrix.org";
|
||||||
|
email = "me@srp.life";
|
||||||
|
github = "SeraphimRP";
|
||||||
|
githubId = 8297347;
|
||||||
|
};
|
||||||
srv6d = {
|
srv6d = {
|
||||||
name = "Marvin Vogt";
|
name = "Marvin Vogt";
|
||||||
github = "SRv6d";
|
github = "SRv6d";
|
||||||
|
@ -23478,6 +23485,11 @@
|
||||||
githubId = 506181;
|
githubId = 506181;
|
||||||
name = "Peter Marheine";
|
name = "Peter Marheine";
|
||||||
};
|
};
|
||||||
|
tarinaky = {
|
||||||
|
github = "Tarinaky";
|
||||||
|
githubId = 186027;
|
||||||
|
name = "Tarinaky";
|
||||||
|
};
|
||||||
tasmo = {
|
tasmo = {
|
||||||
email = "tasmo@tasmo.de";
|
email = "tasmo@tasmo.de";
|
||||||
github = "tasmo";
|
github = "tasmo";
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
{ pkgs
|
{
|
||||||
, options
|
pkgs,
|
||||||
, config
|
options,
|
||||||
, version
|
config,
|
||||||
, revision
|
version,
|
||||||
, extraSources ? []
|
revision,
|
||||||
, baseOptionsJSON ? null
|
extraSources ? [ ],
|
||||||
, warningsAreErrors ? true
|
baseOptionsJSON ? null,
|
||||||
, prefix ? ../../..
|
warningsAreErrors ? true,
|
||||||
|
prefix ? ../../..,
|
||||||
}:
|
}:
|
||||||
|
|
||||||
let
|
let
|
||||||
|
@ -38,8 +39,16 @@ let
|
||||||
stripAnyPrefixes = flip (foldr removePrefix) prefixesToStrip;
|
stripAnyPrefixes = flip (foldr removePrefix) prefixesToStrip;
|
||||||
|
|
||||||
optionsDoc = buildPackages.nixosOptionsDoc {
|
optionsDoc = buildPackages.nixosOptionsDoc {
|
||||||
inherit options revision baseOptionsJSON warningsAreErrors;
|
inherit
|
||||||
transformOptions = opt: opt // {
|
options
|
||||||
|
revision
|
||||||
|
baseOptionsJSON
|
||||||
|
warningsAreErrors
|
||||||
|
;
|
||||||
|
transformOptions =
|
||||||
|
opt:
|
||||||
|
opt
|
||||||
|
// {
|
||||||
# Clean up declaration sites to not refer to the NixOS source tree.
|
# Clean up declaration sites to not refer to the NixOS source tree.
|
||||||
declarations = map stripAnyPrefixes opt.declarations;
|
declarations = map stripAnyPrefixes opt.declarations;
|
||||||
};
|
};
|
||||||
|
@ -47,34 +56,44 @@ let
|
||||||
|
|
||||||
nixos-lib = import ../../lib { };
|
nixos-lib = import ../../lib { };
|
||||||
|
|
||||||
testOptionsDoc = let
|
testOptionsDoc =
|
||||||
|
let
|
||||||
eval = nixos-lib.evalTest {
|
eval = nixos-lib.evalTest {
|
||||||
# Avoid evaluating a NixOS config prototype.
|
# Avoid evaluating a NixOS config prototype.
|
||||||
config.node.type = types.deferredModule;
|
config.node.type = types.deferredModule;
|
||||||
options._module.args = mkOption { internal = true; };
|
options._module.args = mkOption { internal = true; };
|
||||||
};
|
};
|
||||||
in buildPackages.nixosOptionsDoc {
|
in
|
||||||
|
buildPackages.nixosOptionsDoc {
|
||||||
inherit (eval) options;
|
inherit (eval) options;
|
||||||
inherit revision;
|
inherit revision;
|
||||||
transformOptions = opt: opt // {
|
transformOptions =
|
||||||
|
opt:
|
||||||
|
opt
|
||||||
|
// {
|
||||||
# Clean up declaration sites to not refer to the NixOS source tree.
|
# Clean up declaration sites to not refer to the NixOS source tree.
|
||||||
declarations =
|
declarations = map (
|
||||||
map
|
decl:
|
||||||
(decl:
|
if hasPrefix (toString ../../..) (toString decl) then
|
||||||
if hasPrefix (toString ../../..) (toString decl)
|
let
|
||||||
then
|
subpath = removePrefix "/" (removePrefix (toString ../../..) (toString decl));
|
||||||
let subpath = removePrefix "/" (removePrefix (toString ../../..) (toString decl));
|
in
|
||||||
in { url = "https://github.com/NixOS/nixpkgs/blob/master/${subpath}"; name = subpath; }
|
{
|
||||||
else decl)
|
url = "https://github.com/NixOS/nixpkgs/blob/master/${subpath}";
|
||||||
opt.declarations;
|
name = subpath;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
decl
|
||||||
|
) opt.declarations;
|
||||||
};
|
};
|
||||||
documentType = "none";
|
documentType = "none";
|
||||||
variablelistId = "test-options-list";
|
variablelistId = "test-options-list";
|
||||||
optionIdPrefix = "test-opt-";
|
optionIdPrefix = "test-opt-";
|
||||||
};
|
};
|
||||||
|
|
||||||
testDriverMachineDocstrings = pkgs.callPackage
|
testDriverMachineDocstrings =
|
||||||
../../../nixos/lib/test-driver/nixos-test-driver-docstrings.nix {};
|
pkgs.callPackage ../../../nixos/lib/test-driver/nixos-test-driver-docstrings.nix
|
||||||
|
{ };
|
||||||
|
|
||||||
prepareManualFromMD = ''
|
prepareManualFromMD = ''
|
||||||
cp -r --no-preserve=all $inputs/* .
|
cp -r --no-preserve=all $inputs/* .
|
||||||
|
@ -99,15 +118,18 @@ let
|
||||||
-i ./development/writing-nixos-tests.section.md
|
-i ./development/writing-nixos-tests.section.md
|
||||||
'';
|
'';
|
||||||
|
|
||||||
in rec {
|
in
|
||||||
|
rec {
|
||||||
inherit (optionsDoc) optionsJSON optionsNix optionsDocBook;
|
inherit (optionsDoc) optionsJSON optionsNix optionsDocBook;
|
||||||
|
|
||||||
# Generate the NixOS manual.
|
# Generate the NixOS manual.
|
||||||
manualHTML = runCommand "nixos-manual-html"
|
manualHTML =
|
||||||
{ nativeBuildInputs = [ buildPackages.nixos-render-docs ];
|
runCommand "nixos-manual-html"
|
||||||
|
{
|
||||||
|
nativeBuildInputs = [ buildPackages.nixos-render-docs ];
|
||||||
inputs = sourceFilesBySuffices ./. [ ".md" ];
|
inputs = sourceFilesBySuffices ./. [ ".md" ];
|
||||||
meta.description = "The NixOS manual in HTML format";
|
meta.description = "The NixOS manual in HTML format";
|
||||||
allowedReferences = ["out"];
|
allowedReferences = [ "out" ];
|
||||||
}
|
}
|
||||||
''
|
''
|
||||||
# Generate the HTML manual.
|
# Generate the HTML manual.
|
||||||
|
@ -149,8 +171,14 @@ in rec {
|
||||||
# Index page of the NixOS manual.
|
# Index page of the NixOS manual.
|
||||||
manualHTMLIndex = "${manualHTML}/${common.outputPath}/${common.indexPath}";
|
manualHTMLIndex = "${manualHTML}/${common.outputPath}/${common.indexPath}";
|
||||||
|
|
||||||
manualEpub = runCommand "nixos-manual-epub"
|
manualEpub =
|
||||||
{ nativeBuildInputs = [ buildPackages.libxml2.bin buildPackages.libxslt.bin buildPackages.zip ];
|
runCommand "nixos-manual-epub"
|
||||||
|
{
|
||||||
|
nativeBuildInputs = [
|
||||||
|
buildPackages.libxml2.bin
|
||||||
|
buildPackages.libxslt.bin
|
||||||
|
buildPackages.zip
|
||||||
|
];
|
||||||
doc = ''
|
doc = ''
|
||||||
<book xmlns="http://docbook.org/ns/docbook"
|
<book xmlns="http://docbook.org/ns/docbook"
|
||||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
@ -197,14 +225,15 @@ in rec {
|
||||||
echo "doc-epub manual $manual" >> $out/nix-support/hydra-build-products
|
echo "doc-epub manual $manual" >> $out/nix-support/hydra-build-products
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
|
||||||
# Generate the `man configuration.nix` package
|
# Generate the `man configuration.nix` package
|
||||||
nixos-configuration-reference-manpage = runCommand "nixos-configuration-reference-manpage"
|
nixos-configuration-reference-manpage =
|
||||||
{ nativeBuildInputs = [
|
runCommand "nixos-configuration-reference-manpage"
|
||||||
|
{
|
||||||
|
nativeBuildInputs = [
|
||||||
buildPackages.installShellFiles
|
buildPackages.installShellFiles
|
||||||
buildPackages.nixos-render-docs
|
buildPackages.nixos-render-docs
|
||||||
];
|
];
|
||||||
allowedReferences = ["out"];
|
allowedReferences = [ "out" ];
|
||||||
}
|
}
|
||||||
''
|
''
|
||||||
# Generate manpages.
|
# Generate manpages.
|
||||||
|
|
|
@ -41,6 +41,15 @@
|
||||||
"module-services-crab-hole-upstream-options": [
|
"module-services-crab-hole-upstream-options": [
|
||||||
"index.html#module-services-crab-hole-upstream-options"
|
"index.html#module-services-crab-hole-upstream-options"
|
||||||
],
|
],
|
||||||
|
"module-services-strfry": [
|
||||||
|
"index.html#module-services-strfry"
|
||||||
|
],
|
||||||
|
"module-services-strfry-basic-usage": [
|
||||||
|
"index.html#module-services-strfry-basic-usage"
|
||||||
|
],
|
||||||
|
"module-services-strfry-reverse-proxy": [
|
||||||
|
"index.html#module-services-strfry-reverse-proxy"
|
||||||
|
],
|
||||||
"preface": [
|
"preface": [
|
||||||
"index.html#preface"
|
"index.html#preface"
|
||||||
],
|
],
|
||||||
|
|
|
@ -140,6 +140,8 @@
|
||||||
|
|
||||||
- [nostr-rs-relay](https://git.sr.ht/~gheartsfield/nostr-rs-relay/), This is a nostr relay, written in Rust. Available as [services.nostr-rs-relay](options.html#opt-services.nostr-rs-relay.enable).
|
- [nostr-rs-relay](https://git.sr.ht/~gheartsfield/nostr-rs-relay/), This is a nostr relay, written in Rust. Available as [services.nostr-rs-relay](options.html#opt-services.nostr-rs-relay.enable).
|
||||||
|
|
||||||
|
- [strfry](https://github.com/hoytech/strfry), a relay for the nostr protocol. Available as [services.strfry](options.html#opt-services.strfry.enable).
|
||||||
|
|
||||||
- [Prometheus Node Cert Exporter](https://github.com/amimof/node-cert-exporter), a prometheus exporter to check for SSL cert expiry. Available under [services.prometheus.exporters.node-cert](#opt-services.prometheus.exporters.node-cert.enable).
|
- [Prometheus Node Cert Exporter](https://github.com/amimof/node-cert-exporter), a prometheus exporter to check for SSL cert expiry. Available under [services.prometheus.exporters.node-cert](#opt-services.prometheus.exporters.node-cert.enable).
|
||||||
|
|
||||||
- [Actual Budget](https://actualbudget.org/), a local-first personal finance app. Available as [services.actual](#opt-services.actual.enable).
|
- [Actual Budget](https://actualbudget.org/), a local-first personal finance app. Available as [services.actual](#opt-services.actual.enable).
|
||||||
|
@ -192,6 +194,8 @@
|
||||||
|
|
||||||
- [Orthanc](https://orthanc.uclouvain.be/) a lightweight, RESTful DICOM server for healthcare and medical research. Available as [services.orthanc](#opt-services.orthanc.enable).
|
- [Orthanc](https://orthanc.uclouvain.be/) a lightweight, RESTful DICOM server for healthcare and medical research. Available as [services.orthanc](#opt-services.orthanc.enable).
|
||||||
|
|
||||||
|
- [Docling Serve](https://github.com/docling-project/docling-serve) running [Docling](https://github.com/docling-project/docling) as an API service. Available as [services.docling-serve](#opt-services.docling-serve.enable).
|
||||||
|
|
||||||
- [Pareto Security](https://paretosecurity.com/) is an alternative to corporate compliance solutions for companies that care about security but know it doesn't have to be invasive. Available as [services.paretosecurity](#opt-services.paretosecurity.enable)
|
- [Pareto Security](https://paretosecurity.com/) is an alternative to corporate compliance solutions for companies that care about security but know it doesn't have to be invasive. Available as [services.paretosecurity](#opt-services.paretosecurity.enable)
|
||||||
|
|
||||||
- [ipfs-cluster](https://ipfscluster.io/), Pinset orchestration for IPFS. Available as [services.ipfs-cluster](#opt-services.ipfs-cluster.enable)
|
- [ipfs-cluster](https://ipfscluster.io/), Pinset orchestration for IPFS. Available as [services.ipfs-cluster](#opt-services.ipfs-cluster.enable)
|
||||||
|
|
|
@ -8,31 +8,33 @@
|
||||||
# as subcomponents (e.g. the container feature, or nixops if network
|
# as subcomponents (e.g. the container feature, or nixops if network
|
||||||
# expressions are ever made modular at the top level) can just use
|
# expressions are ever made modular at the top level) can just use
|
||||||
# types.submodule instead of using eval-config.nix
|
# types.submodule instead of using eval-config.nix
|
||||||
evalConfigArgs@
|
evalConfigArgs@{
|
||||||
{ # !!! system can be set modularly, would be nice to remove,
|
# !!! system can be set modularly, would be nice to remove,
|
||||||
# however, removing or changing this default is too much
|
# however, removing or changing this default is too much
|
||||||
# of a breaking change. To set it modularly, pass `null`.
|
# of a breaking change. To set it modularly, pass `null`.
|
||||||
system ? builtins.currentSystem
|
system ? builtins.currentSystem,
|
||||||
, # !!! is this argument needed any more? The pkgs argument can
|
# !!! is this argument needed any more? The pkgs argument can
|
||||||
# be set modularly anyway.
|
# be set modularly anyway.
|
||||||
pkgs ? null
|
pkgs ? null,
|
||||||
, # !!! what do we gain by making this configurable?
|
# !!! what do we gain by making this configurable?
|
||||||
# we can add modules that are included in specialisations, regardless
|
# we can add modules that are included in specialisations, regardless
|
||||||
# of inheritParentConfig.
|
# of inheritParentConfig.
|
||||||
baseModules ? import ../modules/module-list.nix
|
baseModules ? import ../modules/module-list.nix,
|
||||||
, # !!! See comment about args in lib/modules.nix
|
# !!! See comment about args in lib/modules.nix
|
||||||
extraArgs ? {}
|
extraArgs ? { },
|
||||||
, # !!! See comment about args in lib/modules.nix
|
# !!! See comment about args in lib/modules.nix
|
||||||
specialArgs ? {}
|
specialArgs ? { },
|
||||||
, modules
|
modules,
|
||||||
, modulesLocation ? (builtins.unsafeGetAttrPos "modules" evalConfigArgs).file or null
|
modulesLocation ? (builtins.unsafeGetAttrPos "modules" evalConfigArgs).file or null,
|
||||||
, # !!! See comment about check in lib/modules.nix
|
# !!! See comment about check in lib/modules.nix
|
||||||
check ? true
|
check ? true,
|
||||||
, prefix ? []
|
prefix ? [ ],
|
||||||
, lib ? import ../../lib
|
lib ? import ../../lib,
|
||||||
, extraModules ?
|
extraModules ?
|
||||||
let e = builtins.getEnv "NIXOS_EXTRA_MODULE_PATH";
|
let
|
||||||
in lib.optional (e != "") (
|
e = builtins.getEnv "NIXOS_EXTRA_MODULE_PATH";
|
||||||
|
in
|
||||||
|
lib.optional (e != "") (
|
||||||
lib.warn
|
lib.warn
|
||||||
''
|
''
|
||||||
The NIXOS_EXTRA_MODULE_PATH environment variable is deprecated and will be
|
The NIXOS_EXTRA_MODULE_PATH environment variable is deprecated and will be
|
||||||
|
@ -51,14 +53,16 @@ evalConfigArgs@
|
||||||
''
|
''
|
||||||
# NOTE: this import call is unnecessary and it even removes the file name
|
# NOTE: this import call is unnecessary and it even removes the file name
|
||||||
# from error messages.
|
# from error messages.
|
||||||
import e
|
import
|
||||||
)
|
e
|
||||||
|
),
|
||||||
}:
|
}:
|
||||||
|
|
||||||
let
|
let
|
||||||
inherit (lib) optional;
|
inherit (lib) optional;
|
||||||
|
|
||||||
evalModulesMinimal = (import ./default.nix {
|
evalModulesMinimal =
|
||||||
|
(import ./default.nix {
|
||||||
inherit lib;
|
inherit lib;
|
||||||
# Implicit use of feature is noted in implementation.
|
# Implicit use of feature is noted in implementation.
|
||||||
featureFlags.minimalModules = { };
|
featureFlags.minimalModules = { };
|
||||||
|
@ -75,18 +79,23 @@ let
|
||||||
# they way through, but has the last priority behind everything else.
|
# they way through, but has the last priority behind everything else.
|
||||||
nixpkgs.system = lib.mkDefault system;
|
nixpkgs.system = lib.mkDefault system;
|
||||||
})
|
})
|
||||||
++
|
++ (optional (pkgs != null) {
|
||||||
(optional (pkgs != null) {
|
|
||||||
# This should be default priority, so it conflicts with any user-defined pkgs.
|
# This should be default priority, so it conflicts with any user-defined pkgs.
|
||||||
nixpkgs.pkgs = pkgs;
|
nixpkgs.pkgs = pkgs;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
withWarnings = x:
|
withWarnings =
|
||||||
lib.warnIf (evalConfigArgs?extraArgs) "The extraArgs argument to eval-config.nix is deprecated. Please set config._module.args instead."
|
x:
|
||||||
lib.warnIf (evalConfigArgs?check) "The check argument to eval-config.nix is deprecated. Please set config._module.check instead."
|
lib.warnIf (evalConfigArgs ? extraArgs)
|
||||||
lib.warnIf (specialArgs?pkgs) ''
|
"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."
|
||||||
|
lib.warnIf
|
||||||
|
(specialArgs ? pkgs)
|
||||||
|
''
|
||||||
You have set specialArgs.pkgs, which means that options like nixpkgs.config
|
You have set specialArgs.pkgs, which means that options like nixpkgs.config
|
||||||
and nixpkgs.overlays will be ignored. If you wish to reuse an already created
|
and nixpkgs.overlays will be ignored. If you wish to reuse an already created
|
||||||
pkgs, which you know is configured correctly for this NixOS configuration,
|
pkgs, which you know is configured correctly for this NixOS configuration,
|
||||||
|
@ -97,12 +106,12 @@ let
|
||||||
x;
|
x;
|
||||||
|
|
||||||
legacyModules =
|
legacyModules =
|
||||||
lib.optional (evalConfigArgs?extraArgs) {
|
lib.optional (evalConfigArgs ? extraArgs) {
|
||||||
config = {
|
config = {
|
||||||
_module.args = extraArgs;
|
_module.args = extraArgs;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
++ lib.optional (evalConfigArgs?check) {
|
++ lib.optional (evalConfigArgs ? check) {
|
||||||
config = {
|
config = {
|
||||||
_module.check = lib.mkDefault check;
|
_module.check = lib.mkDefault check;
|
||||||
};
|
};
|
||||||
|
@ -122,21 +131,35 @@ let
|
||||||
|
|
||||||
noUserModules = evalModulesMinimal ({
|
noUserModules = evalModulesMinimal ({
|
||||||
inherit prefix specialArgs;
|
inherit prefix specialArgs;
|
||||||
modules = baseModules ++ extraModules ++ [ pkgsModule modulesModule ];
|
modules =
|
||||||
|
baseModules
|
||||||
|
++ extraModules
|
||||||
|
++ [
|
||||||
|
pkgsModule
|
||||||
|
modulesModule
|
||||||
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
# Extra arguments that are useful for constructing a similar configuration.
|
# Extra arguments that are useful for constructing a similar configuration.
|
||||||
modulesModule = {
|
modulesModule = {
|
||||||
config = {
|
config = {
|
||||||
_module.args = {
|
_module.args = {
|
||||||
inherit noUserModules baseModules extraModules modules;
|
inherit
|
||||||
|
noUserModules
|
||||||
|
baseModules
|
||||||
|
extraModules
|
||||||
|
modules
|
||||||
|
;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
nixosWithUserModules = noUserModules.extendModules { modules = allUserModules; };
|
nixosWithUserModules = noUserModules.extendModules { modules = allUserModules; };
|
||||||
|
|
||||||
withExtraAttrs = configuration: configuration // {
|
withExtraAttrs =
|
||||||
|
configuration:
|
||||||
|
configuration
|
||||||
|
// {
|
||||||
inherit extraArgs;
|
inherit extraArgs;
|
||||||
inherit (configuration._module.args) pkgs;
|
inherit (configuration._module.args) pkgs;
|
||||||
inherit lib;
|
inherit lib;
|
||||||
|
|
|
@ -3,20 +3,22 @@
|
||||||
# contents of a directory that can be populated with commands. The
|
# contents of a directory that can be populated with commands. The
|
||||||
# generated image is sized to only fit its contents, with the expectation
|
# generated image is sized to only fit its contents, with the expectation
|
||||||
# that a script resizes the filesystem at boot time.
|
# that a script resizes the filesystem at boot time.
|
||||||
{ pkgs
|
{
|
||||||
, lib
|
pkgs,
|
||||||
# List of derivations to be included
|
lib,
|
||||||
, storePaths
|
# List of derivations to be included
|
||||||
# Whether or not to compress the resulting image with zstd
|
storePaths,
|
||||||
, compressImage ? false, zstd
|
# Whether or not to compress the resulting image with zstd
|
||||||
# Shell commands to populate the ./files directory.
|
compressImage ? false,
|
||||||
# All files in that directory are copied to the root of the FS.
|
zstd,
|
||||||
, populateImageCommands ? ""
|
# Shell commands to populate the ./files directory.
|
||||||
, volumeLabel
|
# All files in that directory are copied to the root of the FS.
|
||||||
, uuid ? "44444444-4444-4444-8888-888888888888"
|
populateImageCommands ? "",
|
||||||
, btrfs-progs
|
volumeLabel,
|
||||||
, libfaketime
|
uuid ? "44444444-4444-4444-8888-888888888888",
|
||||||
, fakeroot
|
btrfs-progs,
|
||||||
|
libfaketime,
|
||||||
|
fakeroot,
|
||||||
}:
|
}:
|
||||||
|
|
||||||
let
|
let
|
||||||
|
@ -25,10 +27,13 @@ in
|
||||||
pkgs.stdenv.mkDerivation {
|
pkgs.stdenv.mkDerivation {
|
||||||
name = "btrfs-fs.img${lib.optionalString compressImage ".zst"}";
|
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 =
|
buildCommand = ''
|
||||||
''
|
|
||||||
${if compressImage then "img=temp.img" else "img=$out"}
|
${if compressImage then "img=temp.img" else "img=$out"}
|
||||||
|
|
||||||
set -x
|
set -x
|
||||||
|
|
|
@ -1,18 +1,22 @@
|
||||||
{ lib, stdenv, squashfsTools, closureInfo
|
{
|
||||||
|
lib,
|
||||||
|
stdenv,
|
||||||
|
squashfsTools,
|
||||||
|
closureInfo,
|
||||||
|
|
||||||
, fileName ? "squashfs"
|
fileName ? "squashfs",
|
||||||
, # The root directory of the squashfs filesystem is filled with the
|
# The root directory of the squashfs filesystem is filled with the
|
||||||
# closures of the Nix store paths listed here.
|
# closures of the Nix store paths listed here.
|
||||||
storeContents ? []
|
storeContents ? [ ],
|
||||||
# Pseudo files to be added to squashfs image
|
# Pseudo files to be added to squashfs image
|
||||||
, pseudoFiles ? []
|
pseudoFiles ? [ ],
|
||||||
, noStrip ? false
|
noStrip ? false,
|
||||||
, # Compression parameters.
|
# Compression parameters.
|
||||||
# For zstd compression you can use "zstd -Xcompression-level 6".
|
# For zstd compression you can use "zstd -Xcompression-level 6".
|
||||||
comp ? "xz -Xdict-size 100%"
|
comp ? "xz -Xdict-size 100%",
|
||||||
, # create hydra build product. will put image in directory instead
|
# create hydra build product. will put image in directory instead
|
||||||
# of directly in the store
|
# of directly in the store
|
||||||
hydraBuildProduct ? false
|
hydraBuildProduct ? false,
|
||||||
}:
|
}:
|
||||||
|
|
||||||
let
|
let
|
||||||
|
@ -34,24 +38,28 @@ stdenv.mkDerivation {
|
||||||
cp $closureInfo/registration nix-path-registration
|
cp $closureInfo/registration nix-path-registration
|
||||||
|
|
||||||
imgPath="$out"
|
imgPath="$out"
|
||||||
'' + lib.optionalString hydraBuildProduct ''
|
''
|
||||||
|
+ lib.optionalString hydraBuildProduct ''
|
||||||
|
|
||||||
mkdir $out
|
mkdir $out
|
||||||
imgPath="$out/${fileName}.squashfs"
|
imgPath="$out/${fileName}.squashfs"
|
||||||
'' + lib.optionalString stdenv.buildPlatform.is32bit ''
|
''
|
||||||
|
+ lib.optionalString stdenv.buildPlatform.is32bit ''
|
||||||
|
|
||||||
# 64 cores on i686 does not work
|
# 64 cores on i686 does not work
|
||||||
# fails with FATAL ERROR: mangle2:: xz compress failed with error code 5
|
# fails with FATAL ERROR: mangle2:: xz compress failed with error code 5
|
||||||
if ((NIX_BUILD_CORES > 48)); then
|
if ((NIX_BUILD_CORES > 48)); then
|
||||||
NIX_BUILD_CORES=48
|
NIX_BUILD_CORES=48
|
||||||
fi
|
fi
|
||||||
'' + ''
|
''
|
||||||
|
+ ''
|
||||||
|
|
||||||
# Generate the squashfs image.
|
# Generate the squashfs image.
|
||||||
mksquashfs nix-path-registration $(cat $closureInfo/store-paths) $imgPath ${pseudoFilesArgs} \
|
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} \
|
-no-hardlinks ${lib.optionalString noStrip "-no-strip"} -keep-as-directory -all-root -b 1048576 ${compFlag} \
|
||||||
-processors $NIX_BUILD_CORES -root-mode 0755
|
-processors $NIX_BUILD_CORES -root-mode 0755
|
||||||
'' + lib.optionalString hydraBuildProduct ''
|
''
|
||||||
|
+ lib.optionalString hydraBuildProduct ''
|
||||||
|
|
||||||
mkdir -p $out/nix-support
|
mkdir -p $out/nix-support
|
||||||
echo "file squashfs-image $out/${fileName}.squashfs" >> $out/nix-support/hydra-build-products
|
echo "file squashfs-image $out/${fileName}.squashfs" >> $out/nix-support/hydra-build-products
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
{ config, lib, pkgs, utils }:
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
utils,
|
||||||
|
}:
|
||||||
|
|
||||||
let
|
let
|
||||||
inherit (lib)
|
inherit (lib)
|
||||||
|
@ -54,19 +59,22 @@ let
|
||||||
cfg = config.systemd;
|
cfg = config.systemd;
|
||||||
lndir = "${pkgs.buildPackages.xorg.lndir}/bin/lndir";
|
lndir = "${pkgs.buildPackages.xorg.lndir}/bin/lndir";
|
||||||
systemd = cfg.package;
|
systemd = cfg.package;
|
||||||
in rec {
|
in
|
||||||
|
rec {
|
||||||
|
|
||||||
shellEscape = s: (replaceStrings [ "\\" ] [ "\\\\" ] s);
|
shellEscape = s: (replaceStrings [ "\\" ] [ "\\\\" ] s);
|
||||||
|
|
||||||
mkPathSafeName = replaceStrings ["@" ":" "\\" "[" "]"] ["-" "-" "-" "" ""];
|
mkPathSafeName = replaceStrings [ "@" ":" "\\" "[" "]" ] [ "-" "-" "-" "" "" ];
|
||||||
|
|
||||||
# a type for options that take a unit name
|
# a type for options that take a unit name
|
||||||
unitNameType = types.strMatching "[a-zA-Z0-9@%:_.\\-]+[.](service|socket|device|mount|automount|swap|target|path|timer|scope|slice)";
|
unitNameType = types.strMatching "[a-zA-Z0-9@%:_.\\-]+[.](service|socket|device|mount|automount|swap|target|path|timer|scope|slice)";
|
||||||
|
|
||||||
makeUnit = name: unit:
|
makeUnit =
|
||||||
|
name: unit:
|
||||||
if unit.enable then
|
if unit.enable then
|
||||||
pkgs.runCommand "unit-${mkPathSafeName name}"
|
pkgs.runCommand "unit-${mkPathSafeName name}"
|
||||||
{ preferLocalBuild = true;
|
{
|
||||||
|
preferLocalBuild = true;
|
||||||
allowSubstitutes = false;
|
allowSubstitutes = false;
|
||||||
# unit.text can be null. But variables that are null listed in
|
# unit.text can be null. But variables that are null listed in
|
||||||
# passAsFile are ignored by nix, resulting in no file being created,
|
# passAsFile are ignored by nix, resulting in no file being created,
|
||||||
|
@ -81,7 +89,8 @@ in rec {
|
||||||
''
|
''
|
||||||
else
|
else
|
||||||
pkgs.runCommand "unit-${mkPathSafeName name}-disabled"
|
pkgs.runCommand "unit-${mkPathSafeName name}-disabled"
|
||||||
{ preferLocalBuild = true;
|
{
|
||||||
|
preferLocalBuild = true;
|
||||||
allowSubstitutes = false;
|
allowSubstitutes = false;
|
||||||
}
|
}
|
||||||
''
|
''
|
||||||
|
@ -90,143 +99,222 @@ in rec {
|
||||||
ln -s /dev/null "$out/$name"
|
ln -s /dev/null "$out/$name"
|
||||||
'';
|
'';
|
||||||
|
|
||||||
boolValues = [true false "yes" "no"];
|
boolValues = [
|
||||||
|
true
|
||||||
|
false
|
||||||
|
"yes"
|
||||||
|
"no"
|
||||||
|
];
|
||||||
|
|
||||||
digits = map toString (range 0 9);
|
digits = map toString (range 0 9);
|
||||||
|
|
||||||
isByteFormat = s:
|
isByteFormat =
|
||||||
|
s:
|
||||||
let
|
let
|
||||||
l = reverseList (stringToCharacters s);
|
l = reverseList (stringToCharacters s);
|
||||||
suffix = head l;
|
suffix = head l;
|
||||||
nums = tail l;
|
nums = tail l;
|
||||||
in builtins.isInt s
|
in
|
||||||
|| (elem suffix (["K" "M" "G" "T"] ++ digits)
|
builtins.isInt s
|
||||||
&& all (num: elem num digits) nums);
|
|| (
|
||||||
|
elem suffix (
|
||||||
|
[
|
||||||
|
"K"
|
||||||
|
"M"
|
||||||
|
"G"
|
||||||
|
"T"
|
||||||
|
]
|
||||||
|
++ digits
|
||||||
|
)
|
||||||
|
&& all (num: elem num digits) nums
|
||||||
|
);
|
||||||
|
|
||||||
assertByteFormat = name: group: attr:
|
assertByteFormat =
|
||||||
optional (attr ? ${name} && ! isByteFormat attr.${name})
|
name: group: attr:
|
||||||
"Systemd ${group} field `${name}' must be in byte format [0-9]+[KMGT].";
|
optional (
|
||||||
|
attr ? ${name} && !isByteFormat attr.${name}
|
||||||
|
) "Systemd ${group} field `${name}' must be in byte format [0-9]+[KMGT].";
|
||||||
|
|
||||||
toIntBaseDetected = value: assert (match "[0-9]+|0x[0-9a-fA-F]+" value) != null; (builtins.fromTOML "v=${value}").v;
|
toIntBaseDetected =
|
||||||
|
value:
|
||||||
|
assert (match "[0-9]+|0x[0-9a-fA-F]+" value) != null;
|
||||||
|
(builtins.fromTOML "v=${value}").v;
|
||||||
|
|
||||||
hexChars = stringToCharacters "0123456789abcdefABCDEF";
|
hexChars = stringToCharacters "0123456789abcdefABCDEF";
|
||||||
|
|
||||||
isMacAddress = s: stringLength s == 17
|
isMacAddress =
|
||||||
&& flip all (splitString ":" s) (bytes:
|
s:
|
||||||
all (byte: elem byte hexChars) (stringToCharacters bytes)
|
stringLength s == 17
|
||||||
);
|
&& flip all (splitString ":" s) (bytes: all (byte: elem byte hexChars) (stringToCharacters bytes));
|
||||||
|
|
||||||
assertMacAddress = name: group: attr:
|
assertMacAddress =
|
||||||
optional (attr ? ${name} && ! isMacAddress attr.${name})
|
name: group: attr:
|
||||||
"Systemd ${group} field `${name}' must be a valid MAC address.";
|
optional (
|
||||||
|
attr ? ${name} && !isMacAddress attr.${name}
|
||||||
|
) "Systemd ${group} field `${name}' must be a valid MAC address.";
|
||||||
|
|
||||||
assertNetdevMacAddress = name: group: attr:
|
assertNetdevMacAddress =
|
||||||
optional (attr ? ${name} && (! isMacAddress attr.${name} && attr.${name} != "none"))
|
name: group: attr:
|
||||||
"Systemd ${group} field `${name}` must be a valid MAC address or the special value `none`.";
|
optional (
|
||||||
|
attr ? ${name} && (!isMacAddress attr.${name} && attr.${name} != "none")
|
||||||
|
) "Systemd ${group} field `${name}` must be a valid MAC address or the special value `none`.";
|
||||||
|
|
||||||
isNumberOrRangeOf = check: v:
|
isNumberOrRangeOf =
|
||||||
if isInt v
|
check: v:
|
||||||
then check v
|
if isInt v then
|
||||||
else let
|
check v
|
||||||
|
else
|
||||||
|
let
|
||||||
parts = splitString "-" v;
|
parts = splitString "-" v;
|
||||||
lower = toIntBase10 (head parts);
|
lower = toIntBase10 (head parts);
|
||||||
upper = if tail parts != [] then toIntBase10 (head (tail parts)) else lower;
|
upper = if tail parts != [ ] then toIntBase10 (head (tail parts)) else lower;
|
||||||
in
|
in
|
||||||
length parts <= 2 && lower <= upper && check lower && check upper;
|
length parts <= 2 && lower <= upper && check lower && check upper;
|
||||||
isPort = i: i >= 0 && i <= 65535;
|
isPort = i: i >= 0 && i <= 65535;
|
||||||
isPortOrPortRange = isNumberOrRangeOf isPort;
|
isPortOrPortRange = isNumberOrRangeOf isPort;
|
||||||
|
|
||||||
assertPort = name: group: attr:
|
assertPort =
|
||||||
optional (attr ? ${name} && ! isPort attr.${name})
|
name: group: attr:
|
||||||
"Error on the systemd ${group} field `${name}': ${attr.name} is not a valid port number.";
|
optional (
|
||||||
|
attr ? ${name} && !isPort attr.${name}
|
||||||
|
) "Error on the systemd ${group} field `${name}': ${attr.name} is not a valid port number.";
|
||||||
|
|
||||||
assertPortOrPortRange = name: group: attr:
|
assertPortOrPortRange =
|
||||||
optional (attr ? ${name} && ! isPortOrPortRange attr.${name})
|
name: group: attr:
|
||||||
|
optional (attr ? ${name} && !isPortOrPortRange attr.${name})
|
||||||
"Error on the systemd ${group} field `${name}': ${attr.name} is not a valid port number or range of port numbers.";
|
"Error on the systemd ${group} field `${name}': ${attr.name} is not a valid port number or range of port numbers.";
|
||||||
|
|
||||||
assertValueOneOf = name: values: group: attr:
|
assertValueOneOf =
|
||||||
optional (attr ? ${name} && !elem attr.${name} values)
|
name: values: group: attr:
|
||||||
"Systemd ${group} field `${name}' cannot have value `${toString attr.${name}}'.";
|
optional (
|
||||||
|
attr ? ${name} && !elem attr.${name} values
|
||||||
|
) "Systemd ${group} field `${name}' cannot have value `${toString attr.${name}}'.";
|
||||||
|
|
||||||
assertValuesSomeOfOr = name: values: default: group: attr:
|
assertValuesSomeOfOr =
|
||||||
optional (attr ? ${name} && !(all (x: elem x values) (splitString " " attr.${name}) || attr.${name} == default))
|
name: values: default: group: attr:
|
||||||
"Systemd ${group} field `${name}' cannot have value `${toString attr.${name}}'.";
|
optional (
|
||||||
|
attr ? ${name}
|
||||||
|
&& !(all (x: elem x values) (splitString " " attr.${name}) || attr.${name} == default)
|
||||||
|
) "Systemd ${group} field `${name}' cannot have value `${toString attr.${name}}'.";
|
||||||
|
|
||||||
assertHasField = name: group: attr:
|
assertHasField =
|
||||||
optional (!(attr ? ${name}))
|
name: group: attr:
|
||||||
"Systemd ${group} field `${name}' must exist.";
|
optional (!(attr ? ${name})) "Systemd ${group} field `${name}' must exist.";
|
||||||
|
|
||||||
assertRange = name: min: max: group: attr:
|
assertRange =
|
||||||
optional (attr ? ${name} && !(min <= attr.${name} && max >= attr.${name}))
|
name: min: max: group: attr:
|
||||||
"Systemd ${group} field `${name}' is outside the range [${toString min},${toString max}]";
|
optional (
|
||||||
|
attr ? ${name} && !(min <= attr.${name} && max >= attr.${name})
|
||||||
|
) "Systemd ${group} field `${name}' is outside the range [${toString min},${toString max}]";
|
||||||
|
|
||||||
assertRangeOrOneOf = name: min: max: values: group: attr:
|
assertRangeOrOneOf =
|
||||||
optional (attr ? ${name} && !(((isInt attr.${name} || isFloat attr.${name}) && min <= attr.${name} && max >= attr.${name}) || elem attr.${name} values))
|
name: min: max: values: group: attr:
|
||||||
|
optional
|
||||||
|
(
|
||||||
|
attr ? ${name}
|
||||||
|
&& !(
|
||||||
|
((isInt attr.${name} || isFloat attr.${name}) && min <= attr.${name} && max >= attr.${name})
|
||||||
|
|| elem attr.${name} values
|
||||||
|
)
|
||||||
|
)
|
||||||
"Systemd ${group} field `${name}' is not a value in range [${toString min},${toString max}], or one of ${toString values}";
|
"Systemd ${group} field `${name}' is not a value in range [${toString min},${toString max}], or one of ${toString values}";
|
||||||
|
|
||||||
assertRangeWithOptionalMask = name: min: max: group: attr:
|
assertRangeWithOptionalMask =
|
||||||
|
name: min: max: group: attr:
|
||||||
if (attr ? ${name}) then
|
if (attr ? ${name}) then
|
||||||
if isInt attr.${name} then
|
if isInt attr.${name} then
|
||||||
assertRange name min max group attr
|
assertRange name min max group attr
|
||||||
else if isString attr.${name} then
|
else if isString attr.${name} then
|
||||||
let
|
let
|
||||||
fields = match "([0-9]+|0x[0-9a-fA-F]+)(/([0-9]+|0x[0-9a-fA-F]+))?" attr.${name};
|
fields = match "([0-9]+|0x[0-9a-fA-F]+)(/([0-9]+|0x[0-9a-fA-F]+))?" attr.${name};
|
||||||
in if fields == null then ["Systemd ${group} field `${name}' must either be an integer or two integers separated by a slash (/)."]
|
in
|
||||||
else let
|
if fields == null then
|
||||||
|
[
|
||||||
|
"Systemd ${group} field `${name}' must either be an integer or two integers separated by a slash (/)."
|
||||||
|
]
|
||||||
|
else
|
||||||
|
let
|
||||||
value = toIntBaseDetected (elemAt fields 0);
|
value = toIntBaseDetected (elemAt fields 0);
|
||||||
mask = mapNullable toIntBaseDetected (elemAt fields 2);
|
mask = mapNullable toIntBaseDetected (elemAt fields 2);
|
||||||
in
|
in
|
||||||
optional (!(min <= value && max >= value)) "Systemd ${group} field `${name}' has main value outside the range [${toString min},${toString max}]."
|
optional (!(min <= value && max >= value))
|
||||||
++ optional (mask != null && !(min <= mask && max >= mask)) "Systemd ${group} field `${name}' has mask outside the range [${toString min},${toString max}]."
|
"Systemd ${group} field `${name}' has main value outside the range [${toString min},${toString max}]."
|
||||||
else ["Systemd ${group} field `${name}' must either be an integer or a string."]
|
++ optional (
|
||||||
else [];
|
mask != null && !(min <= mask && max >= mask)
|
||||||
|
) "Systemd ${group} field `${name}' has mask outside the range [${toString min},${toString max}]."
|
||||||
|
else
|
||||||
|
[ "Systemd ${group} field `${name}' must either be an integer or a string." ]
|
||||||
|
else
|
||||||
|
[ ];
|
||||||
|
|
||||||
assertMinimum = name: min: group: attr:
|
assertMinimum =
|
||||||
optional (attr ? ${name} && attr.${name} < min)
|
name: min: group: attr:
|
||||||
"Systemd ${group} field `${name}' must be greater than or equal to ${toString min}";
|
optional (
|
||||||
|
attr ? ${name} && attr.${name} < min
|
||||||
|
) "Systemd ${group} field `${name}' must be greater than or equal to ${toString min}";
|
||||||
|
|
||||||
assertOnlyFields = fields: group: attr:
|
assertOnlyFields =
|
||||||
let badFields = filter (name: ! elem name fields) (attrNames attr); in
|
fields: group: attr:
|
||||||
optional (badFields != [ ])
|
let
|
||||||
"Systemd ${group} has extra fields [${concatStringsSep " " badFields}].";
|
badFields = filter (name: !elem name fields) (attrNames attr);
|
||||||
|
in
|
||||||
|
optional (
|
||||||
|
badFields != [ ]
|
||||||
|
) "Systemd ${group} has extra fields [${concatStringsSep " " badFields}].";
|
||||||
|
|
||||||
assertInt = name: group: attr:
|
assertInt =
|
||||||
optional (attr ? ${name} && !isInt attr.${name})
|
name: group: attr:
|
||||||
"Systemd ${group} field `${name}' is not an integer";
|
optional (
|
||||||
|
attr ? ${name} && !isInt attr.${name}
|
||||||
|
) "Systemd ${group} field `${name}' is not an integer";
|
||||||
|
|
||||||
assertRemoved = name: see: group: attr:
|
assertRemoved =
|
||||||
optional (attr ? ${name})
|
name: see: group: attr:
|
||||||
"Systemd ${group} field `${name}' has been removed. See ${see}";
|
optional (attr ? ${name}) "Systemd ${group} field `${name}' has been removed. See ${see}";
|
||||||
|
|
||||||
assertKeyIsSystemdCredential = name: group: attr:
|
assertKeyIsSystemdCredential =
|
||||||
optional (attr ? ${name} && !(hasPrefix "@" attr.${name}))
|
name: group: attr:
|
||||||
"Systemd ${group} field `${name}' is not a systemd credential";
|
optional (
|
||||||
|
attr ? ${name} && !(hasPrefix "@" attr.${name})
|
||||||
|
) "Systemd ${group} field `${name}' is not a systemd credential";
|
||||||
|
|
||||||
checkUnitConfig = group: checks: attrs: let
|
checkUnitConfig =
|
||||||
|
group: checks: attrs:
|
||||||
|
let
|
||||||
# We're applied at the top-level type (attrsOf unitOption), so the actual
|
# We're applied at the top-level type (attrsOf unitOption), so the actual
|
||||||
# unit options might contain attributes from mkOverride and mkIf that we need to
|
# unit options might contain attributes from mkOverride and mkIf that we need to
|
||||||
# convert into single values before checking them.
|
# convert into single values before checking them.
|
||||||
defs = mapAttrs (const (v:
|
defs = mapAttrs (const (
|
||||||
if v._type or "" == "override" then v.content
|
v:
|
||||||
else if v._type or "" == "if" then v.content
|
if v._type or "" == "override" then
|
||||||
else v
|
v.content
|
||||||
|
else if v._type or "" == "if" then
|
||||||
|
v.content
|
||||||
|
else
|
||||||
|
v
|
||||||
)) attrs;
|
)) attrs;
|
||||||
errors = concatMap (c: c group defs) checks;
|
errors = concatMap (c: c group defs) checks;
|
||||||
in if errors == [] then true
|
in
|
||||||
else trace (concatStringsSep "\n" errors) false;
|
if errors == [ ] then true else trace (concatStringsSep "\n" errors) false;
|
||||||
|
|
||||||
checkUnitConfigWithLegacyKey = legacyKey: group: checks: attrs:
|
checkUnitConfigWithLegacyKey =
|
||||||
|
legacyKey: group: checks: attrs:
|
||||||
let
|
let
|
||||||
dump = lib.generators.toPretty { }
|
dump = lib.generators.toPretty { } (
|
||||||
(lib.generators.withRecursion { depthLimit = 2; throwOnDepthLimit = false; } attrs);
|
lib.generators.withRecursion {
|
||||||
|
depthLimit = 2;
|
||||||
|
throwOnDepthLimit = false;
|
||||||
|
} attrs
|
||||||
|
);
|
||||||
attrs' =
|
attrs' =
|
||||||
if legacyKey == null
|
if legacyKey == null then
|
||||||
then attrs
|
attrs
|
||||||
else if ! attrs?${legacyKey}
|
else if !attrs ? ${legacyKey} then
|
||||||
then attrs
|
attrs
|
||||||
else if removeAttrs attrs [ legacyKey ] == {}
|
else if removeAttrs attrs [ legacyKey ] == { } then
|
||||||
then attrs.${legacyKey}
|
attrs.${legacyKey}
|
||||||
else throw ''
|
else
|
||||||
|
throw ''
|
||||||
The declaration
|
The declaration
|
||||||
|
|
||||||
${dump}
|
${dump}
|
||||||
|
@ -239,31 +327,53 @@ in rec {
|
||||||
in
|
in
|
||||||
checkUnitConfig group checks attrs';
|
checkUnitConfig group checks attrs';
|
||||||
|
|
||||||
toOption = x:
|
toOption =
|
||||||
if x == true then "true"
|
x:
|
||||||
else if x == false then "false"
|
if x == true then
|
||||||
else toString x;
|
"true"
|
||||||
|
else if x == false then
|
||||||
|
"false"
|
||||||
|
else
|
||||||
|
toString x;
|
||||||
|
|
||||||
attrsToSection = as:
|
attrsToSection =
|
||||||
concatStrings (concatLists (mapAttrsToList (name: value:
|
as:
|
||||||
|
concatStrings (
|
||||||
|
concatLists (
|
||||||
|
mapAttrsToList (
|
||||||
|
name: value:
|
||||||
map (x: ''
|
map (x: ''
|
||||||
${name}=${toOption x}
|
${name}=${toOption x}
|
||||||
'')
|
'') (if isList value then value else [ value ])
|
||||||
(if isList value then value else [value]))
|
) as
|
||||||
as));
|
)
|
||||||
|
);
|
||||||
|
|
||||||
generateUnits = { allowCollisions ? true, type, units, upstreamUnits, upstreamWants, packages ? cfg.packages, package ? cfg.package }:
|
generateUnits =
|
||||||
|
{
|
||||||
|
allowCollisions ? true,
|
||||||
|
type,
|
||||||
|
units,
|
||||||
|
upstreamUnits,
|
||||||
|
upstreamWants,
|
||||||
|
packages ? cfg.packages,
|
||||||
|
package ? cfg.package,
|
||||||
|
}:
|
||||||
let
|
let
|
||||||
typeDir = ({
|
typeDir =
|
||||||
|
({
|
||||||
system = "system";
|
system = "system";
|
||||||
initrd = "system";
|
initrd = "system";
|
||||||
user = "user";
|
user = "user";
|
||||||
nspawn = "nspawn";
|
nspawn = "nspawn";
|
||||||
}).${type};
|
}).${type};
|
||||||
in pkgs.runCommand "${type}-units"
|
in
|
||||||
{ preferLocalBuild = true;
|
pkgs.runCommand "${type}-units"
|
||||||
|
{
|
||||||
|
preferLocalBuild = true;
|
||||||
allowSubstitutes = false;
|
allowSubstitutes = false;
|
||||||
} ''
|
}
|
||||||
|
''
|
||||||
mkdir -p $out
|
mkdir -p $out
|
||||||
|
|
||||||
# Copy the upstream systemd units we're interested in.
|
# Copy the upstream systemd units we're interested in.
|
||||||
|
@ -322,21 +432,32 @@ in rec {
|
||||||
# systemd or systemd.packages, then add them as
|
# systemd or systemd.packages, then add them as
|
||||||
# <unit-name>.d/overrides.conf, which makes them extend the
|
# <unit-name>.d/overrides.conf, which makes them extend the
|
||||||
# upstream unit.
|
# upstream unit.
|
||||||
for i in ${toString (mapAttrsToList
|
for i in ${
|
||||||
(n: v: v.unit)
|
toString (
|
||||||
(filterAttrs (n: v: (attrByPath [ "overrideStrategy" ] "asDropinIfExists" v) == "asDropinIfExists") units))}; do
|
mapAttrsToList (n: v: v.unit) (
|
||||||
|
filterAttrs (
|
||||||
|
n: v: (attrByPath [ "overrideStrategy" ] "asDropinIfExists" v) == "asDropinIfExists"
|
||||||
|
) units
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}; do
|
||||||
fn=$(basename $i/*)
|
fn=$(basename $i/*)
|
||||||
if [ -e $out/$fn ]; then
|
if [ -e $out/$fn ]; then
|
||||||
if [ "$(readlink -f $i/$fn)" = /dev/null ]; then
|
if [ "$(readlink -f $i/$fn)" = /dev/null ]; then
|
||||||
ln -sfn /dev/null $out/$fn
|
ln -sfn /dev/null $out/$fn
|
||||||
else
|
else
|
||||||
${if allowCollisions then ''
|
${
|
||||||
|
if allowCollisions then
|
||||||
|
''
|
||||||
mkdir -p $out/$fn.d
|
mkdir -p $out/$fn.d
|
||||||
ln -s $i/$fn $out/$fn.d/overrides.conf
|
ln -s $i/$fn $out/$fn.d/overrides.conf
|
||||||
'' else ''
|
''
|
||||||
|
else
|
||||||
|
''
|
||||||
echo "Found multiple derivations configuring $fn!"
|
echo "Found multiple derivations configuring $fn!"
|
||||||
exit 1
|
exit 1
|
||||||
''}
|
''
|
||||||
|
}
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
ln -fs $i/$fn $out/
|
ln -fs $i/$fn $out/
|
||||||
|
@ -345,39 +466,59 @@ in rec {
|
||||||
|
|
||||||
# Symlink units defined by systemd.units which shall be
|
# Symlink units defined by systemd.units which shall be
|
||||||
# treated as drop-in file.
|
# treated as drop-in file.
|
||||||
for i in ${toString (mapAttrsToList
|
for i in ${
|
||||||
(n: v: v.unit)
|
toString (
|
||||||
(filterAttrs (n: v: v ? overrideStrategy && v.overrideStrategy == "asDropin") units))}; do
|
mapAttrsToList (n: v: v.unit) (
|
||||||
|
filterAttrs (n: v: v ? overrideStrategy && v.overrideStrategy == "asDropin") units
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}; do
|
||||||
fn=$(basename $i/*)
|
fn=$(basename $i/*)
|
||||||
mkdir -p $out/$fn.d
|
mkdir -p $out/$fn.d
|
||||||
ln -s $i/$fn $out/$fn.d/overrides.conf
|
ln -s $i/$fn $out/$fn.d/overrides.conf
|
||||||
done
|
done
|
||||||
|
|
||||||
# Create service aliases from aliases option.
|
# Create service aliases from aliases option.
|
||||||
${concatStrings (mapAttrsToList (name: unit:
|
${concatStrings (
|
||||||
|
mapAttrsToList (
|
||||||
|
name: unit:
|
||||||
concatMapStrings (name2: ''
|
concatMapStrings (name2: ''
|
||||||
ln -sfn '${name}' $out/'${name2}'
|
ln -sfn '${name}' $out/'${name2}'
|
||||||
'') (unit.aliases or [])) units)}
|
'') (unit.aliases or [ ])
|
||||||
|
) units
|
||||||
|
)}
|
||||||
|
|
||||||
# Create .wants, .upholds and .requires symlinks from the wantedBy, upheldBy and
|
# Create .wants, .upholds and .requires symlinks from the wantedBy, upheldBy and
|
||||||
# requiredBy options.
|
# requiredBy options.
|
||||||
${concatStrings (mapAttrsToList (name: unit:
|
${concatStrings (
|
||||||
|
mapAttrsToList (
|
||||||
|
name: unit:
|
||||||
concatMapStrings (name2: ''
|
concatMapStrings (name2: ''
|
||||||
mkdir -p $out/'${name2}.wants'
|
mkdir -p $out/'${name2}.wants'
|
||||||
ln -sfn '../${name}' $out/'${name2}.wants'/
|
ln -sfn '../${name}' $out/'${name2}.wants'/
|
||||||
'') (unit.wantedBy or [])) units)}
|
'') (unit.wantedBy or [ ])
|
||||||
|
) units
|
||||||
|
)}
|
||||||
|
|
||||||
${concatStrings (mapAttrsToList (name: unit:
|
${concatStrings (
|
||||||
|
mapAttrsToList (
|
||||||
|
name: unit:
|
||||||
concatMapStrings (name2: ''
|
concatMapStrings (name2: ''
|
||||||
mkdir -p $out/'${name2}.upholds'
|
mkdir -p $out/'${name2}.upholds'
|
||||||
ln -sfn '../${name}' $out/'${name2}.upholds'/
|
ln -sfn '../${name}' $out/'${name2}.upholds'/
|
||||||
'') (unit.upheldBy or [])) units)}
|
'') (unit.upheldBy or [ ])
|
||||||
|
) units
|
||||||
|
)}
|
||||||
|
|
||||||
${concatStrings (mapAttrsToList (name: unit:
|
${concatStrings (
|
||||||
|
mapAttrsToList (
|
||||||
|
name: unit:
|
||||||
concatMapStrings (name2: ''
|
concatMapStrings (name2: ''
|
||||||
mkdir -p $out/'${name2}.requires'
|
mkdir -p $out/'${name2}.requires'
|
||||||
ln -sfn '../${name}' $out/'${name2}.requires'/
|
ln -sfn '../${name}' $out/'${name2}.requires'/
|
||||||
'') (unit.requiredBy or [])) units)}
|
'') (unit.requiredBy or [ ])
|
||||||
|
) units
|
||||||
|
)}
|
||||||
|
|
||||||
${optionalString (type == "system") ''
|
${optionalString (type == "system") ''
|
||||||
# Stupid misc. symlinks.
|
# Stupid misc. symlinks.
|
||||||
|
@ -392,11 +533,17 @@ in rec {
|
||||||
''}
|
''}
|
||||||
''; # */
|
''; # */
|
||||||
|
|
||||||
makeJobScript = { name, text, enableStrictShellChecks }:
|
makeJobScript =
|
||||||
|
{
|
||||||
|
name,
|
||||||
|
text,
|
||||||
|
enableStrictShellChecks,
|
||||||
|
}:
|
||||||
let
|
let
|
||||||
scriptName = replaceStrings [ "\\" "@" ] [ "-" "_" ] (shellEscape name);
|
scriptName = replaceStrings [ "\\" "@" ] [ "-" "_" ] (shellEscape name);
|
||||||
out = (
|
out =
|
||||||
if ! enableStrictShellChecks then
|
(
|
||||||
|
if !enableStrictShellChecks then
|
||||||
pkgs.writeShellScriptBin scriptName ''
|
pkgs.writeShellScriptBin scriptName ''
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
|
@ -407,57 +554,68 @@ in rec {
|
||||||
name = scriptName;
|
name = scriptName;
|
||||||
inherit text;
|
inherit text;
|
||||||
}
|
}
|
||||||
).overrideAttrs (_: {
|
).overrideAttrs
|
||||||
|
(_: {
|
||||||
# The derivation name is different from the script file name
|
# The derivation name is different from the script file name
|
||||||
# to keep the script file name short to avoid cluttering logs.
|
# to keep the script file name short to avoid cluttering logs.
|
||||||
name = "unit-script-${scriptName}";
|
name = "unit-script-${scriptName}";
|
||||||
});
|
});
|
||||||
in lib.getExe out;
|
in
|
||||||
|
lib.getExe out;
|
||||||
|
|
||||||
unitConfig = { config, name, options, ... }: {
|
unitConfig =
|
||||||
|
{
|
||||||
|
config,
|
||||||
|
name,
|
||||||
|
options,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
{
|
||||||
config = {
|
config = {
|
||||||
unitConfig =
|
unitConfig =
|
||||||
optionalAttrs (config.requires != [])
|
optionalAttrs (config.requires != [ ]) { Requires = toString config.requires; }
|
||||||
{ Requires = toString config.requires; }
|
// optionalAttrs (config.wants != [ ]) { Wants = toString config.wants; }
|
||||||
// optionalAttrs (config.wants != [])
|
// optionalAttrs (config.upholds != [ ]) { Upholds = toString config.upholds; }
|
||||||
{ Wants = toString config.wants; }
|
// optionalAttrs (config.after != [ ]) { After = toString config.after; }
|
||||||
// optionalAttrs (config.upholds != [])
|
// optionalAttrs (config.before != [ ]) { Before = toString config.before; }
|
||||||
{ Upholds = toString config.upholds; }
|
// optionalAttrs (config.bindsTo != [ ]) { BindsTo = toString config.bindsTo; }
|
||||||
// optionalAttrs (config.after != [])
|
// optionalAttrs (config.partOf != [ ]) { PartOf = toString config.partOf; }
|
||||||
{ After = toString config.after; }
|
// optionalAttrs (config.conflicts != [ ]) { Conflicts = toString config.conflicts; }
|
||||||
// optionalAttrs (config.before != [])
|
// optionalAttrs (config.requisite != [ ]) { Requisite = toString config.requisite; }
|
||||||
{ Before = toString config.before; }
|
// optionalAttrs (config ? restartTriggers && config.restartTriggers != [ ]) {
|
||||||
// optionalAttrs (config.bindsTo != [])
|
X-Restart-Triggers = "${pkgs.writeText "X-Restart-Triggers-${name}" (
|
||||||
{ BindsTo = toString config.bindsTo; }
|
pipe config.restartTriggers [
|
||||||
// optionalAttrs (config.partOf != [])
|
|
||||||
{ PartOf = toString config.partOf; }
|
|
||||||
// optionalAttrs (config.conflicts != [])
|
|
||||||
{ Conflicts = toString config.conflicts; }
|
|
||||||
// optionalAttrs (config.requisite != [])
|
|
||||||
{ Requisite = toString config.requisite; }
|
|
||||||
// optionalAttrs (config ? restartTriggers && config.restartTriggers != [])
|
|
||||||
{ X-Restart-Triggers = "${pkgs.writeText "X-Restart-Triggers-${name}" (pipe config.restartTriggers [
|
|
||||||
flatten
|
flatten
|
||||||
(map (x: if isPath x then "${x}" else x))
|
(map (x: if isPath x then "${x}" else x))
|
||||||
toString
|
toString
|
||||||
])}"; }
|
]
|
||||||
// optionalAttrs (config ? reloadTriggers && config.reloadTriggers != [])
|
)}";
|
||||||
{ X-Reload-Triggers = "${pkgs.writeText "X-Reload-Triggers-${name}" (pipe config.reloadTriggers [
|
}
|
||||||
|
// optionalAttrs (config ? reloadTriggers && config.reloadTriggers != [ ]) {
|
||||||
|
X-Reload-Triggers = "${pkgs.writeText "X-Reload-Triggers-${name}" (
|
||||||
|
pipe config.reloadTriggers [
|
||||||
flatten
|
flatten
|
||||||
(map (x: if isPath x then "${x}" else x))
|
(map (x: if isPath x then "${x}" else x))
|
||||||
toString
|
toString
|
||||||
])}"; }
|
]
|
||||||
|
)}";
|
||||||
|
}
|
||||||
// optionalAttrs (config.description != "") {
|
// optionalAttrs (config.description != "") {
|
||||||
Description = config.description; }
|
Description = config.description;
|
||||||
// optionalAttrs (config.documentation != []) {
|
}
|
||||||
Documentation = toString config.documentation; }
|
// optionalAttrs (config.documentation != [ ]) {
|
||||||
// optionalAttrs (config.onFailure != []) {
|
Documentation = toString config.documentation;
|
||||||
OnFailure = toString config.onFailure; }
|
}
|
||||||
// optionalAttrs (config.onSuccess != []) {
|
// optionalAttrs (config.onFailure != [ ]) {
|
||||||
OnSuccess = toString config.onSuccess; }
|
OnFailure = toString config.onFailure;
|
||||||
|
}
|
||||||
|
// optionalAttrs (config.onSuccess != [ ]) {
|
||||||
|
OnSuccess = toString config.onSuccess;
|
||||||
|
}
|
||||||
// optionalAttrs (options.startLimitIntervalSec.isDefined) {
|
// optionalAttrs (options.startLimitIntervalSec.isDefined) {
|
||||||
StartLimitIntervalSec = toString config.startLimitIntervalSec;
|
StartLimitIntervalSec = toString config.startLimitIntervalSec;
|
||||||
} // optionalAttrs (options.startLimitBurst.isDefined) {
|
}
|
||||||
|
// optionalAttrs (options.startLimitBurst.isDefined) {
|
||||||
StartLimitBurst = toString config.startLimitBurst;
|
StartLimitBurst = toString config.startLimitBurst;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -467,40 +625,58 @@ in rec {
|
||||||
let
|
let
|
||||||
nixosConfig = config;
|
nixosConfig = config;
|
||||||
in
|
in
|
||||||
{ name, lib, config, ... }: {
|
{
|
||||||
|
name,
|
||||||
|
lib,
|
||||||
|
config,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
{
|
||||||
config = {
|
config = {
|
||||||
name = "${name}.service";
|
name = "${name}.service";
|
||||||
environment.PATH = mkIf (config.path != []) "${makeBinPath config.path}:${makeSearchPathOutput "bin" "sbin" config.path}";
|
environment.PATH =
|
||||||
|
mkIf (config.path != [ ])
|
||||||
|
"${makeBinPath config.path}:${makeSearchPathOutput "bin" "sbin" config.path}";
|
||||||
|
|
||||||
enableStrictShellChecks = lib.mkOptionDefault nixosConfig.systemd.enableStrictShellChecks;
|
enableStrictShellChecks = lib.mkOptionDefault nixosConfig.systemd.enableStrictShellChecks;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
pathConfig = { name, config, ... }: {
|
pathConfig =
|
||||||
|
{ name, config, ... }:
|
||||||
|
{
|
||||||
config = {
|
config = {
|
||||||
name = "${name}.path";
|
name = "${name}.path";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
socketConfig = { name, config, ... }: {
|
socketConfig =
|
||||||
|
{ name, config, ... }:
|
||||||
|
{
|
||||||
config = {
|
config = {
|
||||||
name = "${name}.socket";
|
name = "${name}.socket";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
sliceConfig = { name, config, ... }: {
|
sliceConfig =
|
||||||
|
{ name, config, ... }:
|
||||||
|
{
|
||||||
config = {
|
config = {
|
||||||
name = "${name}.slice";
|
name = "${name}.slice";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
targetConfig = { name, config, ... }: {
|
targetConfig =
|
||||||
|
{ name, config, ... }:
|
||||||
|
{
|
||||||
config = {
|
config = {
|
||||||
name = "${name}.target";
|
name = "${name}.target";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
timerConfig = { name, config, ... }: {
|
timerConfig =
|
||||||
|
{ name, config, ... }:
|
||||||
|
{
|
||||||
config = {
|
config = {
|
||||||
name = "${name}.timer";
|
name = "${name}.timer";
|
||||||
};
|
};
|
||||||
|
@ -520,73 +696,128 @@ in rec {
|
||||||
|
|
||||||
stage1ServiceConfig = serviceConfig;
|
stage1ServiceConfig = serviceConfig;
|
||||||
|
|
||||||
mountConfig = { config, ... }: {
|
mountConfig =
|
||||||
|
{ config, ... }:
|
||||||
|
{
|
||||||
config = {
|
config = {
|
||||||
name = "${utils.escapeSystemdPath config.where}.mount";
|
name = "${utils.escapeSystemdPath config.where}.mount";
|
||||||
mountConfig =
|
mountConfig =
|
||||||
{ What = config.what;
|
{
|
||||||
|
What = config.what;
|
||||||
Where = config.where;
|
Where = config.where;
|
||||||
} // optionalAttrs (config.type != "") {
|
}
|
||||||
|
// optionalAttrs (config.type != "") {
|
||||||
Type = config.type;
|
Type = config.type;
|
||||||
} // optionalAttrs (config.options != "") {
|
}
|
||||||
|
// optionalAttrs (config.options != "") {
|
||||||
Options = config.options;
|
Options = config.options;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
automountConfig = { config, ... }: {
|
automountConfig =
|
||||||
|
{ config, ... }:
|
||||||
|
{
|
||||||
config = {
|
config = {
|
||||||
name = "${utils.escapeSystemdPath config.where}.automount";
|
name = "${utils.escapeSystemdPath config.where}.automount";
|
||||||
automountConfig =
|
automountConfig = {
|
||||||
{ Where = config.where;
|
Where = config.where;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
commonUnitText = def: lines: ''
|
commonUnitText =
|
||||||
|
def: lines:
|
||||||
|
''
|
||||||
[Unit]
|
[Unit]
|
||||||
${attrsToSection def.unitConfig}
|
${attrsToSection def.unitConfig}
|
||||||
'' + lines + optionalString (def.wantedBy != [ ]) ''
|
''
|
||||||
|
+ lines
|
||||||
|
+ optionalString (def.wantedBy != [ ]) ''
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=${concatStringsSep " " def.wantedBy}
|
WantedBy=${concatStringsSep " " def.wantedBy}
|
||||||
'';
|
'';
|
||||||
|
|
||||||
targetToUnit = def:
|
targetToUnit = def: {
|
||||||
{ inherit (def) name aliases wantedBy requiredBy upheldBy enable overrideStrategy;
|
inherit (def)
|
||||||
text =
|
name
|
||||||
''
|
aliases
|
||||||
|
wantedBy
|
||||||
|
requiredBy
|
||||||
|
upheldBy
|
||||||
|
enable
|
||||||
|
overrideStrategy
|
||||||
|
;
|
||||||
|
text = ''
|
||||||
[Unit]
|
[Unit]
|
||||||
${attrsToSection def.unitConfig}
|
${attrsToSection def.unitConfig}
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
serviceToUnit = def:
|
serviceToUnit = def: {
|
||||||
{ inherit (def) name aliases wantedBy requiredBy upheldBy enable overrideStrategy;
|
inherit (def)
|
||||||
text = commonUnitText def (''
|
name
|
||||||
|
aliases
|
||||||
|
wantedBy
|
||||||
|
requiredBy
|
||||||
|
upheldBy
|
||||||
|
enable
|
||||||
|
overrideStrategy
|
||||||
|
;
|
||||||
|
text = commonUnitText def (
|
||||||
|
''
|
||||||
[Service]
|
[Service]
|
||||||
'' + (let env = cfg.globalEnvironment // def.environment;
|
''
|
||||||
in concatMapStrings (n:
|
+ (
|
||||||
let s = optionalString (env.${n} != null)
|
let
|
||||||
"Environment=${toJSON "${n}=${env.${n}}"}\n";
|
env = cfg.globalEnvironment // def.environment;
|
||||||
|
in
|
||||||
|
concatMapStrings (
|
||||||
|
n:
|
||||||
|
let
|
||||||
|
s = optionalString (env.${n} != null) "Environment=${toJSON "${n}=${env.${n}}"}\n";
|
||||||
# systemd max line length is now 1MiB
|
# systemd max line length is now 1MiB
|
||||||
# https://github.com/systemd/systemd/commit/e6dde451a51dc5aaa7f4d98d39b8fe735f73d2af
|
# https://github.com/systemd/systemd/commit/e6dde451a51dc5aaa7f4d98d39b8fe735f73d2af
|
||||||
in if stringLength s >= 1048576 then throw "The value of the environment variable ‘${n}’ in systemd service ‘${def.name}.service’ is too long." else s) (attrNames env))
|
in
|
||||||
+ (if def ? reloadIfChanged && def.reloadIfChanged then ''
|
if stringLength s >= 1048576 then
|
||||||
|
throw "The value of the environment variable ‘${n}’ in systemd service ‘${def.name}.service’ is too long."
|
||||||
|
else
|
||||||
|
s
|
||||||
|
) (attrNames env)
|
||||||
|
)
|
||||||
|
+ (
|
||||||
|
if def ? reloadIfChanged && def.reloadIfChanged then
|
||||||
|
''
|
||||||
X-ReloadIfChanged=true
|
X-ReloadIfChanged=true
|
||||||
'' else if (def ? restartIfChanged && !def.restartIfChanged) then ''
|
''
|
||||||
|
else if (def ? restartIfChanged && !def.restartIfChanged) then
|
||||||
|
''
|
||||||
X-RestartIfChanged=false
|
X-RestartIfChanged=false
|
||||||
'' else "")
|
''
|
||||||
|
else
|
||||||
|
""
|
||||||
|
)
|
||||||
+ optionalString (def ? stopIfChanged && !def.stopIfChanged) ''
|
+ optionalString (def ? stopIfChanged && !def.stopIfChanged) ''
|
||||||
X-StopIfChanged=false
|
X-StopIfChanged=false
|
||||||
''
|
''
|
||||||
+ optionalString (def ? notSocketActivated && def.notSocketActivated) ''
|
+ optionalString (def ? notSocketActivated && def.notSocketActivated) ''
|
||||||
X-NotSocketActivated=true
|
X-NotSocketActivated=true
|
||||||
'' + attrsToSection def.serviceConfig);
|
''
|
||||||
|
+ attrsToSection def.serviceConfig
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
socketToUnit = def:
|
socketToUnit = def: {
|
||||||
{ inherit (def) name aliases wantedBy requiredBy upheldBy enable overrideStrategy;
|
inherit (def)
|
||||||
|
name
|
||||||
|
aliases
|
||||||
|
wantedBy
|
||||||
|
requiredBy
|
||||||
|
upheldBy
|
||||||
|
enable
|
||||||
|
overrideStrategy
|
||||||
|
;
|
||||||
text = commonUnitText def ''
|
text = commonUnitText def ''
|
||||||
[Socket]
|
[Socket]
|
||||||
${attrsToSection def.socketConfig}
|
${attrsToSection def.socketConfig}
|
||||||
|
@ -595,40 +826,80 @@ in rec {
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
timerToUnit = def:
|
timerToUnit = def: {
|
||||||
{ inherit (def) name aliases wantedBy requiredBy upheldBy enable overrideStrategy;
|
inherit (def)
|
||||||
|
name
|
||||||
|
aliases
|
||||||
|
wantedBy
|
||||||
|
requiredBy
|
||||||
|
upheldBy
|
||||||
|
enable
|
||||||
|
overrideStrategy
|
||||||
|
;
|
||||||
text = commonUnitText def ''
|
text = commonUnitText def ''
|
||||||
[Timer]
|
[Timer]
|
||||||
${attrsToSection def.timerConfig}
|
${attrsToSection def.timerConfig}
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
pathToUnit = def:
|
pathToUnit = def: {
|
||||||
{ inherit (def) name aliases wantedBy requiredBy upheldBy enable overrideStrategy;
|
inherit (def)
|
||||||
|
name
|
||||||
|
aliases
|
||||||
|
wantedBy
|
||||||
|
requiredBy
|
||||||
|
upheldBy
|
||||||
|
enable
|
||||||
|
overrideStrategy
|
||||||
|
;
|
||||||
text = commonUnitText def ''
|
text = commonUnitText def ''
|
||||||
[Path]
|
[Path]
|
||||||
${attrsToSection def.pathConfig}
|
${attrsToSection def.pathConfig}
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
mountToUnit = def:
|
mountToUnit = def: {
|
||||||
{ inherit (def) name aliases wantedBy requiredBy upheldBy enable overrideStrategy;
|
inherit (def)
|
||||||
|
name
|
||||||
|
aliases
|
||||||
|
wantedBy
|
||||||
|
requiredBy
|
||||||
|
upheldBy
|
||||||
|
enable
|
||||||
|
overrideStrategy
|
||||||
|
;
|
||||||
text = commonUnitText def ''
|
text = commonUnitText def ''
|
||||||
[Mount]
|
[Mount]
|
||||||
${attrsToSection def.mountConfig}
|
${attrsToSection def.mountConfig}
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
automountToUnit = def:
|
automountToUnit = def: {
|
||||||
{ inherit (def) name aliases wantedBy requiredBy upheldBy enable overrideStrategy;
|
inherit (def)
|
||||||
|
name
|
||||||
|
aliases
|
||||||
|
wantedBy
|
||||||
|
requiredBy
|
||||||
|
upheldBy
|
||||||
|
enable
|
||||||
|
overrideStrategy
|
||||||
|
;
|
||||||
text = commonUnitText def ''
|
text = commonUnitText def ''
|
||||||
[Automount]
|
[Automount]
|
||||||
${attrsToSection def.automountConfig}
|
${attrsToSection def.automountConfig}
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
sliceToUnit = def:
|
sliceToUnit = def: {
|
||||||
{ inherit (def) name aliases wantedBy requiredBy upheldBy enable overrideStrategy;
|
inherit (def)
|
||||||
|
name
|
||||||
|
aliases
|
||||||
|
wantedBy
|
||||||
|
requiredBy
|
||||||
|
upheldBy
|
||||||
|
enable
|
||||||
|
overrideStrategy
|
||||||
|
;
|
||||||
text = commonUnitText def ''
|
text = commonUnitText def ''
|
||||||
[Slice]
|
[Slice]
|
||||||
${attrsToSection def.sliceConfig}
|
${attrsToSection def.sliceConfig}
|
||||||
|
@ -638,17 +909,14 @@ in rec {
|
||||||
# Create a directory that contains systemd definition files from an attrset
|
# Create a directory that contains systemd definition files from an attrset
|
||||||
# that contains the file names as keys and the content as values. The values
|
# that contains the file names as keys and the content as values. The values
|
||||||
# in that attrset are determined by the supplied format.
|
# in that attrset are determined by the supplied format.
|
||||||
definitions = directoryName: format: definitionAttrs:
|
definitions =
|
||||||
|
directoryName: format: definitionAttrs:
|
||||||
let
|
let
|
||||||
listOfDefinitions = mapAttrsToList
|
listOfDefinitions = mapAttrsToList (name: format.generate "${name}.conf") definitionAttrs;
|
||||||
(name: format.generate "${name}.conf")
|
|
||||||
definitionAttrs;
|
|
||||||
in
|
in
|
||||||
pkgs.runCommand directoryName { } ''
|
pkgs.runCommand directoryName { } ''
|
||||||
mkdir -p $out
|
mkdir -p $out
|
||||||
${(concatStringsSep "\n"
|
${(concatStringsSep "\n" (map (pkg: "cp ${pkg} $out/${pkg.name}") listOfDefinitions))}
|
||||||
(map (pkg: "cp ${pkg} $out/${pkg.name}") listOfDefinitions)
|
|
||||||
)}
|
|
||||||
'';
|
'';
|
||||||
|
|
||||||
# The maximum number of characters allowed in a GPT partition label. This
|
# The maximum number of characters allowed in a GPT partition label. This
|
||||||
|
|
|
@ -30,24 +30,39 @@ let
|
||||||
|
|
||||||
checkService = checkUnitConfig "Service" [
|
checkService = checkUnitConfig "Service" [
|
||||||
(assertValueOneOf "Type" [
|
(assertValueOneOf "Type" [
|
||||||
"exec" "simple" "forking" "oneshot" "dbus" "notify" "notify-reload" "idle"
|
"exec"
|
||||||
|
"simple"
|
||||||
|
"forking"
|
||||||
|
"oneshot"
|
||||||
|
"dbus"
|
||||||
|
"notify"
|
||||||
|
"notify-reload"
|
||||||
|
"idle"
|
||||||
])
|
])
|
||||||
(assertValueOneOf "Restart" [
|
(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 {
|
unitOption = mkOptionType {
|
||||||
name = "systemd option";
|
name = "systemd option";
|
||||||
merge = loc: defs:
|
merge =
|
||||||
|
loc: defs:
|
||||||
let
|
let
|
||||||
defs' = filterOverrides defs;
|
defs' = filterOverrides defs;
|
||||||
in
|
in
|
||||||
if any (def: isList def.value) defs'
|
if any (def: isList def.value) defs' then
|
||||||
then concatMap (def: toList def.value) defs'
|
concatMap (def: toList def.value) defs'
|
||||||
else mergeEqualOption loc defs';
|
else
|
||||||
|
mergeEqualOption loc defs';
|
||||||
};
|
};
|
||||||
|
|
||||||
sharedOptions = {
|
sharedOptions = {
|
||||||
|
@ -76,7 +91,10 @@ in rec {
|
||||||
|
|
||||||
overrideStrategy = mkOption {
|
overrideStrategy = mkOption {
|
||||||
default = "asDropinIfExists";
|
default = "asDropinIfExists";
|
||||||
type = types.enum [ "asDropinIfExists" "asDropin" ];
|
type = types.enum [
|
||||||
|
"asDropinIfExists"
|
||||||
|
"asDropin"
|
||||||
|
];
|
||||||
description = ''
|
description = ''
|
||||||
Defines how unit configuration is provided for systemd:
|
Defines how unit configuration is provided for systemd:
|
||||||
|
|
||||||
|
@ -91,7 +109,7 @@ in rec {
|
||||||
};
|
};
|
||||||
|
|
||||||
requiredBy = mkOption {
|
requiredBy = mkOption {
|
||||||
default = [];
|
default = [ ];
|
||||||
type = types.listOf unitNameType;
|
type = types.listOf unitNameType;
|
||||||
description = ''
|
description = ''
|
||||||
Units that require (i.e. depend on and need to go down with) this unit.
|
Units that require (i.e. depend on and need to go down with) this unit.
|
||||||
|
@ -101,7 +119,7 @@ in rec {
|
||||||
};
|
};
|
||||||
|
|
||||||
upheldBy = mkOption {
|
upheldBy = mkOption {
|
||||||
default = [];
|
default = [ ];
|
||||||
type = types.listOf unitNameType;
|
type = types.listOf unitNameType;
|
||||||
description = ''
|
description = ''
|
||||||
Keep this unit running as long as the listed units are running. This is a continuously
|
Keep this unit running as long as the listed units are running. This is a continuously
|
||||||
|
@ -110,7 +128,7 @@ in rec {
|
||||||
};
|
};
|
||||||
|
|
||||||
wantedBy = mkOption {
|
wantedBy = mkOption {
|
||||||
default = [];
|
default = [ ];
|
||||||
type = types.listOf unitNameType;
|
type = types.listOf unitNameType;
|
||||||
description = ''
|
description = ''
|
||||||
Units that want (i.e. depend on) this unit. The default method for
|
Units that want (i.e. depend on) this unit. The default method for
|
||||||
|
@ -128,7 +146,7 @@ in rec {
|
||||||
};
|
};
|
||||||
|
|
||||||
aliases = mkOption {
|
aliases = mkOption {
|
||||||
default = [];
|
default = [ ];
|
||||||
type = types.listOf unitNameType;
|
type = types.listOf unitNameType;
|
||||||
description = "Aliases of that unit.";
|
description = "Aliases of that unit.";
|
||||||
};
|
};
|
||||||
|
@ -160,13 +178,13 @@ in rec {
|
||||||
};
|
};
|
||||||
|
|
||||||
documentation = mkOption {
|
documentation = mkOption {
|
||||||
default = [];
|
default = [ ];
|
||||||
type = types.listOf types.str;
|
type = types.listOf types.str;
|
||||||
description = "A list of URIs referencing documentation for this unit or its configuration.";
|
description = "A list of URIs referencing documentation for this unit or its configuration.";
|
||||||
};
|
};
|
||||||
|
|
||||||
requires = mkOption {
|
requires = mkOption {
|
||||||
default = [];
|
default = [ ];
|
||||||
type = types.listOf unitNameType;
|
type = types.listOf unitNameType;
|
||||||
description = ''
|
description = ''
|
||||||
Start the specified units when this unit is started, and stop
|
Start the specified units when this unit is started, and stop
|
||||||
|
@ -175,7 +193,7 @@ in rec {
|
||||||
};
|
};
|
||||||
|
|
||||||
wants = mkOption {
|
wants = mkOption {
|
||||||
default = [];
|
default = [ ];
|
||||||
type = types.listOf unitNameType;
|
type = types.listOf unitNameType;
|
||||||
description = ''
|
description = ''
|
||||||
Start the specified units when this unit is started.
|
Start the specified units when this unit is started.
|
||||||
|
@ -183,7 +201,7 @@ in rec {
|
||||||
};
|
};
|
||||||
|
|
||||||
upholds = mkOption {
|
upholds = mkOption {
|
||||||
default = [];
|
default = [ ];
|
||||||
type = types.listOf unitNameType;
|
type = types.listOf unitNameType;
|
||||||
description = ''
|
description = ''
|
||||||
Keeps the specified running while this unit is running. A continuous version of `wants`.
|
Keeps the specified running while this unit is running. A continuous version of `wants`.
|
||||||
|
@ -191,7 +209,7 @@ in rec {
|
||||||
};
|
};
|
||||||
|
|
||||||
after = mkOption {
|
after = mkOption {
|
||||||
default = [];
|
default = [ ];
|
||||||
type = types.listOf unitNameType;
|
type = types.listOf unitNameType;
|
||||||
description = ''
|
description = ''
|
||||||
If the specified units are started at the same time as
|
If the specified units are started at the same time as
|
||||||
|
@ -200,7 +218,7 @@ in rec {
|
||||||
};
|
};
|
||||||
|
|
||||||
before = mkOption {
|
before = mkOption {
|
||||||
default = [];
|
default = [ ];
|
||||||
type = types.listOf unitNameType;
|
type = types.listOf unitNameType;
|
||||||
description = ''
|
description = ''
|
||||||
If the specified units are started at the same time as
|
If the specified units are started at the same time as
|
||||||
|
@ -209,7 +227,7 @@ in rec {
|
||||||
};
|
};
|
||||||
|
|
||||||
bindsTo = mkOption {
|
bindsTo = mkOption {
|
||||||
default = [];
|
default = [ ];
|
||||||
type = types.listOf unitNameType;
|
type = types.listOf unitNameType;
|
||||||
description = ''
|
description = ''
|
||||||
Like ‘requires’, but in addition, if the specified units
|
Like ‘requires’, but in addition, if the specified units
|
||||||
|
@ -218,7 +236,7 @@ in rec {
|
||||||
};
|
};
|
||||||
|
|
||||||
partOf = mkOption {
|
partOf = mkOption {
|
||||||
default = [];
|
default = [ ];
|
||||||
type = types.listOf unitNameType;
|
type = types.listOf unitNameType;
|
||||||
description = ''
|
description = ''
|
||||||
If the specified units are stopped or restarted, then this
|
If the specified units are stopped or restarted, then this
|
||||||
|
@ -227,7 +245,7 @@ in rec {
|
||||||
};
|
};
|
||||||
|
|
||||||
conflicts = mkOption {
|
conflicts = mkOption {
|
||||||
default = [];
|
default = [ ];
|
||||||
type = types.listOf unitNameType;
|
type = types.listOf unitNameType;
|
||||||
description = ''
|
description = ''
|
||||||
If the specified units are started, then this unit is stopped
|
If the specified units are started, then this unit is stopped
|
||||||
|
@ -236,7 +254,7 @@ in rec {
|
||||||
};
|
};
|
||||||
|
|
||||||
requisite = mkOption {
|
requisite = mkOption {
|
||||||
default = [];
|
default = [ ];
|
||||||
type = types.listOf unitNameType;
|
type = types.listOf unitNameType;
|
||||||
description = ''
|
description = ''
|
||||||
Similar to requires. However if the units listed are not started,
|
Similar to requires. However if the units listed are not started,
|
||||||
|
@ -245,8 +263,10 @@ in rec {
|
||||||
};
|
};
|
||||||
|
|
||||||
unitConfig = mkOption {
|
unitConfig = mkOption {
|
||||||
default = {};
|
default = { };
|
||||||
example = { RequiresMountsFor = "/data"; };
|
example = {
|
||||||
|
RequiresMountsFor = "/data";
|
||||||
|
};
|
||||||
type = types.attrsOf unitOption;
|
type = types.attrsOf unitOption;
|
||||||
description = ''
|
description = ''
|
||||||
Each attribute in this set specifies an option in the
|
Each attribute in this set specifies an option in the
|
||||||
|
@ -256,7 +276,7 @@ in rec {
|
||||||
};
|
};
|
||||||
|
|
||||||
onFailure = mkOption {
|
onFailure = mkOption {
|
||||||
default = [];
|
default = [ ];
|
||||||
type = types.listOf unitNameType;
|
type = types.listOf unitNameType;
|
||||||
description = ''
|
description = ''
|
||||||
A list of one or more units that are activated when
|
A list of one or more units that are activated when
|
||||||
|
@ -265,7 +285,7 @@ in rec {
|
||||||
};
|
};
|
||||||
|
|
||||||
onSuccess = mkOption {
|
onSuccess = mkOption {
|
||||||
default = [];
|
default = [ ];
|
||||||
type = types.listOf unitNameType;
|
type = types.listOf unitNameType;
|
||||||
description = ''
|
description = ''
|
||||||
A list of one or more units that are activated when
|
A list of one or more units that are activated when
|
||||||
|
@ -301,7 +321,7 @@ in rec {
|
||||||
|
|
||||||
options = {
|
options = {
|
||||||
restartTriggers = mkOption {
|
restartTriggers = mkOption {
|
||||||
default = [];
|
default = [ ];
|
||||||
type = types.listOf types.unspecified;
|
type = types.listOf types.unspecified;
|
||||||
description = ''
|
description = ''
|
||||||
An arbitrary list of items such as derivations. If any item
|
An arbitrary list of items such as derivations. If any item
|
||||||
|
@ -311,7 +331,7 @@ in rec {
|
||||||
};
|
};
|
||||||
|
|
||||||
reloadTriggers = mkOption {
|
reloadTriggers = mkOption {
|
||||||
default = [];
|
default = [ ];
|
||||||
type = types.listOf unitOption;
|
type = types.listOf unitOption;
|
||||||
description = ''
|
description = ''
|
||||||
An arbitrary list of items such as derivations. If any item
|
An arbitrary list of items such as derivations. If any item
|
||||||
|
@ -324,19 +344,37 @@ in rec {
|
||||||
};
|
};
|
||||||
stage1CommonUnitOptions = commonUnitOptions;
|
stage1CommonUnitOptions = commonUnitOptions;
|
||||||
|
|
||||||
serviceOptions = { name, config, ... }: {
|
serviceOptions =
|
||||||
|
{ name, config, ... }:
|
||||||
|
{
|
||||||
options = {
|
options = {
|
||||||
|
|
||||||
environment = mkOption {
|
environment = mkOption {
|
||||||
default = {};
|
default = { };
|
||||||
type = with types; attrsOf (nullOr (oneOf [ str path package ]));
|
type =
|
||||||
example = { PATH = "/foo/bar/bin"; LANG = "nl_NL.UTF-8"; };
|
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.";
|
description = "Environment variables passed to the service's processes.";
|
||||||
};
|
};
|
||||||
|
|
||||||
path = mkOption {
|
path = mkOption {
|
||||||
default = [];
|
default = [ ];
|
||||||
type = with types; listOf (oneOf [ package str ]);
|
type =
|
||||||
|
with types;
|
||||||
|
listOf (oneOf [
|
||||||
|
package
|
||||||
|
str
|
||||||
|
]);
|
||||||
description = ''
|
description = ''
|
||||||
Packages added to the service's {env}`PATH`
|
Packages added to the service's {env}`PATH`
|
||||||
environment variable. Both the {file}`bin`
|
environment variable. Both the {file}`bin`
|
||||||
|
@ -346,9 +384,9 @@ in rec {
|
||||||
};
|
};
|
||||||
|
|
||||||
serviceConfig = mkOption {
|
serviceConfig = mkOption {
|
||||||
default = {};
|
default = { };
|
||||||
example =
|
example = {
|
||||||
{ RestartSec = 5;
|
RestartSec = 5;
|
||||||
};
|
};
|
||||||
type = types.addCheck (types.attrsOf unitOption) checkService;
|
type = types.addCheck (types.attrsOf unitOption) checkService;
|
||||||
description = ''
|
description = ''
|
||||||
|
@ -430,7 +468,7 @@ in rec {
|
||||||
type = with types; coercedTo path singleton (listOf path);
|
type = with types; coercedTo path singleton (listOf path);
|
||||||
internal = true;
|
internal = true;
|
||||||
description = "A list of all job script derivations of this unit.";
|
description = "A list of all job script derivations of this unit.";
|
||||||
default = [];
|
default = [ ];
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -549,7 +587,7 @@ in rec {
|
||||||
|
|
||||||
startAt = mkOption {
|
startAt = mkOption {
|
||||||
type = with types; either str (listOf str);
|
type = with types; either str (listOf str);
|
||||||
default = [];
|
default = [ ];
|
||||||
example = "Sun 14:00:00";
|
example = "Sun 14:00:00";
|
||||||
description = ''
|
description = ''
|
||||||
Automatically start this unit at the given date/time, which
|
Automatically start this unit at the given date/time, which
|
||||||
|
@ -570,14 +608,16 @@ in rec {
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
socketOptions = {
|
socketOptions = {
|
||||||
options = {
|
options = {
|
||||||
|
|
||||||
listenStreams = mkOption {
|
listenStreams = mkOption {
|
||||||
default = [];
|
default = [ ];
|
||||||
type = types.listOf types.str;
|
type = types.listOf types.str;
|
||||||
example = [ "0.0.0.0:993" "/run/my-socket" ];
|
example = [
|
||||||
|
"0.0.0.0:993"
|
||||||
|
"/run/my-socket"
|
||||||
|
];
|
||||||
description = ''
|
description = ''
|
||||||
For each item in this list, a `ListenStream`
|
For each item in this list, a `ListenStream`
|
||||||
option in the `[Socket]` section will be created.
|
option in the `[Socket]` section will be created.
|
||||||
|
@ -585,9 +625,12 @@ in rec {
|
||||||
};
|
};
|
||||||
|
|
||||||
listenDatagrams = mkOption {
|
listenDatagrams = mkOption {
|
||||||
default = [];
|
default = [ ];
|
||||||
type = types.listOf types.str;
|
type = types.listOf types.str;
|
||||||
example = [ "0.0.0.0:993" "/run/my-socket" ];
|
example = [
|
||||||
|
"0.0.0.0:993"
|
||||||
|
"/run/my-socket"
|
||||||
|
];
|
||||||
description = ''
|
description = ''
|
||||||
For each item in this list, a `ListenDatagram`
|
For each item in this list, a `ListenDatagram`
|
||||||
option in the `[Socket]` section will be created.
|
option in the `[Socket]` section will be created.
|
||||||
|
@ -595,8 +638,10 @@ in rec {
|
||||||
};
|
};
|
||||||
|
|
||||||
socketConfig = mkOption {
|
socketConfig = mkOption {
|
||||||
default = {};
|
default = { };
|
||||||
example = { ListenStream = "/run/my-socket"; };
|
example = {
|
||||||
|
ListenStream = "/run/my-socket";
|
||||||
|
};
|
||||||
type = types.attrsOf unitOption;
|
type = types.attrsOf unitOption;
|
||||||
description = ''
|
description = ''
|
||||||
Each attribute in this set specifies an option in the
|
Each attribute in this set specifies an option in the
|
||||||
|
@ -622,13 +667,15 @@ in rec {
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
timerOptions = {
|
timerOptions = {
|
||||||
options = {
|
options = {
|
||||||
|
|
||||||
timerConfig = mkOption {
|
timerConfig = mkOption {
|
||||||
default = {};
|
default = { };
|
||||||
example = { OnCalendar = "Sun 14:00:00"; Unit = "foo.service"; };
|
example = {
|
||||||
|
OnCalendar = "Sun 14:00:00";
|
||||||
|
Unit = "foo.service";
|
||||||
|
};
|
||||||
type = types.attrsOf unitOption;
|
type = types.attrsOf unitOption;
|
||||||
description = ''
|
description = ''
|
||||||
Each attribute in this set specifies an option in the
|
Each attribute in this set specifies an option in the
|
||||||
|
@ -655,13 +702,15 @@ in rec {
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
pathOptions = {
|
pathOptions = {
|
||||||
options = {
|
options = {
|
||||||
|
|
||||||
pathConfig = mkOption {
|
pathConfig = mkOption {
|
||||||
default = {};
|
default = { };
|
||||||
example = { PathChanged = "/some/path"; Unit = "changedpath.service"; };
|
example = {
|
||||||
|
PathChanged = "/some/path";
|
||||||
|
Unit = "changedpath.service";
|
||||||
|
};
|
||||||
type = types.attrsOf unitOption;
|
type = types.attrsOf unitOption;
|
||||||
description = ''
|
description = ''
|
||||||
Each attribute in this set specifies an option in the
|
Each attribute in this set specifies an option in the
|
||||||
|
@ -687,7 +736,6 @@ in rec {
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
mountOptions = {
|
mountOptions = {
|
||||||
options = {
|
options = {
|
||||||
|
|
||||||
|
@ -721,8 +769,10 @@ in rec {
|
||||||
};
|
};
|
||||||
|
|
||||||
mountConfig = mkOption {
|
mountConfig = mkOption {
|
||||||
default = {};
|
default = { };
|
||||||
example = { DirectoryMode = "0775"; };
|
example = {
|
||||||
|
DirectoryMode = "0775";
|
||||||
|
};
|
||||||
type = types.attrsOf unitOption;
|
type = types.attrsOf unitOption;
|
||||||
description = ''
|
description = ''
|
||||||
Each attribute in this set specifies an option in the
|
Each attribute in this set specifies an option in the
|
||||||
|
@ -761,8 +811,10 @@ in rec {
|
||||||
};
|
};
|
||||||
|
|
||||||
automountConfig = mkOption {
|
automountConfig = mkOption {
|
||||||
default = {};
|
default = { };
|
||||||
example = { DirectoryMode = "0775"; };
|
example = {
|
||||||
|
DirectoryMode = "0775";
|
||||||
|
};
|
||||||
type = types.attrsOf unitOption;
|
type = types.attrsOf unitOption;
|
||||||
description = ''
|
description = ''
|
||||||
Each attribute in this set specifies an option in the
|
Each attribute in this set specifies an option in the
|
||||||
|
@ -792,8 +844,10 @@ in rec {
|
||||||
options = {
|
options = {
|
||||||
|
|
||||||
sliceConfig = mkOption {
|
sliceConfig = mkOption {
|
||||||
default = {};
|
default = { };
|
||||||
example = { MemoryMax = "2G"; };
|
example = {
|
||||||
|
MemoryMax = "2G";
|
||||||
|
};
|
||||||
type = types.attrsOf unitOption;
|
type = types.attrsOf unitOption;
|
||||||
description = ''
|
description = ''
|
||||||
Each attribute in this set specifies an option in the
|
Each attribute in this set specifies an option in the
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
{ config, lib, hostPkgs, ... }:
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
hostPkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
let
|
let
|
||||||
inherit (lib) mkOption types literalMD;
|
inherit (lib) mkOption types literalMD;
|
||||||
|
|
||||||
|
@ -11,10 +16,9 @@ let
|
||||||
tesseract4 = hostPkgs.tesseract4.override { enableLanguages = [ "eng" ]; };
|
tesseract4 = hostPkgs.tesseract4.override { enableLanguages = [ "eng" ]; };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
vlans = map (
|
||||||
vlans = map (m: (
|
m: (m.virtualisation.vlans ++ (lib.mapAttrsToList (_: v: v.vlan) m.virtualisation.interfaces))
|
||||||
m.virtualisation.vlans ++
|
) (lib.attrValues config.nodes);
|
||||||
(lib.mapAttrsToList (_: v: v.vlan) m.virtualisation.interfaces))) (lib.attrValues config.nodes);
|
|
||||||
vms = map (m: m.system.build.vm) (lib.attrValues config.nodes);
|
vms = map (m: m.system.build.vm) (lib.attrValues config.nodes);
|
||||||
|
|
||||||
nodeHostNames =
|
nodeHostNames =
|
||||||
|
@ -23,13 +27,14 @@ let
|
||||||
in
|
in
|
||||||
nodesList ++ lib.optional (lib.length nodesList == 1 && !lib.elem "machine" nodesList) "machine";
|
nodesList ++ lib.optional (lib.length nodesList == 1 && !lib.elem "machine" nodesList) "machine";
|
||||||
|
|
||||||
pythonizeName = name:
|
pythonizeName =
|
||||||
|
name:
|
||||||
let
|
let
|
||||||
head = lib.substring 0 1 name;
|
head = lib.substring 0 1 name;
|
||||||
tail = lib.substring 1 (-1) name;
|
tail = lib.substring 1 (-1) name;
|
||||||
in
|
in
|
||||||
(if builtins.match "[A-z_]" head == null then "_" else head) +
|
(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;
|
+ lib.stringAsChars (c: if builtins.match "[A-z0-9_]" c == null then "_" else c) tail;
|
||||||
|
|
||||||
uniqueVlans = lib.unique (builtins.concatLists vlans);
|
uniqueVlans = lib.unique (builtins.concatLists vlans);
|
||||||
vlanNames = map (i: "vlan${toString i}: VLan;") uniqueVlans;
|
vlanNames = map (i: "vlan${toString i}: VLan;") uniqueVlans;
|
||||||
|
@ -96,7 +101,12 @@ let
|
||||||
--set testScript "$out/test-script" \
|
--set testScript "$out/test-script" \
|
||||||
--set globalTimeout "${toString config.globalTimeout}" \
|
--set globalTimeout "${toString config.globalTimeout}" \
|
||||||
--set vlans '${toString vlans}' \
|
--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
|
in
|
||||||
|
@ -165,7 +175,7 @@ in
|
||||||
They become part of [{option}`driver`](#test-opt-driver) via `wrapProgram`.
|
They become part of [{option}`driver`](#test-opt-driver) via `wrapProgram`.
|
||||||
'';
|
'';
|
||||||
type = types.listOf types.str;
|
type = types.listOf types.str;
|
||||||
default = [];
|
default = [ ];
|
||||||
};
|
};
|
||||||
|
|
||||||
skipLint = mkOption {
|
skipLint = mkOption {
|
||||||
|
@ -191,8 +201,7 @@ in
|
||||||
_module.args = {
|
_module.args = {
|
||||||
hostPkgs =
|
hostPkgs =
|
||||||
# Comment is in nixos/modules/misc/nixpkgs.nix
|
# Comment is in nixos/modules/misc/nixpkgs.nix
|
||||||
lib.mkOverride lib.modules.defaultOverridePriority
|
lib.mkOverride lib.modules.defaultOverridePriority config.hostPkgs.__splicedPackages;
|
||||||
config.hostPkgs.__splicedPackages;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
driver = withChecks driver;
|
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 ]; }"
|
# 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
|
let
|
||||||
copyChannel = true;
|
copyChannel = true;
|
||||||
format = "qcow2";
|
format = "qcow2";
|
||||||
|
@ -17,12 +22,16 @@ in
|
||||||
system.nixos.tags = [ "openstack" ];
|
system.nixos.tags = [ "openstack" ];
|
||||||
system.build.image = config.system.build.openstackImage;
|
system.build.image = config.system.build.openstackImage;
|
||||||
system.build.openstackImage = import ../../../lib/make-disk-image.nix {
|
system.build.openstackImage = import ../../../lib/make-disk-image.nix {
|
||||||
inherit lib config copyChannel format;
|
inherit
|
||||||
|
lib
|
||||||
|
config
|
||||||
|
copyChannel
|
||||||
|
format
|
||||||
|
;
|
||||||
inherit (config.image) baseName;
|
inherit (config.image) baseName;
|
||||||
additionalSpace = "1024M";
|
additionalSpace = "1024M";
|
||||||
pkgs = import ../../../.. { inherit (pkgs) system; }; # ensure we use the regular qemu-kvm package
|
pkgs = import ../../../.. { inherit (pkgs) system; }; # ensure we use the regular qemu-kvm package
|
||||||
configFile = pkgs.writeText "configuration.nix"
|
configFile = pkgs.writeText "configuration.nix" ''
|
||||||
''
|
|
||||||
{
|
{
|
||||||
imports = [ <nixpkgs/nixos/modules/virtualisation/openstack-config.nix> ];
|
imports = [ <nixpkgs/nixos/modules/virtualisation/openstack-config.nix> ];
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,24 @@
|
||||||
{ config, lib, pkgs, ... }:
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
let
|
let
|
||||||
cfg = config.console;
|
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);
|
isUnicode = lib.hasSuffix "UTF-8" (lib.toUpper config.i18n.defaultLocale);
|
||||||
|
|
||||||
optimizedKeymap = pkgs.runCommand "keymap" {
|
optimizedKeymap =
|
||||||
|
pkgs.runCommand "keymap"
|
||||||
|
{
|
||||||
nativeBuildInputs = [ pkgs.buildPackages.kbd ];
|
nativeBuildInputs = [ pkgs.buildPackages.kbd ];
|
||||||
LOADKEYS_KEYMAP_PATH = "${consoleEnv pkgs.kbd}/share/keymaps/**";
|
LOADKEYS_KEYMAP_PATH = "${consoleEnv pkgs.kbd}/share/keymaps/**";
|
||||||
preferLocalBuild = true;
|
preferLocalBuild = true;
|
||||||
} ''
|
}
|
||||||
|
''
|
||||||
loadkeys -b ${lib.optionalString isUnicode "-u"} "${cfg.keyMap}" > $out
|
loadkeys -b ${lib.optionalString isUnicode "-u"} "${cfg.keyMap}" > $out
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
@ -20,7 +28,9 @@ let
|
||||||
${lib.optionalString (cfg.font != null) "FONT=${cfg.font}"}
|
${lib.optionalString (cfg.font != null) "FONT=${cfg.font}"}
|
||||||
'';
|
'';
|
||||||
|
|
||||||
consoleEnv = kbd: pkgs.buildEnv {
|
consoleEnv =
|
||||||
|
kbd:
|
||||||
|
pkgs.buildEnv {
|
||||||
name = "console-env";
|
name = "console-env";
|
||||||
paths = [ kbd ] ++ cfg.packages;
|
paths = [ kbd ] ++ cfg.packages;
|
||||||
pathsToLink = [
|
pathsToLink = [
|
||||||
|
@ -70,10 +80,22 @@ in
|
||||||
type = with lib.types; listOf (strMatching "[[:xdigit:]]{6}");
|
type = with lib.types; listOf (strMatching "[[:xdigit:]]{6}");
|
||||||
default = [ ];
|
default = [ ];
|
||||||
example = [
|
example = [
|
||||||
"002b36" "dc322f" "859900" "b58900"
|
"002b36"
|
||||||
"268bd2" "d33682" "2aa198" "eee8d5"
|
"dc322f"
|
||||||
"002b36" "cb4b16" "586e75" "657b83"
|
"859900"
|
||||||
"839496" "6c71c4" "93a1a1" "fdf6e3"
|
"b58900"
|
||||||
|
"268bd2"
|
||||||
|
"d33682"
|
||||||
|
"2aa198"
|
||||||
|
"eee8d5"
|
||||||
|
"002b36"
|
||||||
|
"cb4b16"
|
||||||
|
"586e75"
|
||||||
|
"657b83"
|
||||||
|
"839496"
|
||||||
|
"6c71c4"
|
||||||
|
"93a1a1"
|
||||||
|
"fdf6e3"
|
||||||
];
|
];
|
||||||
description = ''
|
description = ''
|
||||||
The 16 colors palette used by the virtual consoles.
|
The 16 colors palette used by the virtual consoles.
|
||||||
|
@ -112,24 +134,30 @@ in
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
###### implementation
|
###### implementation
|
||||||
|
|
||||||
config = lib.mkMerge [
|
config = lib.mkMerge [
|
||||||
{ console.keyMap = with config.services.xserver;
|
{
|
||||||
lib.mkIf cfg.useXkbConfig
|
console.keyMap =
|
||||||
(pkgs.runCommand "xkb-console-keymap" { preferLocalBuild = true; } ''
|
with config.services.xserver;
|
||||||
|
lib.mkIf cfg.useXkbConfig (
|
||||||
|
pkgs.runCommand "xkb-console-keymap" { preferLocalBuild = true; } ''
|
||||||
'${pkgs.buildPackages.ckbcomp}/bin/ckbcomp' \
|
'${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}' \
|
-model '${xkb.model}' -layout '${xkb.layout}' \
|
||||||
-option '${xkb.options}' -variant '${xkb.variant}' > "$out"
|
-option '${xkb.options}' -variant '${xkb.variant}' > "$out"
|
||||||
'');
|
''
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
(lib.mkIf cfg.enable (lib.mkMerge [
|
(lib.mkIf cfg.enable (
|
||||||
{ environment.systemPackages = [ pkgs.kbd ];
|
lib.mkMerge [
|
||||||
|
{
|
||||||
|
environment.systemPackages = [ pkgs.kbd ];
|
||||||
|
|
||||||
# Let systemd-vconsole-setup.service do the work of setting up the
|
# Let systemd-vconsole-setup.service do the work of setting up the
|
||||||
# virtual consoles.
|
# virtual consoles.
|
||||||
|
@ -137,7 +165,8 @@ in
|
||||||
# Provide kbd with additional packages.
|
# Provide kbd with additional packages.
|
||||||
environment.etc.kbd.source = "${consoleEnv pkgs.kbd}/share";
|
environment.etc.kbd.source = "${consoleEnv pkgs.kbd}/share";
|
||||||
|
|
||||||
boot.initrd.preLVMCommands = lib.mkIf (!config.boot.initrd.systemd.enable) (lib.mkBefore ''
|
boot.initrd.preLVMCommands = lib.mkIf (!config.boot.initrd.systemd.enable) (
|
||||||
|
lib.mkBefore ''
|
||||||
kbd_mode ${if isUnicode then "-u" else "-a"} -C /dev/console
|
kbd_mode ${if isUnicode then "-u" else "-a"} -C /dev/console
|
||||||
printf "\033%%${if isUnicode then "G" else "@"}" >> /dev/console
|
printf "\033%%${if isUnicode then "G" else "@"}" >> /dev/console
|
||||||
loadkmap < ${optimizedKeymap}
|
loadkmap < ${optimizedKeymap}
|
||||||
|
@ -145,26 +174,34 @@ in
|
||||||
${lib.optionalString (cfg.earlySetup && cfg.font != null) ''
|
${lib.optionalString (cfg.earlySetup && cfg.font != null) ''
|
||||||
setfont -C /dev/console $extraUtils/share/consolefonts/font.psf
|
setfont -C /dev/console $extraUtils/share/consolefonts/font.psf
|
||||||
''}
|
''}
|
||||||
'');
|
''
|
||||||
|
);
|
||||||
|
|
||||||
boot.initrd.systemd.contents = {
|
boot.initrd.systemd.contents = {
|
||||||
"/etc/vconsole.conf".source = vconsoleConf;
|
"/etc/vconsole.conf".source = vconsoleConf;
|
||||||
# Add everything if we want full console setup...
|
# Add everything if we want full console setup...
|
||||||
"/etc/kbd" = lib.mkIf cfg.earlySetup { source = "${consoleEnv config.boot.initrd.systemd.package.kbd}/share"; };
|
"/etc/kbd" = lib.mkIf cfg.earlySetup {
|
||||||
|
source = "${consoleEnv config.boot.initrd.systemd.package.kbd}/share";
|
||||||
|
};
|
||||||
# ...but only the keymaps if we don't
|
# ...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"; };
|
"/etc/kbd/keymaps" = lib.mkIf (!cfg.earlySetup) {
|
||||||
|
source = "${consoleEnv config.boot.initrd.systemd.package.kbd}/share/keymaps";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
boot.initrd.systemd.additionalUpstreamUnits = [
|
boot.initrd.systemd.additionalUpstreamUnits = [
|
||||||
"systemd-vconsole-setup.service"
|
"systemd-vconsole-setup.service"
|
||||||
];
|
];
|
||||||
boot.initrd.systemd.storePaths = [
|
boot.initrd.systemd.storePaths =
|
||||||
|
[
|
||||||
"${config.boot.initrd.systemd.package}/lib/systemd/systemd-vconsole-setup"
|
"${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/setfont"
|
||||||
"${config.boot.initrd.systemd.package.kbd}/bin/loadkeys"
|
"${config.boot.initrd.systemd.package.kbd}/bin/loadkeys"
|
||||||
"${config.boot.initrd.systemd.package.kbd.gzip}/bin/gzip" # Fonts and keyboard layouts are compressed
|
"${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) [
|
]
|
||||||
|
++ lib.optionals (cfg.font != null && lib.hasPrefix builtins.storeDir cfg.font) [
|
||||||
"${cfg.font}"
|
"${cfg.font}"
|
||||||
] ++ lib.optionals (lib.hasPrefix builtins.storeDir cfg.keyMap) [
|
]
|
||||||
|
++ lib.optionals (lib.hasPrefix builtins.storeDir cfg.keyMap) [
|
||||||
"${cfg.keyMap}"
|
"${cfg.keyMap}"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -172,20 +209,23 @@ in
|
||||||
"systemd-vconsole-setup.service"
|
"systemd-vconsole-setup.service"
|
||||||
];
|
];
|
||||||
|
|
||||||
systemd.services.reload-systemd-vconsole-setup =
|
systemd.services.reload-systemd-vconsole-setup = {
|
||||||
{ description = "Reset console on configuration changes";
|
description = "Reset console on configuration changes";
|
||||||
wantedBy = [ "multi-user.target" ];
|
wantedBy = [ "multi-user.target" ];
|
||||||
restartTriggers = [ vconsoleConf (consoleEnv pkgs.kbd) ];
|
restartTriggers = [
|
||||||
|
vconsoleConf
|
||||||
|
(consoleEnv pkgs.kbd)
|
||||||
|
];
|
||||||
reloadIfChanged = true;
|
reloadIfChanged = true;
|
||||||
serviceConfig =
|
serviceConfig = {
|
||||||
{ RemainAfterExit = true;
|
RemainAfterExit = true;
|
||||||
ExecStart = "${pkgs.coreutils}/bin/true";
|
ExecStart = "${pkgs.coreutils}/bin/true";
|
||||||
ExecReload = "/run/current-system/systemd/bin/systemctl restart systemd-vconsole-setup";
|
ExecReload = "/run/current-system/systemd/bin/systemctl restart systemd-vconsole-setup";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
(lib.mkIf (cfg.colors != []) {
|
(lib.mkIf (cfg.colors != [ ]) {
|
||||||
boot.kernelParams = [
|
boot.kernelParams = [
|
||||||
"vt.default_red=${makeColor 0 cfg.colors}"
|
"vt.default_red=${makeColor 0 cfg.colors}"
|
||||||
"vt.default_grn=${makeColor 1 cfg.colors}"
|
"vt.default_grn=${makeColor 1 cfg.colors}"
|
||||||
|
@ -196,11 +236,16 @@ in
|
||||||
(lib.mkIf (cfg.earlySetup && cfg.font != null && !config.boot.initrd.systemd.enable) {
|
(lib.mkIf (cfg.earlySetup && cfg.font != null && !config.boot.initrd.systemd.enable) {
|
||||||
boot.initrd.extraUtilsCommands = ''
|
boot.initrd.extraUtilsCommands = ''
|
||||||
mkdir -p $out/share/consolefonts
|
mkdir -p $out/share/consolefonts
|
||||||
${if lib.substring 0 1 cfg.font == "/" then ''
|
${
|
||||||
|
if lib.substring 0 1 cfg.font == "/" then
|
||||||
|
''
|
||||||
font="${cfg.font}"
|
font="${cfg.font}"
|
||||||
'' else ''
|
''
|
||||||
|
else
|
||||||
|
''
|
||||||
font="$(echo ${consoleEnv pkgs.kbd}/share/consolefonts/${cfg.font}.*)"
|
font="$(echo ${consoleEnv pkgs.kbd}/share/consolefonts/${cfg.font}.*)"
|
||||||
''}
|
''
|
||||||
|
}
|
||||||
if [[ $font == *.gz ]]; then
|
if [[ $font == *.gz ]]; then
|
||||||
gzip -cd $font > $out/share/consolefonts/font.psf
|
gzip -cd $font > $out/share/consolefonts/font.psf
|
||||||
else
|
else
|
||||||
|
@ -208,7 +253,8 @@ in
|
||||||
fi
|
fi
|
||||||
'';
|
'';
|
||||||
})
|
})
|
||||||
]))
|
]
|
||||||
|
))
|
||||||
];
|
];
|
||||||
|
|
||||||
imports = [
|
imports = [
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
{ config, lib, pkgs, ... }:
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
{
|
{
|
||||||
###### interface
|
###### interface
|
||||||
|
|
||||||
|
@ -39,8 +44,11 @@
|
||||||
|
|
||||||
extraLocaleSettings = lib.mkOption {
|
extraLocaleSettings = lib.mkOption {
|
||||||
type = lib.types.attrsOf lib.types.str;
|
type = lib.types.attrsOf lib.types.str;
|
||||||
default = {};
|
default = { };
|
||||||
example = { LC_MESSAGES = "en_US.UTF-8"; LC_TIME = "de_DE.UTF-8"; };
|
example = {
|
||||||
|
LC_MESSAGES = "en_US.UTF-8";
|
||||||
|
LC_TIME = "de_DE.UTF-8";
|
||||||
|
};
|
||||||
description = ''
|
description = ''
|
||||||
A set of additional system-wide locale settings other than
|
A set of additional system-wide locale settings other than
|
||||||
`LANG` which can be configured with
|
`LANG` which can be configured with
|
||||||
|
@ -50,14 +58,18 @@
|
||||||
|
|
||||||
supportedLocales = lib.mkOption {
|
supportedLocales = lib.mkOption {
|
||||||
type = lib.types.listOf lib.types.str;
|
type = lib.types.listOf lib.types.str;
|
||||||
default = lib.unique
|
default = lib.unique (
|
||||||
(builtins.map (l: (lib.replaceStrings [ "utf8" "utf-8" "UTF8" ] [ "UTF-8" "UTF-8" "UTF-8" ] l) + "/UTF-8") (
|
builtins.map
|
||||||
|
(l: (lib.replaceStrings [ "utf8" "utf-8" "UTF8" ] [ "UTF-8" "UTF-8" "UTF-8" ] l) + "/UTF-8")
|
||||||
|
(
|
||||||
[
|
[
|
||||||
"C.UTF-8"
|
"C.UTF-8"
|
||||||
"en_US.UTF-8"
|
"en_US.UTF-8"
|
||||||
config.i18n.defaultLocale
|
config.i18n.defaultLocale
|
||||||
] ++ (lib.attrValues (lib.filterAttrs (n: v: n != "LANGUAGE") config.i18n.extraLocaleSettings))
|
]
|
||||||
));
|
++ (lib.attrValues (lib.filterAttrs (n: v: n != "LANGUAGE") config.i18n.extraLocaleSettings))
|
||||||
|
)
|
||||||
|
);
|
||||||
defaultText = lib.literalExpression ''
|
defaultText = lib.literalExpression ''
|
||||||
lib.unique
|
lib.unique
|
||||||
(builtins.map (l: (lib.replaceStrings [ "utf8" "utf-8" "UTF8" ] [ "UTF-8" "UTF-8" "UTF-8" ] l) + "/UTF-8") (
|
(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))
|
] ++ (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 = ''
|
description = ''
|
||||||
List of locales that the system should support. The value
|
List of locales that the system should support. The value
|
||||||
`"all"` means that all locales supported by
|
`"all"` means that all locales supported by
|
||||||
|
@ -81,29 +97,29 @@
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
###### implementation
|
###### implementation
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
|
|
||||||
environment.systemPackages =
|
environment.systemPackages =
|
||||||
# We increase the priority a little, so that plain glibc in systemPackages can't win.
|
# 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 =
|
environment.sessionVariables = {
|
||||||
{ LANG = config.i18n.defaultLocale;
|
LANG = config.i18n.defaultLocale;
|
||||||
LOCALE_ARCHIVE = "/run/current-system/sw/lib/locale/locale-archive";
|
LOCALE_ARCHIVE = "/run/current-system/sw/lib/locale/locale-archive";
|
||||||
} // config.i18n.extraLocaleSettings;
|
} // 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";
|
LOCALE_ARCHIVE = "${config.i18n.glibcLocales}/lib/locale/locale-archive";
|
||||||
};
|
};
|
||||||
|
|
||||||
# ‘/etc/locale.conf’ is used by systemd.
|
# ‘/etc/locale.conf’ is used by systemd.
|
||||||
environment.etc."locale.conf".source = pkgs.writeText "locale.conf"
|
environment.etc."locale.conf".source = pkgs.writeText "locale.conf" ''
|
||||||
''
|
|
||||||
LANG=${config.i18n.defaultLocale}
|
LANG=${config.i18n.defaultLocale}
|
||||||
${lib.concatStringsSep "\n" (lib.mapAttrsToList (n: v: "${n}=${v}") config.i18n.extraLocaleSettings)}
|
${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.
|
# /etc files related to networking, such as /etc/services.
|
||||||
{ config, lib, options, pkgs, ... }:
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
options,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
let
|
let
|
||||||
|
|
||||||
cfg = config.networking;
|
cfg = config.networking;
|
||||||
opt = options.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
|
in
|
||||||
|
|
||||||
|
@ -136,7 +149,7 @@ in
|
||||||
envVars = lib.mkOption {
|
envVars = lib.mkOption {
|
||||||
type = lib.types.attrs;
|
type = lib.types.attrs;
|
||||||
internal = true;
|
internal = true;
|
||||||
default = {};
|
default = { };
|
||||||
description = ''
|
description = ''
|
||||||
Environment variables used for the network proxy.
|
Environment variables used for the network proxy.
|
||||||
'';
|
'';
|
||||||
|
@ -146,26 +159,31 @@ in
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
|
|
||||||
assertions = [{
|
assertions = [
|
||||||
|
{
|
||||||
assertion = !localhostMultiple;
|
assertion = !localhostMultiple;
|
||||||
message = ''
|
message = ''
|
||||||
`networking.hosts` maps "localhost" to something other than "127.0.0.1"
|
`networking.hosts` maps "localhost" to something other than "127.0.0.1"
|
||||||
or "::1". This will break some applications. Please use
|
or "::1". This will break some applications. Please use
|
||||||
`networking.extraHosts` if you really want to add such a mapping.
|
`networking.extraHosts` if you really want to add such a mapping.
|
||||||
'';
|
'';
|
||||||
}];
|
}
|
||||||
|
];
|
||||||
|
|
||||||
# These entries are required for "hostname -f" and to resolve both the
|
# These entries are required for "hostname -f" and to resolve both the
|
||||||
# hostname and FQDN correctly:
|
# hostname and FQDN correctly:
|
||||||
networking.hosts = let
|
networking.hosts =
|
||||||
|
let
|
||||||
hostnames = # Note: The FQDN (canonical hostname) has to come first:
|
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.domain != null) "${cfg.hostName}.${cfg.domain}"
|
||||||
++ lib.optional (cfg.hostName != "") cfg.hostName; # Then the hostname (without the domain)
|
++ lib.optional (cfg.hostName != "") cfg.hostName; # Then the hostname (without the domain)
|
||||||
in {
|
in
|
||||||
|
{
|
||||||
"127.0.0.2" = hostnames;
|
"127.0.0.2" = hostnames;
|
||||||
};
|
};
|
||||||
|
|
||||||
networking.hostFiles = let
|
networking.hostFiles =
|
||||||
|
let
|
||||||
# Note: localhostHosts has to appear first in /etc/hosts so that 127.0.0.1
|
# 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
|
# resolves back to "localhost" (as some applications assume) instead of
|
||||||
# the FQDN! By default "networking.hosts" also contains entries for the
|
# the FQDN! By default "networking.hosts" also contains entries for the
|
||||||
|
@ -178,12 +196,19 @@ in
|
||||||
let
|
let
|
||||||
oneToString = set: ip: ip + " " + lib.concatStringsSep " " set.${ip} + "\n";
|
oneToString = set: ip: ip + " " + lib.concatStringsSep " " set.${ip} + "\n";
|
||||||
allToString = set: lib.concatMapStrings (oneToString set) (lib.attrNames set);
|
allToString = set: lib.concatMapStrings (oneToString set) (lib.attrNames set);
|
||||||
in pkgs.writeText "string-hosts" (allToString (lib.filterAttrs (_: v: v != []) cfg.hosts));
|
in
|
||||||
|
pkgs.writeText "string-hosts" (allToString (lib.filterAttrs (_: v: v != [ ]) cfg.hosts));
|
||||||
extraHosts = pkgs.writeText "extra-hosts" cfg.extraHosts;
|
extraHosts = pkgs.writeText "extra-hosts" cfg.extraHosts;
|
||||||
in lib.mkBefore [ localhostHosts stringHosts extraHosts ];
|
in
|
||||||
|
lib.mkBefore [
|
||||||
|
localhostHosts
|
||||||
|
stringHosts
|
||||||
|
extraHosts
|
||||||
|
];
|
||||||
|
|
||||||
environment.etc =
|
environment.etc =
|
||||||
{ # /etc/services: TCP/UDP port assignments.
|
{
|
||||||
|
# /etc/services: TCP/UDP port assignments.
|
||||||
services.source = pkgs.iana-etc + "/etc/services";
|
services.source = pkgs.iana-etc + "/etc/services";
|
||||||
|
|
||||||
# /etc/protocols: IP protocol numbers.
|
# /etc/protocols: IP protocol numbers.
|
||||||
|
@ -200,7 +225,8 @@ in
|
||||||
multi on
|
multi on
|
||||||
'';
|
'';
|
||||||
|
|
||||||
} // lib.optionalAttrs (pkgs.stdenv.hostPlatform.libc == "glibc") {
|
}
|
||||||
|
// lib.optionalAttrs (pkgs.stdenv.hostPlatform.libc == "glibc") {
|
||||||
# /etc/rpc: RPC program numbers.
|
# /etc/rpc: RPC program numbers.
|
||||||
rpc.source = pkgs.stdenv.cc.libc.out + "/etc/rpc";
|
rpc.source = pkgs.stdenv.cc.libc.out + "/etc/rpc";
|
||||||
};
|
};
|
||||||
|
@ -209,17 +235,23 @@ in
|
||||||
lib.optionalAttrs (cfg.proxy.default != null) {
|
lib.optionalAttrs (cfg.proxy.default != null) {
|
||||||
# other options already fallback to proxy.default
|
# other options already fallback to proxy.default
|
||||||
no_proxy = "127.0.0.1,localhost";
|
no_proxy = "127.0.0.1,localhost";
|
||||||
} // lib.optionalAttrs (cfg.proxy.httpProxy != null) {
|
}
|
||||||
|
// lib.optionalAttrs (cfg.proxy.httpProxy != null) {
|
||||||
http_proxy = cfg.proxy.httpProxy;
|
http_proxy = cfg.proxy.httpProxy;
|
||||||
} // lib.optionalAttrs (cfg.proxy.httpsProxy != null) {
|
}
|
||||||
|
// lib.optionalAttrs (cfg.proxy.httpsProxy != null) {
|
||||||
https_proxy = cfg.proxy.httpsProxy;
|
https_proxy = cfg.proxy.httpsProxy;
|
||||||
} // lib.optionalAttrs (cfg.proxy.rsyncProxy != null) {
|
}
|
||||||
|
// lib.optionalAttrs (cfg.proxy.rsyncProxy != null) {
|
||||||
rsync_proxy = cfg.proxy.rsyncProxy;
|
rsync_proxy = cfg.proxy.rsyncProxy;
|
||||||
} // lib.optionalAttrs (cfg.proxy.ftpProxy != null) {
|
}
|
||||||
|
// lib.optionalAttrs (cfg.proxy.ftpProxy != null) {
|
||||||
ftp_proxy = cfg.proxy.ftpProxy;
|
ftp_proxy = cfg.proxy.ftpProxy;
|
||||||
} // lib.optionalAttrs (cfg.proxy.allProxy != null) {
|
}
|
||||||
|
// lib.optionalAttrs (cfg.proxy.allProxy != null) {
|
||||||
all_proxy = cfg.proxy.allProxy;
|
all_proxy = cfg.proxy.allProxy;
|
||||||
} // lib.optionalAttrs (cfg.proxy.noProxy != null) {
|
}
|
||||||
|
// lib.optionalAttrs (cfg.proxy.noProxy != null) {
|
||||||
no_proxy = cfg.proxy.noProxy;
|
no_proxy = cfg.proxy.noProxy;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
See also
|
See also
|
||||||
- ./nix.nix
|
- ./nix.nix
|
||||||
- ./nix-flakes.nix
|
- ./nix-flakes.nix
|
||||||
*/
|
*/
|
||||||
{ config, lib, ... }:
|
{ config, lib, ... }:
|
||||||
let
|
let
|
||||||
inherit (lib)
|
inherit (lib)
|
||||||
|
@ -42,13 +42,14 @@ in
|
||||||
nixPath = mkOption {
|
nixPath = mkOption {
|
||||||
type = types.listOf types.str;
|
type = types.listOf types.str;
|
||||||
default =
|
default =
|
||||||
if cfg.channel.enable
|
if cfg.channel.enable then
|
||||||
then [
|
[
|
||||||
"nixpkgs=/nix/var/nix/profiles/per-user/root/channels/nixos"
|
"nixpkgs=/nix/var/nix/profiles/per-user/root/channels/nixos"
|
||||||
"nixos-config=/etc/nixos/configuration.nix"
|
"nixos-config=/etc/nixos/configuration.nix"
|
||||||
"/nix/var/nix/profiles/per-user/root/channels"
|
"/nix/var/nix/profiles/per-user/root/channels"
|
||||||
]
|
]
|
||||||
else [ ];
|
else
|
||||||
|
[ ];
|
||||||
defaultText = ''
|
defaultText = ''
|
||||||
if nix.channel.enable
|
if nix.channel.enable
|
||||||
then [
|
then [
|
||||||
|
@ -78,8 +79,7 @@ in
|
||||||
|
|
||||||
config = mkIf cfg.enable {
|
config = mkIf cfg.enable {
|
||||||
|
|
||||||
environment.extraInit =
|
environment.extraInit = mkIf cfg.channel.enable ''
|
||||||
mkIf cfg.channel.enable ''
|
|
||||||
if [ -e "$HOME/.nix-defexpr/channels" ]; then
|
if [ -e "$HOME/.nix-defexpr/channels" ]; then
|
||||||
export NIX_PATH="$HOME/.nix-defexpr/channels''${NIX_PATH:+:$NIX_PATH}"
|
export NIX_PATH="$HOME/.nix-defexpr/channels''${NIX_PATH:+:$NIX_PATH}"
|
||||||
fi
|
fi
|
||||||
|
@ -99,7 +99,8 @@ in
|
||||||
''f /root/.nix-channels - - - - ${config.system.defaultChannel} nixos\n''
|
''f /root/.nix-channels - - - - ${config.system.defaultChannel} nixos\n''
|
||||||
];
|
];
|
||||||
|
|
||||||
system.activationScripts.no-nix-channel = mkIf (!cfg.channel.enable)
|
system.activationScripts.no-nix-channel = mkIf (!cfg.channel.enable) (
|
||||||
(stringAfter [ "etc" "users" ] (builtins.readFile ./nix-channel/activation-check.sh));
|
stringAfter [ "etc" "users" ] (builtins.readFile ./nix-channel/activation-check.sh)
|
||||||
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,13 @@
|
||||||
- ./nix-flakes.nix
|
- ./nix-flakes.nix
|
||||||
- ./nix-remote-build.nix
|
- ./nix-remote-build.nix
|
||||||
- nixos/modules/services/system/nix-daemon.nix
|
- nixos/modules/services/system/nix-daemon.nix
|
||||||
*/
|
*/
|
||||||
{ config, lib, pkgs, ... }:
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
let
|
let
|
||||||
inherit (lib)
|
inherit (lib)
|
||||||
|
@ -45,12 +50,14 @@ let
|
||||||
|
|
||||||
isNixAtLeast = versionAtLeast (getVersion nixPackage);
|
isNixAtLeast = versionAtLeast (getVersion nixPackage);
|
||||||
|
|
||||||
defaultSystemFeatures = [
|
defaultSystemFeatures =
|
||||||
|
[
|
||||||
"nixos-test"
|
"nixos-test"
|
||||||
"benchmark"
|
"benchmark"
|
||||||
"big-parallel"
|
"big-parallel"
|
||||||
"kvm"
|
"kvm"
|
||||||
] ++ optionals (pkgs.stdenv.hostPlatform ? gcc.arch) (
|
]
|
||||||
|
++ optionals (pkgs.stdenv.hostPlatform ? gcc.arch) (
|
||||||
# a builder can run code for `gcc.arch` and inferior architectures
|
# a builder can run code for `gcc.arch` and inferior architectures
|
||||||
[ "gccarch-${pkgs.stdenv.hostPlatform.gcc.arch}" ]
|
[ "gccarch-${pkgs.stdenv.hostPlatform.gcc.arch}" ]
|
||||||
++ map (x: "gccarch-${x}") (
|
++ map (x: "gccarch-${x}") (
|
||||||
|
@ -73,17 +80,19 @@ let
|
||||||
systemFeatures = "system-features";
|
systemFeatures = "system-features";
|
||||||
};
|
};
|
||||||
|
|
||||||
semanticConfType = with types;
|
semanticConfType =
|
||||||
|
with types;
|
||||||
let
|
let
|
||||||
confAtom = nullOr
|
confAtom =
|
||||||
(oneOf [
|
nullOr (oneOf [
|
||||||
bool
|
bool
|
||||||
int
|
int
|
||||||
float
|
float
|
||||||
str
|
str
|
||||||
path
|
path
|
||||||
package
|
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
|
in
|
||||||
|
@ -93,17 +102,28 @@ let
|
||||||
assert isNixAtLeast "2.2";
|
assert isNixAtLeast "2.2";
|
||||||
let
|
let
|
||||||
|
|
||||||
mkValueString = v:
|
mkValueString =
|
||||||
if v == null then ""
|
v:
|
||||||
else if isInt v then toString v
|
if v == null then
|
||||||
else if isBool v then boolToString v
|
""
|
||||||
else if isFloat v then floatToString v
|
else if isInt v then
|
||||||
else if isList v then toString v
|
toString v
|
||||||
else if isDerivation v then toString v
|
else if isBool v then
|
||||||
else if builtins.isPath v then toString v
|
boolToString v
|
||||||
else if isString v then v
|
else if isFloat v then
|
||||||
else if strings.isConvertibleWithToString v then toString v
|
floatToString v
|
||||||
else abort "The nix conf value: ${toPretty {} v} can not be encoded";
|
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}";
|
mkKeyValue = k: v: "${escape [ "=" ] k} = ${mkValueString v}";
|
||||||
|
|
||||||
|
@ -125,7 +145,8 @@ let
|
||||||
${cfg.extraOptions}
|
${cfg.extraOptions}
|
||||||
'';
|
'';
|
||||||
checkPhase = lib.optionalString cfg.checkConfig (
|
checkPhase = lib.optionalString cfg.checkConfig (
|
||||||
if pkgs.stdenv.hostPlatform != pkgs.stdenv.buildPlatform then ''
|
if pkgs.stdenv.hostPlatform != pkgs.stdenv.buildPlatform then
|
||||||
|
''
|
||||||
echo "Ignoring validation for cross-compilation"
|
echo "Ignoring validation for cross-compilation"
|
||||||
''
|
''
|
||||||
else
|
else
|
||||||
|
@ -143,23 +164,52 @@ let
|
||||||
|& sed -e 's/^warning:/error:/' \
|
|& sed -e 's/^warning:/error:/' \
|
||||||
| (! grep '${if cfg.checkAllErrors then "^error:" else "^error: unknown setting"}')
|
| (! grep '${if cfg.checkAllErrors then "^error:" else "^error: unknown setting"}')
|
||||||
set -o pipefail
|
set -o pipefail
|
||||||
'');
|
''
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
imports = [
|
imports =
|
||||||
(mkRenamedOptionModuleWith { sinceRelease = 2003; from = [ "nix" "useChroot" ]; to = [ "nix" "useSandbox" ]; })
|
[
|
||||||
(mkRenamedOptionModuleWith { sinceRelease = 2003; from = [ "nix" "chrootDirs" ]; to = [ "nix" "sandboxPaths" ]; })
|
(mkRenamedOptionModuleWith {
|
||||||
] ++
|
sinceRelease = 2003;
|
||||||
mapAttrsToList
|
from = [
|
||||||
(oldConf: newConf:
|
"nix"
|
||||||
|
"useChroot"
|
||||||
|
];
|
||||||
|
to = [
|
||||||
|
"nix"
|
||||||
|
"useSandbox"
|
||||||
|
];
|
||||||
|
})
|
||||||
|
(mkRenamedOptionModuleWith {
|
||||||
|
sinceRelease = 2003;
|
||||||
|
from = [
|
||||||
|
"nix"
|
||||||
|
"chrootDirs"
|
||||||
|
];
|
||||||
|
to = [
|
||||||
|
"nix"
|
||||||
|
"sandboxPaths"
|
||||||
|
];
|
||||||
|
})
|
||||||
|
]
|
||||||
|
++ mapAttrsToList (
|
||||||
|
oldConf: newConf:
|
||||||
mkRenamedOptionModuleWith {
|
mkRenamedOptionModuleWith {
|
||||||
sinceRelease = 2205;
|
sinceRelease = 2205;
|
||||||
from = [ "nix" oldConf ];
|
from = [
|
||||||
to = [ "nix" "settings" newConf ];
|
"nix"
|
||||||
})
|
oldConf
|
||||||
legacyConfMappings;
|
];
|
||||||
|
to = [
|
||||||
|
"nix"
|
||||||
|
"settings"
|
||||||
|
newConf
|
||||||
|
];
|
||||||
|
}
|
||||||
|
) legacyConfMappings;
|
||||||
|
|
||||||
options = {
|
options = {
|
||||||
nix = {
|
nix = {
|
||||||
|
@ -258,7 +308,10 @@ in
|
||||||
extra-sandbox-paths = mkOption {
|
extra-sandbox-paths = mkOption {
|
||||||
type = types.listOf types.str;
|
type = types.listOf types.str;
|
||||||
default = [ ];
|
default = [ ];
|
||||||
example = [ "/dev" "/proc" ];
|
example = [
|
||||||
|
"/dev"
|
||||||
|
"/proc"
|
||||||
|
];
|
||||||
description = ''
|
description = ''
|
||||||
Directories from the host filesystem to be included
|
Directories from the host filesystem to be included
|
||||||
in the sandbox.
|
in the sandbox.
|
||||||
|
@ -314,7 +367,11 @@ in
|
||||||
|
|
||||||
trusted-users = mkOption {
|
trusted-users = mkOption {
|
||||||
type = types.listOf types.str;
|
type = types.listOf types.str;
|
||||||
example = [ "root" "alice" "@wheel" ];
|
example = [
|
||||||
|
"root"
|
||||||
|
"alice"
|
||||||
|
"@wheel"
|
||||||
|
];
|
||||||
description = ''
|
description = ''
|
||||||
A list of names of users that have additional rights when
|
A list of names of users that have additional rights when
|
||||||
connecting to the Nix daemon, such as the ability to specify
|
connecting to the Nix daemon, such as the ability to specify
|
||||||
|
@ -342,7 +399,12 @@ in
|
||||||
allowed-users = mkOption {
|
allowed-users = mkOption {
|
||||||
type = types.listOf types.str;
|
type = types.listOf types.str;
|
||||||
default = [ "*" ];
|
default = [ "*" ];
|
||||||
example = [ "@wheel" "@builders" "alice" "bob" ];
|
example = [
|
||||||
|
"@wheel"
|
||||||
|
"@builders"
|
||||||
|
"alice"
|
||||||
|
"bob"
|
||||||
|
];
|
||||||
description = ''
|
description = ''
|
||||||
A list of names of users (separated by whitespace) that are
|
A list of names of users (separated by whitespace) that are
|
||||||
allowed to connect to the Nix daemon. As with
|
allowed to connect to the Nix daemon. As with
|
||||||
|
|
|
@ -1,25 +1,33 @@
|
||||||
# This module defines a global environment configuration and
|
# This module defines a global environment configuration and
|
||||||
# a common configuration for all shells.
|
# a common configuration for all shells.
|
||||||
{ config, lib, utils, pkgs, ... }:
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
utils,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
let
|
let
|
||||||
|
|
||||||
cfg = config.environment;
|
cfg = config.environment;
|
||||||
|
|
||||||
exportedEnvVars =
|
exportedEnvVars =
|
||||||
let
|
let
|
||||||
absoluteVariables =
|
absoluteVariables = lib.mapAttrs (n: lib.toList) cfg.variables;
|
||||||
lib.mapAttrs (n: lib.toList) cfg.variables;
|
|
||||||
|
|
||||||
suffixedVariables =
|
suffixedVariables = lib.flip lib.mapAttrs cfg.profileRelativeEnvVars (
|
||||||
lib.flip lib.mapAttrs cfg.profileRelativeEnvVars (envVar: listSuffixes:
|
envVar: listSuffixes:
|
||||||
lib.concatMap (profile: map (suffix: "${profile}${suffix}") listSuffixes) cfg.profiles
|
lib.concatMap (profile: map (suffix: "${profile}${suffix}") listSuffixes) cfg.profiles
|
||||||
);
|
);
|
||||||
|
|
||||||
allVariables =
|
allVariables = lib.zipAttrsWith (n: lib.concatLists) [
|
||||||
lib.zipAttrsWith (n: lib.concatLists) [ absoluteVariables suffixedVariables ];
|
absoluteVariables
|
||||||
|
suffixedVariables
|
||||||
|
];
|
||||||
|
|
||||||
exportVariables =
|
exportVariables = lib.mapAttrsToList (
|
||||||
lib.mapAttrsToList (n: v: ''export ${n}="${lib.concatStringsSep ":" v}"'') allVariables;
|
n: v: ''export ${n}="${lib.concatStringsSep ":" v}"''
|
||||||
|
) allVariables;
|
||||||
in
|
in
|
||||||
lib.concatStringsSep "\n" exportVariables;
|
lib.concatStringsSep "\n" exportVariables;
|
||||||
in
|
in
|
||||||
|
@ -29,8 +37,11 @@ in
|
||||||
options = {
|
options = {
|
||||||
|
|
||||||
environment.variables = lib.mkOption {
|
environment.variables = lib.mkOption {
|
||||||
default = {};
|
default = { };
|
||||||
example = { EDITOR = "nvim"; VISUAL = "nvim"; };
|
example = {
|
||||||
|
EDITOR = "nvim";
|
||||||
|
VISUAL = "nvim";
|
||||||
|
};
|
||||||
description = ''
|
description = ''
|
||||||
A set of environment variables used in the global environment.
|
A set of environment variables used in the global environment.
|
||||||
These variables will be set on shell initialisation (e.g. in /etc/profile).
|
These variables will be set on shell initialisation (e.g. in /etc/profile).
|
||||||
|
@ -42,14 +53,32 @@ in
|
||||||
Setting a variable to `null` does nothing. You can override a
|
Setting a variable to `null` does nothing. You can override a
|
||||||
variable set by another module to `null` to unset it.
|
variable set by another module to `null` to unset it.
|
||||||
'';
|
'';
|
||||||
type = with lib.types; attrsOf (nullOr (oneOf [ (listOf (oneOf [ int str path ])) int str path ]));
|
type =
|
||||||
apply = let
|
with lib.types;
|
||||||
|
attrsOf (
|
||||||
|
nullOr (oneOf [
|
||||||
|
(listOf (oneOf [
|
||||||
|
int
|
||||||
|
str
|
||||||
|
path
|
||||||
|
]))
|
||||||
|
int
|
||||||
|
str
|
||||||
|
path
|
||||||
|
])
|
||||||
|
);
|
||||||
|
apply =
|
||||||
|
let
|
||||||
toStr = v: if lib.isPath v then "${v}" else toString v;
|
toStr = v: if lib.isPath v then "${v}" else toString v;
|
||||||
in attrs: lib.mapAttrs (n: v: if lib.isList v then lib.concatMapStringsSep ":" toStr v else toStr v) (lib.filterAttrs (n: v: v != null) attrs);
|
in
|
||||||
|
attrs:
|
||||||
|
lib.mapAttrs (n: v: if lib.isList v then lib.concatMapStringsSep ":" toStr v else toStr v) (
|
||||||
|
lib.filterAttrs (n: v: v != null) attrs
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
environment.profiles = lib.mkOption {
|
environment.profiles = lib.mkOption {
|
||||||
default = [];
|
default = [ ];
|
||||||
description = ''
|
description = ''
|
||||||
A list of profiles used to setup the global environment.
|
A list of profiles used to setup the global environment.
|
||||||
'';
|
'';
|
||||||
|
@ -58,7 +87,13 @@ in
|
||||||
|
|
||||||
environment.profileRelativeEnvVars = lib.mkOption {
|
environment.profileRelativeEnvVars = lib.mkOption {
|
||||||
type = lib.types.attrsOf (lib.types.listOf lib.types.str);
|
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 = ''
|
description = ''
|
||||||
Attribute set of environment variable. Each attribute maps to a list
|
Attribute set of environment variable. Each attribute maps to a list
|
||||||
of relative paths. Each relative path is appended to the each profile
|
of relative paths. Each relative path is appended to the each profile
|
||||||
|
@ -110,7 +145,10 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
environment.shellAliases = lib.mkOption {
|
environment.shellAliases = lib.mkOption {
|
||||||
example = { l = null; ll = "ls -l"; };
|
example = {
|
||||||
|
l = null;
|
||||||
|
ll = "ls -l";
|
||||||
|
};
|
||||||
description = ''
|
description = ''
|
||||||
An attribute set that maps aliases (the top level attribute names in
|
An attribute set that maps aliases (the top level attribute names in
|
||||||
this option) to command strings or directly to build outputs. The
|
this option) to command strings or directly to build outputs. The
|
||||||
|
@ -151,7 +189,7 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
environment.shells = lib.mkOption {
|
environment.shells = lib.mkOption {
|
||||||
default = [];
|
default = [ ];
|
||||||
example = lib.literalExpression "[ pkgs.bashInteractive pkgs.zsh ]";
|
example = lib.literalExpression "[ pkgs.bashInteractive pkgs.zsh ]";
|
||||||
description = ''
|
description = ''
|
||||||
A list of permissible login shells for user accounts.
|
A list of permissible login shells for user accounts.
|
||||||
|
@ -181,8 +219,7 @@ in
|
||||||
l = "ls -alh";
|
l = "ls -alh";
|
||||||
};
|
};
|
||||||
|
|
||||||
environment.etc.shells.text =
|
environment.etc.shells.text = ''
|
||||||
''
|
|
||||||
${lib.concatStringsSep "\n" (map utils.toShellPath cfg.shells)}
|
${lib.concatStringsSep "\n" (map utils.toShellPath cfg.shells)}
|
||||||
/bin/sh
|
/bin/sh
|
||||||
'';
|
'';
|
||||||
|
@ -191,8 +228,7 @@ in
|
||||||
# and discoverability (see motivation of #30418).
|
# and discoverability (see motivation of #30418).
|
||||||
environment.etc.set-environment.source = config.system.build.setEnvironment;
|
environment.etc.set-environment.source = config.system.build.setEnvironment;
|
||||||
|
|
||||||
system.build.setEnvironment = pkgs.writeText "set-environment"
|
system.build.setEnvironment = pkgs.writeText "set-environment" ''
|
||||||
''
|
|
||||||
# DO NOT EDIT -- this file has been generated automatically.
|
# DO NOT EDIT -- this file has been generated automatically.
|
||||||
|
|
||||||
# Prevent this file from being sourced by child shells.
|
# Prevent this file from being sourced by child shells.
|
||||||
|
@ -212,8 +248,7 @@ in
|
||||||
''}
|
''}
|
||||||
'';
|
'';
|
||||||
|
|
||||||
system.activationScripts.binsh = lib.stringAfter [ "stdio" ]
|
system.activationScripts.binsh = lib.stringAfter [ "stdio" ] ''
|
||||||
''
|
|
||||||
# Create the required /bin/sh symlink; otherwise lots of things
|
# Create the required /bin/sh symlink; otherwise lots of things
|
||||||
# (notably the system() function) won't work.
|
# (notably the system() function) won't work.
|
||||||
mkdir -p /bin
|
mkdir -p /bin
|
||||||
|
|
|
@ -1,11 +1,19 @@
|
||||||
{ config, lib, pkgs, utils, ... }:
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
utils,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
let
|
let
|
||||||
inherit (lib) mkIf mkOption types;
|
inherit (lib) mkIf mkOption types;
|
||||||
|
|
||||||
randomEncryptionCoerce = enable: { inherit enable; };
|
randomEncryptionCoerce = enable: { inherit enable; };
|
||||||
|
|
||||||
randomEncryptionOpts = { ... }: {
|
randomEncryptionOpts =
|
||||||
|
{ ... }:
|
||||||
|
{
|
||||||
|
|
||||||
options = {
|
options = {
|
||||||
|
|
||||||
|
@ -86,7 +94,9 @@ let
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
swapCfg = {config, options, ...}: {
|
swapCfg =
|
||||||
|
{ config, options, ... }:
|
||||||
|
{
|
||||||
|
|
||||||
options = {
|
options = {
|
||||||
|
|
||||||
|
@ -152,7 +162,13 @@ let
|
||||||
discardPolicy = mkOption {
|
discardPolicy = mkOption {
|
||||||
default = null;
|
default = null;
|
||||||
example = "once";
|
example = "once";
|
||||||
type = types.nullOr (types.enum ["once" "pages" "both" ]);
|
type = types.nullOr (
|
||||||
|
types.enum [
|
||||||
|
"once"
|
||||||
|
"pages"
|
||||||
|
"both"
|
||||||
|
]
|
||||||
|
);
|
||||||
description = ''
|
description = ''
|
||||||
Specify the discard policy for the swap device. If "once", then the
|
Specify the discard policy for the swap device. If "once", then the
|
||||||
whole swap space is discarded at swapon invocation. If "pages",
|
whole swap space is discarded at swapon invocation. If "pages",
|
||||||
|
@ -184,10 +200,10 @@ let
|
||||||
};
|
};
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
device = mkIf options.label.isDefined
|
device = mkIf options.label.isDefined "/dev/disk/by-label/${config.label}";
|
||||||
"/dev/disk/by-label/${config.label}";
|
deviceName = lib.replaceStrings [ "\\" ] [ "" ] (utils.escapeSystemdPath config.device);
|
||||||
deviceName = lib.replaceStrings ["\\"] [""] (utils.escapeSystemdPath config.device);
|
realDevice =
|
||||||
realDevice = if config.randomEncryption.enable then "/dev/mapper/${config.deviceName}" else config.device;
|
if config.randomEncryption.enable then "/dev/mapper/${config.deviceName}" else config.device;
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -201,7 +217,7 @@ in
|
||||||
options = {
|
options = {
|
||||||
|
|
||||||
swapDevices = mkOption {
|
swapDevices = mkOption {
|
||||||
default = [];
|
default = [ ];
|
||||||
example = [
|
example = [
|
||||||
{ device = "/dev/hda7"; }
|
{ device = "/dev/hda7"; }
|
||||||
{ device = "/var/swapfile"; }
|
{ device = "/var/swapfile"; }
|
||||||
|
@ -224,7 +240,8 @@ in
|
||||||
|
|
||||||
config = mkIf ((lib.length config.swapDevices) != 0) {
|
config = mkIf ((lib.length config.swapDevices) != 0) {
|
||||||
assertions = lib.map (sw: {
|
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 = ''
|
message = ''
|
||||||
You cannot use swap device "${sw.device}" with randomEncryption enabled.
|
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.
|
The UUIDs and labels will get erased on every boot when the partition is encrypted.
|
||||||
|
@ -232,12 +249,13 @@ in
|
||||||
'';
|
'';
|
||||||
}) config.swapDevices;
|
}) config.swapDevices;
|
||||||
|
|
||||||
warnings =
|
warnings = lib.concatMap (
|
||||||
lib.concatMap (sw:
|
sw:
|
||||||
if sw.size != null && lib.hasPrefix "/dev/" sw.device
|
if sw.size != null && lib.hasPrefix "/dev/" sw.device then
|
||||||
then [ "Setting the swap size of block device ${sw.device} has no effect" ]
|
[ "Setting the swap size of block device ${sw.device} has no effect" ]
|
||||||
else [ ])
|
else
|
||||||
config.swapDevices;
|
[ ]
|
||||||
|
) config.swapDevices;
|
||||||
|
|
||||||
system.requiredKernelConfig = [
|
system.requiredKernelConfig = [
|
||||||
(config.lib.kernelConfig.isYes "SWAP")
|
(config.lib.kernelConfig.isYes "SWAP")
|
||||||
|
@ -246,25 +264,32 @@ in
|
||||||
# Create missing swapfiles.
|
# Create missing swapfiles.
|
||||||
systemd.services =
|
systemd.services =
|
||||||
let
|
let
|
||||||
createSwapDevice = sw:
|
createSwapDevice =
|
||||||
let realDevice' = utils.escapeSystemdPath sw.realDevice;
|
sw:
|
||||||
in lib.nameValuePair "mkswap-${sw.deviceName}"
|
let
|
||||||
{ description = "Initialisation of swap device ${sw.device}";
|
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
|
# The mkswap service fails for file-backed swap devices if the
|
||||||
# loop module has not been loaded before the service runs.
|
# loop module has not been loaded before the service runs.
|
||||||
# We add an ordering constraint to run after systemd-modules-load to
|
# We add an ordering constraint to run after systemd-modules-load to
|
||||||
# avoid this race condition.
|
# avoid this race condition.
|
||||||
after = [ "systemd-modules-load.service" ];
|
after = [ "systemd-modules-load.service" ];
|
||||||
wantedBy = [ "${realDevice'}.swap" ];
|
wantedBy = [ "${realDevice'}.swap" ];
|
||||||
before = [ "${realDevice'}.swap" "shutdown.target"];
|
before = [
|
||||||
|
"${realDevice'}.swap"
|
||||||
|
"shutdown.target"
|
||||||
|
];
|
||||||
conflicts = [ "shutdown.target" ];
|
conflicts = [ "shutdown.target" ];
|
||||||
path = [ pkgs.util-linux pkgs.e2fsprogs ]
|
path = [
|
||||||
++ lib.optional sw.randomEncryption.enable pkgs.cryptsetup;
|
pkgs.util-linux
|
||||||
|
pkgs.e2fsprogs
|
||||||
|
] ++ lib.optional sw.randomEncryption.enable pkgs.cryptsetup;
|
||||||
|
|
||||||
environment.DEVICE = sw.device;
|
environment.DEVICE = sw.device;
|
||||||
|
|
||||||
script =
|
script = ''
|
||||||
''
|
|
||||||
${lib.optionalString (sw.size != null) ''
|
${lib.optionalString (sw.size != null) ''
|
||||||
currentSize=$(( $(stat -c "%s" "$DEVICE" 2>/dev/null || echo 0) / 1024 / 1024 ))
|
currentSize=$(( $(stat -c "%s" "$DEVICE" 2>/dev/null || echo 0) / 1024 / 1024 ))
|
||||||
if [[ ! -b "$DEVICE" && "${toString sw.size}" != "$currentSize" ]]; then
|
if [[ ! -b "$DEVICE" && "${toString sw.size}" != "$currentSize" ]]; then
|
||||||
|
@ -279,11 +304,19 @@ in
|
||||||
''}
|
''}
|
||||||
${lib.optionalString sw.randomEncryption.enable ''
|
${lib.optionalString sw.randomEncryption.enable ''
|
||||||
cryptsetup plainOpen -c ${sw.randomEncryption.cipher} -d ${sw.randomEncryption.source} \
|
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.concatStringsSep " \\\n" (
|
||||||
(lib.optional (sw.randomEncryption.keySize != null) "--key-size=${toString sw.randomEncryption.keySize}")
|
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")
|
(lib.optional sw.randomEncryption.allowDiscards "--allow-discards")
|
||||||
])} ${sw.device} ${sw.deviceName}
|
]
|
||||||
|
)
|
||||||
|
} ${sw.device} ${sw.deviceName}
|
||||||
mkswap ${sw.realDevice}
|
mkswap ${sw.realDevice}
|
||||||
''}
|
''}
|
||||||
'';
|
'';
|
||||||
|
@ -299,7 +332,12 @@ in
|
||||||
restartIfChanged = false;
|
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
|
# This module defines a system-wide environment that will be
|
||||||
# initialised by pam_env (that is, not only in shells).
|
# initialised by pam_env (that is, not only in shells).
|
||||||
{ config, lib, options, pkgs, ... }:
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
options,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
let
|
let
|
||||||
|
|
||||||
cfg = config.environment;
|
cfg = config.environment;
|
||||||
|
@ -12,7 +18,7 @@ in
|
||||||
options = {
|
options = {
|
||||||
|
|
||||||
environment.sessionVariables = lib.mkOption {
|
environment.sessionVariables = lib.mkOption {
|
||||||
default = {};
|
default = { };
|
||||||
description = ''
|
description = ''
|
||||||
A set of environment variables used in the global environment.
|
A set of environment variables used in the global environment.
|
||||||
These variables will be set by PAM early in the login process.
|
These variables will be set by PAM early in the login process.
|
||||||
|
@ -37,7 +43,13 @@ in
|
||||||
|
|
||||||
environment.profileRelativeSessionVariables = lib.mkOption {
|
environment.profileRelativeSessionVariables = lib.mkOption {
|
||||||
type = lib.types.attrsOf (lib.types.listOf lib.types.str);
|
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 = ''
|
description = ''
|
||||||
Attribute set of environment variable used in the global
|
Attribute set of environment variable used in the global
|
||||||
environment. These variables will be set by PAM early in the
|
environment. These variables will be set by PAM early in the
|
||||||
|
@ -61,27 +73,24 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
environment.etc."pam/environment".text = let
|
environment.etc."pam/environment".text =
|
||||||
suffixedVariables =
|
let
|
||||||
lib.flip lib.mapAttrs cfg.profileRelativeSessionVariables (envVar: suffixes:
|
suffixedVariables = lib.flip lib.mapAttrs cfg.profileRelativeSessionVariables (
|
||||||
lib.flip lib.concatMap cfg.profiles (profile:
|
envVar: suffixes:
|
||||||
map (suffix: "${profile}${suffix}") 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.
|
# 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
|
# That means we need to map the env variables that people might use to their
|
||||||
# equivalent PAM variable.
|
# equivalent PAM variable.
|
||||||
replaceEnvVars = lib.replaceStrings ["$HOME" "$USER"] ["@{HOME}" "@{PAM_USER}"];
|
replaceEnvVars = lib.replaceStrings [ "$HOME" "$USER" ] [ "@{HOME}" "@{PAM_USER}" ];
|
||||||
|
|
||||||
pamVariable = n: v:
|
pamVariable =
|
||||||
''${n} DEFAULT="${lib.concatStringsSep ":" (map replaceEnvVars (lib.toList v))}"'';
|
n: v: ''${n} DEFAULT="${lib.concatStringsSep ":" (map replaceEnvVars (lib.toList v))}"'';
|
||||||
|
|
||||||
pamVariables =
|
pamVariables = lib.concatStringsSep "\n" (
|
||||||
lib.concatStringsSep "\n"
|
lib.mapAttrsToList pamVariable (
|
||||||
(lib.mapAttrsToList pamVariable
|
lib.zipAttrsWith (n: lib.concatLists) [
|
||||||
(lib.zipAttrsWith (n: lib.concatLists)
|
|
||||||
[
|
|
||||||
# Make sure security wrappers are prioritized without polluting
|
# Make sure security wrappers are prioritized without polluting
|
||||||
# shell environments with an extra entry. Sessions which depend on
|
# shell environments with an extra entry. Sessions which depend on
|
||||||
# pam for its environment will otherwise have eg. broken sudo. In
|
# pam for its environment will otherwise have eg. broken sudo. In
|
||||||
|
@ -91,8 +100,11 @@ in
|
||||||
|
|
||||||
(lib.mapAttrs (n: lib.toList) cfg.sessionVariables)
|
(lib.mapAttrs (n: lib.toList) cfg.sessionVariables)
|
||||||
suffixedVariables
|
suffixedVariables
|
||||||
]));
|
]
|
||||||
in ''
|
)
|
||||||
|
);
|
||||||
|
in
|
||||||
|
''
|
||||||
${pamVariables}
|
${pamVariables}
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,4 +1,10 @@
|
||||||
{ config, lib, utils, pkgs, ... }:
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
utils,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
let
|
let
|
||||||
inherit (lib)
|
inherit (lib)
|
||||||
|
@ -44,10 +50,11 @@ let
|
||||||
cfg = config.users;
|
cfg = config.users;
|
||||||
|
|
||||||
# Check whether a password hash will allow login.
|
# Check whether a password hash will allow login.
|
||||||
allowsLogin = hash:
|
allowsLogin =
|
||||||
|
hash:
|
||||||
hash == "" # login without password
|
hash == "" # login without password
|
||||||
|| !(lib.elem hash
|
|| !(lib.elem hash [
|
||||||
[ null # password login disabled
|
null # password login disabled
|
||||||
"!" # password login disabled
|
"!" # password login disabled
|
||||||
"!!" # a variant of "!"
|
"!!" # a variant of "!"
|
||||||
"*" # password unset
|
"*" # password unset
|
||||||
|
@ -120,7 +127,9 @@ let
|
||||||
password (i.e. via {command}`login` command).
|
password (i.e. via {command}`login` command).
|
||||||
'';
|
'';
|
||||||
|
|
||||||
userOpts = { name, config, ... }: {
|
userOpts =
|
||||||
|
{ name, config, ... }:
|
||||||
|
{
|
||||||
|
|
||||||
options = {
|
options = {
|
||||||
|
|
||||||
|
@ -136,7 +145,12 @@ let
|
||||||
|
|
||||||
name = mkOption {
|
name = mkOption {
|
||||||
type = types.passwdEntry types.str;
|
type = types.passwdEntry types.str;
|
||||||
apply = x: assert (stringLength x < 32 || abort "Username '${x}' is longer than 31 characters which is not allowed!"); x;
|
apply =
|
||||||
|
x:
|
||||||
|
assert (
|
||||||
|
stringLength x < 32 || abort "Username '${x}' is longer than 31 characters which is not allowed!"
|
||||||
|
);
|
||||||
|
x;
|
||||||
description = ''
|
description = ''
|
||||||
The name of the user account. If undefined, the name of the
|
The name of the user account. If undefined, the name of the
|
||||||
attribute set will be used.
|
attribute set will be used.
|
||||||
|
@ -194,14 +208,19 @@ let
|
||||||
|
|
||||||
group = mkOption {
|
group = mkOption {
|
||||||
type = types.str;
|
type = types.str;
|
||||||
apply = x: assert (stringLength x < 32 || abort "Group name '${x}' is longer than 31 characters which is not allowed!"); x;
|
apply =
|
||||||
|
x:
|
||||||
|
assert (
|
||||||
|
stringLength x < 32 || abort "Group name '${x}' is longer than 31 characters which is not allowed!"
|
||||||
|
);
|
||||||
|
x;
|
||||||
default = "";
|
default = "";
|
||||||
description = "The user's primary group.";
|
description = "The user's primary group.";
|
||||||
};
|
};
|
||||||
|
|
||||||
extraGroups = mkOption {
|
extraGroups = mkOption {
|
||||||
type = types.listOf types.str;
|
type = types.listOf types.str;
|
||||||
default = [];
|
default = [ ];
|
||||||
description = "The user's auxiliary groups.";
|
description = "The user's auxiliary groups.";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -228,7 +247,7 @@ let
|
||||||
|
|
||||||
pamMount = mkOption {
|
pamMount = mkOption {
|
||||||
type = with types; attrsOf str;
|
type = with types; attrsOf str;
|
||||||
default = {};
|
default = { };
|
||||||
description = ''
|
description = ''
|
||||||
Attributes for user's entry in
|
Attributes for user's entry in
|
||||||
{file}`pam_mount.conf.xml`.
|
{file}`pam_mount.conf.xml`.
|
||||||
|
@ -266,10 +285,16 @@ let
|
||||||
|
|
||||||
subUidRanges = mkOption {
|
subUidRanges = mkOption {
|
||||||
type = with types; listOf (submodule subordinateUidRange);
|
type = with types; listOf (submodule subordinateUidRange);
|
||||||
default = [];
|
default = [ ];
|
||||||
example = [
|
example = [
|
||||||
{ startUid = 1000; count = 1; }
|
{
|
||||||
{ startUid = 100001; count = 65534; }
|
startUid = 1000;
|
||||||
|
count = 1;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
startUid = 100001;
|
||||||
|
count = 65534;
|
||||||
|
}
|
||||||
];
|
];
|
||||||
description = ''
|
description = ''
|
||||||
Subordinate user ids that user is allowed to use.
|
Subordinate user ids that user is allowed to use.
|
||||||
|
@ -280,10 +305,16 @@ let
|
||||||
|
|
||||||
subGidRanges = mkOption {
|
subGidRanges = mkOption {
|
||||||
type = with types; listOf (submodule subordinateGidRange);
|
type = with types; listOf (submodule subordinateGidRange);
|
||||||
default = [];
|
default = [ ];
|
||||||
example = [
|
example = [
|
||||||
{ startGid = 100; count = 1; }
|
{
|
||||||
{ startGid = 1001; count = 999; }
|
startGid = 100;
|
||||||
|
count = 1;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
startGid = 1001;
|
||||||
|
count = 999;
|
||||||
|
}
|
||||||
];
|
];
|
||||||
description = ''
|
description = ''
|
||||||
Subordinate group ids that user is allowed to use.
|
Subordinate group ids that user is allowed to use.
|
||||||
|
@ -402,7 +433,7 @@ let
|
||||||
|
|
||||||
packages = mkOption {
|
packages = mkOption {
|
||||||
type = types.listOf types.package;
|
type = types.listOf types.package;
|
||||||
default = [];
|
default = [ ];
|
||||||
example = literalExpression "[ pkgs.firefox pkgs.thunderbird ]";
|
example = literalExpression "[ pkgs.firefox pkgs.thunderbird ]";
|
||||||
description = ''
|
description = ''
|
||||||
The set of packages that should be made available to the user.
|
The set of packages that should be made available to the user.
|
||||||
|
@ -438,8 +469,9 @@ let
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
config = mkMerge
|
config = mkMerge [
|
||||||
[ { name = mkDefault name;
|
{
|
||||||
|
name = mkDefault name;
|
||||||
shell = mkIf config.useDefaultShell (mkDefault cfg.defaultUserShell);
|
shell = mkIf config.useDefaultShell (mkDefault cfg.defaultUserShell);
|
||||||
}
|
}
|
||||||
(mkIf config.isNormalUser {
|
(mkIf config.isNormalUser {
|
||||||
|
@ -458,14 +490,16 @@ let
|
||||||
(mkIf (!cfg.mutableUsers && config.initialHashedPassword != null) {
|
(mkIf (!cfg.mutableUsers && config.initialHashedPassword != null) {
|
||||||
hashedPassword = mkDefault config.initialHashedPassword;
|
hashedPassword = mkDefault config.initialHashedPassword;
|
||||||
})
|
})
|
||||||
(mkIf (config.isNormalUser && config.subUidRanges == [] && config.subGidRanges == []) {
|
(mkIf (config.isNormalUser && config.subUidRanges == [ ] && config.subGidRanges == [ ]) {
|
||||||
autoSubUidGidRange = mkDefault true;
|
autoSubUidGidRange = mkDefault true;
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
groupOpts = { name, config, ... }: {
|
groupOpts =
|
||||||
|
{ name, config, ... }:
|
||||||
|
{
|
||||||
|
|
||||||
options = {
|
options = {
|
||||||
|
|
||||||
|
@ -488,7 +522,7 @@ let
|
||||||
|
|
||||||
members = mkOption {
|
members = mkOption {
|
||||||
type = with types; listOf (passwdEntry str);
|
type = with types; listOf (passwdEntry str);
|
||||||
default = [];
|
default = [ ];
|
||||||
description = ''
|
description = ''
|
||||||
The user names of the group members, added to the
|
The user names of the group members, added to the
|
||||||
`/etc/group` file.
|
`/etc/group` file.
|
||||||
|
@ -541,36 +575,86 @@ let
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
idsAreUnique = set: idAttr: !(foldr (name: args@{ dup, acc }:
|
idsAreUnique =
|
||||||
|
set: idAttr:
|
||||||
|
!(foldr
|
||||||
|
(
|
||||||
|
name:
|
||||||
|
args@{ dup, acc }:
|
||||||
let
|
let
|
||||||
id = toString (getAttr idAttr (getAttr name set));
|
id = toString (getAttr idAttr (getAttr name set));
|
||||||
exists = hasAttr id acc;
|
exists = hasAttr id acc;
|
||||||
newAcc = acc // (listToAttrs [ { name = id; value = true; } ]);
|
newAcc =
|
||||||
in if dup then args else if exists
|
acc
|
||||||
then trace "Duplicate ${idAttr} ${id}" { dup = true; acc = null; }
|
// (listToAttrs [
|
||||||
else { dup = false; acc = newAcc; }
|
{
|
||||||
) { dup = false; acc = {}; } (attrNames set)).dup;
|
name = id;
|
||||||
|
value = true;
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
in
|
||||||
|
if dup then
|
||||||
|
args
|
||||||
|
else if exists then
|
||||||
|
trace "Duplicate ${idAttr} ${id}" {
|
||||||
|
dup = true;
|
||||||
|
acc = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dup = false;
|
||||||
|
acc = newAcc;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
{
|
||||||
|
dup = false;
|
||||||
|
acc = { };
|
||||||
|
}
|
||||||
|
(attrNames set)
|
||||||
|
).dup;
|
||||||
|
|
||||||
uidsAreUnique = idsAreUnique (filterAttrs (n: u: u.uid != null) cfg.users) "uid";
|
uidsAreUnique = idsAreUnique (filterAttrs (n: u: u.uid != null) cfg.users) "uid";
|
||||||
gidsAreUnique = idsAreUnique (filterAttrs (n: g: g.gid != null) cfg.groups) "gid";
|
gidsAreUnique = idsAreUnique (filterAttrs (n: g: g.gid != null) cfg.groups) "gid";
|
||||||
sdInitrdUidsAreUnique = idsAreUnique (filterAttrs (n: u: u.uid != null) config.boot.initrd.systemd.users) "uid";
|
sdInitrdUidsAreUnique = idsAreUnique (filterAttrs (
|
||||||
sdInitrdGidsAreUnique = idsAreUnique (filterAttrs (n: g: g.gid != null) config.boot.initrd.systemd.groups) "gid";
|
n: u: u.uid != null
|
||||||
|
) config.boot.initrd.systemd.users) "uid";
|
||||||
|
sdInitrdGidsAreUnique = idsAreUnique (filterAttrs (
|
||||||
|
n: g: g.gid != null
|
||||||
|
) config.boot.initrd.systemd.groups) "gid";
|
||||||
groupNames = lib.mapAttrsToList (n: g: g.name) cfg.groups;
|
groupNames = lib.mapAttrsToList (n: g: g.name) cfg.groups;
|
||||||
usersWithoutExistingGroup = lib.filterAttrs (n: u: u.group != "" && !lib.elem u.group groupNames) cfg.users;
|
usersWithoutExistingGroup = lib.filterAttrs (
|
||||||
|
n: u: u.group != "" && !lib.elem u.group groupNames
|
||||||
|
) cfg.users;
|
||||||
usersWithNullShells = attrNames (filterAttrs (name: cfg: cfg.shell == null) cfg.users);
|
usersWithNullShells = attrNames (filterAttrs (name: cfg: cfg.shell == null) cfg.users);
|
||||||
|
|
||||||
spec = pkgs.writeText "users-groups.json" (builtins.toJSON {
|
spec = pkgs.writeText "users-groups.json" (
|
||||||
|
builtins.toJSON {
|
||||||
inherit (cfg) mutableUsers;
|
inherit (cfg) mutableUsers;
|
||||||
users = mapAttrsToList (_: u:
|
users = mapAttrsToList (_: u: {
|
||||||
{ inherit (u)
|
inherit (u)
|
||||||
name uid group description home homeMode createHome isSystemUser
|
name
|
||||||
password hashedPasswordFile hashedPassword
|
uid
|
||||||
autoSubUidGidRange subUidRanges subGidRanges
|
group
|
||||||
initialPassword initialHashedPassword expires;
|
description
|
||||||
|
home
|
||||||
|
homeMode
|
||||||
|
createHome
|
||||||
|
isSystemUser
|
||||||
|
password
|
||||||
|
hashedPasswordFile
|
||||||
|
hashedPassword
|
||||||
|
autoSubUidGidRange
|
||||||
|
subUidRanges
|
||||||
|
subGidRanges
|
||||||
|
initialPassword
|
||||||
|
initialHashedPassword
|
||||||
|
expires
|
||||||
|
;
|
||||||
shell = utils.toShellPath u.shell;
|
shell = utils.toShellPath u.shell;
|
||||||
}) (filterAttrs (_: u: u.enable) cfg.users);
|
}) (filterAttrs (_: u: u.enable) cfg.users);
|
||||||
groups = attrValues cfg.groups;
|
groups = attrValues cfg.groups;
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
systemShells =
|
systemShells =
|
||||||
let
|
let
|
||||||
|
@ -579,11 +663,15 @@ let
|
||||||
filter types.shellPackage.check shells;
|
filter types.shellPackage.check shells;
|
||||||
|
|
||||||
lingeringUsers = map (u: u.name) (attrValues (flip filterAttrs cfg.users (n: u: u.linger)));
|
lingeringUsers = map (u: u.name) (attrValues (flip filterAttrs cfg.users (n: u: u.linger)));
|
||||||
in {
|
in
|
||||||
|
{
|
||||||
imports = [
|
imports = [
|
||||||
(mkAliasOptionModuleMD [ "users" "extraUsers" ] [ "users" "users" ])
|
(mkAliasOptionModuleMD [ "users" "extraUsers" ] [ "users" "users" ])
|
||||||
(mkAliasOptionModuleMD [ "users" "extraGroups" ] [ "users" "groups" ])
|
(mkAliasOptionModuleMD [ "users" "extraGroups" ] [ "users" "groups" ])
|
||||||
(mkRenamedOptionModule ["security" "initialRootPassword"] ["users" "users" "root" "initialHashedPassword"])
|
(mkRenamedOptionModule
|
||||||
|
[ "security" "initialRootPassword" ]
|
||||||
|
[ "users" "users" "root" "initialHashedPassword" ]
|
||||||
|
)
|
||||||
];
|
];
|
||||||
|
|
||||||
###### interface
|
###### interface
|
||||||
|
@ -623,7 +711,7 @@ in {
|
||||||
};
|
};
|
||||||
|
|
||||||
users.users = mkOption {
|
users.users = mkOption {
|
||||||
default = {};
|
default = { };
|
||||||
type = with types; attrsOf (submodule userOpts);
|
type = with types; attrsOf (submodule userOpts);
|
||||||
example = {
|
example = {
|
||||||
alice = {
|
alice = {
|
||||||
|
@ -632,7 +720,7 @@ in {
|
||||||
home = "/home/alice";
|
home = "/home/alice";
|
||||||
createHome = true;
|
createHome = true;
|
||||||
group = "users";
|
group = "users";
|
||||||
extraGroups = ["wheel"];
|
extraGroups = [ "wheel" ];
|
||||||
shell = "/bin/sh";
|
shell = "/bin/sh";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -643,9 +731,9 @@ in {
|
||||||
};
|
};
|
||||||
|
|
||||||
users.groups = mkOption {
|
users.groups = mkOption {
|
||||||
default = {};
|
default = { };
|
||||||
example =
|
example = {
|
||||||
{ students.gid = 1001;
|
students.gid = 1001;
|
||||||
hackers = { };
|
hackers = { };
|
||||||
};
|
};
|
||||||
type = with types; attrsOf (submodule groupOpts);
|
type = with types; attrsOf (submodule groupOpts);
|
||||||
|
@ -654,7 +742,6 @@ in {
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
users.allowNoPasswordLogin = mkOption {
|
users.allowNoPasswordLogin = mkOption {
|
||||||
type = types.bool;
|
type = types.bool;
|
||||||
default = false;
|
default = false;
|
||||||
|
@ -671,8 +758,11 @@ in {
|
||||||
description = ''
|
description = ''
|
||||||
Users to include in initrd.
|
Users to include in initrd.
|
||||||
'';
|
'';
|
||||||
default = {};
|
default = { };
|
||||||
type = types.attrsOf (types.submodule ({ name, ... }: {
|
type = types.attrsOf (
|
||||||
|
types.submodule (
|
||||||
|
{ name, ... }:
|
||||||
|
{
|
||||||
options.uid = mkOption {
|
options.uid = mkOption {
|
||||||
type = types.int;
|
type = types.int;
|
||||||
description = ''
|
description = ''
|
||||||
|
@ -697,15 +787,20 @@ in {
|
||||||
default = "${pkgs.shadow}/bin/nologin";
|
default = "${pkgs.shadow}/bin/nologin";
|
||||||
defaultText = literalExpression "\${pkgs.shadow}/bin/nologin";
|
defaultText = literalExpression "\${pkgs.shadow}/bin/nologin";
|
||||||
};
|
};
|
||||||
}));
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
boot.initrd.systemd.groups = mkOption {
|
boot.initrd.systemd.groups = mkOption {
|
||||||
description = ''
|
description = ''
|
||||||
Groups to include in initrd.
|
Groups to include in initrd.
|
||||||
'';
|
'';
|
||||||
default = {};
|
default = { };
|
||||||
type = types.attrsOf (types.submodule ({ name, ... }: {
|
type = types.attrsOf (
|
||||||
|
types.submodule (
|
||||||
|
{ name, ... }:
|
||||||
|
{
|
||||||
options.gid = mkOption {
|
options.gid = mkOption {
|
||||||
type = types.int;
|
type = types.int;
|
||||||
description = ''
|
description = ''
|
||||||
|
@ -714,16 +809,19 @@ in {
|
||||||
defaultText = literalExpression "config.users.groups.\${name}.gid";
|
defaultText = literalExpression "config.users.groups.\${name}.gid";
|
||||||
default = cfg.groups.${name}.gid;
|
default = cfg.groups.${name}.gid;
|
||||||
};
|
};
|
||||||
}));
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
###### implementation
|
###### implementation
|
||||||
|
|
||||||
config = let
|
config =
|
||||||
|
let
|
||||||
cryptSchemeIdPatternGroup = "(${lib.concatStringsSep "|" pkgs.libxcrypt.enabledCryptSchemeIds})";
|
cryptSchemeIdPatternGroup = "(${lib.concatStringsSep "|" pkgs.libxcrypt.enabledCryptSchemeIds})";
|
||||||
in {
|
in
|
||||||
|
{
|
||||||
|
|
||||||
users.users = {
|
users.users = {
|
||||||
root = {
|
root = {
|
||||||
|
@ -767,28 +865,39 @@ in {
|
||||||
shadow.gid = ids.gids.shadow;
|
shadow.gid = ids.gids.shadow;
|
||||||
};
|
};
|
||||||
|
|
||||||
system.activationScripts.users = if !config.systemd.sysusers.enable then {
|
system.activationScripts.users =
|
||||||
|
if !config.systemd.sysusers.enable then
|
||||||
|
{
|
||||||
supportsDryActivation = true;
|
supportsDryActivation = true;
|
||||||
text = ''
|
text = ''
|
||||||
install -m 0700 -d /root
|
install -m 0700 -d /root
|
||||||
install -m 0755 -d /home
|
install -m 0755 -d /home
|
||||||
|
|
||||||
${pkgs.perl.withPackages (p: [ p.FileSlurp p.JSON ])}/bin/perl \
|
${
|
||||||
|
pkgs.perl.withPackages (p: [
|
||||||
|
p.FileSlurp
|
||||||
|
p.JSON
|
||||||
|
])
|
||||||
|
}/bin/perl \
|
||||||
-w ${./update-users-groups.pl} ${spec}
|
-w ${./update-users-groups.pl} ${spec}
|
||||||
'';
|
'';
|
||||||
} else ""; # keep around for backwards compatibility
|
}
|
||||||
|
else
|
||||||
|
""; # keep around for backwards compatibility
|
||||||
|
|
||||||
systemd.services.linger-users = lib.mkIf ((length lingeringUsers) > 0) {
|
systemd.services.linger-users = lib.mkIf ((length lingeringUsers) > 0) {
|
||||||
wantedBy = ["multi-user.target"];
|
wantedBy = [ "multi-user.target" ];
|
||||||
after = ["systemd-logind.service"];
|
after = [ "systemd-logind.service" ];
|
||||||
requires = ["systemd-logind.service"];
|
requires = [ "systemd-logind.service" ];
|
||||||
|
|
||||||
script = let
|
script =
|
||||||
|
let
|
||||||
lingerDir = "/var/lib/systemd/linger";
|
lingerDir = "/var/lib/systemd/linger";
|
||||||
lingeringUsersFile = builtins.toFile "lingering-users"
|
lingeringUsersFile = builtins.toFile "lingering-users" (
|
||||||
(concatStrings (map (s: "${s}\n")
|
concatStrings (map (s: "${s}\n") (sort (a: b: a < b) lingeringUsers))
|
||||||
(sort (a: b: a < b) lingeringUsers))); # this sorting is important for `comm` to work correctly
|
); # this sorting is important for `comm` to work correctly
|
||||||
in ''
|
in
|
||||||
|
''
|
||||||
mkdir -vp ${lingerDir}
|
mkdir -vp ${lingerDir}
|
||||||
cd ${lingerDir}
|
cd ${lingerDir}
|
||||||
for user in $(ls); do
|
for user in $(ls); do
|
||||||
|
@ -807,7 +916,9 @@ in {
|
||||||
# Warn about user accounts with deprecated password hashing schemes
|
# Warn about user accounts with deprecated password hashing schemes
|
||||||
# This does not work when the users and groups are created by
|
# This does not work when the users and groups are created by
|
||||||
# systemd-sysusers because the users are created too late then.
|
# systemd-sysusers because the users are created too late then.
|
||||||
system.activationScripts.hashes = if !config.systemd.sysusers.enable then {
|
system.activationScripts.hashes =
|
||||||
|
if !config.systemd.sysusers.enable then
|
||||||
|
{
|
||||||
deps = [ "users" ];
|
deps = [ "users" ];
|
||||||
text = ''
|
text = ''
|
||||||
users=()
|
users=()
|
||||||
|
@ -825,7 +936,9 @@ in {
|
||||||
printf ' - %s\n' "''${users[@]}"
|
printf ' - %s\n' "''${users[@]}"
|
||||||
fi
|
fi
|
||||||
'';
|
'';
|
||||||
} else ""; # keep around for backwards compatibility
|
}
|
||||||
|
else
|
||||||
|
""; # keep around for backwards compatibility
|
||||||
|
|
||||||
# for backwards compatibility
|
# for backwards compatibility
|
||||||
system.activationScripts.groups = stringAfter [ "users" ] "";
|
system.activationScripts.groups = stringAfter [ "users" ] "";
|
||||||
|
@ -833,7 +946,10 @@ in {
|
||||||
# Install all the user shells
|
# Install all the user shells
|
||||||
environment.systemPackages = systemShells;
|
environment.systemPackages = systemShells;
|
||||||
|
|
||||||
environment.etc = mapAttrs' (_: { packages, name, ... }: {
|
environment.etc = mapAttrs' (
|
||||||
|
_:
|
||||||
|
{ packages, name, ... }:
|
||||||
|
{
|
||||||
name = "profiles/per-user/${name}";
|
name = "profiles/per-user/${name}";
|
||||||
value.source = pkgs.buildEnv {
|
value.source = pkgs.buildEnv {
|
||||||
name = "user-environment";
|
name = "user-environment";
|
||||||
|
@ -841,7 +957,8 @@ in {
|
||||||
inherit (config.environment) pathsToLink extraOutputsToInstall;
|
inherit (config.environment) pathsToLink extraOutputsToInstall;
|
||||||
inherit (config.system.path) ignoreCollisions postBuild;
|
inherit (config.system.path) ignoreCollisions postBuild;
|
||||||
};
|
};
|
||||||
}) (filterAttrs (_: u: u.packages != []) cfg.users);
|
}
|
||||||
|
) (filterAttrs (_: u: u.packages != [ ]) cfg.users);
|
||||||
|
|
||||||
environment.profiles = [
|
environment.profiles = [
|
||||||
"$HOME/.nix-profile"
|
"$HOME/.nix-profile"
|
||||||
|
@ -854,58 +971,82 @@ in {
|
||||||
boot.initrd.systemd = lib.mkIf config.boot.initrd.systemd.enable {
|
boot.initrd.systemd = lib.mkIf config.boot.initrd.systemd.enable {
|
||||||
contents = {
|
contents = {
|
||||||
"/etc/passwd".text = ''
|
"/etc/passwd".text = ''
|
||||||
${lib.concatStringsSep "\n" (lib.mapAttrsToList (n: { uid, group, shell }: let
|
${lib.concatStringsSep "\n" (
|
||||||
|
lib.mapAttrsToList (
|
||||||
|
n:
|
||||||
|
{
|
||||||
|
uid,
|
||||||
|
group,
|
||||||
|
shell,
|
||||||
|
}:
|
||||||
|
let
|
||||||
g = config.boot.initrd.systemd.groups.${group};
|
g = config.boot.initrd.systemd.groups.${group};
|
||||||
in "${n}:x:${toString uid}:${toString g.gid}::/var/empty:${shell}") config.boot.initrd.systemd.users)}
|
in
|
||||||
|
"${n}:x:${toString uid}:${toString g.gid}::/var/empty:${shell}"
|
||||||
|
) config.boot.initrd.systemd.users
|
||||||
|
)}
|
||||||
'';
|
'';
|
||||||
"/etc/group".text = ''
|
"/etc/group".text = ''
|
||||||
${lib.concatStringsSep "\n" (lib.mapAttrsToList (n: { gid }: "${n}:x:${toString gid}:") config.boot.initrd.systemd.groups)}
|
${lib.concatStringsSep "\n" (
|
||||||
|
lib.mapAttrsToList (n: { gid }: "${n}:x:${toString gid}:") config.boot.initrd.systemd.groups
|
||||||
|
)}
|
||||||
'';
|
'';
|
||||||
"/etc/shells".text = lib.concatStringsSep "\n" (lib.unique (lib.mapAttrsToList (_: u: u.shell) config.boot.initrd.systemd.users)) + "\n";
|
"/etc/shells".text =
|
||||||
|
lib.concatStringsSep "\n" (
|
||||||
|
lib.unique (lib.mapAttrsToList (_: u: u.shell) config.boot.initrd.systemd.users)
|
||||||
|
)
|
||||||
|
+ "\n";
|
||||||
};
|
};
|
||||||
|
|
||||||
storePaths = [ "${pkgs.shadow}/bin/nologin" ];
|
storePaths = [ "${pkgs.shadow}/bin/nologin" ];
|
||||||
|
|
||||||
users = {
|
users = {
|
||||||
root = { shell = lib.mkDefault "/bin/bash"; };
|
root = {
|
||||||
nobody = {};
|
shell = lib.mkDefault "/bin/bash";
|
||||||
|
};
|
||||||
|
nobody = { };
|
||||||
};
|
};
|
||||||
|
|
||||||
groups = {
|
groups = {
|
||||||
root = {};
|
root = { };
|
||||||
nogroup = {};
|
nogroup = { };
|
||||||
systemd-journal = {};
|
systemd-journal = { };
|
||||||
tty = {};
|
tty = { };
|
||||||
dialout = {};
|
dialout = { };
|
||||||
kmem = {};
|
kmem = { };
|
||||||
input = {};
|
input = { };
|
||||||
video = {};
|
video = { };
|
||||||
render = {};
|
render = { };
|
||||||
sgx = {};
|
sgx = { };
|
||||||
audio = {};
|
audio = { };
|
||||||
video = {};
|
video = { };
|
||||||
lp = {};
|
lp = { };
|
||||||
disk = {};
|
disk = { };
|
||||||
cdrom = {};
|
cdrom = { };
|
||||||
tape = {};
|
tape = { };
|
||||||
kvm = {};
|
kvm = { };
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
assertions = [
|
assertions =
|
||||||
{ assertion = !cfg.enforceIdUniqueness || (uidsAreUnique && gidsAreUnique);
|
[
|
||||||
|
{
|
||||||
|
assertion = !cfg.enforceIdUniqueness || (uidsAreUnique && gidsAreUnique);
|
||||||
message = "UIDs and GIDs must be unique!";
|
message = "UIDs and GIDs must be unique!";
|
||||||
}
|
}
|
||||||
{ assertion = !cfg.enforceIdUniqueness || (sdInitrdUidsAreUnique && sdInitrdGidsAreUnique);
|
{
|
||||||
|
assertion = !cfg.enforceIdUniqueness || (sdInitrdUidsAreUnique && sdInitrdGidsAreUnique);
|
||||||
message = "systemd initrd UIDs and GIDs must be unique!";
|
message = "systemd initrd UIDs and GIDs must be unique!";
|
||||||
}
|
}
|
||||||
{ assertion = usersWithoutExistingGroup == {};
|
{
|
||||||
|
assertion = usersWithoutExistingGroup == { };
|
||||||
message =
|
message =
|
||||||
let
|
let
|
||||||
errUsers = lib.attrNames usersWithoutExistingGroup;
|
errUsers = lib.attrNames usersWithoutExistingGroup;
|
||||||
missingGroups = lib.unique (lib.mapAttrsToList (n: u: u.group) usersWithoutExistingGroup);
|
missingGroups = lib.unique (lib.mapAttrsToList (n: u: u.group) usersWithoutExistingGroup);
|
||||||
mkConfigHint = group: "users.groups.${group} = {};";
|
mkConfigHint = group: "users.groups.${group} = {};";
|
||||||
in ''
|
in
|
||||||
|
''
|
||||||
The following users have a primary group that is undefined: ${lib.concatStringsSep " " errUsers}
|
The following users have a primary group that is undefined: ${lib.concatStringsSep " " errUsers}
|
||||||
Hint: Add this to your NixOS configuration:
|
Hint: Add this to your NixOS configuration:
|
||||||
${lib.concatStringsSep "\n " (map mkConfigHint missingGroups)}
|
${lib.concatStringsSep "\n " (map mkConfigHint missingGroups)}
|
||||||
|
@ -920,27 +1061,33 @@ in {
|
||||||
Misconfigured users: ${lib.concatStringsSep " " usersWithNullShells}
|
Misconfigured users: ${lib.concatStringsSep " " usersWithNullShells}
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
{ # If mutableUsers is false, to prevent users creating a
|
{
|
||||||
|
# If mutableUsers is false, to prevent users creating a
|
||||||
# configuration that locks them out of the system, ensure that
|
# configuration that locks them out of the system, ensure that
|
||||||
# there is at least one "privileged" account that has a
|
# there is at least one "privileged" account that has a
|
||||||
# password or an SSH authorized key. Privileged accounts are
|
# password or an SSH authorized key. Privileged accounts are
|
||||||
# root and users in the wheel group.
|
# root and users in the wheel group.
|
||||||
# The check does not apply when users.allowNoPasswordLogin
|
# The check does not apply when users.allowNoPasswordLogin
|
||||||
# The check does not apply when users.mutableUsers
|
# The check does not apply when users.mutableUsers
|
||||||
assertion = !cfg.mutableUsers -> !cfg.allowNoPasswordLogin ->
|
assertion =
|
||||||
any id (mapAttrsToList (name: cfg:
|
!cfg.mutableUsers
|
||||||
(name == "root"
|
-> !cfg.allowNoPasswordLogin
|
||||||
|| cfg.group == "wheel"
|
-> any id (
|
||||||
|| elem "wheel" cfg.extraGroups)
|
mapAttrsToList (
|
||||||
&&
|
name: cfg:
|
||||||
(allowsLogin cfg.hashedPassword
|
(name == "root" || cfg.group == "wheel" || elem "wheel" cfg.extraGroups)
|
||||||
|
&& (
|
||||||
|
allowsLogin cfg.hashedPassword
|
||||||
|| cfg.password != null
|
|| cfg.password != null
|
||||||
|| cfg.hashedPasswordFile != null
|
|| cfg.hashedPasswordFile != null
|
||||||
|| cfg.openssh.authorizedKeys.keys != []
|
|| cfg.openssh.authorizedKeys.keys != [ ]
|
||||||
|| cfg.openssh.authorizedKeys.keyFiles != [])
|
|| cfg.openssh.authorizedKeys.keyFiles != [ ]
|
||||||
) cfg.users ++ [
|
)
|
||||||
|
) cfg.users
|
||||||
|
++ [
|
||||||
config.security.googleOsLogin.enable
|
config.security.googleOsLogin.enable
|
||||||
]);
|
]
|
||||||
|
);
|
||||||
message = ''
|
message = ''
|
||||||
Neither the root account nor any wheel user has a password or SSH authorized key.
|
Neither the root account nor any wheel user has a password or SSH authorized key.
|
||||||
You must set one to prevent being locked out of your system.
|
You must set one to prevent being locked out of your system.
|
||||||
|
@ -949,7 +1096,10 @@ in {
|
||||||
manually running passwd root to set the root password.
|
manually running passwd root to set the root password.
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
] ++ flatten (flip mapAttrsToList cfg.users (name: user:
|
]
|
||||||
|
++ flatten (
|
||||||
|
flip mapAttrsToList cfg.users (
|
||||||
|
name: user:
|
||||||
[
|
[
|
||||||
(
|
(
|
||||||
let
|
let
|
||||||
|
@ -967,8 +1117,7 @@ in {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
assertion = (user.hashedPassword != null)
|
assertion = (user.hashedPassword != null) -> (match ".*:.*" user.hashedPassword == null);
|
||||||
-> (match ".*:.*" user.hashedPassword == null);
|
|
||||||
message = ''
|
message = ''
|
||||||
The password hash of user "${user.name}" contains a ":" character.
|
The password hash of user "${user.name}" contains a ":" character.
|
||||||
This is invalid and would break the login system because the fields
|
This is invalid and would break the login system because the fields
|
||||||
|
@ -984,10 +1133,13 @@ in {
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
assertion = let
|
assertion =
|
||||||
|
let
|
||||||
# we do an extra check on isNormalUser here, to not trigger this assertion when isNormalUser is set and uid to < 1000
|
# we do an extra check on isNormalUser here, to not trigger this assertion when isNormalUser is set and uid to < 1000
|
||||||
isEffectivelySystemUser = user.isSystemUser || (user.uid != null && user.uid < 1000 && !user.isNormalUser);
|
isEffectivelySystemUser =
|
||||||
in xor isEffectivelySystemUser user.isNormalUser;
|
user.isSystemUser || (user.uid != null && user.uid < 1000 && !user.isNormalUser);
|
||||||
|
in
|
||||||
|
xor isEffectivelySystemUser user.isNormalUser;
|
||||||
message = ''
|
message = ''
|
||||||
Exactly one of users.users.${user.name}.isSystemUser and users.users.${user.name}.isNormalUser must be set.
|
Exactly one of users.users.${user.name}.isSystemUser and users.users.${user.name}.isNormalUser must be set.
|
||||||
'';
|
'';
|
||||||
|
@ -1002,8 +1154,13 @@ in {
|
||||||
users.groups.${user.name} = {};
|
users.groups.${user.name} = {};
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
] ++ (map (shell: {
|
]
|
||||||
assertion = !user.ignoreShellProgramCheck -> (user.shell == pkgs.${shell}) -> (config.programs.${shell}.enable == true);
|
++ (map
|
||||||
|
(shell: {
|
||||||
|
assertion =
|
||||||
|
!user.ignoreShellProgramCheck
|
||||||
|
-> (user.shell == pkgs.${shell})
|
||||||
|
-> (config.programs.${shell}.enable == true);
|
||||||
message = ''
|
message = ''
|
||||||
users.users.${user.name}.shell is set to ${shell}, but
|
users.users.${user.name}.shell is set to ${shell}, but
|
||||||
programs.${shell}.enable is not true. This will cause the ${shell}
|
programs.${shell}.enable is not true. This will cause the ${shell}
|
||||||
|
@ -1015,28 +1172,36 @@ in {
|
||||||
set users.users.${user.name}.ignoreShellProgramCheck = true;
|
set users.users.${user.name}.ignoreShellProgramCheck = true;
|
||||||
instead.
|
instead.
|
||||||
'';
|
'';
|
||||||
}) [
|
})
|
||||||
|
[
|
||||||
"fish"
|
"fish"
|
||||||
"xonsh"
|
"xonsh"
|
||||||
"zsh"
|
"zsh"
|
||||||
])
|
]
|
||||||
));
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
warnings =
|
warnings =
|
||||||
flip concatMap (attrValues cfg.users) (user: let
|
flip concatMap (attrValues cfg.users) (
|
||||||
passwordOptions = [
|
user:
|
||||||
|
let
|
||||||
|
passwordOptions =
|
||||||
|
[
|
||||||
"hashedPassword"
|
"hashedPassword"
|
||||||
"hashedPasswordFile"
|
"hashedPasswordFile"
|
||||||
"password"
|
"password"
|
||||||
] ++ optionals cfg.mutableUsers [
|
]
|
||||||
|
++ optionals cfg.mutableUsers [
|
||||||
# For immutable users, initialHashedPassword is set to hashedPassword,
|
# For immutable users, initialHashedPassword is set to hashedPassword,
|
||||||
# so using these options would always trigger the assertion.
|
# so using these options would always trigger the assertion.
|
||||||
"initialHashedPassword"
|
"initialHashedPassword"
|
||||||
"initialPassword"
|
"initialPassword"
|
||||||
];
|
];
|
||||||
unambiguousPasswordConfiguration = 1 >= length
|
unambiguousPasswordConfiguration =
|
||||||
(filter (x: x != null) (map (flip getAttr user) passwordOptions));
|
1 >= length (filter (x: x != null) (map (flip getAttr user) passwordOptions));
|
||||||
in optional (!unambiguousPasswordConfiguration) ''
|
in
|
||||||
|
optional (!unambiguousPasswordConfiguration) ''
|
||||||
The user '${user.name}' has multiple of the options
|
The user '${user.name}' has multiple of the options
|
||||||
`initialHashedPassword`, `hashedPassword`, `initialPassword`, `password`
|
`initialHashedPassword`, `hashedPassword`, `initialPassword`, `password`
|
||||||
& `hashedPasswordFile` set to a non-null value.
|
& `hashedPasswordFile` set to a non-null value.
|
||||||
|
@ -1044,14 +1209,14 @@ in {
|
||||||
${multiplePasswordsWarning}
|
${multiplePasswordsWarning}
|
||||||
${overrideOrderText cfg.mutableUsers}
|
${overrideOrderText cfg.mutableUsers}
|
||||||
The values of these options are:
|
The values of these options are:
|
||||||
${concatMapStringsSep
|
${concatMapStringsSep "\n" (
|
||||||
"\n"
|
value: "* users.users.\"${user.name}\".${value}: ${generators.toPretty { } user.${value}}"
|
||||||
(value:
|
) passwordOptions}
|
||||||
"* users.users.\"${user.name}\".${value}: ${generators.toPretty {} user.${value}}")
|
''
|
||||||
passwordOptions}
|
)
|
||||||
'')
|
|
||||||
++ filter (x: x != null) (
|
++ filter (x: x != null) (
|
||||||
flip mapAttrsToList cfg.users (_: user:
|
flip mapAttrsToList cfg.users (
|
||||||
|
_: user:
|
||||||
# This regex matches a subset of the Modular Crypto Format (MCF)[1]
|
# This regex matches a subset of the Modular Crypto Format (MCF)[1]
|
||||||
# informal standard. Since this depends largely on the OS or the
|
# informal standard. Since this depends largely on the OS or the
|
||||||
# specific implementation of crypt(3) we only support the (sane)
|
# specific implementation of crypt(3) we only support the (sane)
|
||||||
|
@ -1071,19 +1236,28 @@ in {
|
||||||
content = "${base64}${sep}${base64}(${sep}${base64})?";
|
content = "${base64}${sep}${base64}(${sep}${base64})?";
|
||||||
mcf = "^${sep}${scheme}${sep}${content}$";
|
mcf = "^${sep}${scheme}${sep}${content}$";
|
||||||
in
|
in
|
||||||
if (allowsLogin user.hashedPassword
|
if
|
||||||
|
(
|
||||||
|
allowsLogin user.hashedPassword
|
||||||
&& user.hashedPassword != "" # login without password
|
&& user.hashedPassword != "" # login without password
|
||||||
&& match mcf user.hashedPassword == null)
|
&& match mcf user.hashedPassword == null
|
||||||
then ''
|
)
|
||||||
|
then
|
||||||
|
''
|
||||||
The password hash of user "${user.name}" may be invalid. You must set a
|
The password hash of user "${user.name}" may be invalid. You must set a
|
||||||
valid hash or the user will be locked out of their account. Please
|
valid hash or the user will be locked out of their account. Please
|
||||||
check the value of option `users.users."${user.name}".hashedPassword`.''
|
check the value of option `users.users."${user.name}".hashedPassword`.''
|
||||||
else null)
|
else
|
||||||
++ flip mapAttrsToList cfg.users (name: user:
|
null
|
||||||
|
)
|
||||||
|
++ flip mapAttrsToList cfg.users (
|
||||||
|
name: user:
|
||||||
if user.passwordFile != null then
|
if user.passwordFile != null then
|
||||||
''The option `users.users."${name}".passwordFile' has been renamed '' +
|
''The option `users.users."${name}".passwordFile' has been renamed ''
|
||||||
''to `users.users."${name}".hashedPasswordFile'.''
|
+ ''to `users.users."${name}".hashedPasswordFile'.''
|
||||||
else null)
|
else
|
||||||
|
null
|
||||||
|
)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,14 @@
|
||||||
{ config, lib, pkgs, ... }:
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
let
|
let
|
||||||
cfg = config.xdg.mime;
|
cfg = config.xdg.mime;
|
||||||
associationOptions = with lib.types; attrsOf (
|
associationOptions =
|
||||||
coercedTo (either (listOf str) str) (x: lib.concatStringsSep ";" (lib.toList x)) str
|
with lib.types;
|
||||||
);
|
attrsOf (coercedTo (either (listOf str) str) (x: lib.concatStringsSep ";" (lib.toList x)) str);
|
||||||
in
|
in
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -24,10 +29,13 @@ in
|
||||||
|
|
||||||
xdg.mime.addedAssociations = lib.mkOption {
|
xdg.mime.addedAssociations = lib.mkOption {
|
||||||
type = associationOptions;
|
type = associationOptions;
|
||||||
default = {};
|
default = { };
|
||||||
example = {
|
example = {
|
||||||
"application/pdf" = "firefox.desktop";
|
"application/pdf" = "firefox.desktop";
|
||||||
"text/xml" = [ "nvim.desktop" "codium.desktop" ];
|
"text/xml" = [
|
||||||
|
"nvim.desktop"
|
||||||
|
"codium.desktop"
|
||||||
|
];
|
||||||
};
|
};
|
||||||
description = ''
|
description = ''
|
||||||
Adds associations between mimetypes and applications. See the
|
Adds associations between mimetypes and applications. See the
|
||||||
|
@ -38,10 +46,13 @@ in
|
||||||
|
|
||||||
xdg.mime.defaultApplications = lib.mkOption {
|
xdg.mime.defaultApplications = lib.mkOption {
|
||||||
type = associationOptions;
|
type = associationOptions;
|
||||||
default = {};
|
default = { };
|
||||||
example = {
|
example = {
|
||||||
"application/pdf" = "firefox.desktop";
|
"application/pdf" = "firefox.desktop";
|
||||||
"image/png" = [ "sxiv.desktop" "gimp.desktop" ];
|
"image/png" = [
|
||||||
|
"sxiv.desktop"
|
||||||
|
"gimp.desktop"
|
||||||
|
];
|
||||||
};
|
};
|
||||||
description = ''
|
description = ''
|
||||||
Sets the default applications for given mimetypes. See the
|
Sets the default applications for given mimetypes. See the
|
||||||
|
@ -52,9 +63,12 @@ in
|
||||||
|
|
||||||
xdg.mime.removedAssociations = lib.mkOption {
|
xdg.mime.removedAssociations = lib.mkOption {
|
||||||
type = associationOptions;
|
type = associationOptions;
|
||||||
default = {};
|
default = { };
|
||||||
example = {
|
example = {
|
||||||
"audio/mp3" = [ "mpv.desktop" "umpv.desktop" ];
|
"audio/mp3" = [
|
||||||
|
"mpv.desktop"
|
||||||
|
"umpv.desktop"
|
||||||
|
];
|
||||||
"inode/directory" = "codium.desktop";
|
"inode/directory" = "codium.desktop";
|
||||||
};
|
};
|
||||||
description = ''
|
description = ''
|
||||||
|
@ -66,11 +80,10 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkIf cfg.enable {
|
||||||
environment.etc."xdg/mimeapps.list" = lib.mkIf (
|
environment.etc."xdg/mimeapps.list" =
|
||||||
cfg.addedAssociations != {}
|
lib.mkIf
|
||||||
|| cfg.defaultApplications != {}
|
(cfg.addedAssociations != { } || cfg.defaultApplications != { } || cfg.removedAssociations != { })
|
||||||
|| cfg.removedAssociations != {}
|
{
|
||||||
) {
|
|
||||||
text = lib.generators.toINI { } {
|
text = lib.generators.toINI { } {
|
||||||
"Added Associations" = cfg.addedAssociations;
|
"Added Associations" = cfg.addedAssociations;
|
||||||
"Default Applications" = cfg.defaultApplications;
|
"Default Applications" = cfg.defaultApplications;
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
{ config, lib, pkgs, ... }:
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
let
|
let
|
||||||
|
|
||||||
|
@ -10,7 +15,10 @@ in
|
||||||
{
|
{
|
||||||
|
|
||||||
imports = [
|
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
|
###### interface
|
||||||
|
@ -73,7 +81,16 @@ in
|
||||||
algorithm = lib.mkOption {
|
algorithm = lib.mkOption {
|
||||||
default = "zstd";
|
default = "zstd";
|
||||||
example = "lz4";
|
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 = ''
|
description = ''
|
||||||
Compression algorithm. `lzo` has good compression,
|
Compression algorithm. `lzo` has good compression,
|
||||||
but is slow. `lz4` has bad compression, but is fast.
|
but is slow. `lz4` has bad compression, but is fast.
|
||||||
|
@ -107,23 +124,24 @@ in
|
||||||
|
|
||||||
services.zram-generator.enable = true;
|
services.zram-generator.enable = true;
|
||||||
|
|
||||||
services.zram-generator.settings = lib.listToAttrs
|
services.zram-generator.settings = lib.listToAttrs (
|
||||||
(builtins.map
|
builtins.map (dev: {
|
||||||
(dev: {
|
|
||||||
name = dev;
|
name = dev;
|
||||||
value =
|
value =
|
||||||
let
|
let
|
||||||
size = "${toString cfg.memoryPercent} / 100 * ram";
|
size = "${toString cfg.memoryPercent} / 100 * ram";
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
zram-size = if cfg.memoryMax != null then "min(${size}, ${toString cfg.memoryMax} / 1024 / 1024)" else size;
|
zram-size =
|
||||||
|
if cfg.memoryMax != null then "min(${size}, ${toString cfg.memoryMax} / 1024 / 1024)" else size;
|
||||||
compression-algorithm = cfg.algorithm;
|
compression-algorithm = cfg.algorithm;
|
||||||
swap-priority = cfg.priority;
|
swap-priority = cfg.priority;
|
||||||
} // lib.optionalAttrs (cfg.writebackDevice != null) {
|
}
|
||||||
|
// lib.optionalAttrs (cfg.writebackDevice != null) {
|
||||||
writeback-device = cfg.writebackDevice;
|
writeback-device = cfg.writebackDevice;
|
||||||
};
|
};
|
||||||
})
|
}) devices
|
||||||
devices);
|
);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
{ config, lib, pkgs, ... }:
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
let
|
let
|
||||||
cfg = config.hardware.deviceTree;
|
cfg = config.hardware.deviceTree;
|
||||||
|
|
||||||
|
@ -62,10 +67,12 @@ let
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
filterDTBs = src: if cfg.filter == null
|
filterDTBs =
|
||||||
then src
|
src:
|
||||||
|
if cfg.filter == null then
|
||||||
|
src
|
||||||
else
|
else
|
||||||
pkgs.runCommand "dtbs-filtered" {} ''
|
pkgs.runCommand "dtbs-filtered" { } ''
|
||||||
mkdir -p $out
|
mkdir -p $out
|
||||||
cd ${src}
|
cd ${src}
|
||||||
find . -type f -name '${cfg.filter}' -print0 \
|
find . -type f -name '${cfg.filter}' -print0 \
|
||||||
|
@ -76,9 +83,17 @@ let
|
||||||
|
|
||||||
# Fill in `dtboFile` for each overlay if not set already.
|
# Fill in `dtboFile` for each overlay if not set already.
|
||||||
# Existence of one of these is guarded by assertion below
|
# Existence of one of these is guarded by assertion below
|
||||||
withDTBOs = xs: lib.flip map xs (o: o // { dtboFile =
|
withDTBOs =
|
||||||
|
xs:
|
||||||
|
lib.flip map xs (
|
||||||
|
o:
|
||||||
|
o
|
||||||
|
// {
|
||||||
|
dtboFile =
|
||||||
let
|
let
|
||||||
includePaths = ["${lib.getDev cfg.kernelPackage}/lib/modules/${cfg.kernelPackage.modDirVersion}/source/scripts/dtc/include-prefixes"] ++ cfg.dtboBuildExtraIncludePaths;
|
includePaths = [
|
||||||
|
"${lib.getDev cfg.kernelPackage}/lib/modules/${cfg.kernelPackage.modDirVersion}/source/scripts/dtc/include-prefixes"
|
||||||
|
] ++ cfg.dtboBuildExtraIncludePaths;
|
||||||
extraPreprocessorFlags = cfg.dtboBuildExtraPreprocessorFlags;
|
extraPreprocessorFlags = cfg.dtboBuildExtraPreprocessorFlags;
|
||||||
in
|
in
|
||||||
if o.dtboFile == null then
|
if o.dtboFile == null then
|
||||||
|
@ -89,12 +104,19 @@ let
|
||||||
name = "${o.name}-dtbo";
|
name = "${o.name}-dtbo";
|
||||||
inherit includePaths extraPreprocessorFlags dtsFile;
|
inherit includePaths extraPreprocessorFlags dtsFile;
|
||||||
}
|
}
|
||||||
else o.dtboFile; } );
|
else
|
||||||
|
o.dtboFile;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
(lib.mkRemovedOptionModule [ "hardware" "deviceTree" "base" ] "Use hardware.deviceTree.kernelPackage instead")
|
(lib.mkRemovedOptionModule [
|
||||||
|
"hardware"
|
||||||
|
"deviceTree"
|
||||||
|
"base"
|
||||||
|
] "Use hardware.deviceTree.kernelPackage instead")
|
||||||
];
|
];
|
||||||
|
|
||||||
options = {
|
options = {
|
||||||
|
@ -119,7 +141,7 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
dtboBuildExtraPreprocessorFlags = lib.mkOption {
|
dtboBuildExtraPreprocessorFlags = lib.mkOption {
|
||||||
default = [];
|
default = [ ];
|
||||||
example = lib.literalExpression "[ \"-DMY_DTB_DEFINE\" ]";
|
example = lib.literalExpression "[ \"-DMY_DTB_DEFINE\" ]";
|
||||||
type = lib.types.listOf lib.types.str;
|
type = lib.types.listOf lib.types.str;
|
||||||
description = ''
|
description = ''
|
||||||
|
@ -128,7 +150,7 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
dtboBuildExtraIncludePaths = lib.mkOption {
|
dtboBuildExtraIncludePaths = lib.mkOption {
|
||||||
default = [];
|
default = [ ];
|
||||||
example = lib.literalExpression ''
|
example = lib.literalExpression ''
|
||||||
[
|
[
|
||||||
./my_custom_include_dir_1
|
./my_custom_include_dir_1
|
||||||
|
@ -172,7 +194,7 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
overlays = lib.mkOption {
|
overlays = lib.mkOption {
|
||||||
default = [];
|
default = [ ];
|
||||||
example = lib.literalExpression ''
|
example = lib.literalExpression ''
|
||||||
[
|
[
|
||||||
{ name = "pps"; dtsFile = ./dts/pps.dts; }
|
{ name = "pps"; dtsFile = ./dts/pps.dts; }
|
||||||
|
@ -182,11 +204,13 @@ in
|
||||||
{ name = "precompiled"; dtboFile = ./dtbos/example.dtbo; }
|
{ name = "precompiled"; dtboFile = ./dtbos/example.dtbo; }
|
||||||
]
|
]
|
||||||
'';
|
'';
|
||||||
type = lib.types.listOf (lib.types.coercedTo lib.types.path (path: {
|
type = lib.types.listOf (
|
||||||
|
lib.types.coercedTo lib.types.path (path: {
|
||||||
name = baseNameOf path;
|
name = baseNameOf path;
|
||||||
filter = null;
|
filter = null;
|
||||||
dtboFile = path;
|
dtboFile = path;
|
||||||
}) overlayType);
|
}) overlayType
|
||||||
|
);
|
||||||
description = ''
|
description = ''
|
||||||
List of overlays to apply to base device-tree (.dtb) files.
|
List of overlays to apply to base device-tree (.dtb) files.
|
||||||
'';
|
'';
|
||||||
|
@ -205,9 +229,11 @@ in
|
||||||
|
|
||||||
config = lib.mkIf (cfg.enable) {
|
config = lib.mkIf (cfg.enable) {
|
||||||
|
|
||||||
assertions = let
|
assertions =
|
||||||
|
let
|
||||||
invalidOverlay = o: (o.dtsFile == null) && (o.dtsText == null) && (o.dtboFile == null);
|
invalidOverlay = o: (o.dtsFile == null) && (o.dtsText == null) && (o.dtboFile == null);
|
||||||
in lib.singleton {
|
in
|
||||||
|
lib.singleton {
|
||||||
assertion = lib.all (o: !invalidOverlay o) cfg.overlays;
|
assertion = lib.all (o: !invalidOverlay o) cfg.overlays;
|
||||||
message = ''
|
message = ''
|
||||||
deviceTree overlay needs one of dtsFile, dtsText or dtboFile set.
|
deviceTree overlay needs one of dtsFile, dtsText or dtboFile set.
|
||||||
|
@ -216,8 +242,10 @@ in
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
hardware.deviceTree.package = if (cfg.overlays != [])
|
hardware.deviceTree.package =
|
||||||
then pkgs.deviceTree.applyOverlays filteredDTBs (withDTBOs cfg.overlays)
|
if (cfg.overlays != [ ]) then
|
||||||
else filteredDTBs;
|
pkgs.deviceTree.applyOverlays filteredDTBs (withDTBOs cfg.overlays)
|
||||||
|
else
|
||||||
|
filteredDTBs;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
{ config, lib, pkgs, ... }:
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
let
|
let
|
||||||
cfg = config.hardware.graphics;
|
cfg = config.hardware.graphics;
|
||||||
|
|
||||||
|
@ -14,16 +19,35 @@ let
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
(lib.mkRenamedOptionModule [ "services" "xserver" "vaapiDrivers" ] [ "hardware" "graphics" "extraPackages" ])
|
(lib.mkRenamedOptionModule
|
||||||
(lib.mkRemovedOptionModule [ "hardware" "opengl" "s3tcSupport" ] "S3TC support is now always enabled in Mesa.")
|
[ "services" "xserver" "vaapiDrivers" ]
|
||||||
(lib.mkRemovedOptionModule [ "hardware" "opengl" "driSupport"] "The setting can be removed.")
|
[ "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" "enable" ] [ "hardware" "graphics" "enable" ])
|
||||||
(lib.mkRenamedOptionModule [ "hardware" "opengl" "driSupport32Bit"] [ "hardware" "graphics" "enable32Bit" ])
|
(lib.mkRenamedOptionModule
|
||||||
(lib.mkRenamedOptionModule [ "hardware" "opengl" "package"] [ "hardware" "graphics" "package" ])
|
[ "hardware" "opengl" "driSupport32Bit" ]
|
||||||
(lib.mkRenamedOptionModule [ "hardware" "opengl" "package32"] [ "hardware" "graphics" "package32" ])
|
[ "hardware" "graphics" "enable32Bit" ]
|
||||||
(lib.mkRenamedOptionModule [ "hardware" "opengl" "extraPackages"] [ "hardware" "graphics" "extraPackages" ])
|
)
|
||||||
(lib.mkRenamedOptionModule [ "hardware" "opengl" "extraPackages32"] [ "hardware" "graphics" "extraPackages32" ])
|
(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 = {
|
options.hardware.graphics = {
|
||||||
|
@ -78,7 +102,7 @@ in
|
||||||
:::
|
:::
|
||||||
'';
|
'';
|
||||||
type = lib.types.listOf lib.types.package;
|
type = lib.types.listOf lib.types.package;
|
||||||
default = [];
|
default = [ ];
|
||||||
example = lib.literalExpression "with pkgs; [ intel-media-driver intel-ocl intel-vaapi-driver ]";
|
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;
|
type = lib.types.listOf lib.types.package;
|
||||||
default = [];
|
default = [ ];
|
||||||
example = lib.literalExpression "with pkgs.pkgsi686Linux; [ intel-media-driver intel-vaapi-driver ]";
|
example = lib.literalExpression "with pkgs.pkgsi686Linux; [ intel-media-driver intel-vaapi-driver ]";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -117,7 +141,7 @@ in
|
||||||
else if cfg.enable32Bit then
|
else if cfg.enable32Bit then
|
||||||
{ "L+".argument = toString driversEnv32; }
|
{ "L+".argument = toString driversEnv32; }
|
||||||
else
|
else
|
||||||
{ "r" = {}; };
|
{ "r" = { }; };
|
||||||
};
|
};
|
||||||
|
|
||||||
hardware.graphics.package = lib.mkDefault pkgs.mesa;
|
hardware.graphics.package = lib.mkDefault pkgs.mesa;
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
{ config, lib, pkgs, ... }:
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
let
|
let
|
||||||
|
|
||||||
cfg = config.hardware.steam-hardware;
|
cfg = config.hardware.steam-hardware;
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
{ config, lib, pkgs, ... }:
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
{
|
{
|
||||||
options.hardware.wooting.enable = lib.mkEnableOption "support for Wooting keyboards";
|
options.hardware.wooting.enable = lib.mkEnableOption "support for Wooting keyboards";
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
{ config, pkgs, lib, ... }:
|
{
|
||||||
|
config,
|
||||||
|
pkgs,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}:
|
||||||
let
|
let
|
||||||
imcfg = config.i18n.inputMethod;
|
imcfg = config.i18n.inputMethod;
|
||||||
cfg = imcfg.ibus;
|
cfg = imcfg.ibus;
|
||||||
|
@ -6,7 +11,7 @@ let
|
||||||
ibusEngine = lib.types.mkOptionType {
|
ibusEngine = lib.types.mkOptionType {
|
||||||
name = "ibus-engine";
|
name = "ibus-engine";
|
||||||
inherit (lib.types.package) descriptionClass merge;
|
inherit (lib.types.package) descriptionClass merge;
|
||||||
check = x: (lib.types.package.check x) && (lib.attrByPath ["meta" "isIbusEngine"] false x);
|
check = x: (lib.types.package.check x) && (lib.attrByPath [ "meta" "isIbusEngine" ] false x);
|
||||||
};
|
};
|
||||||
|
|
||||||
impanel = lib.optionalString (cfg.panel != null) "--panel=${cfg.panel}";
|
impanel = lib.optionalString (cfg.panel != null) "--panel=${cfg.panel}";
|
||||||
|
@ -26,21 +31,24 @@ let
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
(lib.mkRenamedOptionModule [ "programs" "ibus" "plugins" ] [ "i18n" "inputMethod" "ibus" "engines" ])
|
(lib.mkRenamedOptionModule
|
||||||
|
[ "programs" "ibus" "plugins" ]
|
||||||
|
[ "i18n" "inputMethod" "ibus" "engines" ]
|
||||||
|
)
|
||||||
];
|
];
|
||||||
|
|
||||||
options = {
|
options = {
|
||||||
i18n.inputMethod.ibus = {
|
i18n.inputMethod.ibus = {
|
||||||
engines = lib.mkOption {
|
engines = lib.mkOption {
|
||||||
type = with lib.types; listOf ibusEngine;
|
type = with lib.types; listOf ibusEngine;
|
||||||
default = [];
|
default = [ ];
|
||||||
example = lib.literalExpression "with pkgs.ibus-engines; [ mozc hangul ]";
|
example = lib.literalExpression "with pkgs.ibus-engines; [ mozc hangul ]";
|
||||||
description =
|
description =
|
||||||
let
|
let
|
||||||
enginesDrv = lib.filterAttrs (lib.const lib.isDerivation) pkgs.ibus-engines;
|
enginesDrv = lib.filterAttrs (lib.const lib.isDerivation) pkgs.ibus-engines;
|
||||||
engines = lib.concatStringsSep ", "
|
engines = lib.concatStringsSep ", " (map (name: "`${name}`") (lib.attrNames enginesDrv));
|
||||||
(map (name: "`${name}`") (lib.attrNames enginesDrv));
|
in
|
||||||
in "Enabled IBus engines. Available engines are: ${engines}.";
|
"Enabled IBus engines. Available engines are: ${engines}.";
|
||||||
};
|
};
|
||||||
panel = lib.mkOption {
|
panel = lib.mkOption {
|
||||||
type = with lib.types; nullOr path;
|
type = with lib.types; nullOr path;
|
||||||
|
|
|
@ -1,72 +1,95 @@
|
||||||
# This is an expression meant to be called from `./repart.nix`, it is NOT a
|
# This is an expression meant to be called from `./repart.nix`, it is NOT a
|
||||||
# NixOS module that can be imported.
|
# NixOS module that can be imported.
|
||||||
|
|
||||||
{ lib
|
{
|
||||||
, stdenvNoCC
|
lib,
|
||||||
, runCommand
|
stdenvNoCC,
|
||||||
, python3
|
runCommand,
|
||||||
, black
|
python3,
|
||||||
, ruff
|
black,
|
||||||
, mypy
|
ruff,
|
||||||
, systemd
|
mypy,
|
||||||
, fakeroot
|
systemd,
|
||||||
, util-linux
|
fakeroot,
|
||||||
|
util-linux,
|
||||||
|
|
||||||
# filesystem tools
|
# filesystem tools
|
||||||
, dosfstools
|
dosfstools,
|
||||||
, mtools
|
mtools,
|
||||||
, e2fsprogs
|
e2fsprogs,
|
||||||
, squashfsTools
|
squashfsTools,
|
||||||
, erofs-utils
|
erofs-utils,
|
||||||
, btrfs-progs
|
btrfs-progs,
|
||||||
, xfsprogs
|
xfsprogs,
|
||||||
|
|
||||||
# compression tools
|
# compression tools
|
||||||
, zstd
|
zstd,
|
||||||
, xz
|
xz,
|
||||||
, zeekstd
|
zeekstd,
|
||||||
|
|
||||||
# arguments
|
# arguments
|
||||||
, name
|
name,
|
||||||
, version
|
version,
|
||||||
, imageFileBasename
|
imageFileBasename,
|
||||||
, compression
|
compression,
|
||||||
, fileSystems
|
fileSystems,
|
||||||
, finalPartitions
|
finalPartitions,
|
||||||
, split
|
split,
|
||||||
, seed
|
seed,
|
||||||
, definitionsDirectory
|
definitionsDirectory,
|
||||||
, sectorSize
|
sectorSize,
|
||||||
, mkfsEnv ? {}
|
mkfsEnv ? { },
|
||||||
, createEmpty ? true
|
createEmpty ? true,
|
||||||
}:
|
}:
|
||||||
|
|
||||||
let
|
let
|
||||||
systemdArch = let
|
systemdArch =
|
||||||
|
let
|
||||||
inherit (stdenvNoCC) hostPlatform;
|
inherit (stdenvNoCC) hostPlatform;
|
||||||
in
|
in
|
||||||
if hostPlatform.isAarch32 then "arm"
|
if hostPlatform.isAarch32 then
|
||||||
else if hostPlatform.isAarch64 then "arm64"
|
"arm"
|
||||||
else if hostPlatform.isx86_32 then "x86"
|
else if hostPlatform.isAarch64 then
|
||||||
else if hostPlatform.isx86_64 then "x86-64"
|
"arm64"
|
||||||
else if hostPlatform.isMips32 then "mips-le"
|
else if hostPlatform.isx86_32 then
|
||||||
else if hostPlatform.isMips64 then "mips64-le"
|
"x86"
|
||||||
else if hostPlatform.isPower then "ppc"
|
else if hostPlatform.isx86_64 then
|
||||||
else if hostPlatform.isPower64 then "ppc64"
|
"x86-64"
|
||||||
else if hostPlatform.isRiscV32 then "riscv32"
|
else if hostPlatform.isMips32 then
|
||||||
else if hostPlatform.isRiscV64 then "riscv64"
|
"mips-le"
|
||||||
else if hostPlatform.isS390 then "s390"
|
else if hostPlatform.isMips64 then
|
||||||
else if hostPlatform.isS390x then "s390x"
|
"mips64-le"
|
||||||
else if hostPlatform.isLoongArch64 then "loongarch64"
|
else if hostPlatform.isPower then
|
||||||
else if hostPlatform.isAlpha then "alpha"
|
"ppc"
|
||||||
else hostPlatform.parsed.cpu.name;
|
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"
|
amendRepartDefinitions =
|
||||||
|
runCommand "amend-repart-definitions.py"
|
||||||
{
|
{
|
||||||
# TODO: ruff does not splice properly in nativeBuildInputs
|
# TODO: ruff does not splice properly in nativeBuildInputs
|
||||||
depsBuildBuild = [ ruff ];
|
depsBuildBuild = [ ruff ];
|
||||||
nativeBuildInputs = [ python3 black mypy ];
|
nativeBuildInputs = [
|
||||||
} ''
|
python3
|
||||||
|
black
|
||||||
|
mypy
|
||||||
|
];
|
||||||
|
}
|
||||||
|
''
|
||||||
install ${./amend-repart-definitions.py} $out
|
install ${./amend-repart-definitions.py} $out
|
||||||
patchShebangs --build $out
|
patchShebangs --build $out
|
||||||
|
|
||||||
|
@ -76,7 +99,10 @@ let
|
||||||
'';
|
'';
|
||||||
|
|
||||||
fileSystemToolMapping = {
|
fileSystemToolMapping = {
|
||||||
"vfat" = [ dosfstools mtools ];
|
"vfat" = [
|
||||||
|
dosfstools
|
||||||
|
mtools
|
||||||
|
];
|
||||||
"ext4" = [ e2fsprogs.bin ];
|
"ext4" = [ e2fsprogs.bin ];
|
||||||
"squashfs" = [ squashfsTools ];
|
"squashfs" = [ squashfsTools ];
|
||||||
"erofs" = [ erofs-utils ];
|
"erofs" = [ erofs-utils ];
|
||||||
|
@ -87,37 +113,51 @@ let
|
||||||
|
|
||||||
fileSystemTools = builtins.concatMap (f: fileSystemToolMapping."${f}") fileSystems;
|
fileSystemTools = builtins.concatMap (f: fileSystemToolMapping."${f}") fileSystems;
|
||||||
|
|
||||||
compressionPkg = {
|
compressionPkg =
|
||||||
|
{
|
||||||
"zstd" = zstd;
|
"zstd" = zstd;
|
||||||
"xz" = xz;
|
"xz" = xz;
|
||||||
"zstd-seekable" = zeekstd;
|
"zstd-seekable" = zeekstd;
|
||||||
}."${compression.algorithm}";
|
}
|
||||||
|
."${compression.algorithm}";
|
||||||
|
|
||||||
compressionCommand = {
|
compressionCommand =
|
||||||
|
{
|
||||||
"zstd" = "zstd --no-progress --threads=$NIX_BUILD_CORES -${toString compression.level}";
|
"zstd" = "zstd --no-progress --threads=$NIX_BUILD_CORES -${toString compression.level}";
|
||||||
"xz" = "xz --keep --verbose --threads=$NIX_BUILD_CORES -${toString compression.level}";
|
"xz" = "xz --keep --verbose --threads=$NIX_BUILD_CORES -${toString compression.level}";
|
||||||
"zstd-seekable" = "zeekstd --quiet --max-frame-size 2M --compression-level ${toString compression.level}";
|
"zstd-seekable" =
|
||||||
}."${compression.algorithm}";
|
"zeekstd --quiet --max-frame-size 2M --compression-level ${toString compression.level}";
|
||||||
|
}
|
||||||
|
."${compression.algorithm}";
|
||||||
in
|
in
|
||||||
stdenvNoCC.mkDerivation (finalAttrs:
|
stdenvNoCC.mkDerivation (
|
||||||
(if (version != null)
|
finalAttrs:
|
||||||
then { pname = name; inherit version; }
|
(
|
||||||
else { inherit name; }
|
if (version != null) then
|
||||||
) // {
|
{
|
||||||
|
pname = name;
|
||||||
|
inherit version;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ inherit name; }
|
||||||
|
)
|
||||||
|
// {
|
||||||
__structuredAttrs = true;
|
__structuredAttrs = true;
|
||||||
|
|
||||||
|
|
||||||
# the image will be self-contained so we can drop references
|
# the image will be self-contained so we can drop references
|
||||||
# to the closure that was used to build it
|
# to the closure that was used to build it
|
||||||
unsafeDiscardReferences.out = true;
|
unsafeDiscardReferences.out = true;
|
||||||
|
|
||||||
nativeBuildInputs = [
|
nativeBuildInputs =
|
||||||
|
[
|
||||||
systemd
|
systemd
|
||||||
util-linux
|
util-linux
|
||||||
fakeroot
|
fakeroot
|
||||||
] ++ lib.optionals (compression.enable) [
|
]
|
||||||
|
++ lib.optionals (compression.enable) [
|
||||||
compressionPkg
|
compressionPkg
|
||||||
] ++ fileSystemTools;
|
]
|
||||||
|
++ fileSystemTools;
|
||||||
|
|
||||||
env = mkfsEnv;
|
env = mkfsEnv;
|
||||||
|
|
||||||
|
@ -128,7 +168,8 @@ in
|
||||||
# relative path to the repart definitions that are read by systemd-repart
|
# relative path to the repart definitions that are read by systemd-repart
|
||||||
finalRepartDefinitions = "repart.d";
|
finalRepartDefinitions = "repart.d";
|
||||||
|
|
||||||
systemdRepartFlags = [
|
systemdRepartFlags =
|
||||||
|
[
|
||||||
"--architecture=${systemdArch}"
|
"--architecture=${systemdArch}"
|
||||||
"--dry-run=no"
|
"--dry-run=no"
|
||||||
"--size=auto"
|
"--size=auto"
|
||||||
|
@ -136,9 +177,11 @@ in
|
||||||
"--definitions=${finalAttrs.finalRepartDefinitions}"
|
"--definitions=${finalAttrs.finalRepartDefinitions}"
|
||||||
"--split=${lib.boolToString split}"
|
"--split=${lib.boolToString split}"
|
||||||
"--json=pretty"
|
"--json=pretty"
|
||||||
] ++ lib.optionals createEmpty [
|
]
|
||||||
|
++ lib.optionals createEmpty [
|
||||||
"--empty=create"
|
"--empty=create"
|
||||||
] ++ lib.optionals (sectorSize != null) [
|
]
|
||||||
|
++ lib.optionals (sectorSize != null) [
|
||||||
"--sector-size=${toString sectorSize}"
|
"--sector-size=${toString sectorSize}"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -167,7 +210,8 @@ in
|
||||||
runHook postBuild
|
runHook postBuild
|
||||||
'';
|
'';
|
||||||
|
|
||||||
installPhase = ''
|
installPhase =
|
||||||
|
''
|
||||||
runHook preInstall
|
runHook preInstall
|
||||||
|
|
||||||
mkdir -p $out
|
mkdir -p $out
|
||||||
|
@ -175,14 +219,14 @@ in
|
||||||
# Compression is implemented in the same derivation as opposed to in a
|
# Compression is implemented in the same derivation as opposed to in a
|
||||||
# separate derivation to allow users to save disk space. Disk images are
|
# 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.
|
# already very space intensive so we want to allow users to mitigate this.
|
||||||
+ lib.optionalString compression.enable
|
+ lib.optionalString compression.enable ''
|
||||||
''
|
|
||||||
for f in ${imageFileBasename}*; do
|
for f in ${imageFileBasename}*; do
|
||||||
echo "Compressing $f with ${compression.algorithm}..."
|
echo "Compressing $f with ${compression.algorithm}..."
|
||||||
# Keep the original file when compressing and only delete it afterwards
|
# Keep the original file when compressing and only delete it afterwards
|
||||||
${compressionCommand} $f && rm $f
|
${compressionCommand} $f && rm $f
|
||||||
done
|
done
|
||||||
'' + ''
|
''
|
||||||
|
+ ''
|
||||||
mv -v repart-output.json ${imageFileBasename}* $out
|
mv -v repart-output.json ${imageFileBasename}* $out
|
||||||
|
|
||||||
runHook postInstall
|
runHook postInstall
|
||||||
|
@ -191,4 +235,5 @@ in
|
||||||
passthru = {
|
passthru = {
|
||||||
inherit amendRepartDefinitions;
|
inherit amendRepartDefinitions;
|
||||||
};
|
};
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
|
@ -1,7 +1,13 @@
|
||||||
# This module exposes options to build a disk image with a GUID Partition Table
|
# This module exposes options to build a disk image with a GUID Partition Table
|
||||||
# (GPT). It uses systemd-repart to build the image.
|
# (GPT). It uses systemd-repart to build the image.
|
||||||
|
|
||||||
{ config, pkgs, lib, utils, ... }:
|
{
|
||||||
|
config,
|
||||||
|
pkgs,
|
||||||
|
lib,
|
||||||
|
utils,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
let
|
let
|
||||||
cfg = config.image.repart;
|
cfg = config.image.repart;
|
||||||
|
@ -27,7 +33,9 @@ let
|
||||||
};
|
};
|
||||||
|
|
||||||
contents = lib.mkOption {
|
contents = lib.mkOption {
|
||||||
type = with lib.types; attrsOf (submodule {
|
type =
|
||||||
|
with lib.types;
|
||||||
|
attrsOf (submodule {
|
||||||
options = {
|
options = {
|
||||||
source = lib.mkOption {
|
source = lib.mkOption {
|
||||||
type = types.path;
|
type = types.path;
|
||||||
|
@ -48,7 +56,14 @@ let
|
||||||
};
|
};
|
||||||
|
|
||||||
repartConfig = lib.mkOption {
|
repartConfig = lib.mkOption {
|
||||||
type = with lib.types; attrsOf (oneOf [ str int bool (listOf str) ]);
|
type =
|
||||||
|
with lib.types;
|
||||||
|
attrsOf (oneOf [
|
||||||
|
str
|
||||||
|
int
|
||||||
|
bool
|
||||||
|
(listOf str)
|
||||||
|
]);
|
||||||
example = {
|
example = {
|
||||||
Type = "home";
|
Type = "home";
|
||||||
SizeMinBytes = "512M";
|
SizeMinBytes = "512M";
|
||||||
|
@ -63,7 +78,9 @@ let
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
mkfsOptionsToEnv = opts: lib.mapAttrs' (fsType: options: {
|
mkfsOptionsToEnv =
|
||||||
|
opts:
|
||||||
|
lib.mapAttrs' (fsType: options: {
|
||||||
name = "SYSTEMD_REPART_MKFS_OPTIONS_${lib.toUpper fsType}";
|
name = "SYSTEMD_REPART_MKFS_OPTIONS_${lib.toUpper fsType}";
|
||||||
value = builtins.concatStringsSep " " options;
|
value = builtins.concatStringsSep " " options;
|
||||||
}) opts;
|
}) opts;
|
||||||
|
@ -113,7 +130,11 @@ in
|
||||||
enable = lib.mkEnableOption "Image compression";
|
enable = lib.mkEnableOption "Image compression";
|
||||||
|
|
||||||
algorithm = lib.mkOption {
|
algorithm = lib.mkOption {
|
||||||
type = lib.types.enum [ "zstd" "xz" "zstd-seekable" ];
|
type = lib.types.enum [
|
||||||
|
"zstd"
|
||||||
|
"xz"
|
||||||
|
"zstd-seekable"
|
||||||
|
];
|
||||||
default = "zstd";
|
default = "zstd";
|
||||||
description = "Compression algorithm";
|
description = "Compression algorithm";
|
||||||
};
|
};
|
||||||
|
@ -159,7 +180,10 @@ in
|
||||||
package = lib.mkPackageOption pkgs "systemd-repart" {
|
package = lib.mkPackageOption pkgs "systemd-repart" {
|
||||||
# We use buildPackages so that repart images are built with the build
|
# We use buildPackages so that repart images are built with the build
|
||||||
# platform's systemd, allowing for cross-compiled systems to work.
|
# platform's systemd, allowing for cross-compiled systems to work.
|
||||||
default = [ "buildPackages" "systemd" ];
|
default = [
|
||||||
|
"buildPackages"
|
||||||
|
"systemd"
|
||||||
|
];
|
||||||
example = "pkgs.buildPackages.systemdMinimal.override { withCryptsetup = true; }";
|
example = "pkgs.buildPackages.systemdMinimal.override { withCryptsetup = true; }";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -196,7 +220,7 @@ in
|
||||||
|
|
||||||
mkfsOptions = lib.mkOption {
|
mkfsOptions = lib.mkOption {
|
||||||
type = with lib.types; attrsOf (listOf str);
|
type = with lib.types; attrsOf (listOf str);
|
||||||
default = {};
|
default = { };
|
||||||
example = lib.literalExpression ''
|
example = lib.literalExpression ''
|
||||||
{
|
{
|
||||||
vfat = [ "-S 512" "-c" ];
|
vfat = [ "-S 512" "-c" ];
|
||||||
|
@ -230,7 +254,8 @@ in
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
|
|
||||||
assertions = lib.mapAttrsToList (fileName: partitionConfig:
|
assertions = lib.mapAttrsToList (
|
||||||
|
fileName: partitionConfig:
|
||||||
let
|
let
|
||||||
inherit (partitionConfig) repartConfig;
|
inherit (partitionConfig) repartConfig;
|
||||||
labelLength = builtins.stringLength repartConfig.Label;
|
labelLength = builtins.stringLength repartConfig.Label;
|
||||||
|
@ -240,52 +265,59 @@ in
|
||||||
message = ''
|
message = ''
|
||||||
The partition label '${repartConfig.Label}'
|
The partition label '${repartConfig.Label}'
|
||||||
defined for '${fileName}' is ${toString labelLength} characters long,
|
defined for '${fileName}' is ${toString labelLength} characters long,
|
||||||
but the maximum label length supported by UEFI is ${toString
|
but the maximum label length supported by UEFI is ${toString GPTMaxLabelLength}.
|
||||||
GPTMaxLabelLength}.
|
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
) cfg.partitions;
|
) cfg.partitions;
|
||||||
|
|
||||||
warnings = lib.filter (v: v != null) (lib.mapAttrsToList (fileName: partitionConfig:
|
warnings = lib.filter (v: v != null) (
|
||||||
|
lib.mapAttrsToList (
|
||||||
|
fileName: partitionConfig:
|
||||||
let
|
let
|
||||||
inherit (partitionConfig) repartConfig;
|
inherit (partitionConfig) repartConfig;
|
||||||
suggestedMaxLabelLength = GPTMaxLabelLength - 2;
|
suggestedMaxLabelLength = GPTMaxLabelLength - 2;
|
||||||
labelLength = builtins.stringLength repartConfig.Label;
|
labelLength = builtins.stringLength repartConfig.Label;
|
||||||
in
|
in
|
||||||
if (repartConfig ? Label && labelLength >= suggestedMaxLabelLength) then ''
|
if (repartConfig ? Label && labelLength >= suggestedMaxLabelLength) then
|
||||||
|
''
|
||||||
The partition label '${repartConfig.Label}'
|
The partition label '${repartConfig.Label}'
|
||||||
defined for '${fileName}' is ${toString labelLength} characters long.
|
defined for '${fileName}' is ${toString labelLength} characters long.
|
||||||
The suggested maximum label length is ${toString
|
The suggested maximum label length is ${toString suggestedMaxLabelLength}.
|
||||||
suggestedMaxLabelLength}.
|
|
||||||
|
|
||||||
If you use sytemd-sysupdate style A/B updates, this might
|
If you use sytemd-sysupdate style A/B updates, this might
|
||||||
not leave enough space to increment the version number included in
|
not leave enough space to increment the version number included in
|
||||||
the label in a future release. For example, if your label is
|
the label in a future release. For example, if your label is
|
||||||
${toString GPTMaxLabelLength} characters long (the maximum enforced by UEFI) and
|
${toString GPTMaxLabelLength} characters long (the maximum enforced by UEFI) and
|
||||||
you're at version 9, you cannot increment this to 10.
|
you're at version 9, you cannot increment this to 10.
|
||||||
'' else null
|
''
|
||||||
) cfg.partitions);
|
else
|
||||||
|
null
|
||||||
|
) cfg.partitions
|
||||||
|
);
|
||||||
|
|
||||||
image.repart =
|
image.repart =
|
||||||
let
|
let
|
||||||
version = config.image.repart.version;
|
version = config.image.repart.version;
|
||||||
versionInfix = if version != null then "_${version}" else "";
|
versionInfix = if version != null then "_${version}" else "";
|
||||||
compressionSuffix = lib.optionalString cfg.compression.enable
|
compressionSuffix =
|
||||||
|
lib.optionalString cfg.compression.enable
|
||||||
{
|
{
|
||||||
"zstd" = ".zst";
|
"zstd" = ".zst";
|
||||||
"xz" = ".xz";
|
"xz" = ".xz";
|
||||||
"zstd-seekable" = ".zst";
|
"zstd-seekable" = ".zst";
|
||||||
}."${cfg.compression.algorithm}";
|
}
|
||||||
|
."${cfg.compression.algorithm}";
|
||||||
|
|
||||||
makeClosure = paths: pkgs.closureInfo { rootPaths = paths; };
|
makeClosure = paths: pkgs.closureInfo { rootPaths = paths; };
|
||||||
|
|
||||||
# Add the closure of the provided Nix store paths to cfg.partitions so
|
# Add the closure of the provided Nix store paths to cfg.partitions so
|
||||||
# that amend-repart-definitions.py can read it.
|
# that amend-repart-definitions.py can read it.
|
||||||
addClosure = _name: partitionConfig: partitionConfig // (
|
addClosure =
|
||||||
lib.optionalAttrs
|
_name: partitionConfig:
|
||||||
(partitionConfig.storePaths or [ ] != [ ])
|
partitionConfig
|
||||||
{ closure = "${makeClosure partitionConfig.storePaths}/store-paths"; }
|
// (lib.optionalAttrs (partitionConfig.storePaths or [ ] != [ ]) {
|
||||||
);
|
closure = "${makeClosure partitionConfig.storePaths}/store-paths";
|
||||||
|
});
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
name = lib.mkIf (config.system.image.id != null) (lib.mkOptionDefault config.system.image.id);
|
name = lib.mkIf (config.system.image.id != null) (lib.mkOptionDefault config.system.image.id);
|
||||||
|
@ -296,11 +328,14 @@ in
|
||||||
# Generally default to slightly faster than default compression
|
# Generally default to slightly faster than default compression
|
||||||
# levels under the assumption that most of the building will be done
|
# levels under the assumption that most of the building will be done
|
||||||
# for development and release builds will be customized.
|
# for development and release builds will be customized.
|
||||||
level = lib.mkOptionDefault {
|
level =
|
||||||
|
lib.mkOptionDefault
|
||||||
|
{
|
||||||
"zstd" = 3;
|
"zstd" = 3;
|
||||||
"xz" = 3;
|
"xz" = 3;
|
||||||
"zstd-seekable" = 3;
|
"zstd-seekable" = 3;
|
||||||
}."${cfg.compression.algorithm}";
|
}
|
||||||
|
."${cfg.compression.algorithm}";
|
||||||
};
|
};
|
||||||
|
|
||||||
finalPartitions = lib.mapAttrs addClosure cfg.partitions;
|
finalPartitions = lib.mapAttrs addClosure cfg.partitions;
|
||||||
|
@ -308,27 +343,37 @@ in
|
||||||
|
|
||||||
system.build.image =
|
system.build.image =
|
||||||
let
|
let
|
||||||
fileSystems = lib.filter
|
fileSystems = lib.filter (f: f != null) (
|
||||||
(f: f != null)
|
lib.mapAttrsToList (_n: v: v.repartConfig.Format or null) cfg.partitions
|
||||||
(lib.mapAttrsToList (_n: v: v.repartConfig.Format or null) cfg.partitions);
|
);
|
||||||
|
|
||||||
|
|
||||||
format = pkgs.formats.ini { listsAsDuplicateKeys = true; };
|
format = pkgs.formats.ini { listsAsDuplicateKeys = true; };
|
||||||
|
|
||||||
definitionsDirectory = utils.systemdUtils.lib.definitions
|
definitionsDirectory = utils.systemdUtils.lib.definitions "repart.d" format (
|
||||||
"repart.d"
|
lib.mapAttrs (_n: v: { Partition = v.repartConfig; }) cfg.finalPartitions
|
||||||
format
|
);
|
||||||
(lib.mapAttrs (_n: v: { Partition = v.repartConfig; }) cfg.finalPartitions);
|
|
||||||
|
|
||||||
mkfsEnv = mkfsOptionsToEnv cfg.mkfsOptions;
|
mkfsEnv = mkfsOptionsToEnv cfg.mkfsOptions;
|
||||||
in
|
in
|
||||||
pkgs.callPackage ./repart-image.nix {
|
pkgs.callPackage ./repart-image.nix {
|
||||||
systemd = cfg.package;
|
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;
|
inherit fileSystems definitionsDirectory mkfsEnv;
|
||||||
};
|
};
|
||||||
|
|
||||||
meta.maintainers = with lib.maintainers; [ nikstur willibutz ];
|
meta.maintainers = with lib.maintainers; [
|
||||||
|
nikstur
|
||||||
|
willibutz
|
||||||
|
];
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,15 @@
|
||||||
# This module contains the basic configuration for building a NixOS
|
# This module contains the basic configuration for building a NixOS
|
||||||
# installation CD.
|
# installation CD.
|
||||||
{ config, lib, options, pkgs, ... }:
|
|
||||||
{
|
{
|
||||||
imports =
|
config,
|
||||||
[ ./iso-image.nix
|
lib,
|
||||||
|
options,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
./iso-image.nix
|
||||||
|
|
||||||
# Profiles of this basic installation CD.
|
# Profiles of this basic installation CD.
|
||||||
../../profiles/base.nix
|
../../profiles/base.nix
|
||||||
|
|
|
@ -1,7 +1,13 @@
|
||||||
# This module creates netboot media containing the given NixOS
|
# This module creates netboot media containing the given NixOS
|
||||||
# configuration.
|
# configuration.
|
||||||
|
|
||||||
{ config, lib, pkgs, modulesPath, ... }:
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
modulesPath,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
with lib;
|
with lib;
|
||||||
|
|
||||||
|
@ -36,28 +42,30 @@ with lib;
|
||||||
# here and it causes a cyclic dependency.
|
# here and it causes a cyclic dependency.
|
||||||
boot.loader.grub.enable = false;
|
boot.loader.grub.enable = false;
|
||||||
|
|
||||||
fileSystems."/" = mkImageMediaOverride
|
fileSystems."/" = mkImageMediaOverride {
|
||||||
{ fsType = "tmpfs";
|
fsType = "tmpfs";
|
||||||
options = [ "mode=0755" ];
|
options = [ "mode=0755" ];
|
||||||
};
|
};
|
||||||
|
|
||||||
# In stage 1, mount a tmpfs on top of /nix/store (the squashfs
|
# In stage 1, mount a tmpfs on top of /nix/store (the squashfs
|
||||||
# image) to make this a live CD.
|
# image) to make this a live CD.
|
||||||
fileSystems."/nix/.ro-store" = mkImageMediaOverride
|
fileSystems."/nix/.ro-store" = mkImageMediaOverride {
|
||||||
{ fsType = "squashfs";
|
fsType = "squashfs";
|
||||||
device = "../nix-store.squashfs";
|
device = "../nix-store.squashfs";
|
||||||
options = [ "loop" ] ++ lib.optional (config.boot.kernelPackages.kernel.kernelAtLeast "6.2") "threads=multi";
|
options = [
|
||||||
|
"loop"
|
||||||
|
] ++ lib.optional (config.boot.kernelPackages.kernel.kernelAtLeast "6.2") "threads=multi";
|
||||||
neededForBoot = true;
|
neededForBoot = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
fileSystems."/nix/.rw-store" = mkImageMediaOverride
|
fileSystems."/nix/.rw-store" = mkImageMediaOverride {
|
||||||
{ fsType = "tmpfs";
|
fsType = "tmpfs";
|
||||||
options = [ "mode=0755" ];
|
options = [ "mode=0755" ];
|
||||||
neededForBoot = true;
|
neededForBoot = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
fileSystems."/nix/store" = mkImageMediaOverride
|
fileSystems."/nix/store" = mkImageMediaOverride {
|
||||||
{ overlay = {
|
overlay = {
|
||||||
lowerdir = [ "/nix/.ro-store" ];
|
lowerdir = [ "/nix/.ro-store" ];
|
||||||
upperdir = "/nix/.rw-store/store";
|
upperdir = "/nix/.rw-store/store";
|
||||||
workdir = "/nix/.rw-store/work";
|
workdir = "/nix/.rw-store/work";
|
||||||
|
@ -65,14 +73,19 @@ with lib;
|
||||||
neededForBoot = true;
|
neededForBoot = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
boot.initrd.availableKernelModules = [ "squashfs" "overlay" ];
|
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
|
# Closures to be copied to the Nix store, namely the init
|
||||||
# script and the top-level system configuration directory.
|
# script and the top-level system configuration directory.
|
||||||
netboot.storeContents =
|
netboot.storeContents = [ config.system.build.toplevel ];
|
||||||
[ config.system.build.toplevel ];
|
|
||||||
|
|
||||||
# Create the squashfs image that contains the Nix store.
|
# Create the squashfs image that contains the Nix store.
|
||||||
system.build.squashfsStore = pkgs.callPackage ../../../lib/make-squashfs.nix {
|
system.build.squashfsStore = pkgs.callPackage ../../../lib/make-squashfs.nix {
|
||||||
|
@ -80,14 +93,14 @@ with lib;
|
||||||
comp = config.netboot.squashfsCompression;
|
comp = config.netboot.squashfsCompression;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
# Create the initrd
|
# Create the initrd
|
||||||
system.build.netbootRamdisk = pkgs.makeInitrdNG {
|
system.build.netbootRamdisk = pkgs.makeInitrdNG {
|
||||||
inherit (config.boot.initrd) compressor;
|
inherit (config.boot.initrd) compressor;
|
||||||
prepend = [ "${config.system.build.initialRamdisk}/initrd" ];
|
prepend = [ "${config.system.build.initialRamdisk}/initrd" ];
|
||||||
|
|
||||||
contents =
|
contents = [
|
||||||
[ { source = config.system.build.squashfsStore;
|
{
|
||||||
|
source = config.system.build.squashfsStore;
|
||||||
target = "/nix-store.squashfs";
|
target = "/nix-store.squashfs";
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
@ -137,7 +150,9 @@ with lib;
|
||||||
image.filePath = "tarball/${config.image.fileName}";
|
image.filePath = "tarball/${config.image.fileName}";
|
||||||
system.nixos.tags = [ "kexec" ];
|
system.nixos.tags = [ "kexec" ];
|
||||||
system.build.image = config.system.build.kexecTarball;
|
system.build.image = config.system.build.kexecTarball;
|
||||||
system.build.kexecTarball = pkgs.callPackage "${toString modulesPath}/../lib/make-system-tarball.nix" {
|
system.build.kexecTarball =
|
||||||
|
pkgs.callPackage "${toString modulesPath}/../lib/make-system-tarball.nix"
|
||||||
|
{
|
||||||
fileName = config.image.baseName;
|
fileName = config.image.baseName;
|
||||||
storeContents = [
|
storeContents = [
|
||||||
{
|
{
|
||||||
|
@ -145,7 +160,7 @@ with lib;
|
||||||
symlink = "/kexec_nixos";
|
symlink = "/kexec_nixos";
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
contents = [];
|
contents = [ ];
|
||||||
};
|
};
|
||||||
|
|
||||||
boot.loader.timeout = 10;
|
boot.loader.timeout = 10;
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
# To build, use:
|
# To build, use:
|
||||||
# nix-build nixos -I nixos-config=nixos/modules/installer/sd-card/sd-image-aarch64.nix -A config.system.build.sdImage
|
# 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 = [
|
imports = [
|
||||||
|
@ -16,10 +21,15 @@
|
||||||
# The serial ports listed here are:
|
# The serial ports listed here are:
|
||||||
# - ttyS0: for Tegra (Jetson TX1)
|
# - ttyS0: for Tegra (Jetson TX1)
|
||||||
# - ttyAMA0: for QEMU's -machine virt
|
# - 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 = {
|
sdImage = {
|
||||||
populateFirmwareCommands = let
|
populateFirmwareCommands =
|
||||||
|
let
|
||||||
configTxt = pkgs.writeText "config.txt" ''
|
configTxt = pkgs.writeText "config.txt" ''
|
||||||
[pi3]
|
[pi3]
|
||||||
kernel=u-boot-rpi3.bin
|
kernel=u-boot-rpi3.bin
|
||||||
|
@ -61,7 +71,8 @@
|
||||||
# when attempting to show low-voltage or overtemperature warnings.
|
# when attempting to show low-voltage or overtemperature warnings.
|
||||||
avoid_warnings=1
|
avoid_warnings=1
|
||||||
'';
|
'';
|
||||||
in ''
|
in
|
||||||
|
''
|
||||||
(cd ${pkgs.raspberrypifw}/share/raspberrypi/boot && cp bootcode.bin fixup*.dat start*.elf $NIX_BUILD_TOP/firmware/)
|
(cd ${pkgs.raspberrypifw}/share/raspberrypi/boot && cp bootcode.bin fixup*.dat start*.elf $NIX_BUILD_TOP/firmware/)
|
||||||
|
|
||||||
# Add the config
|
# Add the config
|
||||||
|
|
|
@ -11,24 +11,36 @@
|
||||||
# The derivation for the SD image will be placed in
|
# The derivation for the SD image will be placed in
|
||||||
# config.system.build.sdImage
|
# config.system.build.sdImage
|
||||||
|
|
||||||
{ config, lib, pkgs, ... }:
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
with lib;
|
with lib;
|
||||||
|
|
||||||
let
|
let
|
||||||
rootfsImage = pkgs.callPackage ../../../lib/make-ext4-fs.nix ({
|
rootfsImage = pkgs.callPackage ../../../lib/make-ext4-fs.nix (
|
||||||
|
{
|
||||||
inherit (config.sdImage) storePaths;
|
inherit (config.sdImage) storePaths;
|
||||||
compressImage = config.sdImage.compressImage;
|
compressImage = config.sdImage.compressImage;
|
||||||
populateImageCommands = config.sdImage.populateRootCommands;
|
populateImageCommands = config.sdImage.populateRootCommands;
|
||||||
volumeLabel = "NIXOS_SD";
|
volumeLabel = "NIXOS_SD";
|
||||||
} // optionalAttrs (config.sdImage.rootPartitionUUID != null) {
|
}
|
||||||
|
// optionalAttrs (config.sdImage.rootPartitionUUID != null) {
|
||||||
uuid = config.sdImage.rootPartitionUUID;
|
uuid = config.sdImage.rootPartitionUUID;
|
||||||
});
|
}
|
||||||
|
);
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
imports = [
|
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" "bootPartitionID" ]
|
||||||
(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.")
|
"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."
|
||||||
|
)
|
||||||
(lib.mkRenamedOptionModuleWith {
|
(lib.mkRenamedOptionModuleWith {
|
||||||
sinceRelease = 2505;
|
sinceRelease = 2505;
|
||||||
from = [
|
from = [
|
||||||
|
@ -180,7 +192,10 @@ in
|
||||||
# Alternatively, this could be removed from the configuration.
|
# Alternatively, this could be removed from the configuration.
|
||||||
# The filesystem is not needed at runtime, it could be treated
|
# The filesystem is not needed at runtime, it could be treated
|
||||||
# as an opaque blob instead of a discrete FAT32 filesystem.
|
# as an opaque blob instead of a discrete FAT32 filesystem.
|
||||||
options = [ "nofail" "noauto" ];
|
options = [
|
||||||
|
"nofail"
|
||||||
|
"noauto"
|
||||||
|
];
|
||||||
};
|
};
|
||||||
"/" = {
|
"/" = {
|
||||||
device = "/dev/disk/by-label/NIXOS_SD";
|
device = "/dev/disk/by-label/NIXOS_SD";
|
||||||
|
@ -194,12 +209,26 @@ in
|
||||||
image.filePath = "sd-card/${config.image.fileName}";
|
image.filePath = "sd-card/${config.image.fileName}";
|
||||||
system.nixos.tags = [ "sd-card" ];
|
system.nixos.tags = [ "sd-card" ];
|
||||||
system.build.image = config.system.build.sdImage;
|
system.build.image = config.system.build.sdImage;
|
||||||
system.build.sdImage = pkgs.callPackage ({ stdenv, dosfstools, e2fsprogs,
|
system.build.sdImage = pkgs.callPackage (
|
||||||
mtools, libfaketime, util-linux, zstd }: stdenv.mkDerivation {
|
{
|
||||||
|
stdenv,
|
||||||
|
dosfstools,
|
||||||
|
e2fsprogs,
|
||||||
|
mtools,
|
||||||
|
libfaketime,
|
||||||
|
util-linux,
|
||||||
|
zstd,
|
||||||
|
}:
|
||||||
|
stdenv.mkDerivation {
|
||||||
name = config.image.fileName;
|
name = config.image.fileName;
|
||||||
|
|
||||||
nativeBuildInputs = [ dosfstools e2fsprogs libfaketime mtools util-linux ]
|
nativeBuildInputs = [
|
||||||
++ lib.optional config.sdImage.compressImage zstd;
|
dosfstools
|
||||||
|
e2fsprogs
|
||||||
|
libfaketime
|
||||||
|
mtools
|
||||||
|
util-linux
|
||||||
|
] ++ lib.optional config.sdImage.compressImage zstd;
|
||||||
|
|
||||||
inherit (config.sdImage) compressImage;
|
inherit (config.sdImage) compressImage;
|
||||||
|
|
||||||
|
@ -277,9 +306,11 @@ in
|
||||||
zstd -T$NIX_BUILD_CORES --rm $img
|
zstd -T$NIX_BUILD_CORES --rm $img
|
||||||
fi
|
fi
|
||||||
'';
|
'';
|
||||||
}) {};
|
}
|
||||||
|
) { };
|
||||||
|
|
||||||
boot.postBootCommands = let
|
boot.postBootCommands =
|
||||||
|
let
|
||||||
expandOnBoot = lib.optionalString config.sdImage.expandOnBoot ''
|
expandOnBoot = lib.optionalString config.sdImage.expandOnBoot ''
|
||||||
# Figure out device names for the boot device and root filesystem.
|
# Figure out device names for the boot device and root filesystem.
|
||||||
rootPart=$(${pkgs.util-linux}/bin/findmnt -n -o SOURCE /)
|
rootPart=$(${pkgs.util-linux}/bin/findmnt -n -o SOURCE /)
|
||||||
|
@ -292,7 +323,8 @@ in
|
||||||
${pkgs.e2fsprogs}/bin/resize2fs $rootPart
|
${pkgs.e2fsprogs}/bin/resize2fs $rootPart
|
||||||
'';
|
'';
|
||||||
nixPathRegistrationFile = config.sdImage.nixPathRegistrationFile;
|
nixPathRegistrationFile = config.sdImage.nixPathRegistrationFile;
|
||||||
in ''
|
in
|
||||||
|
''
|
||||||
# On the first boot do some maintenance tasks
|
# On the first boot do some maintenance tasks
|
||||||
if [ -f ${nixPathRegistrationFile} ]; then
|
if [ -f ${nixPathRegistrationFile} ]; then
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
|
@ -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
|
let
|
||||||
inherit (lib)
|
inherit (lib)
|
||||||
|
@ -31,7 +43,8 @@ let
|
||||||
cfg = config.documentation;
|
cfg = config.documentation;
|
||||||
allOpts = options;
|
allOpts = options;
|
||||||
|
|
||||||
canCacheDocs = m:
|
canCacheDocs =
|
||||||
|
m:
|
||||||
let
|
let
|
||||||
f = import m;
|
f = import m;
|
||||||
instance = f (mapAttrs (n: _: abort "evaluating ${n} for `meta` failed") (functionArgs f));
|
instance = f (mapAttrs (n: _: abort "evaluating ${n} for `meta` failed") (functionArgs f));
|
||||||
|
@ -59,9 +72,11 @@ let
|
||||||
options =
|
options =
|
||||||
let
|
let
|
||||||
scrubbedEval = evalModules {
|
scrubbedEval = evalModules {
|
||||||
modules = [ {
|
modules = [
|
||||||
|
{
|
||||||
_module.check = false;
|
_module.check = false;
|
||||||
} ] ++ docModules.eager;
|
}
|
||||||
|
] ++ docModules.eager;
|
||||||
class = "nixos";
|
class = "nixos";
|
||||||
specialArgs = specialArgs // {
|
specialArgs = specialArgs // {
|
||||||
pkgs = scrubDerivations "pkgs" pkgs;
|
pkgs = scrubDerivations "pkgs" pkgs;
|
||||||
|
@ -71,33 +86,37 @@ let
|
||||||
inherit modulesPath utils;
|
inherit modulesPath utils;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
scrubDerivations = namePrefix: pkgSet: mapAttrs
|
scrubDerivations =
|
||||||
(name: value:
|
namePrefix: pkgSet:
|
||||||
|
mapAttrs (
|
||||||
|
name: value:
|
||||||
let
|
let
|
||||||
wholeName = "${namePrefix}.${name}";
|
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.";
|
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
|
scrubDerivations wholeName value
|
||||||
// optionalAttrs (isDerivation value) {
|
// optionalAttrs (isDerivation value) {
|
||||||
outPath = guard "\${${wholeName}}";
|
outPath = guard "\${${wholeName}}";
|
||||||
drvPath = guard value.drvPath;
|
drvPath = guard value.drvPath;
|
||||||
}
|
}
|
||||||
else value
|
else
|
||||||
)
|
value
|
||||||
pkgSet;
|
) pkgSet;
|
||||||
in scrubbedEval.options;
|
in
|
||||||
|
scrubbedEval.options;
|
||||||
|
|
||||||
baseOptionsJSON =
|
baseOptionsJSON =
|
||||||
let
|
let
|
||||||
filter =
|
filter = builtins.filterSource (
|
||||||
builtins.filterSource
|
n: t:
|
||||||
(n: t:
|
|
||||||
cleanSourceFilter n t
|
cleanSourceFilter n t
|
||||||
&& (t == "directory" -> baseNameOf n != "tests")
|
&& (t == "directory" -> baseNameOf n != "tests")
|
||||||
&& (t == "file" -> hasSuffix ".nix" n)
|
&& (t == "file" -> hasSuffix ".nix" n)
|
||||||
);
|
);
|
||||||
in
|
in
|
||||||
pkgs.runCommand "lazy-options.json" {
|
pkgs.runCommand "lazy-options.json"
|
||||||
|
{
|
||||||
libPath = filter (pkgs.path + "/lib");
|
libPath = filter (pkgs.path + "/lib");
|
||||||
pkgsLibPath = filter (pkgs.path + "/pkgs/pkgs-lib");
|
pkgsLibPath = filter (pkgs.path + "/pkgs/pkgs-lib");
|
||||||
nixosPath = filter (pkgs.path + "/nixos");
|
nixosPath = filter (pkgs.path + "/nixos");
|
||||||
|
@ -107,7 +126,8 @@ let
|
||||||
+ concatMapStringsSep " " (p: ''"${removePrefix "${modulesPath}/" (toString p)}"'') docModules.lazy
|
+ concatMapStringsSep " " (p: ''"${removePrefix "${modulesPath}/" (toString p)}"'') docModules.lazy
|
||||||
+ " ]";
|
+ " ]";
|
||||||
passAsFile = [ "modules" ];
|
passAsFile = [ "modules" ];
|
||||||
} ''
|
}
|
||||||
|
''
|
||||||
export NIX_STORE_DIR=$TMPDIR/store
|
export NIX_STORE_DIR=$TMPDIR/store
|
||||||
export NIX_STATE_DIR=$TMPDIR/state
|
export NIX_STATE_DIR=$TMPDIR/state
|
||||||
${pkgs.buildPackages.nix}/bin/nix-instantiate \
|
${pkgs.buildPackages.nix}/bin/nix-instantiate \
|
||||||
|
@ -139,8 +159,8 @@ let
|
||||||
inherit (cfg.nixos.options) warningsAreErrors;
|
inherit (cfg.nixos.options) warningsAreErrors;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
nixos-help =
|
||||||
nixos-help = let
|
let
|
||||||
helpScript = pkgs.writeShellScriptBin "nixos-help" ''
|
helpScript = pkgs.writeShellScriptBin "nixos-help" ''
|
||||||
# Finds first executable browser in a colon-separated list.
|
# Finds first executable browser in a colon-separated list.
|
||||||
# (see how xdg-open defines BROWSER)
|
# (see how xdg-open defines BROWSER)
|
||||||
|
@ -165,10 +185,11 @@ let
|
||||||
comment = "View NixOS documentation in a web browser";
|
comment = "View NixOS documentation in a web browser";
|
||||||
icon = "nix-snowflake";
|
icon = "nix-snowflake";
|
||||||
exec = "nixos-help";
|
exec = "nixos-help";
|
||||||
categories = ["System"];
|
categories = [ "System" ];
|
||||||
};
|
};
|
||||||
|
|
||||||
in pkgs.symlinkJoin {
|
in
|
||||||
|
pkgs.symlinkJoin {
|
||||||
name = "nixos-help";
|
name = "nixos-help";
|
||||||
paths = [
|
paths = [
|
||||||
helpScript
|
helpScript
|
||||||
|
@ -189,9 +210,12 @@ in
|
||||||
(mkRenamedOptionModule [ "programs" "info" "enable" ] [ "documentation" "info" "enable" ])
|
(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" ])
|
(mkRenamedOptionModule [ "services" "nixosManual" "enable" ] [ "documentation" "nixos" "enable" ])
|
||||||
(mkRemovedOptionModule
|
(mkRemovedOptionModule [
|
||||||
[ "documentation" "nixos" "options" "allowDocBook" ]
|
"documentation"
|
||||||
"DocBook option documentation is no longer supported")
|
"nixos"
|
||||||
|
"options"
|
||||||
|
"allowDocBook"
|
||||||
|
] "DocBook option documentation is no longer supported")
|
||||||
];
|
];
|
||||||
|
|
||||||
options = {
|
options = {
|
||||||
|
@ -280,7 +304,7 @@ in
|
||||||
|
|
||||||
nixos.extraModules = mkOption {
|
nixos.extraModules = mkOption {
|
||||||
type = types.listOf types.raw;
|
type = types.listOf types.raw;
|
||||||
default = [];
|
default = [ ];
|
||||||
description = ''
|
description = ''
|
||||||
Modules for which to show options even when not imported.
|
Modules for which to show options even when not imported.
|
||||||
'';
|
'';
|
||||||
|
@ -380,9 +404,13 @@ in
|
||||||
(mkIf cfg.nixos.enable {
|
(mkIf cfg.nixos.enable {
|
||||||
system.build.manual = manual;
|
system.build.manual = manual;
|
||||||
|
|
||||||
environment.systemPackages = []
|
environment.systemPackages =
|
||||||
|
[ ]
|
||||||
++ optional cfg.man.enable manual.nixos-configuration-reference-manpage
|
++ 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
|
||||||
|
];
|
||||||
})
|
})
|
||||||
|
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -790,6 +790,7 @@
|
||||||
./services/misc/dictd.nix
|
./services/misc/dictd.nix
|
||||||
./services/misc/disnix.nix
|
./services/misc/disnix.nix
|
||||||
./services/misc/docker-registry.nix
|
./services/misc/docker-registry.nix
|
||||||
|
./services/misc/docling-serve.nix
|
||||||
./services/misc/domoticz.nix
|
./services/misc/domoticz.nix
|
||||||
./services/misc/duckdns.nix
|
./services/misc/duckdns.nix
|
||||||
./services/misc/duckling.nix
|
./services/misc/duckling.nix
|
||||||
|
@ -1611,6 +1612,7 @@
|
||||||
./services/web-apps/rutorrent.nix
|
./services/web-apps/rutorrent.nix
|
||||||
./services/web-apps/screego.nix
|
./services/web-apps/screego.nix
|
||||||
./services/web-apps/sftpgo.nix
|
./services/web-apps/sftpgo.nix
|
||||||
|
./services/web-apps/strfry.nix
|
||||||
./services/web-apps/suwayomi-server.nix
|
./services/web-apps/suwayomi-server.nix
|
||||||
./services/web-apps/readeck.nix
|
./services/web-apps/readeck.nix
|
||||||
./services/web-apps/rss-bridge.nix
|
./services/web-apps/rss-bridge.nix
|
||||||
|
|
|
@ -1,11 +1,16 @@
|
||||||
# Provide a basic configuration for installation devices like CDs.
|
# Provide a basic configuration for installation devices like CDs.
|
||||||
{ config, pkgs, lib, ... }:
|
{
|
||||||
|
config,
|
||||||
|
pkgs,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
with lib;
|
with lib;
|
||||||
|
|
||||||
{
|
{
|
||||||
imports =
|
imports = [
|
||||||
[ # Enable devices which are usually scanned, because we don't know the
|
# Enable devices which are usually scanned, because we don't know the
|
||||||
# target system.
|
# target system.
|
||||||
../installer/scan/detected.nix
|
../installer/scan/detected.nix
|
||||||
../installer/scan/not-detected.nix
|
../installer/scan/not-detected.nix
|
||||||
|
@ -31,7 +36,11 @@ with lib;
|
||||||
# Use less privileged nixos user
|
# Use less privileged nixos user
|
||||||
users.users.nixos = {
|
users.users.nixos = {
|
||||||
isNormalUser = true;
|
isNormalUser = true;
|
||||||
extraGroups = [ "wheel" "networkmanager" "video" ];
|
extraGroups = [
|
||||||
|
"wheel"
|
||||||
|
"networkmanager"
|
||||||
|
"video"
|
||||||
|
];
|
||||||
# Allow the graphical user to login without password
|
# Allow the graphical user to login without password
|
||||||
initialHashedPassword = "";
|
initialHashedPassword = "";
|
||||||
};
|
};
|
||||||
|
@ -52,7 +61,8 @@ with lib;
|
||||||
services.getty.autologinUser = "nixos";
|
services.getty.autologinUser = "nixos";
|
||||||
|
|
||||||
# Some more help text.
|
# Some more help text.
|
||||||
services.getty.helpLine = ''
|
services.getty.helpLine =
|
||||||
|
''
|
||||||
The "nixos" and "root" accounts have empty passwords.
|
The "nixos" and "root" accounts have empty passwords.
|
||||||
|
|
||||||
To log in over ssh you must set a password for either "nixos" or "root"
|
To log in over ssh you must set a password for either "nixos" or "root"
|
||||||
|
@ -62,7 +72,8 @@ with lib;
|
||||||
If you need a wireless connection, type
|
If you need a wireless connection, type
|
||||||
`sudo systemctl start wpa_supplicant` and configure a
|
`sudo systemctl start wpa_supplicant` and configure a
|
||||||
network using `wpa_cli`. See the NixOS manual for details.
|
network using `wpa_cli`. See the NixOS manual for details.
|
||||||
'' + optionalString config.services.xserver.enable ''
|
''
|
||||||
|
+ optionalString config.services.xserver.enable ''
|
||||||
|
|
||||||
Type `sudo systemctl start display-manager' to
|
Type `sudo systemctl start display-manager' to
|
||||||
start the graphical user interface.
|
start the graphical user interface.
|
||||||
|
@ -81,7 +92,7 @@ with lib;
|
||||||
# Enable wpa_supplicant, but don't start it by default.
|
# Enable wpa_supplicant, but don't start it by default.
|
||||||
networking.wireless.enable = mkDefault true;
|
networking.wireless.enable = mkDefault true;
|
||||||
networking.wireless.userControlled.enable = 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.
|
# Tell the Nix evaluator to garbage collect more aggressively.
|
||||||
# This is desirable in memory-constrained environments that don't
|
# This is desirable in memory-constrained environments that don't
|
||||||
|
@ -97,7 +108,8 @@ with lib;
|
||||||
|
|
||||||
# To speed up installation a little bit, include the complete
|
# To speed up installation a little bit, include the complete
|
||||||
# stdenv in the Nix store on the CD.
|
# stdenv in the Nix store on the CD.
|
||||||
system.extraDependencies = with pkgs;
|
system.extraDependencies =
|
||||||
|
with pkgs;
|
||||||
[
|
[
|
||||||
stdenv
|
stdenv
|
||||||
stdenvNoCC # for runCommand
|
stdenvNoCC # for runCommand
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
{ config, lib, pkgs, ... }:
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
let
|
let
|
||||||
|
|
||||||
|
@ -7,13 +12,13 @@ let
|
||||||
cfg = config.programs.fish;
|
cfg = config.programs.fish;
|
||||||
|
|
||||||
fishAbbrs = lib.concatStringsSep "\n" (
|
fishAbbrs = lib.concatStringsSep "\n" (
|
||||||
lib.mapAttrsToList (k: v: "abbr -ag ${k} ${lib.escapeShellArg v}")
|
lib.mapAttrsToList (k: v: "abbr -ag ${k} ${lib.escapeShellArg v}") cfg.shellAbbrs
|
||||||
cfg.shellAbbrs
|
|
||||||
);
|
);
|
||||||
|
|
||||||
fishAliases = lib.concatStringsSep "\n" (
|
fishAliases = lib.concatStringsSep "\n" (
|
||||||
lib.mapAttrsToList (k: v: "alias ${k} ${lib.escapeShellArg v}")
|
lib.mapAttrsToList (k: v: "alias ${k} ${lib.escapeShellArg v}") (
|
||||||
(lib.filterAttrs (k: v: v != null) cfg.shellAliases)
|
lib.filterAttrs (k: v: v != null) cfg.shellAliases
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
envShellInit = pkgs.writeText "shellInit" cfge.shellInit;
|
envShellInit = pkgs.writeText "shellInit" cfge.shellInit;
|
||||||
|
@ -22,7 +27,8 @@ let
|
||||||
|
|
||||||
envInteractiveShellInit = pkgs.writeText "interactiveShellInit" cfge.interactiveShellInit;
|
envInteractiveShellInit = pkgs.writeText "interactiveShellInit" cfge.interactiveShellInit;
|
||||||
|
|
||||||
sourceEnv = file:
|
sourceEnv =
|
||||||
|
file:
|
||||||
if cfg.useBabelfish then
|
if cfg.useBabelfish then
|
||||||
"source /etc/fish/${file}.fish"
|
"source /etc/fish/${file}.fish"
|
||||||
else
|
else
|
||||||
|
@ -32,7 +38,8 @@ let
|
||||||
set -e fish_function_path[1]
|
set -e fish_function_path[1]
|
||||||
'';
|
'';
|
||||||
|
|
||||||
babelfishTranslate = path: name:
|
babelfishTranslate =
|
||||||
|
path: name:
|
||||||
pkgs.runCommand "${name}.fish" {
|
pkgs.runCommand "${name}.fish" {
|
||||||
preferLocalBuild = true;
|
preferLocalBuild = true;
|
||||||
nativeBuildInputs = [ pkgs.babelfish ];
|
nativeBuildInputs = [ pkgs.babelfish ];
|
||||||
|
@ -90,7 +97,7 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
shellAbbrs = lib.mkOption {
|
shellAbbrs = lib.mkOption {
|
||||||
default = {};
|
default = { };
|
||||||
example = {
|
example = {
|
||||||
gco = "git checkout";
|
gco = "git checkout";
|
||||||
npu = "nix-prefetch-url";
|
npu = "nix-prefetch-url";
|
||||||
|
@ -102,7 +109,7 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
shellAliases = lib.mkOption {
|
shellAliases = lib.mkOption {
|
||||||
default = {};
|
default = { };
|
||||||
description = ''
|
description = ''
|
||||||
Set of aliases for fish shell, which overrides {option}`environment.shellAliases`.
|
Set of aliases for fish shell, which overrides {option}`environment.shellAliases`.
|
||||||
See {option}`environment.shellAliases` for an option format description.
|
See {option}`environment.shellAliases` for an option format description.
|
||||||
|
@ -154,16 +161,16 @@ in
|
||||||
documentation.man.generateCaches = lib.mkDefault true;
|
documentation.man.generateCaches = lib.mkDefault true;
|
||||||
|
|
||||||
environment = lib.mkMerge [
|
environment = lib.mkMerge [
|
||||||
(lib.mkIf cfg.useBabelfish
|
(lib.mkIf cfg.useBabelfish {
|
||||||
{
|
etc."fish/setEnvironment.fish".source =
|
||||||
etc."fish/setEnvironment.fish".source = babelfishTranslate config.system.build.setEnvironment "setEnvironment";
|
babelfishTranslate config.system.build.setEnvironment "setEnvironment";
|
||||||
etc."fish/shellInit.fish".source = babelfishTranslate envShellInit "shellInit";
|
etc."fish/shellInit.fish".source = babelfishTranslate envShellInit "shellInit";
|
||||||
etc."fish/loginShellInit.fish".source = babelfishTranslate envLoginShellInit "loginShellInit";
|
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/shellInit".source = envShellInit;
|
||||||
etc."fish/foreign-env/loginShellInit".source = envLoginShellInit;
|
etc."fish/foreign-env/loginShellInit".source = envLoginShellInit;
|
||||||
etc."fish/foreign-env/interactiveShellInit".source = envInteractiveShellInit;
|
etc."fish/foreign-env/interactiveShellInit".source = envInteractiveShellInit;
|
||||||
|
@ -171,14 +178,15 @@ in
|
||||||
|
|
||||||
{
|
{
|
||||||
etc."fish/nixos-env-preinit.fish".text =
|
etc."fish/nixos-env-preinit.fish".text =
|
||||||
if cfg.useBabelfish
|
if cfg.useBabelfish then
|
||||||
then ''
|
''
|
||||||
# source the NixOS environment config
|
# source the NixOS environment config
|
||||||
if [ -z "$__NIXOS_SET_ENVIRONMENT_DONE" ]
|
if [ -z "$__NIXOS_SET_ENVIRONMENT_DONE" ]
|
||||||
source /etc/fish/setEnvironment.fish
|
source /etc/fish/setEnvironment.fish
|
||||||
end
|
end
|
||||||
''
|
''
|
||||||
else ''
|
else
|
||||||
|
''
|
||||||
# This happens before $__fish_datadir/config.fish sets fish_function_path, so it is currently
|
# 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
|
# 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
|
set fish_function_path ${pkgs.fishPlugins.foreign-env}/share/fish/vendor_functions.d $__fish_datadir/functions
|
||||||
|
@ -259,15 +267,24 @@ in
|
||||||
preferLocalBuild = true;
|
preferLocalBuild = true;
|
||||||
allowSubstitutes = false;
|
allowSubstitutes = false;
|
||||||
};
|
};
|
||||||
generateCompletions = package: pkgs.runCommand
|
generateCompletions =
|
||||||
( with lib.strings; let
|
package:
|
||||||
|
pkgs.runCommand
|
||||||
|
(
|
||||||
|
with lib.strings;
|
||||||
|
let
|
||||||
storeLength = stringLength storeDir + 34; # Nix' StorePath::HashLen + 2 for the separating slash and dash
|
storeLength = stringLength storeDir + 34; # Nix' StorePath::HashLen + 2 for the separating slash and dash
|
||||||
pathName = substring storeLength (stringLength package - storeLength) package;
|
pathName = substring storeLength (stringLength package - storeLength) package;
|
||||||
in (package.name or pathName) + "_fish-completions")
|
in
|
||||||
( { inherit package;
|
(package.name or pathName) + "_fish-completions"
|
||||||
|
)
|
||||||
|
(
|
||||||
|
{
|
||||||
|
inherit package;
|
||||||
preferLocalBuild = true;
|
preferLocalBuild = true;
|
||||||
} //
|
}
|
||||||
lib.optionalAttrs (package ? meta.priority) { meta.priority = package.meta.priority; })
|
// lib.optionalAttrs (package ? meta.priority) { meta.priority = package.meta.priority; }
|
||||||
|
)
|
||||||
''
|
''
|
||||||
mkdir -p $out
|
mkdir -p $out
|
||||||
if [ -d $package/share/man ]; then
|
if [ -d $package/share/man ]; then
|
||||||
|
@ -284,7 +301,8 @@ in
|
||||||
|
|
||||||
# include programs that bring their own completions
|
# include programs that bring their own completions
|
||||||
{
|
{
|
||||||
pathsToLink = []
|
pathsToLink =
|
||||||
|
[ ]
|
||||||
++ lib.optional cfg.vendor.config.enable "/share/fish/vendor_conf.d"
|
++ 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.completions.enable "/share/fish/vendor_completions.d"
|
||||||
++ lib.optional cfg.vendor.functions.enable "/share/fish/vendor_functions.d";
|
++ lib.optional cfg.vendor.functions.enable "/share/fish/vendor_functions.d";
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
{ pkgs, config, lib, ... }:
|
{
|
||||||
|
pkgs,
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
let
|
let
|
||||||
cfg = config.programs.fzf;
|
cfg = config.programs.fzf;
|
||||||
|
@ -16,19 +21,24 @@ in
|
||||||
|
|
||||||
programs = {
|
programs = {
|
||||||
# load after programs.bash.completion.enable
|
# load after programs.bash.completion.enable
|
||||||
bash.promptPluginInit = lib.mkAfter (lib.optionalString cfg.fuzzyCompletion ''
|
bash.promptPluginInit = lib.mkAfter (
|
||||||
|
lib.optionalString cfg.fuzzyCompletion ''
|
||||||
source ${pkgs.fzf}/share/fzf/completion.bash
|
source ${pkgs.fzf}/share/fzf/completion.bash
|
||||||
'' + lib.optionalString cfg.keybindings ''
|
''
|
||||||
|
+ lib.optionalString cfg.keybindings ''
|
||||||
source ${pkgs.fzf}/share/fzf/key-bindings.bash
|
source ${pkgs.fzf}/share/fzf/key-bindings.bash
|
||||||
'');
|
''
|
||||||
|
);
|
||||||
|
|
||||||
zsh = {
|
zsh = {
|
||||||
interactiveShellInit = lib.optionalString (!config.programs.zsh.ohMyZsh.enable)
|
interactiveShellInit = lib.optionalString (!config.programs.zsh.ohMyZsh.enable) (
|
||||||
(lib.optionalString cfg.fuzzyCompletion ''
|
lib.optionalString cfg.fuzzyCompletion ''
|
||||||
source ${pkgs.fzf}/share/fzf/completion.zsh
|
source ${pkgs.fzf}/share/fzf/completion.zsh
|
||||||
'' + lib.optionalString cfg.keybindings ''
|
''
|
||||||
|
+ lib.optionalString cfg.keybindings ''
|
||||||
source ${pkgs.fzf}/share/fzf/key-bindings.zsh
|
source ${pkgs.fzf}/share/fzf/key-bindings.zsh
|
||||||
'');
|
''
|
||||||
|
);
|
||||||
|
|
||||||
ohMyZsh.plugins = lib.mkIf config.programs.zsh.ohMyZsh.enable [ "fzf" ];
|
ohMyZsh.plugins = lib.mkIf config.programs.zsh.ohMyZsh.enable [ "fzf" ];
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
{ config, lib, pkgs, ... }:
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
let
|
let
|
||||||
cfg = config.programs.git;
|
cfg = config.programs.git;
|
||||||
|
@ -19,15 +24,28 @@ in
|
||||||
let
|
let
|
||||||
gitini = attrsOf (attrsOf anything);
|
gitini = attrsOf (attrsOf anything);
|
||||||
in
|
in
|
||||||
either gitini (listOf gitini) // {
|
either gitini (listOf gitini)
|
||||||
merge = loc: defs:
|
// {
|
||||||
|
merge =
|
||||||
|
loc: defs:
|
||||||
let
|
let
|
||||||
config = builtins.foldl'
|
config =
|
||||||
(acc: { value, ... }@x: acc // (if builtins.isList value then {
|
builtins.foldl'
|
||||||
|
(
|
||||||
|
acc:
|
||||||
|
{ value, ... }@x:
|
||||||
|
acc
|
||||||
|
// (
|
||||||
|
if builtins.isList value then
|
||||||
|
{
|
||||||
ordered = acc.ordered ++ value;
|
ordered = acc.ordered ++ value;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
unordered = acc.unordered ++ [ x ];
|
unordered = acc.unordered ++ [ x ];
|
||||||
}))
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
{
|
{
|
||||||
ordered = [ ];
|
ordered = [ ];
|
||||||
unordered = [ ];
|
unordered = [ ];
|
||||||
|
@ -39,7 +57,10 @@ in
|
||||||
default = [ ];
|
default = [ ];
|
||||||
example = {
|
example = {
|
||||||
init.defaultBranch = "main";
|
init.defaultBranch = "main";
|
||||||
url."https://github.com/".insteadOf = [ "gh:" "github:" ];
|
url."https://github.com/".insteadOf = [
|
||||||
|
"gh:"
|
||||||
|
"github:"
|
||||||
|
];
|
||||||
};
|
};
|
||||||
description = ''
|
description = ''
|
||||||
Configuration to write to /etc/gitconfig. A list can also be
|
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 ];
|
meta.maintainers = [ lib.maintainers.league ];
|
||||||
|
@ -23,6 +28,6 @@
|
||||||
config = lib.mkIf config.programs.gphoto2.enable {
|
config = lib.mkIf config.programs.gphoto2.enable {
|
||||||
services.udev.packages = [ pkgs.libgphoto2 ];
|
services.udev.packages = [ pkgs.libgphoto2 ];
|
||||||
environment.systemPackages = [ pkgs.gphoto2 ];
|
environment.systemPackages = [ pkgs.gphoto2 ];
|
||||||
users.groups.camera = {};
|
users.groups.camera = { };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,14 @@
|
||||||
{ config, pkgs, lib, ... }:
|
{
|
||||||
|
config,
|
||||||
|
pkgs,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
let
|
let
|
||||||
cfg = config.programs.iotop;
|
cfg = config.programs.iotop;
|
||||||
in {
|
in
|
||||||
|
{
|
||||||
options = {
|
options = {
|
||||||
programs.iotop.enable = lib.mkEnableOption "iotop + setcap wrapper";
|
programs.iotop.enable = lib.mkEnableOption "iotop + setcap wrapper";
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,7 +2,10 @@
|
||||||
|
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
(lib.mkRemovedOptionModule [ "programs" "k3b" "enable" ]
|
(lib.mkRemovedOptionModule [
|
||||||
"Please add kdePackages.k3b to environment.systemPackages instead")
|
"programs"
|
||||||
|
"k3b"
|
||||||
|
"enable"
|
||||||
|
] "Please add kdePackages.k3b to environment.systemPackages instead")
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
{ config, lib, pkgs, ... }:
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
let
|
let
|
||||||
cfg = config.programs.nano;
|
cfg = config.programs.nano;
|
||||||
|
@ -37,11 +42,13 @@ in
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkIf cfg.enable {
|
||||||
environment = {
|
environment = {
|
||||||
etc.nanorc.text = (lib.optionalString cfg.syntaxHighlight ''
|
etc.nanorc.text =
|
||||||
|
(lib.optionalString cfg.syntaxHighlight ''
|
||||||
# load syntax highlighting files
|
# load syntax highlighting files
|
||||||
include "${cfg.package}/share/nano/*.nanorc"
|
include "${cfg.package}/share/nano/*.nanorc"
|
||||||
include "${cfg.package}/share/nano/extra/*.nanorc"
|
include "${cfg.package}/share/nano/extra/*.nanorc"
|
||||||
'') + cfg.nanorc;
|
'')
|
||||||
|
+ cfg.nanorc;
|
||||||
systemPackages = [ cfg.package ];
|
systemPackages = [ cfg.package ];
|
||||||
pathsToLink = [ "/share/nano" ];
|
pathsToLink = [ "/share/nano" ];
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,7 +14,7 @@ in
|
||||||
programs.qgroundcontrol = {
|
programs.qgroundcontrol = {
|
||||||
enable = lib.mkEnableOption "qgroundcontrol";
|
enable = lib.mkEnableOption "qgroundcontrol";
|
||||||
|
|
||||||
package = lib.mkPackageOption pkgs "qgroundcontrol" {};
|
package = lib.mkPackageOption pkgs "qgroundcontrol" { };
|
||||||
|
|
||||||
blacklistModemManagerFromTTYUSB = lib.mkOption {
|
blacklistModemManagerFromTTYUSB = lib.mkOption {
|
||||||
type = lib.types.bool;
|
type = lib.types.bool;
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
# Configuration for the pwdutils suite of tools: passwd, useradd, etc.
|
# Configuration for the pwdutils suite of tools: passwd, useradd, etc.
|
||||||
{ config, lib, utils, pkgs, ... }:
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
utils,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
let
|
let
|
||||||
cfg = config.security.loginDefs;
|
cfg = config.security.loginDefs;
|
||||||
in
|
in
|
||||||
|
@ -35,7 +41,8 @@ in
|
||||||
'';
|
'';
|
||||||
type = lib.types.submodule {
|
type = lib.types.submodule {
|
||||||
freeformType = (pkgs.formats.keyValue { }).type;
|
freeformType = (pkgs.formats.keyValue { }).type;
|
||||||
/* There are three different sources for user/group id ranges, each of which gets
|
/*
|
||||||
|
There are three different sources for user/group id ranges, each of which gets
|
||||||
used by different programs:
|
used by different programs:
|
||||||
- The login.defs file, used by the useradd, groupadd and newusers commands
|
- 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
|
- The update-users-groups.pl file, used by NixOS in the activation phase to
|
||||||
|
@ -48,14 +55,23 @@ in
|
||||||
DEFAULT_HOME = lib.mkOption {
|
DEFAULT_HOME = lib.mkOption {
|
||||||
description = "Indicate if login is allowed if we can't cd to the home directory.";
|
description = "Indicate if login is allowed if we can't cd to the home directory.";
|
||||||
default = "yes";
|
default = "yes";
|
||||||
type = lib.types.enum [ "yes" "no" ];
|
type = lib.types.enum [
|
||||||
|
"yes"
|
||||||
|
"no"
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
ENCRYPT_METHOD = lib.mkOption {
|
ENCRYPT_METHOD = lib.mkOption {
|
||||||
description = "This defines the system default encryption algorithm for encrypting passwords.";
|
description = "This defines the system default encryption algorithm for encrypting passwords.";
|
||||||
# The default crypt() method, keep in sync with the PAM default
|
# The default crypt() method, keep in sync with the PAM default
|
||||||
default = "YESCRYPT";
|
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 {
|
SYS_UID_MIN = lib.mkOption {
|
||||||
|
@ -180,7 +196,8 @@ in
|
||||||
|
|
||||||
security.loginDefs.settings.CHFN_RESTRICT = lib.mkIf (cfg.chfnRestrict != null) cfg.chfnRestrict;
|
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 (lib.types.shellPackage.check config.users.defaultUserShell) config.users.defaultUserShell
|
||||||
++ lib.optional (cfg.chfnRestrict != null) pkgs.util-linux;
|
++ lib.optional (cfg.chfnRestrict != null) pkgs.util-linux;
|
||||||
|
|
||||||
|
@ -191,7 +208,8 @@ in
|
||||||
toKeyValue = lib.generators.toKeyValue {
|
toKeyValue = lib.generators.toKeyValue {
|
||||||
mkKeyValue = lib.generators.mkKeyValueDefault { } " ";
|
mkKeyValue = lib.generators.mkKeyValueDefault { } " ";
|
||||||
};
|
};
|
||||||
in {
|
in
|
||||||
|
{
|
||||||
# /etc/login.defs: global configuration for pwdutils.
|
# /etc/login.defs: global configuration for pwdutils.
|
||||||
# You cannot login without it!
|
# You cannot login without it!
|
||||||
"login.defs".source = pkgs.writeText "login.defs" (toKeyValue cfg.settings);
|
"login.defs".source = pkgs.writeText "login.defs" (toKeyValue cfg.settings);
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
{ config, lib, pkgs, ... }:
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
let
|
let
|
||||||
cfg = config.programs.steam;
|
cfg = config.programs.steam;
|
||||||
|
@ -6,8 +11,11 @@ let
|
||||||
|
|
||||||
extraCompatPaths = lib.makeSearchPathOutput "steamcompattool" "" cfg.extraCompatPackages;
|
extraCompatPaths = lib.makeSearchPathOutput "steamcompattool" "" cfg.extraCompatPackages;
|
||||||
|
|
||||||
steam-gamescope = let
|
steam-gamescope =
|
||||||
exports = builtins.attrValues (builtins.mapAttrs (n: v: "export ${n}=${v}") cfg.gamescopeSession.env);
|
let
|
||||||
|
exports = builtins.attrValues (
|
||||||
|
builtins.mapAttrs (n: v: "export ${n}=${v}") cfg.gamescopeSession.env
|
||||||
|
);
|
||||||
in
|
in
|
||||||
pkgs.writeShellScriptBin "steam-gamescope" ''
|
pkgs.writeShellScriptBin "steam-gamescope" ''
|
||||||
${builtins.concatStringsSep "\n" exports}
|
${builtins.concatStringsSep "\n" exports}
|
||||||
|
@ -21,8 +29,12 @@ let
|
||||||
Comment=A digital distribution platform
|
Comment=A digital distribution platform
|
||||||
Exec=${steam-gamescope}/bin/steam-gamescope
|
Exec=${steam-gamescope}/bin/steam-gamescope
|
||||||
Type=Application
|
Type=Application
|
||||||
'').overrideAttrs (_: { passthru.providedSessions = [ "steam" ]; });
|
'').overrideAttrs
|
||||||
in {
|
(_: {
|
||||||
|
passthru.providedSessions = [ "steam" ];
|
||||||
|
});
|
||||||
|
in
|
||||||
|
{
|
||||||
options.programs.steam = {
|
options.programs.steam = {
|
||||||
enable = lib.mkEnableOption "steam";
|
enable = lib.mkEnableOption "steam";
|
||||||
|
|
||||||
|
@ -42,27 +54,40 @@ in {
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
'';
|
'';
|
||||||
apply = steam: steam.override (prev: {
|
apply =
|
||||||
extraEnv = (lib.optionalAttrs (cfg.extraCompatPackages != [ ]) {
|
steam:
|
||||||
STEAM_EXTRA_COMPAT_TOOLS_PATHS = extraCompatPaths;
|
steam.override (
|
||||||
}) // (lib.optionalAttrs cfg.extest.enable {
|
prev:
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
|
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 {
|
buildFHSEnv = pkgs.buildFHSEnv.override {
|
||||||
# use the setuid wrapped bubblewrap
|
# use the setuid wrapped bubblewrap
|
||||||
bubblewrap = "${config.security.wrapperDir}/..";
|
bubblewrap = "${config.security.wrapperDir}/..";
|
||||||
};
|
};
|
||||||
});
|
}
|
||||||
|
);
|
||||||
description = ''
|
description = ''
|
||||||
The Steam package to use. Additional libraries are added from the system
|
The Steam package to use. Additional libraries are added from the system
|
||||||
configuration to ensure graphics work properly.
|
configuration to ensure graphics work properly.
|
||||||
|
@ -141,7 +166,7 @@ in {
|
||||||
|
|
||||||
gamescopeSession = lib.mkOption {
|
gamescopeSession = lib.mkOption {
|
||||||
description = "Run a GameScope driven Steam session from your display-manager";
|
description = "Run a GameScope driven Steam session from your display-manager";
|
||||||
default = {};
|
default = { };
|
||||||
type = lib.types.submodule {
|
type = lib.types.submodule {
|
||||||
options = {
|
options = {
|
||||||
enable = lib.mkEnableOption "GameScope Session";
|
enable = lib.mkEnableOption "GameScope Session";
|
||||||
|
@ -187,7 +212,8 @@ in {
|
||||||
};
|
};
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
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;
|
enable = true;
|
||||||
enable32Bit = true;
|
enable32Bit = true;
|
||||||
};
|
};
|
||||||
|
@ -205,7 +231,9 @@ in {
|
||||||
programs.steam.extraPackages = cfg.fontPackages;
|
programs.steam.extraPackages = cfg.fontPackages;
|
||||||
|
|
||||||
programs.gamescope.enable = lib.mkDefault cfg.gamescopeSession.enable;
|
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
|
# enable 32bit pulseaudio/pipewire support if needed
|
||||||
services.pulseaudio.support32Bit = config.services.pulseaudio.enable;
|
services.pulseaudio.support32Bit = config.services.pulseaudio.enable;
|
||||||
|
@ -213,11 +241,15 @@ in {
|
||||||
|
|
||||||
hardware.steam-hardware.enable = true;
|
hardware.steam-hardware.enable = true;
|
||||||
|
|
||||||
environment.systemPackages = [
|
environment.systemPackages =
|
||||||
|
[
|
||||||
cfg.package
|
cfg.package
|
||||||
cfg.package.run
|
cfg.package.run
|
||||||
] ++ lib.optional cfg.gamescopeSession.enable steam-gamescope
|
]
|
||||||
++ lib.optional cfg.protontricks.enable (cfg.protontricks.package.override { inherit extraCompatPaths; });
|
++ lib.optional cfg.gamescopeSession.enable steam-gamescope
|
||||||
|
++ lib.optional cfg.protontricks.enable (
|
||||||
|
cfg.protontricks.package.override { inherit extraCompatPaths; }
|
||||||
|
);
|
||||||
|
|
||||||
networking.firewall = lib.mkMerge [
|
networking.firewall = lib.mkMerge [
|
||||||
(lib.mkIf (cfg.remotePlay.openFirewall || cfg.localNetworkGameTransfers.openFirewall) {
|
(lib.mkIf (cfg.remotePlay.openFirewall || cfg.localNetworkGameTransfers.openFirewall) {
|
||||||
|
@ -226,7 +258,12 @@ in {
|
||||||
|
|
||||||
(lib.mkIf cfg.remotePlay.openFirewall {
|
(lib.mkIf cfg.remotePlay.openFirewall {
|
||||||
allowedTCPPorts = [ 27036 ];
|
allowedTCPPorts = [ 27036 ];
|
||||||
allowedUDPPortRanges = [ { from = 27031; to = 27035; } ];
|
allowedUDPPortRanges = [
|
||||||
|
{
|
||||||
|
from = 27031;
|
||||||
|
to = 27035;
|
||||||
|
}
|
||||||
|
];
|
||||||
})
|
})
|
||||||
|
|
||||||
(lib.mkIf cfg.dedicatedServer.openFirewall {
|
(lib.mkIf cfg.dedicatedServer.openFirewall {
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
{ config, lib, pkgs, ... }:
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
let
|
let
|
||||||
cfg = config.programs.sway;
|
cfg = config.programs.sway;
|
||||||
|
@ -14,7 +19,8 @@ in
|
||||||
<https://github.com/swaywm/sway/wiki> and
|
<https://github.com/swaywm/sway/wiki> and
|
||||||
"man 5 sway" for more information'';
|
"man 5 sway" for more information'';
|
||||||
|
|
||||||
package = lib.mkPackageOption pkgs "sway" {
|
package =
|
||||||
|
lib.mkPackageOption pkgs "sway" {
|
||||||
nullable = true;
|
nullable = true;
|
||||||
extraDescription = ''
|
extraDescription = ''
|
||||||
If the package is not overridable with `extraSessionCommands`, `extraOptions`,
|
If the package is not overridable with `extraSessionCommands`, `extraOptions`,
|
||||||
|
@ -25,8 +31,13 @@ in
|
||||||
Set to `null` to not add any Sway package to your path.
|
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.
|
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
|
// {
|
||||||
|
apply =
|
||||||
|
p:
|
||||||
|
if p == null then
|
||||||
|
null
|
||||||
|
else
|
||||||
wayland-lib.genFinalPackage p {
|
wayland-lib.genFinalPackage p {
|
||||||
extraSessionCommands = cfg.extraSessionCommands;
|
extraSessionCommands = cfg.extraSessionCommands;
|
||||||
extraOptions = cfg.extraOptions;
|
extraOptions = cfg.extraOptions;
|
||||||
|
@ -38,9 +49,13 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
wrapperFeatures = {
|
wrapperFeatures = {
|
||||||
base = lib.mkEnableOption ''
|
base =
|
||||||
|
lib.mkEnableOption ''
|
||||||
the base wrapper to execute extra session commands and prepend a
|
the base wrapper to execute extra session commands and prepend a
|
||||||
dbus-run-session to the sway command'' // { default = true; };
|
dbus-run-session to the sway command''
|
||||||
|
// {
|
||||||
|
default = true;
|
||||||
|
};
|
||||||
gtk = lib.mkEnableOption ''
|
gtk = lib.mkEnableOption ''
|
||||||
the wrapGAppsHook wrapper to execute sway with required environment
|
the wrapGAppsHook wrapper to execute sway with required environment
|
||||||
variables for GTK applications'';
|
variables for GTK applications'';
|
||||||
|
@ -69,7 +84,7 @@ in
|
||||||
|
|
||||||
extraOptions = lib.mkOption {
|
extraOptions = lib.mkOption {
|
||||||
type = lib.types.listOf lib.types.str;
|
type = lib.types.listOf lib.types.str;
|
||||||
default = [];
|
default = [ ];
|
||||||
example = [
|
example = [
|
||||||
"--verbose"
|
"--verbose"
|
||||||
"--debug"
|
"--debug"
|
||||||
|
@ -81,12 +96,22 @@ in
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
xwayland.enable = lib.mkEnableOption "XWayland" // { default = true; };
|
xwayland.enable = lib.mkEnableOption "XWayland" // {
|
||||||
|
default = true;
|
||||||
|
};
|
||||||
|
|
||||||
extraPackages = lib.mkOption {
|
extraPackages = lib.mkOption {
|
||||||
type = with lib.types; listOf package;
|
type = with lib.types; listOf package;
|
||||||
# Packages used in default config
|
# 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 ''
|
defaultText = lib.literalExpression ''
|
||||||
with pkgs; [ brightnessctl foot grim pulseaudio swayidle swaylock wmenu ];
|
with pkgs; [ brightnessctl foot grim pulseaudio swayidle swaylock wmenu ];
|
||||||
'';
|
'';
|
||||||
|
@ -102,7 +127,8 @@ in
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable (lib.mkMerge [
|
config = lib.mkIf cfg.enable (
|
||||||
|
lib.mkMerge [
|
||||||
{
|
{
|
||||||
assertions = [
|
assertions = [
|
||||||
{
|
{
|
||||||
|
@ -129,7 +155,8 @@ in
|
||||||
# Needed for the default wallpaper:
|
# Needed for the default wallpaper:
|
||||||
pathsToLink = lib.optional (cfg.package != null) "/share/backgrounds/sway";
|
pathsToLink = lib.optional (cfg.package != null) "/share/backgrounds/sway";
|
||||||
|
|
||||||
etc = {
|
etc =
|
||||||
|
{
|
||||||
"sway/config.d/nixos.conf".source = pkgs.writeText "nixos.conf" ''
|
"sway/config.d/nixos.conf".source = pkgs.writeText "nixos.conf" ''
|
||||||
# Import the most important environment variables into the D-Bus and systemd
|
# Import the most important environment variables into the D-Bus and systemd
|
||||||
# user environments (e.g. required for screen sharing and Pinentry prompts):
|
# user environments (e.g. required for screen sharing and Pinentry prompts):
|
||||||
|
@ -138,7 +165,8 @@ in
|
||||||
exec "systemctl --user import-environment {,WAYLAND_}DISPLAY SWAYSOCK; systemctl --user start sway-session.target"
|
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
|
exec swaymsg -t subscribe '["shutdown"]' && systemctl --user stop sway-session.target
|
||||||
'';
|
'';
|
||||||
} // lib.optionalAttrs (cfg.package != null) {
|
}
|
||||||
|
// lib.optionalAttrs (cfg.package != null) {
|
||||||
"sway/config".source = lib.mkOptionDefault "${cfg.package}/etc/sway/config";
|
"sway/config".source = lib.mkOptionDefault "${cfg.package}/etc/sway/config";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -174,7 +202,11 @@ in
|
||||||
inherit lib pkgs;
|
inherit lib pkgs;
|
||||||
enableXWayland = cfg.xwayland.enable;
|
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.
|
# This module defines global configuration for the xonsh.
|
||||||
|
|
||||||
{ config, lib, pkgs, ... }:
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
let
|
let
|
||||||
|
|
||||||
|
@ -45,7 +50,9 @@ in
|
||||||
example = lib.literalExpression ''
|
example = lib.literalExpression ''
|
||||||
ps: with ps; [ numpy xonsh.xontribs.xontrib-vox ]
|
ps: with ps; [ numpy xonsh.xontribs.xontrib-vox ]
|
||||||
'';
|
'';
|
||||||
type = with lib.types; coercedTo (listOf lib.types.package) (v: (_: v)) (functionTo (listOf lib.types.package));
|
type =
|
||||||
|
with lib.types;
|
||||||
|
coercedTo (listOf lib.types.package) (v: (_: v)) (functionTo (listOf lib.types.package));
|
||||||
description = ''
|
description = ''
|
||||||
Xontribs and extra Python packages to be available in xonsh.
|
Xontribs and extra Python packages to be available in xonsh.
|
||||||
'';
|
'';
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
# This module defines global configuration for the zshell.
|
# This module defines global configuration for the zshell.
|
||||||
|
|
||||||
{ config, lib, options, pkgs, ... }:
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
options,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
let
|
let
|
||||||
|
|
||||||
|
@ -10,8 +16,9 @@ let
|
||||||
opt = options.programs.zsh;
|
opt = options.programs.zsh;
|
||||||
|
|
||||||
zshAliases = builtins.concatStringsSep "\n" (
|
zshAliases = builtins.concatStringsSep "\n" (
|
||||||
lib.mapAttrsToList (k: v: "alias -- ${k}=${lib.escapeShellArg v}")
|
lib.mapAttrsToList (k: v: "alias -- ${k}=${lib.escapeShellArg v}") (
|
||||||
(lib.filterAttrs (k: v: v != null) cfg.shellAliases)
|
lib.filterAttrs (k: v: v != null) cfg.shellAliases
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
zshStartupNotes = ''
|
zshStartupNotes = ''
|
||||||
|
@ -121,7 +128,10 @@ in
|
||||||
"SHARE_HISTORY"
|
"SHARE_HISTORY"
|
||||||
"HIST_FCNTL_LOCK"
|
"HIST_FCNTL_LOCK"
|
||||||
];
|
];
|
||||||
example = [ "EXTENDED_HISTORY" "RM_STAR_WAIT" ];
|
example = [
|
||||||
|
"EXTENDED_HISTORY"
|
||||||
|
"RM_STAR_WAIT"
|
||||||
|
];
|
||||||
description = ''
|
description = ''
|
||||||
Configure zsh options. See
|
Configure zsh options. See
|
||||||
{manpage}`zshoptions(1)`.
|
{manpage}`zshoptions(1)`.
|
||||||
|
@ -173,8 +183,7 @@ in
|
||||||
|
|
||||||
programs.zsh.shellAliases = builtins.mapAttrs (name: lib.mkDefault) cfge.shellAliases;
|
programs.zsh.shellAliases = builtins.mapAttrs (name: lib.mkDefault) cfge.shellAliases;
|
||||||
|
|
||||||
environment.etc.zshenv.text =
|
environment.etc.zshenv.text = ''
|
||||||
''
|
|
||||||
# /etc/zshenv: DO NOT EDIT -- this file has been generated automatically.
|
# /etc/zshenv: DO NOT EDIT -- this file has been generated automatically.
|
||||||
# This file is read for all shells.
|
# This file is read for all shells.
|
||||||
|
|
||||||
|
@ -204,8 +213,7 @@ in
|
||||||
fi
|
fi
|
||||||
'';
|
'';
|
||||||
|
|
||||||
environment.etc.zprofile.text =
|
environment.etc.zprofile.text = ''
|
||||||
''
|
|
||||||
# /etc/zprofile: DO NOT EDIT -- this file has been generated automatically.
|
# /etc/zprofile: DO NOT EDIT -- this file has been generated automatically.
|
||||||
# This file is read for login shells.
|
# This file is read for login shells.
|
||||||
#
|
#
|
||||||
|
@ -226,8 +234,7 @@ in
|
||||||
fi
|
fi
|
||||||
'';
|
'';
|
||||||
|
|
||||||
environment.etc.zshrc.text =
|
environment.etc.zshrc.text = ''
|
||||||
''
|
|
||||||
# /etc/zshrc: DO NOT EDIT -- this file has been generated automatically.
|
# /etc/zshrc: DO NOT EDIT -- this file has been generated automatically.
|
||||||
# This file is read for interactive shells.
|
# This file is read for interactive shells.
|
||||||
#
|
#
|
||||||
|
@ -237,7 +244,7 @@ in
|
||||||
if [ -n "$__ETC_ZSHRC_SOURCED" -o -n "$NOSYSZSHRC" ]; then return; fi
|
if [ -n "$__ETC_ZSHRC_SOURCED" -o -n "$NOSYSZSHRC" ]; then return; fi
|
||||||
__ETC_ZSHRC_SOURCED=1
|
__ETC_ZSHRC_SOURCED=1
|
||||||
|
|
||||||
${lib.optionalString (cfg.setOptions != []) ''
|
${lib.optionalString (cfg.setOptions != [ ]) ''
|
||||||
# Set zsh options.
|
# Set zsh options.
|
||||||
setopt ${builtins.concatStringsSep " " cfg.setOptions}
|
setopt ${builtins.concatStringsSep " " cfg.setOptions}
|
||||||
''}
|
''}
|
||||||
|
@ -299,15 +306,15 @@ in
|
||||||
# see https://github.com/NixOS/nixpkgs/issues/132732
|
# see https://github.com/NixOS/nixpkgs/issues/132732
|
||||||
environment.etc.zinputrc.text = builtins.readFile ./zinputrc;
|
environment.etc.zinputrc.text = builtins.readFile ./zinputrc;
|
||||||
|
|
||||||
environment.systemPackages = [ pkgs.zsh ]
|
environment.systemPackages = [
|
||||||
++ lib.optional cfg.enableCompletion pkgs.nix-zsh-completions;
|
pkgs.zsh
|
||||||
|
] ++ lib.optional cfg.enableCompletion pkgs.nix-zsh-completions;
|
||||||
|
|
||||||
environment.pathsToLink = lib.optional cfg.enableCompletion "/share/zsh";
|
environment.pathsToLink = lib.optional cfg.enableCompletion "/share/zsh";
|
||||||
|
|
||||||
#users.defaultUserShell = lib.mkDefault "/run/current-system/sw/bin/zsh";
|
#users.defaultUserShell = lib.mkDefault "/run/current-system/sw/bin/zsh";
|
||||||
|
|
||||||
environment.shells =
|
environment.shells = [
|
||||||
[
|
|
||||||
"/run/current-system/sw/bin/zsh"
|
"/run/current-system/sw/bin/zsh"
|
||||||
"${pkgs.zsh}/bin/zsh"
|
"${pkgs.zsh}/bin/zsh"
|
||||||
];
|
];
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
{ config, lib, pkgs, options, ... }:
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
options,
|
||||||
|
...
|
||||||
|
}:
|
||||||
let
|
let
|
||||||
|
|
||||||
|
|
||||||
cfg = config.security.acme;
|
cfg = config.security.acme;
|
||||||
opt = options.security.acme;
|
opt = options.security.acme;
|
||||||
user = if cfg.useRoot then "root" else "acme";
|
user = if cfg.useRoot then "root" else "acme";
|
||||||
|
@ -22,21 +27,43 @@ let
|
||||||
concurrencyLockfiles = map (n: "${toString n}.lock") (lib.range 1 cfg.maxConcurrentRenewals);
|
concurrencyLockfiles = map (n: "${toString n}.lock") (lib.range 1 cfg.maxConcurrentRenewals);
|
||||||
# Assign elements of `baseList` to each element of `needAssignmentList`, until the latter is exhausted.
|
# Assign elements of `baseList` to each element of `needAssignmentList`, until the latter is exhausted.
|
||||||
# returns: [{fst = "element of baseList"; snd = "element of needAssignmentList"}]
|
# returns: [{fst = "element of baseList"; snd = "element of needAssignmentList"}]
|
||||||
roundRobinAssign = baseList: needAssignmentList:
|
roundRobinAssign =
|
||||||
if baseList == [] then []
|
baseList: needAssignmentList:
|
||||||
else _rrCycler baseList baseList needAssignmentList;
|
if baseList == [ ] then [ ] else _rrCycler baseList baseList needAssignmentList;
|
||||||
_rrCycler = with builtins; origBaseList: workingBaseList: needAssignmentList:
|
_rrCycler =
|
||||||
if (workingBaseList == [] || needAssignmentList == [])
|
with builtins;
|
||||||
then []
|
origBaseList: workingBaseList: needAssignmentList:
|
||||||
|
if (workingBaseList == [ ] || needAssignmentList == [ ]) then
|
||||||
|
[ ]
|
||||||
else
|
else
|
||||||
[{ fst = head workingBaseList; snd = head needAssignmentList;}] ++
|
[
|
||||||
_rrCycler origBaseList (if (tail workingBaseList == []) then origBaseList else tail workingBaseList) (tail needAssignmentList);
|
{
|
||||||
attrsToList = lib.mapAttrsToList (attrname: attrval: {name = attrname; value = attrval;});
|
fst = head workingBaseList;
|
||||||
|
snd = head needAssignmentList;
|
||||||
|
}
|
||||||
|
]
|
||||||
|
++ _rrCycler origBaseList (
|
||||||
|
if (tail workingBaseList == [ ]) then origBaseList else tail workingBaseList
|
||||||
|
) (tail needAssignmentList);
|
||||||
|
attrsToList = lib.mapAttrsToList (
|
||||||
|
attrname: attrval: {
|
||||||
|
name = attrname;
|
||||||
|
value = attrval;
|
||||||
|
}
|
||||||
|
);
|
||||||
# for an AttrSet `funcsAttrs` having functions as values, apply single arguments from
|
# for an AttrSet `funcsAttrs` having functions as values, apply single arguments from
|
||||||
# `argsList` to them in a round-robin manner.
|
# `argsList` to them in a round-robin manner.
|
||||||
# Returns an attribute set with the applied functions as values.
|
# Returns an attribute set with the applied functions as values.
|
||||||
roundRobinApplyAttrs = funcsAttrs: argsList: lib.listToAttrs (map (x: {inherit (x.snd) name; value = x.snd.value x.fst;}) (roundRobinAssign argsList (attrsToList funcsAttrs)));
|
roundRobinApplyAttrs =
|
||||||
wrapInFlock = lockfilePath: script:
|
funcsAttrs: argsList:
|
||||||
|
lib.listToAttrs (
|
||||||
|
map (x: {
|
||||||
|
inherit (x.snd) name;
|
||||||
|
value = x.snd.value x.fst;
|
||||||
|
}) (roundRobinAssign argsList (attrsToList funcsAttrs))
|
||||||
|
);
|
||||||
|
wrapInFlock =
|
||||||
|
lockfilePath: script:
|
||||||
# explainer: https://stackoverflow.com/a/60896531
|
# explainer: https://stackoverflow.com/a/60896531
|
||||||
''
|
''
|
||||||
exec {LOCKFD}> ${lockfilePath}
|
exec {LOCKFD}> ${lockfilePath}
|
||||||
|
@ -44,10 +71,10 @@ let
|
||||||
${pkgs.flock}/bin/flock ''${LOCKFD} || exit 1
|
${pkgs.flock}/bin/flock ''${LOCKFD} || exit 1
|
||||||
echo "Acquired lock ${lockfilePath}"
|
echo "Acquired lock ${lockfilePath}"
|
||||||
''
|
''
|
||||||
+ script + "\n"
|
+ script
|
||||||
|
+ "\n"
|
||||||
+ ''echo "Releasing lock ${lockfilePath}" # only released after process exit'';
|
+ ''echo "Releasing lock ${lockfilePath}" # only released after process exit'';
|
||||||
|
|
||||||
|
|
||||||
# There are many services required to make cert renewals work.
|
# There are many services required to make cert renewals work.
|
||||||
# They all follow a common structure:
|
# They all follow a common structure:
|
||||||
# - They inherit this commonServiceConfig
|
# - They inherit this commonServiceConfig
|
||||||
|
@ -119,14 +146,17 @@ let
|
||||||
cd /var/lib/acme
|
cd /var/lib/acme
|
||||||
chmod -R u=rwX,g=,o= .lego/accounts
|
chmod -R u=rwX,g=,o= .lego/accounts
|
||||||
chown -R ${user} .lego/accounts
|
chown -R ${user} .lego/accounts
|
||||||
'' + (lib.concatStringsSep "\n" (lib.mapAttrsToList (cert: data: ''
|
''
|
||||||
|
+ (lib.concatStringsSep "\n" (
|
||||||
|
lib.mapAttrsToList (cert: data: ''
|
||||||
for fixpath in ${lib.escapeShellArg cert} .lego/${lib.escapeShellArg cert}; do
|
for fixpath in ${lib.escapeShellArg cert} .lego/${lib.escapeShellArg cert}; do
|
||||||
if [ -d "$fixpath" ]; then
|
if [ -d "$fixpath" ]; then
|
||||||
chmod -R u=rwX,g=rX,o= "$fixpath"
|
chmod -R u=rwX,g=rX,o= "$fixpath"
|
||||||
chown -R ${user}:${data.group} "$fixpath"
|
chown -R ${user}:${data.group} "$fixpath"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
'') certConfigs))
|
'') certConfigs
|
||||||
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
# This is defined with lib.mkMerge so that we can separate the config per function.
|
# This is defined with lib.mkMerge so that we can separate the config per function.
|
||||||
|
@ -147,7 +177,11 @@ let
|
||||||
|
|
||||||
# StateDirectory entries are a cleaner, service-level mechanism
|
# StateDirectory entries are a cleaner, service-level mechanism
|
||||||
# for dealing with persistent service data
|
# for dealing with persistent service data
|
||||||
StateDirectory = [ "acme" "acme/.lego" "acme/.lego/accounts" ];
|
StateDirectory = [
|
||||||
|
"acme"
|
||||||
|
"acme/.lego"
|
||||||
|
"acme/.lego/accounts"
|
||||||
|
];
|
||||||
StateDirectoryMode = "0755";
|
StateDirectoryMode = "0755";
|
||||||
|
|
||||||
# Creates ${lockdir}. Earlier RemainAfterExit=true means
|
# Creates ${lockdir}. Earlier RemainAfterExit=true means
|
||||||
|
@ -179,7 +213,9 @@ let
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
|
|
||||||
certToConfig = cert: data: let
|
certToConfig =
|
||||||
|
cert: data:
|
||||||
|
let
|
||||||
acmeServer = data.server;
|
acmeServer = data.server;
|
||||||
useDns = data.dnsProvider != null;
|
useDns = data.dnsProvider != null;
|
||||||
destPath = "/var/lib/acme/${cert}";
|
destPath = "/var/lib/acme/${cert}";
|
||||||
|
@ -188,15 +224,15 @@ let
|
||||||
# Minica and lego have a "feature" which replaces * with _. We need
|
# Minica and lego have a "feature" which replaces * with _. We need
|
||||||
# to make this substitution to reference the output files from both programs.
|
# to make this substitution to reference the output files from both programs.
|
||||||
# End users never see this since we rename the certs.
|
# End users never see this since we rename the certs.
|
||||||
keyName = builtins.replaceStrings ["*"] ["_"] data.domain;
|
keyName = builtins.replaceStrings [ "*" ] [ "_" ] data.domain;
|
||||||
|
|
||||||
# FIXME when mkChangedOptionModule supports submodules, change to that.
|
# FIXME when mkChangedOptionModule supports submodules, change to that.
|
||||||
# This is a workaround
|
# This is a workaround
|
||||||
extraDomains = data.extraDomainNames ++ (
|
extraDomains =
|
||||||
lib.optionals
|
data.extraDomainNames
|
||||||
(data.extraDomains != "_mkMergedOptionModule")
|
++ (lib.optionals (data.extraDomains != "_mkMergedOptionModule") (
|
||||||
(builtins.attrNames data.extraDomains)
|
builtins.attrNames data.extraDomains
|
||||||
);
|
));
|
||||||
|
|
||||||
# Create hashes for cert data directories based on configuration
|
# Create hashes for cert data directories based on configuration
|
||||||
# Flags are separated to avoid collisions
|
# Flags are separated to avoid collisions
|
||||||
|
@ -213,23 +249,59 @@ let
|
||||||
accountHash = (mkAccountHash acmeServer data);
|
accountHash = (mkAccountHash acmeServer data);
|
||||||
accountDir = accountDirRoot + accountHash;
|
accountDir = accountDirRoot + accountHash;
|
||||||
|
|
||||||
protocolOpts = if useDns then (
|
protocolOpts =
|
||||||
[ "--dns" data.dnsProvider ]
|
if useDns then
|
||||||
|
(
|
||||||
|
[
|
||||||
|
"--dns"
|
||||||
|
data.dnsProvider
|
||||||
|
]
|
||||||
++ lib.optionals (!data.dnsPropagationCheck) [ "--dns.propagation-disable-ans" ]
|
++ lib.optionals (!data.dnsPropagationCheck) [ "--dns.propagation-disable-ans" ]
|
||||||
++ lib.optionals (data.dnsResolver != null) [ "--dns.resolvers" data.dnsResolver ]
|
++ lib.optionals (data.dnsResolver != null) [
|
||||||
) else if data.s3Bucket != null then [ "--http" "--http.s3-bucket" data.s3Bucket ]
|
"--dns.resolvers"
|
||||||
else if data.listenHTTP != null then [ "--http" "--http.port" data.listenHTTP ]
|
data.dnsResolver
|
||||||
else [ "--http" "--http.webroot" data.webroot ];
|
]
|
||||||
|
)
|
||||||
|
else if data.s3Bucket != null then
|
||||||
|
[
|
||||||
|
"--http"
|
||||||
|
"--http.s3-bucket"
|
||||||
|
data.s3Bucket
|
||||||
|
]
|
||||||
|
else if data.listenHTTP != null then
|
||||||
|
[
|
||||||
|
"--http"
|
||||||
|
"--http.port"
|
||||||
|
data.listenHTTP
|
||||||
|
]
|
||||||
|
else
|
||||||
|
[
|
||||||
|
"--http"
|
||||||
|
"--http.webroot"
|
||||||
|
data.webroot
|
||||||
|
];
|
||||||
|
|
||||||
commonOpts = [
|
commonOpts =
|
||||||
|
[
|
||||||
"--accept-tos" # Checking the option is covered by the assertions
|
"--accept-tos" # Checking the option is covered by the assertions
|
||||||
"--path" "."
|
"--path"
|
||||||
"-d" data.domain
|
"."
|
||||||
"--email" data.email
|
"-d"
|
||||||
"--key-type" data.keyType
|
data.domain
|
||||||
] ++ protocolOpts
|
"--email"
|
||||||
++ lib.optionals (acmeServer != null) [ "--server" acmeServer ]
|
data.email
|
||||||
++ lib.concatMap (name: [ "-d" name ]) extraDomains
|
"--key-type"
|
||||||
|
data.keyType
|
||||||
|
]
|
||||||
|
++ protocolOpts
|
||||||
|
++ lib.optionals (acmeServer != null) [
|
||||||
|
"--server"
|
||||||
|
acmeServer
|
||||||
|
]
|
||||||
|
++ lib.concatMap (name: [
|
||||||
|
"-d"
|
||||||
|
name
|
||||||
|
]) extraDomains
|
||||||
++ data.extraLegoFlags;
|
++ data.extraLegoFlags;
|
||||||
|
|
||||||
# Although --must-staple is common to both modes, it is not declared as a
|
# Although --must-staple is common to both modes, it is not declared as a
|
||||||
|
@ -242,20 +314,21 @@ let
|
||||||
);
|
);
|
||||||
renewOpts = lib.escapeShellArgs (
|
renewOpts = lib.escapeShellArgs (
|
||||||
commonOpts
|
commonOpts
|
||||||
++ [ "renew" "--no-random-sleep" ]
|
++ [
|
||||||
|
"renew"
|
||||||
|
"--no-random-sleep"
|
||||||
|
]
|
||||||
++ lib.optionals data.ocspMustStaple [ "--must-staple" ]
|
++ lib.optionals data.ocspMustStaple [ "--must-staple" ]
|
||||||
++ data.extraLegoRenewFlags
|
++ data.extraLegoRenewFlags
|
||||||
);
|
);
|
||||||
|
|
||||||
# We need to collect all the ACME webroots to grant them write
|
# We need to collect all the ACME webroots to grant them write
|
||||||
# access in the systemd service.
|
# access in the systemd service.
|
||||||
webroots =
|
webroots = lib.remove null (
|
||||||
lib.remove null
|
lib.unique (builtins.map (certAttrs: certAttrs.webroot) (lib.attrValues config.security.acme.certs))
|
||||||
(lib.unique
|
);
|
||||||
(builtins.map
|
in
|
||||||
(certAttrs: certAttrs.webroot)
|
{
|
||||||
(lib.attrValues config.security.acme.certs)));
|
|
||||||
in {
|
|
||||||
inherit accountHash cert selfsignedDeps;
|
inherit accountHash cert selfsignedDeps;
|
||||||
|
|
||||||
group = data.group;
|
group = data.group;
|
||||||
|
@ -331,16 +404,28 @@ let
|
||||||
|
|
||||||
renewService = lockfileName: {
|
renewService = lockfileName: {
|
||||||
description = "Renew ACME certificate for ${cert}";
|
description = "Renew ACME certificate for ${cert}";
|
||||||
after = [ "network.target" "network-online.target" "acme-setup.service" "nss-lookup.target" ] ++ selfsignedDeps;
|
after = [
|
||||||
|
"network.target"
|
||||||
|
"network-online.target"
|
||||||
|
"acme-setup.service"
|
||||||
|
"nss-lookup.target"
|
||||||
|
] ++ selfsignedDeps;
|
||||||
wants = [ "network-online.target" ] ++ selfsignedDeps;
|
wants = [ "network-online.target" ] ++ selfsignedDeps;
|
||||||
requires = [ "acme-setup.service" ];
|
requires = [ "acme-setup.service" ];
|
||||||
|
|
||||||
# https://github.com/NixOS/nixpkgs/pull/81371#issuecomment-605526099
|
# https://github.com/NixOS/nixpkgs/pull/81371#issuecomment-605526099
|
||||||
wantedBy = lib.optionals (!config.boot.isContainer) [ "multi-user.target" ];
|
wantedBy = lib.optionals (!config.boot.isContainer) [ "multi-user.target" ];
|
||||||
|
|
||||||
path = with pkgs; [ lego coreutils diffutils openssl ];
|
path = with pkgs; [
|
||||||
|
lego
|
||||||
|
coreutils
|
||||||
|
diffutils
|
||||||
|
openssl
|
||||||
|
];
|
||||||
|
|
||||||
serviceConfig = commonServiceConfig // {
|
serviceConfig =
|
||||||
|
commonServiceConfig
|
||||||
|
// {
|
||||||
Group = data.group;
|
Group = data.group;
|
||||||
|
|
||||||
# Let's Encrypt Failed Validation Limit allows 5 retries per hour, per account, hostname and hour.
|
# Let's Encrypt Failed Validation Limit allows 5 retries per hour, per account, hostname and hour.
|
||||||
|
@ -373,17 +458,23 @@ let
|
||||||
LoadCredential = lib.mapAttrsToList (k: v: "${k}:${v}") data.credentialFiles;
|
LoadCredential = lib.mapAttrsToList (k: v: "${k}:${v}") data.credentialFiles;
|
||||||
|
|
||||||
# Run as root (Prefixed with +)
|
# Run as root (Prefixed with +)
|
||||||
ExecStartPost = "+" + (pkgs.writeShellScript "acme-postrun" ''
|
ExecStartPost =
|
||||||
|
"+"
|
||||||
|
+ (pkgs.writeShellScript "acme-postrun" ''
|
||||||
cd /var/lib/acme/${lib.escapeShellArg cert}
|
cd /var/lib/acme/${lib.escapeShellArg cert}
|
||||||
if [ -e renewed ]; then
|
if [ -e renewed ]; then
|
||||||
rm renewed
|
rm renewed
|
||||||
${data.postRun}
|
${data.postRun}
|
||||||
${lib.optionalString (data.reloadServices != [])
|
${lib.optionalString (
|
||||||
"systemctl --no-block try-reload-or-restart ${lib.escapeShellArgs data.reloadServices}"
|
data.reloadServices != [ ]
|
||||||
}
|
) "systemctl --no-block try-reload-or-restart ${lib.escapeShellArgs data.reloadServices}"}
|
||||||
fi
|
fi
|
||||||
'');
|
'');
|
||||||
} // lib.optionalAttrs (data.listenHTTP != null && lib.toInt (lib.last (lib.splitString ":" data.listenHTTP)) < 1024) {
|
}
|
||||||
|
//
|
||||||
|
lib.optionalAttrs
|
||||||
|
(data.listenHTTP != null && lib.toInt (lib.last (lib.splitString ":" data.listenHTTP)) < 1024)
|
||||||
|
{
|
||||||
CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ];
|
CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ];
|
||||||
AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ];
|
AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ];
|
||||||
};
|
};
|
||||||
|
@ -493,19 +584,24 @@ let
|
||||||
|
|
||||||
# These options can be specified within
|
# These options can be specified within
|
||||||
# security.acme.defaults or security.acme.certs.<name>
|
# security.acme.defaults or security.acme.certs.<name>
|
||||||
inheritableModule = isDefaults: { config, ... }: let
|
inheritableModule =
|
||||||
|
isDefaults:
|
||||||
|
{ config, ... }:
|
||||||
|
let
|
||||||
defaultAndText = name: default: {
|
defaultAndText = name: default: {
|
||||||
# When ! isDefaults then this is the option declaration for the
|
# When ! isDefaults then this is the option declaration for the
|
||||||
# security.acme.certs.<name> path, which has the extra inheritDefaults
|
# security.acme.certs.<name> path, which has the extra inheritDefaults
|
||||||
# option, which if disabled means that we can't inherit it
|
# option, which if disabled means that we can't inherit it
|
||||||
default = if isDefaults || ! config.inheritDefaults then default else cfg.defaults.${name};
|
default = if isDefaults || !config.inheritDefaults then default else cfg.defaults.${name};
|
||||||
# The docs however don't need to depend on inheritDefaults, they should
|
# The docs however don't need to depend on inheritDefaults, they should
|
||||||
# stay constant. Though notably it wouldn't matter much, because to get
|
# stay constant. Though notably it wouldn't matter much, because to get
|
||||||
# the option information, a submodule with name `<name>` is evaluated
|
# the option information, a submodule with name `<name>` is evaluated
|
||||||
# without any definitions.
|
# without any definitions.
|
||||||
defaultText = if isDefaults then default else lib.literalExpression "config.security.acme.defaults.${name}";
|
defaultText =
|
||||||
|
if isDefaults then default else lib.literalExpression "config.security.acme.defaults.${name}";
|
||||||
};
|
};
|
||||||
in {
|
in
|
||||||
|
{
|
||||||
imports = [
|
imports = [
|
||||||
(lib.mkRenamedOptionModule [ "credentialsFile" ] [ "environmentFile" ])
|
(lib.mkRenamedOptionModule [ "credentialsFile" ] [ "environmentFile" ])
|
||||||
];
|
];
|
||||||
|
@ -545,7 +641,10 @@ let
|
||||||
|
|
||||||
server = lib.mkOption {
|
server = lib.mkOption {
|
||||||
type = lib.types.nullOr lib.types.str;
|
type = lib.types.nullOr lib.types.str;
|
||||||
inherit (defaultAndText "server" "https://acme-v02.api.letsencrypt.org/directory") default defaultText;
|
inherit (defaultAndText "server" "https://acme-v02.api.letsencrypt.org/directory")
|
||||||
|
default
|
||||||
|
defaultText
|
||||||
|
;
|
||||||
example = "https://acme-staging-v02.api.letsencrypt.org/directory";
|
example = "https://acme-staging-v02.api.letsencrypt.org/directory";
|
||||||
description = ''
|
description = ''
|
||||||
ACME Directory Resource URI.
|
ACME Directory Resource URI.
|
||||||
|
@ -573,7 +672,7 @@ let
|
||||||
|
|
||||||
reloadServices = lib.mkOption {
|
reloadServices = lib.mkOption {
|
||||||
type = lib.types.listOf lib.types.str;
|
type = lib.types.listOf lib.types.str;
|
||||||
inherit (defaultAndText "reloadServices" []) default defaultText;
|
inherit (defaultAndText "reloadServices" [ ]) default defaultText;
|
||||||
description = ''
|
description = ''
|
||||||
The list of systemd services to call `systemctl try-reload-or-restart`
|
The list of systemd services to call `systemctl try-reload-or-restart`
|
||||||
on.
|
on.
|
||||||
|
@ -648,7 +747,7 @@ let
|
||||||
|
|
||||||
credentialFiles = lib.mkOption {
|
credentialFiles = lib.mkOption {
|
||||||
type = lib.types.attrsOf (lib.types.path);
|
type = lib.types.attrsOf (lib.types.path);
|
||||||
inherit (defaultAndText "credentialFiles" {}) default defaultText;
|
inherit (defaultAndText "credentialFiles" { }) default defaultText;
|
||||||
description = ''
|
description = ''
|
||||||
Environment variables suffixed by "_FILE" to set for the cert's service
|
Environment variables suffixed by "_FILE" to set for the cert's service
|
||||||
for your selected dnsProvider.
|
for your selected dnsProvider.
|
||||||
|
@ -687,7 +786,7 @@ let
|
||||||
|
|
||||||
extraLegoFlags = lib.mkOption {
|
extraLegoFlags = lib.mkOption {
|
||||||
type = lib.types.listOf lib.types.str;
|
type = lib.types.listOf lib.types.str;
|
||||||
inherit (defaultAndText "extraLegoFlags" []) default defaultText;
|
inherit (defaultAndText "extraLegoFlags" [ ]) default defaultText;
|
||||||
description = ''
|
description = ''
|
||||||
Additional global flags to pass to all lego commands.
|
Additional global flags to pass to all lego commands.
|
||||||
'';
|
'';
|
||||||
|
@ -695,7 +794,7 @@ let
|
||||||
|
|
||||||
extraLegoRenewFlags = lib.mkOption {
|
extraLegoRenewFlags = lib.mkOption {
|
||||||
type = lib.types.listOf lib.types.str;
|
type = lib.types.listOf lib.types.str;
|
||||||
inherit (defaultAndText "extraLegoRenewFlags" []) default defaultText;
|
inherit (defaultAndText "extraLegoRenewFlags" [ ]) default defaultText;
|
||||||
description = ''
|
description = ''
|
||||||
Additional flags to pass to lego renew.
|
Additional flags to pass to lego renew.
|
||||||
'';
|
'';
|
||||||
|
@ -703,7 +802,7 @@ let
|
||||||
|
|
||||||
extraLegoRunFlags = lib.mkOption {
|
extraLegoRunFlags = lib.mkOption {
|
||||||
type = lib.types.listOf lib.types.str;
|
type = lib.types.listOf lib.types.str;
|
||||||
inherit (defaultAndText "extraLegoRunFlags" []) default defaultText;
|
inherit (defaultAndText "extraLegoRunFlags" [ ]) default defaultText;
|
||||||
description = ''
|
description = ''
|
||||||
Additional flags to pass to lego run.
|
Additional flags to pass to lego run.
|
||||||
'';
|
'';
|
||||||
|
@ -711,7 +810,9 @@ let
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
certOpts = { name, config, ... }: {
|
certOpts =
|
||||||
|
{ name, config, ... }:
|
||||||
|
{
|
||||||
options = {
|
options = {
|
||||||
# user option has been removed
|
# user option has been removed
|
||||||
user = lib.mkOption {
|
user = lib.mkOption {
|
||||||
|
@ -746,7 +847,7 @@ let
|
||||||
|
|
||||||
extraDomainNames = lib.mkOption {
|
extraDomainNames = lib.mkOption {
|
||||||
type = lib.types.listOf lib.types.str;
|
type = lib.types.listOf lib.types.str;
|
||||||
default = [];
|
default = [ ];
|
||||||
example = lib.literalExpression ''
|
example = lib.literalExpression ''
|
||||||
[
|
[
|
||||||
"example.org"
|
"example.org"
|
||||||
|
@ -776,7 +877,8 @@ let
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
in {
|
in
|
||||||
|
{
|
||||||
|
|
||||||
options = {
|
options = {
|
||||||
security.acme = {
|
security.acme = {
|
||||||
|
@ -826,7 +928,12 @@ in {
|
||||||
|
|
||||||
certs = lib.mkOption {
|
certs = lib.mkOption {
|
||||||
default = { };
|
default = { };
|
||||||
type = with lib.types; attrsOf (submodule [ (inheritableModule false) certOpts ]);
|
type =
|
||||||
|
with lib.types;
|
||||||
|
attrsOf (submodule [
|
||||||
|
(inheritableModule false)
|
||||||
|
certOpts
|
||||||
|
]);
|
||||||
description = ''
|
description = ''
|
||||||
Attribute set of certificates to get signed and renewed. Creates
|
Attribute set of certificates to get signed and renewed. Creates
|
||||||
`acme-''${cert}.{service,timer}` systemd units for
|
`acme-''${cert}.{service,timer}` systemd units for
|
||||||
|
@ -869,15 +976,41 @@ in {
|
||||||
To use the let's encrypt staging server, use security.acme.server =
|
To use the let's encrypt staging server, use security.acme.server =
|
||||||
"https://acme-staging-v02.api.letsencrypt.org/directory".
|
"https://acme-staging-v02.api.letsencrypt.org/directory".
|
||||||
'')
|
'')
|
||||||
(lib.mkRemovedOptionModule [ "security" "acme" "directory" ] "ACME Directory is now hardcoded to /var/lib/acme and its permissions are managed by systemd. See https://github.com/NixOS/nixpkgs/issues/53852 for more info.")
|
(lib.mkRemovedOptionModule [ "security" "acme" "directory" ]
|
||||||
(lib.mkRemovedOptionModule [ "security" "acme" "preDelay" ] "This option has been removed. If you want to make sure that something executes before certificates are provisioned, add a RequiredBy=acme-\${cert}.service to the service you want to execute before the cert renewal")
|
"ACME Directory is now hardcoded to /var/lib/acme and its permissions are managed by systemd. See https://github.com/NixOS/nixpkgs/issues/53852 for more info."
|
||||||
(lib.mkRemovedOptionModule [ "security" "acme" "activationDelay" ] "This option has been removed. If you want to make sure that something executes before certificates are provisioned, add a RequiredBy=acme-\${cert}.service to the service you want to execute before the cert renewal")
|
)
|
||||||
(lib.mkChangedOptionModule [ "security" "acme" "validMin" ] [ "security" "acme" "defaults" "validMinDays" ] (config: config.security.acme.validMin / (24 * 3600)))
|
(lib.mkRemovedOptionModule [ "security" "acme" "preDelay" ]
|
||||||
(lib.mkChangedOptionModule [ "security" "acme" "validMinDays" ] [ "security" "acme" "defaults" "validMinDays" ] (config: config.security.acme.validMinDays))
|
"This option has been removed. If you want to make sure that something executes before certificates are provisioned, add a RequiredBy=acme-\${cert}.service to the service you want to execute before the cert renewal"
|
||||||
(lib.mkChangedOptionModule [ "security" "acme" "renewInterval" ] [ "security" "acme" "defaults" "renewInterval" ] (config: config.security.acme.renewInterval))
|
)
|
||||||
(lib.mkChangedOptionModule [ "security" "acme" "email" ] [ "security" "acme" "defaults" "email" ] (config: config.security.acme.email))
|
(lib.mkRemovedOptionModule [ "security" "acme" "activationDelay" ]
|
||||||
(lib.mkChangedOptionModule [ "security" "acme" "server" ] [ "security" "acme" "defaults" "server" ] (config: config.security.acme.server))
|
"This option has been removed. If you want to make sure that something executes before certificates are provisioned, add a RequiredBy=acme-\${cert}.service to the service you want to execute before the cert renewal"
|
||||||
(lib.mkChangedOptionModule [ "security" "acme" "enableDebugLogs" ] [ "security" "acme" "defaults" "enableDebugLogs" ] (config: config.security.acme.enableDebugLogs))
|
)
|
||||||
|
(lib.mkChangedOptionModule
|
||||||
|
[ "security" "acme" "validMin" ]
|
||||||
|
[ "security" "acme" "defaults" "validMinDays" ]
|
||||||
|
(config: config.security.acme.validMin / (24 * 3600))
|
||||||
|
)
|
||||||
|
(lib.mkChangedOptionModule
|
||||||
|
[ "security" "acme" "validMinDays" ]
|
||||||
|
[ "security" "acme" "defaults" "validMinDays" ]
|
||||||
|
(config: config.security.acme.validMinDays)
|
||||||
|
)
|
||||||
|
(lib.mkChangedOptionModule
|
||||||
|
[ "security" "acme" "renewInterval" ]
|
||||||
|
[ "security" "acme" "defaults" "renewInterval" ]
|
||||||
|
(config: config.security.acme.renewInterval)
|
||||||
|
)
|
||||||
|
(lib.mkChangedOptionModule [ "security" "acme" "email" ] [ "security" "acme" "defaults" "email" ] (
|
||||||
|
config: config.security.acme.email
|
||||||
|
))
|
||||||
|
(lib.mkChangedOptionModule [ "security" "acme" "server" ] [ "security" "acme" "defaults" "server" ]
|
||||||
|
(config: config.security.acme.server)
|
||||||
|
)
|
||||||
|
(lib.mkChangedOptionModule
|
||||||
|
[ "security" "acme" "enableDebugLogs" ]
|
||||||
|
[ "security" "acme" "defaults" "enableDebugLogs" ]
|
||||||
|
(config: config.security.acme.enableDebugLogs)
|
||||||
|
)
|
||||||
];
|
];
|
||||||
|
|
||||||
config = lib.mkMerge [
|
config = lib.mkMerge [
|
||||||
|
@ -885,15 +1018,22 @@ in {
|
||||||
|
|
||||||
# FIXME Most of these custom warnings and filters for security.acme.certs.* are required
|
# FIXME Most of these custom warnings and filters for security.acme.certs.* are required
|
||||||
# because using mkRemovedOptionModule/mkChangedOptionModule with attrsets isn't possible.
|
# because using mkRemovedOptionModule/mkChangedOptionModule with attrsets isn't possible.
|
||||||
warnings = lib.filter (w: w != "") (lib.mapAttrsToList (cert: data: lib.optionalString (data.extraDomains != "_mkMergedOptionModule") ''
|
warnings = lib.filter (w: w != "") (
|
||||||
|
lib.mapAttrsToList (
|
||||||
|
cert: data:
|
||||||
|
lib.optionalString (data.extraDomains != "_mkMergedOptionModule") ''
|
||||||
The option definition `security.acme.certs.${cert}.extraDomains` has changed
|
The option definition `security.acme.certs.${cert}.extraDomains` has changed
|
||||||
to `security.acme.certs.${cert}.extraDomainNames` and is now a list of strings.
|
to `security.acme.certs.${cert}.extraDomainNames` and is now a list of strings.
|
||||||
Setting a custom webroot for extra domains is not possible, instead use separate certs.
|
Setting a custom webroot for extra domains is not possible, instead use separate certs.
|
||||||
'') cfg.certs);
|
''
|
||||||
|
) cfg.certs
|
||||||
|
);
|
||||||
|
|
||||||
assertions = let
|
assertions =
|
||||||
|
let
|
||||||
certs = lib.attrValues cfg.certs;
|
certs = lib.attrValues cfg.certs;
|
||||||
in [
|
in
|
||||||
|
[
|
||||||
{
|
{
|
||||||
assertion = cfg.defaults.email != null || lib.all (certOpts: certOpts.email != null) certs;
|
assertion = cfg.defaults.email != null || lib.all (certOpts: certOpts.email != null) certs;
|
||||||
message = ''
|
message = ''
|
||||||
|
@ -910,7 +1050,9 @@ in {
|
||||||
to `true`. For Let's Encrypt's ToS see https://letsencrypt.org/repository/
|
to `true`. For Let's Encrypt's ToS see https://letsencrypt.org/repository/
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
] ++ (builtins.concatLists (lib.mapAttrsToList (cert: data: [
|
]
|
||||||
|
++ (builtins.concatLists (
|
||||||
|
lib.mapAttrsToList (cert: data: [
|
||||||
{
|
{
|
||||||
assertion = data.user == "_mkRemovedOptionModule";
|
assertion = data.user == "_mkRemovedOptionModule";
|
||||||
message = ''
|
message = ''
|
||||||
|
@ -932,7 +1074,7 @@ in {
|
||||||
# referencing them as a user quite weird too. Best practice is to use
|
# referencing them as a user quite weird too. Best practice is to use
|
||||||
# the domain option.
|
# the domain option.
|
||||||
{
|
{
|
||||||
assertion = ! lib.hasInfix "*" cert;
|
assertion = !lib.hasInfix "*" cert;
|
||||||
message = ''
|
message = ''
|
||||||
The cert option path `security.acme.certs.${cert}.dnsProvider`
|
The cert option path `security.acme.certs.${cert}.dnsProvider`
|
||||||
cannot contain a * character.
|
cannot contain a * character.
|
||||||
|
@ -940,9 +1082,18 @@ in {
|
||||||
and remove the wildcard from the path.
|
and remove the wildcard from the path.
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
(let exclusiveAttrs = {
|
(
|
||||||
inherit (data) dnsProvider webroot listenHTTP s3Bucket;
|
let
|
||||||
}; in {
|
exclusiveAttrs = {
|
||||||
|
inherit (data)
|
||||||
|
dnsProvider
|
||||||
|
webroot
|
||||||
|
listenHTTP
|
||||||
|
s3Bucket
|
||||||
|
;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
assertion = lib.length (lib.filter (x: x != null) (builtins.attrValues exclusiveAttrs)) == 1;
|
assertion = lib.length (lib.filter (x: x != null) (builtins.attrValues exclusiveAttrs)) == 1;
|
||||||
message = ''
|
message = ''
|
||||||
Exactly one of the options
|
Exactly one of the options
|
||||||
|
@ -951,9 +1102,10 @@ in {
|
||||||
`security.acme.certs.${cert}.listenHTTP` and
|
`security.acme.certs.${cert}.listenHTTP` and
|
||||||
`security.acme.certs.${cert}.s3Bucket`
|
`security.acme.certs.${cert}.s3Bucket`
|
||||||
is required.
|
is required.
|
||||||
Current values: ${(lib.generators.toPretty {} exclusiveAttrs)}.
|
Current values: ${(lib.generators.toPretty { } exclusiveAttrs)}.
|
||||||
'';
|
'';
|
||||||
})
|
}
|
||||||
|
)
|
||||||
{
|
{
|
||||||
assertion = lib.all (lib.hasSuffix "_FILE") (lib.attrNames data.credentialFiles);
|
assertion = lib.all (lib.hasSuffix "_FILE") (lib.attrNames data.credentialFiles);
|
||||||
message = ''
|
message = ''
|
||||||
|
@ -961,7 +1113,8 @@ in {
|
||||||
used for variables suffixed by "_FILE".
|
used for variables suffixed by "_FILE".
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
]) cfg.certs));
|
]) cfg.certs
|
||||||
|
));
|
||||||
|
|
||||||
users.users.acme = {
|
users.users.acme = {
|
||||||
home = "/var/lib/acme";
|
home = "/var/lib/acme";
|
||||||
|
@ -970,31 +1123,48 @@ in {
|
||||||
isSystemUser = true;
|
isSystemUser = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
users.groups.acme = {};
|
users.groups.acme = { };
|
||||||
|
|
||||||
systemd.services = let
|
systemd.services =
|
||||||
renewServiceFunctions = lib.mapAttrs' (cert: conf: lib.nameValuePair "acme-${cert}" conf.renewService) certConfigs;
|
let
|
||||||
renewServices = if cfg.maxConcurrentRenewals > 0
|
renewServiceFunctions = lib.mapAttrs' (
|
||||||
then roundRobinApplyAttrs renewServiceFunctions concurrencyLockfiles
|
cert: conf: lib.nameValuePair "acme-${cert}" conf.renewService
|
||||||
else lib.mapAttrs (_: f: f null) renewServiceFunctions;
|
) certConfigs;
|
||||||
selfsignServiceFunctions = lib.mapAttrs' (cert: conf: lib.nameValuePair "acme-selfsigned-${cert}" conf.selfsignService) certConfigs;
|
renewServices =
|
||||||
selfsignServices = if cfg.maxConcurrentRenewals > 0
|
if cfg.maxConcurrentRenewals > 0 then
|
||||||
then roundRobinApplyAttrs selfsignServiceFunctions concurrencyLockfiles
|
roundRobinApplyAttrs renewServiceFunctions concurrencyLockfiles
|
||||||
else lib.mapAttrs (_: f: f null) selfsignServiceFunctions;
|
else
|
||||||
|
lib.mapAttrs (_: f: f null) renewServiceFunctions;
|
||||||
|
selfsignServiceFunctions = lib.mapAttrs' (
|
||||||
|
cert: conf: lib.nameValuePair "acme-selfsigned-${cert}" conf.selfsignService
|
||||||
|
) certConfigs;
|
||||||
|
selfsignServices =
|
||||||
|
if cfg.maxConcurrentRenewals > 0 then
|
||||||
|
roundRobinApplyAttrs selfsignServiceFunctions concurrencyLockfiles
|
||||||
|
else
|
||||||
|
lib.mapAttrs (_: f: f null) selfsignServiceFunctions;
|
||||||
in
|
in
|
||||||
{ acme-setup = setupService; }
|
{
|
||||||
|
acme-setup = setupService;
|
||||||
|
}
|
||||||
// renewServices
|
// renewServices
|
||||||
// lib.optionalAttrs cfg.preliminarySelfsigned selfsignServices;
|
// lib.optionalAttrs cfg.preliminarySelfsigned selfsignServices;
|
||||||
|
|
||||||
systemd.timers = lib.mapAttrs' (cert: conf: lib.nameValuePair "acme-${cert}" conf.renewTimer) certConfigs;
|
systemd.timers = lib.mapAttrs' (
|
||||||
|
cert: conf: lib.nameValuePair "acme-${cert}" conf.renewTimer
|
||||||
|
) certConfigs;
|
||||||
|
|
||||||
systemd.targets = let
|
systemd.targets =
|
||||||
|
let
|
||||||
# Create some targets which can be depended on to be "active" after cert renewals
|
# Create some targets which can be depended on to be "active" after cert renewals
|
||||||
finishedTargets = lib.mapAttrs' (cert: conf: lib.nameValuePair "acme-finished-${cert}" {
|
finishedTargets = lib.mapAttrs' (
|
||||||
|
cert: conf:
|
||||||
|
lib.nameValuePair "acme-finished-${cert}" {
|
||||||
wantedBy = [ "default.target" ];
|
wantedBy = [ "default.target" ];
|
||||||
requires = [ "acme-${cert}.service" ];
|
requires = [ "acme-${cert}.service" ];
|
||||||
after = [ "acme-${cert}.service" ];
|
after = [ "acme-${cert}.service" ];
|
||||||
}) certConfigs;
|
}
|
||||||
|
) certConfigs;
|
||||||
|
|
||||||
# Create targets to limit the number of simultaneous account creations
|
# Create targets to limit the number of simultaneous account creations
|
||||||
# How it works:
|
# How it works:
|
||||||
|
@ -1005,18 +1175,25 @@ in {
|
||||||
# Using a target here is fine - account creation is a one time event. Even if
|
# Using a target here is fine - account creation is a one time event. Even if
|
||||||
# systemd clean --what=state is used to delete the account, so long as the user
|
# systemd clean --what=state is used to delete the account, so long as the user
|
||||||
# then runs one of the cert services, there won't be any issues.
|
# then runs one of the cert services, there won't be any issues.
|
||||||
accountTargets = lib.mapAttrs' (hash: confs: let
|
accountTargets = lib.mapAttrs' (
|
||||||
|
hash: confs:
|
||||||
|
let
|
||||||
dnsConfs = builtins.filter (conf: cfg.certs.${conf.cert}.dnsProvider != null) confs;
|
dnsConfs = builtins.filter (conf: cfg.certs.${conf.cert}.dnsProvider != null) confs;
|
||||||
leaderConf = if dnsConfs != [ ] then builtins.head dnsConfs else builtins.head confs;
|
leaderConf = if dnsConfs != [ ] then builtins.head dnsConfs else builtins.head confs;
|
||||||
leader = "acme-${leaderConf.cert}.service";
|
leader = "acme-${leaderConf.cert}.service";
|
||||||
followers = map (conf: "acme-${conf.cert}.service") (builtins.filter (conf: conf != leaderConf) confs);
|
followers = map (conf: "acme-${conf.cert}.service") (
|
||||||
in lib.nameValuePair "acme-account-${hash}" {
|
builtins.filter (conf: conf != leaderConf) confs
|
||||||
|
);
|
||||||
|
in
|
||||||
|
lib.nameValuePair "acme-account-${hash}" {
|
||||||
requiredBy = followers;
|
requiredBy = followers;
|
||||||
before = followers;
|
before = followers;
|
||||||
requires = [ leader ];
|
requires = [ leader ];
|
||||||
after = [ leader ];
|
after = [ leader ];
|
||||||
}) (lib.groupBy (conf: conf.accountHash) (lib.attrValues certConfigs));
|
}
|
||||||
in finishedTargets // accountTargets;
|
) (lib.groupBy (conf: conf.accountHash) (lib.attrValues certConfigs));
|
||||||
|
in
|
||||||
|
finishedTargets // accountTargets;
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -1,22 +1,30 @@
|
||||||
lib:
|
lib:
|
||||||
|
|
||||||
{ cert, groups, services }:
|
{
|
||||||
|
cert,
|
||||||
|
groups,
|
||||||
|
services,
|
||||||
|
}:
|
||||||
let
|
let
|
||||||
catSep = builtins.concatStringsSep;
|
catSep = builtins.concatStringsSep;
|
||||||
|
|
||||||
svcUser = svc: svc.serviceConfig.User or "root";
|
svcUser = svc: svc.serviceConfig.User or "root";
|
||||||
svcGroups = svc:
|
svcGroups =
|
||||||
|
svc:
|
||||||
(lib.optional (svc.serviceConfig ? Group) svc.serviceConfig.Group)
|
(lib.optional (svc.serviceConfig ? Group) svc.serviceConfig.Group)
|
||||||
++ lib.toList (svc.serviceConfig.SupplementaryGroups or [ ]);
|
++ lib.toList (svc.serviceConfig.SupplementaryGroups or [ ]);
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
assertion = builtins.all (svc:
|
assertion = builtins.all (
|
||||||
|
svc:
|
||||||
svcUser svc == "root"
|
svcUser svc == "root"
|
||||||
|| builtins.elem (svcUser svc) groups.${cert.group}.members
|
|| builtins.elem (svcUser svc) groups.${cert.group}.members
|
||||||
|| builtins.elem cert.group (svcGroups svc)
|
|| builtins.elem cert.group (svcGroups svc)
|
||||||
) services;
|
) services;
|
||||||
|
|
||||||
message = "Certificate ${cert.domain} (group=${cert.group}) must be readable by service(s) ${
|
message = "Certificate ${cert.domain} (group=${cert.group}) must be readable by service(s) ${
|
||||||
catSep ", " (map (svc: "${svc.name} (user=${svcUser svc} groups=${catSep "," (svcGroups svc)})") services)
|
catSep ", " (
|
||||||
|
map (svc: "${svc.name} (user=${svcUser svc} groups=${catSep "," (svcGroups svc)})") services
|
||||||
|
)
|
||||||
}";
|
}";
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,12 +4,10 @@
|
||||||
pkgs,
|
pkgs,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
with lib; let
|
with lib;
|
||||||
|
let
|
||||||
cfg = config.security.ipa;
|
cfg = config.security.ipa;
|
||||||
pyBool = x:
|
pyBool = x: if x then "True" else "False";
|
||||||
if x
|
|
||||||
then "True"
|
|
||||||
else "False";
|
|
||||||
|
|
||||||
ldapConf = pkgs.writeText "ldap.conf" ''
|
ldapConf = pkgs.writeText "ldap.conf" ''
|
||||||
# Turning this off breaks GSSAPI used with krb5 when rdns = false
|
# Turning this off breaks GSSAPI used with krb5 when rdns = false
|
||||||
|
@ -22,13 +20,15 @@ with lib; let
|
||||||
nssDb =
|
nssDb =
|
||||||
pkgs.runCommand "ipa-nssdb"
|
pkgs.runCommand "ipa-nssdb"
|
||||||
{
|
{
|
||||||
nativeBuildInputs = [pkgs.nss.tools];
|
nativeBuildInputs = [ pkgs.nss.tools ];
|
||||||
} ''
|
}
|
||||||
|
''
|
||||||
mkdir -p $out
|
mkdir -p $out
|
||||||
certutil -d $out -N --empty-password
|
certutil -d $out -N --empty-password
|
||||||
certutil -d $out -A --empty-password -n "${cfg.realm} IPA CA" -t CT,C,C -i ${cfg.certificate}
|
certutil -d $out -A --empty-password -n "${cfg.realm} IPA CA" -t CT,C,C -i ${cfg.certificate}
|
||||||
'';
|
'';
|
||||||
in {
|
in
|
||||||
|
{
|
||||||
options = {
|
options = {
|
||||||
security.ipa = {
|
security.ipa = {
|
||||||
enable = mkEnableOption "FreeIPA domain integration";
|
enable = mkEnableOption "FreeIPA domain integration";
|
||||||
|
@ -88,8 +88,11 @@ in {
|
||||||
ipaHostname = mkOption {
|
ipaHostname = mkOption {
|
||||||
type = types.str;
|
type = types.str;
|
||||||
example = "myworkstation.example.com";
|
example = "myworkstation.example.com";
|
||||||
default = if config.networking.domain != null then config.networking.fqdn
|
default =
|
||||||
else "${config.networking.hostName}.${cfg.domain}";
|
if config.networking.domain != null then
|
||||||
|
config.networking.fqdn
|
||||||
|
else
|
||||||
|
"${config.networking.hostName}.${cfg.domain}";
|
||||||
defaultText = literalExpression ''
|
defaultText = literalExpression ''
|
||||||
if config.networking.domain != null then config.networking.fqdn
|
if config.networking.domain != null then config.networking.fqdn
|
||||||
else "''${networking.hostName}.''${security.ipa.domain}"
|
else "''${networking.hostName}.''${security.ipa.domain}"
|
||||||
|
@ -99,7 +102,7 @@ in {
|
||||||
|
|
||||||
ifpAllowedUids = mkOption {
|
ifpAllowedUids = mkOption {
|
||||||
type = types.listOf types.str;
|
type = types.listOf types.str;
|
||||||
default = ["root"];
|
default = [ "root" ];
|
||||||
description = "A list of users allowed to access the ifp dbus interface.";
|
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 = {
|
environment.etc = {
|
||||||
"ipa/default.conf".text = ''
|
"ipa/default.conf".text = ''
|
||||||
|
@ -195,7 +201,10 @@ in {
|
||||||
|
|
||||||
systemd.services."ipa-activation" = {
|
systemd.services."ipa-activation" = {
|
||||||
wantedBy = [ "sysinit.target" ];
|
wantedBy = [ "sysinit.target" ];
|
||||||
before = [ "sysinit.target" "shutdown.target" ];
|
before = [
|
||||||
|
"sysinit.target"
|
||||||
|
"shutdown.target"
|
||||||
|
];
|
||||||
conflicts = [ "shutdown.target" ];
|
conflicts = [ "shutdown.target" ];
|
||||||
unitConfig.DefaultDependencies = false;
|
unitConfig.DefaultDependencies = false;
|
||||||
serviceConfig.Type = "oneshot";
|
serviceConfig.Type = "oneshot";
|
||||||
|
@ -234,8 +243,7 @@ in {
|
||||||
|
|
||||||
cache_credentials = ${pyBool cfg.cacheCredentials}
|
cache_credentials = ${pyBool cfg.cacheCredentials}
|
||||||
krb5_store_password_if_offline = ${pyBool cfg.offlinePasswords}
|
krb5_store_password_if_offline = ${pyBool cfg.offlinePasswords}
|
||||||
${optionalString ((toLower cfg.domain) != (toLower cfg.realm))
|
${optionalString ((toLower cfg.domain) != (toLower cfg.realm)) "krb5_realm = ${cfg.realm}"}
|
||||||
"krb5_realm = ${cfg.realm}"}
|
|
||||||
|
|
||||||
dyndns_update = ${pyBool cfg.dyndns.enable}
|
dyndns_update = ${pyBool cfg.dyndns.enable}
|
||||||
dyndns_iface = ${cfg.dyndns.interface}
|
dyndns_iface = ${cfg.dyndns.interface}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,7 +1,13 @@
|
||||||
# A module for ‘rtkit’, a DBus system service that hands out realtime
|
# A module for ‘rtkit’, a DBus system service that hands out realtime
|
||||||
# scheduling priority to processes that ask for it.
|
# scheduling priority to processes that ask for it.
|
||||||
|
|
||||||
{ config, lib, pkgs, utils, ... }:
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
utils,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
with lib;
|
with lib;
|
||||||
|
|
||||||
|
@ -9,7 +15,8 @@ let
|
||||||
cfg = config.security.rtkit;
|
cfg = config.security.rtkit;
|
||||||
package = pkgs.rtkit;
|
package = pkgs.rtkit;
|
||||||
|
|
||||||
in {
|
in
|
||||||
|
{
|
||||||
|
|
||||||
options = {
|
options = {
|
||||||
|
|
||||||
|
@ -26,7 +33,7 @@ in {
|
||||||
|
|
||||||
security.rtkit.args = mkOption {
|
security.rtkit.args = mkOption {
|
||||||
type = types.listOf types.str;
|
type = types.listOf types.str;
|
||||||
default = [];
|
default = [ ];
|
||||||
description = ''
|
description = ''
|
||||||
Command-line options for `rtkit-daemon`.
|
Command-line options for `rtkit-daemon`.
|
||||||
'';
|
'';
|
||||||
|
@ -38,7 +45,6 @@ in {
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
config = mkIf cfg.enable {
|
config = mkIf cfg.enable {
|
||||||
|
|
||||||
security.polkit.enable = true;
|
security.polkit.enable = true;
|
||||||
|
@ -57,13 +63,12 @@ in {
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
users.users.rtkit =
|
users.users.rtkit = {
|
||||||
{
|
|
||||||
isSystemUser = true;
|
isSystemUser = true;
|
||||||
group = "rtkit";
|
group = "rtkit";
|
||||||
description = "RealtimeKit daemon";
|
description = "RealtimeKit daemon";
|
||||||
};
|
};
|
||||||
users.groups.rtkit = {};
|
users.groups.rtkit = { };
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
{ config, lib, pkgs, ... }:
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
let
|
let
|
||||||
|
|
||||||
inherit (config.security) wrapperDir;
|
inherit (config.security) wrapperDir;
|
||||||
|
@ -11,7 +16,9 @@ let
|
||||||
# musl is security-focused and generally more minimal, so it's a better choice here.
|
# 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
|
# The dynamic linker is still a fairly complex piece of code, and the wrappers are
|
||||||
# quite small, so linking it statically is more appropriate.
|
# quite small, so linking it statically is more appropriate.
|
||||||
securityWrapper = sourceProg : pkgs.pkgsStatic.callPackage ./wrapper.nix {
|
securityWrapper =
|
||||||
|
sourceProg:
|
||||||
|
pkgs.pkgsStatic.callPackage ./wrapper.nix {
|
||||||
inherit sourceProg;
|
inherit sourceProg;
|
||||||
|
|
||||||
# glibc definitions of insecure environment variables
|
# glibc definitions of insecure environment variables
|
||||||
|
@ -22,14 +29,16 @@ let
|
||||||
# They're taken from pkgs.glibc so that we don't have to keep as close
|
# 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,
|
# 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.
|
# so we maintain a slightly stricter list in wrapper.c itself as well.
|
||||||
unsecvars = lib.overrideDerivation (pkgs.srcOnly pkgs.glibc)
|
unsecvars = lib.overrideDerivation (pkgs.srcOnly pkgs.glibc) (
|
||||||
({ name, ... }: {
|
{ name, ... }:
|
||||||
|
{
|
||||||
name = "${name}-unsecvars";
|
name = "${name}-unsecvars";
|
||||||
installPhase = ''
|
installPhase = ''
|
||||||
mkdir $out
|
mkdir $out
|
||||||
cp sysdeps/generic/unsecvars.h $out
|
cp sysdeps/generic/unsecvars.h $out
|
||||||
'';
|
'';
|
||||||
});
|
}
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
fileModeType =
|
fileModeType =
|
||||||
|
@ -39,36 +48,37 @@ let
|
||||||
numeric = "[-+=]?[0-7]{0,4}";
|
numeric = "[-+=]?[0-7]{0,4}";
|
||||||
mode = "((${symbolic})(,${symbolic})*)|(${numeric})";
|
mode = "((${symbolic})(,${symbolic})*)|(${numeric})";
|
||||||
in
|
in
|
||||||
lib.types.strMatching mode
|
lib.types.strMatching mode // { description = "file mode string"; };
|
||||||
// { description = "file mode string"; };
|
|
||||||
|
|
||||||
wrapperType = lib.types.submodule ({ name, config, ... }: {
|
wrapperType = lib.types.submodule (
|
||||||
options.enable = lib.mkOption
|
{ name, config, ... }:
|
||||||
{ type = lib.types.bool;
|
{
|
||||||
|
options.enable = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
default = true;
|
default = true;
|
||||||
description = "Whether to enable the wrapper.";
|
description = "Whether to enable the wrapper.";
|
||||||
};
|
};
|
||||||
options.source = lib.mkOption
|
options.source = lib.mkOption {
|
||||||
{ type = lib.types.path;
|
type = lib.types.path;
|
||||||
description = "The absolute path to the program to be wrapped.";
|
description = "The absolute path to the program to be wrapped.";
|
||||||
};
|
};
|
||||||
options.program = lib.mkOption
|
options.program = lib.mkOption {
|
||||||
{ type = with lib.types; nullOr str;
|
type = with lib.types; nullOr str;
|
||||||
default = name;
|
default = name;
|
||||||
description = ''
|
description = ''
|
||||||
The name of the wrapper program. Defaults to the attribute name.
|
The name of the wrapper program. Defaults to the attribute name.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
options.owner = lib.mkOption
|
options.owner = lib.mkOption {
|
||||||
{ type = lib.types.str;
|
type = lib.types.str;
|
||||||
description = "The owner of the wrapper program.";
|
description = "The owner of the wrapper program.";
|
||||||
};
|
};
|
||||||
options.group = lib.mkOption
|
options.group = lib.mkOption {
|
||||||
{ type = lib.types.str;
|
type = lib.types.str;
|
||||||
description = "The group of the wrapper program.";
|
description = "The group of the wrapper program.";
|
||||||
};
|
};
|
||||||
options.permissions = lib.mkOption
|
options.permissions = lib.mkOption {
|
||||||
{ type = fileModeType;
|
type = fileModeType;
|
||||||
default = "u+rx,g+x,o+x";
|
default = "u+rx,g+x,o+x";
|
||||||
example = "a+rx";
|
example = "a+rx";
|
||||||
description = ''
|
description = ''
|
||||||
|
@ -76,8 +86,8 @@ let
|
||||||
symbolic or numeric file mode understood by {command}`chmod`.
|
symbolic or numeric file mode understood by {command}`chmod`.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
options.capabilities = lib.mkOption
|
options.capabilities = lib.mkOption {
|
||||||
{ type = lib.types.commas;
|
type = lib.types.commas;
|
||||||
default = "";
|
default = "";
|
||||||
description = ''
|
description = ''
|
||||||
A comma-separated list of capability clauses to be given to the
|
A comma-separated list of capability clauses to be given to the
|
||||||
|
@ -96,27 +106,29 @@ let
|
||||||
:::
|
:::
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
options.setuid = lib.mkOption
|
options.setuid = lib.mkOption {
|
||||||
{ type = lib.types.bool;
|
type = lib.types.bool;
|
||||||
default = false;
|
default = false;
|
||||||
description = "Whether to add the setuid bit the wrapper program.";
|
description = "Whether to add the setuid bit the wrapper program.";
|
||||||
};
|
};
|
||||||
options.setgid = lib.mkOption
|
options.setgid = lib.mkOption {
|
||||||
{ type = lib.types.bool;
|
type = lib.types.bool;
|
||||||
default = false;
|
default = false;
|
||||||
description = "Whether to add the setgid bit the wrapper program.";
|
description = "Whether to add the setgid bit the wrapper program.";
|
||||||
};
|
};
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
###### Activation script for the setcap wrappers
|
###### Activation script for the setcap wrappers
|
||||||
mkSetcapProgram =
|
mkSetcapProgram =
|
||||||
{ program
|
{
|
||||||
, capabilities
|
program,
|
||||||
, source
|
capabilities,
|
||||||
, owner
|
source,
|
||||||
, group
|
owner,
|
||||||
, permissions
|
group,
|
||||||
, ...
|
permissions,
|
||||||
|
...
|
||||||
}:
|
}:
|
||||||
''
|
''
|
||||||
cp ${securityWrapper source}/bin/security-wrapper "$wrapperDir/${program}"
|
cp ${securityWrapper source}/bin/security-wrapper "$wrapperDir/${program}"
|
||||||
|
@ -136,14 +148,15 @@ let
|
||||||
|
|
||||||
###### Activation script for the setuid wrappers
|
###### Activation script for the setuid wrappers
|
||||||
mkSetuidProgram =
|
mkSetuidProgram =
|
||||||
{ program
|
{
|
||||||
, source
|
program,
|
||||||
, owner
|
source,
|
||||||
, group
|
owner,
|
||||||
, setuid
|
group,
|
||||||
, setgid
|
setuid,
|
||||||
, permissions
|
setgid,
|
||||||
, ...
|
permissions,
|
||||||
|
...
|
||||||
}:
|
}:
|
||||||
''
|
''
|
||||||
cp ${securityWrapper source}/bin/security-wrapper "$wrapperDir/${program}"
|
cp ${securityWrapper source}/bin/security-wrapper "$wrapperDir/${program}"
|
||||||
|
@ -155,12 +168,8 @@ let
|
||||||
chmod "u${if setuid then "+" else "-"}s,g${if setgid then "+" else "-"}s,${permissions}" "$wrapperDir/${program}"
|
chmod "u${if setuid then "+" else "-"}s,g${if setgid then "+" else "-"}s,${permissions}" "$wrapperDir/${program}"
|
||||||
'';
|
'';
|
||||||
|
|
||||||
mkWrappedPrograms =
|
mkWrappedPrograms = builtins.map (
|
||||||
builtins.map
|
opts: if opts.capabilities != "" then mkSetcapProgram opts else mkSetuidProgram opts
|
||||||
(opts:
|
|
||||||
if opts.capabilities != ""
|
|
||||||
then mkSetcapProgram opts
|
|
||||||
else mkSetuidProgram opts
|
|
||||||
) (lib.attrValues wrappers);
|
) (lib.attrValues wrappers);
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
|
@ -178,9 +187,8 @@ in
|
||||||
|
|
||||||
security.wrappers = lib.mkOption {
|
security.wrappers = lib.mkOption {
|
||||||
type = lib.types.attrsOf wrapperType;
|
type = lib.types.attrsOf wrapperType;
|
||||||
default = {};
|
default = { };
|
||||||
example = lib.literalExpression
|
example = lib.literalExpression ''
|
||||||
''
|
|
||||||
{
|
{
|
||||||
# a setuid root program
|
# a setuid root program
|
||||||
doas =
|
doas =
|
||||||
|
@ -239,26 +247,25 @@ in
|
||||||
###### implementation
|
###### implementation
|
||||||
config = lib.mkIf config.security.enableWrappers {
|
config = lib.mkIf config.security.enableWrappers {
|
||||||
|
|
||||||
assertions = lib.mapAttrsToList
|
assertions = lib.mapAttrsToList (name: opts: {
|
||||||
(name: opts:
|
assertion = opts.setuid || opts.setgid -> opts.capabilities == "";
|
||||||
{ assertion = opts.setuid || opts.setgid -> opts.capabilities == "";
|
|
||||||
message = ''
|
message = ''
|
||||||
The security.wrappers.${name} wrapper is not valid:
|
The security.wrappers.${name} wrapper is not valid:
|
||||||
setuid/setgid and capabilities are mutually exclusive.
|
setuid/setgid and capabilities are mutually exclusive.
|
||||||
'';
|
'';
|
||||||
}
|
}) wrappers;
|
||||||
) wrappers;
|
|
||||||
|
|
||||||
security.wrappers =
|
security.wrappers =
|
||||||
let
|
let
|
||||||
mkSetuidRoot = source:
|
mkSetuidRoot = source: {
|
||||||
{ setuid = true;
|
setuid = true;
|
||||||
owner = "root";
|
owner = "root";
|
||||||
group = "root";
|
group = "root";
|
||||||
inherit source;
|
inherit source;
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
{ # These are mount related wrappers that require the +s permission.
|
{
|
||||||
|
# These are mount related wrappers that require the +s permission.
|
||||||
fusermount = mkSetuidRoot "${lib.getBin pkgs.fuse}/bin/fusermount";
|
fusermount = mkSetuidRoot "${lib.getBin pkgs.fuse}/bin/fusermount";
|
||||||
fusermount3 = mkSetuidRoot "${lib.getBin pkgs.fuse3}/bin/fusermount3";
|
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";
|
||||||
|
@ -272,15 +279,20 @@ in
|
||||||
export PATH="${wrapperDir}:$PATH"
|
export PATH="${wrapperDir}:$PATH"
|
||||||
'';
|
'';
|
||||||
|
|
||||||
security.apparmor.includes = lib.mapAttrs' (wrapName: wrap: lib.nameValuePair
|
security.apparmor.includes = lib.mapAttrs' (
|
||||||
"nixos/security.wrappers/${wrapName}" ''
|
wrapName: wrap:
|
||||||
include "${pkgs.apparmorRulesFromClosure { name="security.wrappers.${wrapName}"; } [
|
lib.nameValuePair "nixos/security.wrappers/${wrapName}" ''
|
||||||
|
include "${
|
||||||
|
pkgs.apparmorRulesFromClosure { name = "security.wrappers.${wrapName}"; } [
|
||||||
(securityWrapper wrap.source)
|
(securityWrapper wrap.source)
|
||||||
]}"
|
]
|
||||||
|
}"
|
||||||
mrpx ${wrap.source},
|
mrpx ${wrap.source},
|
||||||
'') wrappers;
|
''
|
||||||
|
) wrappers;
|
||||||
|
|
||||||
systemd.mounts = [{
|
systemd.mounts = [
|
||||||
|
{
|
||||||
where = parentWrapperDir;
|
where = parentWrapperDir;
|
||||||
what = "tmpfs";
|
what = "tmpfs";
|
||||||
type = "tmpfs";
|
type = "tmpfs";
|
||||||
|
@ -289,16 +301,23 @@ in
|
||||||
"mode=755"
|
"mode=755"
|
||||||
"size=${config.security.wrapperDirSize}"
|
"size=${config.security.wrapperDirSize}"
|
||||||
]);
|
]);
|
||||||
}];
|
}
|
||||||
|
];
|
||||||
|
|
||||||
systemd.services.suid-sgid-wrappers = {
|
systemd.services.suid-sgid-wrappers = {
|
||||||
description = "Create SUID/SGID Wrappers";
|
description = "Create SUID/SGID Wrappers";
|
||||||
wantedBy = [ "sysinit.target" ];
|
wantedBy = [ "sysinit.target" ];
|
||||||
before = [ "sysinit.target" "shutdown.target" ];
|
before = [
|
||||||
|
"sysinit.target"
|
||||||
|
"shutdown.target"
|
||||||
|
];
|
||||||
conflicts = [ "shutdown.target" ];
|
conflicts = [ "shutdown.target" ];
|
||||||
after = [ "systemd-sysusers.service" ];
|
after = [ "systemd-sysusers.service" ];
|
||||||
unitConfig.DefaultDependencies = false;
|
unitConfig.DefaultDependencies = false;
|
||||||
unitConfig.RequiresMountsFor = [ "/nix/store" "/run/wrappers" ];
|
unitConfig.RequiresMountsFor = [
|
||||||
|
"/nix/store"
|
||||||
|
"/run/wrappers"
|
||||||
|
];
|
||||||
serviceConfig.Type = "oneshot";
|
serviceConfig.Type = "oneshot";
|
||||||
script = ''
|
script = ''
|
||||||
chmod 755 "${parentWrapperDir}"
|
chmod 755 "${parentWrapperDir}"
|
||||||
|
@ -327,17 +346,19 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
###### wrappers consistency checks
|
###### wrappers consistency checks
|
||||||
system.checks = lib.singleton (pkgs.runCommand "ensure-all-wrappers-paths-exist" {
|
system.checks = lib.singleton (
|
||||||
|
pkgs.runCommand "ensure-all-wrappers-paths-exist"
|
||||||
|
{
|
||||||
preferLocalBuild = true;
|
preferLocalBuild = true;
|
||||||
} ''
|
}
|
||||||
|
''
|
||||||
# make sure we produce output
|
# make sure we produce output
|
||||||
mkdir -p $out
|
mkdir -p $out
|
||||||
|
|
||||||
echo -n "Checking that Nix store paths of all wrapped programs exist... "
|
echo -n "Checking that Nix store paths of all wrapped programs exist... "
|
||||||
|
|
||||||
declare -A wrappers
|
declare -A wrappers
|
||||||
${lib.concatStringsSep "\n" (lib.mapAttrsToList (n: v:
|
${lib.concatStringsSep "\n" (lib.mapAttrsToList (n: v: "wrappers['${n}']='${v.source}'") wrappers)}
|
||||||
"wrappers['${n}']='${v.source}'") wrappers)}
|
|
||||||
|
|
||||||
for name in "''${!wrappers[@]}"; do
|
for name in "''${!wrappers[@]}"; do
|
||||||
path="''${wrappers[$name]}"
|
path="''${wrappers[$name]}"
|
||||||
|
@ -352,6 +373,7 @@ in
|
||||||
done
|
done
|
||||||
|
|
||||||
echo "OK"
|
echo "OK"
|
||||||
'');
|
''
|
||||||
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
{ config, lib, pkgs, ... }:
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
let
|
let
|
||||||
cfg = config.services.gonic;
|
cfg = config.services.gonic;
|
||||||
settingsFormat = pkgs.formats.keyValue {
|
settingsFormat = pkgs.formats.keyValue {
|
||||||
|
@ -42,7 +47,9 @@ in
|
||||||
ExecStart =
|
ExecStart =
|
||||||
let
|
let
|
||||||
# these values are null by default but should not appear in the final config
|
# these values are null by default but should not appear in the final config
|
||||||
filteredSettings = lib.filterAttrs (n: v: !((n == "tls-cert" || n == "tls-key") && v == null)) cfg.settings;
|
filteredSettings = lib.filterAttrs (
|
||||||
|
n: v: !((n == "tls-cert" || n == "tls-key") && v == null)
|
||||||
|
) cfg.settings;
|
||||||
in
|
in
|
||||||
"${pkgs.gonic}/bin/gonic -config-path ${settingsFormat.generate "gonic" filteredSettings}";
|
"${pkgs.gonic}/bin/gonic -config-path ${settingsFormat.generate "gonic" filteredSettings}";
|
||||||
DynamicUser = true;
|
DynamicUser = true;
|
||||||
|
@ -56,16 +63,22 @@ in
|
||||||
cfg.settings.playlists-path
|
cfg.settings.playlists-path
|
||||||
cfg.settings.podcast-path
|
cfg.settings.podcast-path
|
||||||
];
|
];
|
||||||
BindReadOnlyPaths = [
|
BindReadOnlyPaths =
|
||||||
|
[
|
||||||
# gonic can access scrobbling services
|
# gonic can access scrobbling services
|
||||||
"-/etc/resolv.conf"
|
"-/etc/resolv.conf"
|
||||||
"${config.security.pki.caBundle}:/etc/ssl/certs/ca-certificates.crt"
|
"${config.security.pki.caBundle}:/etc/ssl/certs/ca-certificates.crt"
|
||||||
builtins.storeDir
|
builtins.storeDir
|
||||||
] ++ cfg.settings.music-path
|
]
|
||||||
|
++ cfg.settings.music-path
|
||||||
++ lib.optional (cfg.settings.tls-cert != null) cfg.settings.tls-cert
|
++ lib.optional (cfg.settings.tls-cert != null) cfg.settings.tls-cert
|
||||||
++ lib.optional (cfg.settings.tls-key != null) cfg.settings.tls-key;
|
++ lib.optional (cfg.settings.tls-key != null) cfg.settings.tls-key;
|
||||||
CapabilityBoundingSet = "";
|
CapabilityBoundingSet = "";
|
||||||
RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ];
|
RestrictAddressFamilies = [
|
||||||
|
"AF_UNIX"
|
||||||
|
"AF_INET"
|
||||||
|
"AF_INET6"
|
||||||
|
];
|
||||||
RestrictNamespaces = true;
|
RestrictNamespaces = true;
|
||||||
PrivateDevices = true;
|
PrivateDevices = true;
|
||||||
PrivateUsers = true;
|
PrivateUsers = true;
|
||||||
|
@ -76,7 +89,10 @@ in
|
||||||
ProtectKernelModules = true;
|
ProtectKernelModules = true;
|
||||||
ProtectKernelTunables = true;
|
ProtectKernelTunables = true;
|
||||||
SystemCallArchitectures = "native";
|
SystemCallArchitectures = "native";
|
||||||
SystemCallFilter = [ "@system-service" "~@privileged" ];
|
SystemCallFilter = [
|
||||||
|
"@system-service"
|
||||||
|
"~@privileged"
|
||||||
|
];
|
||||||
RestrictRealtime = true;
|
RestrictRealtime = true;
|
||||||
LockPersonality = true;
|
LockPersonality = true;
|
||||||
MemoryDenyWriteExecute = true;
|
MemoryDenyWriteExecute = true;
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
{ config, lib, pkgs, ... }:
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
let
|
let
|
||||||
|
|
||||||
name = "mpd";
|
name = "mpd";
|
||||||
|
@ -7,13 +12,17 @@ let
|
||||||
gid = config.ids.gids.mpd;
|
gid = config.ids.gids.mpd;
|
||||||
cfg = config.services.mpd;
|
cfg = config.services.mpd;
|
||||||
|
|
||||||
credentialsPlaceholder = (creds:
|
credentialsPlaceholder = (
|
||||||
|
creds:
|
||||||
let
|
let
|
||||||
placeholders = (lib.imap0
|
placeholders = (
|
||||||
(i: c: ''password "{{password-${toString i}}}@${lib.concatStringsSep "," c.permissions}"'')
|
lib.imap0 (
|
||||||
creds);
|
i: c: ''password "{{password-${toString i}}}@${lib.concatStringsSep "," c.permissions}"''
|
||||||
|
) creds
|
||||||
|
);
|
||||||
in
|
in
|
||||||
lib.concatStringsSep "\n" placeholders);
|
lib.concatStringsSep "\n" placeholders
|
||||||
|
);
|
||||||
|
|
||||||
mpdConf = pkgs.writeText "mpd.conf" ''
|
mpdConf = pkgs.writeText "mpd.conf" ''
|
||||||
# This file was automatically generated by NixOS. Edit mpd's configuration
|
# This file was automatically generated by NixOS. Edit mpd's configuration
|
||||||
|
@ -28,7 +37,9 @@ let
|
||||||
state_file "${cfg.dataDir}/state"
|
state_file "${cfg.dataDir}/state"
|
||||||
sticker_file "${cfg.dataDir}/sticker.sql"
|
sticker_file "${cfg.dataDir}/sticker.sql"
|
||||||
|
|
||||||
${lib.optionalString (cfg.network.listenAddress != "any") ''bind_to_address "${cfg.network.listenAddress}"''}
|
${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.port != 6600) ''port "${toString cfg.network.port}"''}
|
||||||
${lib.optionalString (cfg.fluidsynth) ''
|
${lib.optionalString (cfg.fluidsynth) ''
|
||||||
decoder {
|
decoder {
|
||||||
|
@ -37,12 +48,13 @@ let
|
||||||
}
|
}
|
||||||
''}
|
''}
|
||||||
|
|
||||||
${lib.optionalString (cfg.credentials != []) (credentialsPlaceholder cfg.credentials)}
|
${lib.optionalString (cfg.credentials != [ ]) (credentialsPlaceholder cfg.credentials)}
|
||||||
|
|
||||||
${cfg.extraConfig}
|
${cfg.extraConfig}
|
||||||
'';
|
'';
|
||||||
|
|
||||||
in {
|
in
|
||||||
|
{
|
||||||
|
|
||||||
###### interface
|
###### interface
|
||||||
|
|
||||||
|
@ -160,7 +172,8 @@ in {
|
||||||
};
|
};
|
||||||
|
|
||||||
credentials = lib.mkOption {
|
credentials = lib.mkOption {
|
||||||
type = lib.types.listOf (lib.types.submodule {
|
type = lib.types.listOf (
|
||||||
|
lib.types.submodule {
|
||||||
options = {
|
options = {
|
||||||
passwordFile = lib.mkOption {
|
passwordFile = lib.mkOption {
|
||||||
type = lib.types.path;
|
type = lib.types.path;
|
||||||
|
@ -168,9 +181,16 @@ in {
|
||||||
Path to file containing the password.
|
Path to file containing the password.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
permissions = let
|
permissions =
|
||||||
perms = ["read" "add" "control" "admin"];
|
let
|
||||||
in lib.mkOption {
|
perms = [
|
||||||
|
"read"
|
||||||
|
"add"
|
||||||
|
"control"
|
||||||
|
"admin"
|
||||||
|
];
|
||||||
|
in
|
||||||
|
lib.mkOption {
|
||||||
type = lib.types.listOf (lib.types.enum perms);
|
type = lib.types.listOf (lib.types.enum perms);
|
||||||
default = [ "read" ];
|
default = [ "read" ];
|
||||||
description = ''
|
description = ''
|
||||||
|
@ -179,14 +199,26 @@ in {
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
});
|
}
|
||||||
|
);
|
||||||
description = ''
|
description = ''
|
||||||
Credentials and permissions for accessing the mpd server.
|
Credentials and permissions for accessing the mpd server.
|
||||||
'';
|
'';
|
||||||
default = [];
|
default = [ ];
|
||||||
example = [
|
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
|
###### implementation
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkIf cfg.enable {
|
||||||
|
@ -213,9 +244,14 @@ in {
|
||||||
wantedBy = [ "sockets.target" ];
|
wantedBy = [ "sockets.target" ];
|
||||||
listenStreams = [
|
listenStreams = [
|
||||||
"" # Note: this is needed to override the upstream unit
|
"" # Note: this is needed to override the upstream unit
|
||||||
(if pkgs.lib.hasPrefix "/" cfg.network.listenAddress
|
(
|
||||||
then cfg.network.listenAddress
|
if pkgs.lib.hasPrefix "/" cfg.network.listenAddress then
|
||||||
else "${lib.optionalString (cfg.network.listenAddress != "any") "${cfg.network.listenAddress}:"}${toString cfg.network.port}")
|
cfg.network.listenAddress
|
||||||
|
else
|
||||||
|
"${
|
||||||
|
lib.optionalString (cfg.network.listenAddress != "any") "${cfg.network.listenAddress}:"
|
||||||
|
}${toString cfg.network.port}"
|
||||||
|
)
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -226,22 +262,35 @@ in {
|
||||||
''
|
''
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
install -m 600 ${mpdConf} /run/mpd/mpd.conf
|
install -m 600 ${mpdConf} /run/mpd/mpd.conf
|
||||||
'' + lib.optionalString (cfg.credentials != [])
|
''
|
||||||
(lib.concatStringsSep "\n"
|
+ lib.optionalString (cfg.credentials != [ ]) (
|
||||||
(lib.imap0
|
lib.concatStringsSep "\n" (
|
||||||
(i: c: ''${pkgs.replace-secret}/bin/replace-secret '{{password-${toString i}}}' '${c.passwordFile}' /run/mpd/mpd.conf'')
|
lib.imap0 (
|
||||||
cfg.credentials));
|
i: c:
|
||||||
|
''${pkgs.replace-secret}/bin/replace-secret '{{password-${toString i}}}' '${c.passwordFile}' /run/mpd/mpd.conf''
|
||||||
|
) cfg.credentials
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
serviceConfig =
|
serviceConfig = {
|
||||||
{
|
|
||||||
User = "${cfg.user}";
|
User = "${cfg.user}";
|
||||||
# Note: the first "" overrides the ExecStart from the upstream unit
|
# Note: the first "" overrides the ExecStart from the upstream unit
|
||||||
ExecStart = [ "" "${pkgs.mpd}/bin/mpd --systemd /run/mpd/mpd.conf" ];
|
ExecStart = [
|
||||||
|
""
|
||||||
|
"${pkgs.mpd}/bin/mpd --systemd /run/mpd/mpd.conf"
|
||||||
|
];
|
||||||
RuntimeDirectory = "mpd";
|
RuntimeDirectory = "mpd";
|
||||||
StateDirectory = []
|
StateDirectory =
|
||||||
|
[ ]
|
||||||
++ lib.optionals (cfg.dataDir == "/var/lib/${name}") [ name ]
|
++ lib.optionals (cfg.dataDir == "/var/lib/${name}") [ name ]
|
||||||
++ lib.optionals (cfg.playlistDirectory == "/var/lib/${name}/playlists") [ name "${name}/playlists" ]
|
++ lib.optionals (cfg.playlistDirectory == "/var/lib/${name}/playlists") [
|
||||||
++ lib.optionals (cfg.musicDirectory == "/var/lib/${name}/music") [ name "${name}/music" ];
|
name
|
||||||
|
"${name}/playlists"
|
||||||
|
]
|
||||||
|
++ lib.optionals (cfg.musicDirectory == "/var/lib/${name}/music") [
|
||||||
|
name
|
||||||
|
"${name}/music"
|
||||||
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,34 +1,56 @@
|
||||||
{ config, lib, pkgs, ... }:
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
let
|
let
|
||||||
|
|
||||||
isLocalPath = x:
|
isLocalPath =
|
||||||
|
x:
|
||||||
builtins.substring 0 1 x == "/" # absolute path
|
builtins.substring 0 1 x == "/" # absolute path
|
||||||
|| builtins.substring 0 1 x == "." # relative path
|
|| builtins.substring 0 1 x == "." # relative path
|
||||||
|| builtins.match "[.*:.*]" == null; # not machine:path
|
|| builtins.match "[.*:.*]" == null; # not machine:path
|
||||||
|
|
||||||
mkExcludeFile = cfg:
|
mkExcludeFile =
|
||||||
|
cfg:
|
||||||
# Write each exclude pattern to a new line
|
# Write each exclude pattern to a new line
|
||||||
pkgs.writeText "excludefile" (lib.concatMapStrings (s: s + "\n") cfg.exclude);
|
pkgs.writeText "excludefile" (lib.concatMapStrings (s: s + "\n") cfg.exclude);
|
||||||
|
|
||||||
mkPatternsFile = cfg:
|
mkPatternsFile =
|
||||||
|
cfg:
|
||||||
# Write each pattern to a new line
|
# Write each pattern to a new line
|
||||||
pkgs.writeText "patternsfile" (lib.concatMapStrings (s: s + "\n") cfg.patterns);
|
pkgs.writeText "patternsfile" (lib.concatMapStrings (s: s + "\n") cfg.patterns);
|
||||||
|
|
||||||
mkKeepArgs = cfg:
|
mkKeepArgs =
|
||||||
|
cfg:
|
||||||
# If cfg.prune.keep e.g. has a yearly attribute,
|
# If cfg.prune.keep e.g. has a yearly attribute,
|
||||||
# its content is passed on as --keep-yearly
|
# its content is passed on as --keep-yearly
|
||||||
lib.concatStringsSep " "
|
lib.concatStringsSep " " (lib.mapAttrsToList (x: y: "--keep-${x}=${toString y}") cfg.prune.keep);
|
||||||
(lib.mapAttrsToList (x: y: "--keep-${x}=${toString y}") cfg.prune.keep);
|
|
||||||
|
|
||||||
mkExtraArgs = cfg:
|
mkExtraArgs =
|
||||||
|
cfg:
|
||||||
# Create BASH arrays of extra args
|
# Create BASH arrays of extra args
|
||||||
lib.concatLines
|
lib.concatLines (
|
||||||
(lib.mapAttrsToList (name: values: ''
|
lib.mapAttrsToList
|
||||||
|
(name: values: ''
|
||||||
${name}=(${values})
|
${name}=(${values})
|
||||||
'')
|
'')
|
||||||
{ inherit (cfg) extraArgs extraInitArgs extraCreateArgs extraPruneArgs extraCompactArgs; });
|
{
|
||||||
|
inherit (cfg)
|
||||||
|
extraArgs
|
||||||
|
extraInitArgs
|
||||||
|
extraCreateArgs
|
||||||
|
extraPruneArgs
|
||||||
|
extraCompactArgs
|
||||||
|
;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
mkBackupScript = name: cfg: pkgs.writeShellScript "${name}-script" (''
|
mkBackupScript =
|
||||||
|
name: cfg:
|
||||||
|
pkgs.writeShellScript "${name}-script" (
|
||||||
|
''
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
${mkExtraArgs cfg}
|
${mkExtraArgs cfg}
|
||||||
|
@ -52,10 +74,13 @@ let
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
archiveName="${lib.optionalString (cfg.archiveBaseName != null) (cfg.archiveBaseName + "-")}$(date ${cfg.dateFormat})"
|
archiveName="${
|
||||||
|
lib.optionalString (cfg.archiveBaseName != null) (cfg.archiveBaseName + "-")
|
||||||
|
}$(date ${cfg.dateFormat})"
|
||||||
archiveSuffix="${lib.optionalString cfg.appendFailedSuffix ".failed"}"
|
archiveSuffix="${lib.optionalString cfg.appendFailedSuffix ".failed"}"
|
||||||
${cfg.preHook}
|
${cfg.preHook}
|
||||||
'' + lib.optionalString cfg.doInit ''
|
''
|
||||||
|
+ lib.optionalString cfg.doInit ''
|
||||||
# Run borg init if the repo doesn't exist yet
|
# Run borg init if the repo doesn't exist yet
|
||||||
if ! borgWrapper list "''${extraArgs[@]}" > /dev/null; then
|
if ! borgWrapper list "''${extraArgs[@]}" > /dev/null; then
|
||||||
borgWrapper init "''${extraArgs[@]}" \
|
borgWrapper init "''${extraArgs[@]}" \
|
||||||
|
@ -63,7 +88,8 @@ let
|
||||||
"''${extraInitArgs[@]}"
|
"''${extraInitArgs[@]}"
|
||||||
${cfg.postInit}
|
${cfg.postInit}
|
||||||
fi
|
fi
|
||||||
'' + ''
|
''
|
||||||
|
+ ''
|
||||||
(
|
(
|
||||||
set -o pipefail
|
set -o pipefail
|
||||||
${lib.optionalString (cfg.dumpCommand != null) ''${lib.escapeShellArg cfg.dumpCommand} | \''}
|
${lib.optionalString (cfg.dumpCommand != null) ''${lib.escapeShellArg cfg.dumpCommand} | \''}
|
||||||
|
@ -75,43 +101,60 @@ let
|
||||||
"::$archiveName$archiveSuffix" \
|
"::$archiveName$archiveSuffix" \
|
||||||
${if cfg.paths == null then "-" else lib.escapeShellArgs cfg.paths}
|
${if cfg.paths == null then "-" else lib.escapeShellArgs cfg.paths}
|
||||||
)
|
)
|
||||||
'' + lib.optionalString cfg.appendFailedSuffix ''
|
''
|
||||||
|
+ lib.optionalString cfg.appendFailedSuffix ''
|
||||||
borgWrapper rename "''${extraArgs[@]}" \
|
borgWrapper rename "''${extraArgs[@]}" \
|
||||||
"::$archiveName$archiveSuffix" "$archiveName"
|
"::$archiveName$archiveSuffix" "$archiveName"
|
||||||
'' + ''
|
''
|
||||||
|
+ ''
|
||||||
${cfg.postCreate}
|
${cfg.postCreate}
|
||||||
'' + lib.optionalString (cfg.prune.keep != { }) ''
|
''
|
||||||
|
+ lib.optionalString (cfg.prune.keep != { }) ''
|
||||||
borgWrapper prune "''${extraArgs[@]}" \
|
borgWrapper prune "''${extraArgs[@]}" \
|
||||||
${mkKeepArgs cfg} \
|
${mkKeepArgs cfg} \
|
||||||
${lib.optionalString (cfg.prune.prefix != null) "--glob-archives ${lib.escapeShellArg "${cfg.prune.prefix}*"}"} \
|
${
|
||||||
|
lib.optionalString (
|
||||||
|
cfg.prune.prefix != null
|
||||||
|
) "--glob-archives ${lib.escapeShellArg "${cfg.prune.prefix}*"}"
|
||||||
|
} \
|
||||||
"''${extraPruneArgs[@]}"
|
"''${extraPruneArgs[@]}"
|
||||||
borgWrapper compact "''${extraArgs[@]}" "''${extraCompactArgs[@]}"
|
borgWrapper compact "''${extraArgs[@]}" "''${extraCompactArgs[@]}"
|
||||||
${cfg.postPrune}
|
${cfg.postPrune}
|
||||||
'');
|
''
|
||||||
|
);
|
||||||
|
|
||||||
mkPassEnv = cfg: with cfg.encryption;
|
mkPassEnv =
|
||||||
|
cfg:
|
||||||
|
with cfg.encryption;
|
||||||
if passCommand != null then
|
if passCommand != null then
|
||||||
{ BORG_PASSCOMMAND = passCommand; }
|
{ BORG_PASSCOMMAND = passCommand; }
|
||||||
else if passphrase != null then
|
else if passphrase != null then
|
||||||
{ BORG_PASSPHRASE = passphrase; }
|
{ BORG_PASSPHRASE = passphrase; }
|
||||||
else { };
|
else
|
||||||
|
{ };
|
||||||
|
|
||||||
mkBackupService = name: cfg:
|
mkBackupService =
|
||||||
|
name: cfg:
|
||||||
let
|
let
|
||||||
userHome = config.users.users.${cfg.user}.home;
|
userHome = config.users.users.${cfg.user}.home;
|
||||||
backupJobName = "borgbackup-job-${name}";
|
backupJobName = "borgbackup-job-${name}";
|
||||||
backupScript = mkBackupScript backupJobName cfg;
|
backupScript = mkBackupScript backupJobName cfg;
|
||||||
in lib.nameValuePair backupJobName {
|
in
|
||||||
|
lib.nameValuePair backupJobName {
|
||||||
description = "BorgBackup job ${name}";
|
description = "BorgBackup job ${name}";
|
||||||
path = [
|
path = [
|
||||||
config.services.borgbackup.package pkgs.openssh
|
config.services.borgbackup.package
|
||||||
|
pkgs.openssh
|
||||||
];
|
];
|
||||||
script = "exec " + lib.optionalString cfg.inhibitsSleep ''
|
script =
|
||||||
|
"exec "
|
||||||
|
+ lib.optionalString cfg.inhibitsSleep ''
|
||||||
${pkgs.systemd}/bin/systemd-inhibit \
|
${pkgs.systemd}/bin/systemd-inhibit \
|
||||||
--who="borgbackup" \
|
--who="borgbackup" \
|
||||||
--what="sleep" \
|
--what="sleep" \
|
||||||
--why="Scheduled backup" \
|
--why="Scheduled backup" \
|
||||||
'' + backupScript;
|
''
|
||||||
|
+ backupScript;
|
||||||
unitConfig = lib.optionalAttrs (isLocalPath cfg.repo) {
|
unitConfig = lib.optionalAttrs (isLocalPath cfg.repo) {
|
||||||
RequiresMountsFor = [ cfg.repo ];
|
RequiresMountsFor = [ cfg.repo ];
|
||||||
};
|
};
|
||||||
|
@ -123,18 +166,25 @@ let
|
||||||
IOSchedulingClass = "idle";
|
IOSchedulingClass = "idle";
|
||||||
ProtectSystem = "strict";
|
ProtectSystem = "strict";
|
||||||
ReadWritePaths =
|
ReadWritePaths =
|
||||||
[ "${userHome}/.config/borg" "${userHome}/.cache/borg" ]
|
[
|
||||||
|
"${userHome}/.config/borg"
|
||||||
|
"${userHome}/.cache/borg"
|
||||||
|
]
|
||||||
++ cfg.readWritePaths
|
++ cfg.readWritePaths
|
||||||
# Borg needs write access to repo if it is not remote
|
# Borg needs write access to repo if it is not remote
|
||||||
++ lib.optional (isLocalPath cfg.repo) cfg.repo;
|
++ lib.optional (isLocalPath cfg.repo) cfg.repo;
|
||||||
PrivateTmp = cfg.privateTmp;
|
PrivateTmp = cfg.privateTmp;
|
||||||
};
|
};
|
||||||
environment = {
|
environment =
|
||||||
|
{
|
||||||
BORG_REPO = cfg.repo;
|
BORG_REPO = cfg.repo;
|
||||||
} // (mkPassEnv cfg) // cfg.environment;
|
}
|
||||||
|
// (mkPassEnv cfg)
|
||||||
|
// cfg.environment;
|
||||||
};
|
};
|
||||||
|
|
||||||
mkBackupTimers = name: cfg:
|
mkBackupTimers =
|
||||||
|
name: cfg:
|
||||||
lib.nameValuePair "borgbackup-job-${name}" {
|
lib.nameValuePair "borgbackup-job-${name}" {
|
||||||
description = "BorgBackup job ${name} timer";
|
description = "BorgBackup job ${name} timer";
|
||||||
wantedBy = [ "timers.target" ];
|
wantedBy = [ "timers.target" ];
|
||||||
|
@ -148,45 +198,62 @@ let
|
||||||
};
|
};
|
||||||
|
|
||||||
# utility function around makeWrapper
|
# utility function around makeWrapper
|
||||||
mkWrapperDrv = {
|
mkWrapperDrv =
|
||||||
original, name, set ? {}
|
{
|
||||||
|
original,
|
||||||
|
name,
|
||||||
|
set ? { },
|
||||||
}:
|
}:
|
||||||
pkgs.runCommand "${name}-wrapper" {
|
pkgs.runCommand "${name}-wrapper"
|
||||||
|
{
|
||||||
nativeBuildInputs = [ pkgs.makeWrapper ];
|
nativeBuildInputs = [ pkgs.makeWrapper ];
|
||||||
} (with lib; ''
|
}
|
||||||
|
(
|
||||||
|
with lib;
|
||||||
|
''
|
||||||
makeWrapper "${original}" "$out/bin/${name}" \
|
makeWrapper "${original}" "$out/bin/${name}" \
|
||||||
${lib.concatStringsSep " \\\n " (lib.mapAttrsToList (name: value: ''--set ${name} "${value}"'') set)}
|
${lib.concatStringsSep " \\\n " (
|
||||||
'');
|
lib.mapAttrsToList (name: value: ''--set ${name} "${value}"'') set
|
||||||
|
)}
|
||||||
|
''
|
||||||
|
);
|
||||||
|
|
||||||
mkBorgWrapper = name: cfg: mkWrapperDrv {
|
mkBorgWrapper =
|
||||||
|
name: cfg:
|
||||||
|
mkWrapperDrv {
|
||||||
original = lib.getExe config.services.borgbackup.package;
|
original = lib.getExe config.services.borgbackup.package;
|
||||||
name = "borg-job-${name}";
|
name = "borg-job-${name}";
|
||||||
set = { BORG_REPO = cfg.repo; } // (mkPassEnv cfg) // cfg.environment;
|
set = { BORG_REPO = cfg.repo; } // (mkPassEnv cfg) // cfg.environment;
|
||||||
};
|
};
|
||||||
|
|
||||||
# Paths listed in ReadWritePaths must exist before service is started
|
# Paths listed in ReadWritePaths must exist before service is started
|
||||||
mkTmpfiles = name: cfg:
|
mkTmpfiles =
|
||||||
|
name: cfg:
|
||||||
let
|
let
|
||||||
settings = { inherit (cfg) user group; };
|
settings = { inherit (cfg) user group; };
|
||||||
in lib.nameValuePair "borgbackup-job-${name}" ({
|
in
|
||||||
|
lib.nameValuePair "borgbackup-job-${name}" (
|
||||||
|
{
|
||||||
# Create parent dirs separately, to ensure correct ownership.
|
# Create parent dirs separately, to ensure correct ownership.
|
||||||
"${config.users.users."${cfg.user}".home}/.config".d = settings;
|
"${config.users.users."${cfg.user}".home}/.config".d = settings;
|
||||||
"${config.users.users."${cfg.user}".home}/.cache".d = settings;
|
"${config.users.users."${cfg.user}".home}/.cache".d = settings;
|
||||||
"${config.users.users."${cfg.user}".home}/.config/borg".d = settings;
|
"${config.users.users."${cfg.user}".home}/.config/borg".d = settings;
|
||||||
"${config.users.users."${cfg.user}".home}/.cache/borg".d = settings;
|
"${config.users.users."${cfg.user}".home}/.cache/borg".d = settings;
|
||||||
} // lib.optionalAttrs (isLocalPath cfg.repo && !cfg.removableDevice) {
|
}
|
||||||
|
// lib.optionalAttrs (isLocalPath cfg.repo && !cfg.removableDevice) {
|
||||||
"${cfg.repo}".d = settings;
|
"${cfg.repo}".d = settings;
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
mkPassAssertion = name: cfg: {
|
mkPassAssertion = name: cfg: {
|
||||||
assertion = with cfg.encryption;
|
assertion = with cfg.encryption; mode != "none" -> passCommand != null || passphrase != null;
|
||||||
mode != "none" -> passCommand != null || passphrase != null;
|
|
||||||
message =
|
message =
|
||||||
"passCommand or passphrase has to be specified because"
|
"passCommand or passphrase has to be specified because"
|
||||||
+ " borgbackup.jobs.${name}.encryption != \"none\"";
|
+ " borgbackup.jobs.${name}.encryption != \"none\"";
|
||||||
};
|
};
|
||||||
|
|
||||||
mkRepoService = name: cfg:
|
mkRepoService =
|
||||||
|
name: cfg:
|
||||||
lib.nameValuePair "borgbackup-repo-${name}" {
|
lib.nameValuePair "borgbackup-repo-${name}" {
|
||||||
description = "Create BorgBackup repository ${name} directory";
|
description = "Create BorgBackup repository ${name} directory";
|
||||||
script = ''
|
script = ''
|
||||||
|
@ -200,7 +267,8 @@ let
|
||||||
wantedBy = [ "multi-user.target" ];
|
wantedBy = [ "multi-user.target" ];
|
||||||
};
|
};
|
||||||
|
|
||||||
mkAuthorizedKey = cfg: appendOnly: key:
|
mkAuthorizedKey =
|
||||||
|
cfg: appendOnly: key:
|
||||||
let
|
let
|
||||||
# Because of the following line, clients do not need to specify an absolute repo path
|
# Because of the following line, clients do not need to specify an absolute repo path
|
||||||
cdCommand = "cd ${lib.escapeShellArg cfg.path}";
|
cdCommand = "cd ${lib.escapeShellArg cfg.path}";
|
||||||
|
@ -213,9 +281,10 @@ let
|
||||||
|
|
||||||
mkUsersConfig = name: cfg: {
|
mkUsersConfig = name: cfg: {
|
||||||
users.${cfg.user} = {
|
users.${cfg.user} = {
|
||||||
openssh.authorizedKeys.keys =
|
openssh.authorizedKeys.keys = (
|
||||||
(map (mkAuthorizedKey cfg false) cfg.authorizedKeys
|
map (mkAuthorizedKey cfg false) cfg.authorizedKeys
|
||||||
++ map (mkAuthorizedKey cfg true) cfg.authorizedKeysAppendOnly);
|
++ map (mkAuthorizedKey cfg true) cfg.authorizedKeysAppendOnly
|
||||||
|
);
|
||||||
useDefaultShell = true;
|
useDefaultShell = true;
|
||||||
group = cfg.group;
|
group = cfg.group;
|
||||||
isSystemUser = true;
|
isSystemUser = true;
|
||||||
|
@ -225,13 +294,15 @@ let
|
||||||
|
|
||||||
mkKeysAssertion = name: cfg: {
|
mkKeysAssertion = name: cfg: {
|
||||||
assertion = cfg.authorizedKeys != [ ] || cfg.authorizedKeysAppendOnly != [ ];
|
assertion = cfg.authorizedKeys != [ ] || cfg.authorizedKeysAppendOnly != [ ];
|
||||||
message =
|
message = "borgbackup.repos.${name} does not make sense" + " without at least one public key";
|
||||||
"borgbackup.repos.${name} does not make sense"
|
|
||||||
+ " without at least one public key";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
mkSourceAssertions = name: cfg: {
|
mkSourceAssertions = name: cfg: {
|
||||||
assertion = lib.count isNull [ cfg.dumpCommand cfg.paths ] == 1;
|
assertion =
|
||||||
|
lib.count isNull [
|
||||||
|
cfg.dumpCommand
|
||||||
|
cfg.paths
|
||||||
|
] == 1;
|
||||||
message = ''
|
message = ''
|
||||||
Exactly one of borgbackup.jobs.${name}.paths or borgbackup.jobs.${name}.dumpCommand
|
Exactly one of borgbackup.jobs.${name}.paths or borgbackup.jobs.${name}.dumpCommand
|
||||||
must be set.
|
must be set.
|
||||||
|
@ -245,8 +316,12 @@ let
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
in {
|
in
|
||||||
meta.maintainers = with lib.maintainers; [ dotlambda Scrumplex ];
|
{
|
||||||
|
meta.maintainers = with lib.maintainers; [
|
||||||
|
dotlambda
|
||||||
|
Scrumplex
|
||||||
|
];
|
||||||
meta.doc = ./borgbackup.md;
|
meta.doc = ./borgbackup.md;
|
||||||
|
|
||||||
###### interface
|
###### interface
|
||||||
|
@ -293,8 +368,13 @@ in {
|
||||||
startAt = "daily";
|
startAt = "daily";
|
||||||
};
|
};
|
||||||
'';
|
'';
|
||||||
type = lib.types.attrsOf (lib.types.submodule (let globalConfig = config; in
|
type = lib.types.attrsOf (
|
||||||
{ name, config, ... }: {
|
lib.types.submodule (
|
||||||
|
let
|
||||||
|
globalConfig = config;
|
||||||
|
in
|
||||||
|
{ name, config, ... }:
|
||||||
|
{
|
||||||
options = {
|
options = {
|
||||||
|
|
||||||
paths = lib.mkOption {
|
paths = lib.mkOption {
|
||||||
|
@ -408,9 +488,12 @@ in {
|
||||||
|
|
||||||
encryption.mode = lib.mkOption {
|
encryption.mode = lib.mkOption {
|
||||||
type = lib.types.enum [
|
type = lib.types.enum [
|
||||||
"repokey" "keyfile"
|
"repokey"
|
||||||
"repokey-blake2" "keyfile-blake2"
|
"keyfile"
|
||||||
"authenticated" "authenticated-blake2"
|
"repokey-blake2"
|
||||||
|
"keyfile-blake2"
|
||||||
|
"authenticated"
|
||||||
|
"authenticated-blake2"
|
||||||
"none"
|
"none"
|
||||||
];
|
];
|
||||||
description = ''
|
description = ''
|
||||||
|
@ -579,7 +662,9 @@ in {
|
||||||
You can for example specify which SSH key to use.
|
You can for example specify which SSH key to use.
|
||||||
'';
|
'';
|
||||||
default = { };
|
default = { };
|
||||||
example = { BORG_RSH = "ssh -i /path/to/key"; };
|
example = {
|
||||||
|
BORG_RSH = "ssh -i /path/to/key";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
preHook = lib.mkOption {
|
preHook = lib.mkOption {
|
||||||
|
@ -684,7 +769,8 @@ in {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
));
|
)
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
options.services.borgbackup.repos = lib.mkOption {
|
options.services.borgbackup.repos = lib.mkOption {
|
||||||
|
@ -696,8 +782,10 @@ in {
|
||||||
i.e. `user@machine:.` is enough. (Note colon and dot.)
|
i.e. `user@machine:.` is enough. (Note colon and dot.)
|
||||||
'';
|
'';
|
||||||
default = { };
|
default = { };
|
||||||
type = lib.types.attrsOf (lib.types.submodule (
|
type = lib.types.attrsOf (
|
||||||
{ ... }: {
|
lib.types.submodule (
|
||||||
|
{ ... }:
|
||||||
|
{
|
||||||
options = {
|
options = {
|
||||||
path = lib.mkOption {
|
path = lib.mkOption {
|
||||||
type = lib.types.path;
|
type = lib.types.path;
|
||||||
|
@ -776,13 +864,15 @@ in {
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
));
|
)
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
###### implementation
|
###### implementation
|
||||||
|
|
||||||
config = lib.mkIf (with config.services.borgbackup; jobs != { } || repos != { })
|
config = lib.mkIf (with config.services.borgbackup; jobs != { } || repos != { }) (
|
||||||
(with config.services.borgbackup; {
|
with config.services.borgbackup;
|
||||||
|
{
|
||||||
assertions =
|
assertions =
|
||||||
lib.mapAttrsToList mkPassAssertion jobs
|
lib.mapAttrsToList mkPassAssertion jobs
|
||||||
++ lib.mapAttrsToList mkKeysAssertion repos
|
++ lib.mapAttrsToList mkKeysAssertion repos
|
||||||
|
@ -799,11 +889,13 @@ in {
|
||||||
|
|
||||||
# A job named "foo" is mapped to systemd.timers.borgbackup-job-foo
|
# A job named "foo" is mapped to systemd.timers.borgbackup-job-foo
|
||||||
# only generate the timer if interval (startAt) is set
|
# only generate the timer if interval (startAt) is set
|
||||||
systemd.timers = lib.mapAttrs' mkBackupTimers (lib.filterAttrs (_: cfg: cfg.startAt != []) jobs);
|
systemd.timers = lib.mapAttrs' mkBackupTimers (lib.filterAttrs (_: cfg: cfg.startAt != [ ]) jobs);
|
||||||
|
|
||||||
users = lib.mkMerge (lib.mapAttrsToList mkUsersConfig repos);
|
users = lib.mkMerge (lib.mapAttrsToList mkUsersConfig repos);
|
||||||
|
|
||||||
environment.systemPackages =
|
environment.systemPackages = [
|
||||||
[ config.services.borgbackup.package ] ++ (lib.mapAttrsToList mkBorgWrapper jobs);
|
config.services.borgbackup.package
|
||||||
});
|
] ++ (lib.mapAttrsToList mkBorgWrapper jobs);
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,23 @@
|
||||||
{ config, lib, pkgs, ... }:
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
let
|
let
|
||||||
cfg = config.services.sanoid;
|
cfg = config.services.sanoid;
|
||||||
|
|
||||||
datasetSettingsType = with lib.types;
|
datasetSettingsType =
|
||||||
(attrsOf (nullOr (oneOf [ str int bool (listOf str) ]))) // {
|
with lib.types;
|
||||||
|
(attrsOf (
|
||||||
|
nullOr (oneOf [
|
||||||
|
str
|
||||||
|
int
|
||||||
|
bool
|
||||||
|
(listOf str)
|
||||||
|
])
|
||||||
|
))
|
||||||
|
// {
|
||||||
description = "dataset/template options";
|
description = "dataset/template options";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -48,10 +62,13 @@ let
|
||||||
datasetOptions = rec {
|
datasetOptions = rec {
|
||||||
use_template = lib.mkOption {
|
use_template = lib.mkOption {
|
||||||
description = "Names of the templates to use for this dataset.";
|
description = "Names of the templates to use for this dataset.";
|
||||||
type = lib.types.listOf (lib.types.str // {
|
type = lib.types.listOf (
|
||||||
|
lib.types.str
|
||||||
|
// {
|
||||||
check = (lib.types.enum (lib.attrNames cfg.templates)).check;
|
check = (lib.types.enum (lib.attrNames cfg.templates)).check;
|
||||||
description = "configured template name";
|
description = "configured template name";
|
||||||
});
|
}
|
||||||
|
);
|
||||||
default = [ ];
|
default = [ ];
|
||||||
};
|
};
|
||||||
useTemplate = use_template;
|
useTemplate = use_template;
|
||||||
|
@ -63,7 +80,12 @@ let
|
||||||
recursively in an atomic way without the possibility to
|
recursively in an atomic way without the possibility to
|
||||||
override settings for child datasets.
|
override settings for child datasets.
|
||||||
'';
|
'';
|
||||||
type = with lib.types; oneOf [ bool (enum [ "zfs" ]) ];
|
type =
|
||||||
|
with lib.types;
|
||||||
|
oneOf [
|
||||||
|
bool
|
||||||
|
(enum [ "zfs" ])
|
||||||
|
];
|
||||||
default = false;
|
default = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -80,7 +102,9 @@ let
|
||||||
|
|
||||||
# Function to build "zfs allow" and "zfs unallow" commands for the
|
# Function to build "zfs allow" and "zfs unallow" commands for the
|
||||||
# filesystems we've delegated permissions to.
|
# filesystems we've delegated permissions to.
|
||||||
buildAllowCommand = zfsAction: permissions: dataset: lib.escapeShellArgs [
|
buildAllowCommand =
|
||||||
|
zfsAction: permissions: dataset:
|
||||||
|
lib.escapeShellArgs [
|
||||||
# Here we explicitly use the booted system to guarantee the stable API needed by ZFS
|
# Here we explicitly use the booted system to guarantee the stable API needed by ZFS
|
||||||
"-+/run/booted-system/sw/bin/zfs"
|
"-+/run/booted-system/sw/bin/zfs"
|
||||||
zfsAction
|
zfsAction
|
||||||
|
@ -91,15 +115,19 @@ let
|
||||||
|
|
||||||
configFile =
|
configFile =
|
||||||
let
|
let
|
||||||
mkValueString = v:
|
mkValueString =
|
||||||
if lib.isList v then lib.concatStringsSep "," v
|
v: if lib.isList v then lib.concatStringsSep "," v else lib.generators.mkValueStringDefault { } v;
|
||||||
else lib.generators.mkValueStringDefault { } v;
|
|
||||||
|
|
||||||
mkKeyValue = k: v:
|
mkKeyValue =
|
||||||
if v == null then ""
|
k: v:
|
||||||
else if k == "processChildrenOnly" then ""
|
if v == null then
|
||||||
else if k == "useTemplate" then ""
|
""
|
||||||
else lib.generators.mkKeyValueDefault { inherit mkValueString; } "=" k v;
|
else if k == "processChildrenOnly" then
|
||||||
|
""
|
||||||
|
else if k == "useTemplate" then
|
||||||
|
""
|
||||||
|
else
|
||||||
|
lib.generators.mkKeyValueDefault { inherit mkValueString; } "=" k v;
|
||||||
in
|
in
|
||||||
lib.generators.toINI { inherit mkKeyValue; } cfg.settings;
|
lib.generators.toINI { inherit mkKeyValue; } cfg.settings;
|
||||||
|
|
||||||
|
@ -111,7 +139,7 @@ in
|
||||||
options.services.sanoid = {
|
options.services.sanoid = {
|
||||||
enable = lib.mkEnableOption "Sanoid ZFS snapshotting service";
|
enable = lib.mkEnableOption "Sanoid ZFS snapshotting service";
|
||||||
|
|
||||||
package = lib.mkPackageOption pkgs "sanoid" {};
|
package = lib.mkPackageOption pkgs "sanoid" { };
|
||||||
|
|
||||||
interval = lib.mkOption {
|
interval = lib.mkOption {
|
||||||
type = lib.types.str;
|
type = lib.types.str;
|
||||||
|
@ -126,21 +154,32 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
datasets = lib.mkOption {
|
datasets = lib.mkOption {
|
||||||
type = lib.types.attrsOf (lib.types.submodule ({ config, options, ... }: {
|
type = lib.types.attrsOf (
|
||||||
|
lib.types.submodule (
|
||||||
|
{ config, options, ... }:
|
||||||
|
{
|
||||||
freeformType = datasetSettingsType;
|
freeformType = datasetSettingsType;
|
||||||
options = commonOptions // datasetOptions;
|
options = commonOptions // datasetOptions;
|
||||||
config.use_template = lib.modules.mkAliasAndWrapDefsWithPriority lib.id (options.useTemplate or { });
|
config.use_template = lib.modules.mkAliasAndWrapDefsWithPriority lib.id (
|
||||||
config.process_children_only = lib.modules.mkAliasAndWrapDefsWithPriority lib.id (options.processChildrenOnly or { });
|
options.useTemplate or { }
|
||||||
}));
|
);
|
||||||
|
config.process_children_only = lib.modules.mkAliasAndWrapDefsWithPriority lib.id (
|
||||||
|
options.processChildrenOnly or { }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
default = { };
|
default = { };
|
||||||
description = "Datasets to snapshot.";
|
description = "Datasets to snapshot.";
|
||||||
};
|
};
|
||||||
|
|
||||||
templates = lib.mkOption {
|
templates = lib.mkOption {
|
||||||
type = lib.types.attrsOf (lib.types.submodule {
|
type = lib.types.attrsOf (
|
||||||
|
lib.types.submodule {
|
||||||
freeformType = datasetSettingsType;
|
freeformType = datasetSettingsType;
|
||||||
options = commonOptions;
|
options = commonOptions;
|
||||||
});
|
}
|
||||||
|
);
|
||||||
default = { };
|
default = { };
|
||||||
description = "Templates for datasets.";
|
description = "Templates for datasets.";
|
||||||
};
|
};
|
||||||
|
@ -157,7 +196,11 @@ in
|
||||||
extraArgs = lib.mkOption {
|
extraArgs = lib.mkOption {
|
||||||
type = lib.types.listOf lib.types.str;
|
type = lib.types.listOf lib.types.str;
|
||||||
default = [ ];
|
default = [ ];
|
||||||
example = [ "--verbose" "--readonly" "--debug" ];
|
example = [
|
||||||
|
"--verbose"
|
||||||
|
"--readonly"
|
||||||
|
"--debug"
|
||||||
|
];
|
||||||
description = ''
|
description = ''
|
||||||
Extra arguments to pass to sanoid. See
|
Extra arguments to pass to sanoid. See
|
||||||
<https://github.com/jimsalterjrs/sanoid/#sanoid-command-line-options>
|
<https://github.com/jimsalterjrs/sanoid/#sanoid-command-line-options>
|
||||||
|
@ -177,14 +220,29 @@ in
|
||||||
systemd.services.sanoid = {
|
systemd.services.sanoid = {
|
||||||
description = "Sanoid snapshot service";
|
description = "Sanoid snapshot service";
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
ExecStartPre = (map (buildAllowCommand "allow" [ "snapshot" "mount" "destroy" ]) datasets);
|
ExecStartPre = (
|
||||||
ExecStopPost = (map (buildAllowCommand "unallow" [ "snapshot" "mount" "destroy" ]) datasets);
|
map (buildAllowCommand "allow" [
|
||||||
ExecStart = lib.escapeShellArgs ([
|
"snapshot"
|
||||||
|
"mount"
|
||||||
|
"destroy"
|
||||||
|
]) datasets
|
||||||
|
);
|
||||||
|
ExecStopPost = (
|
||||||
|
map (buildAllowCommand "unallow" [
|
||||||
|
"snapshot"
|
||||||
|
"mount"
|
||||||
|
"destroy"
|
||||||
|
]) datasets
|
||||||
|
);
|
||||||
|
ExecStart = lib.escapeShellArgs (
|
||||||
|
[
|
||||||
"${cfg.package}/bin/sanoid"
|
"${cfg.package}/bin/sanoid"
|
||||||
"--cron"
|
"--cron"
|
||||||
"--configdir"
|
"--configdir"
|
||||||
(pkgs.writeTextDir "sanoid.conf" configFile)
|
(pkgs.writeTextDir "sanoid.conf" configFile)
|
||||||
] ++ cfg.extraArgs);
|
]
|
||||||
|
++ cfg.extraArgs
|
||||||
|
);
|
||||||
User = "sanoid";
|
User = "sanoid";
|
||||||
Group = "sanoid";
|
Group = "sanoid";
|
||||||
DynamicUser = true;
|
DynamicUser = true;
|
||||||
|
|
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