From dc163054691d4d7d08567c48505b840abe6ed514 Mon Sep 17 00:00:00 2001 From: tcmal Date: Thu, 16 May 2024 15:37:36 +0100 Subject: [PATCH 1/4] fix dependency on nixpkgs/core testing and documentation is now in `extra/`, and uses npins rather than flakes --- README.md | 13 +- extra/default.nix | 62 + {doc => extra/doc}/default.nix | 23 +- {doc => extra/doc}/sections.nix | 38 +- extra/npins/default.nix | 47 + extra/npins/sources.json | 11 + extra/tests/auxlib/default.nix | 23 + extra/tests/auxlib/test-with-nix.nix | 18 + extra/tests/default.nix | 13 + extra/tests/formatting.nix | 5 + {tests => extra/tests}/nix-for-tests.nix | 0 {tests => extra/tests}/stdlib/README.md | 0 {tests => extra/tests}/stdlib/check-eval.nix | 0 extra/tests/stdlib/default.nix | 32 + {tests => extra/tests}/stdlib/fileset.sh | 0 {tests => extra/tests}/stdlib/filesystem.sh | 0 {tests => extra/tests}/stdlib/misc.nix | 0 {tests => extra/tests}/stdlib/modules.sh | 4 +- ...adhoc-freeformType-survives-type-merge.nix | 0 .../alias-with-priority-can-override.nix | 0 .../stdlib/modules/alias-with-priority.nix | 0 .../modules/attrsOf-conditional-check.nix | 0 .../stdlib/modules/attrsOf-lazy-check.nix | 0 .../tests}/stdlib/modules/boolByOr.nix | 0 .../tests}/stdlib/modules/class-check.nix | 0 .../stdlib/modules/declaration-positions.nix | 0 .../tests}/stdlib/modules/declare-attrsOf.nix | 0 .../modules/declare-attrsOfSub-any-enable.nix | 0 ...e-bare-submodule-deep-option-duplicate.nix | 0 .../declare-bare-submodule-deep-option.nix | 0 .../declare-bare-submodule-nested-option.nix | 0 .../stdlib/modules/declare-bare-submodule.nix | 0 .../modules/declare-coerced-value-unsound.nix | 0 .../stdlib/modules/declare-coerced-value.nix | 0 .../tests}/stdlib/modules/declare-either.nix | 0 .../stdlib/modules/declare-enable-nested.nix | 0 .../tests}/stdlib/modules/declare-enable.nix | 0 .../modules/declare-int-between-value.nix | 0 .../declare-int-positive-value-nested.nix | 0 .../modules/declare-int-positive-value.nix | 0 .../modules/declare-int-unsigned-value.nix | 0 .../stdlib/modules/declare-lazyAttrsOf.nix | 0 .../modules/declare-mkPackageOption.nix | 0 .../tests}/stdlib/modules/declare-oneOf.nix | 0 .../tests}/stdlib/modules/declare-set.nix | 0 .../declare-submodule-via-evalModules.nix | 0 .../modules/declare-submoduleWith-modules.nix | 0 .../declare-submoduleWith-noshorthand.nix | 0 .../modules/declare-submoduleWith-path.nix | 0 .../declare-submoduleWith-shorthand.nix | 0 .../modules/declare-submoduleWith-special.nix | 0 .../stdlib/modules/declare-variants.nix | 0 extra/tests/stdlib/modules/default.nix | 14 + .../stdlib/modules/deferred-module-error.nix | 0 .../tests}/stdlib/modules/deferred-module.nix | 0 .../modules/define-_module-args-custom.nix | 0 .../modules/define-attrsOfSub-bar-enable.nix | 0 .../stdlib/modules/define-attrsOfSub-bar.nix | 0 .../define-attrsOfSub-foo-enable-force.nix | 0 .../define-attrsOfSub-foo-enable-if.nix | 0 .../modules/define-attrsOfSub-foo-enable.nix | 0 .../define-attrsOfSub-foo-force-enable.nix | 0 .../define-attrsOfSub-foo-if-enable.nix | 0 .../stdlib/modules/define-attrsOfSub-foo.nix | 0 .../define-attrsOfSub-force-foo-enable.nix | 0 .../define-attrsOfSub-if-foo-enable.nix | 0 .../modules/define-bare-submodule-values.nix | 0 .../stdlib/modules/define-enable-abort.nix | 0 .../stdlib/modules/define-enable-force.nix | 0 .../stdlib/modules/define-enable-throw.nix | 0 .../modules/define-enable-with-custom-arg.nix | 0 .../define-enable-with-top-level-mkIf.nix | 0 .../tests}/stdlib/modules/define-enable.nix | 0 .../define-force-attrsOfSub-foo-enable.nix | 0 .../stdlib/modules/define-force-enable.nix | 0 .../define-freeform-keywords-shorthand.nix | 0 .../define-if-attrsOfSub-foo-enable.nix | 0 .../stdlib/modules/define-module-check.nix | 0 .../define-option-dependently-nested.nix | 0 .../modules/define-option-dependently.nix | 0 .../modules/define-settingsDict-a-is-b.nix | 0 ...define-shorthandOnlyDefinesConfig-true.nix | 0 .../define-submoduleWith-noshorthand.nix | 0 .../define-submoduleWith-shorthand.nix | 0 .../modules/define-value-int-negative.nix | 0 .../modules/define-value-int-positive.nix | 0 .../stdlib/modules/define-value-int-zero.nix | 0 .../stdlib/modules/define-value-list.nix | 0 .../modules/define-value-string-arbitrary.nix | 0 .../modules/define-value-string-bigint.nix | 0 .../define-value-string-properties.nix | 0 .../stdlib/modules/define-value-string.nix | 0 .../tests}/stdlib/modules/define-variant.nix | 0 .../stdlib/modules/disable-declare-enable.nix | 0 .../disable-define-enable-string-path.nix | 0 .../stdlib/modules/disable-define-enable.nix | 0 .../stdlib/modules/disable-enable-modules.nix | 0 .../stdlib/modules/disable-module-bad-key.nix | 0 .../modules/disable-module-with-key.nix | 0 .../disable-module-with-toString-key.nix | 0 .../stdlib/modules/disable-recursive/bar.nix | 0 .../modules/disable-recursive/disable-bar.nix | 0 .../modules/disable-recursive/disable-foo.nix | 0 .../stdlib/modules/disable-recursive/foo.nix | 0 .../stdlib/modules/disable-recursive/main.nix | 0 .../tests}/stdlib/modules/doRename-basic.nix | 0 .../modules/doRename-condition-enable.nix | 0 .../modules/doRename-condition-migrated.nix | 0 .../modules/doRename-condition-no-enable.nix | 0 .../stdlib/modules/doRename-condition.nix | 0 .../stdlib/modules/doRename-warnings.nix | 0 .../tests}/stdlib/modules/docs.nix | 0 .../tests}/stdlib/modules/emptyValues.nix | 0 .../modules/error-mkOption-in-config.nix | 0 .../error-mkOption-in-submodule-config.nix | 0 .../error-nonEmptyListOf-submodule.nix | 0 .../modules/extendModules-168767-imports.nix | 0 .../stdlib/modules/freeform-attrsOf.nix | 0 .../stdlib/modules/freeform-lazyAttrsOf.nix | 0 .../tests}/stdlib/modules/freeform-nested.nix | 0 .../stdlib/modules/freeform-str-dep-unstr.nix | 0 .../stdlib/modules/freeform-submodules.nix | 0 .../stdlib/modules/freeform-unstr-dep-str.nix | 0 .../stdlib/modules/functionTo/list-order.nix | 0 .../modules/functionTo/merging-attrs.nix | 0 .../modules/functionTo/merging-list.nix | 0 .../modules/functionTo/submodule-options.nix | 0 .../stdlib/modules/functionTo/trivial.nix | 0 .../stdlib/modules/functionTo/wrong-type.nix | 0 .../tests}/stdlib/modules/gvariant.nix | 0 .../stdlib/modules/import-configuration.nix | 0 .../stdlib/modules/import-custom-arg.nix | 0 .../stdlib/modules/import-from-store.nix | 0 .../stdlib/modules/merge-module-with-key.nix | 0 .../stdlib/modules/merge-typeless-option.nix | 0 .../modules/module-argument-default.nix | 0 .../stdlib/modules/module-class-is-darwin.nix | 0 .../stdlib/modules/module-class-is-nixos.nix | 0 .../modules/module-imports-_type-check.nix | 0 .../tests}/stdlib/modules/optionTypeFile.nix | 0 .../stdlib/modules/optionTypeMerging.nix | 0 .../options-type-error-configuration.nix | 0 .../options-type-error-typical-nested.nix | 0 .../modules/options-type-error-typical.nix | 0 {tests => extra/tests}/stdlib/modules/raw.nix | 0 .../tests}/stdlib/modules/shorthand-meta.nix | 0 .../tests}/stdlib/modules/submoduleFiles.nix | 0 .../test-mergeAttrDefinitionsWithPrio.nix | 0 .../types-anything/attrs-coercible.nix | 0 .../modules/types-anything/equal-atoms.nix | 0 .../modules/types-anything/functions.nix | 0 .../stdlib/modules/types-anything/lists.nix | 0 .../stdlib/modules/types-anything/mk-mods.nix | 0 .../modules/types-anything/nested-attrs.nix | 0 .../modules/types-attrTag-wrong-decl.nix | 0 .../tests}/stdlib/modules/types-attrTag.nix | 0 .../tests}/stdlib/modules/types-unique.nix | 0 .../tests}/stdlib/modules/types.nix | 0 .../stdlib/packages-from-directory/a.nix | 0 .../stdlib/packages-from-directory/b.nix | 0 .../c/my-extra-feature.patch | 0 .../c/not-a-namespace/not-a-package.nix | 0 .../packages-from-directory/c/package.nix | 0 .../c/support-definitions.nix | 0 .../my-namespace/d.nix | 0 .../my-namespace/e.nix | 0 .../my-namespace/f/package.nix | 0 .../my-namespace/my-sub-namespace/g.nix | 0 .../my-namespace/my-sub-namespace/h.nix | 0 {tests => extra/tests}/stdlib/path/TODO | 0 extra/tests/stdlib/path/default.nix | 43 + .../tests}/stdlib/path/generate.awk | 0 {tests => extra/tests}/stdlib/path/prop.nix | 0 {tests => extra/tests}/stdlib/path/prop.sh | 0 {tests => extra/tests}/stdlib/path/unit.nix | 0 {tests => extra/tests}/stdlib/sources.sh | 0 {tests => extra/tests}/stdlib/systems.nix | 0 .../stdlib/test-to-plist-expected.plist | 0 extra/tests/stdlib/test-with-nix.nix | 57 + flake.lock | 22 +- flake.nix | 31 +- nix/stdlib/attrsets.nix | 2870 +++++++++-------- shell.nix | 1 - tests/auxlib/default.nix | 16 - tests/auxlib/test-with-nix.nix | 13 - tests/default.nix | 9 - tests/formatting.nix | 5 - tests/stdlib/default.nix | 16 - tests/stdlib/modules/default.nix | 7 - tests/stdlib/path/default.nix | 33 - tests/stdlib/test-with-nix.nix | 49 - 191 files changed, 1864 insertions(+), 1611 deletions(-) create mode 100644 extra/default.nix rename {doc => extra/doc}/default.nix (55%) rename {doc => extra/doc}/sections.nix (63%) create mode 100644 extra/npins/default.nix create mode 100644 extra/npins/sources.json create mode 100644 extra/tests/auxlib/default.nix create mode 100644 extra/tests/auxlib/test-with-nix.nix create mode 100644 extra/tests/default.nix create mode 100644 extra/tests/formatting.nix rename {tests => extra/tests}/nix-for-tests.nix (100%) rename {tests => extra/tests}/stdlib/README.md (100%) rename {tests => extra/tests}/stdlib/check-eval.nix (100%) create mode 100644 extra/tests/stdlib/default.nix rename {tests => extra/tests}/stdlib/fileset.sh (100%) rename {tests => extra/tests}/stdlib/filesystem.sh (100%) rename {tests => extra/tests}/stdlib/misc.nix (100%) rename {tests => extra/tests}/stdlib/modules.sh (99%) rename {tests => extra/tests}/stdlib/modules/adhoc-freeformType-survives-type-merge.nix (100%) rename {tests => extra/tests}/stdlib/modules/alias-with-priority-can-override.nix (100%) rename {tests => extra/tests}/stdlib/modules/alias-with-priority.nix (100%) rename {tests => extra/tests}/stdlib/modules/attrsOf-conditional-check.nix (100%) rename {tests => extra/tests}/stdlib/modules/attrsOf-lazy-check.nix (100%) rename {tests => extra/tests}/stdlib/modules/boolByOr.nix (100%) rename {tests => extra/tests}/stdlib/modules/class-check.nix (100%) rename {tests => extra/tests}/stdlib/modules/declaration-positions.nix (100%) rename {tests => extra/tests}/stdlib/modules/declare-attrsOf.nix (100%) rename {tests => extra/tests}/stdlib/modules/declare-attrsOfSub-any-enable.nix (100%) rename {tests => extra/tests}/stdlib/modules/declare-bare-submodule-deep-option-duplicate.nix (100%) rename {tests => extra/tests}/stdlib/modules/declare-bare-submodule-deep-option.nix (100%) rename {tests => extra/tests}/stdlib/modules/declare-bare-submodule-nested-option.nix (100%) rename {tests => extra/tests}/stdlib/modules/declare-bare-submodule.nix (100%) rename {tests => extra/tests}/stdlib/modules/declare-coerced-value-unsound.nix (100%) rename {tests => extra/tests}/stdlib/modules/declare-coerced-value.nix (100%) rename {tests => extra/tests}/stdlib/modules/declare-either.nix (100%) rename {tests => extra/tests}/stdlib/modules/declare-enable-nested.nix (100%) rename {tests => extra/tests}/stdlib/modules/declare-enable.nix (100%) rename {tests => extra/tests}/stdlib/modules/declare-int-between-value.nix (100%) rename {tests => extra/tests}/stdlib/modules/declare-int-positive-value-nested.nix (100%) rename {tests => extra/tests}/stdlib/modules/declare-int-positive-value.nix (100%) rename {tests => extra/tests}/stdlib/modules/declare-int-unsigned-value.nix (100%) rename {tests => extra/tests}/stdlib/modules/declare-lazyAttrsOf.nix (100%) rename {tests => extra/tests}/stdlib/modules/declare-mkPackageOption.nix (100%) rename {tests => extra/tests}/stdlib/modules/declare-oneOf.nix (100%) rename {tests => extra/tests}/stdlib/modules/declare-set.nix (100%) rename {tests => extra/tests}/stdlib/modules/declare-submodule-via-evalModules.nix (100%) rename {tests => extra/tests}/stdlib/modules/declare-submoduleWith-modules.nix (100%) rename {tests => extra/tests}/stdlib/modules/declare-submoduleWith-noshorthand.nix (100%) rename {tests => extra/tests}/stdlib/modules/declare-submoduleWith-path.nix (100%) rename {tests => extra/tests}/stdlib/modules/declare-submoduleWith-shorthand.nix (100%) rename {tests => extra/tests}/stdlib/modules/declare-submoduleWith-special.nix (100%) rename {tests => extra/tests}/stdlib/modules/declare-variants.nix (100%) create mode 100644 extra/tests/stdlib/modules/default.nix rename {tests => extra/tests}/stdlib/modules/deferred-module-error.nix (100%) rename {tests => extra/tests}/stdlib/modules/deferred-module.nix (100%) rename {tests => extra/tests}/stdlib/modules/define-_module-args-custom.nix (100%) rename {tests => extra/tests}/stdlib/modules/define-attrsOfSub-bar-enable.nix (100%) rename {tests => extra/tests}/stdlib/modules/define-attrsOfSub-bar.nix (100%) rename {tests => extra/tests}/stdlib/modules/define-attrsOfSub-foo-enable-force.nix (100%) rename {tests => extra/tests}/stdlib/modules/define-attrsOfSub-foo-enable-if.nix (100%) rename {tests => extra/tests}/stdlib/modules/define-attrsOfSub-foo-enable.nix (100%) rename {tests => extra/tests}/stdlib/modules/define-attrsOfSub-foo-force-enable.nix (100%) rename {tests => extra/tests}/stdlib/modules/define-attrsOfSub-foo-if-enable.nix (100%) rename {tests => extra/tests}/stdlib/modules/define-attrsOfSub-foo.nix (100%) rename {tests => extra/tests}/stdlib/modules/define-attrsOfSub-force-foo-enable.nix (100%) rename {tests => extra/tests}/stdlib/modules/define-attrsOfSub-if-foo-enable.nix (100%) rename {tests => extra/tests}/stdlib/modules/define-bare-submodule-values.nix (100%) rename {tests => extra/tests}/stdlib/modules/define-enable-abort.nix (100%) rename {tests => extra/tests}/stdlib/modules/define-enable-force.nix (100%) rename {tests => extra/tests}/stdlib/modules/define-enable-throw.nix (100%) rename {tests => extra/tests}/stdlib/modules/define-enable-with-custom-arg.nix (100%) rename {tests => extra/tests}/stdlib/modules/define-enable-with-top-level-mkIf.nix (100%) rename {tests => extra/tests}/stdlib/modules/define-enable.nix (100%) rename {tests => extra/tests}/stdlib/modules/define-force-attrsOfSub-foo-enable.nix (100%) rename {tests => extra/tests}/stdlib/modules/define-force-enable.nix (100%) rename {tests => extra/tests}/stdlib/modules/define-freeform-keywords-shorthand.nix (100%) rename {tests => extra/tests}/stdlib/modules/define-if-attrsOfSub-foo-enable.nix (100%) rename {tests => extra/tests}/stdlib/modules/define-module-check.nix (100%) rename {tests => extra/tests}/stdlib/modules/define-option-dependently-nested.nix (100%) rename {tests => extra/tests}/stdlib/modules/define-option-dependently.nix (100%) rename {tests => extra/tests}/stdlib/modules/define-settingsDict-a-is-b.nix (100%) rename {tests => extra/tests}/stdlib/modules/define-shorthandOnlyDefinesConfig-true.nix (100%) rename {tests => extra/tests}/stdlib/modules/define-submoduleWith-noshorthand.nix (100%) rename {tests => extra/tests}/stdlib/modules/define-submoduleWith-shorthand.nix (100%) rename {tests => extra/tests}/stdlib/modules/define-value-int-negative.nix (100%) rename {tests => extra/tests}/stdlib/modules/define-value-int-positive.nix (100%) rename {tests => extra/tests}/stdlib/modules/define-value-int-zero.nix (100%) rename {tests => extra/tests}/stdlib/modules/define-value-list.nix (100%) rename {tests => extra/tests}/stdlib/modules/define-value-string-arbitrary.nix (100%) rename {tests => extra/tests}/stdlib/modules/define-value-string-bigint.nix (100%) rename {tests => extra/tests}/stdlib/modules/define-value-string-properties.nix (100%) rename {tests => extra/tests}/stdlib/modules/define-value-string.nix (100%) rename {tests => extra/tests}/stdlib/modules/define-variant.nix (100%) rename {tests => extra/tests}/stdlib/modules/disable-declare-enable.nix (100%) rename {tests => extra/tests}/stdlib/modules/disable-define-enable-string-path.nix (100%) rename {tests => extra/tests}/stdlib/modules/disable-define-enable.nix (100%) rename {tests => extra/tests}/stdlib/modules/disable-enable-modules.nix (100%) rename {tests => extra/tests}/stdlib/modules/disable-module-bad-key.nix (100%) rename {tests => extra/tests}/stdlib/modules/disable-module-with-key.nix (100%) rename {tests => extra/tests}/stdlib/modules/disable-module-with-toString-key.nix (100%) rename {tests => extra/tests}/stdlib/modules/disable-recursive/bar.nix (100%) rename {tests => extra/tests}/stdlib/modules/disable-recursive/disable-bar.nix (100%) rename {tests => extra/tests}/stdlib/modules/disable-recursive/disable-foo.nix (100%) rename {tests => extra/tests}/stdlib/modules/disable-recursive/foo.nix (100%) rename {tests => extra/tests}/stdlib/modules/disable-recursive/main.nix (100%) rename {tests => extra/tests}/stdlib/modules/doRename-basic.nix (100%) rename {tests => extra/tests}/stdlib/modules/doRename-condition-enable.nix (100%) rename {tests => extra/tests}/stdlib/modules/doRename-condition-migrated.nix (100%) rename {tests => extra/tests}/stdlib/modules/doRename-condition-no-enable.nix (100%) rename {tests => extra/tests}/stdlib/modules/doRename-condition.nix (100%) rename {tests => extra/tests}/stdlib/modules/doRename-warnings.nix (100%) rename {tests => extra/tests}/stdlib/modules/docs.nix (100%) rename {tests => extra/tests}/stdlib/modules/emptyValues.nix (100%) rename {tests => extra/tests}/stdlib/modules/error-mkOption-in-config.nix (100%) rename {tests => extra/tests}/stdlib/modules/error-mkOption-in-submodule-config.nix (100%) rename {tests => extra/tests}/stdlib/modules/error-nonEmptyListOf-submodule.nix (100%) rename {tests => extra/tests}/stdlib/modules/extendModules-168767-imports.nix (100%) rename {tests => extra/tests}/stdlib/modules/freeform-attrsOf.nix (100%) rename {tests => extra/tests}/stdlib/modules/freeform-lazyAttrsOf.nix (100%) rename {tests => extra/tests}/stdlib/modules/freeform-nested.nix (100%) rename {tests => extra/tests}/stdlib/modules/freeform-str-dep-unstr.nix (100%) rename {tests => extra/tests}/stdlib/modules/freeform-submodules.nix (100%) rename {tests => extra/tests}/stdlib/modules/freeform-unstr-dep-str.nix (100%) rename {tests => extra/tests}/stdlib/modules/functionTo/list-order.nix (100%) rename {tests => extra/tests}/stdlib/modules/functionTo/merging-attrs.nix (100%) rename {tests => extra/tests}/stdlib/modules/functionTo/merging-list.nix (100%) rename {tests => extra/tests}/stdlib/modules/functionTo/submodule-options.nix (100%) rename {tests => extra/tests}/stdlib/modules/functionTo/trivial.nix (100%) rename {tests => extra/tests}/stdlib/modules/functionTo/wrong-type.nix (100%) rename {tests => extra/tests}/stdlib/modules/gvariant.nix (100%) rename {tests => extra/tests}/stdlib/modules/import-configuration.nix (100%) rename {tests => extra/tests}/stdlib/modules/import-custom-arg.nix (100%) rename {tests => extra/tests}/stdlib/modules/import-from-store.nix (100%) rename {tests => extra/tests}/stdlib/modules/merge-module-with-key.nix (100%) rename {tests => extra/tests}/stdlib/modules/merge-typeless-option.nix (100%) rename {tests => extra/tests}/stdlib/modules/module-argument-default.nix (100%) rename {tests => extra/tests}/stdlib/modules/module-class-is-darwin.nix (100%) rename {tests => extra/tests}/stdlib/modules/module-class-is-nixos.nix (100%) rename {tests => extra/tests}/stdlib/modules/module-imports-_type-check.nix (100%) rename {tests => extra/tests}/stdlib/modules/optionTypeFile.nix (100%) rename {tests => extra/tests}/stdlib/modules/optionTypeMerging.nix (100%) rename {tests => extra/tests}/stdlib/modules/options-type-error-configuration.nix (100%) rename {tests => extra/tests}/stdlib/modules/options-type-error-typical-nested.nix (100%) rename {tests => extra/tests}/stdlib/modules/options-type-error-typical.nix (100%) rename {tests => extra/tests}/stdlib/modules/raw.nix (100%) rename {tests => extra/tests}/stdlib/modules/shorthand-meta.nix (100%) rename {tests => extra/tests}/stdlib/modules/submoduleFiles.nix (100%) rename {tests => extra/tests}/stdlib/modules/test-mergeAttrDefinitionsWithPrio.nix (100%) rename {tests => extra/tests}/stdlib/modules/types-anything/attrs-coercible.nix (100%) rename {tests => extra/tests}/stdlib/modules/types-anything/equal-atoms.nix (100%) rename {tests => extra/tests}/stdlib/modules/types-anything/functions.nix (100%) rename {tests => extra/tests}/stdlib/modules/types-anything/lists.nix (100%) rename {tests => extra/tests}/stdlib/modules/types-anything/mk-mods.nix (100%) rename {tests => extra/tests}/stdlib/modules/types-anything/nested-attrs.nix (100%) rename {tests => extra/tests}/stdlib/modules/types-attrTag-wrong-decl.nix (100%) rename {tests => extra/tests}/stdlib/modules/types-attrTag.nix (100%) rename {tests => extra/tests}/stdlib/modules/types-unique.nix (100%) rename {tests => extra/tests}/stdlib/modules/types.nix (100%) rename {tests => extra/tests}/stdlib/packages-from-directory/a.nix (100%) rename {tests => extra/tests}/stdlib/packages-from-directory/b.nix (100%) rename {tests => extra/tests}/stdlib/packages-from-directory/c/my-extra-feature.patch (100%) rename {tests => extra/tests}/stdlib/packages-from-directory/c/not-a-namespace/not-a-package.nix (100%) rename {tests => extra/tests}/stdlib/packages-from-directory/c/package.nix (100%) rename {tests => extra/tests}/stdlib/packages-from-directory/c/support-definitions.nix (100%) rename {tests => extra/tests}/stdlib/packages-from-directory/my-namespace/d.nix (100%) rename {tests => extra/tests}/stdlib/packages-from-directory/my-namespace/e.nix (100%) rename {tests => extra/tests}/stdlib/packages-from-directory/my-namespace/f/package.nix (100%) rename {tests => extra/tests}/stdlib/packages-from-directory/my-namespace/my-sub-namespace/g.nix (100%) rename {tests => extra/tests}/stdlib/packages-from-directory/my-namespace/my-sub-namespace/h.nix (100%) rename {tests => extra/tests}/stdlib/path/TODO (100%) create mode 100644 extra/tests/stdlib/path/default.nix rename {tests => extra/tests}/stdlib/path/generate.awk (100%) rename {tests => extra/tests}/stdlib/path/prop.nix (100%) rename {tests => extra/tests}/stdlib/path/prop.sh (100%) rename {tests => extra/tests}/stdlib/path/unit.nix (100%) rename {tests => extra/tests}/stdlib/sources.sh (100%) rename {tests => extra/tests}/stdlib/systems.nix (100%) rename {tests => extra/tests}/stdlib/test-to-plist-expected.plist (100%) create mode 100644 extra/tests/stdlib/test-with-nix.nix delete mode 100644 shell.nix delete mode 100644 tests/auxlib/default.nix delete mode 100644 tests/auxlib/test-with-nix.nix delete mode 100644 tests/default.nix delete mode 100644 tests/formatting.nix delete mode 100644 tests/stdlib/default.nix delete mode 100644 tests/stdlib/modules/default.nix delete mode 100644 tests/stdlib/path/default.nix delete mode 100644 tests/stdlib/test-with-nix.nix diff --git a/README.md b/README.md index c2b3f3a..521111c 100644 --- a/README.md +++ b/README.md @@ -9,14 +9,23 @@ There are two main parts: - `stdlib` - mostly copied from Nixpkg's standard library. Meant to be forwards-compatible. - `auxlib` - library bits specifically for aux / auxpkgs. Not necessarily forwards-compatible. +## `extra/` + +Due to limitations with flakes, both tests and documentation are stored in `extra/`. +This avoids the `lib` flake having to depend on other places for testing and documentation tools, which are basically dev dependencies. + +This directory uses `npins`, as using flakes requires constantly running `nix flake update` to pick up local changes. + ## Testing -Tests are stored in `tests/`. You can run them all with `nix-build tests/default.nix`, which will run them with the minimum and maximum supported nix versions. +Tests are stored in `extra/tests/`. You can run them all with `nix-build -E '(import ./extra).checks.tests` (or `.stdlib`, `.auxlib`). -Alternatively, if using flakes you can run `nix flake check`. This will also check formatting using `nixfmt`. +You should also check your formatting with `nix-build -E '(import ./extra).checks.formatting`. ## Documentation Reference documentation for library functions is written above each function as a multi-line comment. These comments are processed using [nixdoc](https://github.com/nix-community/nixdoc), although currently we aren't doing much with the output. The nixdoc README describes the [comment format](https://github.com/nix-community/nixdoc#comment-format). + +You can build the documentation with `nix-build -E '(import ./extra).packages.docs'`. diff --git a/extra/default.nix b/extra/default.nix new file mode 100644 index 0000000..699a17d --- /dev/null +++ b/extra/default.nix @@ -0,0 +1,62 @@ +let + sources = import ./npins; + + libSrc = ../.; + lib = import libSrc; + pkgs = import sources.nixpkgs { + overlays = [ + # update nixfmt, as nixpkgs is pretty out of date + ( + final: prev: + prev + // { + nixfmt = prev.nixfmt.overrideAttrs { + src = final.fetchFromGitHub { + owner = "nixos"; + repo = "nixfmt"; + rev = "3bcb63c13e7aaf0b8e14081cf0c14c44f62e840a"; + sha256 = "sha256-8QWhKKqpHSlHuvFW8wdWTbG0pJ6XtuxZQ3HhR4uPbRA="; + }; + }; + } + ) + ]; + }; +in +{ + devShell = pkgs.mkShellNoCC { packages = [ pkgs.nixfmt ]; }; + + packages = { + docs = pkgs.callPackage ./doc { inherit libSrc; }; + }; + + checks = + let + auxlib = import ./tests/auxlib { inherit pkgs libSrc; }; + stdlib = import ./tests/stdlib { inherit pkgs libSrc; }; + tests = pkgs.symlinkJoin { + name = "auxlib-tests"; + paths = [ + auxlib + stdlib + ]; + }; + formatting = pkgs.callPackage ./tests/formatting.nix { inherit libSrc; }; + in + { + all = pkgs.symlinkJoin { + name = "auxlib-checks"; + paths = [ + tests + formatting + ]; + }; + + inherit + auxlib + stdlib + tests + formatting + ; + }; +} diff --git a/doc/default.nix b/extra/doc/default.nix similarity index 55% rename from doc/default.nix rename to extra/doc/default.nix index 5a25b93..ca537a1 100644 --- a/doc/default.nix +++ b/extra/doc/default.nix @@ -1,15 +1,20 @@ -{ lib, runCommand, nixdoc, ... }: +{ + auxlib, + lib, + runCommand, + nixdoc, + ... +}: let inherit (lib) escapeShellArg concatMapStringsSep; - sections = import ./sections.nix; -in runCommand "auxolotl-stdlib-docs" { buildInputs = [ nixdoc ]; } '' + sections = import ./sections.nix auxlib; +in +runCommand "auxolotl-stdlib-docs" { buildInputs = [ nixdoc ]; } '' mkdir $out function docgen { name=$1 path=$2 description=$3 - echo $path - set -x nixdoc -c "$name" -d "lib.$name: $description" -f "$path" > "$out/$name.md" echo "$name.md" >> "$out/index.md" } @@ -19,10 +24,10 @@ in runCommand "auxolotl-stdlib-docs" { buildInputs = [ nixdoc ]; } '' cat > "$out/index.md" << 'EOF' ```{=include=} sections auto-id-prefix=auto-generated EOF - ${concatMapStringsSep "\n" (section: - "docgen ${escapeShellArg section.name} ${section.path} ${ - escapeShellArg section.description - };") sections} + ${concatMapStringsSep "\n" ( + section: + "docgen ${escapeShellArg section.name} ${section.path} ${escapeShellArg section.description};" + ) sections} echo '```' >> "$out/index.md" '' diff --git a/doc/sections.nix b/extra/doc/sections.nix similarity index 63% rename from doc/sections.nix rename to extra/doc/sections.nix index ce77414..8dc49d8 100644 --- a/doc/sections.nix +++ b/extra/doc/sections.nix @@ -1,93 +1,93 @@ -[ +auxlib: let libPath = "${auxlib}/nix"; in [ { name = "asserts"; description = "assertion functions"; - path = ../nix/stdlib/asserts.nix; + path = "${libPath}/stdlib/asserts.nix"; } { name = "attrsets"; description = "attribute set functions"; - path = ../nix/stdlib/attrsets.nix; + path = "${libPath}/stdlib/attrsets.nix"; } { name = "strings"; description = "string manipulation functions"; - path = ../nix/stdlib/strings.nix; + path = "${libPath}/stdlib/strings.nix"; } { name = "versions"; description = "version string functions"; - path = ../nix/stdlib/versions.nix; + path = "${libPath}/stdlib/versions.nix"; } { name = "trivial"; description = "miscellaneous functions"; - path = ../nix/stdlib/trivial.nix; + path = "${libPath}/stdlib/trivial.nix"; } { name = "fixed-points"; description = "explicit recursion functions"; - path = ../nix/stdlib/fixed-points.nix; + path = "${libPath}/stdlib/fixed-points.nix"; } { name = "lists"; description = "list manipulation functions"; - path = ../nix/stdlib/lists.nix; + path = "${libPath}/stdlib/lists.nix"; } { name = "debug"; description = "debugging functions"; - path = ../nix/stdlib/debug.nix; + path = "${libPath}/stdlib/debug.nix"; } { name = "options"; description = "NixOS / nixpkgs option handling"; - path = ../nix/stdlib/options.nix; + path = "${libPath}/stdlib/options.nix"; } { name = "path"; description = "path functions"; - path = ../nix/stdlib/path/default.nix; + path = "${libPath}/stdlib/path/default.nix"; } { name = "filesystem"; description = "filesystem functions"; - path = ../nix/stdlib/filesystem.nix; + path = "${libPath}/stdlib/filesystem.nix"; } { name = "fileset"; description = "file set functions"; - path = ../nix/stdlib/fileset/default.nix; + path = "${libPath}/stdlib/fileset/default.nix"; } { name = "sources"; description = "source filtering functions"; - path = ../nix/stdlib/sources.nix; + path = "${libPath}/stdlib/sources.nix"; } { name = "cli"; description = "command-line serialization functions"; - path = ../nix/stdlib/cli.nix; + path = "${libPath}/stdlib/cli.nix"; } { name = "gvariant"; description = "GVariant formatted string serialization functions"; - path = ../nix/stdlib/gvariant.nix; + path = "${libPath}/stdlib/gvariant.nix"; } { name = "customisation"; description = "Functions to customise (derivation-related) functions, derivatons, or attribute sets"; - path = ../nix/stdlib/customisation.nix; + path = "${libPath}/stdlib/customisation.nix"; } { name = "meta"; description = "functions for derivation metadata"; - path = ../nix/stdlib/meta.nix; + path = "${libPath}/stdlib/meta.nix"; } { name = "derivations"; description = "miscellaneous derivation-specific functions"; - path = ../nix/stdlib/derivations.nix; + path = "${libPath}/stdlib/derivations.nix"; } ] diff --git a/extra/npins/default.nix b/extra/npins/default.nix new file mode 100644 index 0000000..4a7c372 --- /dev/null +++ b/extra/npins/default.nix @@ -0,0 +1,47 @@ +# Generated by npins. Do not modify; will be overwritten regularly +let + data = builtins.fromJSON (builtins.readFile ./sources.json); + version = data.version; + + mkSource = spec: + assert spec ? type; let + path = + if spec.type == "Git" then mkGitSource spec + else if spec.type == "GitRelease" then mkGitSource spec + else if spec.type == "PyPi" then mkPyPiSource spec + else if spec.type == "Channel" then mkChannelSource spec + else builtins.throw "Unknown source type ${spec.type}"; + in + spec // { outPath = path; }; + + mkGitSource = { repository, revision, url ? null, hash, ... }: + assert repository ? type; + # At the moment, either it is a plain git repository (which has an url), or it is a GitHub/GitLab repository + # In the latter case, there we will always be an url to the tarball + if url != null then + (builtins.fetchTarball { + inherit url; + sha256 = hash; # FIXME: check nix version & use SRI hashes + }) + else assert repository.type == "Git"; builtins.fetchGit { + url = repository.url; + rev = revision; + # hash = hash; + }; + + mkPyPiSource = { url, hash, ... }: + builtins.fetchurl { + inherit url; + sha256 = hash; + }; + + mkChannelSource = { url, hash, ... }: + builtins.fetchTarball { + inherit url; + sha256 = hash; + }; +in +if version == 3 then + builtins.mapAttrs (_: mkSource) data.pins +else + throw "Unsupported format version ${toString version} in sources.json. Try running `npins upgrade`" diff --git a/extra/npins/sources.json b/extra/npins/sources.json new file mode 100644 index 0000000..cc65fcd --- /dev/null +++ b/extra/npins/sources.json @@ -0,0 +1,11 @@ +{ + "pins": { + "nixpkgs": { + "type": "Channel", + "name": "nixos-23.11", + "url": "https://releases.nixos.org/nixos/23.11/nixos-23.11.7122.9ddcaffecdf0/nixexprs.tar.xz", + "hash": "1mgli2h99cg1db95gm9n59l8ws7kyi0lhb5dyxqzxlb8csg76j9m" + } + }, + "version": 3 +} \ No newline at end of file diff --git a/extra/tests/auxlib/default.nix b/extra/tests/auxlib/default.nix new file mode 100644 index 0000000..24c05b6 --- /dev/null +++ b/extra/tests/auxlib/default.nix @@ -0,0 +1,23 @@ +{ + # The pkgs used for dependencies for the testing itself + # Don't test properties of pkgs.lib, but rather the lib in the parent directory + pkgs ? import { } // { + lib = throw "pkgs.lib accessed, but the lib tests should use nixpkgs' lib path directly!"; + }, + libSrc, + nix ? pkgs-nixVersions.stable, + nixVersions ? [ + pkgs-nixVersions.minimum + nix + pkgs-nixVersions.unstable + ], + pkgs-nixVersions ? import ../nix-for-tests.nix { inherit pkgs; }, +}: + +let + testWithNix = nix: import ./test-with-nix.nix { inherit libSrc nix pkgs; }; +in +pkgs.symlinkJoin { + name = "auxlib-tests"; + paths = map testWithNix nixVersions; +} diff --git a/extra/tests/auxlib/test-with-nix.nix b/extra/tests/auxlib/test-with-nix.nix new file mode 100644 index 0000000..8200a44 --- /dev/null +++ b/extra/tests/auxlib/test-with-nix.nix @@ -0,0 +1,18 @@ +# * Runs all library tests with a particular version of Nix. +{ + pkgs, + libSrc, + # Only ever use this nix; see comment at top + nix, +}: +pkgs.runCommand "auxlib-tests-nix-${nix.version}" + { + buildInputs = [ + # TODO: Tests! + ]; + strictDeps = true; + } + '' + mkdir $out + echo success > $out/${nix.version} + '' diff --git a/extra/tests/default.nix b/extra/tests/default.nix new file mode 100644 index 0000000..8866497 --- /dev/null +++ b/extra/tests/default.nix @@ -0,0 +1,13 @@ +{ + pkgs ? import { } // { + lib = throw "pkgs.lib accessed, but the lib tests should use nixpkgs' lib path directly!"; + }, + libSrc ? ../.., +}: +pkgs.symlinkJoin { + name = "nixpkgs-lib-tests"; + paths = [ + (import ./stdlib { inherit pkgs libSrc; }) + (import ./auxlib { inherit pkgs libSrc; }) + ]; +} diff --git a/extra/tests/formatting.nix b/extra/tests/formatting.nix new file mode 100644 index 0000000..d4188d4 --- /dev/null +++ b/extra/tests/formatting.nix @@ -0,0 +1,5 @@ +{ auxlib, runCommand, nixfmt }: +runCommand "aux-lib-formatting" { buildInputs = [ nixfmt ]; } '' + find ${auxlib} -iname '*.nix' -type f -print0 | xargs -0 -i nixfmt -c {} + touch $out +'' diff --git a/tests/nix-for-tests.nix b/extra/tests/nix-for-tests.nix similarity index 100% rename from tests/nix-for-tests.nix rename to extra/tests/nix-for-tests.nix diff --git a/tests/stdlib/README.md b/extra/tests/stdlib/README.md similarity index 100% rename from tests/stdlib/README.md rename to extra/tests/stdlib/README.md diff --git a/tests/stdlib/check-eval.nix b/extra/tests/stdlib/check-eval.nix similarity index 100% rename from tests/stdlib/check-eval.nix rename to extra/tests/stdlib/check-eval.nix diff --git a/extra/tests/stdlib/default.nix b/extra/tests/stdlib/default.nix new file mode 100644 index 0000000..f9ed3b2 --- /dev/null +++ b/extra/tests/stdlib/default.nix @@ -0,0 +1,32 @@ +{ + # The pkgs used for dependencies for the testing itself + # Don't test properties of pkgs.lib, but rather the lib in the parent directory + pkgs ? import { } // { + lib = throw "pkgs.lib accessed, but the lib tests should use nixpkgs' lib path directly!"; + }, + libSrc, + nix ? pkgs-nixVersions.stable, + nixVersions ? [ + pkgs-nixVersions.minimum + nix + pkgs-nixVersions.unstable + ], + pkgs-nixVersions ? import ../nix-for-tests.nix { inherit pkgs; }, +}: +let + lib = import "${libSrc}/nix/stdlib/"; + testWithNix = + nix: + import ./test-with-nix.nix { + inherit + lib + nix + pkgs + libSrc + ; + }; +in +pkgs.symlinkJoin { + name = "stdlib-tests"; + paths = map testWithNix nixVersions; +} diff --git a/tests/stdlib/fileset.sh b/extra/tests/stdlib/fileset.sh similarity index 100% rename from tests/stdlib/fileset.sh rename to extra/tests/stdlib/fileset.sh diff --git a/tests/stdlib/filesystem.sh b/extra/tests/stdlib/filesystem.sh similarity index 100% rename from tests/stdlib/filesystem.sh rename to extra/tests/stdlib/filesystem.sh diff --git a/tests/stdlib/misc.nix b/extra/tests/stdlib/misc.nix similarity index 100% rename from tests/stdlib/misc.nix rename to extra/tests/stdlib/misc.nix diff --git a/tests/stdlib/modules.sh b/extra/tests/stdlib/modules.sh similarity index 99% rename from tests/stdlib/modules.sh rename to extra/tests/stdlib/modules.sh index 19f5aed..7eb87dd 100755 --- a/tests/stdlib/modules.sh +++ b/extra/tests/stdlib/modules.sh @@ -5,9 +5,7 @@ # By default it test the version of nixpkgs which is defined in the NIX_PATH. # # Run: -# [nixpkgs]$ ./tests/stdlib/modules.sh -# or: -# [nixpkgs]$ nix-build lib/tests/release.nix +# [nixpkgs]$ ./extra/tests/stdlib/modules.sh set -o errexit -o noclobber -o nounset -o pipefail shopt -s failglob inherit_errexit diff --git a/tests/stdlib/modules/adhoc-freeformType-survives-type-merge.nix b/extra/tests/stdlib/modules/adhoc-freeformType-survives-type-merge.nix similarity index 100% rename from tests/stdlib/modules/adhoc-freeformType-survives-type-merge.nix rename to extra/tests/stdlib/modules/adhoc-freeformType-survives-type-merge.nix diff --git a/tests/stdlib/modules/alias-with-priority-can-override.nix b/extra/tests/stdlib/modules/alias-with-priority-can-override.nix similarity index 100% rename from tests/stdlib/modules/alias-with-priority-can-override.nix rename to extra/tests/stdlib/modules/alias-with-priority-can-override.nix diff --git a/tests/stdlib/modules/alias-with-priority.nix b/extra/tests/stdlib/modules/alias-with-priority.nix similarity index 100% rename from tests/stdlib/modules/alias-with-priority.nix rename to extra/tests/stdlib/modules/alias-with-priority.nix diff --git a/tests/stdlib/modules/attrsOf-conditional-check.nix b/extra/tests/stdlib/modules/attrsOf-conditional-check.nix similarity index 100% rename from tests/stdlib/modules/attrsOf-conditional-check.nix rename to extra/tests/stdlib/modules/attrsOf-conditional-check.nix diff --git a/tests/stdlib/modules/attrsOf-lazy-check.nix b/extra/tests/stdlib/modules/attrsOf-lazy-check.nix similarity index 100% rename from tests/stdlib/modules/attrsOf-lazy-check.nix rename to extra/tests/stdlib/modules/attrsOf-lazy-check.nix diff --git a/tests/stdlib/modules/boolByOr.nix b/extra/tests/stdlib/modules/boolByOr.nix similarity index 100% rename from tests/stdlib/modules/boolByOr.nix rename to extra/tests/stdlib/modules/boolByOr.nix diff --git a/tests/stdlib/modules/class-check.nix b/extra/tests/stdlib/modules/class-check.nix similarity index 100% rename from tests/stdlib/modules/class-check.nix rename to extra/tests/stdlib/modules/class-check.nix diff --git a/tests/stdlib/modules/declaration-positions.nix b/extra/tests/stdlib/modules/declaration-positions.nix similarity index 100% rename from tests/stdlib/modules/declaration-positions.nix rename to extra/tests/stdlib/modules/declaration-positions.nix diff --git a/tests/stdlib/modules/declare-attrsOf.nix b/extra/tests/stdlib/modules/declare-attrsOf.nix similarity index 100% rename from tests/stdlib/modules/declare-attrsOf.nix rename to extra/tests/stdlib/modules/declare-attrsOf.nix diff --git a/tests/stdlib/modules/declare-attrsOfSub-any-enable.nix b/extra/tests/stdlib/modules/declare-attrsOfSub-any-enable.nix similarity index 100% rename from tests/stdlib/modules/declare-attrsOfSub-any-enable.nix rename to extra/tests/stdlib/modules/declare-attrsOfSub-any-enable.nix diff --git a/tests/stdlib/modules/declare-bare-submodule-deep-option-duplicate.nix b/extra/tests/stdlib/modules/declare-bare-submodule-deep-option-duplicate.nix similarity index 100% rename from tests/stdlib/modules/declare-bare-submodule-deep-option-duplicate.nix rename to extra/tests/stdlib/modules/declare-bare-submodule-deep-option-duplicate.nix diff --git a/tests/stdlib/modules/declare-bare-submodule-deep-option.nix b/extra/tests/stdlib/modules/declare-bare-submodule-deep-option.nix similarity index 100% rename from tests/stdlib/modules/declare-bare-submodule-deep-option.nix rename to extra/tests/stdlib/modules/declare-bare-submodule-deep-option.nix diff --git a/tests/stdlib/modules/declare-bare-submodule-nested-option.nix b/extra/tests/stdlib/modules/declare-bare-submodule-nested-option.nix similarity index 100% rename from tests/stdlib/modules/declare-bare-submodule-nested-option.nix rename to extra/tests/stdlib/modules/declare-bare-submodule-nested-option.nix diff --git a/tests/stdlib/modules/declare-bare-submodule.nix b/extra/tests/stdlib/modules/declare-bare-submodule.nix similarity index 100% rename from tests/stdlib/modules/declare-bare-submodule.nix rename to extra/tests/stdlib/modules/declare-bare-submodule.nix diff --git a/tests/stdlib/modules/declare-coerced-value-unsound.nix b/extra/tests/stdlib/modules/declare-coerced-value-unsound.nix similarity index 100% rename from tests/stdlib/modules/declare-coerced-value-unsound.nix rename to extra/tests/stdlib/modules/declare-coerced-value-unsound.nix diff --git a/tests/stdlib/modules/declare-coerced-value.nix b/extra/tests/stdlib/modules/declare-coerced-value.nix similarity index 100% rename from tests/stdlib/modules/declare-coerced-value.nix rename to extra/tests/stdlib/modules/declare-coerced-value.nix diff --git a/tests/stdlib/modules/declare-either.nix b/extra/tests/stdlib/modules/declare-either.nix similarity index 100% rename from tests/stdlib/modules/declare-either.nix rename to extra/tests/stdlib/modules/declare-either.nix diff --git a/tests/stdlib/modules/declare-enable-nested.nix b/extra/tests/stdlib/modules/declare-enable-nested.nix similarity index 100% rename from tests/stdlib/modules/declare-enable-nested.nix rename to extra/tests/stdlib/modules/declare-enable-nested.nix diff --git a/tests/stdlib/modules/declare-enable.nix b/extra/tests/stdlib/modules/declare-enable.nix similarity index 100% rename from tests/stdlib/modules/declare-enable.nix rename to extra/tests/stdlib/modules/declare-enable.nix diff --git a/tests/stdlib/modules/declare-int-between-value.nix b/extra/tests/stdlib/modules/declare-int-between-value.nix similarity index 100% rename from tests/stdlib/modules/declare-int-between-value.nix rename to extra/tests/stdlib/modules/declare-int-between-value.nix diff --git a/tests/stdlib/modules/declare-int-positive-value-nested.nix b/extra/tests/stdlib/modules/declare-int-positive-value-nested.nix similarity index 100% rename from tests/stdlib/modules/declare-int-positive-value-nested.nix rename to extra/tests/stdlib/modules/declare-int-positive-value-nested.nix diff --git a/tests/stdlib/modules/declare-int-positive-value.nix b/extra/tests/stdlib/modules/declare-int-positive-value.nix similarity index 100% rename from tests/stdlib/modules/declare-int-positive-value.nix rename to extra/tests/stdlib/modules/declare-int-positive-value.nix diff --git a/tests/stdlib/modules/declare-int-unsigned-value.nix b/extra/tests/stdlib/modules/declare-int-unsigned-value.nix similarity index 100% rename from tests/stdlib/modules/declare-int-unsigned-value.nix rename to extra/tests/stdlib/modules/declare-int-unsigned-value.nix diff --git a/tests/stdlib/modules/declare-lazyAttrsOf.nix b/extra/tests/stdlib/modules/declare-lazyAttrsOf.nix similarity index 100% rename from tests/stdlib/modules/declare-lazyAttrsOf.nix rename to extra/tests/stdlib/modules/declare-lazyAttrsOf.nix diff --git a/tests/stdlib/modules/declare-mkPackageOption.nix b/extra/tests/stdlib/modules/declare-mkPackageOption.nix similarity index 100% rename from tests/stdlib/modules/declare-mkPackageOption.nix rename to extra/tests/stdlib/modules/declare-mkPackageOption.nix diff --git a/tests/stdlib/modules/declare-oneOf.nix b/extra/tests/stdlib/modules/declare-oneOf.nix similarity index 100% rename from tests/stdlib/modules/declare-oneOf.nix rename to extra/tests/stdlib/modules/declare-oneOf.nix diff --git a/tests/stdlib/modules/declare-set.nix b/extra/tests/stdlib/modules/declare-set.nix similarity index 100% rename from tests/stdlib/modules/declare-set.nix rename to extra/tests/stdlib/modules/declare-set.nix diff --git a/tests/stdlib/modules/declare-submodule-via-evalModules.nix b/extra/tests/stdlib/modules/declare-submodule-via-evalModules.nix similarity index 100% rename from tests/stdlib/modules/declare-submodule-via-evalModules.nix rename to extra/tests/stdlib/modules/declare-submodule-via-evalModules.nix diff --git a/tests/stdlib/modules/declare-submoduleWith-modules.nix b/extra/tests/stdlib/modules/declare-submoduleWith-modules.nix similarity index 100% rename from tests/stdlib/modules/declare-submoduleWith-modules.nix rename to extra/tests/stdlib/modules/declare-submoduleWith-modules.nix diff --git a/tests/stdlib/modules/declare-submoduleWith-noshorthand.nix b/extra/tests/stdlib/modules/declare-submoduleWith-noshorthand.nix similarity index 100% rename from tests/stdlib/modules/declare-submoduleWith-noshorthand.nix rename to extra/tests/stdlib/modules/declare-submoduleWith-noshorthand.nix diff --git a/tests/stdlib/modules/declare-submoduleWith-path.nix b/extra/tests/stdlib/modules/declare-submoduleWith-path.nix similarity index 100% rename from tests/stdlib/modules/declare-submoduleWith-path.nix rename to extra/tests/stdlib/modules/declare-submoduleWith-path.nix diff --git a/tests/stdlib/modules/declare-submoduleWith-shorthand.nix b/extra/tests/stdlib/modules/declare-submoduleWith-shorthand.nix similarity index 100% rename from tests/stdlib/modules/declare-submoduleWith-shorthand.nix rename to extra/tests/stdlib/modules/declare-submoduleWith-shorthand.nix diff --git a/tests/stdlib/modules/declare-submoduleWith-special.nix b/extra/tests/stdlib/modules/declare-submoduleWith-special.nix similarity index 100% rename from tests/stdlib/modules/declare-submoduleWith-special.nix rename to extra/tests/stdlib/modules/declare-submoduleWith-special.nix diff --git a/tests/stdlib/modules/declare-variants.nix b/extra/tests/stdlib/modules/declare-variants.nix similarity index 100% rename from tests/stdlib/modules/declare-variants.nix rename to extra/tests/stdlib/modules/declare-variants.nix diff --git a/extra/tests/stdlib/modules/default.nix b/extra/tests/stdlib/modules/default.nix new file mode 100644 index 0000000..25c8c16 --- /dev/null +++ b/extra/tests/stdlib/modules/default.nix @@ -0,0 +1,14 @@ +{ + lib ? import ../../../../nix/stdlib, + modules ? [ ], +}: +{ + inherit + (lib.evalModules { + inherit modules; + specialArgs.modulesPath = ./.; + }) + config + options + ; +} diff --git a/tests/stdlib/modules/deferred-module-error.nix b/extra/tests/stdlib/modules/deferred-module-error.nix similarity index 100% rename from tests/stdlib/modules/deferred-module-error.nix rename to extra/tests/stdlib/modules/deferred-module-error.nix diff --git a/tests/stdlib/modules/deferred-module.nix b/extra/tests/stdlib/modules/deferred-module.nix similarity index 100% rename from tests/stdlib/modules/deferred-module.nix rename to extra/tests/stdlib/modules/deferred-module.nix diff --git a/tests/stdlib/modules/define-_module-args-custom.nix b/extra/tests/stdlib/modules/define-_module-args-custom.nix similarity index 100% rename from tests/stdlib/modules/define-_module-args-custom.nix rename to extra/tests/stdlib/modules/define-_module-args-custom.nix diff --git a/tests/stdlib/modules/define-attrsOfSub-bar-enable.nix b/extra/tests/stdlib/modules/define-attrsOfSub-bar-enable.nix similarity index 100% rename from tests/stdlib/modules/define-attrsOfSub-bar-enable.nix rename to extra/tests/stdlib/modules/define-attrsOfSub-bar-enable.nix diff --git a/tests/stdlib/modules/define-attrsOfSub-bar.nix b/extra/tests/stdlib/modules/define-attrsOfSub-bar.nix similarity index 100% rename from tests/stdlib/modules/define-attrsOfSub-bar.nix rename to extra/tests/stdlib/modules/define-attrsOfSub-bar.nix diff --git a/tests/stdlib/modules/define-attrsOfSub-foo-enable-force.nix b/extra/tests/stdlib/modules/define-attrsOfSub-foo-enable-force.nix similarity index 100% rename from tests/stdlib/modules/define-attrsOfSub-foo-enable-force.nix rename to extra/tests/stdlib/modules/define-attrsOfSub-foo-enable-force.nix diff --git a/tests/stdlib/modules/define-attrsOfSub-foo-enable-if.nix b/extra/tests/stdlib/modules/define-attrsOfSub-foo-enable-if.nix similarity index 100% rename from tests/stdlib/modules/define-attrsOfSub-foo-enable-if.nix rename to extra/tests/stdlib/modules/define-attrsOfSub-foo-enable-if.nix diff --git a/tests/stdlib/modules/define-attrsOfSub-foo-enable.nix b/extra/tests/stdlib/modules/define-attrsOfSub-foo-enable.nix similarity index 100% rename from tests/stdlib/modules/define-attrsOfSub-foo-enable.nix rename to extra/tests/stdlib/modules/define-attrsOfSub-foo-enable.nix diff --git a/tests/stdlib/modules/define-attrsOfSub-foo-force-enable.nix b/extra/tests/stdlib/modules/define-attrsOfSub-foo-force-enable.nix similarity index 100% rename from tests/stdlib/modules/define-attrsOfSub-foo-force-enable.nix rename to extra/tests/stdlib/modules/define-attrsOfSub-foo-force-enable.nix diff --git a/tests/stdlib/modules/define-attrsOfSub-foo-if-enable.nix b/extra/tests/stdlib/modules/define-attrsOfSub-foo-if-enable.nix similarity index 100% rename from tests/stdlib/modules/define-attrsOfSub-foo-if-enable.nix rename to extra/tests/stdlib/modules/define-attrsOfSub-foo-if-enable.nix diff --git a/tests/stdlib/modules/define-attrsOfSub-foo.nix b/extra/tests/stdlib/modules/define-attrsOfSub-foo.nix similarity index 100% rename from tests/stdlib/modules/define-attrsOfSub-foo.nix rename to extra/tests/stdlib/modules/define-attrsOfSub-foo.nix diff --git a/tests/stdlib/modules/define-attrsOfSub-force-foo-enable.nix b/extra/tests/stdlib/modules/define-attrsOfSub-force-foo-enable.nix similarity index 100% rename from tests/stdlib/modules/define-attrsOfSub-force-foo-enable.nix rename to extra/tests/stdlib/modules/define-attrsOfSub-force-foo-enable.nix diff --git a/tests/stdlib/modules/define-attrsOfSub-if-foo-enable.nix b/extra/tests/stdlib/modules/define-attrsOfSub-if-foo-enable.nix similarity index 100% rename from tests/stdlib/modules/define-attrsOfSub-if-foo-enable.nix rename to extra/tests/stdlib/modules/define-attrsOfSub-if-foo-enable.nix diff --git a/tests/stdlib/modules/define-bare-submodule-values.nix b/extra/tests/stdlib/modules/define-bare-submodule-values.nix similarity index 100% rename from tests/stdlib/modules/define-bare-submodule-values.nix rename to extra/tests/stdlib/modules/define-bare-submodule-values.nix diff --git a/tests/stdlib/modules/define-enable-abort.nix b/extra/tests/stdlib/modules/define-enable-abort.nix similarity index 100% rename from tests/stdlib/modules/define-enable-abort.nix rename to extra/tests/stdlib/modules/define-enable-abort.nix diff --git a/tests/stdlib/modules/define-enable-force.nix b/extra/tests/stdlib/modules/define-enable-force.nix similarity index 100% rename from tests/stdlib/modules/define-enable-force.nix rename to extra/tests/stdlib/modules/define-enable-force.nix diff --git a/tests/stdlib/modules/define-enable-throw.nix b/extra/tests/stdlib/modules/define-enable-throw.nix similarity index 100% rename from tests/stdlib/modules/define-enable-throw.nix rename to extra/tests/stdlib/modules/define-enable-throw.nix diff --git a/tests/stdlib/modules/define-enable-with-custom-arg.nix b/extra/tests/stdlib/modules/define-enable-with-custom-arg.nix similarity index 100% rename from tests/stdlib/modules/define-enable-with-custom-arg.nix rename to extra/tests/stdlib/modules/define-enable-with-custom-arg.nix diff --git a/tests/stdlib/modules/define-enable-with-top-level-mkIf.nix b/extra/tests/stdlib/modules/define-enable-with-top-level-mkIf.nix similarity index 100% rename from tests/stdlib/modules/define-enable-with-top-level-mkIf.nix rename to extra/tests/stdlib/modules/define-enable-with-top-level-mkIf.nix diff --git a/tests/stdlib/modules/define-enable.nix b/extra/tests/stdlib/modules/define-enable.nix similarity index 100% rename from tests/stdlib/modules/define-enable.nix rename to extra/tests/stdlib/modules/define-enable.nix diff --git a/tests/stdlib/modules/define-force-attrsOfSub-foo-enable.nix b/extra/tests/stdlib/modules/define-force-attrsOfSub-foo-enable.nix similarity index 100% rename from tests/stdlib/modules/define-force-attrsOfSub-foo-enable.nix rename to extra/tests/stdlib/modules/define-force-attrsOfSub-foo-enable.nix diff --git a/tests/stdlib/modules/define-force-enable.nix b/extra/tests/stdlib/modules/define-force-enable.nix similarity index 100% rename from tests/stdlib/modules/define-force-enable.nix rename to extra/tests/stdlib/modules/define-force-enable.nix diff --git a/tests/stdlib/modules/define-freeform-keywords-shorthand.nix b/extra/tests/stdlib/modules/define-freeform-keywords-shorthand.nix similarity index 100% rename from tests/stdlib/modules/define-freeform-keywords-shorthand.nix rename to extra/tests/stdlib/modules/define-freeform-keywords-shorthand.nix diff --git a/tests/stdlib/modules/define-if-attrsOfSub-foo-enable.nix b/extra/tests/stdlib/modules/define-if-attrsOfSub-foo-enable.nix similarity index 100% rename from tests/stdlib/modules/define-if-attrsOfSub-foo-enable.nix rename to extra/tests/stdlib/modules/define-if-attrsOfSub-foo-enable.nix diff --git a/tests/stdlib/modules/define-module-check.nix b/extra/tests/stdlib/modules/define-module-check.nix similarity index 100% rename from tests/stdlib/modules/define-module-check.nix rename to extra/tests/stdlib/modules/define-module-check.nix diff --git a/tests/stdlib/modules/define-option-dependently-nested.nix b/extra/tests/stdlib/modules/define-option-dependently-nested.nix similarity index 100% rename from tests/stdlib/modules/define-option-dependently-nested.nix rename to extra/tests/stdlib/modules/define-option-dependently-nested.nix diff --git a/tests/stdlib/modules/define-option-dependently.nix b/extra/tests/stdlib/modules/define-option-dependently.nix similarity index 100% rename from tests/stdlib/modules/define-option-dependently.nix rename to extra/tests/stdlib/modules/define-option-dependently.nix diff --git a/tests/stdlib/modules/define-settingsDict-a-is-b.nix b/extra/tests/stdlib/modules/define-settingsDict-a-is-b.nix similarity index 100% rename from tests/stdlib/modules/define-settingsDict-a-is-b.nix rename to extra/tests/stdlib/modules/define-settingsDict-a-is-b.nix diff --git a/tests/stdlib/modules/define-shorthandOnlyDefinesConfig-true.nix b/extra/tests/stdlib/modules/define-shorthandOnlyDefinesConfig-true.nix similarity index 100% rename from tests/stdlib/modules/define-shorthandOnlyDefinesConfig-true.nix rename to extra/tests/stdlib/modules/define-shorthandOnlyDefinesConfig-true.nix diff --git a/tests/stdlib/modules/define-submoduleWith-noshorthand.nix b/extra/tests/stdlib/modules/define-submoduleWith-noshorthand.nix similarity index 100% rename from tests/stdlib/modules/define-submoduleWith-noshorthand.nix rename to extra/tests/stdlib/modules/define-submoduleWith-noshorthand.nix diff --git a/tests/stdlib/modules/define-submoduleWith-shorthand.nix b/extra/tests/stdlib/modules/define-submoduleWith-shorthand.nix similarity index 100% rename from tests/stdlib/modules/define-submoduleWith-shorthand.nix rename to extra/tests/stdlib/modules/define-submoduleWith-shorthand.nix diff --git a/tests/stdlib/modules/define-value-int-negative.nix b/extra/tests/stdlib/modules/define-value-int-negative.nix similarity index 100% rename from tests/stdlib/modules/define-value-int-negative.nix rename to extra/tests/stdlib/modules/define-value-int-negative.nix diff --git a/tests/stdlib/modules/define-value-int-positive.nix b/extra/tests/stdlib/modules/define-value-int-positive.nix similarity index 100% rename from tests/stdlib/modules/define-value-int-positive.nix rename to extra/tests/stdlib/modules/define-value-int-positive.nix diff --git a/tests/stdlib/modules/define-value-int-zero.nix b/extra/tests/stdlib/modules/define-value-int-zero.nix similarity index 100% rename from tests/stdlib/modules/define-value-int-zero.nix rename to extra/tests/stdlib/modules/define-value-int-zero.nix diff --git a/tests/stdlib/modules/define-value-list.nix b/extra/tests/stdlib/modules/define-value-list.nix similarity index 100% rename from tests/stdlib/modules/define-value-list.nix rename to extra/tests/stdlib/modules/define-value-list.nix diff --git a/tests/stdlib/modules/define-value-string-arbitrary.nix b/extra/tests/stdlib/modules/define-value-string-arbitrary.nix similarity index 100% rename from tests/stdlib/modules/define-value-string-arbitrary.nix rename to extra/tests/stdlib/modules/define-value-string-arbitrary.nix diff --git a/tests/stdlib/modules/define-value-string-bigint.nix b/extra/tests/stdlib/modules/define-value-string-bigint.nix similarity index 100% rename from tests/stdlib/modules/define-value-string-bigint.nix rename to extra/tests/stdlib/modules/define-value-string-bigint.nix diff --git a/tests/stdlib/modules/define-value-string-properties.nix b/extra/tests/stdlib/modules/define-value-string-properties.nix similarity index 100% rename from tests/stdlib/modules/define-value-string-properties.nix rename to extra/tests/stdlib/modules/define-value-string-properties.nix diff --git a/tests/stdlib/modules/define-value-string.nix b/extra/tests/stdlib/modules/define-value-string.nix similarity index 100% rename from tests/stdlib/modules/define-value-string.nix rename to extra/tests/stdlib/modules/define-value-string.nix diff --git a/tests/stdlib/modules/define-variant.nix b/extra/tests/stdlib/modules/define-variant.nix similarity index 100% rename from tests/stdlib/modules/define-variant.nix rename to extra/tests/stdlib/modules/define-variant.nix diff --git a/tests/stdlib/modules/disable-declare-enable.nix b/extra/tests/stdlib/modules/disable-declare-enable.nix similarity index 100% rename from tests/stdlib/modules/disable-declare-enable.nix rename to extra/tests/stdlib/modules/disable-declare-enable.nix diff --git a/tests/stdlib/modules/disable-define-enable-string-path.nix b/extra/tests/stdlib/modules/disable-define-enable-string-path.nix similarity index 100% rename from tests/stdlib/modules/disable-define-enable-string-path.nix rename to extra/tests/stdlib/modules/disable-define-enable-string-path.nix diff --git a/tests/stdlib/modules/disable-define-enable.nix b/extra/tests/stdlib/modules/disable-define-enable.nix similarity index 100% rename from tests/stdlib/modules/disable-define-enable.nix rename to extra/tests/stdlib/modules/disable-define-enable.nix diff --git a/tests/stdlib/modules/disable-enable-modules.nix b/extra/tests/stdlib/modules/disable-enable-modules.nix similarity index 100% rename from tests/stdlib/modules/disable-enable-modules.nix rename to extra/tests/stdlib/modules/disable-enable-modules.nix diff --git a/tests/stdlib/modules/disable-module-bad-key.nix b/extra/tests/stdlib/modules/disable-module-bad-key.nix similarity index 100% rename from tests/stdlib/modules/disable-module-bad-key.nix rename to extra/tests/stdlib/modules/disable-module-bad-key.nix diff --git a/tests/stdlib/modules/disable-module-with-key.nix b/extra/tests/stdlib/modules/disable-module-with-key.nix similarity index 100% rename from tests/stdlib/modules/disable-module-with-key.nix rename to extra/tests/stdlib/modules/disable-module-with-key.nix diff --git a/tests/stdlib/modules/disable-module-with-toString-key.nix b/extra/tests/stdlib/modules/disable-module-with-toString-key.nix similarity index 100% rename from tests/stdlib/modules/disable-module-with-toString-key.nix rename to extra/tests/stdlib/modules/disable-module-with-toString-key.nix diff --git a/tests/stdlib/modules/disable-recursive/bar.nix b/extra/tests/stdlib/modules/disable-recursive/bar.nix similarity index 100% rename from tests/stdlib/modules/disable-recursive/bar.nix rename to extra/tests/stdlib/modules/disable-recursive/bar.nix diff --git a/tests/stdlib/modules/disable-recursive/disable-bar.nix b/extra/tests/stdlib/modules/disable-recursive/disable-bar.nix similarity index 100% rename from tests/stdlib/modules/disable-recursive/disable-bar.nix rename to extra/tests/stdlib/modules/disable-recursive/disable-bar.nix diff --git a/tests/stdlib/modules/disable-recursive/disable-foo.nix b/extra/tests/stdlib/modules/disable-recursive/disable-foo.nix similarity index 100% rename from tests/stdlib/modules/disable-recursive/disable-foo.nix rename to extra/tests/stdlib/modules/disable-recursive/disable-foo.nix diff --git a/tests/stdlib/modules/disable-recursive/foo.nix b/extra/tests/stdlib/modules/disable-recursive/foo.nix similarity index 100% rename from tests/stdlib/modules/disable-recursive/foo.nix rename to extra/tests/stdlib/modules/disable-recursive/foo.nix diff --git a/tests/stdlib/modules/disable-recursive/main.nix b/extra/tests/stdlib/modules/disable-recursive/main.nix similarity index 100% rename from tests/stdlib/modules/disable-recursive/main.nix rename to extra/tests/stdlib/modules/disable-recursive/main.nix diff --git a/tests/stdlib/modules/doRename-basic.nix b/extra/tests/stdlib/modules/doRename-basic.nix similarity index 100% rename from tests/stdlib/modules/doRename-basic.nix rename to extra/tests/stdlib/modules/doRename-basic.nix diff --git a/tests/stdlib/modules/doRename-condition-enable.nix b/extra/tests/stdlib/modules/doRename-condition-enable.nix similarity index 100% rename from tests/stdlib/modules/doRename-condition-enable.nix rename to extra/tests/stdlib/modules/doRename-condition-enable.nix diff --git a/tests/stdlib/modules/doRename-condition-migrated.nix b/extra/tests/stdlib/modules/doRename-condition-migrated.nix similarity index 100% rename from tests/stdlib/modules/doRename-condition-migrated.nix rename to extra/tests/stdlib/modules/doRename-condition-migrated.nix diff --git a/tests/stdlib/modules/doRename-condition-no-enable.nix b/extra/tests/stdlib/modules/doRename-condition-no-enable.nix similarity index 100% rename from tests/stdlib/modules/doRename-condition-no-enable.nix rename to extra/tests/stdlib/modules/doRename-condition-no-enable.nix diff --git a/tests/stdlib/modules/doRename-condition.nix b/extra/tests/stdlib/modules/doRename-condition.nix similarity index 100% rename from tests/stdlib/modules/doRename-condition.nix rename to extra/tests/stdlib/modules/doRename-condition.nix diff --git a/tests/stdlib/modules/doRename-warnings.nix b/extra/tests/stdlib/modules/doRename-warnings.nix similarity index 100% rename from tests/stdlib/modules/doRename-warnings.nix rename to extra/tests/stdlib/modules/doRename-warnings.nix diff --git a/tests/stdlib/modules/docs.nix b/extra/tests/stdlib/modules/docs.nix similarity index 100% rename from tests/stdlib/modules/docs.nix rename to extra/tests/stdlib/modules/docs.nix diff --git a/tests/stdlib/modules/emptyValues.nix b/extra/tests/stdlib/modules/emptyValues.nix similarity index 100% rename from tests/stdlib/modules/emptyValues.nix rename to extra/tests/stdlib/modules/emptyValues.nix diff --git a/tests/stdlib/modules/error-mkOption-in-config.nix b/extra/tests/stdlib/modules/error-mkOption-in-config.nix similarity index 100% rename from tests/stdlib/modules/error-mkOption-in-config.nix rename to extra/tests/stdlib/modules/error-mkOption-in-config.nix diff --git a/tests/stdlib/modules/error-mkOption-in-submodule-config.nix b/extra/tests/stdlib/modules/error-mkOption-in-submodule-config.nix similarity index 100% rename from tests/stdlib/modules/error-mkOption-in-submodule-config.nix rename to extra/tests/stdlib/modules/error-mkOption-in-submodule-config.nix diff --git a/tests/stdlib/modules/error-nonEmptyListOf-submodule.nix b/extra/tests/stdlib/modules/error-nonEmptyListOf-submodule.nix similarity index 100% rename from tests/stdlib/modules/error-nonEmptyListOf-submodule.nix rename to extra/tests/stdlib/modules/error-nonEmptyListOf-submodule.nix diff --git a/tests/stdlib/modules/extendModules-168767-imports.nix b/extra/tests/stdlib/modules/extendModules-168767-imports.nix similarity index 100% rename from tests/stdlib/modules/extendModules-168767-imports.nix rename to extra/tests/stdlib/modules/extendModules-168767-imports.nix diff --git a/tests/stdlib/modules/freeform-attrsOf.nix b/extra/tests/stdlib/modules/freeform-attrsOf.nix similarity index 100% rename from tests/stdlib/modules/freeform-attrsOf.nix rename to extra/tests/stdlib/modules/freeform-attrsOf.nix diff --git a/tests/stdlib/modules/freeform-lazyAttrsOf.nix b/extra/tests/stdlib/modules/freeform-lazyAttrsOf.nix similarity index 100% rename from tests/stdlib/modules/freeform-lazyAttrsOf.nix rename to extra/tests/stdlib/modules/freeform-lazyAttrsOf.nix diff --git a/tests/stdlib/modules/freeform-nested.nix b/extra/tests/stdlib/modules/freeform-nested.nix similarity index 100% rename from tests/stdlib/modules/freeform-nested.nix rename to extra/tests/stdlib/modules/freeform-nested.nix diff --git a/tests/stdlib/modules/freeform-str-dep-unstr.nix b/extra/tests/stdlib/modules/freeform-str-dep-unstr.nix similarity index 100% rename from tests/stdlib/modules/freeform-str-dep-unstr.nix rename to extra/tests/stdlib/modules/freeform-str-dep-unstr.nix diff --git a/tests/stdlib/modules/freeform-submodules.nix b/extra/tests/stdlib/modules/freeform-submodules.nix similarity index 100% rename from tests/stdlib/modules/freeform-submodules.nix rename to extra/tests/stdlib/modules/freeform-submodules.nix diff --git a/tests/stdlib/modules/freeform-unstr-dep-str.nix b/extra/tests/stdlib/modules/freeform-unstr-dep-str.nix similarity index 100% rename from tests/stdlib/modules/freeform-unstr-dep-str.nix rename to extra/tests/stdlib/modules/freeform-unstr-dep-str.nix diff --git a/tests/stdlib/modules/functionTo/list-order.nix b/extra/tests/stdlib/modules/functionTo/list-order.nix similarity index 100% rename from tests/stdlib/modules/functionTo/list-order.nix rename to extra/tests/stdlib/modules/functionTo/list-order.nix diff --git a/tests/stdlib/modules/functionTo/merging-attrs.nix b/extra/tests/stdlib/modules/functionTo/merging-attrs.nix similarity index 100% rename from tests/stdlib/modules/functionTo/merging-attrs.nix rename to extra/tests/stdlib/modules/functionTo/merging-attrs.nix diff --git a/tests/stdlib/modules/functionTo/merging-list.nix b/extra/tests/stdlib/modules/functionTo/merging-list.nix similarity index 100% rename from tests/stdlib/modules/functionTo/merging-list.nix rename to extra/tests/stdlib/modules/functionTo/merging-list.nix diff --git a/tests/stdlib/modules/functionTo/submodule-options.nix b/extra/tests/stdlib/modules/functionTo/submodule-options.nix similarity index 100% rename from tests/stdlib/modules/functionTo/submodule-options.nix rename to extra/tests/stdlib/modules/functionTo/submodule-options.nix diff --git a/tests/stdlib/modules/functionTo/trivial.nix b/extra/tests/stdlib/modules/functionTo/trivial.nix similarity index 100% rename from tests/stdlib/modules/functionTo/trivial.nix rename to extra/tests/stdlib/modules/functionTo/trivial.nix diff --git a/tests/stdlib/modules/functionTo/wrong-type.nix b/extra/tests/stdlib/modules/functionTo/wrong-type.nix similarity index 100% rename from tests/stdlib/modules/functionTo/wrong-type.nix rename to extra/tests/stdlib/modules/functionTo/wrong-type.nix diff --git a/tests/stdlib/modules/gvariant.nix b/extra/tests/stdlib/modules/gvariant.nix similarity index 100% rename from tests/stdlib/modules/gvariant.nix rename to extra/tests/stdlib/modules/gvariant.nix diff --git a/tests/stdlib/modules/import-configuration.nix b/extra/tests/stdlib/modules/import-configuration.nix similarity index 100% rename from tests/stdlib/modules/import-configuration.nix rename to extra/tests/stdlib/modules/import-configuration.nix diff --git a/tests/stdlib/modules/import-custom-arg.nix b/extra/tests/stdlib/modules/import-custom-arg.nix similarity index 100% rename from tests/stdlib/modules/import-custom-arg.nix rename to extra/tests/stdlib/modules/import-custom-arg.nix diff --git a/tests/stdlib/modules/import-from-store.nix b/extra/tests/stdlib/modules/import-from-store.nix similarity index 100% rename from tests/stdlib/modules/import-from-store.nix rename to extra/tests/stdlib/modules/import-from-store.nix diff --git a/tests/stdlib/modules/merge-module-with-key.nix b/extra/tests/stdlib/modules/merge-module-with-key.nix similarity index 100% rename from tests/stdlib/modules/merge-module-with-key.nix rename to extra/tests/stdlib/modules/merge-module-with-key.nix diff --git a/tests/stdlib/modules/merge-typeless-option.nix b/extra/tests/stdlib/modules/merge-typeless-option.nix similarity index 100% rename from tests/stdlib/modules/merge-typeless-option.nix rename to extra/tests/stdlib/modules/merge-typeless-option.nix diff --git a/tests/stdlib/modules/module-argument-default.nix b/extra/tests/stdlib/modules/module-argument-default.nix similarity index 100% rename from tests/stdlib/modules/module-argument-default.nix rename to extra/tests/stdlib/modules/module-argument-default.nix diff --git a/tests/stdlib/modules/module-class-is-darwin.nix b/extra/tests/stdlib/modules/module-class-is-darwin.nix similarity index 100% rename from tests/stdlib/modules/module-class-is-darwin.nix rename to extra/tests/stdlib/modules/module-class-is-darwin.nix diff --git a/tests/stdlib/modules/module-class-is-nixos.nix b/extra/tests/stdlib/modules/module-class-is-nixos.nix similarity index 100% rename from tests/stdlib/modules/module-class-is-nixos.nix rename to extra/tests/stdlib/modules/module-class-is-nixos.nix diff --git a/tests/stdlib/modules/module-imports-_type-check.nix b/extra/tests/stdlib/modules/module-imports-_type-check.nix similarity index 100% rename from tests/stdlib/modules/module-imports-_type-check.nix rename to extra/tests/stdlib/modules/module-imports-_type-check.nix diff --git a/tests/stdlib/modules/optionTypeFile.nix b/extra/tests/stdlib/modules/optionTypeFile.nix similarity index 100% rename from tests/stdlib/modules/optionTypeFile.nix rename to extra/tests/stdlib/modules/optionTypeFile.nix diff --git a/tests/stdlib/modules/optionTypeMerging.nix b/extra/tests/stdlib/modules/optionTypeMerging.nix similarity index 100% rename from tests/stdlib/modules/optionTypeMerging.nix rename to extra/tests/stdlib/modules/optionTypeMerging.nix diff --git a/tests/stdlib/modules/options-type-error-configuration.nix b/extra/tests/stdlib/modules/options-type-error-configuration.nix similarity index 100% rename from tests/stdlib/modules/options-type-error-configuration.nix rename to extra/tests/stdlib/modules/options-type-error-configuration.nix diff --git a/tests/stdlib/modules/options-type-error-typical-nested.nix b/extra/tests/stdlib/modules/options-type-error-typical-nested.nix similarity index 100% rename from tests/stdlib/modules/options-type-error-typical-nested.nix rename to extra/tests/stdlib/modules/options-type-error-typical-nested.nix diff --git a/tests/stdlib/modules/options-type-error-typical.nix b/extra/tests/stdlib/modules/options-type-error-typical.nix similarity index 100% rename from tests/stdlib/modules/options-type-error-typical.nix rename to extra/tests/stdlib/modules/options-type-error-typical.nix diff --git a/tests/stdlib/modules/raw.nix b/extra/tests/stdlib/modules/raw.nix similarity index 100% rename from tests/stdlib/modules/raw.nix rename to extra/tests/stdlib/modules/raw.nix diff --git a/tests/stdlib/modules/shorthand-meta.nix b/extra/tests/stdlib/modules/shorthand-meta.nix similarity index 100% rename from tests/stdlib/modules/shorthand-meta.nix rename to extra/tests/stdlib/modules/shorthand-meta.nix diff --git a/tests/stdlib/modules/submoduleFiles.nix b/extra/tests/stdlib/modules/submoduleFiles.nix similarity index 100% rename from tests/stdlib/modules/submoduleFiles.nix rename to extra/tests/stdlib/modules/submoduleFiles.nix diff --git a/tests/stdlib/modules/test-mergeAttrDefinitionsWithPrio.nix b/extra/tests/stdlib/modules/test-mergeAttrDefinitionsWithPrio.nix similarity index 100% rename from tests/stdlib/modules/test-mergeAttrDefinitionsWithPrio.nix rename to extra/tests/stdlib/modules/test-mergeAttrDefinitionsWithPrio.nix diff --git a/tests/stdlib/modules/types-anything/attrs-coercible.nix b/extra/tests/stdlib/modules/types-anything/attrs-coercible.nix similarity index 100% rename from tests/stdlib/modules/types-anything/attrs-coercible.nix rename to extra/tests/stdlib/modules/types-anything/attrs-coercible.nix diff --git a/tests/stdlib/modules/types-anything/equal-atoms.nix b/extra/tests/stdlib/modules/types-anything/equal-atoms.nix similarity index 100% rename from tests/stdlib/modules/types-anything/equal-atoms.nix rename to extra/tests/stdlib/modules/types-anything/equal-atoms.nix diff --git a/tests/stdlib/modules/types-anything/functions.nix b/extra/tests/stdlib/modules/types-anything/functions.nix similarity index 100% rename from tests/stdlib/modules/types-anything/functions.nix rename to extra/tests/stdlib/modules/types-anything/functions.nix diff --git a/tests/stdlib/modules/types-anything/lists.nix b/extra/tests/stdlib/modules/types-anything/lists.nix similarity index 100% rename from tests/stdlib/modules/types-anything/lists.nix rename to extra/tests/stdlib/modules/types-anything/lists.nix diff --git a/tests/stdlib/modules/types-anything/mk-mods.nix b/extra/tests/stdlib/modules/types-anything/mk-mods.nix similarity index 100% rename from tests/stdlib/modules/types-anything/mk-mods.nix rename to extra/tests/stdlib/modules/types-anything/mk-mods.nix diff --git a/tests/stdlib/modules/types-anything/nested-attrs.nix b/extra/tests/stdlib/modules/types-anything/nested-attrs.nix similarity index 100% rename from tests/stdlib/modules/types-anything/nested-attrs.nix rename to extra/tests/stdlib/modules/types-anything/nested-attrs.nix diff --git a/tests/stdlib/modules/types-attrTag-wrong-decl.nix b/extra/tests/stdlib/modules/types-attrTag-wrong-decl.nix similarity index 100% rename from tests/stdlib/modules/types-attrTag-wrong-decl.nix rename to extra/tests/stdlib/modules/types-attrTag-wrong-decl.nix diff --git a/tests/stdlib/modules/types-attrTag.nix b/extra/tests/stdlib/modules/types-attrTag.nix similarity index 100% rename from tests/stdlib/modules/types-attrTag.nix rename to extra/tests/stdlib/modules/types-attrTag.nix diff --git a/tests/stdlib/modules/types-unique.nix b/extra/tests/stdlib/modules/types-unique.nix similarity index 100% rename from tests/stdlib/modules/types-unique.nix rename to extra/tests/stdlib/modules/types-unique.nix diff --git a/tests/stdlib/modules/types.nix b/extra/tests/stdlib/modules/types.nix similarity index 100% rename from tests/stdlib/modules/types.nix rename to extra/tests/stdlib/modules/types.nix diff --git a/tests/stdlib/packages-from-directory/a.nix b/extra/tests/stdlib/packages-from-directory/a.nix similarity index 100% rename from tests/stdlib/packages-from-directory/a.nix rename to extra/tests/stdlib/packages-from-directory/a.nix diff --git a/tests/stdlib/packages-from-directory/b.nix b/extra/tests/stdlib/packages-from-directory/b.nix similarity index 100% rename from tests/stdlib/packages-from-directory/b.nix rename to extra/tests/stdlib/packages-from-directory/b.nix diff --git a/tests/stdlib/packages-from-directory/c/my-extra-feature.patch b/extra/tests/stdlib/packages-from-directory/c/my-extra-feature.patch similarity index 100% rename from tests/stdlib/packages-from-directory/c/my-extra-feature.patch rename to extra/tests/stdlib/packages-from-directory/c/my-extra-feature.patch diff --git a/tests/stdlib/packages-from-directory/c/not-a-namespace/not-a-package.nix b/extra/tests/stdlib/packages-from-directory/c/not-a-namespace/not-a-package.nix similarity index 100% rename from tests/stdlib/packages-from-directory/c/not-a-namespace/not-a-package.nix rename to extra/tests/stdlib/packages-from-directory/c/not-a-namespace/not-a-package.nix diff --git a/tests/stdlib/packages-from-directory/c/package.nix b/extra/tests/stdlib/packages-from-directory/c/package.nix similarity index 100% rename from tests/stdlib/packages-from-directory/c/package.nix rename to extra/tests/stdlib/packages-from-directory/c/package.nix diff --git a/tests/stdlib/packages-from-directory/c/support-definitions.nix b/extra/tests/stdlib/packages-from-directory/c/support-definitions.nix similarity index 100% rename from tests/stdlib/packages-from-directory/c/support-definitions.nix rename to extra/tests/stdlib/packages-from-directory/c/support-definitions.nix diff --git a/tests/stdlib/packages-from-directory/my-namespace/d.nix b/extra/tests/stdlib/packages-from-directory/my-namespace/d.nix similarity index 100% rename from tests/stdlib/packages-from-directory/my-namespace/d.nix rename to extra/tests/stdlib/packages-from-directory/my-namespace/d.nix diff --git a/tests/stdlib/packages-from-directory/my-namespace/e.nix b/extra/tests/stdlib/packages-from-directory/my-namespace/e.nix similarity index 100% rename from tests/stdlib/packages-from-directory/my-namespace/e.nix rename to extra/tests/stdlib/packages-from-directory/my-namespace/e.nix diff --git a/tests/stdlib/packages-from-directory/my-namespace/f/package.nix b/extra/tests/stdlib/packages-from-directory/my-namespace/f/package.nix similarity index 100% rename from tests/stdlib/packages-from-directory/my-namespace/f/package.nix rename to extra/tests/stdlib/packages-from-directory/my-namespace/f/package.nix diff --git a/tests/stdlib/packages-from-directory/my-namespace/my-sub-namespace/g.nix b/extra/tests/stdlib/packages-from-directory/my-namespace/my-sub-namespace/g.nix similarity index 100% rename from tests/stdlib/packages-from-directory/my-namespace/my-sub-namespace/g.nix rename to extra/tests/stdlib/packages-from-directory/my-namespace/my-sub-namespace/g.nix diff --git a/tests/stdlib/packages-from-directory/my-namespace/my-sub-namespace/h.nix b/extra/tests/stdlib/packages-from-directory/my-namespace/my-sub-namespace/h.nix similarity index 100% rename from tests/stdlib/packages-from-directory/my-namespace/my-sub-namespace/h.nix rename to extra/tests/stdlib/packages-from-directory/my-namespace/my-sub-namespace/h.nix diff --git a/tests/stdlib/path/TODO b/extra/tests/stdlib/path/TODO similarity index 100% rename from tests/stdlib/path/TODO rename to extra/tests/stdlib/path/TODO diff --git a/extra/tests/stdlib/path/default.nix b/extra/tests/stdlib/path/default.nix new file mode 100644 index 0000000..9ae2db5 --- /dev/null +++ b/extra/tests/stdlib/path/default.nix @@ -0,0 +1,43 @@ +{ + pkgs ? import { } // { + lib = throw "pkgs.lib accessed, but the lib tests should use nixpkgs' lib path directly!"; + }, + nix ? pkgs.nixVersions.stable, + libPath ? ../../../../nix/stdlib, + # Random seed + seed ? null, +}: +pkgs.runCommand "lib-path-tests" + { + nativeBuildInputs = + [ nix ] + ++ (with pkgs; [ + jq + bc + ]); + } + '' + # Needed to make Nix evaluation work + export TEST_ROOT=$(pwd)/test-tmp + export NIX_BUILD_HOOK= + export NIX_CONF_DIR=$TEST_ROOT/etc + export NIX_LOCALSTATE_DIR=$TEST_ROOT/var + export NIX_LOG_DIR=$TEST_ROOT/var/log/nix + export NIX_STATE_DIR=$TEST_ROOT/var/nix + export NIX_STORE_DIR=$TEST_ROOT/store + export PAGER=cat + + echo ${libPath} + cp -r ${libPath} lib + export TEST_LIB=$PWD/lib + + echo "Running unit tests lib/tests/stdlib/path/unit.nix" + nix-instantiate --eval --show-trace \ + --argstr libpath "$TEST_LIB" \ + lib/tests/stdlib/path/unit.nix + + echo "Running property tests lib/tests/stdlib/path/prop.sh" + bash lib/tests/stdlib/path/prop.sh ${toString seed} + + touch $out + '' diff --git a/tests/stdlib/path/generate.awk b/extra/tests/stdlib/path/generate.awk similarity index 100% rename from tests/stdlib/path/generate.awk rename to extra/tests/stdlib/path/generate.awk diff --git a/tests/stdlib/path/prop.nix b/extra/tests/stdlib/path/prop.nix similarity index 100% rename from tests/stdlib/path/prop.nix rename to extra/tests/stdlib/path/prop.nix diff --git a/tests/stdlib/path/prop.sh b/extra/tests/stdlib/path/prop.sh similarity index 100% rename from tests/stdlib/path/prop.sh rename to extra/tests/stdlib/path/prop.sh diff --git a/tests/stdlib/path/unit.nix b/extra/tests/stdlib/path/unit.nix similarity index 100% rename from tests/stdlib/path/unit.nix rename to extra/tests/stdlib/path/unit.nix diff --git a/tests/stdlib/sources.sh b/extra/tests/stdlib/sources.sh similarity index 100% rename from tests/stdlib/sources.sh rename to extra/tests/stdlib/sources.sh diff --git a/tests/stdlib/systems.nix b/extra/tests/stdlib/systems.nix similarity index 100% rename from tests/stdlib/systems.nix rename to extra/tests/stdlib/systems.nix diff --git a/tests/stdlib/test-to-plist-expected.plist b/extra/tests/stdlib/test-to-plist-expected.plist similarity index 100% rename from tests/stdlib/test-to-plist-expected.plist rename to extra/tests/stdlib/test-to-plist-expected.plist diff --git a/extra/tests/stdlib/test-with-nix.nix b/extra/tests/stdlib/test-with-nix.nix new file mode 100644 index 0000000..5a86650 --- /dev/null +++ b/extra/tests/stdlib/test-with-nix.nix @@ -0,0 +1,57 @@ +# * Runs all library tests with a particular version of Nix. +{ + pkgs, + lib, + libSrc, + # Only ever use this nix; see comment at top + nix, +}: +pkgs.runCommand "stdlib-tests-nix-${nix.version}" + { + buildInputs = [ + (import ./check-eval.nix lib) + # (import ./path { inherit pkgs libPath nix; }) + ]; + nativeBuildInputs = [ + nix + pkgs.gitMinimal + ] ++ lib.optional pkgs.stdenv.isLinux pkgs.inotify-tools; + strictDeps = true; + } + '' + datadir="${nix}/share" + export TEST_ROOT=$(pwd)/test-tmp + export HOME=$(mktemp -d) + export NIX_BUILD_HOOK= + export NIX_CONF_DIR=$TEST_ROOT/etc + export NIX_LOCALSTATE_DIR=$TEST_ROOT/var + export NIX_LOG_DIR=$TEST_ROOT/var/log/nix + export NIX_STATE_DIR=$TEST_ROOT/var/nix + export NIX_STORE_DIR=$TEST_ROOT/store + export PAGER=cat + cacheDir=$TEST_ROOT/binary-cache + + nix-store --init + + cp -r ${libSrc} lib + echo "Running lib/extra/tests/stdlib/modules.sh" + bash lib/extra/tests/stdlib/modules.sh + + echo "Checking lib.version" + nix-instantiate lib -A version --eval || { + echo "lib.version does not evaluate when lib is isolated from the rest of the nixpkgs tree" + exit 1 + } + + echo "Running lib/extra/tests/stdlib/filesystem.sh" + TEST_LIB=$PWD/lib bash lib/extra/tests/stdlib/filesystem.sh + + echo "Running lib/extra/tests/stdlib/sources.sh" + TEST_LIB=$PWD/lib bash lib/extra/tests/stdlib/sources.sh + + echo "Running lib/tests/stdlib/fileset.sh" + TEST_LIB=$PWD/lib bash lib/extra/tests/stdlib/fileset.sh + + mkdir $out + echo success > $out/${nix.version} + '' diff --git a/flake.lock b/flake.lock index 0802cd9..5999137 100644 --- a/flake.lock +++ b/flake.lock @@ -1,26 +1,6 @@ { "nodes": { - "nixpkgs": { - "locked": { - "lastModified": 1715395895, - "narHash": "sha256-DreMqi6+qa21ffLQqhMQL2XRUkAGt3N7iVB5FhJKie4=", - "owner": "nixos", - "repo": "nixpkgs", - "rev": "71bae31b7dbc335528ca7e96f479ec93462323ff", - "type": "github" - }, - "original": { - "owner": "nixos", - "ref": "nixos-23.11", - "repo": "nixpkgs", - "type": "github" - } - }, - "root": { - "inputs": { - "nixpkgs": "nixpkgs" - } - } + "root": {} }, "root": "root", "version": 7 diff --git a/flake.nix b/flake.nix index b7ac76e..2029162 100644 --- a/flake.nix +++ b/flake.nix @@ -1,32 +1,7 @@ { description = "Auxolotl system-agnostic libraries"; - inputs = { - # TODO: this is required to do the tests with different nix versions, - # and generate documentation - nixpkgs.url = "github:nixos/nixpkgs/nixos-23.11"; + inputs = {}; + outputs = {self}: { + lib = import ./nix; }; - - outputs = { self, nixpkgs, ... }: - let - lib = import ./nix; - forAllSystems = lib.genAttrs lib.systems.flakeExposed; - pkgsForSystem = system: import nixpkgs { inherit system; }; - in { - lib = import ./nix; - - devShells = forAllSystems (system: { - default = (pkgsForSystem system).callPackage ./shell.nix { }; - }); - - packages = forAllSystems - (system: { docs = (pkgsForSystem system).callPackage ./doc { }; }); - - checks = forAllSystems (system: - let pkgs = pkgsForSystem system; - in { - auxlib = import ./tests/auxlib { inherit pkgs; }; - stdlib = import ./tests/stdlib { inherit pkgs; }; - formatting = pkgs.callPackage ./tests/formatting.nix { }; - }); - }; } diff --git a/nix/stdlib/attrsets.nix b/nix/stdlib/attrsets.nix index bc8aa1f..372c861 100644 --- a/nix/stdlib/attrsets.nix +++ b/nix/stdlib/attrsets.nix @@ -1,386 +1,422 @@ -/* * - Operations on attribute sets. +/* +* +Operations on attribute sets. */ -{ lib }: -let +{lib}: let inherit (builtins) head length; inherit (lib.trivial) isInOldestRelease mergeAttrs warn warnIf; - inherit (lib.strings) - concatStringsSep concatMapStringsSep escapeNixIdentifier - sanitizeDerivationName; - inherit (lib.lists) - foldr foldl' concatMap elemAt all partition groupBy take foldl; + inherit + (lib.strings) + concatStringsSep + concatMapStringsSep + escapeNixIdentifier + sanitizeDerivationName + ; + inherit + (lib.lists) + foldr + foldl' + concatMap + elemAt + all + partition + groupBy + take + foldl + ; in rec { inherit (builtins) attrNames listToAttrs hasAttr isAttrs getAttr removeAttrs; - /* * - Return an attribute from nested attribute sets. + /* + * + Return an attribute from nested attribute sets. - Nix has an [attribute selection operator `. or`](https://nixos.org/manual/nix/stable/language/operators#attribute-selection) which is sufficient for such queries, as long as the number of attributes is static. For example: + Nix has an [attribute selection operator `. or`](https://nixos.org/manual/nix/stable/language/operators#attribute-selection) which is sufficient for such queries, as long as the number of attributes is static. For example: - ```nix - (x.a.b or 6) == attrByPath ["a" "b"] 6 x - # and - (x.${f p}."example.com" or 6) == attrByPath [ (f p) "example.com" ] 6 x - ``` + ```nix + (x.a.b or 6) == attrByPath ["a" "b"] 6 x + # and + (x.${f p}."example.com" or 6) == attrByPath [ (f p) "example.com" ] 6 x + ``` - # Inputs + # Inputs - `attrPath` + `attrPath` - : A list of strings representing the attribute path to return from `set` + : A list of strings representing the attribute path to return from `set` - `default` + `default` - : Default value if `attrPath` does not resolve to an existing value + : Default value if `attrPath` does not resolve to an existing value - `set` + `set` - : The nested attribute set to select values from + : The nested attribute set to select values from - # Type + # Type - ``` - attrByPath :: [String] -> Any -> AttrSet -> Any - ``` + ``` + attrByPath :: [String] -> Any -> AttrSet -> Any + ``` - # Examples - :::{.example} - ## `lib.attrsets.attrByPath` usage example + # Examples + :::{.example} + ## `lib.attrsets.attrByPath` usage example - ```nix - x = { a = { b = 3; }; } - # ["a" "b"] is equivalent to x.a.b - # 6 is a default value to return if the path does not exist in attrset - attrByPath ["a" "b"] 6 x - => 3 - attrByPath ["z" "z"] 6 x - => 6 - ``` + ```nix + x = { a = { b = 3; }; } + # ["a" "b"] is equivalent to x.a.b + # 6 is a default value to return if the path does not exist in attrset + attrByPath ["a" "b"] 6 x + => 3 + attrByPath ["z" "z"] 6 x + => 6 + ``` - ::: + ::: */ - attrByPath = attrPath: default: set: - let - lenAttrPath = length attrPath; - attrByPath' = n: s: - (if n == lenAttrPath then - s - else - (let attr = elemAt attrPath n; - in if s ? ${attr} then attrByPath' (n + 1) s.${attr} else default)); - in attrByPath' 0 set; + attrByPath = attrPath: default: set: let + lenAttrPath = length attrPath; + attrByPath' = n: s: ( + if n == lenAttrPath + then s + else + (let + attr = elemAt attrPath n; + in + if s ? ${attr} + then attrByPath' (n + 1) s.${attr} + else default) + ); + in + attrByPath' 0 set; - /* * - Return if an attribute from nested attribute set exists. + /* + * + Return if an attribute from nested attribute set exists. - Nix has a [has attribute operator `?`](https://nixos.org/manual/nix/stable/language/operators#has-attribute), which is sufficient for such queries, as long as the number of attributes is static. For example: + Nix has a [has attribute operator `?`](https://nixos.org/manual/nix/stable/language/operators#has-attribute), which is sufficient for such queries, as long as the number of attributes is static. For example: - ```nix - (x?a.b) == hasAttrByPath ["a" "b"] x - # and - (x?${f p}."example.com") == hasAttrByPath [ (f p) "example.com" ] x - ``` + ```nix + (x?a.b) == hasAttrByPath ["a" "b"] x + # and + (x?${f p}."example.com") == hasAttrByPath [ (f p) "example.com" ] x + ``` - **Laws**: - 1. ```nix - hasAttrByPath [] x == true - ``` + **Laws**: + 1. ```nix + hasAttrByPath [] x == true + ``` - # Inputs + # Inputs - `attrPath` + `attrPath` - : A list of strings representing the attribute path to check from `set` + : A list of strings representing the attribute path to check from `set` - `e` + `e` - : The nested attribute set to check + : The nested attribute set to check - # Type + # Type - ``` - hasAttrByPath :: [String] -> AttrSet -> Bool - ``` + ``` + hasAttrByPath :: [String] -> AttrSet -> Bool + ``` - # Examples - :::{.example} - ## `lib.attrsets.hasAttrByPath` usage example + # Examples + :::{.example} + ## `lib.attrsets.hasAttrByPath` usage example - ```nix - x = { a = { b = 3; }; } - hasAttrByPath ["a" "b"] x - => true - hasAttrByPath ["z" "z"] x - => false - hasAttrByPath [] (throw "no need") - => true - ``` + ```nix + x = { a = { b = 3; }; } + hasAttrByPath ["a" "b"] x + => true + hasAttrByPath ["z" "z"] x + => false + hasAttrByPath [] (throw "no need") + => true + ``` - ::: + ::: */ - hasAttrByPath = attrPath: e: - let - lenAttrPath = length attrPath; - hasAttrByPath' = n: s: - (n == lenAttrPath || (let attr = elemAt attrPath n; - in if s ? ${attr} then hasAttrByPath' (n + 1) s.${attr} else false)); - in hasAttrByPath' 0 e; - - /* * - Return the longest prefix of an attribute path that refers to an existing attribute in a nesting of attribute sets. - - Can be used after [`mapAttrsRecursiveCond`](#function-library-lib.attrsets.mapAttrsRecursiveCond) to apply a condition, - although this will evaluate the predicate function on sibling attributes as well. - - Note that the empty attribute path is valid for all values, so this function only throws an exception if any of its inputs does. - - **Laws**: - 1. ```nix - attrsets.longestValidPathPrefix [] x == [] - ``` - - 2. ```nix - hasAttrByPath (attrsets.longestValidPathPrefix p x) x == true - ``` - - # Inputs - - `attrPath` - - : A list of strings representing the longest possible path that may be returned. - - `v` - - : The nested attribute set to check. - - # Type - - ``` - attrsets.longestValidPathPrefix :: [String] -> Value -> [String] - ``` - - # Examples - :::{.example} - ## `lib.attrsets.longestValidPathPrefix` usage example - - ```nix - x = { a = { b = 3; }; } - attrsets.longestValidPathPrefix ["a" "b" "c"] x - => ["a" "b"] - attrsets.longestValidPathPrefix ["a"] x - => ["a"] - attrsets.longestValidPathPrefix ["z" "z"] x - => [] - attrsets.longestValidPathPrefix ["z" "z"] (throw "no need") - => [] - ``` - - ::: + hasAttrByPath = attrPath: e: let + lenAttrPath = length attrPath; + hasAttrByPath' = n: s: (n + == lenAttrPath + || (let + attr = elemAt attrPath n; + in + if s ? ${attr} + then hasAttrByPath' (n + 1) s.${attr} + else false)); + in + hasAttrByPath' 0 e; + + /* + * + Return the longest prefix of an attribute path that refers to an existing attribute in a nesting of attribute sets. + + Can be used after [`mapAttrsRecursiveCond`](#function-library-lib.attrsets.mapAttrsRecursiveCond) to apply a condition, + although this will evaluate the predicate function on sibling attributes as well. + + Note that the empty attribute path is valid for all values, so this function only throws an exception if any of its inputs does. + + **Laws**: + 1. ```nix + attrsets.longestValidPathPrefix [] x == [] + ``` + + 2. ```nix + hasAttrByPath (attrsets.longestValidPathPrefix p x) x == true + ``` + + # Inputs + + `attrPath` + + : A list of strings representing the longest possible path that may be returned. + + `v` + + : The nested attribute set to check. + + # Type + + ``` + attrsets.longestValidPathPrefix :: [String] -> Value -> [String] + ``` + + # Examples + :::{.example} + ## `lib.attrsets.longestValidPathPrefix` usage example + + ```nix + x = { a = { b = 3; }; } + attrsets.longestValidPathPrefix ["a" "b" "c"] x + => ["a" "b"] + attrsets.longestValidPathPrefix ["a"] x + => ["a"] + attrsets.longestValidPathPrefix ["z" "z"] x + => [] + attrsets.longestValidPathPrefix ["z" "z"] (throw "no need") + => [] + ``` + + ::: */ - longestValidPathPrefix = attrPath: v: - let - lenAttrPath = length attrPath; - getPrefixForSetAtIndex = - # The nested attribute set to check, if it is an attribute set, which - # is not a given. - remainingSet: - # The index of the attribute we're about to check, as well as - # the length of the prefix we've already checked. - remainingPathIndex: - if remainingPathIndex == lenAttrPath then - # All previously checked attributes exist, and no attr names left, - # so we return the whole path. + longestValidPathPrefix = attrPath: v: let + lenAttrPath = length attrPath; + getPrefixForSetAtIndex = + # The nested attribute set to check, if it is an attribute set, which + # is not a given. + remainingSet: + # The index of the attribute we're about to check, as well as + # the length of the prefix we've already checked. + remainingPathIndex: + if remainingPathIndex == lenAttrPath + then + # All previously checked attributes exist, and no attr names left, + # so we return the whole path. attrPath - else - let attr = elemAt attrPath remainingPathIndex; - in if remainingSet ? ${attr} then + else let + attr = elemAt attrPath remainingPathIndex; + in + if remainingSet ? ${attr} + then getPrefixForSetAtIndex remainingSet.${attr} # advance from the set to the attribute value (remainingPathIndex + 1) # advance the path else - # The attribute doesn't exist, so we return the prefix up to the - # previously checked length. + # The attribute doesn't exist, so we return the prefix up to the + # previously checked length. take remainingPathIndex attrPath; - in getPrefixForSetAtIndex v 0; + in + getPrefixForSetAtIndex v 0; - /* * - 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` - : A list of strings representing the attribute path to set + : A list of strings representing the attribute path to set - `value` + `value` - : The value to set at the location described by `attrPath` + : The value to set at the location described by `attrPath` - # Type + # Type - ``` - setAttrByPath :: [String] -> Any -> AttrSet - ``` + ``` + setAttrByPath :: [String] -> Any -> AttrSet + ``` - # Examples - :::{.example} - ## `lib.attrsets.setAttrByPath` usage example + # Examples + :::{.example} + ## `lib.attrsets.setAttrByPath` usage example - ```nix - setAttrByPath ["a" "b"] 3 - => { a = { b = 3; }; } - ``` + ```nix + setAttrByPath ["a" "b"] 3 + => { a = { b = 3; }; } + ``` - ::: + ::: */ - setAttrByPath = attrPath: value: - let - len = length attrPath; - atDepth = n: - if n == len then value else { ${elemAt attrPath n} = atDepth (n + 1); }; - in atDepth 0; + setAttrByPath = attrPath: value: let + len = length attrPath; + atDepth = n: + if n == len + then value + else {${elemAt attrPath n} = atDepth (n + 1);}; + in + atDepth 0; - /* * - Like `attrByPath`, but without a default value. If it doesn't find the - path it will throw an error. + /* + * + Like `attrByPath`, but without a default value. If it doesn't find the + path it will throw an error. - Nix has an [attribute selection operator](https://nixos.org/manual/nix/stable/language/operators#attribute-selection) which is sufficient for such queries, as long as the number of attributes is static. For example: + Nix has an [attribute selection operator](https://nixos.org/manual/nix/stable/language/operators#attribute-selection) which is sufficient for such queries, as long as the number of attributes is static. For example: - ```nix - x.a.b == getAttrByPath ["a" "b"] x - # and - x.${f p}."example.com" == getAttrByPath [ (f p) "example.com" ] x - ``` + ```nix + x.a.b == getAttrByPath ["a" "b"] x + # and + x.${f p}."example.com" == getAttrByPath [ (f p) "example.com" ] x + ``` - # Inputs + # Inputs - `attrPath` + `attrPath` - : A list of strings representing the attribute path to get from `set` + : A list of strings representing the attribute path to get from `set` - `set` + `set` - : The nested attribute set to find the value in. + : The nested attribute set to find the value in. - # Type + # Type - ``` - getAttrFromPath :: [String] -> AttrSet -> Any - ``` + ``` + getAttrFromPath :: [String] -> AttrSet -> Any + ``` - # Examples - :::{.example} - ## `lib.attrsets.getAttrFromPath` usage example + # Examples + :::{.example} + ## `lib.attrsets.getAttrFromPath` usage example - ```nix - x = { a = { b = 3; }; } - getAttrFromPath ["a" "b"] x - => 3 - getAttrFromPath ["z" "z"] x - => error: cannot find attribute `z.z' - ``` + ```nix + x = { a = { b = 3; }; } + getAttrFromPath ["a" "b"] x + => 3 + getAttrFromPath ["z" "z"] x + => error: cannot find attribute `z.z' + ``` - ::: + ::: */ getAttrFromPath = 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` - : 1\. Function argument + : 1\. Function argument - `v` + `v` - : 2\. Function argument + : 2\. Function argument - # Type + # Type - ``` - concatMapAttrs :: (String -> a -> AttrSet) -> AttrSet -> AttrSet - ``` + ``` + concatMapAttrs :: (String -> a -> AttrSet) -> AttrSet -> AttrSet + ``` - # Examples - :::{.example} - ## `lib.attrsets.concatMapAttrs` usage example + # Examples + :::{.example} + ## `lib.attrsets.concatMapAttrs` usage example - ```nix - concatMapAttrs - (name: value: { - ${name} = value; - ${name + value} = value; - }) - { x = "a"; y = "b"; } - => { x = "a"; xa = "a"; y = "b"; yb = "b"; } - ``` + ```nix + concatMapAttrs + (name: value: { + ${name} = value; + ${name + value} = value; + }) + { x = "a"; y = "b"; } + => { x = "a"; xa = "a"; y = "b"; yb = "b"; } + ``` - ::: + ::: */ - concatMapAttrs = f: v: foldl' mergeAttrs { } (attrValues (mapAttrs f v)); - - /* * - Update or set specific paths of an attribute set. - - Takes a list of updates to apply and an attribute set to apply them to, - and returns the attribute set with the updates applied. Updates are - represented as `{ path = ...; update = ...; }` values, where `path` is a - list of strings representing the attribute path that should be updated, - and `update` is a function that takes the old value at that attribute path - as an argument and returns the new - value it should be. - - Properties: - - - Updates to deeper attribute paths are applied before updates to more - shallow attribute paths - - - Multiple updates to the same attribute path are applied in the order - they appear in the update list - - - If any but the last `path` element leads into a value that is not an - attribute set, an error is thrown - - - If there is an update for an attribute path that doesn't exist, - accessing the argument in the update function causes an error, but - intermediate attribute sets are implicitly created as needed - - # Type - - ``` - updateManyAttrsByPath :: [{ path :: [String]; update :: (Any -> Any); }] -> AttrSet -> AttrSet - ``` - - # Examples - :::{.example} - ## `lib.attrsets.updateManyAttrsByPath` usage example - - ```nix - updateManyAttrsByPath [ - { - path = [ "a" "b" ]; - update = old: { d = old.c; }; - } - { - path = [ "a" "b" "c" ]; - update = old: old + 1; - } - { - path = [ "x" "y" ]; - update = old: "xy"; - } - ] { a.b.c = 0; } - => { a = { b = { d = 1; }; }; x = { y = "xy"; }; } - ``` - - ::: + concatMapAttrs = f: v: foldl' mergeAttrs {} (attrValues (mapAttrs f v)); + + /* + * + Update or set specific paths of an attribute set. + + Takes a list of updates to apply and an attribute set to apply them to, + and returns the attribute set with the updates applied. Updates are + represented as `{ path = ...; update = ...; }` values, where `path` is a + list of strings representing the attribute path that should be updated, + and `update` is a function that takes the old value at that attribute path + as an argument and returns the new + value it should be. + + Properties: + + - Updates to deeper attribute paths are applied before updates to more + shallow attribute paths + + - Multiple updates to the same attribute path are applied in the order + they appear in the update list + + - If any but the last `path` element leads into a value that is not an + attribute set, an error is thrown + + - If there is an update for an attribute path that doesn't exist, + accessing the argument in the update function causes an error, but + intermediate attribute sets are implicitly created as needed + + # Type + + ``` + updateManyAttrsByPath :: [{ path :: [String]; update :: (Any -> Any); }] -> AttrSet -> AttrSet + ``` + + # Examples + :::{.example} + ## `lib.attrsets.updateManyAttrsByPath` usage example + + ```nix + updateManyAttrsByPath [ + { + path = [ "a" "b" ]; + update = old: { d = old.c; }; + } + { + path = [ "a" "b" "c" ]; + update = old: old + 1; + } + { + path = [ "x" "y" ]; + update = old: "xy"; + } + ] { a.b.c = 0; } + => { a = { b = { d = 1; }; }; x = { y = "xy"; }; } + ``` + + ::: */ updateManyAttrsByPath = let # When recursing into attributes, instead of updating the `path` of each @@ -388,1492 +424,1565 @@ in rec { # 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 # entries. - go = prefixLength: hasValue: value: updates: - let - # Splits updates into ones on this level (split.right) - # And ones on levels further down (split.wrong) - split = partition (el: length el.path == prefixLength) updates; - - # Groups updates on further down levels into the attributes they modify - nested = groupBy (el: elemAt el.path prefixLength) split.wrong; - - # Applies only nested modification to the input value - withNestedMods = - # Return the value directly if we don't have any nested modifications - if split.wrong == [ ] then - if hasValue then - value - else + go = prefixLength: hasValue: value: updates: let + # Splits updates into ones on this level (split.right) + # And ones on levels further down (split.wrong) + split = partition (el: length el.path == prefixLength) updates; + + # Groups updates on further down levels into the attributes they modify + nested = groupBy (el: elemAt el.path prefixLength) split.wrong; + + # Applies only nested modification to the input value + withNestedMods = + # Return the value directly if we don't have any nested modifications + if split.wrong == [] + then + if hasValue + then value + else # 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 # with `hasValue == false` for nested updates, in which case # it's also always called with at least one update - let updatePath = (head split.right).path; - in throw + let + updatePath = (head split.right).path; + in + throw ("updateManyAttrsByPath: Path '${showAttrPath updatePath}' does " + "not exist in the given value, but the first update to this " + "path tries to access the existing value.") - else + else # If there are nested modifications, try to apply them to the value - if !hasValue then - # But if we don't have a value, just use an empty attribute set - # as the value, but simplify the code a bit + if !hasValue + then + # But if we don't have a value, just use an empty attribute set + # as the value, but simplify the code a bit mapAttrs (name: go (prefixLength + 1) false null) nested - else if isAttrs value then - # If we do have a value and it's an attribute set, override it - # with the nested modifications - value // mapAttrs - (name: go (prefixLength + 1) (value ? ${name}) value.${name}) nested + else if isAttrs value + then + # If we do have a value and it's an attribute set, override it + # with the nested modifications + value + // mapAttrs + (name: go (prefixLength + 1) (value ? ${name}) value.${name}) + nested else - # However if it's not an attribute set, we can't apply the nested - # modifications, throw an error - let updatePath = (head split.wrong).path; - in throw ("updateManyAttrsByPath: Path '${ - showAttrPath updatePath - }' needs to " + "be updated, but path '${ - showAttrPath (take prefixLength updatePath) - }' " + "of the given value is not an attribute set, so we can't " - + "update an attribute inside of it."); - # We get the final result by applying all the updates on this level - # after having applied all the nested updates - # We use foldl instead of foldl' so that in case of multiple updates, - # intermediate values aren't evaluated if not needed - in foldl (acc: el: el.update acc) withNestedMods split.right; - in updates: value: go 0 true value updates; - - /* * - Return the specified attributes from a set. - - # Inputs - - `nameList` - - : The list of attributes to fetch from `set`. Each attribute name must exist on the attrbitue set - - `set` - - : The set to get attribute values from - - # Type - - ``` - attrVals :: [String] -> AttrSet -> [Any] - ``` - - # Examples - :::{.example} - ## `lib.attrsets.attrVals` usage example - - ```nix - attrVals ["a" "b" "c"] as - => [as.a as.b as.c] - ``` - - ::: + # However if it's not an attribute set, we can't apply the nested + # modifications, throw an error + let + updatePath = (head split.wrong).path; + in + throw ("updateManyAttrsByPath: Path '${ + showAttrPath updatePath + }' needs to " + + "be updated, but path '${ + showAttrPath (take prefixLength updatePath) + }' " + + "of the given value is not an attribute set, so we can't " + + "update an attribute inside of it."); + # We get the final result by applying all the updates on this level + # after having applied all the nested updates + # We use foldl instead of foldl' so that in case of multiple updates, + # intermediate values aren't evaluated if not needed + in + foldl (acc: el: el.update acc) withNestedMods split.right; + in + updates: value: go 0 true value updates; + + /* + * + Return the specified attributes from a set. + + # Inputs + + `nameList` + + : The list of attributes to fetch from `set`. Each attribute name must exist on the attrbitue set + + `set` + + : The set to get attribute values from + + # Type + + ``` + attrVals :: [String] -> AttrSet -> [Any] + ``` + + # Examples + :::{.example} + ## `lib.attrsets.attrVals` usage example + + ```nix + attrVals ["a" "b" "c"] as + => [as.a as.b as.c] + ``` + + ::: */ attrVals = nameList: set: map (x: set.${x}) nameList; - /* * - Return the values of all attributes in the given set, sorted by - attribute name. + /* + * + Return the values of all attributes in the given set, sorted by + attribute name. - # Type + # Type - ``` - attrValues :: AttrSet -> [Any] - ``` + ``` + attrValues :: AttrSet -> [Any] + ``` - # Examples - :::{.example} - ## `lib.attrsets.attrValues` usage example + # Examples + :::{.example} + ## `lib.attrsets.attrValues` usage example - ```nix - attrValues {c = 3; a = 1; b = 2;} - => [1 2 3] - ``` + ```nix + attrValues {c = 3; a = 1; b = 2;} + => [1 2 3] + ``` - ::: + ::: */ attrValues = builtins.attrValues; - /* * - Given a set of attribute names, return the set of the corresponding - attributes from the given set. + /* + * + Given a set of attribute names, return the set of the corresponding + attributes from the given set. - # Inputs + # Inputs - `names` + `names` - : A list of attribute names to get out of `set` + : A list of attribute names to get out of `set` - `attrs` + `attrs` - : The set to get the named attributes from + : The set to get the named attributes from - # Type + # Type - ``` - getAttrs :: [String] -> AttrSet -> AttrSet - ``` + ``` + getAttrs :: [String] -> AttrSet -> AttrSet + ``` - # Examples - :::{.example} - ## `lib.attrsets.getAttrs` usage example + # Examples + :::{.example} + ## `lib.attrsets.getAttrs` usage example - ```nix - getAttrs [ "a" "b" ] { a = 1; b = 2; c = 3; } - => { a = 1; b = 2; } - ``` + ```nix + getAttrs [ "a" "b" ] { a = 1; b = 2; c = 3; } + => { a = 1; b = 2; } + ``` - ::: + ::: */ getAttrs = names: attrs: genAttrs names (name: attrs.${name}); - /* * - Collect each attribute named `attr` from a list of attribute - sets. Sets that don't contain the named attribute are ignored. + /* + * + Collect each attribute named `attr` from a list of attribute + sets. Sets that don't contain the named attribute are ignored. - # Inputs + # Inputs - `attr` + `attr` - : The attribute name to get out of the sets. + : The attribute name to get out of the sets. - `list` + `list` - : The list of attribute sets to go through + : The list of attribute sets to go through - # Type + # Type - ``` - catAttrs :: String -> [AttrSet] -> [Any] - ``` + ``` + catAttrs :: String -> [AttrSet] -> [Any] + ``` - # Examples - :::{.example} - ## `lib.attrsets.catAttrs` usage example + # Examples + :::{.example} + ## `lib.attrsets.catAttrs` usage example - ```nix - catAttrs "a" [{a = 1;} {b = 0;} {a = 2;}] - => [1 2] - ``` + ```nix + catAttrs "a" [{a = 1;} {b = 0;} {a = 2;}] + => [1 2] + ``` - ::: + ::: */ catAttrs = builtins.catAttrs; - /* * - Filter an attribute set by removing all attributes for which the - given predicate return false. + /* + * + Filter an attribute set by removing all attributes for which the + given predicate return false. - # Inputs + # Inputs - `pred` + `pred` - : Predicate taking an attribute name and an attribute value, which returns `true` to include the attribute, or `false` to exclude the attribute. + : Predicate taking an attribute name and an attribute value, which returns `true` to include the attribute, or `false` to exclude the attribute. - `set` + `set` - : The attribute set to filter + : The attribute set to filter - # Type + # Type - ``` - filterAttrs :: (String -> Any -> Bool) -> AttrSet -> AttrSet - ``` + ``` + filterAttrs :: (String -> Any -> Bool) -> AttrSet -> AttrSet + ``` - # Examples - :::{.example} - ## `lib.attrsets.filterAttrs` usage example + # Examples + :::{.example} + ## `lib.attrsets.filterAttrs` usage example - ```nix - filterAttrs (n: v: n == "foo") { foo = 1; bar = 2; } - => { foo = 1; } - ``` + ```nix + filterAttrs (n: v: n == "foo") { foo = 1; bar = 2; } + => { foo = 1; } + ``` - ::: + ::: */ filterAttrs = pred: set: - listToAttrs (concatMap (name: - let v = set.${name}; - in if pred name v then [ (nameValuePair name v) ] else [ ]) - (attrNames set)); + listToAttrs (concatMap (name: let + v = set.${name}; + in + if pred name v + then [(nameValuePair name v)] + else []) + (attrNames set)); - /* * - Filter an attribute set recursively by removing all attributes for - which the given predicate return false. + /* + * + Filter an attribute set recursively by removing all attributes for + which the given predicate return false. - # Inputs + # Inputs - `pred` + `pred` - : Predicate taking an attribute name and an attribute value, which returns `true` to include the attribute, or `false` to exclude the attribute. + : Predicate taking an attribute name and an attribute value, which returns `true` to include the attribute, or `false` to exclude the attribute. - `set` + `set` - : The attribute set to filter + : The attribute set to filter - # Type + # Type - ``` - filterAttrsRecursive :: (String -> Any -> Bool) -> AttrSet -> AttrSet - ``` + ``` + filterAttrsRecursive :: (String -> Any -> Bool) -> AttrSet -> AttrSet + ``` - # Examples - :::{.example} - ## `lib.attrsets.filterAttrsRecursive` usage example + # Examples + :::{.example} + ## `lib.attrsets.filterAttrsRecursive` usage example - ```nix - filterAttrsRecursive (n: v: v != null) { foo = { bar = null; }; } - => { foo = {}; } - ``` + ```nix + filterAttrsRecursive (n: v: v != null) { foo = { bar = null; }; } + => { foo = {}; } + ``` - ::: + ::: */ filterAttrsRecursive = pred: set: - listToAttrs (concatMap (name: - let v = set.${name}; - in if pred name v then - [ - (nameValuePair name - (if isAttrs v then filterAttrsRecursive pred v else v)) - ] - else - [ ]) (attrNames set)); - - /* * - Like [`lib.lists.foldl'`](#function-library-lib.lists.foldl-prime) but for attribute sets. - Iterates over every name-value pair in the given attribute set. - The result of the callback function is often called `acc` for accumulator. It is passed between callbacks from left to right and the final `acc` is the return value of `foldlAttrs`. - - Attention: - - There is a completely different function `lib.foldAttrs` - which has nothing to do with this function, despite the similar name. - - # Inputs - - `f` - - : 1\. Function argument - - `init` - - : 2\. Function argument - - `set` - - : 3\. Function argument - - # Type - - ``` - foldlAttrs :: ( a -> String -> b -> a ) -> a -> { ... :: b } -> a - ``` - - # Examples - :::{.example} - ## `lib.attrsets.foldlAttrs` usage example - - ```nix - foldlAttrs - (acc: name: value: { - sum = acc.sum + value; - names = acc.names ++ [name]; - }) - { sum = 0; names = []; } - { - foo = 1; - bar = 10; - } - -> - { - sum = 11; - names = ["bar" "foo"]; - } - - foldlAttrs - (throw "function not needed") - 123 - {}; - -> - 123 - - foldlAttrs - (acc: _: _: acc) - 3 - { z = throw "value not needed"; a = throw "value not needed"; }; - -> - 3 - - The accumulator doesn't have to be an attrset. - It can be as simple as a number or string. - - foldlAttrs - (acc: _: v: acc * 10 + v) - 1 - { z = 1; a = 2; }; - -> - 121 - ``` - - ::: + listToAttrs (concatMap (name: let + v = set.${name}; + in + if pred name v + then [ + (nameValuePair name + ( + if isAttrs v + then filterAttrsRecursive pred v + else v + )) + ] + else []) (attrNames set)); + + /* + * + Like [`lib.lists.foldl'`](#function-library-lib.lists.foldl-prime) but for attribute sets. + Iterates over every name-value pair in the given attribute set. + The result of the callback function is often called `acc` for accumulator. It is passed between callbacks from left to right and the final `acc` is the return value of `foldlAttrs`. + + Attention: + + There is a completely different function `lib.foldAttrs` + which has nothing to do with this function, despite the similar name. + + # Inputs + + `f` + + : 1\. Function argument + + `init` + + : 2\. Function argument + + `set` + + : 3\. Function argument + + # Type + + ``` + foldlAttrs :: ( a -> String -> b -> a ) -> a -> { ... :: b } -> a + ``` + + # Examples + :::{.example} + ## `lib.attrsets.foldlAttrs` usage example + + ```nix + foldlAttrs + (acc: name: value: { + sum = acc.sum + value; + names = acc.names ++ [name]; + }) + { sum = 0; names = []; } + { + foo = 1; + bar = 10; + } + -> + { + sum = 11; + names = ["bar" "foo"]; + } + + foldlAttrs + (throw "function not needed") + 123 + {}; + -> + 123 + + foldlAttrs + (acc: _: _: acc) + 3 + { z = throw "value not needed"; a = throw "value not needed"; }; + -> + 3 + + The accumulator doesn't have to be an attrset. + It can be as simple as a number or string. + + foldlAttrs + (acc: _: v: acc * 10 + v) + 1 + { z = 1; a = 2; }; + -> + 121 + ``` + + ::: */ foldlAttrs = f: init: set: foldl' (acc: name: f acc name set.${name}) init (attrNames set); - /* * - Apply fold functions to values grouped by key. + /* + * + Apply fold functions to values grouped by key. - # Inputs + # Inputs - `op` + `op` - : A function, given a value and a collector combines the two. + : A function, given a value and a collector combines the two. - `nul` + `nul` - : The starting value. + : The starting value. - `list_of_attrs` + `list_of_attrs` - : A list of attribute sets to fold together by key. + : A list of attribute sets to fold together by key. - # Type + # Type - ``` - foldAttrs :: (Any -> Any -> Any) -> Any -> [AttrSets] -> Any - ``` + ``` + foldAttrs :: (Any -> Any -> Any) -> Any -> [AttrSets] -> Any + ``` - # Examples - :::{.example} - ## `lib.attrsets.foldAttrs` usage example + # Examples + :::{.example} + ## `lib.attrsets.foldAttrs` usage example - ```nix - foldAttrs (item: acc: [item] ++ acc) [] [{ a = 2; } { a = 3; }] - => { a = [ 2 3 ]; } - ``` + ```nix + foldAttrs (item: acc: [item] ++ acc) [] [{ a = 2; } { a = 3; }] + => { a = [ 2 3 ]; } + ``` - ::: + ::: */ foldAttrs = op: nul: list_of_attrs: foldr (n: a: - foldr (name: o: o // { ${name} = op n.${name} (a.${name} or nul); }) a - (attrNames n)) { } 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` - from the set `attrs`. The recursion is stopped when the predicate is - verified. + /* + * + Recursively collect sets that verify a given predicate named `pred` + from the set `attrs`. The recursion is stopped when the predicate is + verified. - # Inputs + # Inputs - `pred` + `pred` - : Given an attribute's value, determine if recursion should stop. + : Given an attribute's value, determine if recursion should stop. - `attrs` + `attrs` - : The attribute set to recursively collect. + : The attribute set to recursively collect. - # Type + # Type - ``` - collect :: (AttrSet -> Bool) -> AttrSet -> [x] - ``` + ``` + collect :: (AttrSet -> Bool) -> AttrSet -> [x] + ``` - # Examples - :::{.example} - ## `lib.attrsets.collect` usage example + # Examples + :::{.example} + ## `lib.attrsets.collect` usage example - ```nix - collect isList { a = { b = ["b"]; }; c = [1]; } - => [["b"] [1]] + ```nix + collect isList { a = { b = ["b"]; }; c = [1]; } + => [["b"] [1]] - collect (x: x ? outPath) - { a = { outPath = "a/"; }; b = { outPath = "b/"; }; } - => [{ outPath = "a/"; } { outPath = "b/"; }] - ``` + collect (x: x ? outPath) + { a = { outPath = "a/"; }; b = { outPath = "b/"; }; } + => [{ outPath = "a/"; } { outPath = "b/"; }] + ``` - ::: + ::: */ collect = pred: attrs: - if pred attrs then - [ attrs ] - else if isAttrs attrs then - concatMap (collect pred) (attrValues attrs) - else - [ ]; + if pred attrs + then [attrs] + else if isAttrs attrs + then concatMap (collect pred) (attrValues attrs) + else []; - /* * - Return the cartesian product of attribute set value combinations. + /* + * + Return the cartesian product of attribute set value combinations. - # Inputs + # Inputs - `attrsOfLists` + `attrsOfLists` - : Attribute set with attributes that are lists of values + : Attribute set with attributes that are lists of values - # Type + # Type - ``` - cartesianProduct :: AttrSet -> [AttrSet] - ``` + ``` + cartesianProduct :: AttrSet -> [AttrSet] + ``` - # Examples - :::{.example} - ## `lib.attrsets.cartesianProduct` usage example + # Examples + :::{.example} + ## `lib.attrsets.cartesianProduct` usage example - ```nix - cartesianProduct { a = [ 1 2 ]; b = [ 10 20 ]; } - => [ - { a = 1; b = 10; } - { a = 1; b = 20; } - { a = 2; b = 10; } - { a = 2; b = 20; } - ] - ``` + ```nix + cartesianProduct { a = [ 1 2 ]; b = [ 10 20 ]; } + => [ + { a = 1; b = 10; } + { a = 1; b = 20; } + { a = 2; b = 10; } + { a = 2; b = 20; } + ] + ``` - ::: + ::: */ cartesianProduct = attrsOfLists: foldl' (listOfAttrs: attrName: concatMap (attrs: - map (listValue: attrs // { ${attrName} = listValue; }) - attrsOfLists.${attrName}) listOfAttrs) [ { } ] (attrNames attrsOfLists); + map (listValue: attrs // {${attrName} = listValue;}) + attrsOfLists.${attrName}) + listOfAttrs) [{}] (attrNames attrsOfLists); - /* * - Return the result of function f applied to the cartesian product of attribute set value combinations. - Equivalent to using cartesianProduct followed by map. + /* + * + Return the result of function f applied to the cartesian product of attribute set value combinations. + Equivalent to using cartesianProduct followed by map. - # Inputs + # Inputs - `f` + `f` - : A function, given an attribute set, it returns a new value. + : A function, given an attribute set, it returns a new value. - `attrsOfLists` + `attrsOfLists` - : Attribute set with attributes that are lists of values + : Attribute set with attributes that are lists of values - # Type + # Type - ``` - mapCartesianProduct :: (AttrSet -> a) -> AttrSet -> [a] - ``` + ``` + mapCartesianProduct :: (AttrSet -> a) -> AttrSet -> [a] + ``` - # Examples - :::{.example} - ## `lib.attrsets.mapCartesianProduct` usage example + # Examples + :::{.example} + ## `lib.attrsets.mapCartesianProduct` usage example - ```nix - mapCartesianProduct ({a, b}: "${a}-${b}") { a = [ "1" "2" ]; b = [ "3" "4" ]; } - => [ "1-3" "1-4" "2-3" "2-4" ] - ``` + ```nix + mapCartesianProduct ({a, b}: "${a}-${b}") { a = [ "1" "2" ]; b = [ "3" "4" ]; } + => [ "1-3" "1-4" "2-3" "2-4" ] + ``` - ::: + ::: */ 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` - : Attribute name + : Attribute name - `value` + `value` - : Attribute value + : Attribute value - # Type + # Type - ``` - nameValuePair :: String -> Any -> { name :: String; value :: Any; } - ``` + ``` + nameValuePair :: String -> Any -> { name :: String; value :: Any; } + ``` - # Examples - :::{.example} - ## `lib.attrsets.nameValuePair` usage example + # Examples + :::{.example} + ## `lib.attrsets.nameValuePair` usage example - ```nix - nameValuePair "some" 6 - => { name = "some"; value = 6; } - ``` + ```nix + nameValuePair "some" 6 + => { name = "some"; value = 6; } + ``` - ::: + ::: */ - nameValuePair = name: value: { inherit name value; }; + nameValuePair = 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. - # Inputs + # Inputs - `f` + `f` - : A function that takes an attribute name and its value, and returns the new value for the attribute. + : A function that takes an attribute name and its value, and returns the new value for the attribute. - `attrset` + `attrset` - : The attribute set to iterate through. + : The attribute set to iterate through. - # Type + # Type - ``` - mapAttrs :: (String -> Any -> Any) -> AttrSet -> AttrSet - ``` + ``` + mapAttrs :: (String -> Any -> Any) -> AttrSet -> AttrSet + ``` - # Examples - :::{.example} - ## `lib.attrsets.mapAttrs` usage example + # Examples + :::{.example} + ## `lib.attrsets.mapAttrs` usage example - ```nix - mapAttrs (name: value: name + "-" + value) - { x = "foo"; y = "bar"; } - => { x = "x-foo"; y = "y-bar"; } - ``` + ```nix + mapAttrs (name: value: name + "-" + value) + { x = "foo"; y = "bar"; } + => { x = "x-foo"; y = "y-bar"; } + ``` - ::: + ::: */ mapAttrs = builtins.mapAttrs; - /* * - Like `mapAttrs`, but allows the name of each attribute to be - changed in addition to the value. The applied function should - return both the new name and value as a `nameValuePair`. + /* + * + Like `mapAttrs`, but allows the name of each attribute to be + changed in addition to the value. The applied function should + return both the new name and value as a `nameValuePair`. - # Inputs + # Inputs - `f` + `f` - : A function, given an attribute's name and value, returns a new `nameValuePair`. + : A function, given an attribute's name and value, returns a new `nameValuePair`. - `set` + `set` - : Attribute set to map over. + : Attribute set to map over. - # Type + # Type - ``` - mapAttrs' :: (String -> Any -> { name :: String; value :: Any; }) -> AttrSet -> AttrSet - ``` + ``` + mapAttrs' :: (String -> Any -> { name :: String; value :: Any; }) -> AttrSet -> AttrSet + ``` - # Examples - :::{.example} - ## `lib.attrsets.mapAttrs'` usage example + # Examples + :::{.example} + ## `lib.attrsets.mapAttrs'` usage example - ```nix - mapAttrs' (name: value: nameValuePair ("foo_" + name) ("bar-" + value)) - { x = "a"; y = "b"; } - => { foo_x = "bar-a"; foo_y = "bar-b"; } - ``` + ```nix + mapAttrs' (name: value: nameValuePair ("foo_" + name) ("bar-" + value)) + { x = "a"; y = "b"; } + => { foo_x = "bar-a"; foo_y = "bar-b"; } + ``` - ::: + ::: */ mapAttrs' = f: set: listToAttrs (map (attr: f attr set.${attr}) (attrNames set)); - /* * - Call a function for each attribute in the given set and return - the result in a list. + /* + * + Call a function for each attribute in the given set and return + the result in a list. - # Inputs + # Inputs - `f` + `f` - : A function, given an attribute's name and value, returns a new value. + : A function, given an attribute's name and value, returns a new value. - `attrs` + `attrs` - : Attribute set to map over. + : Attribute set to map over. - # Type + # Type - ``` - mapAttrsToList :: (String -> a -> b) -> AttrSet -> [b] - ``` + ``` + mapAttrsToList :: (String -> a -> b) -> AttrSet -> [b] + ``` - # Examples - :::{.example} - ## `lib.attrsets.mapAttrsToList` usage example + # Examples + :::{.example} + ## `lib.attrsets.mapAttrsToList` usage example - ```nix - mapAttrsToList (name: value: name + value) - { x = "a"; y = "b"; } - => [ "xa" "yb" ] - ``` + ```nix + mapAttrsToList (name: value: name + value) + { x = "a"; y = "b"; } + => [ "xa" "yb" ] + ``` - ::: + ::: */ mapAttrsToList = 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). - Each element of the resulting list is an attribute set with these attributes: - - `name` (string): The name of the attribute - - `value` (any): The value of the attribute + /* + * + 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). + Each element of the resulting list is an attribute set with these attributes: + - `name` (string): The name of the attribute + - `value` (any): The value of the attribute - The following is always true: - ```nix - builtins.listToAttrs (attrsToList attrs) == attrs - ``` + The following is always true: + ```nix + builtins.listToAttrs (attrsToList attrs) == attrs + ``` - :::{.warning} - The opposite is not always true. In general expect that - ```nix - attrsToList (builtins.listToAttrs list) != list - ``` + :::{.warning} + The opposite is not always true. In general expect that + ```nix + attrsToList (builtins.listToAttrs list) != list + ``` - This is because the `listToAttrs` removes duplicate names and doesn't preserve the order of the list. - ::: + This is because the `listToAttrs` removes duplicate names and doesn't preserve the order of the list. + ::: - # Inputs + # Inputs - `set` + `set` - : The attribute set to deconstruct. + : The attribute set to deconstruct. - # Type + # Type - ``` - attrsToList :: AttrSet -> [ { name :: String; value :: Any; } ] - ``` + ``` + attrsToList :: AttrSet -> [ { name :: String; value :: Any; } ] + ``` - # Examples - :::{.example} - ## `lib.attrsets.attrsToList` usage example + # Examples + :::{.example} + ## `lib.attrsets.attrsToList` usage example - ```nix - attrsToList { foo = 1; bar = "asdf"; } - => [ { name = "bar"; value = "asdf"; } { name = "foo"; value = 1; } ] - ``` + ```nix + attrsToList { foo = 1; bar = "asdf"; } + => [ { name = "bar"; value = "asdf"; } { name = "foo"; value = 1; } ] + ``` - ::: + ::: */ attrsToList = mapAttrsToList nameValuePair; - /* * - 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. - Also, the first argument of the mapping function is a *list* of the attribute names that form the path to the leaf attribute. - - For a function that gives you control over what counts as a leaf, see `mapAttrsRecursiveCond`. - - :::{#map-attrs-recursive-example .example} - # Map over leaf attributes - - ```nix - mapAttrsRecursive (path: value: concatStringsSep "-" (path ++ [value])) - { n = { a = "A"; m = { b = "B"; c = "C"; }; }; d = "D"; } - ``` - evaluates to - ```nix - { n = { a = "n-a-A"; m = { b = "n-m-b-B"; c = "n-m-c-C"; }; }; d = "d-D"; } - ``` - ::: - - # Type - ``` - mapAttrsRecursive :: ([String] -> a -> b) -> AttrSet -> AttrSet - ``` + /* + * + 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. + Also, the first argument of the mapping function is a *list* of the attribute names that form the path to the leaf attribute. + + For a function that gives you control over what counts as a leaf, see `mapAttrsRecursiveCond`. + + :::{#map-attrs-recursive-example .example} + # Map over leaf attributes + + ```nix + mapAttrsRecursive (path: value: concatStringsSep "-" (path ++ [value])) + { n = { a = "A"; m = { b = "B"; c = "C"; }; }; d = "D"; } + ``` + evaluates to + ```nix + { n = { a = "n-a-A"; m = { b = "n-m-b-B"; c = "n-m-c-C"; }; }; d = "d-D"; } + ``` + ::: + + # Type + ``` + mapAttrsRecursive :: ([String] -> a -> b) -> AttrSet -> AttrSet + ``` */ mapAttrsRecursive = 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. - If the predicate returns false, `mapAttrsRecursiveCond` does not recurse, but instead applies the mapping function. - If the predicate returns true, it does recurse, and does not apply the mapping function. - - :::{#map-attrs-recursive-cond-example .example} - # Map over an leaf attributes defined by a condition - - Map derivations to their `name` attribute. - Derivatons are identified as attribute sets that contain `{ type = "derivation"; }`. - ```nix - mapAttrsRecursiveCond - (as: !(as ? "type" && as.type == "derivation")) - (x: x.name) - attrs - ``` - ::: - - # Type - ``` - mapAttrsRecursiveCond :: (AttrSet -> Bool) -> ([String] -> a -> b) -> AttrSet -> AttrSet - ``` + /* + * + Like `mapAttrsRecursive`, but it takes an additional predicate that tells it whether to recurse into an attribute set. + If the predicate returns false, `mapAttrsRecursiveCond` does not recurse, but instead applies the mapping function. + If the predicate returns true, it does recurse, and does not apply the mapping function. + + :::{#map-attrs-recursive-cond-example .example} + # Map over an leaf attributes defined by a condition + + Map derivations to their `name` attribute. + Derivatons are identified as attribute sets that contain `{ type = "derivation"; }`. + ```nix + mapAttrsRecursiveCond + (as: !(as ? "type" && as.type == "derivation")) + (x: x.name) + attrs + ``` + ::: + + # Type + ``` + mapAttrsRecursiveCond :: (AttrSet -> Bool) -> ([String] -> a -> b) -> AttrSet -> AttrSet + ``` */ - mapAttrsRecursiveCond = cond: f: set: - let - recurse = path: - mapAttrs (name: value: - if isAttrs value && cond value then - recurse (path ++ [ name ]) value - else - f (path ++ [ name ]) value); - in recurse [ ] set; + mapAttrsRecursiveCond = cond: f: set: let + recurse = path: + mapAttrs (name: value: + if isAttrs value && cond value + then recurse (path ++ [name]) value + else f (path ++ [name]) value); + in + recurse [] set; - /* * - Generate an attribute set by mapping a function over a list of - attribute names. + /* + * + Generate an attribute set by mapping a function over a list of + attribute names. - # Inputs + # Inputs - `names` + `names` - : Names of values in the resulting attribute set. + : Names of values in the resulting attribute set. - `f` + `f` - : A function, given the name of the attribute, returns the attribute's value. + : A function, given the name of the attribute, returns the attribute's value. - # Type + # Type - ``` - genAttrs :: [ String ] -> (String -> Any) -> AttrSet - ``` + ``` + genAttrs :: [ String ] -> (String -> Any) -> AttrSet + ``` - # Examples - :::{.example} - ## `lib.attrsets.genAttrs` usage example + # Examples + :::{.example} + ## `lib.attrsets.genAttrs` usage example - ```nix - genAttrs [ "foo" "bar" ] (name: "x_" + name) - => { foo = "x_foo"; bar = "x_bar"; } - ``` + ```nix + genAttrs [ "foo" "bar" ] (name: "x_" + name) + => { foo = "x_foo"; bar = "x_bar"; } + ``` - ::: + ::: */ genAttrs = names: f: listToAttrs (map (n: nameValuePair n (f n)) names); - /* * - Check whether the argument is a derivation. Any set with - `{ type = "derivation"; }` counts as a derivation. + /* + * + Check whether the argument is a derivation. Any set with + `{ type = "derivation"; }` counts as a derivation. - # Inputs + # Inputs - `value` + `value` - : Value to check. + : Value to check. - # Type + # Type - ``` - isDerivation :: Any -> Bool - ``` + ``` + isDerivation :: Any -> Bool + ``` - # Examples - :::{.example} - ## `lib.attrsets.isDerivation` usage example + # Examples + :::{.example} + ## `lib.attrsets.isDerivation` usage example - ```nix - nixpkgs = import {} - isDerivation nixpkgs.ruby - => true - isDerivation "foobar" - => false - ``` + ```nix + nixpkgs = import {} + isDerivation nixpkgs.ruby + => true + isDerivation "foobar" + => false + ``` - ::: + ::: */ isDerivation = 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` - : A store path to convert to a derivation. + : A store path to convert to a derivation. - # Type + # Type - ``` - toDerivation :: Path -> Derivation - ``` + ``` + toDerivation :: Path -> Derivation + ``` */ - toDerivation = path: - let - path' = builtins.storePath path; - res = { - type = "derivation"; - name = sanitizeDerivationName - (builtins.substring 33 (-1) (baseNameOf path')); - outPath = path'; - outputs = [ "out" ]; - out = res; - outputName = "out"; - }; - in res; - - /* * - If `cond` is true, return the attribute set `as`, - otherwise an empty attribute set. - - # Inputs - - `cond` - - : Condition under which the `as` attribute set is returned. - - `as` - - : The attribute set to return if `cond` is `true`. - - # Type - - ``` - optionalAttrs :: Bool -> AttrSet -> AttrSet - ``` - - # Examples - :::{.example} - ## `lib.attrsets.optionalAttrs` usage example - - ```nix - optionalAttrs (true) { my = "set"; } - => { my = "set"; } - optionalAttrs (false) { my = "set"; } - => { } - ``` - - ::: + toDerivation = path: let + path' = builtins.storePath path; + res = { + type = "derivation"; + name = + sanitizeDerivationName + (builtins.substring 33 (-1) (baseNameOf path')); + outPath = path'; + outputs = ["out"]; + out = res; + outputName = "out"; + }; + in + res; + + /* + * + If `cond` is true, return the attribute set `as`, + otherwise an empty attribute set. + + # Inputs + + `cond` + + : Condition under which the `as` attribute set is returned. + + `as` + + : The attribute set to return if `cond` is `true`. + + # Type + + ``` + optionalAttrs :: Bool -> AttrSet -> AttrSet + ``` + + # Examples + :::{.example} + ## `lib.attrsets.optionalAttrs` usage example + + ```nix + optionalAttrs (true) { my = "set"; } + => { my = "set"; } + optionalAttrs (false) { my = "set"; } + => { } + ``` + + ::: */ - optionalAttrs = cond: as: if cond then as else { }; + optionalAttrs = cond: as: + if cond + then as + else {}; - /* * - Merge sets of attributes and use the function `f` to merge attributes - values. + /* + * + Merge sets of attributes and use the function `f` to merge attributes + values. - # Inputs + # Inputs - `names` + `names` - : List of attribute names to zip. + : List of attribute names to zip. - `f` + `f` - : A function, accepts an attribute name, all the values, and returns a combined value. + : A function, accepts an attribute name, all the values, and returns a combined value. - `sets` + `sets` - : List of values from the list of attribute sets. + : List of values from the list of attribute sets. - # Type + # Type - ``` - zipAttrsWithNames :: [ String ] -> (String -> [ Any ] -> Any) -> [ AttrSet ] -> AttrSet - ``` + ``` + zipAttrsWithNames :: [ String ] -> (String -> [ Any ] -> Any) -> [ AttrSet ] -> AttrSet + ``` - # Examples - :::{.example} - ## `lib.attrsets.zipAttrsWithNames` usage example + # Examples + :::{.example} + ## `lib.attrsets.zipAttrsWithNames` usage example - ```nix - zipAttrsWithNames ["a"] (name: vs: vs) [{a = "x";} {a = "y"; b = "z";}] - => { a = ["x" "y"]; } - ``` + ```nix + zipAttrsWithNames ["a"] (name: vs: vs) [{a = "x";} {a = "y"; b = "z";}] + => { a = ["x" "y"]; } + ``` - ::: + ::: */ zipAttrsWithNames = names: f: sets: listToAttrs (map (name: { - inherit name; - value = f name (catAttrs name sets); - }) names); + inherit name; + value = f name (catAttrs name sets); + }) + names); - /* * - Merge sets of attributes and use the function f to merge attribute values. - Like `lib.attrsets.zipAttrsWithNames` with all key names are passed for `names`. + /* + * + Merge sets of attributes and use the function f to merge attribute values. + Like `lib.attrsets.zipAttrsWithNames` with all key names are passed for `names`. - Implementation note: Common names appear multiple times in the list of - names, hopefully this does not affect the system because the maximal - laziness avoid computing twice the same expression and `listToAttrs` does - not care about duplicated attribute names. + Implementation note: Common names appear multiple times in the list of + names, hopefully this does not affect the system because the maximal + laziness avoid computing twice the same expression and `listToAttrs` does + not care about duplicated attribute names. - # Type + # Type - ``` - zipAttrsWith :: (String -> [ Any ] -> Any) -> [ AttrSet ] -> AttrSet - ``` + ``` + zipAttrsWith :: (String -> [ Any ] -> Any) -> [ AttrSet ] -> AttrSet + ``` - # Examples - :::{.example} - ## `lib.attrsets.zipAttrsWith` usage example + # Examples + :::{.example} + ## `lib.attrsets.zipAttrsWith` usage example - ```nix - zipAttrsWith (name: values: values) [{a = "x";} {a = "y"; b = "z";}] - => { a = ["x" "y"]; b = ["z"]; } - ``` + ```nix + zipAttrsWith (name: values: values) [{a = "x";} {a = "y"; b = "z";}] + => { a = ["x" "y"]; b = ["z"]; } + ``` - ::: + ::: */ - zipAttrsWith = builtins.zipAttrsWith or (f: sets: - zipAttrsWithNames (concatMap attrNames sets) f sets); + zipAttrsWith = + 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. - Like `lib.attrsets.zipAttrsWith` with `(name: values: values)` as the function. + Like `lib.attrsets.zipAttrsWith` with `(name: values: values)` as the function. - # Type + # Type - ``` - zipAttrs :: [ AttrSet ] -> AttrSet - ``` + ``` + zipAttrs :: [ AttrSet ] -> AttrSet + ``` - # Examples - :::{.example} - ## `lib.attrsets.zipAttrs` usage example + # Examples + :::{.example} + ## `lib.attrsets.zipAttrs` usage example - ```nix - zipAttrs [{a = "x";} {a = "y"; b = "z";}] - => { a = ["x" "y"]; b = ["z"]; } - ``` + ```nix + zipAttrs [{a = "x";} {a = "y"; b = "z";}] + => { a = ["x" "y"]; b = ["z"]; } + ``` - ::: + ::: */ zipAttrs = zipAttrsWith (name: values: values); - /* * - Merge a list of attribute sets together using the `//` operator. - In case of duplicate attributes, values from later list elements take precedence over earlier ones. - 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). + /* + * + Merge a list of attribute sets together using the `//` operator. + In case of duplicate attributes, values from later list elements take precedence over earlier ones. + 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). - # Inputs + # Inputs - `list` + `list` - : 1\. Function argument + : 1\. Function argument - # Type + # Type - ``` - mergeAttrsList :: [ Attrs ] -> Attrs - ``` + ``` + mergeAttrsList :: [ Attrs ] -> Attrs + ``` - # Examples - :::{.example} - ## `lib.attrsets.mergeAttrsList` usage example + # Examples + :::{.example} + ## `lib.attrsets.mergeAttrsList` usage example - ```nix - mergeAttrsList [ { a = 0; b = 1; } { c = 2; d = 3; } ] - => { a = 0; b = 1; c = 2; d = 3; } - mergeAttrsList [ { a = 0; } { a = 1; } ] - => { a = 1; } - ``` + ```nix + mergeAttrsList [ { a = 0; b = 1; } { c = 2; d = 3; } ] + => { a = 0; b = 1; c = 2; d = 3; } + mergeAttrsList [ { a = 0; } { a = 1; } ] + => { a = 1; } + ``` - ::: + ::: */ - mergeAttrsList = list: - let - # `binaryMerge start end` merges the elements at indices `index` of `list` such that `start <= index < end` - # Type: Int -> Int -> Attrs - binaryMerge = start: end: - # assert start < end; # Invariant - if end - start >= 2 then + mergeAttrsList = list: let + # `binaryMerge start end` merges the elements at indices `index` of `list` such that `start <= index < end` + # Type: Int -> Int -> Attrs + binaryMerge = start: end: + # assert start < end; # Invariant + 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 # The invariant is satisfied because each half will have at least 1 element - binaryMerge start (start + (end - start) / 2) - // binaryMerge (start + (end - start) / 2) end - else + binaryMerge start (start + (end - start) / 2) + // binaryMerge (start + (end - start) / 2) end + else # Otherwise there will be exactly 1 element due to the invariant, in which case we just return it directly - elemAt list start; - in if list == [ ] then - # Calling binaryMerge as below would not satisfy its invariant - { } - else - binaryMerge 0 (length list); - - /* * - Does the same as the update operator '//' except that attributes are - merged until the given predicate is verified. The predicate should - accept 3 arguments which are the path to reach the attribute, a part of - the first attribute set and a part of the second attribute set. When - the predicate is satisfied, the value of the first attribute set is - replaced by the value of the second attribute set. - - # Inputs - - `pred` - - : Predicate, taking the path to the current attribute as a list of strings for attribute names, and the two values at that path from the original arguments. - - `lhs` - - : Left attribute set of the merge. - - `rhs` - - : Right attribute set of the merge. - - # Type - - ``` - recursiveUpdateUntil :: ( [ String ] -> AttrSet -> AttrSet -> Bool ) -> AttrSet -> AttrSet -> AttrSet - ``` - - # Examples - :::{.example} - ## `lib.attrsets.recursiveUpdateUntil` usage example - - ```nix - recursiveUpdateUntil (path: l: r: path == ["foo"]) { - # first attribute set - foo.bar = 1; - foo.baz = 2; - bar = 3; - } { - #second attribute set - foo.bar = 1; - foo.quz = 2; - baz = 4; - } - - => { - foo.bar = 1; # 'foo.*' from the second set - foo.quz = 2; # - bar = 3; # 'bar' from the first set - baz = 4; # 'baz' from the second set - } - ``` - - ::: + elemAt list start; + in + if list == [] + then + # Calling binaryMerge as below would not satisfy its invariant + {} + else binaryMerge 0 (length list); + + /* + * + Does the same as the update operator '//' except that attributes are + merged until the given predicate is verified. The predicate should + accept 3 arguments which are the path to reach the attribute, a part of + the first attribute set and a part of the second attribute set. When + the predicate is satisfied, the value of the first attribute set is + replaced by the value of the second attribute set. + + # Inputs + + `pred` + + : Predicate, taking the path to the current attribute as a list of strings for attribute names, and the two values at that path from the original arguments. + + `lhs` + + : Left attribute set of the merge. + + `rhs` + + : Right attribute set of the merge. + + # Type + + ``` + recursiveUpdateUntil :: ( [ String ] -> AttrSet -> AttrSet -> Bool ) -> AttrSet -> AttrSet -> AttrSet + ``` + + # Examples + :::{.example} + ## `lib.attrsets.recursiveUpdateUntil` usage example + + ```nix + recursiveUpdateUntil (path: l: r: path == ["foo"]) { + # first attribute set + foo.bar = 1; + foo.baz = 2; + bar = 3; + } { + #second attribute set + foo.bar = 1; + foo.quz = 2; + baz = 4; + } + + => { + foo.bar = 1; # 'foo.*' from the second set + foo.quz = 2; # + bar = 3; # 'bar' from the first set + baz = 4; # 'baz' from the second set + } + ``` + + ::: */ - recursiveUpdateUntil = pred: lhs: rhs: - let - f = attrPath: - zipAttrsWith (n: values: - let here = attrPath ++ [ n ]; - in if length values == 1 - || pred here (elemAt values 1) (head values) then - head values - else - f here values); - in f [ ] [ rhs lhs ]; - - /* * - A recursive variant of the update operator ‘//’. The recursion - stops when one of the attribute values is not an attribute set, - in which case the right hand side value takes precedence over the - left hand side value. - - # Inputs - - `lhs` - - : Left attribute set of the merge. - - `rhs` - - : Right attribute set of the merge. - - # Type - - ``` - recursiveUpdate :: AttrSet -> AttrSet -> AttrSet - ``` - - # Examples - :::{.example} - ## `lib.attrsets.recursiveUpdate` usage example - - ```nix - recursiveUpdate { - boot.loader.grub.enable = true; - boot.loader.grub.device = "/dev/hda"; - } { - boot.loader.grub.device = ""; - } - - returns: { - boot.loader.grub.enable = true; - boot.loader.grub.device = ""; - } - ``` - - ::: + recursiveUpdateUntil = pred: lhs: rhs: let + f = attrPath: + zipAttrsWith (n: values: let + here = attrPath ++ [n]; + in + if + length values + == 1 + || pred here (elemAt values 1) (head values) + then head values + else f here values); + in + f [] [rhs lhs]; + + /* + * + A recursive variant of the update operator ‘//’. The recursion + stops when one of the attribute values is not an attribute set, + in which case the right hand side value takes precedence over the + left hand side value. + + # Inputs + + `lhs` + + : Left attribute set of the merge. + + `rhs` + + : Right attribute set of the merge. + + # Type + + ``` + recursiveUpdate :: AttrSet -> AttrSet -> AttrSet + ``` + + # Examples + :::{.example} + ## `lib.attrsets.recursiveUpdate` usage example + + ```nix + recursiveUpdate { + boot.loader.grub.enable = true; + boot.loader.grub.device = "/dev/hda"; + } { + boot.loader.grub.device = ""; + } + + returns: { + boot.loader.grub.enable = true; + boot.loader.grub.device = ""; + } + ``` + + ::: */ recursiveUpdate = lhs: rhs: recursiveUpdateUntil (path: lhs: rhs: !(isAttrs lhs && isAttrs rhs)) lhs rhs; - /* * - Recurse into every attribute set of the first argument and check that: - - 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. + /* + * + Recurse into every attribute set of the first argument and check that: + - 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. - # Inputs + # Inputs - `pattern` + `pattern` - : Attribute set structure to match + : Attribute set structure to match - `attrs` + `attrs` - : Attribute set to check + : Attribute set to check - # Type + # Type - ``` - matchAttrs :: AttrSet -> AttrSet -> Bool - ``` + ``` + matchAttrs :: AttrSet -> AttrSet -> Bool + ``` - # Examples - :::{.example} - ## `lib.attrsets.matchAttrs` usage example + # Examples + :::{.example} + ## `lib.attrsets.matchAttrs` usage example - ```nix - matchAttrs { cpu = {}; } { cpu = { bits = 64; }; } - => true - ``` + ```nix + matchAttrs { cpu = {}; } { cpu = { bits = 64; }; } + => true + ``` - ::: + ::: */ matchAttrs = pattern: attrs: assert isAttrs pattern; - all ( # Compare equality between `pattern` & `attrs`. - attr: - # Missing attr, not equal. - attrs ? ${attr} && (let - lhs = pattern.${attr}; - rhs = attrs.${attr}; - # If attrset check recursively - in if isAttrs lhs then isAttrs rhs && matchAttrs lhs rhs else lhs == rhs)) - (attrNames pattern); - - /* * - Override only the attributes that are already present in the old set - useful for deep-overriding. - - # Inputs - - `old` - - : Original attribute set - - `new` - - : Attribute set with attributes to override in `old`. - - # Type - - ``` - overrideExisting :: AttrSet -> AttrSet -> AttrSet - ``` - - # Examples - :::{.example} - ## `lib.attrsets.overrideExisting` usage example - - ```nix - overrideExisting {} { a = 1; } - => {} - overrideExisting { b = 2; } { a = 1; } - => { b = 2; } - overrideExisting { a = 3; b = 2; } { a = 1; } - => { a = 1; b = 2; } - ``` - - ::: + all ( # Compare equality between `pattern` & `attrs`. + attr: + # Missing attr, not equal. + attrs + ? ${attr} + && (let + lhs = pattern.${attr}; + rhs = attrs.${attr}; + # If attrset check recursively + in + if isAttrs lhs + then isAttrs rhs && matchAttrs lhs rhs + else lhs == rhs) + ) + (attrNames pattern); + + /* + * + Override only the attributes that are already present in the old set + useful for deep-overriding. + + # Inputs + + `old` + + : Original attribute set + + `new` + + : Attribute set with attributes to override in `old`. + + # Type + + ``` + overrideExisting :: AttrSet -> AttrSet -> AttrSet + ``` + + # Examples + :::{.example} + ## `lib.attrsets.overrideExisting` usage example + + ```nix + overrideExisting {} { a = 1; } + => {} + overrideExisting { b = 2; } { a = 1; } + => { b = 2; } + overrideExisting { a = 3; b = 2; } { a = 1; } + => { a = 1; b = 2; } + ``` + + ::: */ overrideExisting = old: new: mapAttrs (name: value: new.${name} or value) old; - /* * - Turns a list of strings into a human-readable description of those - strings represented as an attribute path. The result of this function is - not intended to be machine-readable. - Create a new attribute set with `value` set at the nested attribute location specified in `attrPath`. + /* + * + Turns a list of strings into a human-readable description of those + strings represented as an attribute path. The result of this function is + not intended to be machine-readable. + Create a new attribute set with `value` set at the nested attribute location specified in `attrPath`. - # Inputs + # Inputs - `path` + `path` - : Attribute path to render to a string + : Attribute path to render to a string - # Type + # Type - ``` - showAttrPath :: [String] -> String - ``` + ``` + showAttrPath :: [String] -> String + ``` - # Examples - :::{.example} - ## `lib.attrsets.showAttrPath` usage example + # Examples + :::{.example} + ## `lib.attrsets.showAttrPath` usage example - ```nix - showAttrPath [ "foo" "10" "bar" ] - => "foo.\"10\".bar" - showAttrPath [] - => "" - ``` + ```nix + showAttrPath [ "foo" "10" "bar" ] + => "foo.\"10\".bar" + showAttrPath [] + => "" + ``` - ::: + ::: */ showAttrPath = path: - if path == [ ] then - "" - else - concatMapStringsSep "." escapeNixIdentifier path; + if path == [] + then "" + else concatMapStringsSep "." escapeNixIdentifier path; - /* * - Get a package output. - If no output is found, fallback to `.out` and then to the default. + /* + * + Get a package output. + If no output is found, fallback to `.out` and then to the default. - # Inputs + # Inputs - `output` + `output` - : 1\. Function argument + : 1\. Function argument - `pkg` + `pkg` - : 2\. Function argument + : 2\. Function argument - # Type + # Type - ``` - getOutput :: String -> Derivation -> String - ``` + ``` + getOutput :: String -> Derivation -> String + ``` - # Examples - :::{.example} - ## `lib.attrsets.getOutput` usage example + # Examples + :::{.example} + ## `lib.attrsets.getOutput` usage example - ```nix - getOutput "dev" pkgs.openssl - => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r-dev" - ``` + ```nix + getOutput "dev" pkgs.openssl + => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r-dev" + ``` - ::: + ::: */ getOutput = output: pkg: - if !pkg ? outputSpecified || !pkg.outputSpecified then - pkg.${output} or pkg.out or pkg - else - pkg; + if !pkg ? outputSpecified || !pkg.outputSpecified + then pkg.${output} or pkg.out or pkg + else pkg; - /* * - Get a package's `bin` output. - If the output does not exist, fallback to `.out` and then to the default. + /* + * + Get a package's `bin` output. + If the output does not exist, fallback to `.out` and then to the default. - # Inputs + # Inputs - `pkg` + `pkg` - : The package whose `bin` output will be retrieved. + : The package whose `bin` output will be retrieved. - # Type + # Type - ``` - getBin :: Derivation -> String - ``` + ``` + getBin :: Derivation -> String + ``` - # Examples - :::{.example} - ## `lib.attrsets.getBin` usage example + # Examples + :::{.example} + ## `lib.attrsets.getBin` usage example - ```nix - getBin pkgs.openssl - => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r" - ``` + ```nix + getBin pkgs.openssl + => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r" + ``` - ::: + ::: */ getBin = getOutput "bin"; - /* * - Get a package's `lib` output. - If the output does not exist, fallback to `.out` and then to the default. + /* + * + Get a package's `lib` output. + If the output does not exist, fallback to `.out` and then to the default. - # Inputs + # Inputs - `pkg` + `pkg` - : The package whose `lib` output will be retrieved. + : The package whose `lib` output will be retrieved. - # Type + # Type - ``` - getLib :: Derivation -> String - ``` + ``` + getLib :: Derivation -> String + ``` - # Examples - :::{.example} - ## `lib.attrsets.getLib` usage example + # Examples + :::{.example} + ## `lib.attrsets.getLib` usage example - ```nix - getLib pkgs.openssl - => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r-lib" - ``` + ```nix + getLib pkgs.openssl + => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r-lib" + ``` - ::: + ::: */ getLib = getOutput "lib"; - /* * - Get a package's `dev` output. - If the output does not exist, fallback to `.out` and then to the default. + /* + * + Get a package's `dev` output. + If the output does not exist, fallback to `.out` and then to the default. - # Inputs + # Inputs - `pkg` + `pkg` - : The package whose `dev` output will be retrieved. + : The package whose `dev` output will be retrieved. - # Type + # Type - ``` - getDev :: Derivation -> String - ``` + ``` + getDev :: Derivation -> String + ``` - # Examples - :::{.example} - ## `lib.attrsets.getDev` usage example + # Examples + :::{.example} + ## `lib.attrsets.getDev` usage example - ```nix - getDev pkgs.openssl - => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r-dev" - ``` + ```nix + getDev pkgs.openssl + => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r-dev" + ``` - ::: + ::: */ getDev = getOutput "dev"; - /* * - Get a package's `man` output. - If the output does not exist, fallback to `.out` and then to the default. + /* + * + Get a package's `man` output. + If the output does not exist, fallback to `.out` and then to the default. - # Inputs + # Inputs - `pkg` + `pkg` - : The package whose `man` output will be retrieved. + : The package whose `man` output will be retrieved. - # Type + # Type - ``` - getMan :: Derivation -> String - ``` + ``` + getMan :: Derivation -> String + ``` - # Examples - :::{.example} - ## `lib.attrsets.getMan` usage example + # Examples + :::{.example} + ## `lib.attrsets.getMan` usage example - ```nix - getMan pkgs.openssl - => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r-man" - ``` + ```nix + getMan pkgs.openssl + => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r-man" + ``` - ::: + ::: */ getMan = getOutput "man"; - /* * - Pick the outputs of packages to place in `buildInputs` + /* + * + Pick the outputs of packages to place in `buildInputs` - # Inputs + # Inputs - `pkgs` + `pkgs` - : List of packages. + : List of packages. - # Type + # Type - ``` - chooseDevOutputs :: [Derivation] -> [String] - ``` + ``` + chooseDevOutputs :: [Derivation] -> [String] + ``` */ chooseDevOutputs = builtins.map getDev; - /* * - Make various Nix tools consider the contents of the resulting - attribute set when looking for what to build, find, etc. + /* + * + Make various Nix tools consider the contents of the resulting + attribute set when looking for what to build, find, etc. - This function only affects a single attribute set; it does not - apply itself recursively for nested attribute sets. + This function only affects a single attribute set; it does not + apply itself recursively for nested attribute sets. - # Inputs + # Inputs - `attrs` + `attrs` - : An attribute set to scan for derivations. + : An attribute set to scan for derivations. - # Type + # Type - ``` - recurseIntoAttrs :: AttrSet -> AttrSet - ``` + ``` + recurseIntoAttrs :: AttrSet -> AttrSet + ``` - # Examples - :::{.example} - ## `lib.attrsets.recurseIntoAttrs` usage example + # Examples + :::{.example} + ## `lib.attrsets.recurseIntoAttrs` usage example - ```nix - { pkgs ? import {} }: - { - myTools = pkgs.lib.recurseIntoAttrs { - inherit (pkgs) hello figlet; - }; - } - ``` + ```nix + { pkgs ? import {} }: + { + myTools = pkgs.lib.recurseIntoAttrs { + inherit (pkgs) hello figlet; + }; + } + ``` - ::: + ::: */ - recurseIntoAttrs = attrs: attrs // { recurseForDerivations = true; }; + recurseIntoAttrs = attrs: attrs // {recurseForDerivations = true;}; - /* * - Undo the effect of recurseIntoAttrs. + /* + * + Undo the effect of recurseIntoAttrs. - # Inputs + # Inputs - `attrs` + `attrs` - : An attribute set to not scan for derivations. + : An attribute set to not scan for derivations. - # Type + # Type - ``` - dontRecurseIntoAttrs :: AttrSet -> AttrSet - ``` + ``` + dontRecurseIntoAttrs :: AttrSet -> AttrSet + ``` */ - dontRecurseIntoAttrs = attrs: attrs // { recurseForDerivations = false; }; + dontRecurseIntoAttrs = attrs: attrs // {recurseForDerivations = false;}; - /* * - `unionOfDisjoint x y` is equal to `x // y // z` where the - attrnames in `z` are the intersection of the attrnames in `x` and - `y`, and all values `assert` with an error message. This - operator is commutative, unlike (//). + /* + * + `unionOfDisjoint x y` is equal to `x // y // z` where the + attrnames in `z` are the intersection of the attrnames in `x` and + `y`, and all values `assert` with an error message. This + operator is commutative, unlike (//). - # Inputs + # Inputs - `x` + `x` - : 1\. Function argument + : 1\. Function argument - `y` + `y` - : 2\. Function argument + : 2\. Function argument - # Type + # Type - ``` - unionOfDisjoint :: AttrSet -> AttrSet -> AttrSet - ``` + ``` + unionOfDisjoint :: AttrSet -> AttrSet -> AttrSet + ``` */ - unionOfDisjoint = x: y: - let - intersection = builtins.intersectAttrs x y; - collisions = lib.concatStringsSep " " (builtins.attrNames intersection); - mask = builtins.mapAttrs (name: value: - builtins.throw - "unionOfDisjoint: collision on ${name}; complete list: ${collisions}") - intersection; - in (x // y) // mask; + unionOfDisjoint = x: y: let + intersection = builtins.intersectAttrs x y; + collisions = lib.concatStringsSep " " (builtins.attrNames intersection); + mask = builtins.mapAttrs (name: value: + builtins.throw + "unionOfDisjoint: collision on ${name}; complete list: ${collisions}") + intersection; + in + (x // y) // mask; # DEPRECATED zipWithNames = @@ -1884,7 +1993,8 @@ in rec { zip = warn "lib.zip is a deprecated alias of lib.zipAttrsWith." zipAttrsWith; # DEPRECATED - cartesianProductOfSets = warnIf (isInOldestRelease 2405) + cartesianProductOfSets = + warnIf (isInOldestRelease 2405) "lib.cartesianProductOfSets is a deprecated alias of lib.cartesianProduct." cartesianProduct; } diff --git a/shell.nix b/shell.nix deleted file mode 100644 index 6dd862c..0000000 --- a/shell.nix +++ /dev/null @@ -1 +0,0 @@ -{ mkShellNoCC, nixfmt, ... }: mkShellNoCC { packages = [ nixfmt ]; } diff --git a/tests/auxlib/default.nix b/tests/auxlib/default.nix deleted file mode 100644 index 0b982a6..0000000 --- a/tests/auxlib/default.nix +++ /dev/null @@ -1,16 +0,0 @@ -{ -# The pkgs used for dependencies for the testing itself -# Don't test properties of pkgs.lib, but rather the lib in the parent directory -pkgs ? import { } // { - lib = throw - "pkgs.lib accessed, but the lib tests should use nixpkgs' lib path directly!"; -}, nix ? pkgs-nixVersions.stable -, nixVersions ? [ pkgs-nixVersions.minimum nix pkgs-nixVersions.unstable ] -, pkgs-nixVersions ? import ../nix-for-tests.nix { inherit pkgs; }, }: -let - lib = import ../../nix; - testWithNix = nix: import ./test-with-nix.nix { inherit lib nix pkgs; }; -in pkgs.symlinkJoin { - name = "auxlib-tests"; - paths = map testWithNix nixVersions; -} diff --git a/tests/auxlib/test-with-nix.nix b/tests/auxlib/test-with-nix.nix deleted file mode 100644 index 1fec2fd..0000000 --- a/tests/auxlib/test-with-nix.nix +++ /dev/null @@ -1,13 +0,0 @@ -# * Runs all library tests with a particular version of Nix. -{ pkgs, lib, libPath ? ../../., -# Only ever use this nix; see comment at top -nix, }: -pkgs.runCommand "auxlib-tests-nix-${nix.version}" { - buildInputs = [ - # TODO: Tests! - ]; - strictDeps = true; -} '' - mkdir $out - echo success > $out/${nix.version} -'' diff --git a/tests/default.nix b/tests/default.nix deleted file mode 100644 index b3531fd..0000000 --- a/tests/default.nix +++ /dev/null @@ -1,9 +0,0 @@ -{ pkgs ? import { } // { - lib = throw - "pkgs.lib accessed, but the lib tests should use nixpkgs' lib path directly!"; -} }: -pkgs.symlinkJoin { - name = "nixpkgs-lib-tests"; - paths = - [ (import ./stdlib { inherit pkgs; }) (import ./auxlib { inherit pkgs; }) ]; -} diff --git a/tests/formatting.nix b/tests/formatting.nix deleted file mode 100644 index 8af9cc9..0000000 --- a/tests/formatting.nix +++ /dev/null @@ -1,5 +0,0 @@ -{ runCommand, nixfmt }: -runCommand "aux-lib-formatting" { buildInputs = [ nixfmt ]; } '' - find ${../.} -iname '*.nix' -type f -print0 | xargs -0 -i nixfmt -c {} - touch $out -'' diff --git a/tests/stdlib/default.nix b/tests/stdlib/default.nix deleted file mode 100644 index d5397e9..0000000 --- a/tests/stdlib/default.nix +++ /dev/null @@ -1,16 +0,0 @@ -{ -# The pkgs used for dependencies for the testing itself -# Don't test properties of pkgs.lib, but rather the lib in the parent directory -pkgs ? import { } // { - lib = throw - "pkgs.lib accessed, but the lib tests should use nixpkgs' lib path directly!"; -}, nix ? pkgs-nixVersions.stable -, nixVersions ? [ pkgs-nixVersions.minimum nix pkgs-nixVersions.unstable ] -, pkgs-nixVersions ? import ../nix-for-tests.nix { inherit pkgs; }, }: -let - lib = import ../../nix/stdlib; - testWithNix = nix: import ./test-with-nix.nix { inherit lib nix pkgs; }; -in pkgs.symlinkJoin { - name = "stdlib-tests"; - paths = map testWithNix nixVersions; -} diff --git a/tests/stdlib/modules/default.nix b/tests/stdlib/modules/default.nix deleted file mode 100644 index 0b947a5..0000000 --- a/tests/stdlib/modules/default.nix +++ /dev/null @@ -1,7 +0,0 @@ -{ lib ? import ../../../nix/stdlib, modules ? [ ], }: { - inherit (lib.evalModules { - inherit modules; - specialArgs.modulesPath = ./.; - }) - config options; -} diff --git a/tests/stdlib/path/default.nix b/tests/stdlib/path/default.nix deleted file mode 100644 index a24e7af..0000000 --- a/tests/stdlib/path/default.nix +++ /dev/null @@ -1,33 +0,0 @@ -{ pkgs ? import { } // { - lib = throw - "pkgs.lib accessed, but the lib tests should use nixpkgs' lib path directly!"; -}, nix ? pkgs.nixVersions.stable, libPath ? ../../../nix/stdlib, -# Random seed -seed ? null, }: -pkgs.runCommand "lib-path-tests" { - nativeBuildInputs = [ nix ] ++ (with pkgs; [ jq bc ]); -} '' - # Needed to make Nix evaluation work - export TEST_ROOT=$(pwd)/test-tmp - export NIX_BUILD_HOOK= - export NIX_CONF_DIR=$TEST_ROOT/etc - export NIX_LOCALSTATE_DIR=$TEST_ROOT/var - export NIX_LOG_DIR=$TEST_ROOT/var/log/nix - export NIX_STATE_DIR=$TEST_ROOT/var/nix - export NIX_STORE_DIR=$TEST_ROOT/store - export PAGER=cat - - echo ${libPath} - cp -r ${libPath} lib - export TEST_LIB=$PWD/lib - - echo "Running unit tests lib/tests/stdlib/path/unit.nix" - nix-instantiate --eval --show-trace \ - --argstr libpath "$TEST_LIB" \ - lib/tests/stdlib/path/unit.nix - - echo "Running property tests lib/tests/stdlib/path/prop.sh" - bash lib/tests/stdlib/path/prop.sh ${toString seed} - - touch $out -'' diff --git a/tests/stdlib/test-with-nix.nix b/tests/stdlib/test-with-nix.nix deleted file mode 100644 index f6d4d8e..0000000 --- a/tests/stdlib/test-with-nix.nix +++ /dev/null @@ -1,49 +0,0 @@ -# * Runs all library tests with a particular version of Nix. -{ pkgs, lib, libPath ? ../../., -# Only ever use this nix; see comment at top -nix, }: -pkgs.runCommand "stdlib-tests-nix-${nix.version}" { - buildInputs = [ - (import ./check-eval.nix lib) - (import ./path { inherit pkgs libPath nix; }) - ]; - nativeBuildInputs = [ nix pkgs.gitMinimal ] - ++ lib.optional pkgs.stdenv.isLinux pkgs.inotify-tools; - strictDeps = true; -} '' - datadir="${nix}/share" - export TEST_ROOT=$(pwd)/test-tmp - export HOME=$(mktemp -d) - export NIX_BUILD_HOOK= - export NIX_CONF_DIR=$TEST_ROOT/etc - export NIX_LOCALSTATE_DIR=$TEST_ROOT/var - export NIX_LOG_DIR=$TEST_ROOT/var/log/nix - export NIX_STATE_DIR=$TEST_ROOT/var/nix - export NIX_STORE_DIR=$TEST_ROOT/store - export PAGER=cat - cacheDir=$TEST_ROOT/binary-cache - - nix-store --init - - cp -r ${libPath} lib - echo "Running lib/tests/stdlib/modules.sh" - bash lib/tests/stdlib/modules.sh - - echo "Checking lib.version" - nix-instantiate lib -A version --eval || { - echo "lib.version does not evaluate when lib is isolated from the rest of the nixpkgs tree" - exit 1 - } - - echo "Running lib/tests/stdlib/filesystem.sh" - TEST_LIB=$PWD/lib bash lib/tests/stdlib/filesystem.sh - - echo "Running lib/tests/stdlib/sources.sh" - TEST_LIB=$PWD/lib bash lib/tests/stdlib/sources.sh - - echo "Running lib/tests/stdlib/fileset.sh" - TEST_LIB=$PWD/lib bash lib/tests/stdlib/fileset.sh - - mkdir $out - echo success > $out/${nix.version} -'' From ad0cc6c2a38738a2b1f0bd23c0bbdf0f16e0453b Mon Sep 17 00:00:00 2001 From: tcmal Date: Thu, 16 May 2024 15:46:33 +0100 Subject: [PATCH 2/4] reformatting for newer version of nixfmt --- extra/default.nix | 7 +- extra/doc/sections.nix | 9 +- extra/npins/default.nix | 48 +- extra/tests/formatting.nix | 6 +- extra/tests/nix-for-tests.nix | 6 +- extra/tests/stdlib/check-eval.nix | 8 +- extra/tests/stdlib/misc.nix | 2461 ++++++++++----- ...adhoc-freeformType-survives-type-merge.nix | 12 +- .../alias-with-priority-can-override.nix | 26 +- .../stdlib/modules/alias-with-priority.nix | 26 +- .../modules/attrsOf-conditional-check.nix | 3 +- .../stdlib/modules/attrsOf-lazy-check.nix | 3 +- extra/tests/stdlib/modules/boolByOr.nix | 27 +- extra/tests/stdlib/modules/class-check.nix | 36 +- .../stdlib/modules/declaration-positions.nix | 46 +- .../tests/stdlib/modules/declare-attrsOf.nix | 10 +- .../modules/declare-attrsOfSub-any-enable.nix | 26 +- ...e-bare-submodule-deep-option-duplicate.nix | 6 +- .../declare-bare-submodule-deep-option.nix | 6 +- .../declare-bare-submodule-nested-option.nix | 20 +- .../stdlib/modules/declare-bare-submodule.nix | 6 +- extra/tests/stdlib/modules/declare-either.nix | 6 +- .../declare-int-positive-value-nested.nix | 4 +- .../modules/declare-int-positive-value.nix | 4 +- .../modules/declare-int-unsigned-value.nix | 4 +- .../stdlib/modules/declare-lazyAttrsOf.nix | 6 +- .../modules/declare-mkPackageOption.nix | 25 +- extra/tests/stdlib/modules/declare-oneOf.nix | 3 +- extra/tests/stdlib/modules/declare-set.nix | 4 +- .../declare-submodule-via-evalModules.nix | 40 +- .../modules/declare-submoduleWith-modules.nix | 32 +- .../declare-submoduleWith-noshorthand.nix | 3 +- .../modules/declare-submoduleWith-path.nix | 3 +- .../declare-submoduleWith-shorthand.nix | 3 +- .../modules/declare-submoduleWith-special.nix | 14 +- .../tests/stdlib/modules/declare-variants.nix | 6 +- .../stdlib/modules/deferred-module-error.nix | 26 +- .../tests/stdlib/modules/deferred-module.nix | 66 +- .../modules/define-_module-args-custom.nix | 4 +- .../modules/define-enable-with-custom-arg.nix | 4 +- .../define-freeform-keywords-shorthand.nix | 28 +- .../define-option-dependently-nested.nix | 10 +- .../modules/define-option-dependently.nix | 10 +- .../modules/define-settingsDict-a-is-b.nix | 5 +- .../define-value-string-properties.nix | 11 +- extra/tests/stdlib/modules/define-variant.nix | 19 +- .../stdlib/modules/disable-enable-modules.nix | 5 +- .../stdlib/modules/disable-module-bad-key.nix | 11 +- .../modules/disable-module-with-key.nix | 20 +- .../disable-module-with-toString-key.nix | 20 +- .../modules/disable-recursive/disable-bar.nix | 1 - .../modules/disable-recursive/disable-foo.nix | 1 - .../stdlib/modules/disable-recursive/main.nix | 5 +- extra/tests/stdlib/modules/doRename-basic.nix | 22 +- .../modules/doRename-condition-enable.nix | 12 +- .../modules/doRename-condition-migrated.nix | 11 +- .../modules/doRename-condition-no-enable.nix | 11 +- .../stdlib/modules/doRename-condition.nix | 55 +- .../stdlib/modules/doRename-warnings.nix | 14 +- extra/tests/stdlib/modules/docs.nix | 26 +- extra/tests/stdlib/modules/emptyValues.nix | 17 +- .../modules/error-mkOption-in-config.nix | 6 +- .../error-mkOption-in-submodule-config.nix | 6 +- .../error-nonEmptyListOf-submodule.nix | 3 +- .../modules/extendModules-168767-imports.nix | 27 +- .../tests/stdlib/modules/freeform-attrsOf.nix | 3 +- .../stdlib/modules/freeform-lazyAttrsOf.nix | 3 +- .../tests/stdlib/modules/freeform-nested.nix | 10 +- .../stdlib/modules/freeform-str-dep-unstr.nix | 3 +- .../stdlib/modules/freeform-submodules.nix | 14 +- .../stdlib/modules/freeform-unstr-dep-str.nix | 3 +- .../stdlib/modules/functionTo/list-order.nix | 24 +- .../modules/functionTo/merging-attrs.nix | 20 +- .../modules/functionTo/merging-list.nix | 23 +- .../modules/functionTo/submodule-options.nix | 60 +- .../stdlib/modules/functionTo/trivial.nix | 6 +- .../stdlib/modules/functionTo/wrong-type.nix | 6 +- extra/tests/stdlib/modules/gvariant.nix | 56 +- .../stdlib/modules/import-configuration.nix | 6 +- .../stdlib/modules/import-from-store.nix | 5 +- .../stdlib/modules/merge-module-with-key.nix | 34 +- .../stdlib/modules/merge-typeless-option.nix | 15 +- .../modules/module-argument-default.nix | 11 +- .../modules/module-imports-_type-check.nix | 2 +- extra/tests/stdlib/modules/optionTypeFile.nix | 8 +- .../stdlib/modules/optionTypeMerging.nix | 8 +- .../options-type-error-configuration.nix | 3 +- .../options-type-error-typical-nested.nix | 7 +- .../modules/options-type-error-typical.nix | 7 +- extra/tests/stdlib/modules/raw.nix | 18 +- extra/tests/stdlib/modules/shorthand-meta.nix | 29 +- extra/tests/stdlib/modules/submoduleFiles.nix | 25 +- .../test-mergeAttrDefinitionsWithPrio.nix | 18 +- .../types-anything/attrs-coercible.nix | 4 +- .../modules/types-anything/equal-atoms.nix | 4 +- .../modules/types-anything/functions.nix | 7 +- .../stdlib/modules/types-anything/lists.nix | 9 +- .../stdlib/modules/types-anything/mk-mods.nix | 8 +- .../modules/types-anything/nested-attrs.nix | 4 +- .../modules/types-attrTag-wrong-decl.nix | 10 +- extra/tests/stdlib/modules/types-attrTag.nix | 166 +- extra/tests/stdlib/modules/types-unique.nix | 33 +- extra/tests/stdlib/modules/types.nix | 15 +- extra/tests/stdlib/path/prop.nix | 48 +- extra/tests/stdlib/path/unit.nix | 42 +- extra/tests/stdlib/systems.nix | 146 +- flake.nix | 10 +- nix/default.nix | 3 +- nix/stdlib/asserts.nix | 175 +- nix/stdlib/attrsets.nix | 2765 +++++++++-------- nix/stdlib/cli.nix | 156 +- nix/stdlib/customisation.nix | 883 +++--- nix/stdlib/debug.nix | 318 +- nix/stdlib/default.nix | 506 ++- nix/stdlib/deprecated/misc.nix | 460 ++- nix/stdlib/derivations.nix | 256 +- nix/stdlib/fetchers.nix | 3 +- nix/stdlib/fileset/default.nix | 1099 +++---- nix/stdlib/fileset/internal.nix | 835 ++--- nix/stdlib/fileset/mock-splitRoot.nix | 19 +- nix/stdlib/filesystem.nix | 390 +-- nix/stdlib/fixed-points.nix | 478 +-- nix/stdlib/flake-version-info.nix | 7 +- nix/stdlib/flake.nix | 11 +- nix/stdlib/generators.nix | 669 ++-- nix/stdlib/gvariant.nix | 341 +- nix/stdlib/kernel.nix | 9 +- nix/stdlib/licenses.nix | 2663 ++++++++-------- nix/stdlib/lists.nix | 2388 +++++++------- nix/stdlib/meta.nix | 252 +- nix/stdlib/modules.nix | 1989 +++++++----- nix/stdlib/options.nix | 712 +++-- nix/stdlib/path/default.nix | 736 ++--- nix/stdlib/source-types.nix | 7 +- nix/stdlib/sources.nix | 403 ++- nix/stdlib/strings-with-deps.nix | 124 +- nix/stdlib/strings.nix | 1992 ++++++------ nix/stdlib/systems/architectures.nix | 334 +- nix/stdlib/systems/default.nix | 804 ++--- nix/stdlib/systems/doubles.nix | 54 +- nix/stdlib/systems/examples.nix | 78 +- nix/stdlib/systems/flake-systems.nix | 3 +- nix/stdlib/systems/inspect.nix | 359 ++- nix/stdlib/systems/parse.nix | 1120 ++++--- nix/stdlib/systems/platforms.nix | 39 +- nix/stdlib/trivial.nix | 1220 ++++---- nix/stdlib/types.nix | 1354 ++++---- nix/stdlib/versions.nix | 74 +- 148 files changed, 17354 insertions(+), 13078 deletions(-) diff --git a/extra/default.nix b/extra/default.nix index 699a17d..4d45956 100644 --- a/extra/default.nix +++ b/extra/default.nix @@ -1,9 +1,10 @@ +{ + nixpkgs ? (import ./npins).nixpkgs, +}: let - sources = import ./npins; - libSrc = ../.; lib = import libSrc; - pkgs = import sources.nixpkgs { + pkgs = import nixpkgs { overlays = [ # update nixfmt, as nixpkgs is pretty out of date ( diff --git a/extra/doc/sections.nix b/extra/doc/sections.nix index 8dc49d8..0225018 100644 --- a/extra/doc/sections.nix +++ b/extra/doc/sections.nix @@ -1,4 +1,8 @@ -auxlib: let libPath = "${auxlib}/nix"; in [ +auxlib: +let + libPath = "${auxlib}/nix"; +in +[ { name = "asserts"; description = "assertion functions"; @@ -76,8 +80,7 @@ auxlib: let libPath = "${auxlib}/nix"; in [ } { name = "customisation"; - description = - "Functions to customise (derivation-related) functions, derivatons, or attribute sets"; + description = "Functions to customise (derivation-related) functions, derivatons, or attribute sets"; path = "${libPath}/stdlib/customisation.nix"; } { diff --git a/extra/npins/default.nix b/extra/npins/default.nix index 4a7c372..ae29155 100644 --- a/extra/npins/default.nix +++ b/extra/npins/default.nix @@ -3,18 +3,32 @@ let data = builtins.fromJSON (builtins.readFile ./sources.json); version = data.version; - mkSource = spec: - assert spec ? type; let + mkSource = + spec: + assert spec ? type; + let path = - if spec.type == "Git" then mkGitSource spec - else if spec.type == "GitRelease" then mkGitSource spec - else if spec.type == "PyPi" then mkPyPiSource spec - else if spec.type == "Channel" then mkChannelSource spec - else builtins.throw "Unknown source type ${spec.type}"; + if spec.type == "Git" then + mkGitSource spec + else if spec.type == "GitRelease" then + mkGitSource spec + else if spec.type == "PyPi" then + mkPyPiSource spec + else if spec.type == "Channel" then + mkChannelSource spec + else + builtins.throw "Unknown source type ${spec.type}"; in spec // { outPath = path; }; - mkGitSource = { repository, revision, url ? null, hash, ... }: + mkGitSource = + { + repository, + revision, + url ? null, + hash, + ... + }: assert repository ? type; # At the moment, either it is a plain git repository (which has an url), or it is a GitHub/GitLab repository # In the latter case, there we will always be an url to the tarball @@ -23,19 +37,23 @@ let inherit url; sha256 = hash; # FIXME: check nix version & use SRI hashes }) - else assert repository.type == "Git"; builtins.fetchGit { - url = repository.url; - rev = revision; - # hash = hash; - }; + else + assert repository.type == "Git"; + builtins.fetchGit { + url = repository.url; + rev = revision; + # hash = hash; + }; - mkPyPiSource = { url, hash, ... }: + mkPyPiSource = + { url, hash, ... }: builtins.fetchurl { inherit url; sha256 = hash; }; - mkChannelSource = { url, hash, ... }: + mkChannelSource = + { url, hash, ... }: builtins.fetchTarball { inherit url; sha256 = hash; diff --git a/extra/tests/formatting.nix b/extra/tests/formatting.nix index d4188d4..caabd94 100644 --- a/extra/tests/formatting.nix +++ b/extra/tests/formatting.nix @@ -1,4 +1,8 @@ -{ auxlib, runCommand, nixfmt }: +{ + auxlib, + runCommand, + nixfmt, +}: runCommand "aux-lib-formatting" { buildInputs = [ nixfmt ]; } '' find ${auxlib} -iname '*.nix' -type f -print0 | xargs -0 -i nixfmt -c {} touch $out diff --git a/extra/tests/nix-for-tests.nix b/extra/tests/nix-for-tests.nix index f19f71d..042d620 100644 --- a/extra/tests/nix-for-tests.nix +++ b/extra/tests/nix-for-tests.nix @@ -7,6 +7,6 @@ # CI. Bonus: the tests build way faster. # # See also: https://github.com/NixOS/nix/issues/7582 -builtins.mapAttrs (_: pkg: - if builtins.isAttrs pkg then pkg.override { withAWS = false; } else pkg) -pkgs.nixVersions +builtins.mapAttrs ( + _: pkg: if builtins.isAttrs pkg then pkg.override { withAWS = false; } else pkg +) pkgs.nixVersions diff --git a/extra/tests/stdlib/check-eval.nix b/extra/tests/stdlib/check-eval.nix index c546d39..3137ab1 100644 --- a/extra/tests/stdlib/check-eval.nix +++ b/extra/tests/stdlib/check-eval.nix @@ -1,6 +1,10 @@ # Throws an error if any of our lib tests fail. lib: let - tests = [ "misc" "systems" ]; + tests = [ + "misc" + "systems" + ]; all = builtins.concatLists (map (f: import (./. + "/${f}.nix") lib) tests); -in if all == [ ] then null else throw (builtins.toJSON all) +in +if all == [ ] then null else throw (builtins.toJSON all) diff --git a/extra/tests/stdlib/misc.nix b/extra/tests/stdlib/misc.nix index 21de3f2..2e8c4c5 100644 --- a/extra/tests/stdlib/misc.nix +++ b/extra/tests/stdlib/misc.nix @@ -1,30 +1,105 @@ -/* * - Nix evaluation tests for various lib functions. +/* + * + Nix evaluation tests for various lib functions. - Since these tests are implemented with Nix evaluation, - error checking is limited to what `builtins.tryEval` can detect, - which is `throw`'s and `abort`'s, without error messages. + Since these tests are implemented with Nix evaluation, + error checking is limited to what `builtins.tryEval` can detect, + which is `throw`'s and `abort`'s, without error messages. - If you need to test error messages or more complex evaluations, see - `lib/tests/modules.sh`, `lib/tests/sources.sh` or `lib/tests/filesystem.sh` as examples. + If you need to test error messages or more complex evaluations, see + `lib/tests/modules.sh`, `lib/tests/sources.sh` or `lib/tests/filesystem.sh` as examples. */ lib: let inherit (lib) - allUnique and attrNames attrsets attrsToList bitAnd bitOr bitXor - boolToString callPackagesWith callPackageWith cartesianProduct cli - composeExtensions composeManyExtensions concatLines concatMapAttrs - concatMapStrings concatStrings concatStringsSep const escapeXML evalModules - filter fix fold foldAttrs foldl foldl' foldlAttrs foldr functionArgs - generators genList getExe getExe' groupBy groupBy' hasAttrByPath hasInfix id - ifilter0 isStorePath lazyDerivation length lists listToAttrs makeExtensible - makeIncludePath makeOverridable mapAttrs mapCartesianProduct matchAttrs - mergeAttrs meta mod nameValuePair optionalDrvAttr optionAttrSetToDocList - overrideExisting packagesFromDirectoryRecursive pipe range - recursiveUpdateUntil removePrefix replicate runTests setFunctionArgs - showAttrPath sort sortOn stringLength strings stringToCharacters systems - tail take testAllTrue toBaseDigits toHexString toInt toIntBase10 toShellVars - types updateManyAttrsByPath versions xor; + allUnique + and + attrNames + attrsets + attrsToList + bitAnd + bitOr + bitXor + boolToString + callPackagesWith + callPackageWith + cartesianProduct + cli + composeExtensions + composeManyExtensions + concatLines + concatMapAttrs + concatMapStrings + concatStrings + concatStringsSep + const + escapeXML + evalModules + filter + fix + fold + foldAttrs + foldl + foldl' + foldlAttrs + foldr + functionArgs + generators + genList + getExe + getExe' + groupBy + groupBy' + hasAttrByPath + hasInfix + id + ifilter0 + isStorePath + lazyDerivation + length + lists + listToAttrs + makeExtensible + makeIncludePath + makeOverridable + mapAttrs + mapCartesianProduct + matchAttrs + mergeAttrs + meta + mod + nameValuePair + optionalDrvAttr + optionAttrSetToDocList + overrideExisting + packagesFromDirectoryRecursive + pipe + range + recursiveUpdateUntil + removePrefix + replicate + runTests + setFunctionArgs + showAttrPath + sort + sortOn + stringLength + strings + stringToCharacters + systems + tail + take + testAllTrue + toBaseDigits + toHexString + toInt + toIntBase10 + toShellVars + types + updateManyAttrsByPath + versions + xor + ; testingThrow = expr: { expr = builtins.tryEval (builtins.seq expr "didn't throw"); @@ -38,23 +113,35 @@ let expected = true; }; - testSanitizeDerivationName = { name, expected, }: + testSanitizeDerivationName = + { name, expected }: let drv = derivation { name = strings.sanitizeDerivationName name; builder = "x"; system = "x"; }; - in { + in + { # Evaluate the derivation so an invalid name would be caught expr = builtins.seq drv.drvPath drv.name; inherit expected; }; -in runTests { +in +runTests { # CUSTOMIZATION testFunctionArgsMakeOverridable = { - expr = functionArgs (makeOverridable ({ a, b, c ? null, }: { })); + expr = functionArgs ( + makeOverridable ( + { + a, + b, + c ? null, + }: + { } + ) + ); expected = { a = false; b = false; @@ -63,10 +150,22 @@ in runTests { }; testFunctionArgsMakeOverridableOverride = { - expr = functionArgs (makeOverridable ({ a, b, c ? null, }: { }) { - a = 1; - b = 2; - }).override; + expr = + functionArgs + (makeOverridable + ( + { + a, + b, + c ? null, + }: + { } + ) + { + a = 1; + b = 2; + } + ).override; expected = { a = false; b = false; @@ -74,27 +173,43 @@ in runTests { }; }; - testCallPackageWithOverridePreservesArguments = let - f = { a ? 0, b, }: { }; - f' = callPackageWith { - a = 1; - b = 2; - } f { }; - in { - expr = functionArgs f'.override; - expected = functionArgs f; - }; + testCallPackageWithOverridePreservesArguments = + let + f = + { + a ? 0, + b, + }: + { }; + f' = callPackageWith { + a = 1; + b = 2; + } f { }; + in + { + expr = functionArgs f'.override; + expected = functionArgs f; + }; - testCallPackagesWithOverridePreservesArguments = let - f = { a ? 0, b, }: { nested = { }; }; - f' = callPackagesWith { - a = 1; - b = 2; - } f { }; - in { - expr = functionArgs f'.nested.override; - expected = functionArgs f; - }; + testCallPackagesWithOverridePreservesArguments = + let + f = + { + a ? 0, + b, + }: + { + nested = { }; + }; + f' = callPackagesWith { + a = 1; + b = 2; + } f { }; + in + { + expr = functionArgs f'.nested.override; + expected = functionArgs f; + }; # TRIVIAL @@ -122,17 +237,28 @@ in runTests { }; testPipeStrings = { - expr = pipe [ 3 4 ] [ (map toString) (map (s: s + "\n")) concatStrings ]; + expr = + pipe + [ + 3 + 4 + ] + [ + (map toString) + (map (s: s + "\n")) + concatStrings + ]; expected = '' 3 4 ''; }; - /* testOr = { - expr = or true false; - expected = true; - }; + /* + testOr = { + expr = or true false; + expected = true; + }; */ testAnd = { @@ -141,54 +267,85 @@ in runTests { }; testXor = { - expr = - [ (xor true false) (xor true true) (xor false false) (xor false true) ]; - expected = [ true false false true ]; + expr = [ + (xor true false) + (xor true true) + (xor false false) + (xor false true) + ]; + expected = [ + true + false + false + true + ]; }; testFix = { - expr = fix (x: { a = if x ? a then "a" else "b"; }); - expected = { a = "a"; }; + expr = fix (x: { + a = if x ? a then "a" else "b"; + }); + expected = { + a = "a"; + }; }; testComposeExtensions = { - expr = let - obj = makeExtensible (self: { foo = self.bar; }); - f = self: super: { - bar = false; - baz = true; - }; - g = self: super: { bar = super.baz or false; }; - f_o_g = composeExtensions f g; - composed = obj.extend f_o_g; - in composed.foo; + expr = + let + obj = makeExtensible (self: { + foo = self.bar; + }); + f = self: super: { + bar = false; + baz = true; + }; + g = self: super: { bar = super.baz or false; }; + f_o_g = composeExtensions f g; + composed = obj.extend f_o_g; + in + composed.foo; expected = true; }; testComposeManyExtensions0 = { - expr = let - obj = makeExtensible (self: { foo = true; }); - emptyComposition = composeManyExtensions [ ]; - composed = obj.extend emptyComposition; - in composed.foo; + expr = + let + obj = makeExtensible (self: { + foo = true; + }); + emptyComposition = composeManyExtensions [ ]; + composed = obj.extend emptyComposition; + in + composed.foo; expected = true; }; - testComposeManyExtensions = let - f = self: super: { - bar = false; - baz = true; + testComposeManyExtensions = + let + f = self: super: { + bar = false; + baz = true; + }; + g = self: super: { bar = super.baz or false; }; + h = self: super: { qux = super.bar or false; }; + obj = makeExtensible (self: { + foo = self.qux; + }); + in + { + expr = + let + composition = composeManyExtensions [ + f + g + h + ]; + composed = obj.extend composition; + in + composed.foo; + expected = (obj.extend (composeExtensions f (composeExtensions g h))).foo; }; - g = self: super: { bar = super.baz or false; }; - h = self: super: { qux = super.bar or false; }; - obj = makeExtensible (self: { foo = self.qux; }); - in { - expr = let - composition = composeManyExtensions [ f g h ]; - composed = obj.extend composition; - in composed.foo; - expected = (obj.extend (composeExtensions f (composeExtensions g h))).foo; - }; testBitAnd = { expr = bitAnd 3 10; @@ -212,11 +369,15 @@ in runTests { testToBaseDigits = { expr = toBaseDigits 2 6; - expected = [ 1 1 0 ]; + expected = [ + 1 + 1 + 0 + ]; }; testFunctionArgsFunctor = { - expr = functionArgs { __functor = self: { a, b, }: null; }; + expr = functionArgs { __functor = self: { a, b }: null; }; expected = { a = false; b = false; @@ -225,23 +386,37 @@ in runTests { testFunctionArgsSetFunctionArgs = { expr = functionArgs (setFunctionArgs (args: args.x) { x = false; }); - expected = { x = false; }; + expected = { + x = false; + }; }; # STRINGS testConcatMapStrings = { - expr = concatMapStrings (x: x + ";") [ "a" "b" "c" ]; + expr = concatMapStrings (x: x + ";") [ + "a" + "b" + "c" + ]; expected = "a;b;c;"; }; testConcatStringsSep = { - expr = concatStringsSep "," [ "a" "b" "c" ]; + expr = concatStringsSep "," [ + "a" + "b" + "c" + ]; expected = "a,b,c"; }; testConcatLines = { - expr = concatLines [ "a" "b" "c" ]; + expr = concatLines [ + "a" + "b" + "c" + ]; expected = '' a b @@ -263,9 +438,7 @@ in runTests { outPath = "/default"; } # And it returns the derivation directly if there's no "out" either - { - outPath = "/default"; - } + { outPath = "/default"; } # Same if the output is specified explicitly, even if there's a "dev" { dev.outPath = "/dev"; @@ -287,7 +460,10 @@ in runTests { }; testMakeIncludePathWithManyString = { - expr = makeIncludePath [ "/usr" "/usr/local" ]; + expr = makeIncludePath [ + "/usr" + "/usr/local" + ]; expected = "/usr/include:/usr/local/include"; }; @@ -298,12 +474,21 @@ in runTests { testSplitStringsSimple = { expr = strings.splitString "." "a.b.c.d"; - expected = [ "a" "b" "c" "d" ]; + expected = [ + "a" + "b" + "c" + "d" + ]; }; testSplitStringsEmpty = { expr = strings.splitString "." "a..b"; - expected = [ "a" "" "b" ]; + expected = [ + "a" + "" + "b" + ]; }; testSplitStringsOne = { @@ -318,26 +503,49 @@ in runTests { testSplitStringsFirstEmpty = { expr = strings.splitString "/" "/a/b/c"; - expected = [ "" "a" "b" "c" ]; + expected = [ + "" + "a" + "b" + "c" + ]; }; testSplitStringsLastEmpty = { expr = strings.splitString ":" "2001:db8:0:0042::8a2e:370:"; - expected = [ "2001" "db8" "0" "0042" "" "8a2e" "370" "" ]; + expected = [ + "2001" + "db8" + "0" + "0042" + "" + "8a2e" + "370" + "" + ]; }; testSplitStringsRegex = { expr = strings.splitString "\\[{}]()^$?*+|." "A\\[{}]()^$?*+|.B"; - expected = [ "A" "B" ]; + expected = [ + "A" + "B" + ]; }; testSplitStringsDerivation = { - expr = take 3 (strings.splitString "/" (derivation { - name = "name"; - builder = "builder"; - system = "system"; - })); - expected = [ "" "nix" "store" ]; + expr = take 3 ( + strings.splitString "/" (derivation { + name = "name"; + builder = "builder"; + system = "system"; + }) + ); + expected = [ + "" + "nix" + "store" + ]; }; testSplitVersionSingle = { @@ -347,12 +555,19 @@ in runTests { testSplitVersionDouble = { expr = versions.splitVersion "1.2"; - expected = [ "1" "2" ]; + expected = [ + "1" + "2" + ]; }; testSplitVersionTriple = { expr = versions.splitVersion "1.2.3"; - expected = [ "1" "2" "3" ]; + expected = [ + "1" + "2" + "3" + ]; }; testPadVersionLess = { @@ -371,23 +586,23 @@ in runTests { }; testIsStorePath = { - expr = let - goodPath = - "${builtins.storeDir}/d945ibfx9x185xf04b890y4f9g3cbb63-python-2.7.11"; - in { - storePath = isStorePath goodPath; - # storePathDerivation = isStorePath (import ../.. { system = "x86_64-linux"; }).hello; - storePathAppendix = isStorePath "${goodPath}/bin/python"; - nonAbsolute = - isStorePath (concatStrings (tail (stringToCharacters goodPath))); - asPath = isStorePath (/. + goodPath); - otherPath = isStorePath "/something/else"; - otherVals = { - attrset = isStorePath { }; - list = isStorePath [ ]; - int = isStorePath 42; + expr = + let + goodPath = "${builtins.storeDir}/d945ibfx9x185xf04b890y4f9g3cbb63-python-2.7.11"; + in + { + storePath = isStorePath goodPath; + # storePathDerivation = isStorePath (import ../.. { system = "x86_64-linux"; }).hello; + storePathAppendix = isStorePath "${goodPath}/bin/python"; + nonAbsolute = isStorePath (concatStrings (tail (stringToCharacters goodPath))); + asPath = isStorePath (/. + goodPath); + otherPath = isStorePath "/something/else"; + otherVals = { + attrset = isStorePath { }; + list = isStorePath [ ]; + int = isStorePath 42; + }; }; - }; expected = { storePath = true; # storePathDerivation = true; @@ -412,7 +627,10 @@ in runTests { expr = '' ${toShellVars { STRing01 = "just a 'string'"; - _array_ = [ "with" "more strings" ]; + _array_ = [ + "with" + "more strings" + ]; assoc."with some" = '' strings possibly newlines @@ -507,8 +725,10 @@ in runTests { ("Hello%20World" == strings.escapeURL "Hello World") ("Hello%2FWorld" == strings.escapeURL "Hello/World") ("42%25" == strings.escapeURL "42%") - ("%20%3F%26%3D%23%2B%25%21%3C%3E%23%22%7B%7D%7C%5C%5E%5B%5D%60%09%3A%2F%40%24%27%28%29%2A%2C%3B" - == strings.escapeURL " ?&=#+%!<>#\"{}|\\^[]` :/@$'()*,;") + ( + "%20%3F%26%3D%23%2B%25%21%3C%3E%23%22%7B%7D%7C%5C%5E%5B%5D%60%09%3A%2F%40%24%27%28%29%2A%2C%3B" + == strings.escapeURL " ?&=#+%!<>#\"{}|\\^[]` :/@$'()*,;" + ) ]; testToInt = testAllTrue [ @@ -528,58 +748,84 @@ in runTests { ]; testToIntFails = testAllTrue [ - (builtins.tryEval (toInt "") == { - success = false; - value = false; - }) - (builtins.tryEval (toInt "123 123") == { - success = false; - value = false; - }) - (builtins.tryEval (toInt "0 123") == { - success = false; - value = false; - }) - (builtins.tryEval (toInt " 0d ") == { - success = false; - value = false; - }) - (builtins.tryEval (toInt " 1d ") == { - success = false; - value = false; - }) - (builtins.tryEval (toInt " d0 ") == { - success = false; - value = false; - }) - (builtins.tryEval (toInt "00") == { - success = false; - value = false; - }) - (builtins.tryEval (toInt "01") == { - success = false; - value = false; - }) - (builtins.tryEval (toInt "002") == { - success = false; - value = false; - }) - (builtins.tryEval (toInt " 002 ") == { - success = false; - value = false; - }) - (builtins.tryEval (toInt " foo ") == { - success = false; - value = false; - }) - (builtins.tryEval (toInt " foo 123 ") == { - success = false; - value = false; - }) - (builtins.tryEval (toInt " foo123 ") == { - success = false; - value = false; - }) + ( + builtins.tryEval (toInt "") == { + success = false; + value = false; + } + ) + ( + builtins.tryEval (toInt "123 123") == { + success = false; + value = false; + } + ) + ( + builtins.tryEval (toInt "0 123") == { + success = false; + value = false; + } + ) + ( + builtins.tryEval (toInt " 0d ") == { + success = false; + value = false; + } + ) + ( + builtins.tryEval (toInt " 1d ") == { + success = false; + value = false; + } + ) + ( + builtins.tryEval (toInt " d0 ") == { + success = false; + value = false; + } + ) + ( + builtins.tryEval (toInt "00") == { + success = false; + value = false; + } + ) + ( + builtins.tryEval (toInt "01") == { + success = false; + value = false; + } + ) + ( + builtins.tryEval (toInt "002") == { + success = false; + value = false; + } + ) + ( + builtins.tryEval (toInt " 002 ") == { + success = false; + value = false; + } + ) + ( + builtins.tryEval (toInt " foo ") == { + success = false; + value = false; + } + ) + ( + builtins.tryEval (toInt " foo 123 ") == { + success = false; + value = false; + } + ) + ( + builtins.tryEval (toInt " foo123 ") == { + success = false; + value = false; + } + ) ]; testToIntBase10 = testAllTrue [ @@ -613,112 +859,179 @@ in runTests { ]; testToIntBase10Fails = testAllTrue [ - (builtins.tryEval (toIntBase10 "") == { - success = false; - value = false; - }) - (builtins.tryEval (toIntBase10 "123 123") == { - success = false; - value = false; - }) - (builtins.tryEval (toIntBase10 "0 123") == { - success = false; - value = false; - }) - (builtins.tryEval (toIntBase10 " 0d ") == { - success = false; - value = false; - }) - (builtins.tryEval (toIntBase10 " 1d ") == { - success = false; - value = false; - }) - (builtins.tryEval (toIntBase10 " d0 ") == { - success = false; - value = false; - }) - (builtins.tryEval (toIntBase10 " foo ") == { - success = false; - value = false; - }) - (builtins.tryEval (toIntBase10 " foo 123 ") == { - success = false; - value = false; - }) - (builtins.tryEval (toIntBase10 " foo 00123 ") == { - success = false; - value = false; - }) - (builtins.tryEval (toIntBase10 " foo00123 ") == { - success = false; - value = false; - }) + ( + builtins.tryEval (toIntBase10 "") == { + success = false; + value = false; + } + ) + ( + builtins.tryEval (toIntBase10 "123 123") == { + success = false; + value = false; + } + ) + ( + builtins.tryEval (toIntBase10 "0 123") == { + success = false; + value = false; + } + ) + ( + builtins.tryEval (toIntBase10 " 0d ") == { + success = false; + value = false; + } + ) + ( + builtins.tryEval (toIntBase10 " 1d ") == { + success = false; + value = false; + } + ) + ( + builtins.tryEval (toIntBase10 " d0 ") == { + success = false; + value = false; + } + ) + ( + builtins.tryEval (toIntBase10 " foo ") == { + success = false; + value = false; + } + ) + ( + builtins.tryEval (toIntBase10 " foo 123 ") == { + success = false; + value = false; + } + ) + ( + builtins.tryEval (toIntBase10 " foo 00123 ") == { + success = false; + value = false; + } + ) + ( + builtins.tryEval (toIntBase10 " foo00123 ") == { + success = false; + value = false; + } + ) ]; # LISTS testFilter = { - expr = filter (x: x != "a") [ "a" "b" "c" "a" ]; - expected = [ "b" "c" ]; + expr = filter (x: x != "a") [ + "a" + "b" + "c" + "a" + ]; + expected = [ + "b" + "c" + ]; }; testIfilter0Example = { - expr = ifilter0 (i: v: i == 0 || v > 2) [ 1 2 3 ]; - expected = [ 1 3 ]; + expr = ifilter0 (i: v: i == 0 || v > 2) [ + 1 + 2 + 3 + ]; + expected = [ + 1 + 3 + ]; }; testIfilter0Empty = { expr = ifilter0 (i: v: abort "shouldn't be evaluated!") [ ]; expected = [ ]; }; testIfilter0IndexOnly = { - expr = length (ifilter0 (i: v: mod i 2 == 0) [ - (throw "0") - (throw "1") - (throw "2") - (throw "3") - ]); + expr = length ( + ifilter0 (i: v: mod i 2 == 0) [ + (throw "0") + (throw "1") + (throw "2") + (throw "3") + ] + ); expected = 2; }; testIfilter0All = { - expr = ifilter0 (i: v: true) [ 10 11 12 13 14 15 ]; - expected = [ 10 11 12 13 14 15 ]; + expr = ifilter0 (i: v: true) [ + 10 + 11 + 12 + 13 + 14 + 15 + ]; + expected = [ + 10 + 11 + 12 + 13 + 14 + 15 + ]; }; testIfilter0First = { - expr = ifilter0 (i: v: i == 0) [ 10 11 12 13 14 15 ]; + expr = ifilter0 (i: v: i == 0) [ + 10 + 11 + 12 + 13 + 14 + 15 + ]; expected = [ 10 ]; }; testIfilter0Last = { - expr = ifilter0 (i: v: i == 5) [ 10 11 12 13 14 15 ]; + expr = ifilter0 (i: v: i == 5) [ + 10 + 11 + 12 + 13 + 14 + 15 + ]; expected = [ 15 ]; }; - testFold = let - f = op: fold: fold op 0 (range 0 100); - # fold with associative operator - assoc = f builtins.add; - # fold with non-associative operator - nonAssoc = f builtins.sub; - in { - expr = { - assocRight = assoc foldr; - # right fold with assoc operator is same as left fold - assocRightIsLeft = assoc foldr == assoc foldl; - nonAssocRight = nonAssoc foldr; - nonAssocLeft = nonAssoc foldl; - # with non-assoc operator the fold results are not the same - nonAssocRightIsNotLeft = nonAssoc foldl != nonAssoc foldr; - # fold is an alias for foldr - foldIsRight = nonAssoc fold == nonAssoc foldr; - }; - expected = { - assocRight = 5050; - assocRightIsLeft = true; - nonAssocRight = 50; - nonAssocLeft = -5050; - nonAssocRightIsNotLeft = true; - foldIsRight = true; + testFold = + let + f = op: fold: fold op 0 (range 0 100); + # fold with associative operator + assoc = f builtins.add; + # fold with non-associative operator + nonAssoc = f builtins.sub; + in + { + expr = { + assocRight = assoc foldr; + # right fold with assoc operator is same as left fold + assocRightIsLeft = assoc foldr == assoc foldl; + nonAssocRight = nonAssoc foldr; + nonAssocLeft = nonAssoc foldl; + # with non-assoc operator the fold results are not the same + nonAssocRightIsNotLeft = nonAssoc foldl != nonAssoc foldr; + # fold is an alias for foldr + foldIsRight = nonAssoc fold == nonAssoc foldr; + }; + expected = { + assocRight = 5050; + assocRightIsLeft = true; + nonAssocRight = 50; + nonAssocLeft = -5050; + nonAssocRightIsNotLeft = true; + foldIsRight = true; + }; }; - }; testFoldl'Empty = { expr = foldl' (acc: el: abort "operation not called") 0 [ ]; @@ -726,25 +1039,33 @@ in runTests { }; testFoldl'IntegerAdding = { - expr = foldl' (acc: el: acc + el) 0 [ 1 2 3 ]; + expr = foldl' (acc: el: acc + el) 0 [ + 1 + 2 + 3 + ]; expected = 6; }; # The accumulator isn't forced deeply testFoldl'NonDeep = { - expr = take 3 - (foldl' (acc: el: [ el ] ++ acc) [ (abort "unevaluated list entry") ] [ + expr = take 3 ( + foldl' (acc: el: [ el ] ++ acc) [ (abort "unevaluated list entry") ] [ 1 2 3 - ]); - expected = [ 3 2 1 ]; + ] + ); + expected = [ + 3 + 2 + 1 + ]; }; # Compared to builtins.foldl', lib.foldl' evaluates the first accumulator strictly too testFoldl'StrictInitial = { - expr = - (builtins.tryEval (foldl' (acc: el: el) (throw "hello") [ ])).success; + expr = (builtins.tryEval (foldl' (acc: el: el) (throw "hello") [ ])).success; expected = false; }; @@ -756,49 +1077,159 @@ in runTests { }; testTake = testAllTrue [ - ([ ] == (take 0 [ 1 2 3 ])) - ([ 1 ] == (take 1 [ 1 2 3 ])) - ([ 1 2 ] == (take 2 [ 1 2 3 ])) - ([ 1 2 3 ] == (take 3 [ 1 2 3 ])) - ([ 1 2 3 ] == (take 4 [ 1 2 3 ])) + ( + [ ] == (take 0 [ + 1 + 2 + 3 + ]) + ) + ( + [ 1 ] == (take 1 [ + 1 + 2 + 3 + ]) + ) + ( + [ + 1 + 2 + ] == (take 2 [ + 1 + 2 + 3 + ]) + ) + ( + [ + 1 + 2 + 3 + ] == (take 3 [ + 1 + 2 + 3 + ]) + ) + ( + [ + 1 + 2 + 3 + ] == (take 4 [ + 1 + 2 + 3 + ]) + ) ]; testListHasPrefixExample1 = { - expr = lists.hasPrefix [ 1 2 ] [ 1 2 3 4 ]; + expr = + lists.hasPrefix + [ + 1 + 2 + ] + [ + 1 + 2 + 3 + 4 + ]; expected = true; }; testListHasPrefixExample2 = { - expr = lists.hasPrefix [ 0 1 ] [ 1 2 3 4 ]; + expr = + lists.hasPrefix + [ + 0 + 1 + ] + [ + 1 + 2 + 3 + 4 + ]; expected = false; }; testListHasPrefixLazy = { - expr = - lists.hasPrefix [ 1 ] [ 1 (abort "lib.lists.hasPrefix is not lazy") ]; + expr = lists.hasPrefix [ 1 ] [ + 1 + (abort "lib.lists.hasPrefix is not lazy") + ]; expected = true; }; testListHasPrefixEmptyPrefix = { - expr = lists.hasPrefix [ ] [ 1 2 ]; + expr = lists.hasPrefix [ ] [ + 1 + 2 + ]; expected = true; }; testListHasPrefixEmptyList = { - expr = lists.hasPrefix [ 1 2 ] [ ]; + expr = lists.hasPrefix [ + 1 + 2 + ] [ ]; expected = false; }; testListRemovePrefixExample1 = { - expr = lists.removePrefix [ 1 2 ] [ 1 2 3 4 ]; - expected = [ 3 4 ]; + expr = + lists.removePrefix + [ + 1 + 2 + ] + [ + 1 + 2 + 3 + 4 + ]; + expected = [ + 3 + 4 + ]; }; testListRemovePrefixExample2 = { - expr = (builtins.tryEval (lists.removePrefix [ 0 1 ] [ 1 2 3 4 ])).success; + expr = + (builtins.tryEval ( + lists.removePrefix + [ + 0 + 1 + ] + [ + 1 + 2 + 3 + 4 + ] + )).success; expected = false; }; testListRemovePrefixEmptyPrefix = { - expr = lists.removePrefix [ ] [ 1 2 ]; - expected = [ 1 2 ]; + expr = lists.removePrefix [ ] [ + 1 + 2 + ]; + expected = [ + 1 + 2 + ]; }; testListRemovePrefixEmptyList = { - expr = (builtins.tryEval (lists.removePrefix [ 1 2 ] [ ])).success; + expr = + (builtins.tryEval ( + lists.removePrefix [ + 1 + 2 + ] [ ] + )).success; expected = false; }; @@ -814,31 +1245,99 @@ in runTests { } ]; expected = { - a = [ 2 3 ]; + a = [ + 2 + 3 + ]; b = [ 7 ]; c = [ 8 ]; }; }; testListCommonPrefixExample1 = { - expr = lists.commonPrefix [ 1 2 3 4 5 6 ] [ 1 2 4 8 ]; - expected = [ 1 2 ]; + expr = + lists.commonPrefix + [ + 1 + 2 + 3 + 4 + 5 + 6 + ] + [ + 1 + 2 + 4 + 8 + ]; + expected = [ + 1 + 2 + ]; }; testListCommonPrefixExample2 = { - expr = lists.commonPrefix [ 1 2 3 ] [ 1 2 3 4 5 ]; - expected = [ 1 2 3 ]; + expr = + lists.commonPrefix + [ + 1 + 2 + 3 + ] + [ + 1 + 2 + 3 + 4 + 5 + ]; + expected = [ + 1 + 2 + 3 + ]; }; testListCommonPrefixExample3 = { - expr = lists.commonPrefix [ 1 2 3 ] [ 4 5 6 ]; + expr = + lists.commonPrefix + [ + 1 + 2 + 3 + ] + [ + 4 + 5 + 6 + ]; expected = [ ]; }; testListCommonPrefixEmpty = { - expr = lists.commonPrefix [ ] [ 1 2 3 ]; + expr = lists.commonPrefix [ ] [ + 1 + 2 + 3 + ]; expected = [ ]; }; testListCommonPrefixSame = { - expr = lists.commonPrefix [ 1 2 3 ] [ 1 2 3 ]; - expected = [ 1 2 3 ]; + expr = + lists.commonPrefix + [ + 1 + 2 + 3 + ] + [ + 1 + 2 + 3 + ]; + expected = [ + 1 + 2 + 3 + ]; }; testListCommonPrefixLazy = { expr = lists.commonPrefix [ 1 ] [ @@ -848,20 +1347,41 @@ in runTests { expected = [ 1 ]; }; # This would stack overflow if `commonPrefix` were implemented using recursion - testListCommonPrefixLong = let longList = genList (n: n) 100000; - in { - expr = lists.commonPrefix longList longList; - expected = longList; - }; + testListCommonPrefixLong = + let + longList = genList (n: n) 100000; + in + { + expr = lists.commonPrefix longList longList; + expected = longList; + }; testSort = { - expr = sort builtins.lessThan [ 40 2 30 42 ]; - expected = [ 2 30 40 42 ]; + expr = sort builtins.lessThan [ + 40 + 2 + 30 + 42 + ]; + expected = [ + 2 + 30 + 40 + 42 + ]; }; testSortOn = { - expr = sortOn stringLength [ "aa" "b" "cccc" ]; - expected = [ "b" "aa" "cccc" ]; + expr = sortOn stringLength [ + "aa" + "b" + "cccc" + ]; + expected = [ + "b" + "aa" + "cccc" + ]; }; testSortOnEmpty = { @@ -870,26 +1390,36 @@ in runTests { }; testSortOnIncomparable = { - expr = map (x: x.f x.ok) (sortOn (x: x.ok) [ - { - ok = 1; - f = x: x; - } - { - ok = 3; - f = x: x + 3; - } - { - ok = 2; - f = x: x; - } - ]); - expected = [ 1 2 6 ]; + expr = map (x: x.f x.ok) ( + sortOn (x: x.ok) [ + { + ok = 1; + f = x: x; + } + { + ok = 3; + f = x: x + 3; + } + { + ok = 2; + f = x: x; + } + ] + ); + expected = [ + 1 + 2 + 6 + ]; }; testReplicate = { expr = replicate 3 "a"; - expected = [ "a" "a" "a" ]; + expected = [ + "a" + "a" + "a" + ]; }; testToIntShouldConvertStringToInt = { @@ -906,12 +1436,32 @@ in runTests { }; testHasAttrByPathTrue = { - expr = hasAttrByPath [ "a" "b" ] { a = { b = "yey"; }; }; + expr = + hasAttrByPath + [ + "a" + "b" + ] + { + a = { + b = "yey"; + }; + }; expected = true; }; testHasAttrByPathFalse = { - expr = hasAttrByPath [ "a" "b" ] { a = { c = "yey"; }; }; + expr = + hasAttrByPath + [ + "a" + "b" + ] + { + a = { + c = "yey"; + }; + }; expected = false; }; @@ -931,53 +1481,88 @@ in runTests { }; testLongestValidPathPrefix_zero = { - expr = attrsets.longestValidPathPrefix [ "a" (throw "do not use") ] { - d = null; - }; + expr = attrsets.longestValidPathPrefix [ + "a" + (throw "do not use") + ] { d = null; }; expected = [ ]; }; testLongestValidPathPrefix_zero_b = { - expr = attrsets.longestValidPathPrefix [ "z" "z" ] "remarkably harmonious"; + expr = attrsets.longestValidPathPrefix [ + "z" + "z" + ] "remarkably harmonious"; expected = [ ]; }; testLongestValidPathPrefix_one = { - expr = attrsets.longestValidPathPrefix [ "a" "b" "c" ] { a = null; }; + expr = attrsets.longestValidPathPrefix [ + "a" + "b" + "c" + ] { a = null; }; expected = [ "a" ]; }; testLongestValidPathPrefix_two = { - expr = attrsets.longestValidPathPrefix [ "a" "b" "c" ] { a.b = null; }; - expected = [ "a" "b" ]; + expr = attrsets.longestValidPathPrefix [ + "a" + "b" + "c" + ] { a.b = null; }; + expected = [ + "a" + "b" + ]; }; testLongestValidPathPrefix_three = { - expr = attrsets.longestValidPathPrefix [ "a" "b" "c" ] { a.b.c = null; }; - expected = [ "a" "b" "c" ]; + expr = attrsets.longestValidPathPrefix [ + "a" + "b" + "c" + ] { a.b.c = null; }; + expected = [ + "a" + "b" + "c" + ]; }; testLongestValidPathPrefix_three_extra = { - expr = attrsets.longestValidPathPrefix [ "a" "b" "c" ] { - a.b.c.d = throw "nope"; - }; - expected = [ "a" "b" "c" ]; + expr = attrsets.longestValidPathPrefix [ + "a" + "b" + "c" + ] { a.b.c.d = throw "nope"; }; + expected = [ + "a" + "b" + "c" + ]; }; testFindFirstIndexExample1 = { - expr = lists.findFirstIndex (x: x > 3) - (abort "index found, so a default must not be evaluated") [ 1 6 4 ]; + expr = lists.findFirstIndex (x: x > 3) (abort "index found, so a default must not be evaluated") [ + 1 + 6 + 4 + ]; expected = 1; }; testFindFirstIndexExample2 = { - expr = lists.findFirstIndex (x: x > 9) "a very specific default" [ 1 6 4 ]; + expr = lists.findFirstIndex (x: x > 9) "a very specific default" [ + 1 + 6 + 4 + ]; expected = "a very specific default"; }; testFindFirstIndexEmpty = { - expr = lists.findFirstIndex - (abort "when the list is empty, the predicate is not needed") null [ ]; + expr = lists.findFirstIndex (abort "when the list is empty, the predicate is not needed") null [ ]; expected = null; }; @@ -988,17 +1573,18 @@ in runTests { testFindFirstIndexSingleDefault = { expr = lists.findFirstIndex (x: false) null [ - (abort - "if the predicate doesn't access the value, it must not be evaluated") + (abort "if the predicate doesn't access the value, it must not be evaluated") ]; expected = null; }; testFindFirstIndexNone = { - expr = builtins.tryEval (lists.findFirstIndex (x: x == 2) null [ - 1 - (throw "the last element must be evaluated when there's no match") - ]); + expr = builtins.tryEval ( + lists.findFirstIndex (x: x == 2) null [ + 1 + (throw "the last element must be evaluated when there's no match") + ] + ); expected = { success = false; value = false; @@ -1020,34 +1606,55 @@ in runTests { }; testFindFirstExample1 = { - expr = lists.findFirst (x: x > 3) 7 [ 1 6 4 ]; + expr = lists.findFirst (x: x > 3) 7 [ + 1 + 6 + 4 + ]; expected = 6; }; testFindFirstExample2 = { - expr = lists.findFirst (x: x > 9) 7 [ 1 6 4 ]; + expr = lists.findFirst (x: x > 9) 7 [ + 1 + 6 + 4 + ]; expected = 7; }; testAllUnique_true = { - expr = allUnique [ 3 2 4 1 ]; + expr = allUnique [ + 3 + 2 + 4 + 1 + ]; expected = true; }; testAllUnique_false = { - expr = allUnique [ 3 2 3 4 ]; + expr = allUnique [ + 3 + 2 + 3 + 4 + ]; expected = false; }; # ATTRSETS testConcatMapAttrs = { - expr = concatMapAttrs (name: value: { - ${name} = value; - ${name + value} = value; - }) { - foo = "bar"; - foobar = "baz"; - }; + expr = + concatMapAttrs + (name: value: { + ${name} = value; + ${name + value} = value; + }) + { + foo = "bar"; + foobar = "baz"; + }; expected = { foo = "bar"; foobar = "baz"; @@ -1058,33 +1665,54 @@ in runTests { # code from example testFoldlAttrs = { expr = { - example = foldlAttrs (acc: name: value: { - sum = acc.sum + value; - names = acc.names ++ [ name ]; - }) { - sum = 0; - names = [ ]; - } { - foo = 1; - bar = 10; - }; + example = + foldlAttrs + (acc: name: value: { + sum = acc.sum + value; + names = acc.names ++ [ name ]; + }) + { + sum = 0; + names = [ ]; + } + { + foo = 1; + bar = 10; + }; # should just return the initial value emptySet = foldlAttrs (throw "function not needed") 123 { }; # should just evaluate to the last value - valuesNotNeeded = foldlAttrs (acc: _name: _v: acc) 3 { - z = throw "value z not needed"; - a = throw "value a not needed"; - }; + valuesNotNeeded = + foldlAttrs + ( + acc: _name: _v: + acc + ) + 3 + { + z = throw "value z not needed"; + a = throw "value a not needed"; + }; # the accumulator doesnt have to be an attrset it can be as trivial as being just a number or string - trivialAcc = foldlAttrs (acc: _name: v: acc * 10 + v) 1 { - z = 1; - a = 2; - }; + trivialAcc = + foldlAttrs + ( + acc: _name: v: + acc * 10 + v + ) + 1 + { + z = 1; + a = 2; + }; }; expected = { example = { sum = 11; - names = [ "bar" "foo" ]; + names = [ + "bar" + "foo" + ]; }; emptySet = 123; valuesNotNeeded = 3; @@ -1111,34 +1739,55 @@ in runTests { }; }; testMergeAttrsListExample2 = { - expr = attrsets.mergeAttrsList [ { a = 0; } { a = 1; } ]; - expected = { a = 1; }; - }; - testMergeAttrsListExampleMany = let - list = genList (n: - listToAttrs (genList (m: - let - # Integer divide n by two to create duplicate attributes - str = "halfn${toString (n / 2)}m${toString m}"; - in nameValuePair str str) 100)) 100; - in { - expr = attrsets.mergeAttrsList list; - expected = foldl' mergeAttrs { } list; + expr = attrsets.mergeAttrsList [ + { a = 0; } + { a = 1; } + ]; + expected = { + a = 1; + }; }; + testMergeAttrsListExampleMany = + let + list = genList ( + n: + listToAttrs ( + genList ( + m: + let + # Integer divide n by two to create duplicate attributes + str = "halfn${toString (n / 2)}m${toString m}"; + in + nameValuePair str str + ) 100 + ) + ) 100; + in + { + expr = attrsets.mergeAttrsList list; + expected = foldl' mergeAttrs { } list; + }; # code from the example testRecursiveUpdateUntil = { - expr = recursiveUpdateUntil (path: l: r: path == [ "foo" ]) { - # first attribute set - foo.bar = 1; - foo.baz = 2; - bar = 3; - } { - #second attribute set - foo.bar = 1; - foo.quz = 2; - baz = 4; - }; + expr = + recursiveUpdateUntil + ( + path: l: r: + path == [ "foo" ] + ) + { + # first attribute set + foo.bar = 1; + foo.baz = 2; + bar = 3; + } + { + #second attribute set + foo.bar = 1; + foo.quz = 2; + baz = 4; + }; expected = { foo.bar = 1; # 'foo.*' from the second set foo.quz = 2; @@ -1148,17 +1797,43 @@ in runTests { }; testMatchAttrsMatchingExact = { - expr = matchAttrs { cpu = { bits = 64; }; } { cpu = { bits = 64; }; }; + expr = + matchAttrs + { + cpu = { + bits = 64; + }; + } + { + cpu = { + bits = 64; + }; + }; expected = true; }; testMatchAttrsMismatch = { - expr = matchAttrs { cpu = { bits = 128; }; } { cpu = { bits = 64; }; }; + expr = + matchAttrs + { + cpu = { + bits = 128; + }; + } + { + cpu = { + bits = 64; + }; + }; expected = false; }; testMatchAttrsMatchingImplicit = { - expr = matchAttrs { cpu = { }; } { cpu = { bits = 64; }; }; + expr = matchAttrs { cpu = { }; } { + cpu = { + bits = 64; + }; + }; expected = true; }; @@ -1174,7 +1849,9 @@ in runTests { testOverrideExistingDisjoint = { expr = overrideExisting { b = 2; } { a = 1; }; - expected = { b = 2; }; + expected = { + b = 2; + }; }; testOverrideExistingOverride = { @@ -1188,47 +1865,59 @@ in runTests { }; }; - testListAttrsReverse = let - exampleAttrs = { - foo = 1; - bar = "asdf"; - baz = [ 1 3 3 7 ]; - fnord = null; - }; - exampleSingletonList = [{ - name = "foo"; - value = 1; - }]; - in { - expr = { - isReverseToListToAttrs = builtins.listToAttrs (attrsToList exampleAttrs) - == exampleAttrs; - isReverseToAttrsToList = - attrsToList (builtins.listToAttrs exampleSingletonList) - == exampleSingletonList; - testDuplicatePruningBehaviour = attrsToList (builtins.listToAttrs [ - { - name = "a"; - value = 2; - } + testListAttrsReverse = + let + exampleAttrs = { + foo = 1; + bar = "asdf"; + baz = [ + 1 + 3 + 3 + 7 + ]; + fnord = null; + }; + exampleSingletonList = [ { - name = "a"; + name = "foo"; value = 1; } - ]); - }; - expected = { - isReverseToAttrsToList = true; - isReverseToListToAttrs = true; - testDuplicatePruningBehaviour = [{ - name = "a"; - value = 2; - }]; + ]; + in + { + expr = { + isReverseToListToAttrs = builtins.listToAttrs (attrsToList exampleAttrs) == exampleAttrs; + isReverseToAttrsToList = + attrsToList (builtins.listToAttrs exampleSingletonList) == exampleSingletonList; + testDuplicatePruningBehaviour = attrsToList ( + builtins.listToAttrs [ + { + name = "a"; + value = 2; + } + { + name = "a"; + value = 1; + } + ] + ); + }; + expected = { + isReverseToAttrsToList = true; + isReverseToListToAttrs = true; + testDuplicatePruningBehaviour = [ + { + name = "a"; + value = 2; + } + ]; + }; }; - }; - testAttrsToListsCanDealWithFunctions = - testingEval (attrsToList { someFunc = a: a + 1; }); + testAttrsToListsCanDealWithFunctions = testingEval (attrsToList { + someFunc = a: a + 1; + }); # GENERATORS # these tests assume attributes are converted to lists @@ -1240,16 +1929,18 @@ in runTests { }; testMkValueString = { - expr = let - vals = { - int = 42; - string = ''fo"o''; - bool = true; - bool2 = false; - null = null; - # float = 42.23; # floats are strange - }; - in mapAttrs (const (generators.mkValueStringDefault { })) vals; + expr = + let + vals = { + int = 42; + string = ''fo"o''; + bool = true; + bool2 = false; + null = null; + # float = 42.23; # floats are strange + }; + in + mapAttrs (const (generators.mkValueStringDefault { })) vals; expected = { int = "42"; string = ''fo"o''; @@ -1291,7 +1982,10 @@ in runTests { testToINIDuplicateKeys = { expr = generators.toINI { listsAsDuplicateKeys = true; } { foo.bar = true; - baz.qux = [ 1 false ]; + baz.qux = [ + 1 + false + ]; }; expected = '' [baz] @@ -1305,7 +1999,9 @@ in runTests { testToINIDefaultEscapes = { expr = generators.toINI { } { - "no [ and ] allowed unescaped" = { "and also no = in keys" = 42; }; + "no [ and ] allowed unescaped" = { + "and also no = in keys" = 42; + }; }; expected = '' [no \[ and \] allowed unescaped] @@ -1321,7 +2017,9 @@ in runTests { # booleans are converted verbatim by default boolean = false; }; - "foo[]" = { "he\\h=he" = "this is okay"; }; + "foo[]" = { + "he\\h=he" = "this is okay"; + }; }; expected = '' [foo\[\]] @@ -1342,21 +2040,25 @@ in runTests { expected = ""; }; - testToINIWithGlobalSectionGlobalEmptyIsTheSameAsToINI = let - sections = { - "section 1" = { - attribute1 = 5; - x = "Me-se JarJar Binx"; + testToINIWithGlobalSectionGlobalEmptyIsTheSameAsToINI = + let + sections = { + "section 1" = { + attribute1 = 5; + x = "Me-se JarJar Binx"; + }; + "foo" = { + "he\\h=he" = "this is okay"; + }; }; - "foo" = { "he\\h=he" = "this is okay"; }; - }; - in { - expr = generators.toINIWithGlobalSection { } { - globalSection = { }; - sections = sections; + in + { + expr = generators.toINIWithGlobalSection { } { + globalSection = { }; + sections = sections; + }; + expected = generators.toINI { } sections; }; - expected = generators.toINI { } sections; - }; testToINIWithGlobalSectionFull = { expr = generators.toINIWithGlobalSection { } { @@ -1369,7 +2071,9 @@ in runTests { attribute1 = 5; x = "Me-se JarJar Binx"; }; - "foo" = { "he\\h=he" = "this is okay"; }; + "foo" = { + "he\\h=he" = "this is okay"; + }; }; }; expected = '' @@ -1432,126 +2136,170 @@ in runTests { }; # right now only invocation check - testToJSONSimple = let val = { foobar = [ "baz" 1 2 3 ]; }; - in { - expr = generators.toJSON { } val; - # trivial implementation - expected = builtins.toJSON val; - }; + testToJSONSimple = + let + val = { + foobar = [ + "baz" + 1 + 2 + 3 + ]; + }; + in + { + expr = generators.toJSON { } val; + # trivial implementation + expected = builtins.toJSON val; + }; # right now only invocation check - testToYAMLSimple = let - val = { - list = [ { one = 1; } { two = 2; } ]; - all = 42; - }; - in { - expr = generators.toYAML { } val; - # trivial implementation - expected = builtins.toJSON val; - }; - - testToPretty = let - deriv = derivation { - name = "test"; - builder = "/bin/sh"; - system = "aarch64-linux"; - }; - in { - expr = mapAttrs (const (generators.toPretty { multiline = false; })) rec { - int = 42; - float = 0.1337; - bool = true; - emptystring = ""; - string = ''fn''${o}"r\d''; - newlinestring = "\n"; - path = /. + "/foo"; - null_ = null; - function = x: x; - functionArgs = { arg ? 4, foo, }: arg; - list = [ 3 4 function [ false ] ]; - emptylist = [ ]; - attrs = { - foo = null; - "foo b/ar" = "baz"; + testToYAMLSimple = + let + val = { + list = [ + { one = 1; } + { two = 2; } + ]; + all = 42; }; - emptyattrs = { }; - drv = deriv; + in + { + expr = generators.toYAML { } val; + # trivial implementation + expected = builtins.toJSON val; }; - expected = rec { - int = "42"; - float = "0.1337"; - bool = "true"; - emptystring = ''""''; - string = ''"fn\''${o}\"r\\d"''; - newlinestring = ''"\n"''; - path = "/foo"; - null_ = "null"; - function = ""; - functionArgs = ""; - list = "[ 3 4 ${function} [ false ] ]"; - emptylist = "[ ]"; - attrs = ''{ foo = null; "foo b/ar" = "baz"; }''; - emptyattrs = "{ }"; - drv = ""; - }; - }; - - testToPrettyLimit = let - a.b = 1; - a.c = a; - in { - expr = generators.toPretty { } (generators.withRecursion { - throwOnDepthLimit = false; - depthLimit = 2; - } a); - expected = '' - { - b = 1; - c = { - b = ""; + + testToPretty = + let + deriv = derivation { + name = "test"; + builder = "/bin/sh"; + system = "aarch64-linux"; + }; + in + { + expr = mapAttrs (const (generators.toPretty { multiline = false; })) rec { + int = 42; + float = 0.1337; + bool = true; + emptystring = ""; + string = ''fn''${o}"r\d''; + newlinestring = "\n"; + path = /. + "/foo"; + null_ = null; + function = x: x; + functionArgs = + { + arg ? 4, + foo, + }: + arg; + list = [ + 3 + 4 + function + [ false ] + ]; + emptylist = [ ]; + attrs = { + foo = null; + "foo b/ar" = "baz"; + }; + emptyattrs = { }; + drv = deriv; + }; + expected = rec { + int = "42"; + float = "0.1337"; + bool = "true"; + emptystring = ''""''; + string = ''"fn\''${o}\"r\\d"''; + newlinestring = ''"\n"''; + path = "/foo"; + null_ = "null"; + function = ""; + functionArgs = ""; + list = "[ 3 4 ${function} [ false ] ]"; + emptylist = "[ ]"; + attrs = ''{ foo = null; "foo b/ar" = "baz"; }''; + emptyattrs = "{ }"; + drv = ""; + }; + }; + + testToPrettyLimit = + let + a.b = 1; + a.c = a; + in + { + expr = generators.toPretty { } ( + generators.withRecursion { + throwOnDepthLimit = false; + depthLimit = 2; + } a + ); + expected = '' + { + b = 1; c = { b = ""; - c = ""; + c = { + b = ""; + c = ""; + }; }; - }; - }''; - }; + }''; + }; - testToPrettyLimitThrow = let - a.b = 1; - a.c = a; - in { - expr = (builtins.tryEval (generators.toPretty { } - (generators.withRecursion { depthLimit = 2; } a))).success; - expected = false; - }; + testToPrettyLimitThrow = + let + a.b = 1; + a.c = a; + in + { + expr = + (builtins.tryEval (generators.toPretty { } (generators.withRecursion { depthLimit = 2; } a))) + .success; + expected = false; + }; - testWithRecursionDealsWithFunctors = let - functor = { __functor = self: { a, b, }: null; }; - a = { - value = "1234"; - b = functor; - c.d = functor; + testWithRecursionDealsWithFunctors = + let + functor = { + __functor = self: { a, b }: null; + }; + a = { + value = "1234"; + b = functor; + c.d = functor; + }; + in + { + expr = generators.toPretty { } ( + generators.withRecursion { + depthLimit = 1; + throwOnDepthLimit = false; + } a + ); + expected = '' + { + b = ; + c = { + d = ""; + }; + value = ""; + }''; }; - in { - expr = generators.toPretty { } (generators.withRecursion { - depthLimit = 1; - throwOnDepthLimit = false; - } a); - expected = '' - { - b = ; - c = { - d = ""; - }; - value = ""; - }''; - }; testToPrettyMultiline = { expr = mapAttrs (const (generators.toPretty { })) { - list = [ 3 4 [ false ] ]; + list = [ + 3 + 4 + [ false ] + ]; attrs = { foo = null; bar.foo = "baz"; @@ -1621,7 +2369,11 @@ in runTests { newlinestring = "\n"; path = /. + "/foo"; null_ = null; - list = [ 3 4 "test" ]; + list = [ + 3 + 4 + "test" + ]; emptylist = [ ]; attrs = { foo = null; @@ -1631,7 +2383,9 @@ in runTests { }; }; }; - expected = { value = builtins.readFile ./test-to-plist-expected.plist; }; + expected = { + value = builtins.readFile ./test-to-plist-expected.plist; + }; }; testToLuaEmptyAttrSet = { @@ -1645,7 +2399,12 @@ in runTests { }; testToLuaListOfVariousTypes = { - expr = generators.toLua { } [ null 43 3.14159 true ]; + expr = generators.toLua { } [ + null + 43 + 3.14159 + true + ]; expected = '' { nil, @@ -1661,8 +2420,7 @@ in runTests { }; testToLuaAttrsetWithLuaInline = { - expr = - generators.toLua { } { x = generators.mkLuaInline ''"abc" .. "def"''; }; + expr = generators.toLua { } { x = generators.mkLuaInline ''"abc" .. "def"''; }; expected = '' { ["x"] = ("abc" .. "def") @@ -1678,7 +2436,10 @@ in runTests { }; testToLuaWithoutMultiline = { - expr = generators.toLua { multiline = false; } [ 41 43 ]; + expr = generators.toLua { multiline = false; } [ + 41 + 43 + ]; expected = "{ 41, 43 }"; }; @@ -1690,7 +2451,9 @@ in runTests { testToLuaBindings = { expr = generators.toLua { asBindings = true; } { x1 = 41; - _y = { a = 43; }; + _y = { + a = 43; + }; }; expected = '' _y = { @@ -1708,24 +2471,35 @@ in runTests { }; testToLuaIndentedBindings = { - expr = generators.toLua { - asBindings = true; - indent = " "; - } { x = { y = 42; }; }; + expr = + generators.toLua + { + asBindings = true; + indent = " "; + } + { + x = { + y = 42; + }; + }; expected = " x = {\n [\"y\"] = 42\n }\n"; }; - testToLuaBindingsWithSpace = testingThrow - (generators.toLua { asBindings = true; } { "with space" = 42; }); + testToLuaBindingsWithSpace = testingThrow ( + generators.toLua { asBindings = true; } { "with space" = 42; } + ); - testToLuaBindingsWithLeadingDigit = - testingThrow (generators.toLua { asBindings = true; } { "11eleven" = 42; }); + testToLuaBindingsWithLeadingDigit = testingThrow ( + generators.toLua { asBindings = true; } { "11eleven" = 42; } + ); testToLuaBasicExample = { expr = generators.toLua { } { - cmd = [ "typescript-language-server" "--stdio" ]; - settings.workspace.library = - generators.mkLuaInline ''vim.api.nvim_get_runtime_file("", true)''; + cmd = [ + "typescript-language-server" + "--stdio" + ]; + settings.workspace.library = generators.mkLuaInline ''vim.api.nvim_get_runtime_file("", true)''; }; expected = '' { @@ -1749,7 +2523,10 @@ in runTests { X = "PUT"; retry = 3; retry-delay = null; - url = [ "https://example.com/foo" "https://example.com/bar" ]; + url = [ + "https://example.com/foo" + "https://example.com/bar" + ]; silent = false; verbose = true; }; @@ -1775,13 +2552,15 @@ in runTests { X = "PUT"; retry = 3; retry-delay = null; - url = [ "https://example.com/foo" "https://example.com/bar" ]; + url = [ + "https://example.com/foo" + "https://example.com/bar" + ]; silent = false; verbose = true; }; - expected = - "'-X' 'PUT' '--data' '{\"id\":0}' '--retry' '3' '--url' 'https://example.com/foo' '--url' 'https://example.com/bar' '--verbose'"; + expected = "'-X' 'PUT' '--data' '{\"id\":0}' '--retry' '3' '--url' 'https://example.com/foo' '--url' 'https://example.com/bar' '--verbose'"; }; testSanitizeDerivationNameLeadingDots = testSanitizeDerivationName { @@ -1795,24 +2574,18 @@ in runTests { }; testSanitizeDerivationNameAscii = testSanitizeDerivationName { - name = - " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; - expected = - "-+--.-0123456789-=-?-ABCDEFGHIJKLMNOPQRSTUVWXYZ-_-abcdefghijklmnopqrstuvwxyz-"; + name = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; + expected = "-+--.-0123456789-=-?-ABCDEFGHIJKLMNOPQRSTUVWXYZ-_-abcdefghijklmnopqrstuvwxyz-"; }; testSanitizeDerivationNameTooLong = testSanitizeDerivationName { - name = - "This string is loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong"; - expected = - "loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong"; + name = "This string is loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong"; + expected = "loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong"; }; testSanitizeDerivationNameTooLongWithInvalid = testSanitizeDerivationName { - name = - "Hello there aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa &&&&&&&&"; - expected = - "there-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-"; + name = "Hello there aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa &&&&&&&&"; + expected = "there-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-"; }; testSanitizeDerivationNameEmpty = testSanitizeDerivationName { @@ -1821,26 +2594,41 @@ in runTests { }; testFreeformOptions = { - expr = let - submodule = { lib, ... }: { - freeformType = lib.types.attrsOf - (lib.types.submodule { options.bar = lib.mkOption { }; }); - options.bar = lib.mkOption { }; - }; + expr = + let + submodule = + { lib, ... }: + { + freeformType = lib.types.attrsOf (lib.types.submodule { options.bar = lib.mkOption { }; }); + options.bar = lib.mkOption { }; + }; - module = { lib, ... }: { - options.foo = lib.mkOption { type = lib.types.submodule submodule; }; - }; + module = + { lib, ... }: + { + options.foo = lib.mkOption { type = lib.types.submodule submodule; }; + }; - options = (evalModules { modules = [ module ]; }).options; + options = (evalModules { modules = [ module ]; }).options; - locs = filter (o: !o.internal) (optionAttrSetToDocList options); - in map (o: o.loc) locs; + locs = filter (o: !o.internal) (optionAttrSetToDocList options); + in + map (o: o.loc) locs; expected = [ - [ "_module" "args" ] + [ + "_module" + "args" + ] [ "foo" ] - [ "foo" "" "bar" ] - [ "foo" "bar" ] + [ + "foo" + "" + "bar" + ] + [ + "foo" + "bar" + ] ]; }; @@ -1850,14 +2638,27 @@ in runTests { }; testCartesianProductOfOneSet = { - expr = cartesianProduct { a = [ 1 2 3 ]; }; - expected = [ { a = 1; } { a = 2; } { a = 3; } ]; + expr = cartesianProduct { + a = [ + 1 + 2 + 3 + ]; + }; + expected = [ + { a = 1; } + { a = 2; } + { a = 3; } + ]; }; testCartesianProductOfTwoSets = { expr = cartesianProduct { a = [ 1 ]; - b = [ 10 20 ]; + b = [ + 10 + 20 + ]; }; expected = [ { @@ -1874,16 +2675,31 @@ in runTests { testCartesianProductOfTwoSetsWithOneEmpty = { expr = cartesianProduct { a = [ ]; - b = [ 10 20 ]; + b = [ + 10 + 20 + ]; }; expected = [ ]; }; testCartesianProductOfThreeSets = { expr = cartesianProduct { - a = [ 1 2 3 ]; - b = [ 10 20 30 ]; - c = [ 100 200 300 ]; + a = [ + 1 + 2 + 3 + ]; + b = [ + 10 + 20 + 30 + ]; + c = [ + 100 + 200 + 300 + ]; }; expected = [ { @@ -2033,32 +2849,73 @@ in runTests { }; testMapCartesianProductOfOneSet = { - expr = mapCartesianProduct ({ a }: a * 2) { a = [ 1 2 3 ]; }; - expected = [ 2 4 6 ]; + expr = mapCartesianProduct ({ a }: a * 2) { + a = [ + 1 + 2 + 3 + ]; + }; + expected = [ + 2 + 4 + 6 + ]; }; testMapCartesianProductOfTwoSets = { - expr = mapCartesianProduct ({ a, b, }: a + b) { + expr = mapCartesianProduct ({ a, b }: a + b) { a = [ 1 ]; - b = [ 10 20 ]; + b = [ + 10 + 20 + ]; }; - expected = [ 11 21 ]; + expected = [ + 11 + 21 + ]; }; testMapCartesianProcutOfTwoSetsWithOneEmpty = { expr = mapCartesianProduct (x: x.a + x.b) { a = [ ]; - b = [ 10 20 ]; + b = [ + 10 + 20 + ]; }; expected = [ ]; }; testMapCartesianProductOfThreeSets = { - expr = mapCartesianProduct ({ a, b, c, }: a + b + c) { - a = [ 1 2 3 ]; - b = [ 10 20 30 ]; - c = [ 100 200 300 ]; - }; + expr = + mapCartesianProduct + ( + { + a, + b, + c, + }: + a + b + c + ) + { + a = [ + 1 + 2 + 3 + ]; + b = [ + 10 + 20 + 30 + ]; + c = [ + 100 + 200 + 300 + ]; + }; expected = [ 111 211 @@ -2092,7 +2949,11 @@ in runTests { # The example from the showAttrPath documentation testShowAttrPathExample = { - expr = showAttrPath [ "foo" "10" "bar" ]; + expr = showAttrPath [ + "foo" + "10" + "bar" + ]; expected = ''foo."10".bar''; }; @@ -2102,23 +2963,57 @@ in runTests { }; testShowAttrPathVarious = { - expr = showAttrPath [ "." "foo" "2" "a2-b" "_bc'de" ]; + expr = showAttrPath [ + "." + "foo" + "2" + "a2-b" + "_bc'de" + ]; expected = ''".".foo."2".a2-b._bc'de''; }; testGroupBy = { expr = groupBy (n: toString (mod n 5)) (range 0 16); expected = { - "0" = [ 0 5 10 15 ]; - "1" = [ 1 6 11 16 ]; - "2" = [ 2 7 12 ]; - "3" = [ 3 8 13 ]; - "4" = [ 4 9 14 ]; + "0" = [ + 0 + 5 + 10 + 15 + ]; + "1" = [ + 1 + 6 + 11 + 16 + ]; + "2" = [ + 2 + 7 + 12 + ]; + "3" = [ + 3 + 8 + 13 + ]; + "4" = [ + 4 + 9 + 14 + ]; }; }; testGroupBy' = { - expr = groupBy' builtins.add 0 (x: boolToString (x > 2)) [ 5 1 2 3 4 ]; + expr = groupBy' builtins.add 0 (x: boolToString (x > 2)) [ + 5 + 1 + 2 + 3 + 4 + ]; expected = { false = 3; true = 12; @@ -2129,21 +3024,37 @@ in runTests { testUpdateManyAttrsByPathExample = { expr = updateManyAttrsByPath [ { - path = [ "a" "b" ]; + path = [ + "a" + "b" + ]; update = old: { d = old.c; }; } { - path = [ "a" "b" "c" ]; + path = [ + "a" + "b" + "c" + ]; update = old: old + 1; } { - path = [ "x" "y" ]; + path = [ + "x" + "y" + ]; update = old: "xy"; } ] { a.b.c = 0; }; expected = { - a = { b = { d = 1; }; }; - x = { y = "xy"; }; + a = { + b = { + d = 1; + }; + }; + x = { + y = "xy"; + }; }; }; @@ -2155,10 +3066,12 @@ in runTests { # A single update to the root path is just like applying the function directly testUpdateManyAttrsByPathSingleIncrement = { - expr = updateManyAttrsByPath [{ - path = [ ]; - update = old: old + 1; - }] 0; + expr = updateManyAttrsByPath [ + { + path = [ ]; + update = old: old + 1; + } + ] 0; expected = 1; }; @@ -2198,16 +3111,25 @@ in runTests { # Deeply nested attributes can be updated without affecting others testUpdateManyAttrsByPathDeep = { - expr = updateManyAttrsByPath [{ - path = [ "a" "b" "c" ]; - update = old: old + 1; - }] { - a.b.c = 0; + expr = + updateManyAttrsByPath + [ + { + path = [ + "a" + "b" + "c" + ]; + update = old: old + 1; + } + ] + { + a.b.c = 0; - a.b.z = 0; - a.y.z = 0; - x.y.z = 0; - }; + a.b.z = 0; + a.y.z = 0; + x.y.z = 0; + }; expected = { a.b.c = 1; @@ -2225,7 +3147,10 @@ in runTests { update = old: old // { x = old.b; }; } { - path = [ "a" "b" ]; + path = [ + "a" + "b" + ]; update = old: old + 1; } ] { a.b = 0; }; @@ -2391,7 +3316,9 @@ in runTests { # DERIVATIONS testLazyDerivationIsLazyInDerivationForAttrNames = { - expr = attrNames (lazyDerivation { derivation = throw "not lazy enough"; }); + expr = attrNames (lazyDerivation { + derivation = throw "not lazy enough"; + }); # It's ok to add attribute names here when lazyDerivation is improved # in accordance with its inline comments. expected = [ @@ -2408,87 +3335,108 @@ in runTests { }; testLazyDerivationIsLazyInDerivationForPassthruAttr = { - expr = (lazyDerivation { - derivation = throw "not lazy enough"; - passthru.tests = "whatever is in tests"; - }).tests; + expr = + (lazyDerivation { + derivation = throw "not lazy enough"; + passthru.tests = "whatever is in tests"; + }).tests; expected = "whatever is in tests"; }; testLazyDerivationIsLazyInDerivationForPassthruAttr2 = { # passthru.tests is not a special case. It works for any attr. - expr = (lazyDerivation { - derivation = throw "not lazy enough"; - passthru.foo = "whatever is in foo"; - }).foo; + expr = + (lazyDerivation { + derivation = throw "not lazy enough"; + passthru.foo = "whatever is in foo"; + }).foo; expected = "whatever is in foo"; }; testLazyDerivationIsLazyInDerivationForMeta = { - expr = (lazyDerivation { - derivation = throw "not lazy enough"; - meta = "whatever is in meta"; - }).meta; + expr = + (lazyDerivation { + derivation = throw "not lazy enough"; + meta = "whatever is in meta"; + }).meta; expected = "whatever is in meta"; }; - testLazyDerivationReturnsDerivationAttrs = let - derivation = { - type = "derivation"; - outputs = [ "out" ]; - out = "test out"; - outPath = "test outPath"; - outputName = "out"; - drvPath = "test drvPath"; - name = "test name"; - system = "test system"; - meta = "test meta"; - }; - in { - expr = lazyDerivation { inherit derivation; }; - expected = derivation; - }; - - testOptionalDrvAttr = let - mkDerivation = args: - derivation (args // { - builder = "builder"; - system = "system"; - __ignoreNulls = true; - }); - in { - expr = (mkDerivation { - name = "foo"; - x = optionalDrvAttr true 1; - y = optionalDrvAttr false 1; - }).drvPath; - expected = (mkDerivation { - name = "foo"; - x = 1; - }).drvPath; - }; - - testLazyDerivationMultiOutputReturnsDerivationAttrs = let - derivation = { - type = "derivation"; - outputs = [ "out" "dev" ]; - dev = "test dev"; - out = "test out"; - outPath = "test outPath"; - outputName = "out"; - drvPath = "test drvPath"; - name = "test name"; - system = "test system"; - meta.position = "/hi:23"; + testLazyDerivationReturnsDerivationAttrs = + let + derivation = { + type = "derivation"; + outputs = [ "out" ]; + out = "test out"; + outPath = "test outPath"; + outputName = "out"; + drvPath = "test drvPath"; + name = "test name"; + system = "test system"; + meta = "test meta"; + }; + in + { + expr = lazyDerivation { inherit derivation; }; + expected = derivation; }; - in { - expr = lazyDerivation { - inherit derivation; - outputs = [ "out" "dev" ]; - passthru.meta.position = "/hi:23"; + + testOptionalDrvAttr = + let + mkDerivation = + args: + derivation ( + args + // { + builder = "builder"; + system = "system"; + __ignoreNulls = true; + } + ); + in + { + expr = + (mkDerivation { + name = "foo"; + x = optionalDrvAttr true 1; + y = optionalDrvAttr false 1; + }).drvPath; + expected = + (mkDerivation { + name = "foo"; + x = 1; + }).drvPath; + }; + + testLazyDerivationMultiOutputReturnsDerivationAttrs = + let + derivation = { + type = "derivation"; + outputs = [ + "out" + "dev" + ]; + dev = "test dev"; + out = "test out"; + outPath = "test outPath"; + outputName = "out"; + drvPath = "test drvPath"; + name = "test name"; + system = "test system"; + meta.position = "/hi:23"; + }; + in + { + expr = lazyDerivation { + inherit derivation; + outputs = [ + "out" + "dev" + ]; + passthru.meta.position = "/hi:23"; + }; + expected = derivation; }; - expected = derivation; - }; testTypeDescriptionInt = { expr = (with types; int).description; @@ -2500,8 +3448,7 @@ in runTests { }; testTypeDescriptionIntsPositiveOrEnumAuto = { expr = (with types; either ints.positive (enum [ "auto" ])).description; - expected = - ''positive integer, meaning >0, or value "auto" (singular enum)''; + expected = ''positive integer, meaning >0, or value "auto" (singular enum)''; }; testTypeDescriptionListOfPositive = { expr = (with types; listOf ints.positive).description; @@ -2528,23 +3475,35 @@ in runTests { expected = "string or list of boolean"; }; testTypeDescriptionOneOfListOfStrOrBool = { - expr = (with types; oneOf [ (listOf bool) str ]).description; + expr = + ( + with types; + oneOf [ + (listOf bool) + str + ] + ).description; expected = "(list of boolean) or string"; }; testTypeDescriptionOneOfListOfStrOrBoolOrNumber = { - expr = (with types; oneOf [ (listOf bool) str number ]).description; - expected = - "(list of boolean) or string or signed integer or floating point number"; + expr = + ( + with types; + oneOf [ + (listOf bool) + str + number + ] + ).description; + expected = "(list of boolean) or string or signed integer or floating point number"; }; testTypeDescriptionEitherListOfBoolOrEitherStringOrNumber = { expr = (with types; either (listOf bool) (either str number)).description; - expected = - "(list of boolean) or string or signed integer or floating point number"; + expected = "(list of boolean) or string or signed integer or floating point number"; }; testTypeDescriptionEitherEitherListOfBoolOrStringOrNumber = { expr = (with types; either (either (listOf bool) str) number).description; - expected = - "(list of boolean) or string or signed integer or floating point number"; + expected = "(list of boolean) or string or signed integer or floating point number"; }; testTypeDescriptionEitherNullOrBoolOrString = { expr = (with types; either (nullOr bool) str).description; @@ -2579,11 +3538,9 @@ in runTests { expected = "somelonghash/bin/mainProgram"; }; - testGetExe'FailureFirstArg = - testingThrow (getExe' "not a derivation" "executable"); + testGetExe'FailureFirstArg = testingThrow (getExe' "not a derivation" "executable"); - testGetExe'FailureSecondArg = - testingThrow (getExe' { type = "derivation"; } "dir/executable"); + testGetExe'FailureSecondArg = testingThrow (getExe' { type = "derivation"; } "dir/executable"); testPlatformMatch = { expr = meta.platformMatch { system = "x86_64-linux"; } "x86_64-linux"; @@ -2591,8 +3548,8 @@ in runTests { }; testPlatformMatchAttrs = { - expr = meta.platformMatch (systems.elaborate "x86_64-linux") - (systems.elaborate "x86_64-linux").parsed; + expr = meta.platformMatch (systems.elaborate "x86_64-linux") (systems.elaborate "x86_64-linux") + .parsed; expected = true; }; diff --git a/extra/tests/stdlib/modules/adhoc-freeformType-survives-type-merge.nix b/extra/tests/stdlib/modules/adhoc-freeformType-survives-type-merge.nix index b7ea521..b15a9d9 100644 --- a/extra/tests/stdlib/modules/adhoc-freeformType-survives-type-merge.nix +++ b/extra/tests/stdlib/modules/adhoc-freeformType-survives-type-merge.nix @@ -1,14 +1,16 @@ -{ lib, ... }: { +{ lib, ... }: +{ options.dummy = lib.mkOption { type = lib.types.anything; default = { }; }; - freeformType = let - a = lib.types.attrsOf - (lib.types.submodule { options.bar = lib.mkOption { }; }); + freeformType = + let + a = lib.types.attrsOf (lib.types.submodule { options.bar = lib.mkOption { }; }); + in # modifying types like this breaks type merging. # This test makes sure that type merging is not performed when only a single declaration exists. # Don't modify types in practice! - in a // { merge = loc: defs: { freeformItems = a.merge loc defs; }; }; + a // { merge = loc: defs: { freeformItems = a.merge loc defs; }; }; config.foo.bar = "ok"; } diff --git a/extra/tests/stdlib/modules/alias-with-priority-can-override.nix b/extra/tests/stdlib/modules/alias-with-priority-can-override.nix index 39b76b3..4602a19 100644 --- a/extra/tests/stdlib/modules/alias-with-priority-can-override.nix +++ b/extra/tests/stdlib/modules/alias-with-priority-can-override.nix @@ -6,9 +6,15 @@ { config, lib, ... }: -let inherit (lib) mkAliasOptionModule mkForce mkOption types; - -in { +let + inherit (lib) + mkAliasOptionModule + mkForce + mkOption + types + ; +in +{ options = { # A simple boolean option that can be enabled or disabled. enable = mkOption { @@ -39,9 +45,19 @@ in { # Disable the aliased option with a high priority so it # should override the next import. - ({ config, lib, ... }: { enableAlias = mkForce false; }) + ( + { config, lib, ... }: + { + enableAlias = mkForce false; + } + ) # Enable the normal (non-aliased) option. - ({ config, lib, ... }: { enable = true; }) + ( + { config, lib, ... }: + { + enable = true; + } + ) ]; } diff --git a/extra/tests/stdlib/modules/alias-with-priority.nix b/extra/tests/stdlib/modules/alias-with-priority.nix index bd2f7dd..79ee3e9 100644 --- a/extra/tests/stdlib/modules/alias-with-priority.nix +++ b/extra/tests/stdlib/modules/alias-with-priority.nix @@ -6,9 +6,15 @@ { config, lib, ... }: -let inherit (lib) mkAliasOptionModule mkDefault mkOption types; - -in { +let + inherit (lib) + mkAliasOptionModule + mkDefault + mkOption + types + ; +in +{ options = { # A simple boolean option that can be enabled or disabled. enable = mkOption { @@ -39,9 +45,19 @@ in { # Disable the aliased option, but with a default (low) priority so it # should be able to be overridden by the next import. - ({ config, lib, ... }: { enableAlias = mkDefault false; }) + ( + { config, lib, ... }: + { + enableAlias = mkDefault false; + } + ) # Enable the normal (non-aliased) option. - ({ config, lib, ... }: { enable = true; }) + ( + { config, lib, ... }: + { + enable = true; + } + ) ]; } diff --git a/extra/tests/stdlib/modules/attrsOf-conditional-check.nix b/extra/tests/stdlib/modules/attrsOf-conditional-check.nix index a221c17..3f5ffe1 100644 --- a/extra/tests/stdlib/modules/attrsOf-conditional-check.nix +++ b/extra/tests/stdlib/modules/attrsOf-conditional-check.nix @@ -1,4 +1,5 @@ -{ lib, config, ... }: { +{ lib, config, ... }: +{ options.conditionalWorks = lib.mkOption { default = !config.value ? foo; }; config.value.foo = lib.mkIf false "should not be defined"; diff --git a/extra/tests/stdlib/modules/attrsOf-lazy-check.nix b/extra/tests/stdlib/modules/attrsOf-lazy-check.nix index a140fd0..a17a6fa 100644 --- a/extra/tests/stdlib/modules/attrsOf-lazy-check.nix +++ b/extra/tests/stdlib/modules/attrsOf-lazy-check.nix @@ -1,4 +1,5 @@ -{ lib, config, ... }: { +{ lib, config, ... }: +{ options.isLazy = lib.mkOption { default = !config.value ? foo; }; config.value.bar = throw "is not lazy"; diff --git a/extra/tests/stdlib/modules/boolByOr.nix b/extra/tests/stdlib/modules/boolByOr.nix index a0d63b7..cc2ca50 100644 --- a/extra/tests/stdlib/modules/boolByOr.nix +++ b/extra/tests/stdlib/modules/boolByOr.nix @@ -1,13 +1,24 @@ -{ lib, ... }: { +{ lib, ... }: +{ - options.value = - lib.mkOption { type = lib.types.lazyAttrsOf lib.types.boolByOr; }; + options.value = lib.mkOption { type = lib.types.lazyAttrsOf lib.types.boolByOr; }; config.value = { - falseFalse = lib.mkMerge [ false false ]; - trueFalse = lib.mkMerge [ true false ]; - falseTrue = lib.mkMerge [ false true ]; - trueTrue = lib.mkMerge [ true true ]; + falseFalse = lib.mkMerge [ + false + false + ]; + trueFalse = lib.mkMerge [ + true + false + ]; + falseTrue = lib.mkMerge [ + false + true + ]; + trueTrue = lib.mkMerge [ + true + true + ]; }; } - diff --git a/extra/tests/stdlib/modules/class-check.nix b/extra/tests/stdlib/modules/class-check.nix index 9bc41d8..9240478 100644 --- a/extra/tests/stdlib/modules/class-check.nix +++ b/extra/tests/stdlib/modules/class-check.nix @@ -1,4 +1,5 @@ -{ lib, ... }: { +{ lib, ... }: +{ options = { sub = { nixosOk = lib.mkOption { @@ -24,18 +25,20 @@ }; }; }; - imports = [{ - options = { - sub = { - mergeFail = lib.mkOption { - type = lib.types.submoduleWith { - class = "darwin"; - modules = [ ]; + imports = [ + { + options = { + sub = { + mergeFail = lib.mkOption { + type = lib.types.submoduleWith { + class = "darwin"; + modules = [ ]; + }; }; }; }; - }; - }]; + } + ]; config = { _module.freeformType = lib.types.anything; ok = lib.evalModules { @@ -45,7 +48,10 @@ fail = lib.evalModules { class = "nixos"; - modules = [ ./module-class-is-nixos.nix ./module-class-is-darwin.nix ]; + modules = [ + ./module-class-is-nixos.nix + ./module-class-is-darwin.nix + ]; }; fail-anon = lib.evalModules { @@ -61,7 +67,11 @@ ]; }; - sub.nixosOk = { _class = "nixos"; }; - sub.nixosFail = { imports = [ ./module-class-is-darwin.nix ]; }; + sub.nixosOk = { + _class = "nixos"; + }; + sub.nixosFail = { + imports = [ ./module-class-is-darwin.nix ]; + }; }; } diff --git a/extra/tests/stdlib/modules/declaration-positions.nix b/extra/tests/stdlib/modules/declaration-positions.nix index e27221d..d34145a 100644 --- a/extra/tests/stdlib/modules/declaration-positions.nix +++ b/extra/tests/stdlib/modules/declaration-positions.nix @@ -1,23 +1,29 @@ { lib, options, ... }: let discardPositions = lib.mapAttrs (k: v: v); - # unsafeGetAttrPos is unspecified best-effort behavior, so we only want to consider this test on an evaluator that satisfies some basic assumptions about this function. -in assert builtins.unsafeGetAttrPos "a" { a = true; } != null; -assert builtins.unsafeGetAttrPos "a" (discardPositions { a = true; }) == null; { - imports = [{ - options.imported.line8 = lib.mkOption { type = lib.types.int; }; +in +# unsafeGetAttrPos is unspecified best-effort behavior, so we only want to consider this test on an evaluator that satisfies some basic assumptions about this function. +assert builtins.unsafeGetAttrPos "a" { a = true; } != null; +assert + builtins.unsafeGetAttrPos "a" (discardPositions { + a = true; + }) == null; +{ + imports = [ + { + options.imported.line8 = lib.mkOption { type = lib.types.int; }; - # Simulates various patterns of generating modules such as - # programs.firefox.nativeMessagingHosts.ff2mpv. We don't expect to get - # line numbers for these, but we can fall back on knowing the file. - options.generated = - discardPositions { line14 = lib.mkOption { type = lib.types.int; }; }; + # Simulates various patterns of generating modules such as + # programs.firefox.nativeMessagingHosts.ff2mpv. We don't expect to get + # line numbers for these, but we can fall back on knowing the file. + options.generated = discardPositions { line14 = lib.mkOption { type = lib.types.int; }; }; - options.submoduleLine16.extraOptLine16 = lib.mkOption { - default = 1; - type = lib.types.int; - }; - }]; + options.submoduleLine16.extraOptLine16 = lib.mkOption { + default = 1; + type = lib.types.int; + }; + } + ]; options.nested.nestedLine22 = lib.mkOption { type = lib.types.int; }; @@ -25,7 +31,12 @@ assert builtins.unsafeGetAttrPos "a" (discardPositions { a = true; }) == null; { default = { }; type = lib.types.submoduleWith { modules = [ - ({ options, ... }: { options.submodDeclLine28 = lib.mkOption { }; }) + ( + { options, ... }: + { + options.submodDeclLine28 = lib.mkOption { }; + } + ) { freeformType = with lib.types; lazyAttrsOf (uniq unspecified); } ]; }; @@ -33,7 +44,6 @@ assert builtins.unsafeGetAttrPos "a" (discardPositions { a = true; }) == null; { config = { submoduleLine24.submodDeclLine28 = - (options.submoduleLine24.type.getSubOptions - [ ]).submodDeclLine28.declarationPositions; + (options.submoduleLine24.type.getSubOptions [ ]).submodDeclLine28.declarationPositions; }; } diff --git a/extra/tests/stdlib/modules/declare-attrsOf.nix b/extra/tests/stdlib/modules/declare-attrsOf.nix index 184884b..93c866d 100644 --- a/extra/tests/stdlib/modules/declare-attrsOf.nix +++ b/extra/tests/stdlib/modules/declare-attrsOf.nix @@ -1,10 +1,10 @@ { lib, ... }: let - deathtrapArgs = lib.mapAttrs (k: _: - throw - "The module system is too strict, accessing an unused option's ${k} mkOption-attribute.") - (lib.functionArgs lib.mkOption); -in { + deathtrapArgs = lib.mapAttrs ( + k: _: throw "The module system is too strict, accessing an unused option's ${k} mkOption-attribute." + ) (lib.functionArgs lib.mkOption); +in +{ options.value = lib.mkOption { type = lib.types.attrsOf lib.types.str; default = { }; diff --git a/extra/tests/stdlib/modules/declare-attrsOfSub-any-enable.nix b/extra/tests/stdlib/modules/declare-attrsOfSub-any-enable.nix index 8afe303..10cba1c 100644 --- a/extra/tests/stdlib/modules/declare-attrsOfSub-any-enable.nix +++ b/extra/tests/stdlib/modules/declare-attrsOfSub-any-enable.nix @@ -1,20 +1,22 @@ { lib, ... }: let - submod = { ... }: { - options = { - enable = lib.mkOption { - default = false; - example = true; - type = lib.types.bool; - description = '' - Some descriptive text - ''; + submod = + { ... }: + { + options = { + enable = lib.mkOption { + default = false; + example = true; + type = lib.types.bool; + description = '' + Some descriptive text + ''; + }; }; }; - }; - -in { +in +{ options = { attrsOfSub = lib.mkOption { default = { }; diff --git a/extra/tests/stdlib/modules/declare-bare-submodule-deep-option-duplicate.nix b/extra/tests/stdlib/modules/declare-bare-submodule-deep-option-duplicate.nix index 078a8e3..06ad1f6 100644 --- a/extra/tests/stdlib/modules/declare-bare-submodule-deep-option-duplicate.nix +++ b/extra/tests/stdlib/modules/declare-bare-submodule-deep-option-duplicate.nix @@ -1,6 +1,8 @@ { lib, ... }: -let inherit (lib) mkOption types; -in { +let + inherit (lib) mkOption types; +in +{ options.bare-submodule.deep = mkOption { type = types.int; default = 2; diff --git a/extra/tests/stdlib/modules/declare-bare-submodule-deep-option.nix b/extra/tests/stdlib/modules/declare-bare-submodule-deep-option.nix index 078a8e3..06ad1f6 100644 --- a/extra/tests/stdlib/modules/declare-bare-submodule-deep-option.nix +++ b/extra/tests/stdlib/modules/declare-bare-submodule-deep-option.nix @@ -1,6 +1,8 @@ { lib, ... }: -let inherit (lib) mkOption types; -in { +let + inherit (lib) mkOption types; +in +{ options.bare-submodule.deep = mkOption { type = types.int; default = 2; diff --git a/extra/tests/stdlib/modules/declare-bare-submodule-nested-option.nix b/extra/tests/stdlib/modules/declare-bare-submodule-nested-option.nix index e1660a3..da125c8 100644 --- a/extra/tests/stdlib/modules/declare-bare-submodule-nested-option.nix +++ b/extra/tests/stdlib/modules/declare-bare-submodule-nested-option.nix @@ -1,15 +1,19 @@ { config, lib, ... }: -let inherit (lib) mkOption types; -in { +let + inherit (lib) mkOption types; +in +{ options.bare-submodule = mkOption { type = types.submoduleWith { shorthandOnlyDefinesConfig = config.shorthandOnlyDefinesConfig; - modules = [{ - options.nested = mkOption { - type = types.int; - default = 1; - }; - }]; + modules = [ + { + options.nested = mkOption { + type = types.int; + default = 1; + }; + } + ]; }; }; } diff --git a/extra/tests/stdlib/modules/declare-bare-submodule.nix b/extra/tests/stdlib/modules/declare-bare-submodule.nix index 9bd5743..8ab16ba 100644 --- a/extra/tests/stdlib/modules/declare-bare-submodule.nix +++ b/extra/tests/stdlib/modules/declare-bare-submodule.nix @@ -1,6 +1,8 @@ { config, lib, ... }: -let inherit (lib) mkOption types; -in { +let + inherit (lib) mkOption types; +in +{ options.bare-submodule = mkOption { type = types.submoduleWith { modules = [ ]; diff --git a/extra/tests/stdlib/modules/declare-either.nix b/extra/tests/stdlib/modules/declare-either.nix index e58e523..bd6b2f1 100644 --- a/extra/tests/stdlib/modules/declare-either.nix +++ b/extra/tests/stdlib/modules/declare-either.nix @@ -1,4 +1,4 @@ -{ lib, ... }: { - options.value = - lib.mkOption { type = lib.types.either lib.types.int lib.types.str; }; +{ lib, ... }: +{ + options.value = lib.mkOption { type = lib.types.either lib.types.int lib.types.str; }; } diff --git a/extra/tests/stdlib/modules/declare-int-positive-value-nested.nix b/extra/tests/stdlib/modules/declare-int-positive-value-nested.nix index ff2b3f1..312af9d 100644 --- a/extra/tests/stdlib/modules/declare-int-positive-value-nested.nix +++ b/extra/tests/stdlib/modules/declare-int-positive-value-nested.nix @@ -1,5 +1,7 @@ { lib, ... }: { - options.set = { value = lib.mkOption { type = lib.types.ints.positive; }; }; + options.set = { + value = lib.mkOption { type = lib.types.ints.positive; }; + }; } diff --git a/extra/tests/stdlib/modules/declare-int-positive-value.nix b/extra/tests/stdlib/modules/declare-int-positive-value.nix index 2a70a82..2a2b58d 100644 --- a/extra/tests/stdlib/modules/declare-int-positive-value.nix +++ b/extra/tests/stdlib/modules/declare-int-positive-value.nix @@ -1,5 +1,7 @@ { lib, ... }: { - options = { value = lib.mkOption { type = lib.types.ints.positive; }; }; + options = { + value = lib.mkOption { type = lib.types.ints.positive; }; + }; } diff --git a/extra/tests/stdlib/modules/declare-int-unsigned-value.nix b/extra/tests/stdlib/modules/declare-int-unsigned-value.nix index 99c357a..a0b4257 100644 --- a/extra/tests/stdlib/modules/declare-int-unsigned-value.nix +++ b/extra/tests/stdlib/modules/declare-int-unsigned-value.nix @@ -1,5 +1,7 @@ { lib, ... }: { - options = { value = lib.mkOption { type = lib.types.ints.unsigned; }; }; + options = { + value = lib.mkOption { type = lib.types.ints.unsigned; }; + }; } diff --git a/extra/tests/stdlib/modules/declare-lazyAttrsOf.nix b/extra/tests/stdlib/modules/declare-lazyAttrsOf.nix index fe9a8cc..f98b705 100644 --- a/extra/tests/stdlib/modules/declare-lazyAttrsOf.nix +++ b/extra/tests/stdlib/modules/declare-lazyAttrsOf.nix @@ -1,7 +1,7 @@ -{ lib, ... }: { +{ lib, ... }: +{ options.value = lib.mkOption { - type = - lib.types.lazyAttrsOf (lib.types.str // { emptyValue.value = "empty"; }); + type = lib.types.lazyAttrsOf (lib.types.str // { emptyValue.value = "empty"; }); default = { }; }; } diff --git a/extra/tests/stdlib/modules/declare-mkPackageOption.nix b/extra/tests/stdlib/modules/declare-mkPackageOption.nix index 5268b9e..8009174 100644 --- a/extra/tests/stdlib/modules/declare-mkPackageOption.nix +++ b/extra/tests/stdlib/modules/declare-mkPackageOption.nix @@ -4,14 +4,14 @@ let type = "derivation"; pname = "hello"; }; -in { +in +{ options = { package = lib.mkPackageOption pkgs "hello" { }; namedPackage = lib.mkPackageOption pkgs "Hello" { default = [ "hello" ]; }; - namedPackageSingletonDefault = - lib.mkPackageOption pkgs "Hello" { default = "hello"; }; + namedPackageSingletonDefault = lib.mkPackageOption pkgs "Hello" { default = "hello"; }; pathPackage = lib.mkPackageOption pkgs [ "hello" ] { }; @@ -19,8 +19,7 @@ in { example = "pkgs.hello.override { stdenv = pkgs.clangStdenv; }"; }; - packageWithPathExample = - lib.mkPackageOption pkgs "hello" { example = [ "hello" ]; }; + packageWithPathExample = lib.mkPackageOption pkgs "hello" { example = [ "hello" ]; }; packageWithExtraDescription = lib.mkPackageOption pkgs "hello" { extraDescription = "Example extra description."; @@ -33,14 +32,18 @@ in { default = null; }; - nullablePackageWithDefault = - lib.mkPackageOption pkgs "hello" { nullable = true; }; + nullablePackageWithDefault = lib.mkPackageOption pkgs "hello" { nullable = true; }; - packageWithPkgsText = - lib.mkPackageOption pkgs "hello" { pkgsText = "myPkgs"; }; + packageWithPkgsText = lib.mkPackageOption pkgs "hello" { pkgsText = "myPkgs"; }; packageFromOtherSet = - let myPkgs = { hello = pkgs.hello // { pname = "hello-other"; }; }; - in lib.mkPackageOption myPkgs "hello" { }; + let + myPkgs = { + hello = pkgs.hello // { + pname = "hello-other"; + }; + }; + in + lib.mkPackageOption myPkgs "hello" { }; }; } diff --git a/extra/tests/stdlib/modules/declare-oneOf.nix b/extra/tests/stdlib/modules/declare-oneOf.nix index df092a1..3d25f45 100644 --- a/extra/tests/stdlib/modules/declare-oneOf.nix +++ b/extra/tests/stdlib/modules/declare-oneOf.nix @@ -1,4 +1,5 @@ -{ lib, ... }: { +{ lib, ... }: +{ options.value = lib.mkOption { type = lib.types.oneOf [ lib.types.int diff --git a/extra/tests/stdlib/modules/declare-set.nix b/extra/tests/stdlib/modules/declare-set.nix index 8534185..884e6f3 100644 --- a/extra/tests/stdlib/modules/declare-set.nix +++ b/extra/tests/stdlib/modules/declare-set.nix @@ -3,7 +3,9 @@ { options.set = lib.mkOption { default = { }; - example = { a = 1; }; + example = { + a = 1; + }; type = lib.types.attrsOf lib.types.int; description = '' Some descriptive text diff --git a/extra/tests/stdlib/modules/declare-submodule-via-evalModules.nix b/extra/tests/stdlib/modules/declare-submodule-via-evalModules.nix index dcf3b4a..ae47bde 100644 --- a/extra/tests/stdlib/modules/declare-submodule-via-evalModules.nix +++ b/extra/tests/stdlib/modules/declare-submodule-via-evalModules.nix @@ -1,24 +1,32 @@ -{ lib, ... }: { +{ lib, ... }: +{ options.submodule = lib.mkOption { - inherit (lib.evalModules { - modules = [{ - options.inner = lib.mkOption { - type = lib.types.bool; - default = false; - }; - }]; - }) - type; + inherit + (lib.evalModules { + modules = [ + { + options.inner = lib.mkOption { + type = lib.types.bool; + default = false; + }; + } + ]; + }) + type + ; default = { }; }; config.submodule = lib.mkMerge [ - ({ lib, ... }: { - options.outer = lib.mkOption { - type = lib.types.bool; - default = false; - }; - }) + ( + { lib, ... }: + { + options.outer = lib.mkOption { + type = lib.types.bool; + default = false; + }; + } + ) { inner = true; outer = true; diff --git a/extra/tests/stdlib/modules/declare-submoduleWith-modules.nix b/extra/tests/stdlib/modules/declare-submoduleWith-modules.nix index 271825a..a52cb27 100644 --- a/extra/tests/stdlib/modules/declare-submoduleWith-modules.nix +++ b/extra/tests/stdlib/modules/declare-submoduleWith-modules.nix @@ -1,23 +1,29 @@ -{ lib, ... }: { +{ lib, ... }: +{ options.submodule = lib.mkOption { type = lib.types.submoduleWith { - modules = [{ - options.inner = lib.mkOption { - type = lib.types.bool; - default = false; - }; - }]; + modules = [ + { + options.inner = lib.mkOption { + type = lib.types.bool; + default = false; + }; + } + ]; }; default = { }; }; config.submodule = lib.mkMerge [ - ({ lib, ... }: { - options.outer = lib.mkOption { - type = lib.types.bool; - default = false; - }; - }) + ( + { lib, ... }: + { + options.outer = lib.mkOption { + type = lib.types.bool; + default = false; + }; + } + ) { inner = true; outer = true; diff --git a/extra/tests/stdlib/modules/declare-submoduleWith-noshorthand.nix b/extra/tests/stdlib/modules/declare-submoduleWith-noshorthand.nix index 4d203a5..92abd24 100644 --- a/extra/tests/stdlib/modules/declare-submoduleWith-noshorthand.nix +++ b/extra/tests/stdlib/modules/declare-submoduleWith-noshorthand.nix @@ -4,7 +4,8 @@ let type = lib.types.bool; default = false; }; -in { +in +{ options.submodule = lib.mkOption { type = lib.types.submoduleWith { modules = [ sub ]; }; default = { }; diff --git a/extra/tests/stdlib/modules/declare-submoduleWith-path.nix b/extra/tests/stdlib/modules/declare-submoduleWith-path.nix index 5acab9f..e7bfe55 100644 --- a/extra/tests/stdlib/modules/declare-submoduleWith-path.nix +++ b/extra/tests/stdlib/modules/declare-submoduleWith-path.nix @@ -1,4 +1,5 @@ -{ lib, ... }: { +{ lib, ... }: +{ options.submodule = lib.mkOption { type = lib.types.submoduleWith { modules = [ ./declare-enable.nix ]; }; default = { }; diff --git a/extra/tests/stdlib/modules/declare-submoduleWith-shorthand.nix b/extra/tests/stdlib/modules/declare-submoduleWith-shorthand.nix index e051f06..8d9d919 100644 --- a/extra/tests/stdlib/modules/declare-submoduleWith-shorthand.nix +++ b/extra/tests/stdlib/modules/declare-submoduleWith-shorthand.nix @@ -4,7 +4,8 @@ let type = lib.types.bool; default = false; }; -in { +in +{ options.submodule = lib.mkOption { type = lib.types.submoduleWith { modules = [ sub ]; diff --git a/extra/tests/stdlib/modules/declare-submoduleWith-special.nix b/extra/tests/stdlib/modules/declare-submoduleWith-special.nix index cae49ee..b2c72be 100644 --- a/extra/tests/stdlib/modules/declare-submoduleWith-special.nix +++ b/extra/tests/stdlib/modules/declare-submoduleWith-special.nix @@ -1,10 +1,18 @@ -{ lib, ... }: { +{ lib, ... }: +{ options.submodule = lib.mkOption { type = lib.types.submoduleWith { modules = [ - ({ lib, ... }: { options.foo = lib.mkOption { default = lib.foo; }; }) + ( + { lib, ... }: + { + options.foo = lib.mkOption { default = lib.foo; }; + } + ) ]; - specialArgs.lib = lib // { foo = "foo"; }; + specialArgs.lib = lib // { + foo = "foo"; + }; }; default = { }; }; diff --git a/extra/tests/stdlib/modules/declare-variants.nix b/extra/tests/stdlib/modules/declare-variants.nix index 58fdde3..60271fa 100644 --- a/extra/tests/stdlib/modules/declare-variants.nix +++ b/extra/tests/stdlib/modules/declare-variants.nix @@ -1,6 +1,8 @@ { lib, moduleType, ... }: -let inherit (lib) mkOption types; -in { +let + inherit (lib) mkOption types; +in +{ options.variants = mkOption { type = types.lazyAttrsOf moduleType; default = { }; diff --git a/extra/tests/stdlib/modules/deferred-module-error.nix b/extra/tests/stdlib/modules/deferred-module-error.nix index 1319d20..fded00a 100644 --- a/extra/tests/stdlib/modules/deferred-module-error.nix +++ b/extra/tests/stdlib/modules/deferred-module-error.nix @@ -1,16 +1,28 @@ { config, lib, ... }: let - inherit (lib) types mkOption setDefaultModuleLocation evalModules; - inherit (types) deferredModule lazyAttrsOf submodule str raw enum; -in { + inherit (lib) + types + mkOption + setDefaultModuleLocation + evalModules + ; + inherit (types) + deferredModule + lazyAttrsOf + submodule + str + raw + enum + ; +in +{ options = { deferred = mkOption { type = deferredModule; }; - result = mkOption { - default = (evalModules { modules = [ config.deferred ]; }).config.result; - }; + result = mkOption { default = (evalModules { modules = [ config.deferred ]; }).config.result; }; }; config = { - deferred = { ... }: + deferred = + { ... }: # this should be an attrset, so this fails true; }; diff --git a/extra/tests/stdlib/modules/deferred-module.nix b/extra/tests/stdlib/modules/deferred-module.nix index 3249ab1..1bafb4c 100644 --- a/extra/tests/stdlib/modules/deferred-module.nix +++ b/extra/tests/stdlib/modules/deferred-module.nix @@ -1,37 +1,52 @@ { lib, ... }: let inherit (lib) types mkOption setDefaultModuleLocation; - inherit (types) deferredModule lazyAttrsOf submodule str raw enum; -in { + inherit (types) + deferredModule + lazyAttrsOf + submodule + str + raw + enum + ; +in +{ imports = [ # generic module, declaring submodules: # - nodes. # - default # where all nodes include the default - ({ config, ... }: { - _file = "generic.nix"; - options.nodes = mkOption { - type = lazyAttrsOf (submodule { imports = [ config.default ]; }); - default = { }; - }; - options.default = mkOption { - type = deferredModule; - default = { }; - description = '' - Module that is included in all nodes. - ''; - }; - }) + ( + { config, ... }: + { + _file = "generic.nix"; + options.nodes = mkOption { + type = lazyAttrsOf (submodule { + imports = [ config.default ]; + }); + default = { }; + }; + options.default = mkOption { + type = deferredModule; + default = { }; + description = '' + Module that is included in all nodes. + ''; + }; + } + ) { _file = "default-1.nix"; - default = { config, ... }: { - options.settingsDict = lib.mkOption { - type = lazyAttrsOf str; - default = { }; + default = + { config, ... }: + { + options.settingsDict = lib.mkOption { + type = lazyAttrsOf str; + default = { }; + }; + options.bottom = lib.mkOption { type = enum [ ]; }; }; - options.bottom = lib.mkOption { type = enum [ ]; }; - }; } { @@ -51,8 +66,11 @@ in { { _file = "nodes-foo-c-is-a.nix"; - nodes.foo = { config, ... }: { settingsDict.c = config.settingsDict.a; }; + nodes.foo = + { config, ... }: + { + settingsDict.c = config.settingsDict.a; + }; } - ]; } diff --git a/extra/tests/stdlib/modules/define-_module-args-custom.nix b/extra/tests/stdlib/modules/define-_module-args-custom.nix index 4329a1c..e565fd2 100644 --- a/extra/tests/stdlib/modules/define-_module-args-custom.nix +++ b/extra/tests/stdlib/modules/define-_module-args-custom.nix @@ -1,5 +1,7 @@ { lib, ... }: { - config = { _module.args.custom = true; }; + config = { + _module.args.custom = true; + }; } diff --git a/extra/tests/stdlib/modules/define-enable-with-custom-arg.nix b/extra/tests/stdlib/modules/define-enable-with-custom-arg.nix index 99caf7d..7da7467 100644 --- a/extra/tests/stdlib/modules/define-enable-with-custom-arg.nix +++ b/extra/tests/stdlib/modules/define-enable-with-custom-arg.nix @@ -1,5 +1,7 @@ { lib, custom, ... }: { - config = { enable = custom; }; + config = { + enable = custom; + }; } diff --git a/extra/tests/stdlib/modules/define-freeform-keywords-shorthand.nix b/extra/tests/stdlib/modules/define-freeform-keywords-shorthand.nix index e346cdf..3481cb5 100644 --- a/extra/tests/stdlib/modules/define-freeform-keywords-shorthand.nix +++ b/extra/tests/stdlib/modules/define-freeform-keywords-shorthand.nix @@ -1,14 +1,24 @@ -{ config, ... }: { - class = { "just" = "data"; }; +{ config, ... }: +{ + class = { + "just" = "data"; + }; a = "one"; b = "two"; meta = "meta"; - _module.args.result = let r = builtins.removeAttrs config [ "_module" ]; - in builtins.trace (builtins.deepSeq r r) (r == { - a = "one"; - b = "two"; - class = { "just" = "data"; }; - meta = "meta"; - }); + _module.args.result = + let + r = builtins.removeAttrs config [ "_module" ]; + in + builtins.trace (builtins.deepSeq r r) ( + r == { + a = "one"; + b = "two"; + class = { + "just" = "data"; + }; + meta = "meta"; + } + ); } diff --git a/extra/tests/stdlib/modules/define-option-dependently-nested.nix b/extra/tests/stdlib/modules/define-option-dependently-nested.nix index 4ee61de..8bc1f2a 100644 --- a/extra/tests/stdlib/modules/define-option-dependently-nested.nix +++ b/extra/tests/stdlib/modules/define-option-dependently-nested.nix @@ -5,10 +5,10 @@ { # Always defined, but the value depends on the presence of an option. - config.set = { - value = if options ? set.enable then 360 else 7; - } - # Only define if possible. + config.set = + { + value = if options ? set.enable then 360 else 7; + } + # Only define if possible. // lib.optionalAttrs (options ? set.enable) { enable = true; }; - } diff --git a/extra/tests/stdlib/modules/define-option-dependently.nix b/extra/tests/stdlib/modules/define-option-dependently.nix index 5876d09..fa5e677 100644 --- a/extra/tests/stdlib/modules/define-option-dependently.nix +++ b/extra/tests/stdlib/modules/define-option-dependently.nix @@ -5,10 +5,10 @@ { # Always defined, but the value depends on the presence of an option. - config = { - value = if options ? enable then 360 else 7; - } - # Only define if possible. + config = + { + value = if options ? enable then 360 else 7; + } + # Only define if possible. // lib.optionalAttrs (options ? enable) { enable = true; }; - } diff --git a/extra/tests/stdlib/modules/define-settingsDict-a-is-b.nix b/extra/tests/stdlib/modules/define-settingsDict-a-is-b.nix index 46b57ed..569891f 100644 --- a/extra/tests/stdlib/modules/define-settingsDict-a-is-b.nix +++ b/extra/tests/stdlib/modules/define-settingsDict-a-is-b.nix @@ -1 +1,4 @@ -{ config, ... }: { settingsDict.a = config.settingsDict.b; } +{ config, ... }: +{ + settingsDict.a = config.settingsDict.b; +} diff --git a/extra/tests/stdlib/modules/define-value-string-properties.nix b/extra/tests/stdlib/modules/define-value-string-properties.nix index 05d35da..7b0722a 100644 --- a/extra/tests/stdlib/modules/define-value-string-properties.nix +++ b/extra/tests/stdlib/modules/define-value-string-properties.nix @@ -1,7 +1,10 @@ -{ lib, ... }: { +{ lib, ... }: +{ - imports = [{ value = lib.mkDefault "def"; }]; - - value = lib.mkMerge [ (lib.mkIf false "nope") "yes" ]; + imports = [ { value = lib.mkDefault "def"; } ]; + value = lib.mkMerge [ + (lib.mkIf false "nope") + "yes" + ]; } diff --git a/extra/tests/stdlib/modules/define-variant.nix b/extra/tests/stdlib/modules/define-variant.nix index f2d7760..d3c28b3 100644 --- a/extra/tests/stdlib/modules/define-variant.nix +++ b/extra/tests/stdlib/modules/define-variant.nix @@ -1,6 +1,8 @@ { config, lib, ... }: -let inherit (lib) types mkOption attrNames; -in { +let + inherit (lib) types mkOption attrNames; +in +{ options = { attrs = mkOption { type = types.attrsOf lib.types.int; }; result = mkOption { }; @@ -14,11 +16,12 @@ in { variants.bar.attrs.y = 1; variants.foo.variants.bar.attrs.z = 1; variants.foo.variants.foo.attrs.c = 3; - resultFoo = lib.concatMapStringsSep " " toString - (attrNames config.variants.foo.attrs); - resultFooBar = lib.concatMapStringsSep " " toString - (attrNames config.variants.foo.variants.bar.attrs); - resultFooFoo = lib.concatMapStringsSep " " toString - (attrNames config.variants.foo.variants.foo.attrs); + resultFoo = lib.concatMapStringsSep " " toString (attrNames config.variants.foo.attrs); + resultFooBar = lib.concatMapStringsSep " " toString ( + attrNames config.variants.foo.variants.bar.attrs + ); + resultFooFoo = lib.concatMapStringsSep " " toString ( + attrNames config.variants.foo.variants.foo.attrs + ); }; } diff --git a/extra/tests/stdlib/modules/disable-enable-modules.nix b/extra/tests/stdlib/modules/disable-enable-modules.nix index c325f4e..aef9ee2 100644 --- a/extra/tests/stdlib/modules/disable-enable-modules.nix +++ b/extra/tests/stdlib/modules/disable-enable-modules.nix @@ -1,5 +1,8 @@ { lib, ... }: { - disabledModules = [ "define-enable.nix" "declare-enable.nix" ]; + disabledModules = [ + "define-enable.nix" + "declare-enable.nix" + ]; } diff --git a/extra/tests/stdlib/modules/disable-module-bad-key.nix b/extra/tests/stdlib/modules/disable-module-bad-key.nix index 5d06c36..7f918b4 100644 --- a/extra/tests/stdlib/modules/disable-module-bad-key.nix +++ b/extra/tests/stdlib/modules/disable-module-bad-key.nix @@ -2,8 +2,15 @@ let inherit (lib) mkOption types; - moduleWithKey = { config, ... }: { config = { enable = true; }; }; -in { + moduleWithKey = + { config, ... }: + { + config = { + enable = true; + }; + }; +in +{ imports = [ ./declare-enable.nix ]; disabledModules = [ { } ]; } diff --git a/extra/tests/stdlib/modules/disable-module-with-key.nix b/extra/tests/stdlib/modules/disable-module-with-key.nix index 4863d0c..196744a 100644 --- a/extra/tests/stdlib/modules/disable-module-with-key.nix +++ b/extra/tests/stdlib/modules/disable-module-with-key.nix @@ -4,18 +4,28 @@ let moduleWithKey = { key = "disable-module-with-key.nix#moduleWithKey"; - config = { enable = true; }; + config = { + enable = true; + }; }; -in { +in +{ options = { positive = mkOption { - type = - types.submodule { imports = [ ./declare-enable.nix moduleWithKey ]; }; + type = types.submodule { + imports = [ + ./declare-enable.nix + moduleWithKey + ]; + }; default = { }; }; negative = mkOption { type = types.submodule { - imports = [ ./declare-enable.nix moduleWithKey ]; + imports = [ + ./declare-enable.nix + moduleWithKey + ]; disabledModules = [ moduleWithKey ]; }; default = { }; diff --git a/extra/tests/stdlib/modules/disable-module-with-toString-key.nix b/extra/tests/stdlib/modules/disable-module-with-toString-key.nix index b41a535..1198eb4 100644 --- a/extra/tests/stdlib/modules/disable-module-with-toString-key.nix +++ b/extra/tests/stdlib/modules/disable-module-with-toString-key.nix @@ -4,18 +4,28 @@ let moduleWithKey = { key = 123; - config = { enable = true; }; + config = { + enable = true; + }; }; -in { +in +{ options = { positive = mkOption { - type = - types.submodule { imports = [ ./declare-enable.nix moduleWithKey ]; }; + type = types.submodule { + imports = [ + ./declare-enable.nix + moduleWithKey + ]; + }; default = { }; }; negative = mkOption { type = types.submodule { - imports = [ ./declare-enable.nix moduleWithKey ]; + imports = [ + ./declare-enable.nix + moduleWithKey + ]; disabledModules = [ 123 ]; }; default = { }; diff --git a/extra/tests/stdlib/modules/disable-recursive/disable-bar.nix b/extra/tests/stdlib/modules/disable-recursive/disable-bar.nix index 98b8fdc..237bf7f 100644 --- a/extra/tests/stdlib/modules/disable-recursive/disable-bar.nix +++ b/extra/tests/stdlib/modules/disable-recursive/disable-bar.nix @@ -1,5 +1,4 @@ { disabledModules = [ ./bar.nix ]; - } diff --git a/extra/tests/stdlib/modules/disable-recursive/disable-foo.nix b/extra/tests/stdlib/modules/disable-recursive/disable-foo.nix index 77efd00..e0f423e 100644 --- a/extra/tests/stdlib/modules/disable-recursive/disable-foo.nix +++ b/extra/tests/stdlib/modules/disable-recursive/disable-foo.nix @@ -1,5 +1,4 @@ { disabledModules = [ ./foo.nix ]; - } diff --git a/extra/tests/stdlib/modules/disable-recursive/main.nix b/extra/tests/stdlib/modules/disable-recursive/main.nix index dbc2f89..48a3c62 100644 --- a/extra/tests/stdlib/modules/disable-recursive/main.nix +++ b/extra/tests/stdlib/modules/disable-recursive/main.nix @@ -1,5 +1,8 @@ { - imports = [ ./foo.nix ./bar.nix ]; + imports = [ + ./foo.nix + ./bar.nix + ]; enable = true; } diff --git a/extra/tests/stdlib/modules/doRename-basic.nix b/extra/tests/stdlib/modules/doRename-basic.nix index 331ba5e..9de48aa 100644 --- a/extra/tests/stdlib/modules/doRename-basic.nix +++ b/extra/tests/stdlib/modules/doRename-basic.nix @@ -1,13 +1,25 @@ -{ lib, ... }: { +{ lib, ... }: +{ imports = [ (lib.doRename { - from = [ "a" "b" ]; - to = [ "c" "d" "e" ]; + from = [ + "a" + "b" + ]; + to = [ + "c" + "d" + "e" + ]; warn = true; use = x: x; visible = true; }) ]; - options = { c.d.e = lib.mkOption { }; }; - config = { a.b = 1234; }; + options = { + c.d.e = lib.mkOption { }; + }; + config = { + a.b = 1234; + }; } diff --git a/extra/tests/stdlib/modules/doRename-condition-enable.nix b/extra/tests/stdlib/modules/doRename-condition-enable.nix index 8bed721..7b3b7ff 100644 --- a/extra/tests/stdlib/modules/doRename-condition-enable.nix +++ b/extra/tests/stdlib/modules/doRename-condition-enable.nix @@ -1,7 +1,15 @@ -{ config, lib, ... }: { +{ config, lib, ... }: +{ config = { services.foo.enable = true; services.foo.bar = "baz"; - result = assert config.services.foos == { "" = { bar = "baz"; }; }; true; + result = + assert + config.services.foos == { + "" = { + bar = "baz"; + }; + }; + true; }; } diff --git a/extra/tests/stdlib/modules/doRename-condition-migrated.nix b/extra/tests/stdlib/modules/doRename-condition-migrated.nix index d436a7a..2129aad 100644 --- a/extra/tests/stdlib/modules/doRename-condition-migrated.nix +++ b/extra/tests/stdlib/modules/doRename-condition-migrated.nix @@ -1,7 +1,14 @@ -{ config, lib, ... }: { +{ config, lib, ... }: +{ config = { services.foos."".bar = "baz"; - result = assert config.services.foos == { "" = { bar = "baz"; }; }; + result = + assert + config.services.foos == { + "" = { + bar = "baz"; + }; + }; assert config.services.foo.bar == "baz"; true; }; diff --git a/extra/tests/stdlib/modules/doRename-condition-no-enable.nix b/extra/tests/stdlib/modules/doRename-condition-no-enable.nix index 443b963..60e2958 100644 --- a/extra/tests/stdlib/modules/doRename-condition-no-enable.nix +++ b/extra/tests/stdlib/modules/doRename-condition-no-enable.nix @@ -1,6 +1,13 @@ -{ config, lib, options, ... }: { +{ + config, + lib, + options, + ... +}: +{ config = { - result = assert config.services.foos == { }; + result = + assert config.services.foos == { }; assert !options.services.foo.bar.isDefined; true; }; diff --git a/extra/tests/stdlib/modules/doRename-condition.nix b/extra/tests/stdlib/modules/doRename-condition.nix index 2be8899..3c9d6cf 100644 --- a/extra/tests/stdlib/modules/doRename-condition.nix +++ b/extra/tests/stdlib/modules/doRename-condition.nix @@ -1,33 +1,54 @@ -/* * - Simulate a migration from a single-instance `services.foo` to a multi instance - `services.foos.` module, where `name = ""` serves as the legacy / - compatibility instance. +/* + * + Simulate a migration from a single-instance `services.foo` to a multi instance + `services.foos.` module, where `name = ""` serves as the legacy / + compatibility instance. - - No instances must exist, unless one is defined in the multi-instance module, - or if the legacy enable option is set to true. - - The legacy instance options must be renamed to the new instance, if it exists. + - No instances must exist, unless one is defined in the multi-instance module, + or if the legacy enable option is set to true. + - The legacy instance options must be renamed to the new instance, if it exists. - The relevant scenarios are tested in separate files: - - ./doRename-condition-enable.nix - - ./doRename-condition-no-enable.nix + The relevant scenarios are tested in separate files: + - ./doRename-condition-enable.nix + - ./doRename-condition-no-enable.nix */ { config, lib, ... }: -let inherit (lib) mkOption mkEnableOption types doRename; -in { +let + inherit (lib) + mkOption + mkEnableOption + types + doRename + ; +in +{ options = { services.foo.enable = mkEnableOption "foo"; services.foos = mkOption { - type = types.attrsOf (types.submodule { - options = { bar = mkOption { type = types.str; }; }; - }); + type = types.attrsOf ( + types.submodule { + options = { + bar = mkOption { type = types.str; }; + }; + } + ); default = { }; }; result = mkOption { }; }; imports = [ (doRename { - from = [ "services" "foo" "bar" ]; - to = [ "services" "foos" "" "bar" ]; + from = [ + "services" + "foo" + "bar" + ]; + to = [ + "services" + "foos" + "" + "bar" + ]; visible = true; warn = false; use = x: x; diff --git a/extra/tests/stdlib/modules/doRename-warnings.nix b/extra/tests/stdlib/modules/doRename-warnings.nix index 808bb50..c0150ce 100644 --- a/extra/tests/stdlib/modules/doRename-warnings.nix +++ b/extra/tests/stdlib/modules/doRename-warnings.nix @@ -1,8 +1,16 @@ -{ lib, config, ... }: { +{ lib, config, ... }: +{ imports = [ (lib.doRename { - from = [ "a" "b" ]; - to = [ "c" "d" "e" ]; + from = [ + "a" + "b" + ]; + to = [ + "c" + "d" + "e" + ]; warn = true; use = x: x; visible = true; diff --git a/extra/tests/stdlib/modules/docs.nix b/extra/tests/stdlib/modules/docs.nix index 7ddd619..3797cf5 100644 --- a/extra/tests/stdlib/modules/docs.nix +++ b/extra/tests/stdlib/modules/docs.nix @@ -1,25 +1,33 @@ -/* A basic documentation generating module. - Declares and defines a `docs` option, suitable for making assertions about - the extraction "phase" of documentation generation. +/* + A basic documentation generating module. + Declares and defines a `docs` option, suitable for making assertions about + the extraction "phase" of documentation generation. */ { lib, options, ... }: let - inherit (lib) head length mkOption types; + inherit (lib) + head + length + mkOption + types + ; traceListSeq = l: v: lib.foldl' (a: b: lib.traceSeq b a) v l; - -in { +in +{ options.docs = mkOption { type = types.lazyAttrsOf types.raw; description = '' All options to be rendered, without any visibility filtering applied. ''; }; - config.docs = lib.zipAttrsWith (name: values: + config.docs = lib.zipAttrsWith ( + name: values: if length values > 1 then traceListSeq values abort "Multiple options with the same name: ${name}" else - assert length values == 1; head values) - (map (opt: { ${opt.name} = opt; }) (lib.optionAttrSetToDocList options)); + assert length values == 1; + head values + ) (map (opt: { ${opt.name} = opt; }) (lib.optionAttrSetToDocList options)); } diff --git a/extra/tests/stdlib/modules/emptyValues.nix b/extra/tests/stdlib/modules/emptyValues.nix index fdc19fb..40c41c6 100644 --- a/extra/tests/stdlib/modules/emptyValues.nix +++ b/extra/tests/stdlib/modules/emptyValues.nix @@ -1,18 +1,16 @@ { lib, ... }: -let inherit (lib) types; -in { +let + inherit (lib) types; +in +{ options = { int = lib.mkOption { type = types.lazyAttrsOf types.int; }; list = lib.mkOption { type = types.lazyAttrsOf (types.listOf types.int); }; - nonEmptyList = lib.mkOption { - type = types.lazyAttrsOf (types.nonEmptyListOf types.int); - }; - attrs = - lib.mkOption { type = types.lazyAttrsOf (types.attrsOf types.int); }; + nonEmptyList = lib.mkOption { type = types.lazyAttrsOf (types.nonEmptyListOf types.int); }; + attrs = lib.mkOption { type = types.lazyAttrsOf (types.attrsOf types.int); }; null = lib.mkOption { type = types.lazyAttrsOf (types.nullOr types.int); }; - submodule = - lib.mkOption { type = types.lazyAttrsOf (types.submodule { }); }; + submodule = lib.mkOption { type = types.lazyAttrsOf (types.submodule { }); }; }; config = { @@ -23,5 +21,4 @@ in { null.a = lib.mkIf false null; submodule.a = lib.mkIf false null; }; - } diff --git a/extra/tests/stdlib/modules/error-mkOption-in-config.nix b/extra/tests/stdlib/modules/error-mkOption-in-config.nix index 55eb25e..85b5f99 100644 --- a/extra/tests/stdlib/modules/error-mkOption-in-config.nix +++ b/extra/tests/stdlib/modules/error-mkOption-in-config.nix @@ -1,6 +1,8 @@ { lib, ... }: -let inherit (lib) mkOption; -in { +let + inherit (lib) mkOption; +in +{ wrong1 = mkOption { }; # This is not actually reported separately, so could be omitted from the test # but it makes the example more realistic. diff --git a/extra/tests/stdlib/modules/error-mkOption-in-submodule-config.nix b/extra/tests/stdlib/modules/error-mkOption-in-submodule-config.nix index 04f8cff..a3a0112 100644 --- a/extra/tests/stdlib/modules/error-mkOption-in-submodule-config.nix +++ b/extra/tests/stdlib/modules/error-mkOption-in-submodule-config.nix @@ -1,6 +1,8 @@ { lib, ... }: -let inherit (lib) mkOption; -in { +let + inherit (lib) mkOption; +in +{ options.sub = lib.mkOption { type = lib.types.submodule { wrong2 = mkOption { }; }; default = { }; diff --git a/extra/tests/stdlib/modules/error-nonEmptyListOf-submodule.nix b/extra/tests/stdlib/modules/error-nonEmptyListOf-submodule.nix index f0811c6..1189e88 100644 --- a/extra/tests/stdlib/modules/error-nonEmptyListOf-submodule.nix +++ b/extra/tests/stdlib/modules/error-nonEmptyListOf-submodule.nix @@ -1,4 +1,5 @@ -{ lib, ... }: { +{ lib, ... }: +{ options.bad = lib.mkOption { type = lib.types.nonEmptyListOf (lib.types.submodule { }); default = [ ]; diff --git a/extra/tests/stdlib/modules/extendModules-168767-imports.nix b/extra/tests/stdlib/modules/extendModules-168767-imports.nix index 5ddd597..3a69fa4 100644 --- a/extra/tests/stdlib/modules/extendModules-168767-imports.nix +++ b/extra/tests/stdlib/modules/extendModules-168767-imports.nix @@ -1,27 +1,28 @@ { lib, extendModules, ... }: -let inherit (lib) mkOption mkOverride types; -in { +let + inherit (lib) mkOption mkOverride types; +in +{ imports = [ { options.sub = mkOption { default = { }; - type = types.submodule ({ config, extendModules, ... }: { - options.value = mkOption { type = types.int; }; + type = types.submodule ( + { config, extendModules, ... }: + { + options.value = mkOption { type = types.int; }; - options.specialisation = mkOption { - default = { }; - inherit (extendModules { - modules = [{ specialisation = mkOverride 0 { }; }]; - }) - type; - }; - }); + options.specialisation = mkOption { + default = { }; + inherit (extendModules { modules = [ { specialisation = mkOverride 0 { }; } ]; }) type; + }; + } + ); }; } { config.sub.value = 1; } - ]; } diff --git a/extra/tests/stdlib/modules/freeform-attrsOf.nix b/extra/tests/stdlib/modules/freeform-attrsOf.nix index 8cc577f..043ae11 100644 --- a/extra/tests/stdlib/modules/freeform-attrsOf.nix +++ b/extra/tests/stdlib/modules/freeform-attrsOf.nix @@ -1,3 +1,4 @@ -{ lib, ... }: { +{ lib, ... }: +{ freeformType = with lib.types; attrsOf (either str (attrsOf str)); } diff --git a/extra/tests/stdlib/modules/freeform-lazyAttrsOf.nix b/extra/tests/stdlib/modules/freeform-lazyAttrsOf.nix index 36d6c0b..7fd9adc 100644 --- a/extra/tests/stdlib/modules/freeform-lazyAttrsOf.nix +++ b/extra/tests/stdlib/modules/freeform-lazyAttrsOf.nix @@ -1,3 +1,4 @@ -{ lib, ... }: { +{ lib, ... }: +{ freeformType = with lib.types; lazyAttrsOf (either str (lazyAttrsOf str)); } diff --git a/extra/tests/stdlib/modules/freeform-nested.nix b/extra/tests/stdlib/modules/freeform-nested.nix index b21df19..484178c 100644 --- a/extra/tests/stdlib/modules/freeform-nested.nix +++ b/extra/tests/stdlib/modules/freeform-nested.nix @@ -1,10 +1,10 @@ { lib, ... }: let - deathtrapArgs = lib.mapAttrs (k: _: - throw - "The module system is too strict, accessing an unused option's ${k} mkOption-attribute.") - (lib.functionArgs lib.mkOption); -in { + deathtrapArgs = lib.mapAttrs ( + k: _: throw "The module system is too strict, accessing an unused option's ${k} mkOption-attribute." + ) (lib.functionArgs lib.mkOption); +in +{ options.nest.foo = lib.mkOption { type = lib.types.bool; default = false; diff --git a/extra/tests/stdlib/modules/freeform-str-dep-unstr.nix b/extra/tests/stdlib/modules/freeform-str-dep-unstr.nix index a2dfbc8..9748aa9 100644 --- a/extra/tests/stdlib/modules/freeform-str-dep-unstr.nix +++ b/extra/tests/stdlib/modules/freeform-str-dep-unstr.nix @@ -1,4 +1,5 @@ -{ lib, config, ... }: { +{ lib, config, ... }: +{ options.foo = lib.mkOption { type = lib.types.nullOr lib.types.str; default = null; diff --git a/extra/tests/stdlib/modules/freeform-submodules.nix b/extra/tests/stdlib/modules/freeform-submodules.nix index c422f86..d12248e 100644 --- a/extra/tests/stdlib/modules/freeform-submodules.nix +++ b/extra/tests/stdlib/modules/freeform-submodules.nix @@ -1,16 +1,20 @@ { lib, options, ... }: -with lib.types; { +with lib.types; +{ options.fooDeclarations = lib.mkOption { - default = - (options.free.type.getSubOptions [ ])._freeformOptions.foo.declarations; + default = (options.free.type.getSubOptions [ ])._freeformOptions.foo.declarations; }; options.free = lib.mkOption { type = submodule { config._module.freeformType = lib.mkMerge [ - (attrsOf (submodule { options.foo = lib.mkOption { }; })) - (attrsOf (submodule { options.bar = lib.mkOption { }; })) + (attrsOf (submodule { + options.foo = lib.mkOption { }; + })) + (attrsOf (submodule { + options.bar = lib.mkOption { }; + })) ]; }; }; diff --git a/extra/tests/stdlib/modules/freeform-unstr-dep-str.nix b/extra/tests/stdlib/modules/freeform-unstr-dep-str.nix index 549d89a..dae56f9 100644 --- a/extra/tests/stdlib/modules/freeform-unstr-dep-str.nix +++ b/extra/tests/stdlib/modules/freeform-unstr-dep-str.nix @@ -1,4 +1,5 @@ -{ lib, config, ... }: { +{ lib, config, ... }: +{ options.value = lib.mkOption { type = lib.types.nullOr lib.types.str; default = null; diff --git a/extra/tests/stdlib/modules/functionTo/list-order.nix b/extra/tests/stdlib/modules/functionTo/list-order.nix index a1b35a0..520aa3d 100644 --- a/extra/tests/stdlib/modules/functionTo/list-order.nix +++ b/extra/tests/stdlib/modules/functionTo/list-order.nix @@ -1,19 +1,25 @@ { lib, config, ... }: -let inherit (lib) types; -in { +let + inherit (lib) types; +in +{ options = { fun = lib.mkOption { type = types.functionTo (types.listOf types.str); }; result = lib.mkOption { type = types.str; - default = toString (config.fun { - a = "a"; - b = "b"; - c = "c"; - }); + default = toString ( + config.fun { + a = "a"; + b = "b"; + c = "c"; + } + ); }; }; - config.fun = - lib.mkMerge [ (input: lib.mkAfter [ input.a ]) (input: [ input.b ]) ]; + config.fun = lib.mkMerge [ + (input: lib.mkAfter [ input.a ]) + (input: [ input.b ]) + ]; } diff --git a/extra/tests/stdlib/modules/functionTo/merging-attrs.nix b/extra/tests/stdlib/modules/functionTo/merging-attrs.nix index e3cfd39..83bacf8 100644 --- a/extra/tests/stdlib/modules/functionTo/merging-attrs.nix +++ b/extra/tests/stdlib/modules/functionTo/merging-attrs.nix @@ -1,16 +1,22 @@ { lib, config, ... }: -let inherit (lib) types; -in { +let + inherit (lib) types; +in +{ options = { fun = lib.mkOption { type = types.functionTo (types.attrsOf types.str); }; result = lib.mkOption { type = types.str; - default = toString (lib.attrValues (config.fun { - a = "a"; - b = "b"; - c = "c"; - })); + default = toString ( + lib.attrValues ( + config.fun { + a = "a"; + b = "b"; + c = "c"; + } + ) + ); }; }; diff --git a/extra/tests/stdlib/modules/functionTo/merging-list.nix b/extra/tests/stdlib/modules/functionTo/merging-list.nix index 86c45cc..2c1c9fd 100644 --- a/extra/tests/stdlib/modules/functionTo/merging-list.nix +++ b/extra/tests/stdlib/modules/functionTo/merging-list.nix @@ -1,18 +1,25 @@ { lib, config, ... }: -let inherit (lib) types; -in { +let + inherit (lib) types; +in +{ options = { fun = lib.mkOption { type = types.functionTo (types.listOf types.str); }; result = lib.mkOption { type = types.str; - default = toString (config.fun { - a = "a"; - b = "b"; - c = "c"; - }); + default = toString ( + config.fun { + a = "a"; + b = "b"; + c = "c"; + } + ); }; }; - config.fun = lib.mkMerge [ (input: [ input.a ]) (input: [ input.b ]) ]; + config.fun = lib.mkMerge [ + (input: [ input.a ]) + (input: [ input.b ]) + ]; } diff --git a/extra/tests/stdlib/modules/functionTo/submodule-options.nix b/extra/tests/stdlib/modules/functionTo/submodule-options.nix index d6a716b..49f4dc0 100644 --- a/extra/tests/stdlib/modules/functionTo/submodule-options.nix +++ b/extra/tests/stdlib/modules/functionTo/submodule-options.nix @@ -1,41 +1,57 @@ -{ lib, config, options, ... }: -let inherit (lib) types; -in { +{ + lib, + config, + options, + ... +}: +let + inherit (lib) types; +in +{ imports = [ # fun..a - ({ ... }: { - options = { - fun = lib.mkOption { - type = types.functionTo - (types.submodule { options.a = lib.mkOption { default = "a"; }; }); + ( + { ... }: + { + options = { + fun = lib.mkOption { + type = types.functionTo (types.submodule { options.a = lib.mkOption { default = "a"; }; }); + }; }; - }; - }) + } + ) # fun..b - ({ ... }: { - options = { - fun = lib.mkOption { - type = types.functionTo - (types.submodule { options.b = lib.mkOption { default = "b"; }; }); + ( + { ... }: + { + options = { + fun = lib.mkOption { + type = types.functionTo (types.submodule { options.b = lib.mkOption { default = "b"; }; }); + }; }; - }; - }) + } + ) ]; options = { result = lib.mkOption { type = types.str; - default = lib.concatStringsSep " " - (lib.attrValues (config.fun (throw "shouldn't use input param"))); + default = lib.concatStringsSep " " ( + lib.attrValues (config.fun (throw "shouldn't use input param")) + ); }; optionsResult = lib.mkOption { type = types.str; - default = lib.concatStringsSep " " (lib.concatLists (lib.mapAttrsToList - (k: v: if k == "_module" then [ ] else [ (lib.showOption v.loc) ]) - ((options.fun.type.getSubOptions [ "fun" ])))); + default = lib.concatStringsSep " " ( + lib.concatLists ( + lib.mapAttrsToList (k: v: if k == "_module" then [ ] else [ (lib.showOption v.loc) ]) ( + (options.fun.type.getSubOptions [ "fun" ]) + ) + ) + ); }; }; diff --git a/extra/tests/stdlib/modules/functionTo/trivial.nix b/extra/tests/stdlib/modules/functionTo/trivial.nix index c473fc6..0ffc7f5 100644 --- a/extra/tests/stdlib/modules/functionTo/trivial.nix +++ b/extra/tests/stdlib/modules/functionTo/trivial.nix @@ -1,6 +1,8 @@ { lib, config, ... }: -let inherit (lib) types; -in { +let + inherit (lib) types; +in +{ options = { fun = lib.mkOption { type = types.functionTo types.str; }; diff --git a/extra/tests/stdlib/modules/functionTo/wrong-type.nix b/extra/tests/stdlib/modules/functionTo/wrong-type.nix index ea2470f..9f5f5a1 100644 --- a/extra/tests/stdlib/modules/functionTo/wrong-type.nix +++ b/extra/tests/stdlib/modules/functionTo/wrong-type.nix @@ -1,6 +1,8 @@ { lib, config, ... }: -let inherit (lib) types; -in { +let + inherit (lib) types; +in +{ options = { fun = lib.mkOption { type = types.functionTo types.str; }; diff --git a/extra/tests/stdlib/modules/gvariant.nix b/extra/tests/stdlib/modules/gvariant.nix index 69ce09f..0feb378 100644 --- a/extra/tests/stdlib/modules/gvariant.nix +++ b/extra/tests/stdlib/modules/gvariant.nix @@ -24,37 +24,41 @@ escapedString = '' '\ ''; - tuple = mkTuple [ (mkInt32 1) [ "foo" ] ]; + tuple = mkTuple [ + (mkInt32 1) + [ "foo" ] + ]; maybe1 = mkNothing type.string; maybe2 = mkJust (mkUint32 4); variant = mkVariant "foo"; dictionaryEntry = mkDictionaryEntry (mkInt32 1) [ "foo" ]; }; - assertion = let - mkLine = n: v: "${n} = ${toString (lib.gvariant.mkValue v)}"; - result = - lib.concatStringsSep "\n" (lib.mapAttrsToList mkLine config.examples); - in (result + "\n") == '' - array1 = @as ['one'] - array2 = @ai [1] - array3 = @au [@u 2] - bool = true - dictionaryEntry = @{ias} {1,@as ['foo']} - emptyArray = @au [] - escapedString = '\'\\\n' - float = 3.140000 - int16 = @n -42 - int32 = -42 - int64 = @x -42 - maybe1 = @ms nothing - maybe2 = just @u 4 - string = 'foo' - tuple = @(ias) (1,@as ['foo']) - uint16 = @q 42 - uint32 = @u 42 - uint64 = @t 42 - variant = <'foo'> - ''; + assertion = + let + mkLine = n: v: "${n} = ${toString (lib.gvariant.mkValue v)}"; + result = lib.concatStringsSep "\n" (lib.mapAttrsToList mkLine config.examples); + in + (result + "\n") == '' + array1 = @as ['one'] + array2 = @ai [1] + array3 = @au [@u 2] + bool = true + dictionaryEntry = @{ias} {1,@as ['foo']} + emptyArray = @au [] + escapedString = '\'\\\n' + float = 3.140000 + int16 = @n -42 + int32 = -42 + int64 = @x -42 + maybe1 = @ms nothing + maybe2 = just @u 4 + string = 'foo' + tuple = @(ias) (1,@as ['foo']) + uint16 = @q 42 + uint32 = @u 42 + uint64 = @t 42 + variant = <'foo'> + ''; }; } diff --git a/extra/tests/stdlib/modules/import-configuration.nix b/extra/tests/stdlib/modules/import-configuration.nix index 942feea..a2a32bb 100644 --- a/extra/tests/stdlib/modules/import-configuration.nix +++ b/extra/tests/stdlib/modules/import-configuration.nix @@ -1,6 +1,8 @@ { lib, ... }: -let myconf = lib.evalModules { modules = [ { } ]; }; -in { +let + myconf = lib.evalModules { modules = [ { } ]; }; +in +{ imports = [ # We can't do this. A configuration is not equal to its set of a modules. # Equating those would lead to a mess, as specialArgs, anonymous modules diff --git a/extra/tests/stdlib/modules/import-from-store.nix b/extra/tests/stdlib/modules/import-from-store.nix index 7428148..9fe31d9 100644 --- a/extra/tests/stdlib/modules/import-from-store.nix +++ b/extra/tests/stdlib/modules/import-from-store.nix @@ -1,10 +1,9 @@ -{ lib, ... }: { +{ lib, ... }: +{ imports = [ "${builtins.toFile "drv" "{}"}" ./declare-enable.nix ./define-enable.nix ]; - } - diff --git a/extra/tests/stdlib/modules/merge-module-with-key.nix b/extra/tests/stdlib/modules/merge-module-with-key.nix index ed347ea..820727b 100644 --- a/extra/tests/stdlib/modules/merge-module-with-key.nix +++ b/extra/tests/stdlib/modules/merge-module-with-key.nix @@ -2,24 +2,44 @@ let inherit (lib) mkOption types; - moduleWithoutKey = { config = { raw = "pear"; }; }; + moduleWithoutKey = { + config = { + raw = "pear"; + }; + }; moduleWithKey = { key = __curPos.file + "#moduleWithKey"; - config = { raw = "pear"; }; + config = { + raw = "pear"; + }; }; - decl = { options = { raw = mkOption { type = types.lines; }; }; }; -in { + decl = { + options = { + raw = mkOption { type = types.lines; }; + }; + }; +in +{ options = { once = mkOption { - type = - types.submodule { imports = [ decl moduleWithKey moduleWithKey ]; }; + type = types.submodule { + imports = [ + decl + moduleWithKey + moduleWithKey + ]; + }; default = { }; }; twice = mkOption { type = types.submodule { - imports = [ decl moduleWithoutKey moduleWithoutKey ]; + imports = [ + decl + moduleWithoutKey + moduleWithoutKey + ]; }; default = { }; }; diff --git a/extra/tests/stdlib/modules/merge-typeless-option.nix b/extra/tests/stdlib/modules/merge-typeless-option.nix index 97a9444..de0c24a 100644 --- a/extra/tests/stdlib/modules/merge-typeless-option.nix +++ b/extra/tests/stdlib/modules/merge-typeless-option.nix @@ -1,19 +1,24 @@ { lib, ... }: let - typeless = { lib, ... }: + typeless = + { lib, ... }: { options.group = lib.mkOption { }; }; - childOfTypeless = { lib, ... }: + childOfTypeless = + { lib, ... }: { options.group.enable = lib.mkEnableOption "nothing"; }; - -in { - imports = [ typeless childOfTypeless ]; +in +{ + imports = [ + typeless + childOfTypeless + ]; config.group.enable = false; } diff --git a/extra/tests/stdlib/modules/module-argument-default.nix b/extra/tests/stdlib/modules/module-argument-default.nix index a442f4c..c437136 100644 --- a/extra/tests/stdlib/modules/module-argument-default.nix +++ b/extra/tests/stdlib/modules/module-argument-default.nix @@ -1,5 +1,12 @@ -{ a ? false, lib, ... }: { - options = { result = lib.mkOption { }; }; +{ + a ? false, + lib, + ... +}: +{ + options = { + result = lib.mkOption { }; + }; config = { _module.args.a = true; result = a; diff --git a/extra/tests/stdlib/modules/module-imports-_type-check.nix b/extra/tests/stdlib/modules/module-imports-_type-check.nix index e6a40a4..85f9a82 100644 --- a/extra/tests/stdlib/modules/module-imports-_type-check.nix +++ b/extra/tests/stdlib/modules/module-imports-_type-check.nix @@ -1 +1 @@ -{ imports = [{ _type = "flake"; }]; } +{ imports = [ { _type = "flake"; } ]; } diff --git a/extra/tests/stdlib/modules/optionTypeFile.nix b/extra/tests/stdlib/modules/optionTypeFile.nix index 9b76aad..dbcb459 100644 --- a/extra/tests/stdlib/modules/optionTypeFile.nix +++ b/extra/tests/stdlib/modules/optionTypeFile.nix @@ -1,4 +1,5 @@ -{ config, lib, ... }: { +{ config, lib, ... }: +{ _file = "optionTypeFile.nix"; @@ -10,13 +11,10 @@ }; config.theType = lib.mkMerge [ - (lib.types.submodule { - options.nested = lib.mkOption { type = lib.types.int; }; - }) + (lib.types.submodule { options.nested = lib.mkOption { type = lib.types.int; }; }) (lib.types.submodule { _file = "other.nix"; options.nested = lib.mkOption { type = lib.types.str; }; }) ]; - } diff --git a/extra/tests/stdlib/modules/optionTypeMerging.nix b/extra/tests/stdlib/modules/optionTypeMerging.nix index de0dc28..86c18f5 100644 --- a/extra/tests/stdlib/modules/optionTypeMerging.nix +++ b/extra/tests/stdlib/modules/optionTypeMerging.nix @@ -1,4 +1,5 @@ -{ config, lib, ... }: { +{ config, lib, ... }: +{ options.theType = lib.mkOption { type = lib.types.optionType; }; @@ -11,11 +12,8 @@ default = 10; }; }) - (lib.types.submodule { - options.str = lib.mkOption { type = lib.types.str; }; - }) + (lib.types.submodule { options.str = lib.mkOption { type = lib.types.str; }; }) ]; config.theOption.str = "hello"; - } diff --git a/extra/tests/stdlib/modules/options-type-error-configuration.nix b/extra/tests/stdlib/modules/options-type-error-configuration.nix index 68ba783..25a6a1b 100644 --- a/extra/tests/stdlib/modules/options-type-error-configuration.nix +++ b/extra/tests/stdlib/modules/options-type-error-configuration.nix @@ -1,4 +1,5 @@ -{ lib, ... }: { +{ lib, ... }: +{ options = { # unlikely mistake, but we can catch any attrset with _type result = lib.evalModules { modules = [ ]; }; diff --git a/extra/tests/stdlib/modules/options-type-error-typical-nested.nix b/extra/tests/stdlib/modules/options-type-error-typical-nested.nix index fbabd68..09f3143 100644 --- a/extra/tests/stdlib/modules/options-type-error-typical-nested.nix +++ b/extra/tests/stdlib/modules/options-type-error-typical-nested.nix @@ -1 +1,6 @@ -{ lib, ... }: { options = { result.here = lib.types.str; }; } +{ lib, ... }: +{ + options = { + result.here = lib.types.str; + }; +} diff --git a/extra/tests/stdlib/modules/options-type-error-typical.nix b/extra/tests/stdlib/modules/options-type-error-typical.nix index 2289c19..8ae3fd2 100644 --- a/extra/tests/stdlib/modules/options-type-error-typical.nix +++ b/extra/tests/stdlib/modules/options-type-error-typical.nix @@ -1 +1,6 @@ -{ lib, ... }: { options = { result = lib.types.str; }; } +{ lib, ... }: +{ + options = { + result = lib.types.str; + }; +} diff --git a/extra/tests/stdlib/modules/raw.nix b/extra/tests/stdlib/modules/raw.nix index b7299f1..f50f405 100644 --- a/extra/tests/stdlib/modules/raw.nix +++ b/extra/tests/stdlib/modules/raw.nix @@ -1,18 +1,26 @@ -{ lib, config, ... }: { +{ lib, config, ... }: +{ options = { processedToplevel = lib.mkOption { type = lib.types.raw; }; unprocessedNesting = lib.mkOption { type = lib.types.raw; }; multiple = lib.mkOption { type = lib.types.raw; }; priorities = lib.mkOption { type = lib.types.raw; }; - unprocessedNestingEvaluates = - lib.mkOption { default = builtins.tryEval config.unprocessedNesting; }; + unprocessedNestingEvaluates = lib.mkOption { + default = builtins.tryEval config.unprocessedNesting; + }; }; config = { processedToplevel = lib.mkIf true 10; unprocessedNesting.foo = throw "foo"; - multiple = lib.mkMerge [ "foo" "foo" ]; - priorities = lib.mkMerge [ "foo" (lib.mkForce "bar") ]; + multiple = lib.mkMerge [ + "foo" + "foo" + ]; + priorities = lib.mkMerge [ + "foo" + (lib.mkForce "bar") + ]; }; } diff --git a/extra/tests/stdlib/modules/shorthand-meta.nix b/extra/tests/stdlib/modules/shorthand-meta.nix index 35c92b8..9c8c241 100644 --- a/extra/tests/stdlib/modules/shorthand-meta.nix +++ b/extra/tests/stdlib/modules/shorthand-meta.nix @@ -1,14 +1,23 @@ { lib, ... }: -let inherit (lib) types mkOption; -in { +let + inherit (lib) types mkOption; +in +{ imports = [ - ({ config, ... }: { - options = { - meta.foo = mkOption { type = types.listOf types.str; }; - result = - mkOption { default = lib.concatStringsSep " " config.meta.foo; }; - }; - }) - { meta.foo = [ "one" "two" ]; } + ( + { config, ... }: + { + options = { + meta.foo = mkOption { type = types.listOf types.str; }; + result = mkOption { default = lib.concatStringsSep " " config.meta.foo; }; + }; + } + ) + { + meta.foo = [ + "one" + "two" + ]; + } ]; } diff --git a/extra/tests/stdlib/modules/submoduleFiles.nix b/extra/tests/stdlib/modules/submoduleFiles.nix index 5b27a3c..2b08fe1 100644 --- a/extra/tests/stdlib/modules/submoduleFiles.nix +++ b/extra/tests/stdlib/modules/submoduleFiles.nix @@ -1,20 +1,25 @@ -{ lib, ... }: { +{ lib, ... }: +{ options.submodule = lib.mkOption { default = { }; type = lib.types.submoduleWith { modules = [ - ({ options, ... }: { - options.value = lib.mkOption { }; + ( + { options, ... }: + { + options.value = lib.mkOption { }; - options.internalFiles = - lib.mkOption { default = options.value.files; }; - }) + options.internalFiles = lib.mkOption { default = options.value.files; }; + } + ) ]; }; }; - imports = [{ - _file = "the-file.nix"; - submodule.value = 10; - }]; + imports = [ + { + _file = "the-file.nix"; + submodule.value = 10; + } + ]; } diff --git a/extra/tests/stdlib/modules/test-mergeAttrDefinitionsWithPrio.nix b/extra/tests/stdlib/modules/test-mergeAttrDefinitionsWithPrio.nix index 7ef5731..812ac3f 100644 --- a/extra/tests/stdlib/modules/test-mergeAttrDefinitionsWithPrio.nix +++ b/extra/tests/stdlib/modules/test-mergeAttrDefinitionsWithPrio.nix @@ -2,12 +2,11 @@ let defs = lib.modules.mergeAttrDefinitionsWithPrio options._module.args; - assertLazy = pos: - throw "${pos.file}:${toString pos.line}:${ - toString pos.column - }: The test must not evaluate this the assertLazy thunk, but it did. Unexpected strictness leads to unexpected errors and performance problems."; - -in { + assertLazy = + pos: + throw "${pos.file}:${toString pos.line}:${toString pos.column}: The test must not evaluate this the assertLazy thunk, but it did. Unexpected strictness leads to unexpected errors and performance problems."; +in +{ options.result = lib.mkOption { }; config._module.args = { default = lib.mkDefault (assertLazy __curPos); @@ -15,10 +14,9 @@ in { force = lib.mkForce (assertLazy __curPos); unused = assertLazy __curPos; }; - config.result = assert defs.default.highestPrio - == (lib.mkDefault (assertLazy __curPos)).priority; + config.result = + assert defs.default.highestPrio == (lib.mkDefault (assertLazy __curPos)).priority; assert defs.regular.highestPrio == lib.modules.defaultOverridePriority; - assert defs.force.highestPrio - == (lib.mkForce (assertLazy __curPos)).priority; + assert defs.force.highestPrio == (lib.mkForce (assertLazy __curPos)).priority; true; } diff --git a/extra/tests/stdlib/modules/types-anything/attrs-coercible.nix b/extra/tests/stdlib/modules/types-anything/attrs-coercible.nix index 76aaa60..5e31531 100644 --- a/extra/tests/stdlib/modules/types-anything/attrs-coercible.nix +++ b/extra/tests/stdlib/modules/types-anything/attrs-coercible.nix @@ -1,4 +1,5 @@ -{ lib, ... }: { +{ lib, ... }: +{ options.value = lib.mkOption { type = lib.types.anything; }; @@ -6,5 +7,4 @@ outPath = "foo"; err = throw "err"; }; - } diff --git a/extra/tests/stdlib/modules/types-anything/equal-atoms.nix b/extra/tests/stdlib/modules/types-anything/equal-atoms.nix index 994fc79..9284768 100644 --- a/extra/tests/stdlib/modules/types-anything/equal-atoms.nix +++ b/extra/tests/stdlib/modules/types-anything/equal-atoms.nix @@ -1,4 +1,5 @@ -{ lib, ... }: { +{ lib, ... }: +{ options.value = lib.mkOption { type = lib.types.anything; }; @@ -20,5 +21,4 @@ value.float = 0.1; } ]; - } diff --git a/extra/tests/stdlib/modules/types-anything/functions.nix b/extra/tests/stdlib/modules/types-anything/functions.nix index 24f56ab..6722aec 100644 --- a/extra/tests/stdlib/modules/types-anything/functions.nix +++ b/extra/tests/stdlib/modules/types-anything/functions.nix @@ -1,4 +1,5 @@ -{ lib, config, ... }: { +{ lib, config, ... }: +{ options.valueIsFunction = lib.mkOption { default = lib.mapAttrs (name: lib.isFunction) config.value; @@ -6,8 +7,7 @@ options.value = lib.mkOption { type = lib.types.anything; }; - options.applied = - lib.mkOption { default = lib.mapAttrs (name: fun: fun null) config.value; }; + options.applied = lib.mkOption { default = lib.mapAttrs (name: fun: fun null) config.value; }; config = lib.mkMerge [ { @@ -20,5 +20,4 @@ value.merging-lambdas = y: { inherit y; }; } ]; - } diff --git a/extra/tests/stdlib/modules/types-anything/lists.nix b/extra/tests/stdlib/modules/types-anything/lists.nix index cf445fa..a2388bf 100644 --- a/extra/tests/stdlib/modules/types-anything/lists.nix +++ b/extra/tests/stdlib/modules/types-anything/lists.nix @@ -1,7 +1,10 @@ -{ lib, ... }: { +{ lib, ... }: +{ options.value = lib.mkOption { type = lib.types.anything; }; - config = lib.mkMerge [ { value = [ null ]; } { value = [ null ]; } ]; - + config = lib.mkMerge [ + { value = [ null ]; } + { value = [ null ]; } + ]; } diff --git a/extra/tests/stdlib/modules/types-anything/mk-mods.nix b/extra/tests/stdlib/modules/types-anything/mk-mods.nix index a43529a..a3dd33f 100644 --- a/extra/tests/stdlib/modules/types-anything/mk-mods.nix +++ b/extra/tests/stdlib/modules/types-anything/mk-mods.nix @@ -1,4 +1,5 @@ -{ lib, ... }: { +{ lib, ... }: +{ options.value = lib.mkOption { type = lib.types.anything; }; @@ -17,10 +18,11 @@ } (lib.mkIf true { foo = lib.mkIf true (lib.mkForce 1); - bar = { baz = lib.mkDefault "baz"; }; + bar = { + baz = lib.mkDefault "baz"; + }; }) ]; } ]; - } diff --git a/extra/tests/stdlib/modules/types-anything/nested-attrs.nix b/extra/tests/stdlib/modules/types-anything/nested-attrs.nix index e43ff8f..e1f03aa 100644 --- a/extra/tests/stdlib/modules/types-anything/nested-attrs.nix +++ b/extra/tests/stdlib/modules/types-anything/nested-attrs.nix @@ -1,4 +1,5 @@ -{ lib, ... }: { +{ lib, ... }: +{ options.value = lib.mkOption { type = lib.types.anything; }; @@ -8,5 +9,4 @@ { value.l1.l2.foo = null; } { value.l1.l2.l3.foo = null; } ]; - } diff --git a/extra/tests/stdlib/modules/types-attrTag-wrong-decl.nix b/extra/tests/stdlib/modules/types-attrTag-wrong-decl.nix index 172c138..078296d 100644 --- a/extra/tests/stdlib/modules/types-attrTag-wrong-decl.nix +++ b/extra/tests/stdlib/modules/types-attrTag-wrong-decl.nix @@ -1,10 +1,14 @@ { lib, ... }: -let inherit (lib) types mkOption; -in { +let + inherit (lib) types mkOption; +in +{ options = { opt = mkOption { type = types.attrTag { int = types.int; }; - default = { int = 1; }; + default = { + int = 1; + }; }; }; } diff --git a/extra/tests/stdlib/modules/types-attrTag.nix b/extra/tests/stdlib/modules/types-attrTag.nix index c40b3ef..9b07973 100644 --- a/extra/tests/stdlib/modules/types-attrTag.nix +++ b/extra/tests/stdlib/modules/types-attrTag.nix @@ -1,16 +1,23 @@ -{ lib, config, options, ... }: +{ + lib, + config, + options, + ... +}: let inherit (lib) mkOption types; forceDeep = x: builtins.deepSeq x x; - mergedSubOption = (options.merged.type.getSubOptions - options.merged.loc).extensible."merged."; -in { + mergedSubOption = (options.merged.type.getSubOptions options.merged.loc).extensible."merged."; +in +{ options = { intStrings = mkOption { - type = types.attrsOf (types.attrTag { - left = mkOption { type = types.int; }; - right = mkOption { type = types.str; }; - }); + type = types.attrsOf ( + types.attrTag { + left = mkOption { type = types.int; }; + right = mkOption { type = types.str; }; + } + ); }; nested = mkOption { type = types.attrTag { @@ -24,23 +31,29 @@ in { }; }; merged = mkOption { - type = types.attrsOf (types.attrTag { - yay = mkOption { type = types.int; }; - extensible = mkOption { type = types.enum [ "foo" ]; }; - }); + type = types.attrsOf ( + types.attrTag { + yay = mkOption { type = types.int; }; + extensible = mkOption { type = types.enum [ "foo" ]; }; + } + ); }; submodules = mkOption { - type = types.attrsOf (types.attrTag { - foo = mkOption { - type = types.submodule { - options = { bar = mkOption { type = types.int; }; }; + type = types.attrsOf ( + types.attrTag { + foo = mkOption { + type = types.submodule { + options = { + bar = mkOption { type = types.int; }; + }; + }; }; - }; - qux = mkOption { - type = types.str; - description = "A qux for when you don't want a foo"; - }; - }); + qux = mkOption { + type = types.str; + description = "A qux for when you don't want a foo"; + }; + } + ); }; okChecks = mkOption { }; }; @@ -48,10 +61,12 @@ in { ./docs.nix { options.merged = mkOption { - type = types.attrsOf (types.attrTag { - nay = mkOption { type = types.bool; }; - extensible = mkOption { type = types.enum [ "bar" ]; }; - }); + type = types.attrsOf ( + types.attrTag { + nay = mkOption { type = types.bool; }; + extensible = mkOption { type = types.enum [ "bar" ]; }; + } + ); }; } ]; @@ -62,7 +77,10 @@ in { a = true; b = true; }; - intStrings.syntaxError4 = lib.mkMerge [ { a = true; } { b = true; } ]; + intStrings.syntaxError4 = lib.mkMerge [ + { a = true; } + { b = true; } + ]; intStrings.mergeError = lib.mkMerge [ { int = throw "do not eval"; } { string = throw "do not eval"; } @@ -76,49 +94,55 @@ in { merged.positive.yay = 100; merged.extensi-foo.extensible = "foo"; merged.extensi-bar.extensible = "bar"; - okChecks = builtins.addErrorContext "while evaluating the assertions" - (assert config.intStrings.hello == { right = "hello world"; }; - assert config.intStrings.numberOne == { left = 1; }; - assert config.merged.negative == { nay = false; }; - assert config.merged.positive == { yay = 100; }; - assert config.merged.extensi-foo == { extensible = "foo"; }; - assert config.merged.extensi-bar == { extensible = "bar"; }; - assert config.docs."submodules..foo.bar".type == "signed integer"; - assert config.docs."submodules..qux".type == "string"; - assert config.docs."submodules..qux".declarations - == [ __curPos.file ]; - assert config.docs."submodules..qux".loc - == [ "submodules" "" "qux" ]; - assert config.docs."submodules..qux".name - == "submodules..qux"; - assert config.docs."submodules..qux".description - == "A qux for when you don't want a foo"; - assert config.docs."submodules..qux".readOnly == false; - assert config.docs."submodules..qux".visible == true; - # Not available (yet?) - # assert config.docs."submodules..qux".declarationsWithPositions == [ ... ]; - assert options.submodules.declarations == [ __curPos.file ]; - assert lib.length options.submodules.declarationPositions == 1; - assert (lib.head options.submodules.declarationPositions).file - == __curPos.file; - assert options.merged.declarations == [ __curPos.file __curPos.file ]; - assert lib.length options.merged.declarationPositions == 2; - assert (lib.elemAt options.merged.declarationPositions 0).file - == __curPos.file; - assert (lib.elemAt options.merged.declarationPositions 1).file - == __curPos.file; - assert (lib.elemAt options.merged.declarationPositions 0).line - != (lib.elemAt options.merged.declarationPositions 1).line; - assert mergedSubOption.declarations == [ __curPos.file __curPos.file ]; - assert lib.length mergedSubOption.declarationPositions == 2; - assert (lib.elemAt mergedSubOption.declarationPositions 0).file - == __curPos.file; - assert (lib.elemAt mergedSubOption.declarationPositions 1).file - == __curPos.file; - assert (lib.elemAt mergedSubOption.declarationPositions 0).line - != (lib.elemAt mergedSubOption.declarationPositions 1).line; - assert lib.length config.docs."merged..extensible".declarations - == 2; - true); + okChecks = builtins.addErrorContext "while evaluating the assertions" ( + assert config.intStrings.hello == { right = "hello world"; }; + assert config.intStrings.numberOne == { left = 1; }; + assert config.merged.negative == { nay = false; }; + assert config.merged.positive == { yay = 100; }; + assert config.merged.extensi-foo == { extensible = "foo"; }; + assert config.merged.extensi-bar == { extensible = "bar"; }; + assert config.docs."submodules..foo.bar".type == "signed integer"; + assert config.docs."submodules..qux".type == "string"; + assert config.docs."submodules..qux".declarations == [ __curPos.file ]; + assert + config.docs."submodules..qux".loc == [ + "submodules" + "" + "qux" + ]; + assert config.docs."submodules..qux".name == "submodules..qux"; + assert config.docs."submodules..qux".description == "A qux for when you don't want a foo"; + assert config.docs."submodules..qux".readOnly == false; + assert config.docs."submodules..qux".visible == true; + # Not available (yet?) + # assert config.docs."submodules..qux".declarationsWithPositions == [ ... ]; + assert options.submodules.declarations == [ __curPos.file ]; + assert lib.length options.submodules.declarationPositions == 1; + assert (lib.head options.submodules.declarationPositions).file == __curPos.file; + assert + options.merged.declarations == [ + __curPos.file + __curPos.file + ]; + assert lib.length options.merged.declarationPositions == 2; + assert (lib.elemAt options.merged.declarationPositions 0).file == __curPos.file; + assert (lib.elemAt options.merged.declarationPositions 1).file == __curPos.file; + assert + (lib.elemAt options.merged.declarationPositions 0).line + != (lib.elemAt options.merged.declarationPositions 1).line; + assert + mergedSubOption.declarations == [ + __curPos.file + __curPos.file + ]; + assert lib.length mergedSubOption.declarationPositions == 2; + assert (lib.elemAt mergedSubOption.declarationPositions 0).file == __curPos.file; + assert (lib.elemAt mergedSubOption.declarationPositions 1).file == __curPos.file; + assert + (lib.elemAt mergedSubOption.declarationPositions 0).line + != (lib.elemAt mergedSubOption.declarationPositions 1).line; + assert lib.length config.docs."merged..extensible".declarations == 2; + true + ); }; } diff --git a/extra/tests/stdlib/modules/types-unique.nix b/extra/tests/stdlib/modules/types-unique.nix index c99662d..83efcbb 100644 --- a/extra/tests/stdlib/modules/types-unique.nix +++ b/extra/tests/stdlib/modules/types-unique.nix @@ -1,19 +1,32 @@ { lib, ... }: -let inherit (lib) mkOption types; -in { +let + inherit (lib) mkOption types; +in +{ options.examples = mkOption { - type = types.lazyAttrsOf (types.unique { - message = - "We require a single definition, because seeing the whole value at once helps us maintain critical invariants of our system."; - } (types.attrsOf types.str)); + type = types.lazyAttrsOf ( + types.unique { + message = "We require a single definition, because seeing the whole value at once helps us maintain critical invariants of our system."; + } (types.attrsOf types.str) + ); }; imports = [ - { examples.merged = { b = "bee"; }; } + { + examples.merged = { + b = "bee"; + }; + } { examples.override = lib.mkForce { b = "bee"; }; } ]; config.examples = { - merged = { a = "aye"; }; - override = { a = "aye"; }; - badLazyType = { a = true; }; + merged = { + a = "aye"; + }; + override = { + a = "aye"; + }; + badLazyType = { + a = true; + }; }; } diff --git a/extra/tests/stdlib/modules/types.nix b/extra/tests/stdlib/modules/types.nix index 6bd9091..a4e506d 100644 --- a/extra/tests/stdlib/modules/types.nix +++ b/extra/tests/stdlib/modules/types.nix @@ -2,22 +2,19 @@ let inherit (builtins) storeDir; inherit (lib) types mkOption; -in { +in +{ options = { pathInStore = mkOption { type = types.lazyAttrsOf types.pathInStore; }; }; config = { - pathInStore.ok1 = - "${storeDir}/0lz9p8xhf89kb1c1kk6jxrzskaiygnlh-bash-5.2-p15.drv"; - pathInStore.ok2 = - "${storeDir}/0fb3ykw9r5hpayd05sr0cizwadzq1d8q-bash-5.2-p15"; - pathInStore.ok3 = - "${storeDir}/0fb3ykw9r5hpayd05sr0cizwadzq1d8q-bash-5.2-p15/bin/bash"; + pathInStore.ok1 = "${storeDir}/0lz9p8xhf89kb1c1kk6jxrzskaiygnlh-bash-5.2-p15.drv"; + pathInStore.ok2 = "${storeDir}/0fb3ykw9r5hpayd05sr0cizwadzq1d8q-bash-5.2-p15"; + pathInStore.ok3 = "${storeDir}/0fb3ykw9r5hpayd05sr0cizwadzq1d8q-bash-5.2-p15/bin/bash"; pathInStore.bad1 = ""; pathInStore.bad2 = "${storeDir}"; pathInStore.bad3 = "${storeDir}/"; - pathInStore.bad4 = - "${storeDir}/.links"; # technically true, but not reasonable + pathInStore.bad4 = "${storeDir}/.links"; # technically true, but not reasonable pathInStore.bad5 = "/foo/bar"; }; } diff --git a/extra/tests/stdlib/path/prop.nix b/extra/tests/stdlib/path/prop.nix index ae764be..1a88f18 100644 --- a/extra/tests/stdlib/path/prop.nix +++ b/extra/tests/stdlib/path/prop.nix @@ -6,22 +6,25 @@ # If `normalise` fails to evaluate, the attribute value is set to `""`. # If not, the resulting value is normalised again and an appropriate attribute set added to the output list. { -# The path to the nixpkgs lib to use -libpath, -# A flat directory containing files with randomly-generated -# path-like values -dir, }: + # The path to the nixpkgs lib to use + libpath, + # A flat directory containing files with randomly-generated + # path-like values + dir, +}: let lib = import libpath; # read each file into a string - strings = map (name: builtins.readFile (dir + "/${name}")) - (builtins.attrNames (builtins.readDir dir)); + strings = map (name: builtins.readFile (dir + "/${name}")) ( + builtins.attrNames (builtins.readDir dir) + ); inherit (lib.path.subpath) normalise isValid; inherit (lib.asserts) assertMsg; - normaliseAndCheck = str: + normaliseAndCheck = + str: let originalValid = isValid str; @@ -30,19 +33,24 @@ let absConcatOrig = /. + ("/" + str); absConcatNormalised = /. + ("/" + tryOnce.value); - # Check the lib.path.subpath.normalise property to only error on invalid subpaths - in assert assertMsg (originalValid -> tryOnce.success) '' - Even though string "${str}" is valid as a subpath, the normalisation for it failed''; - assert assertMsg (!originalValid -> !tryOnce.success) '' - Even though string "${str}" is invalid as a subpath, the normalisation for it succeeded''; + in + # Check the lib.path.subpath.normalise property to only error on invalid subpaths + assert assertMsg ( + originalValid -> tryOnce.success + ) ''Even though string "${str}" is valid as a subpath, the normalisation for it failed''; + assert assertMsg ( + !originalValid -> !tryOnce.success + ) ''Even though string "${str}" is invalid as a subpath, the normalisation for it succeeded''; # Check normalisation idempotency - assert assertMsg (originalValid -> tryTwice.success) '' - For valid subpath "${str}", the normalisation "${tryOnce.value}" was not a valid subpath''; - assert assertMsg (originalValid -> tryOnce.value == tryTwice.value) '' - For valid subpath "${str}", normalising it once gives "${tryOnce.value}" but normalising it twice gives a different result: "${tryTwice.value}"''; + assert assertMsg ( + originalValid -> tryTwice.success + ) ''For valid subpath "${str}", the normalisation "${tryOnce.value}" was not a valid subpath''; + assert assertMsg (originalValid -> tryOnce.value == tryTwice.value) + ''For valid subpath "${str}", normalising it once gives "${tryOnce.value}" but normalising it twice gives a different result: "${tryTwice.value}"''; # Check that normalisation doesn't change a string when appended to an absolute Nix path value - assert assertMsg (originalValid -> absConcatOrig == absConcatNormalised) '' - For valid subpath "${str}", appending to an absolute Nix path value gives "${absConcatOrig}", but appending the normalised result "${tryOnce.value}" gives a different value "${absConcatNormalised}"''; + assert assertMsg (originalValid -> absConcatOrig == absConcatNormalised) + ''For valid subpath "${str}", appending to an absolute Nix path value gives "${absConcatOrig}", but appending the normalised result "${tryOnce.value}" gives a different value "${absConcatNormalised}"''; # Return an empty string when failed if tryOnce.success then tryOnce.value else ""; -in lib.genAttrs strings normaliseAndCheck +in +lib.genAttrs strings normaliseAndCheck diff --git a/extra/tests/stdlib/path/unit.nix b/extra/tests/stdlib/path/unit.nix index 7fd9ab5..903e8ff 100644 --- a/extra/tests/stdlib/path/unit.nix +++ b/extra/tests/stdlib/path/unit.nix @@ -4,7 +4,13 @@ let lib = import libpath; inherit (lib.path) - hasPrefix removePrefix append splitRoot hasStorePathPrefix subpath; + hasPrefix + removePrefix + append + splitRoot + hasStorePathPrefix + subpath + ; # This is not allowed generally, but we're in the tests here, so we'll allow ourselves. storeDirPath = /. + builtins.storeDir; @@ -105,8 +111,7 @@ let }; testHasStorePathPrefixExample1 = { - expr = hasStorePathPrefix - (storeDirPath + "/nvl9ic0pj1fpyln3zaqrf4cclbqdfn1j-foo/bar/baz"); + expr = hasStorePathPrefix (storeDirPath + "/nvl9ic0pj1fpyln3zaqrf4cclbqdfn1j-foo/bar/baz"); expected = true; }; testHasStorePathPrefixExample2 = { @@ -114,8 +119,7 @@ let expected = false; }; testHasStorePathPrefixExample3 = { - expr = hasStorePathPrefix - (storeDirPath + "/nvl9ic0pj1fpyln3zaqrf4cclbqdfn1j-foo"); + expr = hasStorePathPrefix (storeDirPath + "/nvl9ic0pj1fpyln3zaqrf4cclbqdfn1j-foo"); expected = true; }; testHasStorePathPrefixExample4 = { @@ -123,13 +127,13 @@ let expected = false; }; testHasStorePathPrefixExample5 = { - expr = hasStorePathPrefix (storeDirPath - + "/.links/10gg8k3rmbw8p7gszarbk7qyd9jwxhcfq9i6s5i0qikx8alkk4hq"); + expr = hasStorePathPrefix ( + storeDirPath + "/.links/10gg8k3rmbw8p7gszarbk7qyd9jwxhcfq9i6s5i0qikx8alkk4hq" + ); expected = false; }; testHasStorePathPrefixExample6 = { - expr = hasStorePathPrefix - (storeDirPath + "/nvl9ic0pj1fpyln3zaqrf4cclbqdfn1j-foo.drv"); + expr = hasStorePathPrefix (storeDirPath + "/nvl9ic0pj1fpyln3zaqrf4cclbqdfn1j-foo.drv"); expected = true; }; @@ -202,11 +206,18 @@ let # Test examples from the lib.path.subpath.join documentation testSubpathJoinExample1 = { - expr = subpath.join [ "foo" "bar/baz" ]; + expr = subpath.join [ + "foo" + "bar/baz" + ]; expected = "./foo/bar/baz"; }; testSubpathJoinExample2 = { - expr = subpath.join [ "./foo" "." "bar//./baz/" ]; + expr = subpath.join [ + "./foo" + "." + "bar//./baz/" + ]; expected = "./foo/bar/baz"; }; testSubpathJoinExample3 = { @@ -287,14 +298,19 @@ let }; testSubpathComponentsExample2 = { expr = subpath.components "./foo//bar/./baz/"; - expected = [ "foo" "bar" "baz" ]; + expected = [ + "foo" + "bar" + "baz" + ]; }; testSubpathComponentsExample3 = { expr = (builtins.tryEval (subpath.components "/foo")).success; expected = false; }; }; -in if cases == [ ] then +in +if cases == [ ] then "Unit tests successful" else throw "Path unit tests failed: ${lib.generators.toPretty { } cases}" diff --git a/extra/tests/stdlib/systems.nix b/extra/tests/stdlib/systems.nix index dc5ee17..ae5736d 100644 --- a/extra/tests/stdlib/systems.nix +++ b/extra/tests/stdlib/systems.nix @@ -8,23 +8,26 @@ let expected = lib.sort lib.lessThan y; }; - /* Try to convert an elaborated system back to a simple string. If not possible, - return null. So we have the property: + /* + Try to convert an elaborated system back to a simple string. If not possible, + return null. So we have the property: - sys: _valid_ sys -> - sys == elaborate (toLosslessStringMaybe sys) + sys: _valid_ sys -> + sys == elaborate (toLosslessStringMaybe sys) - NOTE: This property is not guaranteed when `sys` was elaborated by a different - version of Nixpkgs. + NOTE: This property is not guaranteed when `sys` was elaborated by a different + version of Nixpkgs. */ - toLosslessStringMaybe = sys: + toLosslessStringMaybe = + sys: if lib.isString sys then sys else if lib.systems.equals sys (lib.systems.elaborate sys.system) then sys.system else null; -in lib.runTests ( +in +lib.runTests ( # We assert that the new algorithmic way of generating these lists matches the # way they were hard-coded before. # @@ -33,9 +36,21 @@ in lib.runTests ( # tests as new platforms become supported, and then just give the diff a quick # sanity check before committing :). (with lib.systems.doubles; { - testall = mseteq all (linux ++ darwin ++ freebsd ++ openbsd ++ netbsd - ++ illumos ++ wasi ++ windows ++ embedded ++ mmix ++ js ++ genode - ++ redox); + testall = mseteq all ( + linux + ++ darwin + ++ freebsd + ++ openbsd + ++ netbsd + ++ illumos + ++ wasi + ++ windows + ++ embedded + ++ mmix + ++ js + ++ genode + ++ redox + ); testarm = mseteq arm [ "armv5tel-linux" @@ -92,11 +107,20 @@ in lib.runTests ( "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" ]; + 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" @@ -111,18 +135,28 @@ in lib.runTests ( "x86_64-none" ]; - testcygwin = mseteq cygwin [ "i686-cygwin" "x86_64-cygwin" ]; + testcygwin = mseteq cygwin [ + "i686-cygwin" + "x86_64-cygwin" + ]; testdarwin = mseteq darwin [ "x86_64-darwin" "i686-darwin" "aarch64-darwin" "armv7a-darwin" ]; - testfreebsd = mseteq freebsd [ "i686-freebsd13" "x86_64-freebsd13" ]; - testgenode = - mseteq genode [ "aarch64-genode" "i686-genode" "x86_64-genode" ]; + testfreebsd = mseteq freebsd [ + "i686-freebsd13" + "x86_64-freebsd13" + ]; + testgenode = mseteq genode [ + "aarch64-genode" + "i686-genode" + "x86_64-genode" + ]; testredox = mseteq redox [ "x86_64-redox" ]; - testgnu = mseteq gnu (linux + testgnu = mseteq gnu ( + linux # ++ kfreebsd ++ ... ); testillumos = mseteq illumos [ "x86_64-solaris" ]; @@ -162,19 +196,25 @@ in lib.runTests ( "riscv64-netbsd" "x86_64-netbsd" ]; - testopenbsd = mseteq openbsd [ "i686-openbsd" "x86_64-openbsd" ]; + testopenbsd = mseteq openbsd [ + "i686-openbsd" + "x86_64-openbsd" + ]; testwindows = mseteq windows [ "i686-cygwin" "x86_64-cygwin" "i686-windows" "x86_64-windows" ]; - testunix = mseteq unix (linux ++ darwin ++ freebsd ++ openbsd ++ netbsd - ++ illumos ++ cygwin ++ redox); - }) // { + testunix = mseteq unix ( + linux ++ darwin ++ freebsd ++ openbsd ++ netbsd ++ illumos ++ cygwin ++ redox + ); + }) + // { test_equals_example_x86_64-linux = { - expr = lib.systems.equals (lib.systems.elaborate "x86_64-linux") - (lib.systems.elaborate "x86_64-linux"); + expr = lib.systems.equals (lib.systems.elaborate "x86_64-linux") ( + lib.systems.elaborate "x86_64-linux" + ); expected = true; }; @@ -183,30 +223,36 @@ in lib.runTests ( expected = "x86_64-linux"; }; test_toLosslessStringMaybe_fail = { - expr = toLosslessStringMaybe - (lib.systems.elaborate "x86_64-linux" // { something = "extra"; }); + expr = toLosslessStringMaybe (lib.systems.elaborate "x86_64-linux" // { something = "extra"; }); expected = null; }; } # Generate test cases to assert that a change in any non-function attribute makes a platform unequal - // lib.concatMapAttrs (platformAttrName: origValue: { - ${"test_equals_unequal_${platformAttrName}"} = let - modified = assert origValue != arbitraryValue; - lib.systems.elaborate "x86_64-linux" // { - ${platformAttrName} = arbitraryValue; - }; - arbitraryValue = x: "<>"; - in { - expr = lib.systems.equals (lib.systems.elaborate "x86_64-linux") modified; - expected = { - # Changes in these attrs are not detectable because they're function. - # The functions should be derived from the data, so this is not a problem. - canExecute = null; - emulator = null; - emulatorAvailable = null; - isCompatible = null; - } ? ${platformAttrName}; - }; - }) (lib.systems.elaborate "x86_64-linux" - # arbitrary choice, just to get all the elaborated attrNames - )) + // + lib.concatMapAttrs + (platformAttrName: origValue: { + ${"test_equals_unequal_${platformAttrName}"} = + let + modified = + assert origValue != arbitraryValue; + lib.systems.elaborate "x86_64-linux" // { ${platformAttrName} = arbitraryValue; }; + arbitraryValue = x: "<>"; + in + { + expr = lib.systems.equals (lib.systems.elaborate "x86_64-linux") modified; + expected = + { + # Changes in these attrs are not detectable because they're function. + # The functions should be derived from the data, so this is not a problem. + canExecute = null; + emulator = null; + emulatorAvailable = null; + isCompatible = null; + } ? ${platformAttrName}; + }; + }) + ( + lib.systems.elaborate "x86_64-linux" + # arbitrary choice, just to get all the elaborated attrNames + ) +) diff --git a/flake.nix b/flake.nix index 2029162..1b864a7 100644 --- a/flake.nix +++ b/flake.nix @@ -1,7 +1,9 @@ { description = "Auxolotl system-agnostic libraries"; - inputs = {}; - outputs = {self}: { - lib = import ./nix; - }; + inputs = { }; + outputs = + { self }: + { + lib = import ./nix; + }; } diff --git a/nix/default.nix b/nix/default.nix index cd28f30..9b75451 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -1,4 +1,5 @@ let inherit (import ./stdlib/fixed-points.nix { inherit lib; }) makeExtensible; lib = makeExtensible (self: (import ./stdlib) // (import ./auxlib self)); -in lib +in +lib diff --git a/nix/stdlib/asserts.nix b/nix/stdlib/asserts.nix index 8ef7617..2f85333 100644 --- a/nix/stdlib/asserts.nix +++ b/nix/stdlib/asserts.nix @@ -1,130 +1,135 @@ -{ lib }: rec { - /* * - Throw if pred is false, else return pred. - Intended to be used to augment asserts with helpful error messages. +{ lib }: +rec { + /* + * + Throw if pred is false, else return pred. + Intended to be used to augment asserts with helpful error messages. - # Inputs + # Inputs - `pred` + `pred` - : Predicate that needs to succeed, otherwise `msg` is thrown + : Predicate that needs to succeed, otherwise `msg` is thrown - `msg` + `msg` - : Message to throw in case `pred` fails + : Message to throw in case `pred` fails - # Type + # Type - ``` - assertMsg :: Bool -> String -> Bool - ``` + ``` + assertMsg :: Bool -> String -> Bool + ``` - # Examples - :::{.example} - ## `lib.asserts.assertMsg` usage example + # Examples + :::{.example} + ## `lib.asserts.assertMsg` usage example - ```nix - assertMsg false "nope" - stderr> error: nope - assert assertMsg ("foo" == "bar") "foo is not bar, silly"; "" - stderr> error: foo is not bar, silly - ``` + ```nix + assertMsg false "nope" + stderr> error: nope + assert assertMsg ("foo" == "bar") "foo is not bar, silly"; "" + stderr> error: foo is not bar, silly + ``` - ::: + ::: */ # TODO(Profpatsch): add tests that check stderr assertMsg = pred: msg: pred || builtins.throw msg; - /* * - Specialized `assertMsg` for checking if `val` is one of the elements - of the list `xs`. Useful for checking enums. + /* + * + Specialized `assertMsg` for checking if `val` is one of the elements + of the list `xs`. Useful for checking enums. - # Inputs + # Inputs - `name` + `name` - : The name of the variable the user entered `val` into, for inclusion in the error message + : The name of the variable the user entered `val` into, for inclusion in the error message - `val` + `val` - : The value of what the user provided, to be compared against the values in `xs` + : The value of what the user provided, to be compared against the values in `xs` - `xs` + `xs` - : The list of valid values + : The list of valid values - # Type + # Type - ``` - assertOneOf :: String -> ComparableVal -> List ComparableVal -> Bool - ``` + ``` + assertOneOf :: String -> ComparableVal -> List ComparableVal -> Bool + ``` - # Examples - :::{.example} - ## `lib.asserts.assertOneOf` usage example + # Examples + :::{.example} + ## `lib.asserts.assertOneOf` usage example - ```nix - let sslLibrary = "libressl"; - in assertOneOf "sslLibrary" sslLibrary [ "openssl" "bearssl" ] - stderr> error: sslLibrary must be one of [ - stderr> "openssl" - stderr> "bearssl" - stderr> ], but is: "libressl" - ``` + ```nix + let sslLibrary = "libressl"; + in assertOneOf "sslLibrary" sslLibrary [ "openssl" "bearssl" ] + stderr> error: sslLibrary must be one of [ + stderr> "openssl" + stderr> "bearssl" + stderr> ], but is: "libressl" + ``` - ::: + ::: */ - assertOneOf = name: val: xs: - assertMsg (lib.elem val xs) - "${name} must be one of ${lib.generators.toPretty { } xs}, but is: ${ + assertOneOf = + name: val: xs: + assertMsg (lib.elem val xs) "${name} must be one of ${lib.generators.toPretty { } xs}, but is: ${ lib.generators.toPretty { } val }"; - /* * - Specialized `assertMsg` for checking if every one of `vals` is one of the elements - of the list `xs`. Useful for checking lists of supported attributes. + /* + * + Specialized `assertMsg` for checking if every one of `vals` is one of the elements + of the list `xs`. Useful for checking lists of supported attributes. - # Inputs + # Inputs - `name` + `name` - : The name of the variable the user entered `val` into, for inclusion in the error message + : The name of the variable the user entered `val` into, for inclusion in the error message - `vals` + `vals` - : The list of values of what the user provided, to be compared against the values in `xs` + : The list of values of what the user provided, to be compared against the values in `xs` - `xs` + `xs` - : The list of valid values + : The list of valid values - # Type + # Type - ``` - assertEachOneOf :: String -> List ComparableVal -> List ComparableVal -> Bool - ``` + ``` + assertEachOneOf :: String -> List ComparableVal -> List ComparableVal -> Bool + ``` - # Examples - :::{.example} - ## `lib.asserts.assertEachOneOf` usage example + # Examples + :::{.example} + ## `lib.asserts.assertEachOneOf` usage example - ```nix - let sslLibraries = [ "libressl" "bearssl" ]; - in assertEachOneOf "sslLibraries" sslLibraries [ "openssl" "bearssl" ] - stderr> error: each element in sslLibraries must be one of [ - stderr> "openssl" - stderr> "bearssl" - stderr> ], but is: [ - stderr> "libressl" - stderr> "bearssl" - stderr> ] - ``` + ```nix + let sslLibraries = [ "libressl" "bearssl" ]; + in assertEachOneOf "sslLibraries" sslLibraries [ "openssl" "bearssl" ] + stderr> error: each element in sslLibraries must be one of [ + stderr> "openssl" + stderr> "bearssl" + stderr> ], but is: [ + stderr> "libressl" + stderr> "bearssl" + stderr> ] + ``` - ::: + ::: */ - assertEachOneOf = name: vals: xs: + assertEachOneOf = + name: vals: xs: assertMsg (lib.all (val: lib.elem val xs) vals) - "each element in ${name} must be one of ${ - lib.generators.toPretty { } xs - }, but is: ${lib.generators.toPretty { } vals}"; + "each element in ${name} must be one of ${lib.generators.toPretty { } xs}, but is: ${ + lib.generators.toPretty { } vals + }"; } diff --git a/nix/stdlib/attrsets.nix b/nix/stdlib/attrsets.nix index 372c861..92c7149 100644 --- a/nix/stdlib/attrsets.nix +++ b/nix/stdlib/attrsets.nix @@ -1,19 +1,23 @@ /* -* -Operations on attribute sets. + * + Operations on attribute sets. */ -{lib}: let +{ lib }: +let inherit (builtins) head length; - inherit (lib.trivial) isInOldestRelease mergeAttrs warn warnIf; - inherit - (lib.strings) + inherit (lib.trivial) + isInOldestRelease + mergeAttrs + warn + warnIf + ; + inherit (lib.strings) concatStringsSep concatMapStringsSep escapeNixIdentifier sanitizeDerivationName ; - inherit - (lib.lists) + inherit (lib.lists) foldr foldl' concatMap @@ -24,1977 +28,2004 @@ Operations on attribute sets. take foldl ; -in rec { - inherit (builtins) attrNames listToAttrs hasAttr isAttrs getAttr removeAttrs; +in +rec { + inherit (builtins) + attrNames + listToAttrs + hasAttr + isAttrs + getAttr + removeAttrs + ; /* - * - Return an attribute from nested attribute sets. + * + Return an attribute from nested attribute sets. - Nix has an [attribute selection operator `. or`](https://nixos.org/manual/nix/stable/language/operators#attribute-selection) which is sufficient for such queries, as long as the number of attributes is static. For example: + Nix has an [attribute selection operator `. or`](https://nixos.org/manual/nix/stable/language/operators#attribute-selection) which is sufficient for such queries, as long as the number of attributes is static. For example: - ```nix - (x.a.b or 6) == attrByPath ["a" "b"] 6 x - # and - (x.${f p}."example.com" or 6) == attrByPath [ (f p) "example.com" ] 6 x - ``` + ```nix + (x.a.b or 6) == attrByPath ["a" "b"] 6 x + # and + (x.${f p}."example.com" or 6) == attrByPath [ (f p) "example.com" ] 6 x + ``` - # Inputs + # Inputs - `attrPath` + `attrPath` - : A list of strings representing the attribute path to return from `set` + : A list of strings representing the attribute path to return from `set` - `default` + `default` - : Default value if `attrPath` does not resolve to an existing value + : Default value if `attrPath` does not resolve to an existing value - `set` + `set` - : The nested attribute set to select values from + : The nested attribute set to select values from - # Type + # Type - ``` - attrByPath :: [String] -> Any -> AttrSet -> Any - ``` + ``` + attrByPath :: [String] -> Any -> AttrSet -> Any + ``` - # Examples - :::{.example} - ## `lib.attrsets.attrByPath` usage example + # Examples + :::{.example} + ## `lib.attrsets.attrByPath` usage example - ```nix - x = { a = { b = 3; }; } - # ["a" "b"] is equivalent to x.a.b - # 6 is a default value to return if the path does not exist in attrset - attrByPath ["a" "b"] 6 x - => 3 - attrByPath ["z" "z"] 6 x - => 6 - ``` + ```nix + x = { a = { b = 3; }; } + # ["a" "b"] is equivalent to x.a.b + # 6 is a default value to return if the path does not exist in attrset + attrByPath ["a" "b"] 6 x + => 3 + attrByPath ["z" "z"] 6 x + => 6 + ``` - ::: + ::: */ - attrByPath = attrPath: default: set: let - lenAttrPath = length attrPath; - attrByPath' = n: s: ( - if n == lenAttrPath - then s - else - (let - attr = elemAt attrPath n; - in - if s ? ${attr} - then attrByPath' (n + 1) s.${attr} - else default) - ); - in + attrByPath = + attrPath: default: set: + let + lenAttrPath = length attrPath; + attrByPath' = + n: s: + ( + if n == lenAttrPath then + s + else + ( + let + attr = elemAt attrPath n; + in + if s ? ${attr} then attrByPath' (n + 1) s.${attr} else default + ) + ); + in attrByPath' 0 set; /* - * - Return if an attribute from nested attribute set exists. + * + Return if an attribute from nested attribute set exists. - Nix has a [has attribute operator `?`](https://nixos.org/manual/nix/stable/language/operators#has-attribute), which is sufficient for such queries, as long as the number of attributes is static. For example: + Nix has a [has attribute operator `?`](https://nixos.org/manual/nix/stable/language/operators#has-attribute), which is sufficient for such queries, as long as the number of attributes is static. For example: - ```nix - (x?a.b) == hasAttrByPath ["a" "b"] x - # and - (x?${f p}."example.com") == hasAttrByPath [ (f p) "example.com" ] x - ``` + ```nix + (x?a.b) == hasAttrByPath ["a" "b"] x + # and + (x?${f p}."example.com") == hasAttrByPath [ (f p) "example.com" ] x + ``` - **Laws**: - 1. ```nix - hasAttrByPath [] x == true - ``` + **Laws**: + 1. ```nix + hasAttrByPath [] x == true + ``` - # Inputs + # Inputs - `attrPath` + `attrPath` - : A list of strings representing the attribute path to check from `set` + : A list of strings representing the attribute path to check from `set` - `e` + `e` - : The nested attribute set to check + : The nested attribute set to check - # Type + # Type - ``` - hasAttrByPath :: [String] -> AttrSet -> Bool - ``` + ``` + hasAttrByPath :: [String] -> AttrSet -> Bool + ``` - # Examples - :::{.example} - ## `lib.attrsets.hasAttrByPath` usage example + # Examples + :::{.example} + ## `lib.attrsets.hasAttrByPath` usage example - ```nix - x = { a = { b = 3; }; } - hasAttrByPath ["a" "b"] x - => true - hasAttrByPath ["z" "z"] x - => false - hasAttrByPath [] (throw "no need") - => true - ``` + ```nix + x = { a = { b = 3; }; } + hasAttrByPath ["a" "b"] x + => true + hasAttrByPath ["z" "z"] x + => false + hasAttrByPath [] (throw "no need") + => true + ``` - ::: + ::: */ - hasAttrByPath = attrPath: e: let - lenAttrPath = length attrPath; - hasAttrByPath' = n: s: (n - == lenAttrPath - || (let - attr = elemAt attrPath n; - in - if s ? ${attr} - then hasAttrByPath' (n + 1) s.${attr} - else false)); - in + hasAttrByPath = + attrPath: e: + let + lenAttrPath = length attrPath; + hasAttrByPath' = + n: s: + ( + n == lenAttrPath + || ( + let + attr = elemAt attrPath n; + in + if s ? ${attr} then hasAttrByPath' (n + 1) s.${attr} else false + ) + ); + in hasAttrByPath' 0 e; /* - * - Return the longest prefix of an attribute path that refers to an existing attribute in a nesting of attribute sets. + * + Return the longest prefix of an attribute path that refers to an existing attribute in a nesting of attribute sets. - Can be used after [`mapAttrsRecursiveCond`](#function-library-lib.attrsets.mapAttrsRecursiveCond) to apply a condition, - although this will evaluate the predicate function on sibling attributes as well. + Can be used after [`mapAttrsRecursiveCond`](#function-library-lib.attrsets.mapAttrsRecursiveCond) to apply a condition, + although this will evaluate the predicate function on sibling attributes as well. - Note that the empty attribute path is valid for all values, so this function only throws an exception if any of its inputs does. + Note that the empty attribute path is valid for all values, so this function only throws an exception if any of its inputs does. - **Laws**: - 1. ```nix - attrsets.longestValidPathPrefix [] x == [] - ``` + **Laws**: + 1. ```nix + attrsets.longestValidPathPrefix [] x == [] + ``` - 2. ```nix - hasAttrByPath (attrsets.longestValidPathPrefix p x) x == true - ``` + 2. ```nix + hasAttrByPath (attrsets.longestValidPathPrefix p x) x == true + ``` - # Inputs + # Inputs - `attrPath` + `attrPath` - : A list of strings representing the longest possible path that may be returned. + : A list of strings representing the longest possible path that may be returned. - `v` + `v` - : The nested attribute set to check. + : The nested attribute set to check. - # Type + # Type - ``` - attrsets.longestValidPathPrefix :: [String] -> Value -> [String] - ``` + ``` + attrsets.longestValidPathPrefix :: [String] -> Value -> [String] + ``` - # Examples - :::{.example} - ## `lib.attrsets.longestValidPathPrefix` usage example + # Examples + :::{.example} + ## `lib.attrsets.longestValidPathPrefix` usage example - ```nix - x = { a = { b = 3; }; } - attrsets.longestValidPathPrefix ["a" "b" "c"] x - => ["a" "b"] - attrsets.longestValidPathPrefix ["a"] x - => ["a"] - attrsets.longestValidPathPrefix ["z" "z"] x - => [] - attrsets.longestValidPathPrefix ["z" "z"] (throw "no need") - => [] - ``` + ```nix + x = { a = { b = 3; }; } + attrsets.longestValidPathPrefix ["a" "b" "c"] x + => ["a" "b"] + attrsets.longestValidPathPrefix ["a"] x + => ["a"] + attrsets.longestValidPathPrefix ["z" "z"] x + => [] + attrsets.longestValidPathPrefix ["z" "z"] (throw "no need") + => [] + ``` - ::: + ::: */ - longestValidPathPrefix = attrPath: v: let - lenAttrPath = length attrPath; - getPrefixForSetAtIndex = - # The nested attribute set to check, if it is an attribute set, which - # is not a given. - remainingSet: - # The index of the attribute we're about to check, as well as - # the length of the prefix we've already checked. - remainingPathIndex: - if remainingPathIndex == lenAttrPath - then + longestValidPathPrefix = + attrPath: v: + let + lenAttrPath = length attrPath; + getPrefixForSetAtIndex = + # The nested attribute set to check, if it is an attribute set, which + # is not a given. + remainingSet: + # The index of the attribute we're about to check, as well as + # the length of the prefix we've already checked. + remainingPathIndex: + if remainingPathIndex == lenAttrPath then # All previously checked attributes exist, and no attr names left, # so we return the whole path. attrPath - else let - attr = elemAt attrPath remainingPathIndex; - in - if remainingSet ? ${attr} - then - getPrefixForSetAtIndex - remainingSet.${attr} # advance from the set to the attribute value + else + let + attr = elemAt attrPath remainingPathIndex; + in + if remainingSet ? ${attr} then + getPrefixForSetAtIndex remainingSet.${attr} # advance from the set to the attribute value - (remainingPathIndex + 1) # advance the path + (remainingPathIndex + 1) # advance the path else # The attribute doesn't exist, so we return the prefix up to the # previously checked length. take remainingPathIndex attrPath; - in + in getPrefixForSetAtIndex v 0; /* - * - 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` - : A list of strings representing the attribute path to set + : A list of strings representing the attribute path to set - `value` + `value` - : The value to set at the location described by `attrPath` + : The value to set at the location described by `attrPath` - # Type + # Type - ``` - setAttrByPath :: [String] -> Any -> AttrSet - ``` + ``` + setAttrByPath :: [String] -> Any -> AttrSet + ``` - # Examples - :::{.example} - ## `lib.attrsets.setAttrByPath` usage example + # Examples + :::{.example} + ## `lib.attrsets.setAttrByPath` usage example - ```nix - setAttrByPath ["a" "b"] 3 - => { a = { b = 3; }; } - ``` + ```nix + setAttrByPath ["a" "b"] 3 + => { a = { b = 3; }; } + ``` - ::: + ::: */ - setAttrByPath = attrPath: value: let - len = length attrPath; - atDepth = n: - if n == len - then value - else {${elemAt attrPath n} = atDepth (n + 1);}; - in + setAttrByPath = + attrPath: value: + let + len = length attrPath; + atDepth = n: if n == len then value else { ${elemAt attrPath n} = atDepth (n + 1); }; + in atDepth 0; /* - * - Like `attrByPath`, but without a default value. If it doesn't find the - path it will throw an error. + * + Like `attrByPath`, but without a default value. If it doesn't find the + path it will throw an error. - Nix has an [attribute selection operator](https://nixos.org/manual/nix/stable/language/operators#attribute-selection) which is sufficient for such queries, as long as the number of attributes is static. For example: + Nix has an [attribute selection operator](https://nixos.org/manual/nix/stable/language/operators#attribute-selection) which is sufficient for such queries, as long as the number of attributes is static. For example: - ```nix - x.a.b == getAttrByPath ["a" "b"] x - # and - x.${f p}."example.com" == getAttrByPath [ (f p) "example.com" ] x - ``` + ```nix + x.a.b == getAttrByPath ["a" "b"] x + # and + x.${f p}."example.com" == getAttrByPath [ (f p) "example.com" ] x + ``` - # Inputs + # Inputs - `attrPath` + `attrPath` - : A list of strings representing the attribute path to get from `set` + : A list of strings representing the attribute path to get from `set` - `set` + `set` - : The nested attribute set to find the value in. + : The nested attribute set to find the value in. - # Type + # Type - ``` - getAttrFromPath :: [String] -> AttrSet -> Any - ``` + ``` + getAttrFromPath :: [String] -> AttrSet -> Any + ``` - # Examples - :::{.example} - ## `lib.attrsets.getAttrFromPath` usage example + # Examples + :::{.example} + ## `lib.attrsets.getAttrFromPath` usage example - ```nix - x = { a = { b = 3; }; } - getAttrFromPath ["a" "b"] x - => 3 - getAttrFromPath ["z" "z"] x - => error: cannot find attribute `z.z' - ``` + ```nix + x = { a = { b = 3; }; } + getAttrFromPath ["a" "b"] x + => 3 + getAttrFromPath ["z" "z"] x + => error: cannot find attribute `z.z' + ``` - ::: + ::: */ - getAttrFromPath = attrPath: set: - attrByPath attrPath - (abort ("cannot find attribute `" + concatStringsSep "." attrPath + "'")) - set; + getAttrFromPath = + 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` - : 1\. Function argument + : 1\. Function argument - `v` + `v` - : 2\. Function argument + : 2\. Function argument - # Type + # Type - ``` - concatMapAttrs :: (String -> a -> AttrSet) -> AttrSet -> AttrSet - ``` + ``` + concatMapAttrs :: (String -> a -> AttrSet) -> AttrSet -> AttrSet + ``` - # Examples - :::{.example} - ## `lib.attrsets.concatMapAttrs` usage example + # Examples + :::{.example} + ## `lib.attrsets.concatMapAttrs` usage example - ```nix - concatMapAttrs - (name: value: { - ${name} = value; - ${name + value} = value; - }) - { x = "a"; y = "b"; } - => { x = "a"; xa = "a"; y = "b"; yb = "b"; } - ``` + ```nix + concatMapAttrs + (name: value: { + ${name} = value; + ${name + value} = value; + }) + { x = "a"; y = "b"; } + => { x = "a"; xa = "a"; y = "b"; yb = "b"; } + ``` - ::: + ::: */ - concatMapAttrs = f: v: foldl' mergeAttrs {} (attrValues (mapAttrs f v)); + concatMapAttrs = f: v: foldl' mergeAttrs { } (attrValues (mapAttrs f v)); /* - * - Update or set specific paths of an attribute set. - - Takes a list of updates to apply and an attribute set to apply them to, - and returns the attribute set with the updates applied. Updates are - represented as `{ path = ...; update = ...; }` values, where `path` is a - list of strings representing the attribute path that should be updated, - and `update` is a function that takes the old value at that attribute path - as an argument and returns the new - value it should be. - - Properties: - - - Updates to deeper attribute paths are applied before updates to more - shallow attribute paths - - - Multiple updates to the same attribute path are applied in the order - they appear in the update list - - - If any but the last `path` element leads into a value that is not an - attribute set, an error is thrown - - - If there is an update for an attribute path that doesn't exist, - accessing the argument in the update function causes an error, but - intermediate attribute sets are implicitly created as needed - - # Type - - ``` - updateManyAttrsByPath :: [{ path :: [String]; update :: (Any -> Any); }] -> AttrSet -> AttrSet - ``` - - # Examples - :::{.example} - ## `lib.attrsets.updateManyAttrsByPath` usage example - - ```nix - updateManyAttrsByPath [ - { - path = [ "a" "b" ]; - update = old: { d = old.c; }; - } - { - path = [ "a" "b" "c" ]; - update = old: old + 1; - } - { - path = [ "x" "y" ]; - update = old: "xy"; - } - ] { a.b.c = 0; } - => { a = { b = { d = 1; }; }; x = { y = "xy"; }; } - ``` - - ::: + * + Update or set specific paths of an attribute set. + + Takes a list of updates to apply and an attribute set to apply them to, + and returns the attribute set with the updates applied. Updates are + represented as `{ path = ...; update = ...; }` values, where `path` is a + list of strings representing the attribute path that should be updated, + and `update` is a function that takes the old value at that attribute path + as an argument and returns the new + value it should be. + + Properties: + + - Updates to deeper attribute paths are applied before updates to more + shallow attribute paths + + - Multiple updates to the same attribute path are applied in the order + they appear in the update list + + - If any but the last `path` element leads into a value that is not an + attribute set, an error is thrown + + - If there is an update for an attribute path that doesn't exist, + accessing the argument in the update function causes an error, but + intermediate attribute sets are implicitly created as needed + + # Type + + ``` + updateManyAttrsByPath :: [{ path :: [String]; update :: (Any -> Any); }] -> AttrSet -> AttrSet + ``` + + # Examples + :::{.example} + ## `lib.attrsets.updateManyAttrsByPath` usage example + + ```nix + updateManyAttrsByPath [ + { + path = [ "a" "b" ]; + update = old: { d = old.c; }; + } + { + path = [ "a" "b" "c" ]; + update = old: old + 1; + } + { + path = [ "x" "y" ]; + update = old: "xy"; + } + ] { a.b.c = 0; } + => { a = { b = { d = 1; }; }; x = { y = "xy"; }; } + ``` + + ::: */ - updateManyAttrsByPath = let - # When recursing into attributes, instead of updating the `path` of each - # 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 - # path without the prefix length, so that we can reuse the original list - # entries. - go = prefixLength: hasValue: value: updates: let - # Splits updates into ones on this level (split.right) - # And ones on levels further down (split.wrong) - split = partition (el: length el.path == prefixLength) updates; - - # Groups updates on further down levels into the attributes they modify - nested = groupBy (el: elemAt el.path prefixLength) split.wrong; - - # Applies only nested modification to the input value - withNestedMods = - # Return the value directly if we don't have any nested modifications - if split.wrong == [] - then - if hasValue - then value - else - # 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 - # with `hasValue == false` for nested updates, in which case - # it's also always called with at least one update - let - updatePath = (head split.right).path; - in - throw - ("updateManyAttrsByPath: Path '${showAttrPath updatePath}' does " - + "not exist in the given value, but the first update to this " - + "path tries to access the existing value.") - else - # If there are nested modifications, try to apply them to the value - if !hasValue - then - # But if we don't have a value, just use an empty attribute set - # as the value, but simplify the code a bit - mapAttrs (name: go (prefixLength + 1) false null) nested - else if isAttrs value - then - # If we do have a value and it's an attribute set, override it - # with the nested modifications - value - // mapAttrs - (name: go (prefixLength + 1) (value ? ${name}) value.${name}) - nested - else - # However if it's not an attribute set, we can't apply the nested - # modifications, throw an error - let - updatePath = (head split.wrong).path; - in - throw ("updateManyAttrsByPath: Path '${ - showAttrPath updatePath - }' needs to " - + "be updated, but path '${ - showAttrPath (take prefixLength updatePath) - }' " + updateManyAttrsByPath = + let + # When recursing into attributes, instead of updating the `path` of each + # 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 + # path without the prefix length, so that we can reuse the original list + # entries. + go = + prefixLength: hasValue: value: updates: + let + # Splits updates into ones on this level (split.right) + # And ones on levels further down (split.wrong) + split = partition (el: length el.path == prefixLength) updates; + + # Groups updates on further down levels into the attributes they modify + nested = groupBy (el: elemAt el.path prefixLength) split.wrong; + + # Applies only nested modification to the input value + withNestedMods = + # Return the value directly if we don't have any nested modifications + if split.wrong == [ ] then + if hasValue then + value + else + # 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 + # with `hasValue == false` for nested updates, in which case + # it's also always called with at least one update + let + updatePath = (head split.right).path; + in + throw ( + "updateManyAttrsByPath: Path '${showAttrPath updatePath}' does " + + "not exist in the given value, but the first update to this " + + "path tries to access the existing value." + ) + else + # If there are nested modifications, try to apply them to the value + if !hasValue then + # But if we don't have a value, just use an empty attribute set + # as the value, but simplify the code a bit + mapAttrs (name: go (prefixLength + 1) false null) nested + else if isAttrs value then + # If we do have a value and it's an attribute set, override it + # with the nested modifications + value // mapAttrs (name: go (prefixLength + 1) (value ? ${name}) value.${name}) nested + else + # However if it's not an attribute set, we can't apply the nested + # modifications, throw an error + let + updatePath = (head split.wrong).path; + in + throw ( + "updateManyAttrsByPath: Path '${showAttrPath updatePath}' needs to " + + "be updated, but path '${showAttrPath (take prefixLength updatePath)}' " + "of the given value is not an attribute set, so we can't " - + "update an attribute inside of it."); - # We get the final result by applying all the updates on this level - # after having applied all the nested updates - # We use foldl instead of foldl' so that in case of multiple updates, - # intermediate values aren't evaluated if not needed + + "update an attribute inside of it." + ); + in + # We get the final result by applying all the updates on this level + # after having applied all the nested updates + # We use foldl instead of foldl' so that in case of multiple updates, + # intermediate values aren't evaluated if not needed + 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; /* - * - Return the specified attributes from a set. + * + Return the specified attributes from a set. - # Inputs + # Inputs - `nameList` + `nameList` - : The list of attributes to fetch from `set`. Each attribute name must exist on the attrbitue set + : The list of attributes to fetch from `set`. Each attribute name must exist on the attrbitue set - `set` + `set` - : The set to get attribute values from + : The set to get attribute values from - # Type + # Type - ``` - attrVals :: [String] -> AttrSet -> [Any] - ``` + ``` + attrVals :: [String] -> AttrSet -> [Any] + ``` - # Examples - :::{.example} - ## `lib.attrsets.attrVals` usage example + # Examples + :::{.example} + ## `lib.attrsets.attrVals` usage example - ```nix - attrVals ["a" "b" "c"] as - => [as.a as.b as.c] - ``` + ```nix + attrVals ["a" "b" "c"] as + => [as.a as.b as.c] + ``` - ::: + ::: */ attrVals = nameList: set: map (x: set.${x}) nameList; /* - * - Return the values of all attributes in the given set, sorted by - attribute name. + * + Return the values of all attributes in the given set, sorted by + attribute name. - # Type + # Type - ``` - attrValues :: AttrSet -> [Any] - ``` + ``` + attrValues :: AttrSet -> [Any] + ``` - # Examples - :::{.example} - ## `lib.attrsets.attrValues` usage example + # Examples + :::{.example} + ## `lib.attrsets.attrValues` usage example - ```nix - attrValues {c = 3; a = 1; b = 2;} - => [1 2 3] - ``` + ```nix + attrValues {c = 3; a = 1; b = 2;} + => [1 2 3] + ``` - ::: + ::: */ attrValues = builtins.attrValues; /* - * - Given a set of attribute names, return the set of the corresponding - attributes from the given set. + * + Given a set of attribute names, return the set of the corresponding + attributes from the given set. - # Inputs + # Inputs - `names` + `names` - : A list of attribute names to get out of `set` + : A list of attribute names to get out of `set` - `attrs` + `attrs` - : The set to get the named attributes from + : The set to get the named attributes from - # Type + # Type - ``` - getAttrs :: [String] -> AttrSet -> AttrSet - ``` + ``` + getAttrs :: [String] -> AttrSet -> AttrSet + ``` - # Examples - :::{.example} - ## `lib.attrsets.getAttrs` usage example + # Examples + :::{.example} + ## `lib.attrsets.getAttrs` usage example - ```nix - getAttrs [ "a" "b" ] { a = 1; b = 2; c = 3; } - => { a = 1; b = 2; } - ``` + ```nix + getAttrs [ "a" "b" ] { a = 1; b = 2; c = 3; } + => { a = 1; b = 2; } + ``` - ::: + ::: */ getAttrs = names: attrs: genAttrs names (name: attrs.${name}); /* - * - Collect each attribute named `attr` from a list of attribute - sets. Sets that don't contain the named attribute are ignored. + * + Collect each attribute named `attr` from a list of attribute + sets. Sets that don't contain the named attribute are ignored. - # Inputs + # Inputs - `attr` + `attr` - : The attribute name to get out of the sets. + : The attribute name to get out of the sets. - `list` + `list` - : The list of attribute sets to go through + : The list of attribute sets to go through - # Type + # Type - ``` - catAttrs :: String -> [AttrSet] -> [Any] - ``` + ``` + catAttrs :: String -> [AttrSet] -> [Any] + ``` - # Examples - :::{.example} - ## `lib.attrsets.catAttrs` usage example + # Examples + :::{.example} + ## `lib.attrsets.catAttrs` usage example - ```nix - catAttrs "a" [{a = 1;} {b = 0;} {a = 2;}] - => [1 2] - ``` + ```nix + catAttrs "a" [{a = 1;} {b = 0;} {a = 2;}] + => [1 2] + ``` - ::: + ::: */ catAttrs = builtins.catAttrs; /* - * - Filter an attribute set by removing all attributes for which the - given predicate return false. + * + Filter an attribute set by removing all attributes for which the + given predicate return false. - # Inputs + # Inputs - `pred` + `pred` - : Predicate taking an attribute name and an attribute value, which returns `true` to include the attribute, or `false` to exclude the attribute. + : Predicate taking an attribute name and an attribute value, which returns `true` to include the attribute, or `false` to exclude the attribute. - `set` + `set` - : The attribute set to filter + : The attribute set to filter - # Type + # Type - ``` - filterAttrs :: (String -> Any -> Bool) -> AttrSet -> AttrSet - ``` + ``` + filterAttrs :: (String -> Any -> Bool) -> AttrSet -> AttrSet + ``` - # Examples - :::{.example} - ## `lib.attrsets.filterAttrs` usage example + # Examples + :::{.example} + ## `lib.attrsets.filterAttrs` usage example - ```nix - filterAttrs (n: v: n == "foo") { foo = 1; bar = 2; } - => { foo = 1; } - ``` + ```nix + filterAttrs (n: v: n == "foo") { foo = 1; bar = 2; } + => { foo = 1; } + ``` - ::: + ::: */ - filterAttrs = pred: set: - listToAttrs (concatMap (name: let - v = set.${name}; - in - if pred name v - then [(nameValuePair name v)] - else []) - (attrNames set)); + filterAttrs = + pred: set: + listToAttrs ( + concatMap ( + name: + let + v = set.${name}; + in + if pred name v then [ (nameValuePair name v) ] else [ ] + ) (attrNames set) + ); /* - * - Filter an attribute set recursively by removing all attributes for - which the given predicate return false. + * + Filter an attribute set recursively by removing all attributes for + which the given predicate return false. - # Inputs + # Inputs - `pred` + `pred` - : Predicate taking an attribute name and an attribute value, which returns `true` to include the attribute, or `false` to exclude the attribute. + : Predicate taking an attribute name and an attribute value, which returns `true` to include the attribute, or `false` to exclude the attribute. - `set` + `set` - : The attribute set to filter + : The attribute set to filter - # Type + # Type - ``` - filterAttrsRecursive :: (String -> Any -> Bool) -> AttrSet -> AttrSet - ``` + ``` + filterAttrsRecursive :: (String -> Any -> Bool) -> AttrSet -> AttrSet + ``` - # Examples - :::{.example} - ## `lib.attrsets.filterAttrsRecursive` usage example + # Examples + :::{.example} + ## `lib.attrsets.filterAttrsRecursive` usage example - ```nix - filterAttrsRecursive (n: v: v != null) { foo = { bar = null; }; } - => { foo = {}; } - ``` + ```nix + filterAttrsRecursive (n: v: v != null) { foo = { bar = null; }; } + => { foo = {}; } + ``` - ::: + ::: */ - filterAttrsRecursive = pred: set: - listToAttrs (concatMap (name: let - v = set.${name}; - in - if pred name v - then [ - (nameValuePair name - ( - if isAttrs v - then filterAttrsRecursive pred v - else v - )) - ] - else []) (attrNames set)); + filterAttrsRecursive = + pred: set: + listToAttrs ( + concatMap ( + name: + let + v = set.${name}; + in + if pred name v then + [ (nameValuePair name (if isAttrs v then filterAttrsRecursive pred v else v)) ] + else + [ ] + ) (attrNames set) + ); /* - * - Like [`lib.lists.foldl'`](#function-library-lib.lists.foldl-prime) but for attribute sets. - Iterates over every name-value pair in the given attribute set. - The result of the callback function is often called `acc` for accumulator. It is passed between callbacks from left to right and the final `acc` is the return value of `foldlAttrs`. - - Attention: + * + Like [`lib.lists.foldl'`](#function-library-lib.lists.foldl-prime) but for attribute sets. + Iterates over every name-value pair in the given attribute set. + The result of the callback function is often called `acc` for accumulator. It is passed between callbacks from left to right and the final `acc` is the return value of `foldlAttrs`. - There is a completely different function `lib.foldAttrs` - which has nothing to do with this function, despite the similar name. + Attention: - # Inputs + There is a completely different function `lib.foldAttrs` + which has nothing to do with this function, despite the similar name. - `f` + # Inputs - : 1\. Function argument + `f` - `init` + : 1\. Function argument - : 2\. Function argument + `init` - `set` + : 2\. Function argument - : 3\. Function argument + `set` - # Type + : 3\. Function argument - ``` - foldlAttrs :: ( a -> String -> b -> a ) -> a -> { ... :: b } -> a - ``` + # Type - # Examples - :::{.example} - ## `lib.attrsets.foldlAttrs` usage example + ``` + foldlAttrs :: ( a -> String -> b -> a ) -> a -> { ... :: b } -> a + ``` - ```nix - foldlAttrs - (acc: name: value: { - sum = acc.sum + value; - names = acc.names ++ [name]; - }) - { sum = 0; names = []; } - { - foo = 1; - bar = 10; - } - -> - { - sum = 11; - names = ["bar" "foo"]; - } + # Examples + :::{.example} + ## `lib.attrsets.foldlAttrs` usage example - foldlAttrs - (throw "function not needed") - 123 - {}; - -> - 123 - - foldlAttrs - (acc: _: _: acc) - 3 - { z = throw "value not needed"; a = throw "value not needed"; }; - -> - 3 - - The accumulator doesn't have to be an attrset. - It can be as simple as a number or string. - - foldlAttrs - (acc: _: v: acc * 10 + v) - 1 - { z = 1; a = 2; }; - -> - 121 - ``` - - ::: + ```nix + foldlAttrs + (acc: name: value: { + sum = acc.sum + value; + names = acc.names ++ [name]; + }) + { sum = 0; names = []; } + { + foo = 1; + bar = 10; + } + -> + { + sum = 11; + names = ["bar" "foo"]; + } + + foldlAttrs + (throw "function not needed") + 123 + {}; + -> + 123 + + foldlAttrs + (acc: _: _: acc) + 3 + { z = throw "value not needed"; a = throw "value not needed"; }; + -> + 3 + + The accumulator doesn't have to be an attrset. + It can be as simple as a number or string. + + foldlAttrs + (acc: _: v: acc * 10 + v) + 1 + { z = 1; a = 2; }; + -> + 121 + ``` + + ::: */ - foldlAttrs = f: init: set: + foldlAttrs = + f: init: set: foldl' (acc: name: f acc name set.${name}) init (attrNames set); /* - * - Apply fold functions to values grouped by key. + * + Apply fold functions to values grouped by key. - # Inputs + # Inputs - `op` + `op` - : A function, given a value and a collector combines the two. + : A function, given a value and a collector combines the two. - `nul` + `nul` - : The starting value. + : The starting value. - `list_of_attrs` + `list_of_attrs` - : A list of attribute sets to fold together by key. + : A list of attribute sets to fold together by key. - # Type + # Type - ``` - foldAttrs :: (Any -> Any -> Any) -> Any -> [AttrSets] -> Any - ``` + ``` + foldAttrs :: (Any -> Any -> Any) -> Any -> [AttrSets] -> Any + ``` - # Examples - :::{.example} - ## `lib.attrsets.foldAttrs` usage example + # Examples + :::{.example} + ## `lib.attrsets.foldAttrs` usage example - ```nix - foldAttrs (item: acc: [item] ++ acc) [] [{ a = 2; } { a = 3; }] - => { a = [ 2 3 ]; } - ``` + ```nix + foldAttrs (item: acc: [item] ++ acc) [] [{ a = 2; } { a = 3; }] + => { a = [ 2 3 ]; } + ``` - ::: + ::: */ - foldAttrs = op: nul: list_of_attrs: - foldr (n: a: - foldr (name: o: o // {${name} = op n.${name} (a.${name} or nul);}) a - (attrNames n)) {} - list_of_attrs; + foldAttrs = + op: nul: list_of_attrs: + foldr ( + n: a: 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` - from the set `attrs`. The recursion is stopped when the predicate is - verified. + * + Recursively collect sets that verify a given predicate named `pred` + from the set `attrs`. The recursion is stopped when the predicate is + verified. - # Inputs + # Inputs - `pred` + `pred` - : Given an attribute's value, determine if recursion should stop. + : Given an attribute's value, determine if recursion should stop. - `attrs` + `attrs` - : The attribute set to recursively collect. + : The attribute set to recursively collect. - # Type + # Type - ``` - collect :: (AttrSet -> Bool) -> AttrSet -> [x] - ``` + ``` + collect :: (AttrSet -> Bool) -> AttrSet -> [x] + ``` - # Examples - :::{.example} - ## `lib.attrsets.collect` usage example + # Examples + :::{.example} + ## `lib.attrsets.collect` usage example - ```nix - collect isList { a = { b = ["b"]; }; c = [1]; } - => [["b"] [1]] + ```nix + collect isList { a = { b = ["b"]; }; c = [1]; } + => [["b"] [1]] - collect (x: x ? outPath) - { a = { outPath = "a/"; }; b = { outPath = "b/"; }; } - => [{ outPath = "a/"; } { outPath = "b/"; }] - ``` + collect (x: x ? outPath) + { a = { outPath = "a/"; }; b = { outPath = "b/"; }; } + => [{ outPath = "a/"; } { outPath = "b/"; }] + ``` - ::: + ::: */ - collect = pred: attrs: - if pred attrs - then [attrs] - else if isAttrs attrs - then concatMap (collect pred) (attrValues attrs) - else []; + collect = + pred: attrs: + if pred attrs then + [ attrs ] + else if isAttrs attrs then + concatMap (collect pred) (attrValues attrs) + else + [ ]; /* - * - Return the cartesian product of attribute set value combinations. + * + Return the cartesian product of attribute set value combinations. - # Inputs + # Inputs - `attrsOfLists` + `attrsOfLists` - : Attribute set with attributes that are lists of values + : Attribute set with attributes that are lists of values - # Type + # Type - ``` - cartesianProduct :: AttrSet -> [AttrSet] - ``` + ``` + cartesianProduct :: AttrSet -> [AttrSet] + ``` - # Examples - :::{.example} - ## `lib.attrsets.cartesianProduct` usage example + # Examples + :::{.example} + ## `lib.attrsets.cartesianProduct` usage example - ```nix - cartesianProduct { a = [ 1 2 ]; b = [ 10 20 ]; } - => [ - { a = 1; b = 10; } - { a = 1; b = 20; } - { a = 2; b = 10; } - { a = 2; b = 20; } - ] - ``` + ```nix + cartesianProduct { a = [ 1 2 ]; b = [ 10 20 ]; } + => [ + { a = 1; b = 10; } + { a = 1; b = 20; } + { a = 2; b = 10; } + { a = 2; b = 20; } + ] + ``` - ::: + ::: */ - cartesianProduct = attrsOfLists: - foldl' (listOfAttrs: attrName: - concatMap (attrs: - map (listValue: attrs // {${attrName} = listValue;}) - attrsOfLists.${attrName}) - listOfAttrs) [{}] (attrNames attrsOfLists); + cartesianProduct = + attrsOfLists: + foldl' ( + listOfAttrs: attrName: + concatMap ( + attrs: map (listValue: attrs // { ${attrName} = listValue; }) attrsOfLists.${attrName} + ) listOfAttrs + ) [ { } ] (attrNames attrsOfLists); /* - * - Return the result of function f applied to the cartesian product of attribute set value combinations. - Equivalent to using cartesianProduct followed by map. + * + Return the result of function f applied to the cartesian product of attribute set value combinations. + Equivalent to using cartesianProduct followed by map. - # Inputs + # Inputs - `f` + `f` - : A function, given an attribute set, it returns a new value. + : A function, given an attribute set, it returns a new value. - `attrsOfLists` + `attrsOfLists` - : Attribute set with attributes that are lists of values + : Attribute set with attributes that are lists of values - # Type + # Type - ``` - mapCartesianProduct :: (AttrSet -> a) -> AttrSet -> [a] - ``` + ``` + mapCartesianProduct :: (AttrSet -> a) -> AttrSet -> [a] + ``` - # Examples - :::{.example} - ## `lib.attrsets.mapCartesianProduct` usage example + # Examples + :::{.example} + ## `lib.attrsets.mapCartesianProduct` usage example - ```nix - mapCartesianProduct ({a, b}: "${a}-${b}") { a = [ "1" "2" ]; b = [ "3" "4" ]; } - => [ "1-3" "1-4" "2-3" "2-4" ] - ``` + ```nix + mapCartesianProduct ({a, b}: "${a}-${b}") { a = [ "1" "2" ]; b = [ "3" "4" ]; } + => [ "1-3" "1-4" "2-3" "2-4" ] + ``` - ::: + ::: */ 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` - : Attribute name + : Attribute name - `value` + `value` - : Attribute value + : Attribute value - # Type + # Type - ``` - nameValuePair :: String -> Any -> { name :: String; value :: Any; } - ``` + ``` + nameValuePair :: String -> Any -> { name :: String; value :: Any; } + ``` - # Examples - :::{.example} - ## `lib.attrsets.nameValuePair` usage example + # Examples + :::{.example} + ## `lib.attrsets.nameValuePair` usage example - ```nix - nameValuePair "some" 6 - => { name = "some"; value = 6; } - ``` + ```nix + nameValuePair "some" 6 + => { name = "some"; value = 6; } + ``` - ::: + ::: */ - nameValuePair = name: value: {inherit name value;}; + nameValuePair = 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. - # Inputs + # Inputs - `f` + `f` - : A function that takes an attribute name and its value, and returns the new value for the attribute. + : A function that takes an attribute name and its value, and returns the new value for the attribute. - `attrset` + `attrset` - : The attribute set to iterate through. + : The attribute set to iterate through. - # Type + # Type - ``` - mapAttrs :: (String -> Any -> Any) -> AttrSet -> AttrSet - ``` + ``` + mapAttrs :: (String -> Any -> Any) -> AttrSet -> AttrSet + ``` - # Examples - :::{.example} - ## `lib.attrsets.mapAttrs` usage example + # Examples + :::{.example} + ## `lib.attrsets.mapAttrs` usage example - ```nix - mapAttrs (name: value: name + "-" + value) - { x = "foo"; y = "bar"; } - => { x = "x-foo"; y = "y-bar"; } - ``` + ```nix + mapAttrs (name: value: name + "-" + value) + { x = "foo"; y = "bar"; } + => { x = "x-foo"; y = "y-bar"; } + ``` - ::: + ::: */ mapAttrs = builtins.mapAttrs; /* - * - Like `mapAttrs`, but allows the name of each attribute to be - changed in addition to the value. The applied function should - return both the new name and value as a `nameValuePair`. + * + Like `mapAttrs`, but allows the name of each attribute to be + changed in addition to the value. The applied function should + return both the new name and value as a `nameValuePair`. - # Inputs + # Inputs - `f` + `f` - : A function, given an attribute's name and value, returns a new `nameValuePair`. + : A function, given an attribute's name and value, returns a new `nameValuePair`. - `set` + `set` - : Attribute set to map over. + : Attribute set to map over. - # Type + # Type - ``` - mapAttrs' :: (String -> Any -> { name :: String; value :: Any; }) -> AttrSet -> AttrSet - ``` + ``` + mapAttrs' :: (String -> Any -> { name :: String; value :: Any; }) -> AttrSet -> AttrSet + ``` - # Examples - :::{.example} - ## `lib.attrsets.mapAttrs'` usage example + # Examples + :::{.example} + ## `lib.attrsets.mapAttrs'` usage example - ```nix - mapAttrs' (name: value: nameValuePair ("foo_" + name) ("bar-" + value)) - { x = "a"; y = "b"; } - => { foo_x = "bar-a"; foo_y = "bar-b"; } - ``` + ```nix + mapAttrs' (name: value: nameValuePair ("foo_" + name) ("bar-" + value)) + { x = "a"; y = "b"; } + => { foo_x = "bar-a"; foo_y = "bar-b"; } + ``` - ::: + ::: */ - mapAttrs' = f: set: - listToAttrs (map (attr: f attr set.${attr}) (attrNames set)); + mapAttrs' = f: set: listToAttrs (map (attr: f attr set.${attr}) (attrNames set)); /* - * - Call a function for each attribute in the given set and return - the result in a list. + * + Call a function for each attribute in the given set and return + the result in a list. - # Inputs + # Inputs - `f` + `f` - : A function, given an attribute's name and value, returns a new value. + : A function, given an attribute's name and value, returns a new value. - `attrs` + `attrs` - : Attribute set to map over. + : Attribute set to map over. - # Type + # Type - ``` - mapAttrsToList :: (String -> a -> b) -> AttrSet -> [b] - ``` + ``` + mapAttrsToList :: (String -> a -> b) -> AttrSet -> [b] + ``` - # Examples - :::{.example} - ## `lib.attrsets.mapAttrsToList` usage example + # Examples + :::{.example} + ## `lib.attrsets.mapAttrsToList` usage example - ```nix - mapAttrsToList (name: value: name + value) - { x = "a"; y = "b"; } - => [ "xa" "yb" ] - ``` + ```nix + mapAttrsToList (name: value: name + value) + { x = "a"; y = "b"; } + => [ "xa" "yb" ] + ``` - ::: + ::: */ mapAttrsToList = 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). - Each element of the resulting list is an attribute set with these attributes: - - `name` (string): The name of the attribute - - `value` (any): The value of the attribute + * + 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). + Each element of the resulting list is an attribute set with these attributes: + - `name` (string): The name of the attribute + - `value` (any): The value of the attribute - The following is always true: - ```nix - builtins.listToAttrs (attrsToList attrs) == attrs - ``` + The following is always true: + ```nix + builtins.listToAttrs (attrsToList attrs) == attrs + ``` - :::{.warning} - The opposite is not always true. In general expect that - ```nix - attrsToList (builtins.listToAttrs list) != list - ``` + :::{.warning} + The opposite is not always true. In general expect that + ```nix + attrsToList (builtins.listToAttrs list) != list + ``` - This is because the `listToAttrs` removes duplicate names and doesn't preserve the order of the list. - ::: + This is because the `listToAttrs` removes duplicate names and doesn't preserve the order of the list. + ::: - # Inputs + # Inputs - `set` + `set` - : The attribute set to deconstruct. + : The attribute set to deconstruct. - # Type + # Type - ``` - attrsToList :: AttrSet -> [ { name :: String; value :: Any; } ] - ``` + ``` + attrsToList :: AttrSet -> [ { name :: String; value :: Any; } ] + ``` - # Examples - :::{.example} - ## `lib.attrsets.attrsToList` usage example + # Examples + :::{.example} + ## `lib.attrsets.attrsToList` usage example - ```nix - attrsToList { foo = 1; bar = "asdf"; } - => [ { name = "bar"; value = "asdf"; } { name = "foo"; value = 1; } ] - ``` + ```nix + attrsToList { foo = 1; bar = "asdf"; } + => [ { name = "bar"; value = "asdf"; } { name = "foo"; value = 1; } ] + ``` - ::: + ::: */ attrsToList = mapAttrsToList nameValuePair; /* - * - 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. - Also, the first argument of the mapping function is a *list* of the attribute names that form the path to the leaf attribute. - - For a function that gives you control over what counts as a leaf, see `mapAttrsRecursiveCond`. - - :::{#map-attrs-recursive-example .example} - # Map over leaf attributes - - ```nix - mapAttrsRecursive (path: value: concatStringsSep "-" (path ++ [value])) - { n = { a = "A"; m = { b = "B"; c = "C"; }; }; d = "D"; } - ``` - evaluates to - ```nix - { n = { a = "n-a-A"; m = { b = "n-m-b-B"; c = "n-m-c-C"; }; }; d = "d-D"; } - ``` - ::: - - # Type - ``` - mapAttrsRecursive :: ([String] -> a -> b) -> AttrSet -> AttrSet - ``` + * + 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. + Also, the first argument of the mapping function is a *list* of the attribute names that form the path to the leaf attribute. + + For a function that gives you control over what counts as a leaf, see `mapAttrsRecursiveCond`. + + :::{#map-attrs-recursive-example .example} + # Map over leaf attributes + + ```nix + mapAttrsRecursive (path: value: concatStringsSep "-" (path ++ [value])) + { n = { a = "A"; m = { b = "B"; c = "C"; }; }; d = "D"; } + ``` + evaluates to + ```nix + { n = { a = "n-a-A"; m = { b = "n-m-b-B"; c = "n-m-c-C"; }; }; d = "d-D"; } + ``` + ::: + + # Type + ``` + mapAttrsRecursive :: ([String] -> a -> b) -> AttrSet -> AttrSet + ``` */ mapAttrsRecursive = 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. - If the predicate returns false, `mapAttrsRecursiveCond` does not recurse, but instead applies the mapping function. - If the predicate returns true, it does recurse, and does not apply the mapping function. - - :::{#map-attrs-recursive-cond-example .example} - # Map over an leaf attributes defined by a condition - - Map derivations to their `name` attribute. - Derivatons are identified as attribute sets that contain `{ type = "derivation"; }`. - ```nix - mapAttrsRecursiveCond - (as: !(as ? "type" && as.type == "derivation")) - (x: x.name) - attrs - ``` - ::: - - # Type - ``` - mapAttrsRecursiveCond :: (AttrSet -> Bool) -> ([String] -> a -> b) -> AttrSet -> AttrSet - ``` + * + Like `mapAttrsRecursive`, but it takes an additional predicate that tells it whether to recurse into an attribute set. + If the predicate returns false, `mapAttrsRecursiveCond` does not recurse, but instead applies the mapping function. + If the predicate returns true, it does recurse, and does not apply the mapping function. + + :::{#map-attrs-recursive-cond-example .example} + # Map over an leaf attributes defined by a condition + + Map derivations to their `name` attribute. + Derivatons are identified as attribute sets that contain `{ type = "derivation"; }`. + ```nix + mapAttrsRecursiveCond + (as: !(as ? "type" && as.type == "derivation")) + (x: x.name) + attrs + ``` + ::: + + # Type + ``` + mapAttrsRecursiveCond :: (AttrSet -> Bool) -> ([String] -> a -> b) -> AttrSet -> AttrSet + ``` */ - mapAttrsRecursiveCond = cond: f: set: let - recurse = path: - mapAttrs (name: value: - if isAttrs value && cond value - then recurse (path ++ [name]) value - else f (path ++ [name]) value); - in - recurse [] set; + mapAttrsRecursiveCond = + cond: f: set: + let + recurse = + path: + mapAttrs ( + name: value: + if isAttrs value && cond value then recurse (path ++ [ name ]) value else f (path ++ [ name ]) value + ); + in + recurse [ ] set; /* - * - Generate an attribute set by mapping a function over a list of - attribute names. + * + Generate an attribute set by mapping a function over a list of + attribute names. - # Inputs + # Inputs - `names` + `names` - : Names of values in the resulting attribute set. + : Names of values in the resulting attribute set. - `f` + `f` - : A function, given the name of the attribute, returns the attribute's value. + : A function, given the name of the attribute, returns the attribute's value. - # Type + # Type - ``` - genAttrs :: [ String ] -> (String -> Any) -> AttrSet - ``` + ``` + genAttrs :: [ String ] -> (String -> Any) -> AttrSet + ``` - # Examples - :::{.example} - ## `lib.attrsets.genAttrs` usage example + # Examples + :::{.example} + ## `lib.attrsets.genAttrs` usage example - ```nix - genAttrs [ "foo" "bar" ] (name: "x_" + name) - => { foo = "x_foo"; bar = "x_bar"; } - ``` + ```nix + genAttrs [ "foo" "bar" ] (name: "x_" + name) + => { foo = "x_foo"; bar = "x_bar"; } + ``` - ::: + ::: */ genAttrs = names: f: listToAttrs (map (n: nameValuePair n (f n)) names); /* - * - Check whether the argument is a derivation. Any set with - `{ type = "derivation"; }` counts as a derivation. + * + Check whether the argument is a derivation. Any set with + `{ type = "derivation"; }` counts as a derivation. - # Inputs + # Inputs - `value` + `value` - : Value to check. + : Value to check. - # Type + # Type - ``` - isDerivation :: Any -> Bool - ``` + ``` + isDerivation :: Any -> Bool + ``` - # Examples - :::{.example} - ## `lib.attrsets.isDerivation` usage example + # Examples + :::{.example} + ## `lib.attrsets.isDerivation` usage example - ```nix - nixpkgs = import {} - isDerivation nixpkgs.ruby - => true - isDerivation "foobar" - => false - ``` + ```nix + nixpkgs = import {} + isDerivation nixpkgs.ruby + => true + isDerivation "foobar" + => false + ``` - ::: + ::: */ isDerivation = 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` - : A store path to convert to a derivation. + : A store path to convert to a derivation. - # Type + # Type - ``` - toDerivation :: Path -> Derivation - ``` + ``` + toDerivation :: Path -> Derivation + ``` */ - toDerivation = path: let - path' = builtins.storePath path; - res = { - type = "derivation"; - name = - sanitizeDerivationName - (builtins.substring 33 (-1) (baseNameOf path')); - outPath = path'; - outputs = ["out"]; - out = res; - outputName = "out"; - }; - in + toDerivation = + path: + let + path' = builtins.storePath path; + res = { + type = "derivation"; + name = sanitizeDerivationName (builtins.substring 33 (-1) (baseNameOf path')); + outPath = path'; + outputs = [ "out" ]; + out = res; + outputName = "out"; + }; + in res; /* - * - If `cond` is true, return the attribute set `as`, - otherwise an empty attribute set. + * + If `cond` is true, return the attribute set `as`, + otherwise an empty attribute set. - # Inputs + # Inputs - `cond` + `cond` - : Condition under which the `as` attribute set is returned. + : Condition under which the `as` attribute set is returned. - `as` + `as` - : The attribute set to return if `cond` is `true`. + : The attribute set to return if `cond` is `true`. - # Type + # Type - ``` - optionalAttrs :: Bool -> AttrSet -> AttrSet - ``` + ``` + optionalAttrs :: Bool -> AttrSet -> AttrSet + ``` - # Examples - :::{.example} - ## `lib.attrsets.optionalAttrs` usage example + # Examples + :::{.example} + ## `lib.attrsets.optionalAttrs` usage example - ```nix - optionalAttrs (true) { my = "set"; } - => { my = "set"; } - optionalAttrs (false) { my = "set"; } - => { } - ``` + ```nix + optionalAttrs (true) { my = "set"; } + => { my = "set"; } + optionalAttrs (false) { my = "set"; } + => { } + ``` - ::: + ::: */ - optionalAttrs = cond: as: - if cond - then as - else {}; + optionalAttrs = cond: as: if cond then as else { }; /* - * - Merge sets of attributes and use the function `f` to merge attributes - values. + * + Merge sets of attributes and use the function `f` to merge attributes + values. - # Inputs + # Inputs - `names` + `names` - : List of attribute names to zip. + : List of attribute names to zip. - `f` + `f` - : A function, accepts an attribute name, all the values, and returns a combined value. + : A function, accepts an attribute name, all the values, and returns a combined value. - `sets` + `sets` - : List of values from the list of attribute sets. + : List of values from the list of attribute sets. - # Type + # Type - ``` - zipAttrsWithNames :: [ String ] -> (String -> [ Any ] -> Any) -> [ AttrSet ] -> AttrSet - ``` + ``` + zipAttrsWithNames :: [ String ] -> (String -> [ Any ] -> Any) -> [ AttrSet ] -> AttrSet + ``` - # Examples - :::{.example} - ## `lib.attrsets.zipAttrsWithNames` usage example + # Examples + :::{.example} + ## `lib.attrsets.zipAttrsWithNames` usage example - ```nix - zipAttrsWithNames ["a"] (name: vs: vs) [{a = "x";} {a = "y"; b = "z";}] - => { a = ["x" "y"]; } - ``` + ```nix + zipAttrsWithNames ["a"] (name: vs: vs) [{a = "x";} {a = "y"; b = "z";}] + => { a = ["x" "y"]; } + ``` - ::: + ::: */ - zipAttrsWithNames = names: f: sets: - listToAttrs (map (name: { + zipAttrsWithNames = + names: f: sets: + listToAttrs ( + map (name: { inherit name; value = f name (catAttrs name sets); - }) - names); + }) names + ); /* - * - Merge sets of attributes and use the function f to merge attribute values. - Like `lib.attrsets.zipAttrsWithNames` with all key names are passed for `names`. + * + Merge sets of attributes and use the function f to merge attribute values. + Like `lib.attrsets.zipAttrsWithNames` with all key names are passed for `names`. - Implementation note: Common names appear multiple times in the list of - names, hopefully this does not affect the system because the maximal - laziness avoid computing twice the same expression and `listToAttrs` does - not care about duplicated attribute names. + Implementation note: Common names appear multiple times in the list of + names, hopefully this does not affect the system because the maximal + laziness avoid computing twice the same expression and `listToAttrs` does + not care about duplicated attribute names. - # Type + # Type - ``` - zipAttrsWith :: (String -> [ Any ] -> Any) -> [ AttrSet ] -> AttrSet - ``` + ``` + zipAttrsWith :: (String -> [ Any ] -> Any) -> [ AttrSet ] -> AttrSet + ``` - # Examples - :::{.example} - ## `lib.attrsets.zipAttrsWith` usage example + # Examples + :::{.example} + ## `lib.attrsets.zipAttrsWith` usage example - ```nix - zipAttrsWith (name: values: values) [{a = "x";} {a = "y"; b = "z";}] - => { a = ["x" "y"]; b = ["z"]; } - ``` + ```nix + zipAttrsWith (name: values: values) [{a = "x";} {a = "y"; b = "z";}] + => { a = ["x" "y"]; b = ["z"]; } + ``` - ::: + ::: */ 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. - Like `lib.attrsets.zipAttrsWith` with `(name: values: values)` as the function. + Like `lib.attrsets.zipAttrsWith` with `(name: values: values)` as the function. - # Type + # Type - ``` - zipAttrs :: [ AttrSet ] -> AttrSet - ``` + ``` + zipAttrs :: [ AttrSet ] -> AttrSet + ``` - # Examples - :::{.example} - ## `lib.attrsets.zipAttrs` usage example + # Examples + :::{.example} + ## `lib.attrsets.zipAttrs` usage example - ```nix - zipAttrs [{a = "x";} {a = "y"; b = "z";}] - => { a = ["x" "y"]; b = ["z"]; } - ``` + ```nix + zipAttrs [{a = "x";} {a = "y"; b = "z";}] + => { a = ["x" "y"]; b = ["z"]; } + ``` - ::: + ::: */ zipAttrs = zipAttrsWith (name: values: values); /* - * - Merge a list of attribute sets together using the `//` operator. - In case of duplicate attributes, values from later list elements take precedence over earlier ones. - 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). + * + Merge a list of attribute sets together using the `//` operator. + In case of duplicate attributes, values from later list elements take precedence over earlier ones. + 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). - # Inputs + # Inputs - `list` + `list` - : 1\. Function argument + : 1\. Function argument - # Type + # Type - ``` - mergeAttrsList :: [ Attrs ] -> Attrs - ``` + ``` + mergeAttrsList :: [ Attrs ] -> Attrs + ``` - # Examples - :::{.example} - ## `lib.attrsets.mergeAttrsList` usage example + # Examples + :::{.example} + ## `lib.attrsets.mergeAttrsList` usage example - ```nix - mergeAttrsList [ { a = 0; b = 1; } { c = 2; d = 3; } ] - => { a = 0; b = 1; c = 2; d = 3; } - mergeAttrsList [ { a = 0; } { a = 1; } ] - => { a = 1; } - ``` + ```nix + mergeAttrsList [ { a = 0; b = 1; } { c = 2; d = 3; } ] + => { a = 0; b = 1; c = 2; d = 3; } + mergeAttrsList [ { a = 0; } { a = 1; } ] + => { a = 1; } + ``` - ::: + ::: */ - mergeAttrsList = list: let - # `binaryMerge start end` merges the elements at indices `index` of `list` such that `start <= index < end` - # Type: Int -> Int -> Attrs - binaryMerge = start: end: - # assert start < end; # Invariant - 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 - # The invariant is satisfied because each half will have at least 1 element - binaryMerge start (start + (end - start) / 2) - // binaryMerge (start + (end - start) / 2) end - else - # Otherwise there will be exactly 1 element due to the invariant, in which case we just return it directly - elemAt list start; - in - if list == [] - then + mergeAttrsList = + list: + let + # `binaryMerge start end` merges the elements at indices `index` of `list` such that `start <= index < end` + # Type: Int -> Int -> Attrs + binaryMerge = + start: end: + # assert start < end; # Invariant + 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 + # The invariant is satisfied because each half will have at least 1 element + binaryMerge start (start + (end - start) / 2) // binaryMerge (start + (end - start) / 2) end + else + # Otherwise there will be exactly 1 element due to the invariant, in which case we just return it directly + elemAt list start; + in + if list == [ ] then # Calling binaryMerge as below would not satisfy its invariant - {} - else binaryMerge 0 (length list); + { } + else + binaryMerge 0 (length list); /* - * - Does the same as the update operator '//' except that attributes are - merged until the given predicate is verified. The predicate should - accept 3 arguments which are the path to reach the attribute, a part of - the first attribute set and a part of the second attribute set. When - the predicate is satisfied, the value of the first attribute set is - replaced by the value of the second attribute set. + * + Does the same as the update operator '//' except that attributes are + merged until the given predicate is verified. The predicate should + accept 3 arguments which are the path to reach the attribute, a part of + the first attribute set and a part of the second attribute set. When + the predicate is satisfied, the value of the first attribute set is + replaced by the value of the second attribute set. - # Inputs + # Inputs - `pred` + `pred` - : Predicate, taking the path to the current attribute as a list of strings for attribute names, and the two values at that path from the original arguments. + : Predicate, taking the path to the current attribute as a list of strings for attribute names, and the two values at that path from the original arguments. - `lhs` + `lhs` - : Left attribute set of the merge. + : Left attribute set of the merge. - `rhs` + `rhs` - : Right attribute set of the merge. + : Right attribute set of the merge. - # Type + # Type - ``` - recursiveUpdateUntil :: ( [ String ] -> AttrSet -> AttrSet -> Bool ) -> AttrSet -> AttrSet -> AttrSet - ``` + ``` + recursiveUpdateUntil :: ( [ String ] -> AttrSet -> AttrSet -> Bool ) -> AttrSet -> AttrSet -> AttrSet + ``` - # Examples - :::{.example} - ## `lib.attrsets.recursiveUpdateUntil` usage example + # Examples + :::{.example} + ## `lib.attrsets.recursiveUpdateUntil` usage example - ```nix - recursiveUpdateUntil (path: l: r: path == ["foo"]) { - # first attribute set - foo.bar = 1; - foo.baz = 2; - bar = 3; - } { - #second attribute set - foo.bar = 1; - foo.quz = 2; - baz = 4; - } + ```nix + recursiveUpdateUntil (path: l: r: path == ["foo"]) { + # first attribute set + foo.bar = 1; + foo.baz = 2; + bar = 3; + } { + #second attribute set + foo.bar = 1; + foo.quz = 2; + baz = 4; + } - => { - foo.bar = 1; # 'foo.*' from the second set - foo.quz = 2; # - bar = 3; # 'bar' from the first set - baz = 4; # 'baz' from the second set - } - ``` + => { + foo.bar = 1; # 'foo.*' from the second set + foo.quz = 2; # + bar = 3; # 'bar' from the first set + baz = 4; # 'baz' from the second set + } + ``` - ::: + ::: */ - recursiveUpdateUntil = pred: lhs: rhs: let - f = attrPath: - zipAttrsWith (n: values: let - here = attrPath ++ [n]; - in - if - length values - == 1 - || pred here (elemAt values 1) (head values) - then head values - else f here values); - in - f [] [rhs lhs]; + recursiveUpdateUntil = + pred: lhs: rhs: + let + f = + attrPath: + zipAttrsWith ( + n: values: + let + here = attrPath ++ [ n ]; + in + if length values == 1 || pred here (elemAt values 1) (head values) then + head values + else + f here values + ); + in + f [ ] [ + rhs + lhs + ]; /* - * - A recursive variant of the update operator ‘//’. The recursion - stops when one of the attribute values is not an attribute set, - in which case the right hand side value takes precedence over the - left hand side value. + * + A recursive variant of the update operator ‘//’. The recursion + stops when one of the attribute values is not an attribute set, + in which case the right hand side value takes precedence over the + left hand side value. - # Inputs + # Inputs - `lhs` + `lhs` - : Left attribute set of the merge. + : Left attribute set of the merge. - `rhs` + `rhs` - : Right attribute set of the merge. + : Right attribute set of the merge. - # Type + # Type - ``` - recursiveUpdate :: AttrSet -> AttrSet -> AttrSet - ``` + ``` + recursiveUpdate :: AttrSet -> AttrSet -> AttrSet + ``` - # Examples - :::{.example} - ## `lib.attrsets.recursiveUpdate` usage example + # Examples + :::{.example} + ## `lib.attrsets.recursiveUpdate` usage example - ```nix - recursiveUpdate { - boot.loader.grub.enable = true; - boot.loader.grub.device = "/dev/hda"; - } { - boot.loader.grub.device = ""; - } + ```nix + recursiveUpdate { + boot.loader.grub.enable = true; + boot.loader.grub.device = "/dev/hda"; + } { + boot.loader.grub.device = ""; + } - returns: { - boot.loader.grub.enable = true; - boot.loader.grub.device = ""; - } - ``` + returns: { + boot.loader.grub.enable = true; + boot.loader.grub.device = ""; + } + ``` - ::: + ::: */ - recursiveUpdate = lhs: rhs: - recursiveUpdateUntil (path: lhs: rhs: !(isAttrs lhs && isAttrs rhs)) lhs - rhs; + recursiveUpdate = + lhs: rhs: + recursiveUpdateUntil ( + path: lhs: rhs: + !(isAttrs lhs && isAttrs rhs) + ) lhs rhs; /* - * - Recurse into every attribute set of the first argument and check that: - - 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. + * + Recurse into every attribute set of the first argument and check that: + - 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. - # Inputs + # Inputs - `pattern` + `pattern` - : Attribute set structure to match + : Attribute set structure to match - `attrs` + `attrs` - : Attribute set to check + : Attribute set to check - # Type + # Type - ``` - matchAttrs :: AttrSet -> AttrSet -> Bool - ``` + ``` + matchAttrs :: AttrSet -> AttrSet -> Bool + ``` - # Examples - :::{.example} - ## `lib.attrsets.matchAttrs` usage example + # Examples + :::{.example} + ## `lib.attrsets.matchAttrs` usage example - ```nix - matchAttrs { cpu = {}; } { cpu = { bits = 64; }; } - => true - ``` + ```nix + matchAttrs { cpu = {}; } { cpu = { bits = 64; }; } + => true + ``` - ::: + ::: */ - matchAttrs = pattern: attrs: + matchAttrs = + pattern: attrs: assert isAttrs pattern; - all ( # Compare equality between `pattern` & `attrs`. + all + # Compare equality between `pattern` & `attrs`. + ( attr: # Missing attr, not equal. - attrs - ? ${attr} - && (let + attrs ? ${attr} + && ( + let lhs = pattern.${attr}; rhs = attrs.${attr}; - # If attrset check recursively in - if isAttrs lhs - then isAttrs rhs && matchAttrs lhs rhs - else lhs == rhs) + # If attrset check recursively + if isAttrs lhs then isAttrs rhs && matchAttrs lhs rhs else lhs == rhs + ) ) (attrNames pattern); /* - * - Override only the attributes that are already present in the old set - useful for deep-overriding. + * + Override only the attributes that are already present in the old set + useful for deep-overriding. - # Inputs + # Inputs - `old` + `old` - : Original attribute set + : Original attribute set - `new` + `new` - : Attribute set with attributes to override in `old`. + : Attribute set with attributes to override in `old`. - # Type + # Type - ``` - overrideExisting :: AttrSet -> AttrSet -> AttrSet - ``` + ``` + overrideExisting :: AttrSet -> AttrSet -> AttrSet + ``` - # Examples - :::{.example} - ## `lib.attrsets.overrideExisting` usage example + # Examples + :::{.example} + ## `lib.attrsets.overrideExisting` usage example - ```nix - overrideExisting {} { a = 1; } - => {} - overrideExisting { b = 2; } { a = 1; } - => { b = 2; } - overrideExisting { a = 3; b = 2; } { a = 1; } - => { a = 1; b = 2; } - ``` + ```nix + overrideExisting {} { a = 1; } + => {} + overrideExisting { b = 2; } { a = 1; } + => { b = 2; } + overrideExisting { a = 3; b = 2; } { a = 1; } + => { a = 1; b = 2; } + ``` - ::: + ::: */ overrideExisting = old: new: mapAttrs (name: value: new.${name} or value) old; /* - * - Turns a list of strings into a human-readable description of those - strings represented as an attribute path. The result of this function is - not intended to be machine-readable. - Create a new attribute set with `value` set at the nested attribute location specified in `attrPath`. + * + Turns a list of strings into a human-readable description of those + strings represented as an attribute path. The result of this function is + not intended to be machine-readable. + Create a new attribute set with `value` set at the nested attribute location specified in `attrPath`. - # Inputs + # Inputs - `path` + `path` - : Attribute path to render to a string + : Attribute path to render to a string - # Type + # Type - ``` - showAttrPath :: [String] -> String - ``` + ``` + showAttrPath :: [String] -> String + ``` - # Examples - :::{.example} - ## `lib.attrsets.showAttrPath` usage example + # Examples + :::{.example} + ## `lib.attrsets.showAttrPath` usage example - ```nix - showAttrPath [ "foo" "10" "bar" ] - => "foo.\"10\".bar" - showAttrPath [] - => "" - ``` + ```nix + showAttrPath [ "foo" "10" "bar" ] + => "foo.\"10\".bar" + showAttrPath [] + => "" + ``` - ::: + ::: */ - showAttrPath = path: - if path == [] - then "" - else concatMapStringsSep "." escapeNixIdentifier path; + showAttrPath = + path: + if path == [ ] then "" else concatMapStringsSep "." escapeNixIdentifier path; /* - * - Get a package output. - If no output is found, fallback to `.out` and then to the default. + * + Get a package output. + If no output is found, fallback to `.out` and then to the default. - # Inputs + # Inputs - `output` + `output` - : 1\. Function argument + : 1\. Function argument - `pkg` + `pkg` - : 2\. Function argument + : 2\. Function argument - # Type + # Type - ``` - getOutput :: String -> Derivation -> String - ``` + ``` + getOutput :: String -> Derivation -> String + ``` - # Examples - :::{.example} - ## `lib.attrsets.getOutput` usage example + # Examples + :::{.example} + ## `lib.attrsets.getOutput` usage example - ```nix - getOutput "dev" pkgs.openssl - => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r-dev" - ``` + ```nix + getOutput "dev" pkgs.openssl + => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r-dev" + ``` - ::: + ::: */ - getOutput = output: pkg: - if !pkg ? outputSpecified || !pkg.outputSpecified - then pkg.${output} or pkg.out or pkg - else pkg; + getOutput = + output: pkg: + if !pkg ? outputSpecified || !pkg.outputSpecified then pkg.${output} or pkg.out or pkg else pkg; /* - * - Get a package's `bin` output. - If the output does not exist, fallback to `.out` and then to the default. + * + Get a package's `bin` output. + If the output does not exist, fallback to `.out` and then to the default. - # Inputs + # Inputs - `pkg` + `pkg` - : The package whose `bin` output will be retrieved. + : The package whose `bin` output will be retrieved. - # Type + # Type - ``` - getBin :: Derivation -> String - ``` + ``` + getBin :: Derivation -> String + ``` - # Examples - :::{.example} - ## `lib.attrsets.getBin` usage example + # Examples + :::{.example} + ## `lib.attrsets.getBin` usage example - ```nix - getBin pkgs.openssl - => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r" - ``` + ```nix + getBin pkgs.openssl + => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r" + ``` - ::: + ::: */ getBin = getOutput "bin"; /* - * - Get a package's `lib` output. - If the output does not exist, fallback to `.out` and then to the default. + * + Get a package's `lib` output. + If the output does not exist, fallback to `.out` and then to the default. - # Inputs + # Inputs - `pkg` + `pkg` - : The package whose `lib` output will be retrieved. + : The package whose `lib` output will be retrieved. - # Type + # Type - ``` - getLib :: Derivation -> String - ``` + ``` + getLib :: Derivation -> String + ``` - # Examples - :::{.example} - ## `lib.attrsets.getLib` usage example + # Examples + :::{.example} + ## `lib.attrsets.getLib` usage example - ```nix - getLib pkgs.openssl - => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r-lib" - ``` + ```nix + getLib pkgs.openssl + => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r-lib" + ``` - ::: + ::: */ getLib = getOutput "lib"; /* - * - Get a package's `dev` output. - If the output does not exist, fallback to `.out` and then to the default. + * + Get a package's `dev` output. + If the output does not exist, fallback to `.out` and then to the default. - # Inputs + # Inputs - `pkg` + `pkg` - : The package whose `dev` output will be retrieved. + : The package whose `dev` output will be retrieved. - # Type + # Type - ``` - getDev :: Derivation -> String - ``` + ``` + getDev :: Derivation -> String + ``` - # Examples - :::{.example} - ## `lib.attrsets.getDev` usage example + # Examples + :::{.example} + ## `lib.attrsets.getDev` usage example - ```nix - getDev pkgs.openssl - => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r-dev" - ``` + ```nix + getDev pkgs.openssl + => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r-dev" + ``` - ::: + ::: */ getDev = getOutput "dev"; /* - * - Get a package's `man` output. - If the output does not exist, fallback to `.out` and then to the default. + * + Get a package's `man` output. + If the output does not exist, fallback to `.out` and then to the default. - # Inputs + # Inputs - `pkg` + `pkg` - : The package whose `man` output will be retrieved. + : The package whose `man` output will be retrieved. - # Type + # Type - ``` - getMan :: Derivation -> String - ``` + ``` + getMan :: Derivation -> String + ``` - # Examples - :::{.example} - ## `lib.attrsets.getMan` usage example + # Examples + :::{.example} + ## `lib.attrsets.getMan` usage example - ```nix - getMan pkgs.openssl - => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r-man" - ``` + ```nix + getMan pkgs.openssl + => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r-man" + ``` - ::: + ::: */ getMan = getOutput "man"; /* - * - Pick the outputs of packages to place in `buildInputs` + * + Pick the outputs of packages to place in `buildInputs` - # Inputs + # Inputs - `pkgs` + `pkgs` - : List of packages. + : List of packages. - # Type + # Type - ``` - chooseDevOutputs :: [Derivation] -> [String] - ``` + ``` + chooseDevOutputs :: [Derivation] -> [String] + ``` */ chooseDevOutputs = builtins.map getDev; /* - * - Make various Nix tools consider the contents of the resulting - attribute set when looking for what to build, find, etc. + * + Make various Nix tools consider the contents of the resulting + attribute set when looking for what to build, find, etc. - This function only affects a single attribute set; it does not - apply itself recursively for nested attribute sets. + This function only affects a single attribute set; it does not + apply itself recursively for nested attribute sets. - # Inputs + # Inputs - `attrs` + `attrs` - : An attribute set to scan for derivations. + : An attribute set to scan for derivations. - # Type + # Type - ``` - recurseIntoAttrs :: AttrSet -> AttrSet - ``` + ``` + recurseIntoAttrs :: AttrSet -> AttrSet + ``` - # Examples - :::{.example} - ## `lib.attrsets.recurseIntoAttrs` usage example + # Examples + :::{.example} + ## `lib.attrsets.recurseIntoAttrs` usage example - ```nix - { pkgs ? import {} }: - { - myTools = pkgs.lib.recurseIntoAttrs { - inherit (pkgs) hello figlet; - }; - } - ``` + ```nix + { pkgs ? import {} }: + { + myTools = pkgs.lib.recurseIntoAttrs { + inherit (pkgs) hello figlet; + }; + } + ``` - ::: + ::: */ - recurseIntoAttrs = attrs: attrs // {recurseForDerivations = true;}; + recurseIntoAttrs = attrs: attrs // { recurseForDerivations = true; }; /* - * - Undo the effect of recurseIntoAttrs. + * + Undo the effect of recurseIntoAttrs. - # Inputs + # Inputs - `attrs` + `attrs` - : An attribute set to not scan for derivations. + : An attribute set to not scan for derivations. - # Type + # Type - ``` - dontRecurseIntoAttrs :: AttrSet -> AttrSet - ``` + ``` + dontRecurseIntoAttrs :: AttrSet -> AttrSet + ``` */ - dontRecurseIntoAttrs = attrs: attrs // {recurseForDerivations = false;}; + dontRecurseIntoAttrs = attrs: attrs // { recurseForDerivations = false; }; /* - * - `unionOfDisjoint x y` is equal to `x // y // z` where the - attrnames in `z` are the intersection of the attrnames in `x` and - `y`, and all values `assert` with an error message. This - operator is commutative, unlike (//). + * + `unionOfDisjoint x y` is equal to `x // y // z` where the + attrnames in `z` are the intersection of the attrnames in `x` and + `y`, and all values `assert` with an error message. This + operator is commutative, unlike (//). - # Inputs + # Inputs - `x` + `x` - : 1\. Function argument + : 1\. Function argument - `y` + `y` - : 2\. Function argument + : 2\. Function argument - # Type + # Type - ``` - unionOfDisjoint :: AttrSet -> AttrSet -> AttrSet - ``` + ``` + unionOfDisjoint :: AttrSet -> AttrSet -> AttrSet + ``` */ - unionOfDisjoint = x: y: let - intersection = builtins.intersectAttrs x y; - collisions = lib.concatStringsSep " " (builtins.attrNames intersection); - mask = builtins.mapAttrs (name: value: - builtins.throw - "unionOfDisjoint: collision on ${name}; complete list: ${collisions}") - intersection; - in + unionOfDisjoint = + x: y: + let + intersection = builtins.intersectAttrs x y; + collisions = lib.concatStringsSep " " (builtins.attrNames intersection); + mask = builtins.mapAttrs ( + name: value: builtins.throw "unionOfDisjoint: collision on ${name}; complete list: ${collisions}" + ) intersection; + in (x // y) // mask; # DEPRECATED - zipWithNames = - warn "lib.zipWithNames is a deprecated alias of lib.zipAttrsWithNames." - zipAttrsWithNames; + zipWithNames = warn "lib.zipWithNames is a deprecated alias of lib.zipAttrsWithNames." zipAttrsWithNames; # DEPRECATED zip = warn "lib.zip is a deprecated alias of lib.zipAttrsWith." zipAttrsWith; # DEPRECATED - cartesianProductOfSets = - warnIf (isInOldestRelease 2405) - "lib.cartesianProductOfSets is a deprecated alias of lib.cartesianProduct." - cartesianProduct; + cartesianProductOfSets = warnIf (isInOldestRelease + 2405 + ) "lib.cartesianProductOfSets is a deprecated alias of lib.cartesianProduct." cartesianProduct; } diff --git a/nix/stdlib/cli.nix b/nix/stdlib/cli.nix index 9ef7ad7..9caf82e 100644 --- a/nix/stdlib/cli.nix +++ b/nix/stdlib/cli.nix @@ -1,98 +1,104 @@ -{ lib }: rec { - /* * - Automatically convert an attribute set to command-line options. +{ lib }: +rec { + /* + * + Automatically convert an attribute set to command-line options. - This helps protect against malformed command lines and also to reduce - boilerplate related to command-line construction for simple use cases. + This helps protect against malformed command lines and also to reduce + boilerplate related to command-line construction for simple use cases. - `toGNUCommandLine` returns a list of nix strings. + `toGNUCommandLine` returns a list of nix strings. - `toGNUCommandLineShell` returns an escaped shell string. + `toGNUCommandLineShell` returns an escaped shell string. - # Inputs + # Inputs - `options` + `options` - : 1\. Function argument + : 1\. Function argument - `attrs` + `attrs` - : 2\. Function argument + : 2\. Function argument - # Examples - :::{.example} - ## `lib.cli.toGNUCommandLineShell` usage example + # Examples + :::{.example} + ## `lib.cli.toGNUCommandLineShell` usage example - ```nix - cli.toGNUCommandLine {} { - data = builtins.toJSON { id = 0; }; - X = "PUT"; - retry = 3; - retry-delay = null; - url = [ "https://example.com/foo" "https://example.com/bar" ]; - silent = false; - verbose = true; - } - => [ - "-X" "PUT" - "--data" "{\"id\":0}" - "--retry" "3" - "--url" "https://example.com/foo" - "--url" "https://example.com/bar" - "--verbose" - ] + ```nix + cli.toGNUCommandLine {} { + data = builtins.toJSON { id = 0; }; + X = "PUT"; + retry = 3; + retry-delay = null; + url = [ "https://example.com/foo" "https://example.com/bar" ]; + silent = false; + verbose = true; + } + => [ + "-X" "PUT" + "--data" "{\"id\":0}" + "--retry" "3" + "--url" "https://example.com/foo" + "--url" "https://example.com/bar" + "--verbose" + ] - cli.toGNUCommandLineShell {} { - data = builtins.toJSON { id = 0; }; - X = "PUT"; - retry = 3; - retry-delay = null; - url = [ "https://example.com/foo" "https://example.com/bar" ]; - silent = false; - verbose = true; - } - => "'-X' 'PUT' '--data' '{\"id\":0}' '--retry' '3' '--url' 'https://example.com/foo' '--url' 'https://example.com/bar' '--verbose'"; - ``` + cli.toGNUCommandLineShell {} { + data = builtins.toJSON { id = 0; }; + X = "PUT"; + retry = 3; + retry-delay = null; + url = [ "https://example.com/foo" "https://example.com/bar" ]; + silent = false; + verbose = true; + } + => "'-X' 'PUT' '--data' '{\"id\":0}' '--retry' '3' '--url' 'https://example.com/foo' '--url' 'https://example.com/bar' '--verbose'"; + ``` - ::: + ::: */ - toGNUCommandLineShell = options: attrs: - lib.escapeShellArgs (toGNUCommandLine options attrs); + toGNUCommandLineShell = options: attrs: lib.escapeShellArgs (toGNUCommandLine options attrs); - toGNUCommandLine = { - # how to string-format the option name; - # by default one character is a short option (`-`), - # more than one characters a long option (`--`). - mkOptionName ? k: if builtins.stringLength k == 1 then "-${k}" else "--${k}" - , - # how to format a boolean value to a command list; - # by default it’s a flag option - # (only the option name if true, left out completely if false). - mkBool ? k: v: lib.optional v (mkOptionName k), - # how to format a list value to a command list; - # by default the option name is repeated for each value - # and `mkOption` is applied to the values themselves. - mkList ? k: v: lib.concatMap (mkOption k) v, - # how to format any remaining value to a command list; - # on the toplevel, booleans and lists are handled by `mkBool` and `mkList`, - # though they can still appear as values of a list. - # By default, everything is printed verbatim and complex types - # are forbidden (lists, attrsets, functions). `null` values are omitted. - mkOption ? k: v: - if v == null then - [ ] - else [ - (mkOptionName k) - (lib.generators.mkValueStringDefault { } v) - ], }: + toGNUCommandLine = + { + # how to string-format the option name; + # by default one character is a short option (`-`), + # more than one characters a long option (`--`). + mkOptionName ? k: if builtins.stringLength k == 1 then "-${k}" else "--${k}", + # how to format a boolean value to a command list; + # by default it’s a flag option + # (only the option name if true, left out completely if false). + mkBool ? k: v: lib.optional v (mkOptionName k), + # how to format a list value to a command list; + # by default the option name is repeated for each value + # and `mkOption` is applied to the values themselves. + mkList ? k: v: lib.concatMap (mkOption k) v, + # how to format any remaining value to a command list; + # on the toplevel, booleans and lists are handled by `mkBool` and `mkList`, + # though they can still appear as values of a list. + # By default, everything is printed verbatim and complex types + # are forbidden (lists, attrsets, functions). `null` values are omitted. + mkOption ? + k: v: + if v == null then + [ ] + else + [ + (mkOptionName k) + (lib.generators.mkValueStringDefault { } v) + ], + }: options: let - render = k: v: + render = + k: v: if builtins.isBool v then mkBool k v else if builtins.isList v then mkList k v else mkOption k v; - in builtins.concatLists (lib.mapAttrsToList render options); + in + builtins.concatLists (lib.mapAttrsToList render options); } diff --git a/nix/stdlib/customisation.nix b/nix/stdlib/customisation.nix index e515c2a..cd39087 100644 --- a/nix/stdlib/customisation.nix +++ b/nix/stdlib/customisation.nix @@ -2,202 +2,237 @@ let inherit (builtins) intersectAttrs; inherit (lib) - functionArgs isFunction mirrorFunctionArgs isAttrs setFunctionArgs - optionalAttrs attrNames filter elemAt concatStringsSep sortOn take length - filterAttrs optionalString flip pathIsDirectory head pipe isDerivation - listToAttrs mapAttrs seq flatten deepSeq warnIf isInOldestRelease extends; + functionArgs + isFunction + mirrorFunctionArgs + isAttrs + setFunctionArgs + optionalAttrs + attrNames + filter + elemAt + concatStringsSep + sortOn + take + length + filterAttrs + optionalString + flip + pathIsDirectory + head + pipe + isDerivation + listToAttrs + mapAttrs + seq + flatten + deepSeq + warnIf + isInOldestRelease + extends + ; inherit (lib.strings) levenshtein levenshteinAtMost; -in rec { - /* * - `overrideDerivation drv f` takes a derivation (i.e., the result - of a call to the builtin function `derivation`) and returns a new - derivation in which the attributes of the original are overridden - according to the function `f`. The function `f` is called with - the original derivation attributes. +in +rec { + /* + * + `overrideDerivation drv f` takes a derivation (i.e., the result + of a call to the builtin function `derivation`) and returns a new + derivation in which the attributes of the original are overridden + according to the function `f`. The function `f` is called with + the original derivation attributes. - `overrideDerivation` allows certain "ad-hoc" customisation - scenarios (e.g. in ~/.config/nixpkgs/config.nix). For instance, - if you want to "patch" the derivation returned by a package - function in Nixpkgs to build another version than what the - function itself provides. + `overrideDerivation` allows certain "ad-hoc" customisation + scenarios (e.g. in ~/.config/nixpkgs/config.nix). For instance, + if you want to "patch" the derivation returned by a package + function in Nixpkgs to build another version than what the + function itself provides. - For another application, see build-support/vm, where this - function is used to build arbitrary derivations inside a QEMU - virtual machine. + For another application, see build-support/vm, where this + function is used to build arbitrary derivations inside a QEMU + virtual machine. - Note that in order to preserve evaluation errors, the new derivation's - outPath depends on the old one's, which means that this function cannot - be used in circular situations when the old derivation also depends on the - new one. + Note that in order to preserve evaluation errors, the new derivation's + outPath depends on the old one's, which means that this function cannot + be used in circular situations when the old derivation also depends on the + new one. - You should in general prefer `drv.overrideAttrs` over this function; - see the nixpkgs manual for more information on overriding. + You should in general prefer `drv.overrideAttrs` over this function; + see the nixpkgs manual for more information on overriding. - # Inputs + # Inputs - `drv` + `drv` - : 1\. Function argument + : 1\. Function argument - `f` + `f` - : 2\. Function argument + : 2\. Function argument - # Type + # Type - ``` - overrideDerivation :: Derivation -> ( Derivation -> AttrSet ) -> Derivation - ``` + ``` + overrideDerivation :: Derivation -> ( Derivation -> AttrSet ) -> Derivation + ``` - # Examples - :::{.example} - ## `lib.customisation.overrideDerivation` usage example + # Examples + :::{.example} + ## `lib.customisation.overrideDerivation` usage example - ```nix - mySed = overrideDerivation pkgs.gnused (oldAttrs: { - name = "sed-4.2.2-pre"; - src = fetchurl { - url = ftp://alpha.gnu.org/gnu/sed/sed-4.2.2-pre.tar.bz2; - hash = "sha256-MxBJRcM2rYzQYwJ5XKxhXTQByvSg5jZc5cSHEZoB2IY="; - }; - patches = []; - }); - ``` + ```nix + mySed = overrideDerivation pkgs.gnused (oldAttrs: { + name = "sed-4.2.2-pre"; + src = fetchurl { + url = ftp://alpha.gnu.org/gnu/sed/sed-4.2.2-pre.tar.bz2; + hash = "sha256-MxBJRcM2rYzQYwJ5XKxhXTQByvSg5jZc5cSHEZoB2IY="; + }; + patches = []; + }); + ``` - ::: + ::: */ - overrideDerivation = drv: f: - let newDrv = derivation (drv.drvAttrs // (f drv)); - in flip (extendDerivation (seq drv.drvPath true)) newDrv ({ - meta = drv.meta or { }; - passthru = if drv ? passthru then drv.passthru else { }; - } // (drv.passthru or { }) // optionalAttrs (drv ? __spliced) { - __spliced = { } - // (mapAttrs (_: sDrv: overrideDerivation sDrv f) drv.__spliced); - }); + overrideDerivation = + drv: f: + let + newDrv = derivation (drv.drvAttrs // (f drv)); + in + flip (extendDerivation (seq drv.drvPath true)) newDrv ( + { + meta = drv.meta or { }; + passthru = if drv ? passthru then drv.passthru else { }; + } + // (drv.passthru or { }) + // optionalAttrs (drv ? __spliced) { + __spliced = { } // (mapAttrs (_: sDrv: overrideDerivation sDrv f) drv.__spliced); + } + ); - /* * - `makeOverridable` takes a function from attribute set to attribute set and - injects `override` attribute which can be used to override arguments of - the function. + /* + * + `makeOverridable` takes a function from attribute set to attribute set and + injects `override` attribute which can be used to override arguments of + the function. - Please refer to documentation on [`.overrideDerivation`](#sec-pkg-overrideDerivation) to learn about `overrideDerivation` and caveats - related to its use. + Please refer to documentation on [`.overrideDerivation`](#sec-pkg-overrideDerivation) to learn about `overrideDerivation` and caveats + related to its use. - # Inputs + # Inputs - `f` + `f` - : 1\. Function argument + : 1\. Function argument - # Type + # Type - ``` - makeOverridable :: (AttrSet -> a) -> AttrSet -> a - ``` + ``` + makeOverridable :: (AttrSet -> a) -> AttrSet -> a + ``` - # Examples - :::{.example} - ## `lib.customisation.makeOverridable` usage example + # Examples + :::{.example} + ## `lib.customisation.makeOverridable` usage example - ```nix - nix-repl> x = {a, b}: { result = a + b; } + ```nix + nix-repl> x = {a, b}: { result = a + b; } - nix-repl> y = lib.makeOverridable x { a = 1; b = 2; } + nix-repl> y = lib.makeOverridable x { a = 1; b = 2; } - nix-repl> y - { override = «lambda»; overrideDerivation = «lambda»; result = 3; } + nix-repl> y + { override = «lambda»; overrideDerivation = «lambda»; result = 3; } - nix-repl> y.override { a = 10; } - { override = «lambda»; overrideDerivation = «lambda»; result = 12; } - ``` + nix-repl> y.override { a = 10; } + { override = «lambda»; overrideDerivation = «lambda»; result = 12; } + ``` - ::: + ::: */ - makeOverridable = f: + makeOverridable = + f: let # Creates a functor with the same arguments as f mirrorArgs = mirrorFunctionArgs f; - in mirrorArgs (origArgs: + in + mirrorArgs ( + origArgs: let result = f origArgs; # Changes the original arguments with (potentially a function that returns) a set of new attributes - overrideWith = newArgs: - origArgs - // (if isFunction newArgs then newArgs origArgs else newArgs); + overrideWith = newArgs: origArgs // (if isFunction newArgs then newArgs origArgs else newArgs); # Re-call the function but with different arguments - overrideArgs = - mirrorArgs (newArgs: makeOverridable f (overrideWith newArgs)); + overrideArgs = mirrorArgs (newArgs: makeOverridable f (overrideWith newArgs)); # Change the result of the function call by applying g to it - overrideResult = g: - makeOverridable (mirrorArgs (args: g (f args))) origArgs; - in if isAttrs result then - result // { + overrideResult = g: makeOverridable (mirrorArgs (args: g (f args))) origArgs; + in + if isAttrs result then + result + // { override = overrideArgs; - overrideDerivation = fdrv: - overrideResult (x: overrideDerivation x fdrv); - ${if result ? overrideAttrs then "overrideAttrs" else null} = fdrv: - overrideResult (x: x.overrideAttrs fdrv); + overrideDerivation = fdrv: overrideResult (x: overrideDerivation x fdrv); + ${if result ? overrideAttrs then "overrideAttrs" else null} = + fdrv: overrideResult (x: x.overrideAttrs fdrv); } else if isFunction result then - # Transform the result into a functor while propagating its arguments - setFunctionArgs result (functionArgs result) // { - override = overrideArgs; - } + # Transform the result into a functor while propagating its arguments + setFunctionArgs result (functionArgs result) // { override = overrideArgs; } else - result); - - /* * - Call the package function in the file `fn` with the required - arguments automatically. The function is called with the - arguments `args`, but any missing arguments are obtained from - `autoArgs`. This function is intended to be partially - parameterised, e.g., - - ```nix - callPackage = callPackageWith pkgs; - pkgs = { - libfoo = callPackage ./foo.nix { }; - libbar = callPackage ./bar.nix { }; - }; - ``` + result + ); + + /* + * + Call the package function in the file `fn` with the required + arguments automatically. The function is called with the + arguments `args`, but any missing arguments are obtained from + `autoArgs`. This function is intended to be partially + parameterised, e.g., + + ```nix + callPackage = callPackageWith pkgs; + pkgs = { + libfoo = callPackage ./foo.nix { }; + libbar = callPackage ./bar.nix { }; + }; + ``` - If the `libbar` function expects an argument named `libfoo`, it is - automatically passed as an argument. Overrides or missing - arguments can be supplied in `args`, e.g. + If the `libbar` function expects an argument named `libfoo`, it is + automatically passed as an argument. Overrides or missing + arguments can be supplied in `args`, e.g. - ```nix - libbar = callPackage ./bar.nix { - libfoo = null; - enableX11 = true; - }; - ``` + ```nix + libbar = callPackage ./bar.nix { + libfoo = null; + enableX11 = true; + }; + ``` - + - # Inputs + # Inputs - `autoArgs` + `autoArgs` - : 1\. Function argument + : 1\. Function argument - `fn` + `fn` - : 2\. Function argument + : 2\. Function argument - `args` + `args` - : 3\. Function argument + : 3\. Function argument - # Type + # Type - ``` - callPackageWith :: AttrSet -> ((AttrSet -> a) | Path) -> AttrSet -> a - ``` + ``` + callPackageWith :: AttrSet -> ((AttrSet -> a) | Path) -> AttrSet -> a + ``` */ - callPackageWith = autoArgs: fn: args: + callPackageWith = + autoArgs: fn: args: let f = if isFunction fn then fn else import fn; fargs = functionArgs f; @@ -211,11 +246,12 @@ in rec { missingArgs = # Filter out arguments that have a default value filterAttrs (name: value: !value) - # Filter out arguments that would be passed - (removeAttrs fargs (attrNames allArgs)); + # Filter out arguments that would be passed + (removeAttrs fargs (attrNames allArgs)); # Get a list of suggested argument names for a given missing one - getSuggestions = arg: + getSuggestions = + arg: pipe (autoArgs // args) [ attrNames # Only use ones that are at most 2 edits away. While mork would work, @@ -229,170 +265,199 @@ in rec { (map (x: ''"'' + x + ''"'')) ]; - prettySuggestions = suggestions: + prettySuggestions = + suggestions: if suggestions == [ ] then "" else if length suggestions == 1 then ", did you mean ${elemAt suggestions 0}?" else - ", did you mean ${concatStringsSep ", " (lib.init suggestions)} or ${ - lib.last suggestions - }?"; + ", did you mean ${concatStringsSep ", " (lib.init suggestions)} or ${lib.last suggestions}?"; - errorForArg = arg: + errorForArg = + arg: let loc = builtins.unsafeGetAttrPos arg fargs; # loc' can be removed once lib/minver.nix is >2.3.4, since that includes # https://github.com/NixOS/nix/pull/3468 which makes loc be non-null - loc' = if loc != null then - loc.file + ":" + toString loc.line - else if !isFunction fn then - toString fn + optionalString (pathIsDirectory fn) "/default.nix" - else - ""; - in ''Function called without required argument "${arg}" at '' + loc' = + if loc != null then + loc.file + ":" + toString loc.line + else if !isFunction fn then + toString fn + optionalString (pathIsDirectory fn) "/default.nix" + else + ""; + in + ''Function called without required argument "${arg}" at '' + "${loc'}${prettySuggestions (getSuggestions arg)}"; # Only show the error for the first missing argument error = errorForArg (head (attrNames missingArgs)); - in if missingArgs == { } then + in + if missingArgs == { } then makeOverridable f allArgs - # This needs to be an abort so it can't be caught with `builtins.tryEval`, - # which is used by nix-env and ofborg to filter out packages that don't evaluate. - # This way we're forced to fix such errors in Nixpkgs, - # which is especially relevant with allowAliases = false + # This needs to be an abort so it can't be caught with `builtins.tryEval`, + # which is used by nix-env and ofborg to filter out packages that don't evaluate. + # This way we're forced to fix such errors in Nixpkgs, + # which is especially relevant with allowAliases = false else abort "lib.customisation.callPackageWith: ${error}"; - /* * - Like callPackage, but for a function that returns an attribute - set of derivations. The override function is added to the - individual attributes. + /* + * + Like callPackage, but for a function that returns an attribute + set of derivations. The override function is added to the + individual attributes. - # Inputs + # Inputs - `autoArgs` + `autoArgs` - : 1\. Function argument + : 1\. Function argument - `fn` + `fn` - : 2\. Function argument + : 2\. Function argument - `args` + `args` - : 3\. Function argument + : 3\. Function argument - # Type + # Type - ``` - callPackagesWith :: AttrSet -> ((AttrSet -> AttrSet) | Path) -> AttrSet -> AttrSet - ``` + ``` + callPackagesWith :: AttrSet -> ((AttrSet -> AttrSet) | Path) -> AttrSet -> AttrSet + ``` */ - callPackagesWith = autoArgs: fn: args: + callPackagesWith = + autoArgs: fn: args: let f = if isFunction fn then fn else import fn; auto = intersectAttrs (functionArgs f) autoArgs; mirrorArgs = mirrorFunctionArgs f; origArgs = auto // args; pkgs = f origArgs; - mkAttrOverridable = name: _: - makeOverridable (mirrorArgs (newArgs: (f newArgs).${name})) origArgs; - in if isDerivation pkgs then - throw ("function `callPackages` was called on a *single* derivation " + mkAttrOverridable = name: _: makeOverridable (mirrorArgs (newArgs: (f newArgs).${name})) origArgs; + in + if isDerivation pkgs then + throw ( + "function `callPackages` was called on a *single* derivation " + ''"${pkgs.name or ""}";'' - + " did you mean to use `callPackage` instead?") + + " did you mean to use `callPackage` instead?" + ) else mapAttrs mkAttrOverridable pkgs; - /* * - Add attributes to each output of a derivation without changing - the derivation itself and check a given condition when evaluating. + /* + * + Add attributes to each output of a derivation without changing + the derivation itself and check a given condition when evaluating. - # Inputs + # Inputs - `condition` + `condition` - : 1\. Function argument + : 1\. Function argument - `passthru` + `passthru` - : 2\. Function argument + : 2\. Function argument - `drv` + `drv` - : 3\. Function argument + : 3\. Function argument - # Type + # Type - ``` - extendDerivation :: Bool -> Any -> Derivation -> Derivation - ``` + ``` + extendDerivation :: Bool -> Any -> Derivation -> Derivation + ``` */ - extendDerivation = condition: passthru: drv: + extendDerivation = + condition: passthru: drv: let outputs = drv.outputs or [ "out" ]; - commonAttrs = drv // (listToAttrs outputsList) // { - all = map (x: x.value) outputsList; - } // passthru; + commonAttrs = + drv // (listToAttrs outputsList) // { all = map (x: x.value) outputsList; } // passthru; outputToAttrListElement = outputName: { name = outputName; - value = commonAttrs // { - inherit (drv.${outputName}) type outputName; - outputSpecified = true; - drvPath = assert condition; drv.${outputName}.drvPath; - outPath = assert condition; drv.${outputName}.outPath; - } // - # TODO: give the derivation control over the outputs. - # `overrideAttrs` may not be the only attribute that needs - # updating when switching outputs. - optionalAttrs (passthru ? overrideAttrs) { - # TODO: also add overrideAttrs when overrideAttrs is not custom, e.g. when not splicing. - overrideAttrs = f: (passthru.overrideAttrs f).${outputName}; - }; + value = + commonAttrs + // { + inherit (drv.${outputName}) type outputName; + outputSpecified = true; + drvPath = + assert condition; + drv.${outputName}.drvPath; + outPath = + assert condition; + drv.${outputName}.outPath; + } + // + # TODO: give the derivation control over the outputs. + # `overrideAttrs` may not be the only attribute that needs + # updating when switching outputs. + optionalAttrs (passthru ? overrideAttrs) { + # TODO: also add overrideAttrs when overrideAttrs is not custom, e.g. when not splicing. + overrideAttrs = f: (passthru.overrideAttrs f).${outputName}; + }; }; outputsList = map outputToAttrListElement outputs; - in commonAttrs // { - drvPath = assert condition; drv.drvPath; - outPath = assert condition; drv.outPath; + in + commonAttrs + // { + drvPath = + assert condition; + drv.drvPath; + outPath = + assert condition; + drv.outPath; }; - /* * - Strip a derivation of all non-essential attributes, returning - only those needed by hydra-eval-jobs. Also strictly evaluate the - result to ensure that there are no thunks kept alive to prevent - garbage collection. + /* + * + Strip a derivation of all non-essential attributes, returning + only those needed by hydra-eval-jobs. Also strictly evaluate the + result to ensure that there are no thunks kept alive to prevent + garbage collection. - # Inputs + # Inputs - `drv` + `drv` - : 1\. Function argument + : 1\. Function argument - # Type + # Type - ``` - hydraJob :: (Derivation | Null) -> (Derivation | Null) - ``` + ``` + hydraJob :: (Derivation | Null) -> (Derivation | Null) + ``` */ - hydraJob = drv: + hydraJob = + drv: let outputs = drv.outputs or [ "out" ]; - commonAttrs = { - inherit (drv) name system meta; - inherit outputs; - } // optionalAttrs (drv._hydraAggregate or false) { - _hydraAggregate = true; - constituents = map hydraJob (flatten drv.constituents); - } // (listToAttrs outputsList); - - makeOutput = outputName: - let output = drv.${outputName}; - in { + commonAttrs = + { + inherit (drv) name system meta; + inherit outputs; + } + // optionalAttrs (drv._hydraAggregate or false) { + _hydraAggregate = true; + constituents = map hydraJob (flatten drv.constituents); + } + // (listToAttrs outputsList); + + makeOutput = + outputName: + let + output = drv.${outputName}; + in + { name = outputName; value = commonAttrs // { outPath = output.outPath; @@ -405,227 +470,243 @@ in rec { outputsList = map makeOutput outputs; drv' = (head outputsList).value; - in if drv == null then null else deepSeq drv' drv'; - - /* * - Make an attribute set (a "scope") from functions that take arguments from that same attribute set. - See [](#ex-makeScope) for how to use it. - - # Inputs - - 1. `newScope` (`AttrSet -> ((AttrSet -> a) | Path) -> AttrSet -> a`) - - A function that takes an attribute set `attrs` and returns what ends up as `callPackage` in the output. + in + if drv == null then null else deepSeq drv' drv'; - Typical values are `callPackageWith` or the output attribute `newScope`. + /* + * + Make an attribute set (a "scope") from functions that take arguments from that same attribute set. + See [](#ex-makeScope) for how to use it. - 2. `f` (`AttrSet -> AttrSet`) + # Inputs - A function that takes an attribute set as returned by `makeScope newScope f` (a "scope") and returns any attribute set. + 1. `newScope` (`AttrSet -> ((AttrSet -> a) | Path) -> AttrSet -> a`) - This function is used to compute the fixpoint of the resulting scope using `callPackage`. - Its argument is the lazily evaluated reference to the value of that fixpoint, and is typically called `self` or `final`. + A function that takes an attribute set `attrs` and returns what ends up as `callPackage` in the output. - See [](#ex-makeScope) for how to use it. - See [](#sec-functions-library-fixedPoints) for details on fixpoint computation. + Typical values are `callPackageWith` or the output attribute `newScope`. - # Output + 2. `f` (`AttrSet -> AttrSet`) - `makeScope` returns an attribute set of a form called `scope`, which also contains the final attributes produced by `f`: + A function that takes an attribute set as returned by `makeScope newScope f` (a "scope") and returns any attribute set. - ``` - scope :: { - callPackage :: ((AttrSet -> a) | Path) -> AttrSet -> a - newScope = AttrSet -> scope - overrideScope = (scope -> scope -> AttrSet) -> scope - packages :: AttrSet -> AttrSet - } - ``` + This function is used to compute the fixpoint of the resulting scope using `callPackage`. + Its argument is the lazily evaluated reference to the value of that fixpoint, and is typically called `self` or `final`. - - `callPackage` (`((AttrSet -> a) | Path) -> AttrSet -> a`) + See [](#ex-makeScope) for how to use it. + See [](#sec-functions-library-fixedPoints) for details on fixpoint computation. - A function that + # Output - 1. Takes a function `p`, or a path to a Nix file that contains a function `p`, which takes an attribute set and returns value of arbitrary type `a`, - 2. Takes an attribute set `args` with explicit attributes to pass to `p`, - 3. Calls `f` with attributes from the original attribute set `attrs` passed to `newScope` updated with `args, i.e. `attrs // args`, if they match the attributes in the argument of `p`. + `makeScope` returns an attribute set of a form called `scope`, which also contains the final attributes produced by `f`: - All such functions `p` will be called with the same value for `attrs`. + ``` + scope :: { + callPackage :: ((AttrSet -> a) | Path) -> AttrSet -> a + newScope = AttrSet -> scope + overrideScope = (scope -> scope -> AttrSet) -> scope + packages :: AttrSet -> AttrSet + } + ``` - See [](#ex-makeScope-callPackage) for how to use it. + - `callPackage` (`((AttrSet -> a) | Path) -> AttrSet -> a`) - - `newScope` (`AttrSet -> scope`) + A function that - Takes an attribute set `attrs` and returns a scope that extends the original scope. + 1. Takes a function `p`, or a path to a Nix file that contains a function `p`, which takes an attribute set and returns value of arbitrary type `a`, + 2. Takes an attribute set `args` with explicit attributes to pass to `p`, + 3. Calls `f` with attributes from the original attribute set `attrs` passed to `newScope` updated with `args, i.e. `attrs // args`, if they match the attributes in the argument of `p`. - - `overrideScope` (`(scope -> scope -> AttrSet) -> scope`) + All such functions `p` will be called with the same value for `attrs`. - Takes a function `g` of the form `final: prev: { # attributes }` to act as an overlay on `f`, and returns a new scope with values determined by `extends g f`. - See [](https://nixos.org/manual/nixpkgs/unstable/#function-library-lib.fixedPoints.extends) for details. + See [](#ex-makeScope-callPackage) for how to use it. - This allows subsequent modification of the final attribute set in a consistent way, i.e. all functions `p` invoked with `callPackage` will be called with the modified values. + - `newScope` (`AttrSet -> scope`) - - `packages` (`AttrSet -> AttrSet`) + Takes an attribute set `attrs` and returns a scope that extends the original scope. - The value of the argument `f` to `makeScope`. + - `overrideScope` (`(scope -> scope -> AttrSet) -> scope`) - - final attributes + Takes a function `g` of the form `final: prev: { # attributes }` to act as an overlay on `f`, and returns a new scope with values determined by `extends g f`. + See [](https://nixos.org/manual/nixpkgs/unstable/#function-library-lib.fixedPoints.extends) for details. - The final values returned by `f`. + This allows subsequent modification of the final attribute set in a consistent way, i.e. all functions `p` invoked with `callPackage` will be called with the modified values. - # Examples + - `packages` (`AttrSet -> AttrSet`) - :::{#ex-makeScope .example} - # Create an interdependent package set on top of `pkgs` + The value of the argument `f` to `makeScope`. - The functions in `foo.nix` and `bar.nix` can depend on each other, in the sense that `foo.nix` can contain a function that expects `bar` as an attribute in its argument. + - final attributes - ```nix - let - pkgs = import { }; - in - pkgs.lib.makeScope pkgs.newScope (self: { - foo = self.callPackage ./foo.nix { }; - bar = self.callPackage ./bar.nix { }; - }) - ``` + The final values returned by `f`. - evaluates to + # Examples - ```nix - { - callPackage = «lambda»; - newScope = «lambda»; - overrideScope = «lambda»; - packages = «lambda»; - foo = «derivation»; - bar = «derivation»; - } - ``` - ::: + :::{#ex-makeScope .example} + # Create an interdependent package set on top of `pkgs` - :::{#ex-makeScope-callPackage .example} - # Using `callPackage` from a scope + The functions in `foo.nix` and `bar.nix` can depend on each other, in the sense that `foo.nix` can contain a function that expects `bar` as an attribute in its argument. - ```nix - let - pkgs = import { }; - inherit (pkgs) lib; - scope = lib.makeScope lib.callPackageWith (self: { a = 1; b = 2; }); - three = scope.callPackage ({ a, b }: a + b) { }; - four = scope.callPackage ({ a, b }: a + b) { a = 2; }; - in - [ three four ] - ``` - - evaluates to - - ```nix - [ 3 4 ] - ``` - ::: - - # Type - - ``` - makeScope :: (AttrSet -> ((AttrSet -> a) | Path) -> AttrSet -> a) -> (AttrSet -> AttrSet) -> scope - ``` + ```nix + let + pkgs = import { }; + in + pkgs.lib.makeScope pkgs.newScope (self: { + foo = self.callPackage ./foo.nix { }; + bar = self.callPackage ./bar.nix { }; + }) + ``` + + evaluates to + + ```nix + { + callPackage = «lambda»; + newScope = «lambda»; + overrideScope = «lambda»; + packages = «lambda»; + foo = «derivation»; + bar = «derivation»; + } + ``` + ::: + + :::{#ex-makeScope-callPackage .example} + # Using `callPackage` from a scope + + ```nix + let + pkgs = import { }; + inherit (pkgs) lib; + scope = lib.makeScope lib.callPackageWith (self: { a = 1; b = 2; }); + three = scope.callPackage ({ a, b }: a + b) { }; + four = scope.callPackage ({ a, b }: a + b) { a = 2; }; + in + [ three four ] + ``` + + evaluates to + + ```nix + [ 3 4 ] + ``` + ::: + + # Type + + ``` + makeScope :: (AttrSet -> ((AttrSet -> a) | Path) -> AttrSet -> a) -> (AttrSet -> AttrSet) -> scope + ``` */ - makeScope = newScope: f: + makeScope = + newScope: f: let self = f self // { newScope = scope: newScope (self // scope); callPackage = self.newScope { }; overrideScope = g: makeScope newScope (extends g f); # Remove after 24.11 is released. - overrideScope' = g: + overrideScope' = + g: warnIf (isInOldestRelease 2311) - "`overrideScope'` (from `lib.makeScope`) has been renamed to `overrideScope`." - (makeScope newScope (extends g f)); + "`overrideScope'` (from `lib.makeScope`) has been renamed to `overrideScope`." + (makeScope newScope (extends g f)); packages = f; }; - in self; + in + self; - /* * - backward compatibility with old uncurried form; deprecated + /* + * + backward compatibility with old uncurried form; deprecated - # Inputs + # Inputs - `splicePackages` + `splicePackages` - : 1\. Function argument + : 1\. Function argument - `newScope` + `newScope` - : 2\. Function argument + : 2\. Function argument - `otherSplices` + `otherSplices` - : 3\. Function argument + : 3\. Function argument - `keep` + `keep` - : 4\. Function argument + : 4\. Function argument - `extra` + `extra` - : 5\. Function argument + : 5\. Function argument - `f` + `f` - : 6\. Function argument + : 6\. Function argument */ makeScopeWithSplicing = splicePackages: newScope: otherSplices: keep: extra: f: makeScopeWithSplicing' { inherit splicePackages newScope; } { - inherit otherSplices keep extra f; + inherit + otherSplices + keep + extra + f + ; }; - /* * - Like makeScope, but aims to support cross compilation. It's still ugly, but - hopefully it helps a little bit. - - # Type - - ``` - makeScopeWithSplicing' :: - { splicePackages :: Splice -> AttrSet - , newScope :: AttrSet -> ((AttrSet -> a) | Path) -> AttrSet -> a - } - -> { otherSplices :: Splice, keep :: AttrSet -> AttrSet, extra :: AttrSet -> AttrSet } - -> AttrSet - - Splice :: - { pkgsBuildBuild :: AttrSet - , pkgsBuildHost :: AttrSet - , pkgsBuildTarget :: AttrSet - , pkgsHostHost :: AttrSet - , pkgsHostTarget :: AttrSet - , pkgsTargetTarget :: AttrSet - } - ``` + /* + * + Like makeScope, but aims to support cross compilation. It's still ugly, but + hopefully it helps a little bit. + + # Type + + ``` + makeScopeWithSplicing' :: + { splicePackages :: Splice -> AttrSet + , newScope :: AttrSet -> ((AttrSet -> a) | Path) -> AttrSet -> a + } + -> { otherSplices :: Splice, keep :: AttrSet -> AttrSet, extra :: AttrSet -> AttrSet } + -> AttrSet + + Splice :: + { pkgsBuildBuild :: AttrSet + , pkgsBuildHost :: AttrSet + , pkgsBuildTarget :: AttrSet + , pkgsHostHost :: AttrSet + , pkgsHostTarget :: AttrSet + , pkgsTargetTarget :: AttrSet + } + ``` */ - makeScopeWithSplicing' = { splicePackages, newScope, }: - { otherSplices, - # Attrs from `self` which won't be spliced. - # Avoid using keep, it's only used for a python hook workaround, added in PR #104201. - # ex: `keep = (self: { inherit (self) aAttr; })` - keep ? (_self: { }), - # Additional attrs to add to the sets `callPackage`. - # When the package is from a subset (but not a subset within a package IS #211340) - # within `spliced0` it will be spliced. - # When using an package outside the set but it's available from `pkgs`, use the package from `pkgs.__splicedPackages`. - # If the package is not available within the set or in `pkgs`, such as a package in a let binding, it will not be spliced - # ex: - # ``` - # nix-repl> darwin.apple_sdk.frameworks.CoreFoundation - # «derivation ...CoreFoundation-11.0.0.drv» - # nix-repl> darwin.CoreFoundation - # error: attribute 'CoreFoundation' missing - # nix-repl> darwin.callPackage ({ CoreFoundation }: CoreFoundation) { } - # «derivation ...CoreFoundation-11.0.0.drv» - # ``` - extra ? (_spliced0: { }), f, }: + makeScopeWithSplicing' = + { splicePackages, newScope }: + { + otherSplices, + # Attrs from `self` which won't be spliced. + # Avoid using keep, it's only used for a python hook workaround, added in PR #104201. + # ex: `keep = (self: { inherit (self) aAttr; })` + keep ? (_self: { }), + # Additional attrs to add to the sets `callPackage`. + # When the package is from a subset (but not a subset within a package IS #211340) + # within `spliced0` it will be spliced. + # When using an package outside the set but it's available from `pkgs`, use the package from `pkgs.__splicedPackages`. + # If the package is not available within the set or in `pkgs`, such as a package in a let binding, it will not be spliced + # ex: + # ``` + # nix-repl> darwin.apple_sdk.frameworks.CoreFoundation + # «derivation ...CoreFoundation-11.0.0.drv» + # nix-repl> darwin.CoreFoundation + # error: attribute 'CoreFoundation' missing + # nix-repl> darwin.callPackage ({ CoreFoundation }: CoreFoundation) { } + # «derivation ...CoreFoundation-11.0.0.drv» + # ``` + extra ? (_spliced0: { }), + f, + }: let spliced0 = splicePackages { pkgsBuildBuild = otherSplices.selfBuildBuild; @@ -641,12 +722,14 @@ in rec { callPackage = newScope spliced; # == self.newScope {}; # N.B. the other stages of the package set spliced in are *not* # overridden. - overrideScope = g: + overrideScope = + g: (makeScopeWithSplicing' { inherit splicePackages newScope; } { inherit otherSplices keep extra; f = extends g f; }); packages = f; }; - in self; + in + self; } diff --git a/nix/stdlib/debug.nix b/nix/stdlib/debug.nix index 36638ac..551cdf6 100644 --- a/nix/stdlib/debug.nix +++ b/nix/stdlib/debug.nix @@ -1,33 +1,46 @@ -/* Collection of functions useful for debugging - broken nix expressions. - - * `trace`-like functions take two values, print - the first to stderr and return the second. - * `traceVal`-like functions take one argument - which both printed and returned. - * `traceSeq`-like functions fully evaluate their - traced value before printing (not just to “weak - head normal form” like trace does by default). - * Functions that end in `-Fn` take an additional - function as their first argument, which is applied - to the traced value before it is printed. +/* + Collection of functions useful for debugging + broken nix expressions. + + * `trace`-like functions take two values, print + the first to stderr and return the second. + * `traceVal`-like functions take one argument + which both printed and returned. + * `traceSeq`-like functions fully evaluate their + traced value before printing (not just to “weak + head normal form” like trace does by default). + * Functions that end in `-Fn` take an additional + function as their first argument, which is applied + to the traced value before it is printed. */ { lib }: let inherit (lib) - isList isAttrs substring attrValues concatLists const elem generators id - mapAttrs trace; -in rec { + isList + isAttrs + substring + attrValues + concatLists + const + elem + generators + id + mapAttrs + trace + ; +in +rec { # -- TRACING -- - /* Conditionally trace the supplied message, based on a predicate. + /* + Conditionally trace the supplied message, based on a predicate. - Type: traceIf :: bool -> string -> a -> a + Type: traceIf :: bool -> string -> a -> a - Example: - traceIf true "hello" 3 - trace: hello - => 3 + Example: + traceIf true "hello" 3 + trace: hello + => 3 */ traceIf = # Predicate to check @@ -38,15 +51,16 @@ in rec { x: if pred then trace msg x else x; - /* Trace the supplied value after applying a function to it, and - return the original value. + /* + Trace the supplied value after applying a function to it, and + return the original value. - Type: traceValFn :: (a -> b) -> a -> a + Type: traceValFn :: (a -> b) -> a -> a - Example: - traceValFn (v: "mystring ${v}") "foo" - trace: mystring foo - => "foo" + Example: + traceValFn (v: "mystring ${v}") "foo" + trace: mystring foo + => "foo" */ traceValFn = # Function to apply @@ -55,28 +69,30 @@ in rec { x: trace (f x) x; - /* Trace the supplied value and return it. + /* + Trace the supplied value and return it. - Type: traceVal :: a -> a + Type: traceVal :: a -> a - Example: - traceVal 42 - # trace: 42 - => 42 + Example: + traceVal 42 + # trace: 42 + => 42 */ traceVal = traceValFn id; - /* `builtins.trace`, but the value is `builtins.deepSeq`ed first. + /* + `builtins.trace`, but the value is `builtins.deepSeq`ed first. - Type: traceSeq :: a -> b -> b + Type: traceSeq :: a -> b -> b - Example: - trace { a.b.c = 3; } null - trace: { a = ; } - => null - traceSeq { a.b.c = 3; } null - trace: { a = { b = { c = 3; }; }; } - => null + Example: + trace { a.b.c = 3; } null + trace: { a = ; } + => null + traceSeq { a.b.c = 3; } null + trace: { a = { b = { c = 3; }; }; } + => null */ traceSeq = # The value to trace @@ -85,20 +101,23 @@ in rec { y: trace (builtins.deepSeq x x) y; - /* Like `traceSeq`, but only evaluate down to depth n. - This is very useful because lots of `traceSeq` usages - lead to an infinite recursion. + /* + Like `traceSeq`, but only evaluate down to depth n. + This is very useful because lots of `traceSeq` usages + lead to an infinite recursion. - Example: - traceSeqN 2 { a.b.c = 3; } null - trace: { a = { b = {…}; }; } - => null + Example: + traceSeqN 2 { a.b.c = 3; } null + trace: { a = { b = {…}; }; } + => null - Type: traceSeqN :: Int -> a -> b -> b + Type: traceSeqN :: Int -> a -> b -> b */ - traceSeqN = depth: x: y: + traceSeqN = + depth: x: y: let - snip = v: + snip = + v: if isList v then noQuotes "[…]" v else if isAttrs v then @@ -109,7 +128,8 @@ in rec { __pretty = const str; val = v; }; - modify = n: fn: v: + modify = + n: fn: v: if (n == 0) then fn v else if isList v then @@ -118,12 +138,13 @@ in rec { mapAttrs (const (modify (n - 1) fn)) v else v; - in trace - (generators.toPretty { allowPrettyValues = true; } (modify depth snip x)) y; + in + trace (generators.toPretty { allowPrettyValues = true; } (modify depth snip x)) y; - /* A combination of `traceVal` and `traceSeq` that applies a - provided function to the value to be traced after `deepSeq`ing - it. + /* + A combination of `traceVal` and `traceSeq` that applies a + provided function to the value to be traced after `deepSeq`ing + it. */ traceValSeqFn = # Function to apply @@ -135,8 +156,9 @@ in rec { # A combination of `traceVal` and `traceSeq`. traceValSeq = traceValSeqFn id; - /* A combination of `traceVal` and `traceSeqN` that applies a - provided function to the value to be traced. + /* + A combination of `traceVal` and `traceSeqN` that applies a + provided function to the value to be traced. */ traceValSeqNFn = # Function to apply @@ -148,20 +170,24 @@ in rec { # A combination of `traceVal` and `traceSeqN`. traceValSeqN = traceValSeqNFn id; - /* Trace the input and output of a function `f` named `name`, - both down to `depth`. + /* + Trace the input and output of a function `f` named `name`, + both down to `depth`. - This is useful for adding around a function call, - to see the before/after of values as they are transformed. + This is useful for adding around a function call, + to see the before/after of values as they are transformed. - Example: - traceFnSeqN 2 "id" (x: x) { a.b.c = 3; } - trace: { fn = "id"; from = { a.b = {…}; }; to = { a.b = {…}; }; } - => { a.b.c = 3; } + Example: + traceFnSeqN 2 "id" (x: x) { a.b.c = 3; } + trace: { fn = "id"; from = { a.b = {…}; }; to = { a.b = {…}; }; } + => { a.b.c = 3; } */ - traceFnSeqN = depth: name: f: v: - let res = f v; - in lib.traceSeqN (depth + 1) { + traceFnSeqN = + depth: name: f: v: + let + res = f v; + in + lib.traceSeqN (depth + 1) { fn = name; from = v; to = res; @@ -169,82 +195,98 @@ in rec { # -- TESTING -- - /* Evaluates a set of tests. - - A test is an attribute set `{expr, expected}`, - denoting an expression and its expected result. - - The result is a `list` of __failed tests__, each represented as - `{name, expected, result}`, - - - expected - - What was passed as `expected` - - result - - The actual `result` of the test - - Used for regression testing of the functions in lib; see - tests.nix for more examples. - - Important: Only attributes that start with `test` are executed. - - - If you want to run only a subset of the tests add the attribute `tests = ["testName"];` - - Example: - - runTests { - testAndOk = { - expr = lib.and true false; - expected = false; - }; - testAndFail = { - expr = lib.and true false; - expected = true; + /* + Evaluates a set of tests. + + A test is an attribute set `{expr, expected}`, + denoting an expression and its expected result. + + The result is a `list` of __failed tests__, each represented as + `{name, expected, result}`, + + - expected + - What was passed as `expected` + - result + - The actual `result` of the test + + Used for regression testing of the functions in lib; see + tests.nix for more examples. + + Important: Only attributes that start with `test` are executed. + + - If you want to run only a subset of the tests add the attribute `tests = ["testName"];` + + Example: + + runTests { + testAndOk = { + expr = lib.and true false; + expected = false; + }; + testAndFail = { + expr = lib.and true false; + expected = true; + }; + } + -> + [ + { + name = "testAndFail"; + expected = true; + result = false; + } + ] + + Type: + runTests :: { + tests = [ String ]; + ${testName} :: { + expr :: a; + expected :: a; }; } -> [ { - name = "testAndFail"; - expected = true; - result = false; + name :: String; + expected :: a; + result :: a; } ] - - Type: - runTests :: { - tests = [ String ]; - ${testName} :: { - expr :: a; - expected :: a; - }; - } - -> - [ - { - name :: String; - expected :: a; - result :: a; - } - ] */ runTests = # Tests to run tests: - concatLists (attrValues (mapAttrs (name: test: - let testsToRun = if tests ? tests then tests.tests else [ ]; - in if (substring 0 4 name == "test" || elem name testsToRun) - && ((testsToRun == [ ]) || elem name tests.tests) - && (test.expr != test.expected) then [{ - inherit name; - expected = test.expected; - result = test.expr; - }] else - [ ]) tests)); - - /* Create a test assuming that list elements are `true`. - - Example: - { testX = allTrue [ true ]; } + concatLists ( + attrValues ( + mapAttrs ( + name: test: + let + testsToRun = if tests ? tests then tests.tests else [ ]; + in + if + (substring 0 4 name == "test" || elem name testsToRun) + && ((testsToRun == [ ]) || elem name tests.tests) + && (test.expr != test.expected) + then + [ + { + inherit name; + expected = test.expected; + result = test.expr; + } + ] + else + [ ] + ) tests + ) + ); + + /* + Create a test assuming that list elements are `true`. + + Example: + { testX = allTrue [ true ]; } */ testAllTrue = expr: { inherit expr; diff --git a/nix/stdlib/default.nix b/nix/stdlib/default.nix index 4850475..fda7467 100644 --- a/nix/stdlib/default.nix +++ b/nix/stdlib/default.nix @@ -5,9 +5,12 @@ let inherit (import ./fixed-points.nix { inherit lib; }) makeExtensible; - lib = makeExtensible (self: - let callLibs = file: import file { lib = self; }; - in { + lib = makeExtensible ( + self: + let + callLibs = file: import file { lib = self; }; + in + { # often used, or depending on very little trivial = callLibs ./trivial.nix; fixedPoints = callLibs ./fixed-points.nix; @@ -60,107 +63,432 @@ let kernel = callLibs ./kernel.nix; inherit (builtins) - add addErrorContext attrNames concatLists deepSeq elem elemAt filter - genericClosure genList getAttr hasAttr head isAttrs isBool isInt isList - isPath isString length lessThan listToAttrs pathExists readFile - replaceStrings seq stringLength sub substring tail trace; + add + addErrorContext + attrNames + concatLists + deepSeq + elem + elemAt + filter + genericClosure + genList + getAttr + hasAttr + head + isAttrs + isBool + isInt + isList + isPath + isString + length + lessThan + listToAttrs + pathExists + readFile + replaceStrings + seq + stringLength + sub + substring + tail + trace + ; inherit (self.trivial) - id const pipe concat or and xor bitAnd bitOr bitXor bitNot boolToString - mergeAttrs flip mapNullable inNixShell isFloat min max importJSON - importTOML warn warnIf warnIfNot throwIf throwIfNot checkListOfEnum info - showWarnings nixpkgsVersion version isInOldestRelease mod compare - splitByAndCompare functionArgs setFunctionArgs isFunction toFunction - mirrorFunctionArgs toHexString toBaseDigits inPureEvalMode; + id + const + pipe + concat + or + and + xor + bitAnd + bitOr + bitXor + bitNot + boolToString + mergeAttrs + flip + mapNullable + inNixShell + isFloat + min + max + importJSON + importTOML + warn + warnIf + warnIfNot + throwIf + throwIfNot + checkListOfEnum + info + showWarnings + nixpkgsVersion + version + isInOldestRelease + mod + compare + splitByAndCompare + functionArgs + setFunctionArgs + isFunction + toFunction + mirrorFunctionArgs + toHexString + toBaseDigits + inPureEvalMode + ; inherit (self.fixedPoints) - fix fix' converge extends composeExtensions composeManyExtensions - makeExtensible makeExtensibleWithCustomName; + fix + fix' + converge + extends + composeExtensions + composeManyExtensions + makeExtensible + makeExtensibleWithCustomName + ; inherit (self.attrsets) - attrByPath hasAttrByPath setAttrByPath getAttrFromPath attrVals - 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 getBin getLib - getDev getMan chooseDevOutputs zipWithNames zip recurseIntoAttrs - dontRecurseIntoAttrs cartesianProduct cartesianProductOfSets - mapCartesianProduct updateManyAttrsByPath; + attrByPath + hasAttrByPath + setAttrByPath + getAttrFromPath + attrVals + 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 + getBin + getLib + getDev + getMan + chooseDevOutputs + zipWithNames + zip + recurseIntoAttrs + dontRecurseIntoAttrs + cartesianProduct + cartesianProductOfSets + mapCartesianProduct + updateManyAttrsByPath + ; inherit (self.lists) - singleton forEach foldr fold foldl foldl' imap0 imap1 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 sublist - last init crossLists unique allUnique intersectLists subtractLists - mutuallyExclusive groupBy groupBy'; + singleton + forEach + foldr + fold + foldl + foldl' + imap0 + imap1 + 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 + sublist + last + init + crossLists + unique + allUnique + intersectLists + subtractLists + mutuallyExclusive + groupBy + groupBy' + ; inherit (self.strings) - concatStrings concatMapStrings concatImapStrings intersperse - concatStringsSep concatMapStringsSep concatImapStringsSep concatLines - makeSearchPath makeSearchPathOutput makeLibraryPath makeIncludePath - makeBinPath optionalString hasInfix hasPrefix hasSuffix - stringToCharacters stringAsChars escape escapeShellArg escapeShellArgs - isStorePath isStringLike isValidPosixName toShellVar toShellVars - escapeRegex escapeURL escapeXML replaceChars lowerChars upperChars - toLower toUpper addContextFrom splitString removePrefix removeSuffix - versionOlder versionAtLeast getName getVersion cmakeOptionType cmakeBool - cmakeFeature mesonOption mesonBool mesonEnable nameFromURL enableFeature - enableFeatureAs withFeature withFeatureAs fixedWidthString - fixedWidthNumber toInt toIntBase10 readPathsFromFile fileContents; + concatStrings + concatMapStrings + concatImapStrings + intersperse + concatStringsSep + concatMapStringsSep + concatImapStringsSep + concatLines + makeSearchPath + makeSearchPathOutput + makeLibraryPath + makeIncludePath + makeBinPath + optionalString + hasInfix + hasPrefix + hasSuffix + stringToCharacters + stringAsChars + escape + escapeShellArg + escapeShellArgs + isStorePath + isStringLike + isValidPosixName + toShellVar + toShellVars + escapeRegex + escapeURL + escapeXML + replaceChars + lowerChars + upperChars + toLower + toUpper + addContextFrom + splitString + removePrefix + removeSuffix + versionOlder + versionAtLeast + getName + getVersion + 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; + textClosureList + textClosureMap + noDepEntry + fullDepEntry + packEntry + stringAfter + ; inherit (self.customisation) - overrideDerivation makeOverridable callPackageWith callPackagesWith - extendDerivation hydraJob makeScope makeScopeWithSplicing - makeScopeWithSplicing'; + overrideDerivation + makeOverridable + callPackageWith + callPackagesWith + extendDerivation + hydraJob + makeScope + makeScopeWithSplicing + makeScopeWithSplicing' + ; inherit (self.derivations) lazyDerivation optionalDrvAttr; inherit (self.meta) - addMetaAttrs dontDistribute setName updateName appendToName - mapDerivationAttrset setPrio lowPrio lowPrioSet hiPrio hiPrioSet - getLicenseFromSpdxId getExe getExe'; + addMetaAttrs + dontDistribute + setName + updateName + appendToName + mapDerivationAttrset + setPrio + lowPrio + lowPrioSet + hiPrio + hiPrioSet + getLicenseFromSpdxId + getExe + getExe' + ; inherit (self.filesystem) - pathType pathIsDirectory pathIsRegularFile - packagesFromDirectoryRecursive; + pathType + pathIsDirectory + pathIsRegularFile + packagesFromDirectoryRecursive + ; inherit (self.sources) - cleanSourceFilter cleanSource sourceByRegex sourceFilesBySuffices - commitIdFromGitRepo cleanSourceWith pathHasContext canCleanSource - pathIsGitRepo; + 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; + evalModules + setDefaultModuleLocation + unifyModuleSyntax + applyModuleArgsIfFunction + mergeModules + mergeModules' + mergeOptionDecls + mergeDefinitions + pushDownProperties + dischargeProperties + filterOverrides + sortProperties + fixupOptionType + mkIf + mkAssert + mkMerge + mkOverride + mkOptionDefault + mkDefault + mkImageMediaOverride + mkForce + mkVMOverride + mkFixStrictness + mkOrder + mkBefore + mkAfter + mkAliasDefinitions + mkAliasAndWrapDefinitions + fixMergeModules + mkRemovedOptionModule + mkRenamedOptionModule + mkRenamedOptionModuleWith + mkMergedOptionModule + mkChangedOptionModule + mkAliasOptionModule + mkDerivedConfig + doRename + mkAliasOptionModuleMD + ; + evalOptionValue = lib.warn "External use of `lib.evalOptionValue` is deprecated. If your use case isn't covered by non-deprecated functions, we'd like to know more and perhaps support your use case well, instead of providing access to these low level functions. In this case please open an issue in https://github.com/nixos/nixpkgs/issues/." self.modules.evalOptionValue; inherit (self.options) - isOption mkEnableOption mkSinkUndeclaredOptions mergeDefaultOption - mergeOneOption mergeEqualOption mergeUniqueOption getValues getFiles - optionAttrSetToDocList optionAttrSetToDocList' scrubOptionValue - literalExpression literalExample showOption showOptionWithDefLocs - showFiles unknownModule mkOption mkPackageOption mkPackageOptionMD mdDoc - literalMD; + isOption + mkEnableOption + mkSinkUndeclaredOptions + mergeDefaultOption + mergeOneOption + mergeEqualOption + mergeUniqueOption + getValues + getFiles + optionAttrSetToDocList + optionAttrSetToDocList' + scrubOptionValue + literalExpression + literalExample + showOption + showOptionWithDefLocs + showFiles + unknownModule + mkOption + mkPackageOption + mkPackageOptionMD + mdDoc + literalMD + ; inherit (self.types) - isType setType defaultTypeMerge defaultFunctor isOptionType - mkOptionType; + isType + setType + defaultTypeMerge + defaultFunctor + isOptionType + mkOptionType + ; inherit (self.asserts) assertMsg assertOneOf; inherit (self.debug) - traceIf traceVal traceValFn traceSeq traceSeqN traceValSeq traceValSeqFn - traceValSeqN traceValSeqNFn traceFnSeqN runTests testAllTrue; + traceIf + traceVal + traceValFn + traceSeq + traceSeqN + traceValSeq + traceValSeqFn + traceValSeqN + traceValSeqNFn + traceFnSeqN + runTests + testAllTrue + ; inherit (self.misc) - maybeEnv defaultMergeArg defaultMerge foldArgs maybeAttrNullable - maybeAttr ifEnable checkFlag getValue checkReqs uniqList uniqListExt - condConcat lazyGenericClosure innerModifySumArgs modifySumArgs - innerClosePropagation closePropagation mapAttrsFlatten nvs setAttr - setAttrMerge mergeAttrsWithFunc mergeAttrsConcatenateValues - mergeAttrsNoOverride mergeAttrByFunc mergeAttrsByFuncDefaults - mergeAttrsByFuncDefaultsClean mergeAttrBy fakeHash fakeSha256 fakeSha512 - nixType imap; + maybeEnv + defaultMergeArg + defaultMerge + foldArgs + maybeAttrNullable + maybeAttr + ifEnable + checkFlag + getValue + checkReqs + uniqList + uniqListExt + condConcat + lazyGenericClosure + innerModifySumArgs + modifySumArgs + innerClosePropagation + closePropagation + mapAttrsFlatten + nvs + setAttr + setAttrMerge + mergeAttrsWithFunc + mergeAttrsConcatenateValues + mergeAttrsNoOverride + mergeAttrByFunc + mergeAttrsByFuncDefaults + mergeAttrsByFuncDefaultsClean + mergeAttrBy + fakeHash + fakeSha256 + fakeSha512 + nixType + imap + ; inherit (self.versions) splitVersion; - }); -in lib + } + ); +in +lib diff --git a/nix/stdlib/deprecated/misc.nix b/nix/stdlib/deprecated/misc.nix index 5fd1a72..24c22a6 100644 --- a/nix/stdlib/deprecated/misc.nix +++ b/nix/stdlib/deprecated/misc.nix @@ -1,54 +1,98 @@ { lib }: let inherit (lib) - and any attrByPath attrNames compare concat concatMap elem filter foldl - foldr genericClosure head imap1 init isAttrs isFunction isInt isList lists - listToAttrs mapAttrs mergeAttrs meta nameValuePair tail toList; + and + any + attrByPath + attrNames + compare + concat + concatMap + elem + filter + foldl + foldr + genericClosure + head + imap1 + init + isAttrs + isFunction + isInt + isList + lists + listToAttrs + mapAttrs + mergeAttrs + meta + nameValuePair + tail + toList + ; inherit (lib.attrsets) removeAttrs; # returns default if env var is not set - maybeEnv = name: default: - let value = builtins.getEnv name; - in if value == "" then default else value; + maybeEnv = + name: default: + let + value = builtins.getEnv name; + in + if value == "" then default else value; defaultMergeArg = x: y: if builtins.isAttrs y then y else (y x); defaultMerge = x: y: x // (defaultMergeArg x y); - foldArgs = merger: f: init: x: + foldArgs = + merger: f: init: x: let arg = merger init (defaultMergeArg init x); # now add the function with composed args already applied to the final attrs - base = setAttrMerge "passthru" { } (f arg) (z: - z // { + base = setAttrMerge "passthru" { } (f arg) ( + z: + z + // { function = foldArgs merger f arg; - args = (attrByPath [ "passthru" "args" ] { } z) // x; - }); - withStdOverrides = base // { override = base.passthru.function; }; - in withStdOverrides; + args = + (attrByPath [ + "passthru" + "args" + ] { } z) + // x; + } + ); + withStdOverrides = base // { + override = base.passthru.function; + }; + in + withStdOverrides; # shortcut for attrByPath ["name"] default attrs maybeAttrNullable = maybeAttr; # shortcut for attrByPath ["name"] default attrs - maybeAttr = name: default: attrs: attrs.${name} or default; + maybeAttr = + name: default: attrs: + attrs.${name} or default; # Return the second argument if the first one is true or the empty version # of the second argument. - ifEnable = cond: val: + ifEnable = + cond: val: if cond then val else if builtins.isList val then [ ] else if builtins.isAttrs val then { } - # else if builtins.isString val then "" + # else if builtins.isString val then "" else if val == true || val == false then false else null; # Return true only if there is an attribute and it is true. - checkFlag = attrSet: name: + checkFlag = + attrSet: name: if name == "true" then true else if name == "false" then @@ -60,57 +104,88 @@ let # Input : attrSet, [ [name default] ... ], name # Output : its value or default. - getValue = attrSet: argList: name: - (attrByPath [ name ] (if checkFlag attrSet name then - true - else if argList == [ ] then - null - else - let x = builtins.head argList; - in if (head x) == name then - (head (tail x)) + getValue = + attrSet: argList: name: + (attrByPath [ name ] ( + if checkFlag attrSet name then + true + else if argList == [ ] then + null else - (getValue attrSet (tail argList) name)) attrSet); + let + x = builtins.head argList; + in + if (head x) == name then (head (tail x)) else (getValue attrSet (tail argList) name) + ) attrSet); # Input : attrSet, [[name default] ...], [ [flagname reqs..] ... ] # Output : are reqs satisfied? It's asserted. - checkReqs = attrSet: argList: condList: - (foldr and true (map (x: - let name = head x; - in ((checkFlag attrSet name) -> (foldr and true (map (y: - let val = getValue attrSet argList y; - in (val != null) && (val != false)) (tail x))))) condList)); + checkReqs = + attrSet: argList: condList: + (foldr and true ( + map ( + x: + let + name = head x; + in + ( + (checkFlag attrSet name) + -> (foldr and true ( + map ( + y: + let + val = getValue attrSet argList y; + in + (val != null) && (val != false) + ) (tail x) + )) + ) + ) condList + )); # This function has O(n^2) performance. - uniqList = { inputList, acc ? [ ], }: + uniqList = + { + inputList, + acc ? [ ], + }: let - go = xs: acc: + go = + xs: acc: if xs == [ ] then [ ] else let x = head xs; y = if elem x acc then [ ] else [ x ]; - in y ++ go (tail xs) (y ++ acc); - in go inputList acc; + in + y ++ go (tail xs) (y ++ acc); + in + go inputList acc; uniqListExt = - { inputList, outputList ? [ ], getter ? (x: x), compare ? (x: y: x == y), }: + { + inputList, + outputList ? [ ], + getter ? (x: x), + compare ? (x: y: x == y), + }: if inputList == [ ] then outputList else let x = head inputList; isX = y: (compare (getter y) (getter x)); - newOutputList = outputList - ++ (if any isX outputList then [ ] else [ x ]); - in uniqListExt { + newOutputList = outputList ++ (if any isX outputList then [ ] else [ x ]); + in + uniqListExt { outputList = newOutputList; inputList = tail inputList; inherit getter compare; }; - condConcat = name: list: checker: + condConcat = + name: list: checker: if list == [ ] then name else if checker (head list) then @@ -118,45 +193,54 @@ let else condConcat name (tail (tail list)) checker; - lazyGenericClosure = { startSet, operator, }: + lazyGenericClosure = + { startSet, operator }: let - work = list: doneKeys: result: + work = + list: doneKeys: result: if list == [ ] then result else let x = head list; key = x.key; - in if elem key doneKeys then + in + if elem key doneKeys then work (tail list) doneKeys result else - work (tail list ++ operator x) ([ key ] ++ doneKeys) - ([ x ] ++ result); - in work startSet [ ] [ ]; + work (tail list ++ operator x) ([ key ] ++ doneKeys) ([ x ] ++ result); + in + work startSet [ ] [ ]; - innerModifySumArgs = f: x: a: b: + innerModifySumArgs = + f: x: a: b: if b == null then (f a b) // x else innerModifySumArgs f x (a // b); modifySumArgs = f: x: innerModifySumArgs f x { }; - innerClosePropagation = acc: xs: + innerClosePropagation = + acc: xs: if xs == [ ] then acc else let y = head xs; ys = tail xs; - in if !isAttrs y then + in + if !isAttrs y then innerClosePropagation acc ys else - let acc' = [ y ] ++ acc; - in innerClosePropagation acc' (uniqList { - inputList = (maybeAttrNullable "propagatedBuildInputs" [ ] y) - ++ (maybeAttrNullable "propagatedNativeBuildInputs" [ ] y) ++ ys; + let + acc' = [ y ] ++ acc; + in + innerClosePropagation acc' (uniqList { + inputList = + (maybeAttrNullable "propagatedBuildInputs" [ ] y) + ++ (maybeAttrNullable "propagatedNativeBuildInputs" [ ] y) + ++ ys; acc = acc'; }); - closePropagationSlow = list: - (uniqList { inputList = innerClosePropagation [ ] list; }); + closePropagationSlow = list: (uniqList { inputList = innerClosePropagation [ ] list; }); # This is an optimisation of closePropagation which avoids the O(n^2) behavior # Using a list of derivations, it generates the full closure of the propagatedXXXBuildInputs @@ -164,29 +248,35 @@ let # attribute of each derivation. # On some benchmarks, it performs up to 15 times faster than closePropagation. # See https://github.com/NixOS/nixpkgs/pull/194391 for details. - closePropagationFast = list: - builtins.map (x: x.val) (builtins.genericClosure { - startSet = builtins.map (x: { - key = x.outPath; - val = x; - }) (builtins.filter (x: x != null) list); - operator = item: - if !builtins.isAttrs item.val then - [ ] - else - builtins.concatMap (x: - if x != null then [{ - key = x.outPath; - val = x; - }] else - [ ]) ((item.val.propagatedBuildInputs or [ ]) - ++ (item.val.propagatedNativeBuildInputs or [ ])); - }); + closePropagationFast = + list: + builtins.map (x: x.val) ( + builtins.genericClosure { + startSet = builtins.map (x: { + key = x.outPath; + val = x; + }) (builtins.filter (x: x != null) list); + operator = + item: + if !builtins.isAttrs item.val then + [ ] + else + builtins.concatMap ( + x: + if x != null then + [ + { + key = x.outPath; + val = x; + } + ] + else + [ ] + ) ((item.val.propagatedBuildInputs or [ ]) ++ (item.val.propagatedNativeBuildInputs or [ ])); + } + ); - closePropagation = if builtins ? genericClosure then - closePropagationFast - else - closePropagationSlow; + closePropagation = if builtins ? genericClosure then closePropagationFast else closePropagationSlow; # calls a function (f attr value ) for each record item. returns a list mapAttrsFlatten = f: r: map (attr: f attr r.${attr}) (attrNames r); @@ -194,26 +284,29 @@ let # attribute set containing one attribute nvs = name: value: listToAttrs [ (nameValuePair name value) ]; # adds / replaces an attribute of an attribute set - setAttr = set: name: v: set // (nvs name v); + setAttr = + set: name: v: + set // (nvs name v); # setAttrMerge (similar to mergeAttrsWithFunc but only merges the values of a particular name) # setAttrMerge "a" [] { a = [2];} (x: x ++ [3]) -> { a = [2 3]; } # setAttrMerge "a" [] { } (x: x ++ [3]) -> { a = [ 3]; } - setAttrMerge = name: default: attrs: f: + setAttrMerge = + name: default: attrs: f: setAttr attrs name (f (maybeAttr name default attrs)); # Using f = a: b = b the result is similar to // # merge attributes with custom function handling the case that the attribute # exists in both sets - mergeAttrsWithFunc = f: set1: set2: - foldr - (n: set: if set ? ${n} then setAttr set n (f set.${n} set2.${n}) else set) - (set2 // set1) (attrNames set2); + mergeAttrsWithFunc = + f: set1: set2: + foldr (n: set: if set ? ${n} then setAttr set n (f set.${n} set2.${n}) else set) (set2 // set1) ( + attrNames set2 + ); # merging two attribute set concatenating the values of same attribute names # eg { a = 7; } { a = [ 2 3 ]; } becomes { a = [ 7 2 3 ]; } - mergeAttrsConcatenateValues = - mergeAttrsWithFunc (a: b: (toList a) ++ (toList b)); + mergeAttrsConcatenateValues = mergeAttrsWithFunc (a: b: (toList a) ++ (toList b)); # merges attributes using //, if a name exists in both attributes # an error will be triggered unless its listed in mergeLists @@ -223,23 +316,30 @@ let # in these cases the first buildPhase will override the second one # ! deprecated, use mergeAttrByFunc instead mergeAttrsNoOverride = - { mergeLists ? [ "buildInputs" "propagatedBuildInputs" ] - , overrideSnd ? [ "buildPhase" ], }: + { + mergeLists ? [ + "buildInputs" + "propagatedBuildInputs" + ], + overrideSnd ? [ "buildPhase" ], + }: attrs1: attrs2: - foldr (n: set: - setAttr set n (if set ? ${n} then # merge - if elem n - mergeLists # attribute contains list, merge them by concatenating - then - attrs2.${n} ++ attrs1.${n} - else if elem n overrideSnd then - attrs1.${n} + foldr ( + n: set: + setAttr set n ( + if set ? ${n} then # merge + if + elem n mergeLists # attribute contains list, merge them by concatenating + then + attrs2.${n} ++ attrs1.${n} + else if elem n overrideSnd then + attrs1.${n} + else + throw "error mergeAttrsNoOverride, attribute ${n} given in both attributes - no merge func defined" else - 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); + attrs2.${n} # add attribute not existing in attr1 + ) + ) attrs1 (attrNames attrs2); # example usage: # mergeAttrByFunc { @@ -252,50 +352,78 @@ let # { mergeAttrsBy = [...]; buildInputs = [ a b c d ]; } # is used by defaultOverridableDelayableArgs and can be used when composing using # foldArgs, composedArgsAndFun or applyAndFun. Example: composableDerivation in all-packages.nix - mergeAttrByFunc = x: y: + mergeAttrByFunc = + x: y: let mergeAttrBy2 = { mergeAttrBy = mergeAttrs; } // (maybeAttr "mergeAttrBy" { } x) // (maybeAttr "mergeAttrBy" { } y); - in foldr mergeAttrs { } [ + in + foldr mergeAttrs { } [ x y - (mapAttrs (a: v: - # merge special names using given functions - if x ? ${a} then - if y ? ${a} then - v x.${a} y.${a} # both have attr, use merge func + (mapAttrs + ( + a: v: + # merge special names using given functions + if x ? ${a} then + if y ? ${a} then + v x.${a} y.${a} # both have attr, use merge func + else + x.${a} # only x has attr else - x.${a} # only x has attr - else - y.${a} # only y has attr) - ) (removeAttrs mergeAttrBy2 - # don't merge attrs which are neither in x nor y - (filter (a: !x ? ${a} && !y ? ${a}) (attrNames mergeAttrBy2)))) + y.${a} # only y has attr) + ) + ( + removeAttrs mergeAttrBy2 + # don't merge attrs which are neither in x nor y + (filter (a: !x ? ${a} && !y ? ${a}) (attrNames mergeAttrBy2)) + ) + ) ]; mergeAttrsByFuncDefaults = foldl mergeAttrByFunc { inherit mergeAttrBy; }; - mergeAttrsByFuncDefaultsClean = list: - removeAttrs (mergeAttrsByFuncDefaults list) [ "mergeAttrBy" ]; + mergeAttrsByFuncDefaultsClean = list: removeAttrs (mergeAttrsByFuncDefaults list) [ "mergeAttrBy" ]; # sane defaults (same name as attr name so that inherit can be used) mergeAttrBy = # { buildInputs = concatList; [...]; passthru = mergeAttr; [..]; } - listToAttrs (map (n: nameValuePair n concat) [ - "nativeBuildInputs" - "buildInputs" - "propagatedBuildInputs" - "configureFlags" - "prePhases" - "postAll" - "patches" - ]) // listToAttrs - (map (n: nameValuePair n mergeAttrs) [ "passthru" "meta" "cfg" "flags" ]) - // listToAttrs (map (n: - nameValuePair n (a: b: '' - ${a} - ${b}'')) [ "preConfigure" "postInstall" ]); + listToAttrs ( + map (n: nameValuePair n concat) [ + "nativeBuildInputs" + "buildInputs" + "propagatedBuildInputs" + "configureFlags" + "prePhases" + "postAll" + "patches" + ] + ) + // listToAttrs ( + map (n: nameValuePair n mergeAttrs) [ + "passthru" + "meta" + "cfg" + "flags" + ] + ) + // listToAttrs ( + map + ( + n: + nameValuePair n ( + a: b: '' + ${a} + ${b}'' + ) + ) + [ + "preConfigure" + "postInstall" + ] + ); - nixType = x: + nixType = + x: if isAttrs x then if x ? outPath then "derivation" else "attrs" else if isFunction x then @@ -313,30 +441,60 @@ let else "string"; - /* * - # Deprecated + /* + * + # Deprecated - For historical reasons, imap has an index starting at 1. + For historical reasons, imap has an index starting at 1. - But for consistency with the rest of the library we want an index - starting at zero. + But for consistency with the rest of the library we want an index + starting at zero. */ imap = imap1; # Fake hashes. Can be used as hash placeholders, when computing hash ahead isn't trivial fakeHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; - fakeSha256 = - "0000000000000000000000000000000000000000000000000000000000000000"; - fakeSha512 = - "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; - # Everything in this attrset is the public interface of the file. -in { - inherit checkFlag checkReqs closePropagation closePropagationFast - closePropagationSlow condConcat defaultMerge defaultMergeArg fakeHash - fakeSha256 fakeSha512 foldArgs getValue ifEnable imap innerClosePropagation - innerModifySumArgs lazyGenericClosure mapAttrsFlatten maybeAttr - maybeAttrNullable maybeEnv mergeAttrBy mergeAttrByFunc - mergeAttrsByFuncDefaults mergeAttrsByFuncDefaultsClean - mergeAttrsConcatenateValues mergeAttrsNoOverride mergeAttrsWithFunc - modifySumArgs nixType nvs setAttr setAttrMerge uniqList uniqListExt; + fakeSha256 = "0000000000000000000000000000000000000000000000000000000000000000"; + fakeSha512 = "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; +in +# Everything in this attrset is the public interface of the file. +{ + inherit + checkFlag + checkReqs + closePropagation + closePropagationFast + closePropagationSlow + condConcat + defaultMerge + defaultMergeArg + fakeHash + fakeSha256 + fakeSha512 + foldArgs + getValue + ifEnable + imap + innerClosePropagation + innerModifySumArgs + lazyGenericClosure + mapAttrsFlatten + maybeAttr + maybeAttrNullable + maybeEnv + mergeAttrBy + mergeAttrByFunc + mergeAttrsByFuncDefaults + mergeAttrsByFuncDefaultsClean + mergeAttrsConcatenateValues + mergeAttrsNoOverride + mergeAttrsWithFunc + modifySumArgs + nixType + nvs + setAttr + setAttrMerge + uniqList + uniqListExt + ; } diff --git a/nix/stdlib/derivations.nix b/nix/stdlib/derivations.nix index d94b67d..7524bf1 100644 --- a/nix/stdlib/derivations.nix +++ b/nix/stdlib/derivations.nix @@ -2,132 +2,131 @@ let inherit (lib) genAttrs isString throwIfNot; - showMaybeAttrPosPre = prefix: attrName: v: - let pos = builtins.unsafeGetAttrPos attrName v; - in if pos == null then - "" - else - "${prefix}${pos.file}:${toString pos.line}:${toString pos.column}"; + showMaybeAttrPosPre = + prefix: attrName: v: + let + pos = builtins.unsafeGetAttrPos attrName v; + in + if pos == null then "" else "${prefix}${pos.file}:${toString pos.line}:${toString pos.column}"; - showMaybePackagePosPre = prefix: pkg: - if pkg ? meta.position && isString pkg.meta.position then - "${prefix}${pkg.meta.position}" - else - ""; -in { - /* Restrict a derivation to a predictable set of attribute names, so - that the returned attrset is not strict in the actual derivation, - saving a lot of computation when the derivation is non-trivial. + showMaybePackagePosPre = + prefix: pkg: + if pkg ? meta.position && isString pkg.meta.position then "${prefix}${pkg.meta.position}" else ""; +in +{ + /* + Restrict a derivation to a predictable set of attribute names, so + that the returned attrset is not strict in the actual derivation, + saving a lot of computation when the derivation is non-trivial. - This is useful in situations where a derivation might only be used for its - passthru attributes, improving evaluation performance. + This is useful in situations where a derivation might only be used for its + passthru attributes, improving evaluation performance. - The returned attribute set is lazy in `derivation`. Specifically, this - means that the derivation will not be evaluated in at least the - situations below. + The returned attribute set is lazy in `derivation`. Specifically, this + means that the derivation will not be evaluated in at least the + situations below. - For illustration and/or testing, we define derivation such that its - evaluation is very noticeable. + For illustration and/or testing, we define derivation such that its + evaluation is very noticeable. - let derivation = throw "This won't be evaluated."; + let derivation = throw "This won't be evaluated."; - In the following expressions, `derivation` will _not_ be evaluated: + In the following expressions, `derivation` will _not_ be evaluated: - (lazyDerivation { inherit derivation; }).type + (lazyDerivation { inherit derivation; }).type - attrNames (lazyDerivation { inherit derivation; }) + attrNames (lazyDerivation { inherit derivation; }) - (lazyDerivation { inherit derivation; } // { foo = true; }).foo + (lazyDerivation { inherit derivation; } // { foo = true; }).foo - (lazyDerivation { inherit derivation; meta.foo = true; }).meta + (lazyDerivation { inherit derivation; meta.foo = true; }).meta - In these expressions, `derivation` _will_ be evaluated: + In these expressions, `derivation` _will_ be evaluated: - "${lazyDerivation { inherit derivation }}" + "${lazyDerivation { inherit derivation }}" - (lazyDerivation { inherit derivation }).outPath + (lazyDerivation { inherit derivation }).outPath - (lazyDerivation { inherit derivation }).meta + (lazyDerivation { inherit derivation }).meta - And the following expressions are not valid, because the refer to - implementation details and/or attributes that may not be present on - some derivations: + And the following expressions are not valid, because the refer to + implementation details and/or attributes that may not be present on + some derivations: - (lazyDerivation { inherit derivation }).buildInputs + (lazyDerivation { inherit derivation }).buildInputs - (lazyDerivation { inherit derivation }).passthru + (lazyDerivation { inherit derivation }).passthru - (lazyDerivation { inherit derivation }).pythonPath + (lazyDerivation { inherit derivation }).pythonPath */ - lazyDerivation = args@{ - # The derivation to be wrapped. - derivation, - # Optional meta attribute. - # - # While this function is primarily about derivations, it can improve - # the `meta` package attribute, which is usually specified through - # `mkDerivation`. - meta ? null, - # Optional extra values to add to the returned attrset. - # - # This can be used for adding package attributes, such as `tests`. - passthru ? { }, - # Optional list of assumed outputs. Default: ["out"] - # - # This must match the set of outputs that the returned derivation has. - # You must use this when the derivation has multiple outputs. - outputs ? [ "out" ], }: + lazyDerivation = + args@{ + # The derivation to be wrapped. + derivation, + # Optional meta attribute. + # + # While this function is primarily about derivations, it can improve + # the `meta` package attribute, which is usually specified through + # `mkDerivation`. + meta ? null, + # Optional extra values to add to the returned attrset. + # + # This can be used for adding package attributes, such as `tests`. + passthru ? { }, + # Optional list of assumed outputs. Default: ["out"] + # + # This must match the set of outputs that the returned derivation has. + # You must use this when the derivation has multiple outputs. + outputs ? [ "out" ], + }: let # These checks are strict in `drv` and some `drv` attributes, but the # attrset spine returned by lazyDerivation does not depend on it. # Instead, the individual derivation attributes do depend on it. - checked = throwIfNot (derivation.type or null == "derivation") - "lazyDerivation: input must be a derivation." throwIfNot - # NOTE: Technically we could require our outputs to be a subset of the - # actual ones, or even leave them unchecked and fail on a lazy basis. - # However, consider the case where an output is added in the underlying - # derivation, such as dev. lazyDerivation would remove it and cause it - # to fail as a buildInputs item, without any indication as to what - # happened. Hence the more stringent condition. We could consider - # adding a flag to control this behavior if there's a valid case for it, - # but the documentation must have a note like this. - (derivation.outputs == outputs) '' - lib.lazyDerivation: The derivation ${ - derivation.name or "" - } has outputs that don't match the assumed outputs. - - Assumed outputs passed to lazyDerivation${ - showMaybeAttrPosPre '' - , - at '' "outputs" args - }: - ${lib.generators.toPretty { multiline = false; } outputs}; - - Actual outputs of the derivation${ - showMaybePackagePosPre '' + checked = + throwIfNot (derivation.type or null == "derivation") "lazyDerivation: input must be a derivation." + throwIfNot + # NOTE: Technically we could require our outputs to be a subset of the + # actual ones, or even leave them unchecked and fail on a lazy basis. + # However, consider the case where an output is added in the underlying + # derivation, such as dev. lazyDerivation would remove it and cause it + # to fail as a buildInputs item, without any indication as to what + # happened. Hence the more stringent condition. We could consider + # adding a flag to control this behavior if there's a valid case for it, + # but the documentation must have a note like this. + (derivation.outputs == outputs) + '' + lib.lazyDerivation: The derivation ${derivation.name or ""} has outputs that don't match the assumed outputs. + + Assumed outputs passed to lazyDerivation${ + showMaybeAttrPosPre '' + , + at '' "outputs" args + }: + ${lib.generators.toPretty { multiline = false; } outputs}; + + Actual outputs of the derivation${showMaybePackagePosPre '' , - defined at '' derivation - }: - ${ - lib.generators.toPretty { multiline = false; } - derivation.outputs - } - - If the outputs are known ahead of evaluating the derivation, - then update the lazyDerivation call to match the actual outputs, in the same order. - If lazyDerivation is passed a literal value, just change it to the actual outputs. - As a result it will work as before / as intended. - - Otherwise, when the outputs are dynamic and can't be known ahead of time, it won't - be possible to add laziness, but lib.lazyDerivation may still be useful for trimming - the attributes. - If you want to keep trimming the attributes, make sure that the package is in a - variable (don't evaluate it twice!) and pass the variable and its outputs attribute - to lib.lazyDerivation. This largely defeats laziness, but keeps the trimming. - If none of the above works for you, replace the lib.lazyDerivation call by the - expression in the derivation argument. - '' derivation; - in { + defined at '' derivation}: + ${lib.generators.toPretty { multiline = false; } derivation.outputs} + + If the outputs are known ahead of evaluating the derivation, + then update the lazyDerivation call to match the actual outputs, in the same order. + If lazyDerivation is passed a literal value, just change it to the actual outputs. + As a result it will work as before / as intended. + + Otherwise, when the outputs are dynamic and can't be known ahead of time, it won't + be possible to add laziness, but lib.lazyDerivation may still be useful for trimming + the attributes. + If you want to keep trimming the attributes, make sure that the package is in a + variable (don't evaluate it twice!) and pass the variable and its outputs attribute + to lib.lazyDerivation. This largely defeats laziness, but keeps the trimming. + If none of the above works for you, replace the lib.lazyDerivation call by the + expression in the derivation argument. + '' + derivation; + in + { # Hardcoded `type` # # `lazyDerivation` requires its `derivation` argument to be a derivation, @@ -141,33 +140,42 @@ in { # A fixed set of derivation values, so that `lazyDerivation` can return # its attrset before evaluating `derivation`. # This must only list attributes that are available on _all_ derivations. - inherit (checked) outPath outputName drvPath name system; + inherit (checked) + outPath + outputName + drvPath + name + system + ; inherit outputs; # The meta attribute can either be taken from the derivation, or if the # `lazyDerivation` caller knew a shortcut, be taken from there. meta = args.meta or checked.meta; - } // genAttrs outputs (outputName: checked.${outputName}) // passthru; - - /* Conditionally set a derivation attribute. - - Because `mkDerivation` sets `__ignoreNulls = true`, a derivation - attribute set to `null` will not impact the derivation output hash. - Thus, this function passes through its `value` argument if the `cond` - is `true`, but returns `null` if not. - - Type: optionalDrvAttr :: Bool -> a -> a | Null - - Example: - (stdenv.mkDerivation { - name = "foo"; - x = optionalDrvAttr true 1; - y = optionalDrvAttr false 1; - }).drvPath == (stdenv.mkDerivation { - name = "foo"; - x = 1; - }).drvPath - => true + } + // genAttrs outputs (outputName: checked.${outputName}) + // passthru; + + /* + Conditionally set a derivation attribute. + + Because `mkDerivation` sets `__ignoreNulls = true`, a derivation + attribute set to `null` will not impact the derivation output hash. + Thus, this function passes through its `value` argument if the `cond` + is `true`, but returns `null` if not. + + Type: optionalDrvAttr :: Bool -> a -> a | Null + + Example: + (stdenv.mkDerivation { + name = "foo"; + x = optionalDrvAttr true 1; + y = optionalDrvAttr false 1; + }).drvPath == (stdenv.mkDerivation { + name = "foo"; + x = 1; + }).drvPath + => true */ optionalDrvAttr = # Condition diff --git a/nix/stdlib/fetchers.nix b/nix/stdlib/fetchers.nix index 37b94e1..e960e34 100644 --- a/nix/stdlib/fetchers.nix +++ b/nix/stdlib/fetchers.nix @@ -1,5 +1,6 @@ # snippets that can be shared by multiple fetchers (pkgs/build-support) -{ lib }: { +{ lib }: +{ proxyImpureEnvVars = [ # We borrow these environment variables from the caller to allow # easy proxy configuration. This is impure, but a fixed-output diff --git a/nix/stdlib/fileset/default.nix b/nix/stdlib/fileset/default.nix index ab63031..1224406 100644 --- a/nix/stdlib/fileset/default.nix +++ b/nix/stdlib/fileset/default.nix @@ -1,108 +1,129 @@ -/* - []{#sec-fileset} +/* + + []{#sec-fileset} - The [`lib.fileset`](#sec-functions-library-fileset) library allows you to work with _file sets_. - A file set is a (mathematical) set of local files that can be added to the Nix store for use in Nix derivations. - File sets are easy and safe to use, providing obvious and composable semantics with good error messages to prevent mistakes. + The [`lib.fileset`](#sec-functions-library-fileset) library allows you to work with _file sets_. + A file set is a (mathematical) set of local files that can be added to the Nix store for use in Nix derivations. + File sets are easy and safe to use, providing obvious and composable semantics with good error messages to prevent mistakes. - ## Overview {#sec-fileset-overview} + ## Overview {#sec-fileset-overview} - Basics: - - [Implicit coercion from paths to file sets](#sec-fileset-path-coercion) + Basics: + - [Implicit coercion from paths to file sets](#sec-fileset-path-coercion) - - [`lib.fileset.maybeMissing`](#function-library-lib.fileset.maybeMissing): + - [`lib.fileset.maybeMissing`](#function-library-lib.fileset.maybeMissing): - Create a file set from a path that may be missing. + Create a file set from a path that may be missing. - - [`lib.fileset.trace`](#function-library-lib.fileset.trace)/[`lib.fileset.traceVal`](#function-library-lib.fileset.trace): + - [`lib.fileset.trace`](#function-library-lib.fileset.trace)/[`lib.fileset.traceVal`](#function-library-lib.fileset.trace): - Pretty-print file sets for debugging. + Pretty-print file sets for debugging. - - [`lib.fileset.toSource`](#function-library-lib.fileset.toSource): + - [`lib.fileset.toSource`](#function-library-lib.fileset.toSource): - Add files in file sets to the store to use as derivation sources. + Add files in file sets to the store to use as derivation sources. - - [`lib.fileset.toList`](#function-library-lib.fileset.toList): + - [`lib.fileset.toList`](#function-library-lib.fileset.toList): - The list of files contained in a file set. + The list of files contained in a file set. - Combinators: - - [`lib.fileset.union`](#function-library-lib.fileset.union)/[`lib.fileset.unions`](#function-library-lib.fileset.unions): + Combinators: + - [`lib.fileset.union`](#function-library-lib.fileset.union)/[`lib.fileset.unions`](#function-library-lib.fileset.unions): - Create a larger file set from all the files in multiple file sets. + Create a larger file set from all the files in multiple file sets. - - [`lib.fileset.intersection`](#function-library-lib.fileset.intersection): + - [`lib.fileset.intersection`](#function-library-lib.fileset.intersection): - Create a smaller file set from only the files in both file sets. + Create a smaller file set from only the files in both file sets. - - [`lib.fileset.difference`](#function-library-lib.fileset.difference): + - [`lib.fileset.difference`](#function-library-lib.fileset.difference): - Create a smaller file set containing all files that are in one file set, but not another one. + Create a smaller file set containing all files that are in one file set, but not another one. - Filtering: - - [`lib.fileset.fileFilter`](#function-library-lib.fileset.fileFilter): + Filtering: + - [`lib.fileset.fileFilter`](#function-library-lib.fileset.fileFilter): - Create a file set from all files that satisisfy a predicate in a directory. + Create a file set from all files that satisisfy a predicate in a directory. - Utilities: - - [`lib.fileset.fromSource`](#function-library-lib.fileset.fromSource): + Utilities: + - [`lib.fileset.fromSource`](#function-library-lib.fileset.fromSource): - Create a file set from a `lib.sources`-based value. + Create a file set from a `lib.sources`-based value. - - [`lib.fileset.gitTracked`](#function-library-lib.fileset.gitTracked)/[`lib.fileset.gitTrackedWith`](#function-library-lib.fileset.gitTrackedWith): + - [`lib.fileset.gitTracked`](#function-library-lib.fileset.gitTracked)/[`lib.fileset.gitTrackedWith`](#function-library-lib.fileset.gitTrackedWith): - Create a file set from all tracked files in a local Git repository. + Create a file set from all tracked files in a local Git repository. - If you need more file set functions, - see [this issue](https://github.com/NixOS/nixpkgs/issues/266356) to request it. + If you need more file set functions, + see [this issue](https://github.com/NixOS/nixpkgs/issues/266356) to request it. - ## Implicit coercion from paths to file sets {#sec-fileset-path-coercion} + ## Implicit coercion from paths to file sets {#sec-fileset-path-coercion} - All functions accepting file sets as arguments can also accept [paths](https://nixos.org/manual/nix/stable/language/values.html#type-path) as arguments. - Such path arguments are implicitly coerced to file sets containing all files under that path: - - A path to a file turns into a file set containing that single file. - - A path to a directory turns into a file set containing all files _recursively_ in that directory. + All functions accepting file sets as arguments can also accept [paths](https://nixos.org/manual/nix/stable/language/values.html#type-path) as arguments. + Such path arguments are implicitly coerced to file sets containing all files under that path: + - A path to a file turns into a file set containing that single file. + - A path to a directory turns into a file set containing all files _recursively_ in that directory. - If the path points to a non-existent location, an error is thrown. + If the path points to a non-existent location, an error is thrown. - ::: {.note} - Just like in Git, file sets cannot represent empty directories. - Because of this, a path to a directory that contains no files (recursively) will turn into a file set containing no files. - ::: + ::: {.note} + Just like in Git, file sets cannot represent empty directories. + Because of this, a path to a directory that contains no files (recursively) will turn into a file set containing no files. + ::: - :::{.note} - File set coercion does _not_ add any of the files under the coerced paths to the store. - Only the [`toSource`](#function-library-lib.fileset.toSource) function adds files to the Nix store, and only those files contained in the `fileset` argument. - This is in contrast to using [paths in string interpolation](https://nixos.org/manual/nix/stable/language/values.html#type-path), which does add the entire referenced path to the store. - ::: + :::{.note} + File set coercion does _not_ add any of the files under the coerced paths to the store. + Only the [`toSource`](#function-library-lib.fileset.toSource) function adds files to the Nix store, and only those files contained in the `fileset` argument. + This is in contrast to using [paths in string interpolation](https://nixos.org/manual/nix/stable/language/values.html#type-path), which does add the entire referenced path to the store. + ::: - ### Example {#sec-fileset-path-coercion-example} + ### Example {#sec-fileset-path-coercion-example} - Assume we are in a local directory with a file hierarchy like this: - ``` - ├─ a/ - │ ├─ x (file) - │ └─ b/ - │   └─ y (file) - └─ c/ -   └─ d/ - ``` + Assume we are in a local directory with a file hierarchy like this: + ``` + ├─ a/ + │ ├─ x (file) + │ └─ b/ + │   └─ y (file) + └─ c/ +   └─ d/ + ``` - Here's a listing of which files get included when different path expressions get coerced to file sets: - - `./.` as a file set contains both `a/x` and `a/b/y` (`c/` does not contain any files and is therefore omitted). - - `./a` as a file set contains both `a/x` and `a/b/y`. - - `./a/x` as a file set contains only `a/x`. - - `./a/b` as a file set contains only `a/b/y`. - - `./c` as a file set is empty, since neither `c` nor `c/d` contain any files. + Here's a listing of which files get included when different path expressions get coerced to file sets: + - `./.` as a file set contains both `a/x` and `a/b/y` (`c/` does not contain any files and is therefore omitted). + - `./a` as a file set contains both `a/x` and `a/b/y`. + - `./a/x` as a file set contains only `a/x`. + - `./a/b` as a file set contains only `a/b/y`. + - `./c` as a file set is empty, since neither `c` nor `c/d` contain any files. */ { lib }: let inherit (import ./internal.nix { inherit lib; }) - _coerce _singleton _coerceMany _toSourceFilter _fromSourceFilter _toList - _unionMany _fileFilter _printFileset _intersection _difference _fromFetchGit - _fetchGitSubmodulesMinver _emptyWithoutBase; - - inherit (builtins) isBool isList isPath pathExists seq typeOf nixVersion; + _coerce + _singleton + _coerceMany + _toSourceFilter + _fromSourceFilter + _toList + _unionMany + _fileFilter + _printFileset + _intersection + _difference + _fromFetchGit + _fetchGitSubmodulesMinver + _emptyWithoutBase + ; + + inherit (builtins) + isBool + isList + isPath + pathExists + seq + typeOf + nixVersion + ; inherit (lib.lists) elemAt imap0; @@ -115,211 +136,222 @@ let inherit (lib.sources) cleanSourceWith; inherit (lib.trivial) isFunction pipe; -in { - /* Create a file set from a path that may or may not exist: - - If the path does exist, the path is [coerced to a file set](#sec-fileset-path-coercion). - - If the path does not exist, a file set containing no files is returned. - - Type: - maybeMissing :: Path -> FileSet - - Example: - # All files in the current directory, but excluding main.o if it exists - difference ./. (maybeMissing ./main.o) +in +{ + /* + Create a file set from a path that may or may not exist: + - If the path does exist, the path is [coerced to a file set](#sec-fileset-path-coercion). + - If the path does not exist, a file set containing no files is returned. + + Type: + maybeMissing :: Path -> FileSet + + Example: + # All files in the current directory, but excluding main.o if it exists + difference ./. (maybeMissing ./main.o) */ - maybeMissing = path: + maybeMissing = + path: if !isPath path then if isStringLike path then - throw '' - lib.fileset.maybeMissing: Argument ("${ - toString path - }") is a string-like value, but it should be a path instead.'' + throw ''lib.fileset.maybeMissing: Argument ("${toString path}") is a string-like value, but it should be a path instead.'' else - throw "lib.fileset.maybeMissing: Argument is of type ${ - typeOf path - }, but it should be a path instead." + throw "lib.fileset.maybeMissing: Argument is of type ${typeOf path}, but it should be a path instead." else if !pathExists path then _emptyWithoutBase else _singleton path; - /* Incrementally evaluate and trace a file set in a pretty way. - This function is only intended for debugging purposes. - The exact tracing format is unspecified and may change. - - This function takes a final argument to return. - In comparison, [`traceVal`](#function-library-lib.fileset.traceVal) returns - the given file set argument. - - This variant is useful for tracing file sets in the Nix repl. - - Type: - trace :: FileSet -> Any -> Any - - Example: - trace (unions [ ./Makefile ./src ./tests/run.sh ]) null - => - trace: /home/user/src/myProject - trace: - Makefile (regular) - trace: - src (all files in directory) - trace: - tests - trace: - run.sh (regular) - null + /* + Incrementally evaluate and trace a file set in a pretty way. + This function is only intended for debugging purposes. + The exact tracing format is unspecified and may change. + + This function takes a final argument to return. + In comparison, [`traceVal`](#function-library-lib.fileset.traceVal) returns + the given file set argument. + + This variant is useful for tracing file sets in the Nix repl. + + Type: + trace :: FileSet -> Any -> Any + + Example: + trace (unions [ ./Makefile ./src ./tests/run.sh ]) null + => + trace: /home/user/src/myProject + trace: - Makefile (regular) + trace: - src (all files in directory) + trace: - tests + trace: - run.sh (regular) + null */ trace = - /* The file set to trace. + /* + The file set to trace. - This argument can also be a path, - which gets [implicitly coerced to a file set](#sec-fileset-path-coercion). + This argument can also be a path, + which gets [implicitly coerced to a file set](#sec-fileset-path-coercion). */ fileset: let # "fileset" would be a better name, but that would clash with the argument name, # and we cannot change that because of https://github.com/nix-community/nixdoc/issues/76 actualFileset = _coerce "lib.fileset.trace: Argument" fileset; - in seq (_printFileset actualFileset) (x: x); - - /* Incrementally evaluate and trace a file set in a pretty way. - This function is only intended for debugging purposes. - The exact tracing format is unspecified and may change. - - This function returns the given file set. - In comparison, [`trace`](#function-library-lib.fileset.trace) takes another argument to return. - - This variant is useful for tracing file sets passed as arguments to other functions. - - Type: - traceVal :: FileSet -> FileSet - - Example: - toSource { - root = ./.; - fileset = traceVal (unions [ - ./Makefile - ./src - ./tests/run.sh - ]); - } - => - trace: /home/user/src/myProject - trace: - Makefile (regular) - trace: - src (all files in directory) - trace: - tests - trace: - run.sh (regular) - "/nix/store/...-source" + in + seq (_printFileset actualFileset) (x: x); + + /* + Incrementally evaluate and trace a file set in a pretty way. + This function is only intended for debugging purposes. + The exact tracing format is unspecified and may change. + + This function returns the given file set. + In comparison, [`trace`](#function-library-lib.fileset.trace) takes another argument to return. + + This variant is useful for tracing file sets passed as arguments to other functions. + + Type: + traceVal :: FileSet -> FileSet + + Example: + toSource { + root = ./.; + fileset = traceVal (unions [ + ./Makefile + ./src + ./tests/run.sh + ]); + } + => + trace: /home/user/src/myProject + trace: - Makefile (regular) + trace: - src (all files in directory) + trace: - tests + trace: - run.sh (regular) + "/nix/store/...-source" */ traceVal = - /* The file set to trace and return. + /* + The file set to trace and return. - This argument can also be a path, - which gets [implicitly coerced to a file set](#sec-fileset-path-coercion). + This argument can also be a path, + which gets [implicitly coerced to a file set](#sec-fileset-path-coercion). */ fileset: let # "fileset" would be a better name, but that would clash with the argument name, # and we cannot change that because of https://github.com/nix-community/nixdoc/issues/76 actualFileset = _coerce "lib.fileset.traceVal: Argument" fileset; - in seq (_printFileset actualFileset) - # We could also return the original fileset argument here, - # but that would then duplicate work for consumers of the fileset, because then they have to coerce it again - actualFileset; - - /* Add the local files contained in `fileset` to the store as a single [store path](https://nixos.org/manual/nix/stable/glossary#gloss-store-path) rooted at `root`. - - The result is the store path as a string-like value, making it usable e.g. as the `src` of a derivation, or in string interpolation: - ```nix - stdenv.mkDerivation { - src = lib.fileset.toSource { ... }; - # ... - } - ``` - - The name of the store path is always `source`. - - Type: - toSource :: { - root :: Path, - fileset :: FileSet, - } -> SourceLike - - Example: - # Import the current directory into the store - # but only include files under ./src - toSource { - root = ./.; - fileset = ./src; - } - => "/nix/store/...-source" - - # Import the current directory into the store - # but only include ./Makefile and all files under ./src - toSource { - root = ./.; - fileset = union - ./Makefile - ./src; - } - => "/nix/store/...-source" - - # Trying to include a file outside the root will fail - toSource { - root = ./.; - fileset = unions [ - ./Makefile - ./src - ../LICENSE - ]; - } - => - - # The root needs to point to a directory that contains all the files - toSource { - root = ../.; - fileset = unions [ - ./Makefile - ./src - ../LICENSE - ]; - } - => "/nix/store/...-source" - - # The root has to be a local filesystem path - toSource { - root = "/nix/store/...-source"; - fileset = ./.; - } - => + in + seq (_printFileset actualFileset) + # We could also return the original fileset argument here, + # but that would then duplicate work for consumers of the fileset, because then they have to coerce it again + actualFileset; + + /* + Add the local files contained in `fileset` to the store as a single [store path](https://nixos.org/manual/nix/stable/glossary#gloss-store-path) rooted at `root`. + + The result is the store path as a string-like value, making it usable e.g. as the `src` of a derivation, or in string interpolation: + ```nix + stdenv.mkDerivation { + src = lib.fileset.toSource { ... }; + # ... + } + ``` + + The name of the store path is always `source`. + + Type: + toSource :: { + root :: Path, + fileset :: FileSet, + } -> SourceLike + + Example: + # Import the current directory into the store + # but only include files under ./src + toSource { + root = ./.; + fileset = ./src; + } + => "/nix/store/...-source" + + # Import the current directory into the store + # but only include ./Makefile and all files under ./src + toSource { + root = ./.; + fileset = union + ./Makefile + ./src; + } + => "/nix/store/...-source" + + # Trying to include a file outside the root will fail + toSource { + root = ./.; + fileset = unions [ + ./Makefile + ./src + ../LICENSE + ]; + } + => + + # The root needs to point to a directory that contains all the files + toSource { + root = ../.; + fileset = unions [ + ./Makefile + ./src + ../LICENSE + ]; + } + => "/nix/store/...-source" + + # The root has to be a local filesystem path + toSource { + root = "/nix/store/...-source"; + fileset = ./.; + } + => */ - toSource = { - /* (required) The local directory [path](https://nixos.org/manual/nix/stable/language/values.html#type-path) that will correspond to the root of the resulting store path. - Paths in [strings](https://nixos.org/manual/nix/stable/language/values.html#type-string), including Nix store paths, cannot be passed as `root`. - `root` has to be a directory. - - :::{.note} - Changing `root` only affects the directory structure of the resulting store path, it does not change which files are added to the store. - The only way to change which files get added to the store is by changing the `fileset` attribute. - ::: - */ - root, - /* (required) The file set whose files to import into the store. - File sets can be created using other functions in this library. - This argument can also be a path, - which gets [implicitly coerced to a file set](#sec-fileset-path-coercion). - - :::{.note} - If a directory does not recursively contain any file, it is omitted from the store path contents. - ::: - */ - fileset, }: + toSource = + { + /* + (required) The local directory [path](https://nixos.org/manual/nix/stable/language/values.html#type-path) that will correspond to the root of the resulting store path. + Paths in [strings](https://nixos.org/manual/nix/stable/language/values.html#type-string), including Nix store paths, cannot be passed as `root`. + `root` has to be a directory. + + :::{.note} + Changing `root` only affects the directory structure of the resulting store path, it does not change which files are added to the store. + The only way to change which files get added to the store is by changing the `fileset` attribute. + ::: + */ + root, + /* + (required) The file set whose files to import into the store. + File sets can be created using other functions in this library. + This argument can also be a path, + which gets [implicitly coerced to a file set](#sec-fileset-path-coercion). + + :::{.note} + If a directory does not recursively contain any file, it is omitted from the store path contents. + ::: + */ + fileset, + }: let # We cannot rename matched attribute arguments, so let's work around it with an extra `let in` statement filesetArg = fileset; - in let + in + let fileset = _coerce "lib.fileset.toSource: `fileset`" filesetArg; rootFilesystemRoot = (splitRoot root).root; filesetFilesystemRoot = (splitRoot fileset._internalBase).root; sourceFilter = _toSourceFilter fileset; - in if !isPath root then + in + if !isPath root then if root ? _isLibCleanSourceWith then throw '' lib.fileset.toSource: `root` is a `lib.sources`-based value, but it should be a path instead. @@ -327,52 +359,30 @@ in { Note that this only works for sources created from paths.'' else if isStringLike root then throw '' - lib.fileset.toSource: `root` (${ - toString root - }) is a string-like value, but it should be a path instead. + lib.fileset.toSource: `root` (${toString root}) is a string-like value, but it should be a path instead. Paths in strings are not supported by `lib.fileset`, use `lib.sources` or derivations instead.'' else - throw "lib.fileset.toSource: `root` is of type ${ - typeOf root - }, but it should be a path instead." - # Currently all Nix paths have the same filesystem root, but this could change in the future. - # See also ../path/README.md - else if !fileset._internalIsEmptyWithoutBase && rootFilesystemRoot - != filesetFilesystemRoot then + throw "lib.fileset.toSource: `root` is of type ${typeOf root}, but it should be a path instead." + # Currently all Nix paths have the same filesystem root, but this could change in the future. + # See also ../path/README.md + else if !fileset._internalIsEmptyWithoutBase && rootFilesystemRoot != filesetFilesystemRoot then throw '' - lib.fileset.toSource: Filesystem roots are not the same for `fileset` and `root` (${ - toString root - }): + lib.fileset.toSource: Filesystem roots are not the same for `fileset` and `root` (${toString root}): `root`: Filesystem root is "${toString rootFilesystemRoot}" `fileset`: Filesystem root is "${toString filesetFilesystemRoot}" Different filesystem roots are not supported.'' else if !pathExists root then - throw "lib.fileset.toSource: `root` (${ - toString root - }) is a path that does not exist." + throw "lib.fileset.toSource: `root` (${toString root}) is a path that does not exist." else if pathType root != "directory" then throw '' - lib.fileset.toSource: `root` (${ - toString root - }) is a file, but it should be a directory instead. Potential solutions: + lib.fileset.toSource: `root` (${toString root}) is a file, but it should be a directory instead. Potential solutions: - If you want to import the file into the store _without_ a containing directory, use string interpolation or `builtins.path` instead of this function. - - If you want to import the file into the store _with_ a containing directory, set `root` to the containing directory, such as ${ - toString (dirOf root) - }, and set `fileset` to the file path.'' - else if !fileset._internalIsEmptyWithoutBase - && !hasPrefix root fileset._internalBase then + - If you want to import the file into the store _with_ a containing directory, set `root` to the containing directory, such as ${toString (dirOf root)}, and set `fileset` to the file path.'' + else if !fileset._internalIsEmptyWithoutBase && !hasPrefix root fileset._internalBase then throw '' - lib.fileset.toSource: `fileset` could contain files in ${ - toString fileset._internalBase - }, which is not under the `root` (${ - toString root - }). Potential solutions: - - Set `root` to ${ - toString fileset._internalBase - } or any directory higher up. This changes the layout of the resulting store path. - - Set `fileset` to a file set that cannot contain files outside the `root` (${ - toString root - }). This could change the files included in the result.'' + lib.fileset.toSource: `fileset` could contain files in ${toString fileset._internalBase}, which is not under the `root` (${toString root}). Potential solutions: + - Set `root` to ${toString fileset._internalBase} or any directory higher up. This changes the layout of the resulting store path. + - Set `fileset` to a file set that cannot contain files outside the `root` (${toString root}). This could change the files included in the result.'' else seq sourceFilter cleanSourceWith { name = "source"; @@ -380,55 +390,56 @@ in { filter = sourceFilter; }; - /* The list of file paths contained in the given file set. + /* + The list of file paths contained in the given file set. - :::{.note} - This function is strict in the entire file set. - This is in contrast with combinators [`lib.fileset.union`](#function-library-lib.fileset.union), - [`lib.fileset.intersection`](#function-library-lib.fileset.intersection) and [`lib.fileset.difference`](#function-library-lib.fileset.difference). + :::{.note} + This function is strict in the entire file set. + This is in contrast with combinators [`lib.fileset.union`](#function-library-lib.fileset.union), + [`lib.fileset.intersection`](#function-library-lib.fileset.intersection) and [`lib.fileset.difference`](#function-library-lib.fileset.difference). - Thus it is recommended to call `toList` on file sets created using the combinators, - instead of doing list processing on the result of `toList`. - ::: + Thus it is recommended to call `toList` on file sets created using the combinators, + instead of doing list processing on the result of `toList`. + ::: - The resulting list of files can be turned back into a file set using [`lib.fileset.unions`](#function-library-lib.fileset.unions). + The resulting list of files can be turned back into a file set using [`lib.fileset.unions`](#function-library-lib.fileset.unions). - Type: - toList :: FileSet -> [ Path ] + Type: + toList :: FileSet -> [ Path ] - Example: - toList ./. - [ ./README.md ./Makefile ./src/main.c ./src/main.h ] + Example: + toList ./. + [ ./README.md ./Makefile ./src/main.c ./src/main.h ] - toList (difference ./. ./src) - [ ./README.md ./Makefile ] + toList (difference ./. ./src) + [ ./README.md ./Makefile ] */ toList = # The file set whose file paths to return. # This argument can also be a path, # which gets [implicitly coerced to a file set](#sec-fileset-path-coercion). - fileset: - _toList (_coerce "lib.fileset.toList: Argument" fileset); + fileset: _toList (_coerce "lib.fileset.toList: Argument" fileset); - /* The file set containing all files that are in either of two given file sets. - This is the same as [`unions`](#function-library-lib.fileset.unions), - but takes just two file sets instead of a list. - See also [Union (set theory)](https://en.wikipedia.org/wiki/Union_(set_theory)). + /* + The file set containing all files that are in either of two given file sets. + This is the same as [`unions`](#function-library-lib.fileset.unions), + but takes just two file sets instead of a list. + See also [Union (set theory)](https://en.wikipedia.org/wiki/Union_(set_theory)). - The given file sets are evaluated as lazily as possible, - with the first argument being evaluated first if needed. + The given file sets are evaluated as lazily as possible, + with the first argument being evaluated first if needed. - Type: - union :: FileSet -> FileSet -> FileSet + Type: + union :: FileSet -> FileSet -> FileSet - Example: - # Create a file set containing the file `Makefile` - # and all files recursively in the `src` directory - union ./Makefile ./src + Example: + # Create a file set containing the file `Makefile` + # and all files recursively in the `src` directory + union ./Makefile ./src - # Create a file set containing the file `Makefile` - # and the LICENSE file from the parent directory - union ./Makefile ../LICENSE + # Create a file set containing the file `Makefile` + # and the LICENSE file from the parent directory + union ./Makefile ../LICENSE */ union = # The first file set. @@ -439,46 +450,49 @@ in { # This argument can also be a path, # which gets [implicitly coerced to a file set](#sec-fileset-path-coercion). fileset2: - _unionMany (_coerceMany "lib.fileset.union" [ - { - context = "First argument"; - value = fileset1; - } - { - context = "Second argument"; - value = fileset2; - } - ]); - - /* The file set containing all files that are in any of the given file sets. - This is the same as [`union`](#function-library-lib.fileset.unions), - but takes a list of file sets instead of just two. - See also [Union (set theory)](https://en.wikipedia.org/wiki/Union_(set_theory)). - - The given file sets are evaluated as lazily as possible, - with earlier elements being evaluated first if needed. - - Type: - unions :: [ FileSet ] -> FileSet - - Example: - # Create a file set containing selected files - unions [ - # Include the single file `Makefile` in the current directory - # This errors if the file doesn't exist - ./Makefile - - # Recursively include all files in the `src/code` directory - # If this directory is empty this has no effect - ./src/code - - # Include the files `run.sh` and `unit.c` from the `tests` directory - ./tests/run.sh - ./tests/unit.c - - # Include the `LICENSE` file from the parent directory - ../LICENSE - ] + _unionMany ( + _coerceMany "lib.fileset.union" [ + { + context = "First argument"; + value = fileset1; + } + { + context = "Second argument"; + value = fileset2; + } + ] + ); + + /* + The file set containing all files that are in any of the given file sets. + This is the same as [`union`](#function-library-lib.fileset.unions), + but takes a list of file sets instead of just two. + See also [Union (set theory)](https://en.wikipedia.org/wiki/Union_(set_theory)). + + The given file sets are evaluated as lazily as possible, + with earlier elements being evaluated first if needed. + + Type: + unions :: [ FileSet ] -> FileSet + + Example: + # Create a file set containing selected files + unions [ + # Include the single file `Makefile` in the current directory + # This errors if the file doesn't exist + ./Makefile + + # Recursively include all files in the `src/code` directory + # If this directory is empty this has no effect + ./src/code + + # Include the files `run.sh` and `unit.c` from the `tests` directory + ./tests/run.sh + ./tests/unit.c + + # Include the `LICENSE` file from the parent directory + ../LICENSE + ] */ unions = # A list of file sets. @@ -486,32 +500,33 @@ in { # which get [implicitly coerced to file sets](#sec-fileset-path-coercion). filesets: if !isList filesets then - throw "lib.fileset.unions: Argument is of type ${ - typeOf filesets - }, but it should be a list instead." + throw "lib.fileset.unions: Argument is of type ${typeOf filesets}, but it should be a list instead." else pipe filesets [ # Annotate the elements with context, used by _coerceMany for better errors - (imap0 (i: el: { - context = "Element ${toString i}"; - value = el; - })) + (imap0 ( + i: el: { + context = "Element ${toString i}"; + value = el; + } + )) (_coerceMany "lib.fileset.unions") _unionMany ]; - /* The file set containing all files that are in both of two given file sets. - See also [Intersection (set theory)](https://en.wikipedia.org/wiki/Intersection_(set_theory)). + /* + The file set containing all files that are in both of two given file sets. + See also [Intersection (set theory)](https://en.wikipedia.org/wiki/Intersection_(set_theory)). - The given file sets are evaluated as lazily as possible, - with the first argument being evaluated first if needed. + The given file sets are evaluated as lazily as possible, + with the first argument being evaluated first if needed. - Type: - intersection :: FileSet -> FileSet -> FileSet + Type: + intersection :: FileSet -> FileSet -> FileSet - Example: - # Limit the selected files to the ones in ./., so only ./src and ./Makefile - intersection ./. (unions [ ../LICENSE ./src ./Makefile ]) + Example: + # Limit the selected files to the ones in ./., so only ./src and ./Makefile + intersection ./. (unions [ ../LICENSE ./src ./Makefile ]) */ intersection = # The first file set. @@ -533,29 +548,31 @@ in { value = fileset2; } ]; - in _intersection (elemAt filesets 0) (elemAt filesets 1); - - /* The file set containing all files from the first file set that are not in the second file set. - See also [Difference (set theory)](https://en.wikipedia.org/wiki/Complement_(set_theory)#Relative_complement). - - The given file sets are evaluated as lazily as possible, - with the first argument being evaluated first if needed. - - Type: - union :: FileSet -> FileSet -> FileSet - - Example: - # Create a file set containing all files from the current directory, - # except ones under ./tests - difference ./. ./tests - - let - # A set of Nix-related files - nixFiles = unions [ ./default.nix ./nix ./tests/default.nix ]; - in - # Create a file set containing all files under ./tests, except ones in `nixFiles`, - # meaning only without ./tests/default.nix - difference ./tests nixFiles + in + _intersection (elemAt filesets 0) (elemAt filesets 1); + + /* + The file set containing all files from the first file set that are not in the second file set. + See also [Difference (set theory)](https://en.wikipedia.org/wiki/Complement_(set_theory)#Relative_complement). + + The given file sets are evaluated as lazily as possible, + with the first argument being evaluated first if needed. + + Type: + union :: FileSet -> FileSet -> FileSet + + Example: + # Create a file set containing all files from the current directory, + # except ones under ./tests + difference ./. ./tests + + let + # A set of Nix-related files + nixFiles = unions [ ./default.nix ./nix ./tests/default.nix ]; + in + # Create a file set containing all files under ./tests, except ones in `nixFiles`, + # meaning only without ./tests/default.nix + difference ./tests nixFiles */ difference = # The positive file set. @@ -581,118 +598,117 @@ in { value = negative; } ]; - in _difference (elemAt filesets 0) (elemAt filesets 1); - - /* Filter a file set to only contain files matching some predicate. - - Type: - fileFilter :: - ({ - name :: String, - type :: String, - hasExt :: String -> Bool, - ... - } -> Bool) - -> Path - -> FileSet - - Example: - # Include all regular `default.nix` files in the current directory - fileFilter (file: file.name == "default.nix") ./. - - # Include all non-Nix files from the current directory - fileFilter (file: ! file.hasExt "nix") ./. - - # Include all files that start with a "." in the current directory - fileFilter (file: hasPrefix "." file.name) ./. - - # Include all regular files (not symlinks or others) in the current directory - fileFilter (file: file.type == "regular") ./. + in + _difference (elemAt filesets 0) (elemAt filesets 1); + + /* + Filter a file set to only contain files matching some predicate. + + Type: + fileFilter :: + ({ + name :: String, + type :: String, + hasExt :: String -> Bool, + ... + } -> Bool) + -> Path + -> FileSet + + Example: + # Include all regular `default.nix` files in the current directory + fileFilter (file: file.name == "default.nix") ./. + + # Include all non-Nix files from the current directory + fileFilter (file: ! file.hasExt "nix") ./. + + # Include all files that start with a "." in the current directory + fileFilter (file: hasPrefix "." file.name) ./. + + # Include all regular files (not symlinks or others) in the current directory + fileFilter (file: file.type == "regular") ./. */ fileFilter = - /* The predicate function to call on all files contained in given file set. - A file is included in the resulting file set if this function returns true for it. + /* + The predicate function to call on all files contained in given file set. + A file is included in the resulting file set if this function returns true for it. - This function is called with an attribute set containing these attributes: + This function is called with an attribute set containing these attributes: - - `name` (String): The name of the file + - `name` (String): The name of the file - - `type` (String, one of `"regular"`, `"symlink"` or `"unknown"`): The type of the file. - This matches result of calling [`builtins.readFileType`](https://nixos.org/manual/nix/stable/language/builtins.html#builtins-readFileType) on the file's path. + - `type` (String, one of `"regular"`, `"symlink"` or `"unknown"`): The type of the file. + This matches result of calling [`builtins.readFileType`](https://nixos.org/manual/nix/stable/language/builtins.html#builtins-readFileType) on the file's path. - - `hasExt` (String -> Bool): Whether the file has a certain file extension. - `hasExt ext` is true only if `hasSuffix ".${ext}" name`. + - `hasExt` (String -> Bool): Whether the file has a certain file extension. + `hasExt ext` is true only if `hasSuffix ".${ext}" name`. - This also means that e.g. for a file with name `.gitignore`, - `hasExt "gitignore"` is true. + This also means that e.g. for a file with name `.gitignore`, + `hasExt "gitignore"` is true. - Other attributes may be added in the future. + Other attributes may be added in the future. */ predicate: # The path whose files to filter path: if !isFunction predicate then - throw "lib.fileset.fileFilter: First argument is of type ${ - typeOf predicate - }, but it should be a function instead." + throw "lib.fileset.fileFilter: First argument is of type ${typeOf predicate}, but it should be a function instead." else if !isPath path then if path._type or "" == "fileset" then throw '' lib.fileset.fileFilter: Second argument is a file set, but it should be a path instead. If you need to filter files in a file set, use `intersection fileset (fileFilter pred ./.)` instead.'' else - throw "lib.fileset.fileFilter: Second argument is of type ${ - typeOf path - }, but it should be a path instead." + throw "lib.fileset.fileFilter: Second argument is of type ${typeOf path}, but it should be a path instead." else if !pathExists path then - throw "lib.fileset.fileFilter: Second argument (${ - toString path - }) is a path that does not exist." + throw "lib.fileset.fileFilter: Second argument (${toString path}) is a path that does not exist." else _fileFilter predicate path; - /* Create a file set with the same files as a `lib.sources`-based value. - This does not import any of the files into the store. - - This can be used to gradually migrate from `lib.sources`-based filtering to `lib.fileset`. - - A file set can be turned back into a source using [`toSource`](#function-library-lib.fileset.toSource). - - :::{.note} - File sets cannot represent empty directories. - Turning the result of this function back into a source using `toSource` will therefore not preserve empty directories. - ::: - - Type: - fromSource :: SourceLike -> FileSet - - Example: - # There's no cleanSource-like function for file sets yet, - # but we can just convert cleanSource to a file set and use it that way - toSource { - root = ./.; - fileset = fromSource (lib.sources.cleanSource ./.); - } - - # Keeping a previous sourceByRegex (which could be migrated to `lib.fileset.unions`), - # but removing a subdirectory using file set functions - difference - (fromSource (lib.sources.sourceByRegex ./. [ - "^README\.md$" - # This regex includes everything in ./doc - "^doc(/.*)?$" - ]) - ./doc/generated - - # Use cleanSource, but limit it to only include ./Makefile and files under ./src - intersection - (fromSource (lib.sources.cleanSource ./.)) - (unions [ - ./Makefile - ./src - ]); + /* + Create a file set with the same files as a `lib.sources`-based value. + This does not import any of the files into the store. + + This can be used to gradually migrate from `lib.sources`-based filtering to `lib.fileset`. + + A file set can be turned back into a source using [`toSource`](#function-library-lib.fileset.toSource). + + :::{.note} + File sets cannot represent empty directories. + Turning the result of this function back into a source using `toSource` will therefore not preserve empty directories. + ::: + + Type: + fromSource :: SourceLike -> FileSet + + Example: + # There's no cleanSource-like function for file sets yet, + # but we can just convert cleanSource to a file set and use it that way + toSource { + root = ./.; + fileset = fromSource (lib.sources.cleanSource ./.); + } + + # Keeping a previous sourceByRegex (which could be migrated to `lib.fileset.unions`), + # but removing a subdirectory using file set functions + difference + (fromSource (lib.sources.sourceByRegex ./. [ + "^README\.md$" + # This regex includes everything in ./doc + "^doc(/.*)?$" + ]) + ./doc/generated + + # Use cleanSource, but limit it to only include ./Makefile and files under ./src + intersection + (fromSource (lib.sources.cleanSource ./.)) + (unions [ + ./Makefile + ./src + ]); */ - fromSource = source: + fromSource = + source: let # This function uses `._isLibCleanSourceWith`, `.origSrc` and `.filter`, # which are technically internal to lib.sources, @@ -700,105 +716,100 @@ in { # and this function is a bridge between them. isFiltered = source ? _isLibCleanSourceWith; path = if isFiltered then source.origSrc else source; - # We can only support sources created from paths - in if !isPath path then + in + # We can only support sources created from paths + if !isPath path then if isStringLike path then throw '' - lib.fileset.fromSource: The source origin of the argument is a string-like value ("${ - toString path - }"), but it should be a path instead. + lib.fileset.fromSource: The source origin of the argument is a string-like value ("${toString path}"), but it should be a path instead. Sources created from paths in strings cannot be turned into file sets, use `lib.sources` or derivations instead.'' else - throw - "lib.fileset.fromSource: The source origin of the argument is of type ${ - typeOf path - }, but it should be a path instead." + throw "lib.fileset.fromSource: The source origin of the argument is of type ${typeOf path}, but it should be a path instead." else if !pathExists path then - throw "lib.fileset.fromSource: The source origin (${ - toString path - }) of the argument is a path that does not exist." + throw "lib.fileset.fromSource: The source origin (${toString path}) of the argument is a path that does not exist." else if isFiltered then _fromSourceFilter path source.filter else - # If there's no filter, no need to run the expensive conversion, all subpaths will be included + # If there's no filter, no need to run the expensive conversion, all subpaths will be included _singleton path; - /* Create a file set containing all [Git-tracked files](https://git-scm.com/book/en/v2/Git-Basics-Recording-Changes-to-the-Repository) in a repository. + /* + Create a file set containing all [Git-tracked files](https://git-scm.com/book/en/v2/Git-Basics-Recording-Changes-to-the-Repository) in a repository. - This function behaves like [`gitTrackedWith { }`](#function-library-lib.fileset.gitTrackedWith) - using the defaults. + This function behaves like [`gitTrackedWith { }`](#function-library-lib.fileset.gitTrackedWith) - using the defaults. - Type: - gitTracked :: Path -> FileSet + Type: + gitTracked :: Path -> FileSet - Example: - # Include all files tracked by the Git repository in the current directory - gitTracked ./. + Example: + # Include all files tracked by the Git repository in the current directory + gitTracked ./. - # Include only files tracked by the Git repository in the parent directory - # that are also in the current directory - intersection ./. (gitTracked ../.) + # Include only files tracked by the Git repository in the parent directory + # that are also in the current directory + intersection ./. (gitTracked ../.) */ gitTracked = - /* The [path](https://nixos.org/manual/nix/stable/language/values#type-path) to the working directory of a local Git repository. - This directory must contain a `.git` file or subdirectory. + /* + The [path](https://nixos.org/manual/nix/stable/language/values#type-path) to the working directory of a local Git repository. + This directory must contain a `.git` file or subdirectory. */ - path: - _fromFetchGit "gitTracked" "argument" path { }; + path: _fromFetchGit "gitTracked" "argument" path { }; - /* Create a file set containing all [Git-tracked files](https://git-scm.com/book/en/v2/Git-Basics-Recording-Changes-to-the-Repository) in a repository. - The first argument allows configuration with an attribute set, - while the second argument is the path to the Git working tree. + /* + Create a file set containing all [Git-tracked files](https://git-scm.com/book/en/v2/Git-Basics-Recording-Changes-to-the-Repository) in a repository. + The first argument allows configuration with an attribute set, + while the second argument is the path to the Git working tree. - `gitTrackedWith` does not perform any filtering when the path is a [Nix store path](https://nixos.org/manual/nix/stable/store/store-path.html#store-path) and not a repository. - In this way, it accommodates the use case where the expression that makes the `gitTracked` call does not reside in an actual git repository anymore, - and has presumably already been fetched in a way that excludes untracked files. - Fetchers with such equivalent behavior include `builtins.fetchGit`, `builtins.fetchTree` (experimental), and `pkgs.fetchgit` when used without `leaveDotGit`. + `gitTrackedWith` does not perform any filtering when the path is a [Nix store path](https://nixos.org/manual/nix/stable/store/store-path.html#store-path) and not a repository. + In this way, it accommodates the use case where the expression that makes the `gitTracked` call does not reside in an actual git repository anymore, + and has presumably already been fetched in a way that excludes untracked files. + Fetchers with such equivalent behavior include `builtins.fetchGit`, `builtins.fetchTree` (experimental), and `pkgs.fetchgit` when used without `leaveDotGit`. - If you don't need the configuration, - you can use [`gitTracked`](#function-library-lib.fileset.gitTracked) instead. + If you don't need the configuration, + you can use [`gitTracked`](#function-library-lib.fileset.gitTracked) instead. - This is equivalent to the result of [`unions`](#function-library-lib.fileset.unions) on all files returned by [`git ls-files`](https://git-scm.com/docs/git-ls-files) - (which uses [`--cached`](https://git-scm.com/docs/git-ls-files#Documentation/git-ls-files.txt--c) by default). + This is equivalent to the result of [`unions`](#function-library-lib.fileset.unions) on all files returned by [`git ls-files`](https://git-scm.com/docs/git-ls-files) + (which uses [`--cached`](https://git-scm.com/docs/git-ls-files#Documentation/git-ls-files.txt--c) by default). - :::{.warning} - Currently this function is based on [`builtins.fetchGit`](https://nixos.org/manual/nix/stable/language/builtins.html#builtins-fetchGit) - As such, this function causes all Git-tracked files to be unnecessarily added to the Nix store, - without being re-usable by [`toSource`](#function-library-lib.fileset.toSource). + :::{.warning} + Currently this function is based on [`builtins.fetchGit`](https://nixos.org/manual/nix/stable/language/builtins.html#builtins-fetchGit) + As such, this function causes all Git-tracked files to be unnecessarily added to the Nix store, + without being re-usable by [`toSource`](#function-library-lib.fileset.toSource). - This may change in the future. - ::: + This may change in the future. + ::: - Type: - gitTrackedWith :: { recurseSubmodules :: Bool ? false } -> Path -> FileSet + Type: + gitTrackedWith :: { recurseSubmodules :: Bool ? false } -> Path -> FileSet - Example: - # Include all files tracked by the Git repository in the current directory - # and any submodules under it - gitTracked { recurseSubmodules = true; } ./. + Example: + # Include all files tracked by the Git repository in the current directory + # and any submodules under it + gitTracked { recurseSubmodules = true; } ./. */ - gitTrackedWith = { - /* (optional, default: `false`) Whether to recurse into [Git submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules) to also include their tracked files. - - If `true`, this is equivalent to passing the [--recurse-submodules](https://git-scm.com/docs/git-ls-files#Documentation/git-ls-files.txt---recurse-submodules) flag to `git ls-files`. - */ - recurseSubmodules ? false, }: - /* The [path](https://nixos.org/manual/nix/stable/language/values#type-path) to the working directory of a local Git repository. - This directory must contain a `.git` file or subdirectory. + gitTrackedWith = + { + /* + (optional, default: `false`) Whether to recurse into [Git submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules) to also include their tracked files. + + If `true`, this is equivalent to passing the [--recurse-submodules](https://git-scm.com/docs/git-ls-files#Documentation/git-ls-files.txt---recurse-submodules) flag to `git ls-files`. + */ + recurseSubmodules ? false, + }: + /* + The [path](https://nixos.org/manual/nix/stable/language/values#type-path) to the working directory of a local Git repository. + This directory must contain a `.git` file or subdirectory. */ path: if !isBool recurseSubmodules then - throw - "lib.fileset.gitTrackedWith: Expected the attribute `recurseSubmodules` of the first argument to be a boolean, but it's a ${ - typeOf recurseSubmodules - } instead." - else if recurseSubmodules - && versionOlder nixVersion _fetchGitSubmodulesMinver then - throw - "lib.fileset.gitTrackedWith: Setting the attribute `recurseSubmodules` to `true` is only supported for Nix version ${_fetchGitSubmodulesMinver} and after, but Nix version ${nixVersion} is used." + throw "lib.fileset.gitTrackedWith: Expected the attribute `recurseSubmodules` of the first argument to be a boolean, but it's a ${typeOf recurseSubmodules} instead." + else if recurseSubmodules && versionOlder nixVersion _fetchGitSubmodulesMinver then + throw "lib.fileset.gitTrackedWith: Setting the attribute `recurseSubmodules` to `true` is only supported for Nix version ${_fetchGitSubmodulesMinver} and after, but Nix version ${nixVersion} is used." else _fromFetchGit "gitTrackedWith" "second argument" path - # This is the only `fetchGit` parameter that makes sense in this context. - # We can't just pass `submodules = recurseSubmodules` here because - # this would fail for Nix versions that don't support `submodules`. - (lib.optionalAttrs recurseSubmodules { submodules = true; }); + # This is the only `fetchGit` parameter that makes sense in this context. + # We can't just pass `submodules = recurseSubmodules` here because + # this would fail for Nix versions that don't support `submodules`. + (lib.optionalAttrs recurseSubmodules { submodules = true; }); } diff --git a/nix/stdlib/fileset/internal.nix b/nix/stdlib/fileset/internal.nix index 97bdadf..eb3b73c 100644 --- a/nix/stdlib/fileset/internal.nix +++ b/nix/stdlib/fileset/internal.nix @@ -1,32 +1,71 @@ -{ lib ? import ../. }: +{ + lib ? import ../., +}: let inherit (builtins) - isAttrs isPath isString nixVersion pathExists readDir split trace typeOf - fetchGit; + isAttrs + isPath + isString + nixVersion + pathExists + readDir + split + trace + typeOf + fetchGit + ; inherit (lib.attrsets) - attrNames attrValues mapAttrs mapAttrsToList optionalAttrs zipAttrsWith; + attrNames + attrValues + mapAttrs + mapAttrsToList + optionalAttrs + zipAttrsWith + ; inherit (lib.filesystem) pathType; inherit (lib.lists) - all commonPrefix concatLists elemAt filter findFirst findFirstIndex foldl' - head length sublist tail; - - inherit (lib.path) append splitRoot hasStorePathPrefix splitStorePath; + all + commonPrefix + concatLists + elemAt + filter + findFirst + findFirstIndex + foldl' + head + length + sublist + tail + ; + + inherit (lib.path) + append + splitRoot + hasStorePathPrefix + splitStorePath + ; inherit (lib.path.subpath) components join; inherit (lib.strings) - isStringLike concatStringsSep substring stringLength hasSuffix - versionAtLeast; + isStringLike + concatStringsSep + substring + stringLength + hasSuffix + versionAtLeast + ; inherit (lib.trivial) inPureEvalMode; - # Rare case of justified usage of rec: - # - This file is internal, so the return value doesn't matter, no need to make things overridable - # - The functions depend on each other - # - We want to expose all of these functions for easy testing -in rec { +in +# Rare case of justified usage of rec: +# - This file is internal, so the return value doesn't matter, no need to make things overridable +# - The functions depend on each other +# - We want to expose all of these functions for easy testing +rec { # If you change the internal representation, make sure to: # - Increment this version # - Add an additional migration function below @@ -36,28 +75,36 @@ in rec { # Migrations between versions. The 0th element converts from v0 to v1, and so on migrations = [ # Convert v0 into v1: Add the _internalBase{Root,Components} attributes - (filesetV0: - let parts = splitRoot filesetV0._internalBase; - in filesetV0 // { + ( + filesetV0: + let + parts = splitRoot filesetV0._internalBase; + in + filesetV0 + // { _internalVersion = 1; _internalBaseRoot = parts.root; _internalBaseComponents = components parts.subpath; - }) + } + ) # Convert v1 into v2: filesetTree's can now also omit attributes to signal paths not being included - (filesetV1: + ( + filesetV1: # This change is backwards compatible (but not forwards compatible, so we still need a new version) - filesetV1 // { - _internalVersion = 2; - }) + filesetV1 // { _internalVersion = 2; } + ) # Convert v2 into v3: filesetTree's now have a representation for an empty file set without a base path - (filesetV2: - filesetV2 // { + ( + filesetV2: + filesetV2 + // { # All v1 file sets are not the new empty file set _internalIsEmptyWithoutBase = false; _internalVersion = 3; - }) + } + ) ]; _noEvalMessage = '' @@ -82,12 +129,14 @@ in rec { # Create a fileset, see ./README.md#fileset # Type: path -> filesetTree -> fileset - _create = base: tree: + _create = + base: tree: let # Decompose the base into its components # See ../path/README.md for why we're not just using `toString` parts = splitRoot base; - in { + in + { _type = "fileset"; _internalVersion = _currentVersion; @@ -107,22 +156,23 @@ in rec { # Coerce a value to a fileset, erroring when the value cannot be coerced. # The string gives the context for error messages. # Type: String -> (fileset | Path) -> fileset - _coerce = context: value: + _coerce = + context: value: if value._type or "" == "fileset" then if value._internalVersion > _currentVersion then throw '' ${context} is a file set created from a future version of the file set library with a different internal representation: - - Internal version of the file set: ${ - toString value._internalVersion - } + - Internal version of the file set: ${toString value._internalVersion} - Internal version of the library: ${toString _currentVersion} Make sure to update your Nixpkgs to have a newer version of `lib.fileset`.'' else if value._internalVersion < _currentVersion then let # Get all the migration functions necessary to convert from the old to the current version - migrationsToApply = sublist value._internalVersion - (_currentVersion - value._internalVersion) migrations; - in foldl' (value: migration: migration value) value migrationsToApply + migrationsToApply = sublist value._internalVersion ( + _currentVersion - value._internalVersion + ) migrations; + in + foldl' (value: migration: migration value) value migrationsToApply else value else if !isPath value then @@ -133,14 +183,10 @@ in rec { Note that this only works for sources created from paths.'' else if isStringLike value then throw '' - ${context} ("${ - toString value - }") is a string-like value, but it should be a file set or a path instead. + ${context} ("${toString value}") is a string-like value, but it should be a file set or a path instead. Paths represented as strings are not supported by `lib.fileset`, use `lib.sources` or derivations instead.'' else - throw "${context} is of type ${ - typeOf value - }, but it should be a file set or a path instead." + throw "${context} is of type ${typeOf value}, but it should be a file set or a path instead." else if !pathExists value then throw '' ${context} (${toString value}) is a path that does not exist. @@ -151,84 +197,86 @@ in rec { # Coerce many values to filesets, erroring when any value cannot be coerced, # or if the filesystem root of the values doesn't match. # Type: String -> [ { context :: String, value :: fileset | Path } ] -> [ fileset ] - _coerceMany = functionContext: list: + _coerceMany = + functionContext: list: let - filesets = map - ({ context, value, }: _coerce "${functionContext}: ${context}" value) - list; + filesets = map ({ context, value }: _coerce "${functionContext}: ${context}" value) list; # Find the first value with a base, there may be none! - firstWithBase = - findFirst (fileset: !fileset._internalIsEmptyWithoutBase) null filesets; + firstWithBase = findFirst (fileset: !fileset._internalIsEmptyWithoutBase) null filesets; # This value is only accessed if first != null firstBaseRoot = firstWithBase._internalBaseRoot; # Finds the first element with a filesystem root different than the first element, if any - differentIndex = findFirstIndex (fileset: + differentIndex = findFirstIndex ( + fileset: # The empty value without a base doesn't have a base path - !fileset._internalIsEmptyWithoutBase && firstBaseRoot - != fileset._internalBaseRoot) null filesets; - # Only evaluates `differentIndex` if there are any elements with a base - in if firstWithBase != null && differentIndex != null then + !fileset._internalIsEmptyWithoutBase && firstBaseRoot != fileset._internalBaseRoot + ) null filesets; + in + # Only evaluates `differentIndex` if there are any elements with a base + if firstWithBase != null && differentIndex != null then throw '' ${functionContext}: Filesystem roots are not the same: - ${(head list).context}: Filesystem root is "${ - toString firstBaseRoot - }" - ${(elemAt list differentIndex).context}: Filesystem root is "${ - toString (elemAt filesets differentIndex)._internalBaseRoot - }" + ${(head list).context}: Filesystem root is "${toString firstBaseRoot}" + ${(elemAt list differentIndex).context}: Filesystem root is "${toString (elemAt filesets differentIndex)._internalBaseRoot}" Different filesystem roots are not supported.'' else filesets; # Create a file set from a path. # Type: Path -> fileset - _singleton = path: - let type = pathType path; - in if type == "directory" then + _singleton = + path: + let + type = pathType path; + in + if type == "directory" then _create path type else - # This turns a file path ./default.nix into a fileset with - # - _internalBase: ./. - # - _internalTree: { - # "default.nix" = ; - # } - # See ./README.md#single-files + # This turns a file path ./default.nix into a fileset with + # - _internalBase: ./. + # - _internalTree: { + # "default.nix" = ; + # } + # See ./README.md#single-files _create (dirOf path) { ${baseNameOf path} = type; }; # Expand a directory representation to an equivalent one in attribute set form. # All directory entries are included in the result. # Type: Path -> filesetTree -> { = filesetTree; } - _directoryEntries = path: value: + _directoryEntries = + path: value: if value == "directory" then readDir path else - # Set all entries not present to null + # Set all entries not present to null mapAttrs (name: value: null) (readDir path) // value; - /* A normalisation of a filesetTree suitable filtering with `builtins.path`: - - Replace all directories that have no files with `null`. - This removes directories that would be empty - - Replace all directories with all files with `"directory"`. - This speeds up the source filter function + /* + A normalisation of a filesetTree suitable filtering with `builtins.path`: + - Replace all directories that have no files with `null`. + This removes directories that would be empty + - Replace all directories with all files with `"directory"`. + This speeds up the source filter function - Note that this function is strict, it evaluates the entire tree + Note that this function is strict, it evaluates the entire tree - Type: Path -> filesetTree -> filesetTree + Type: Path -> filesetTree -> filesetTree */ - _normaliseTreeFilter = path: tree: + _normaliseTreeFilter = + path: tree: if tree == "directory" || isAttrs tree then let entries = _directoryEntries path tree; - normalisedSubtrees = - mapAttrs (name: _normaliseTreeFilter (path + "/${name}")) entries; + normalisedSubtrees = mapAttrs (name: _normaliseTreeFilter (path + "/${name}")) entries; subtreeValues = attrValues normalisedSubtrees; - # This triggers either when all files in a directory are filtered out - # Or when the directory doesn't contain any files at all - in if all isNull subtreeValues then + in + # This triggers either when all files in a directory are filtered out + # Or when the directory doesn't contain any files at all + if all isNull subtreeValues then null - # Triggers when we have the same as a `readDir path`, so we can turn it back into an equivalent "directory". + # Triggers when we have the same as a `readDir path`, so we can turn it back into an equivalent "directory". else if all isString subtreeValues then "directory" else @@ -236,40 +284,40 @@ in rec { else tree; - /* A minimal normalisation of a filesetTree, intended for pretty-printing: - - If all children of a path are recursively included or empty directories, the path itself is also recursively included - - If all children of a path are fully excluded or empty directories, the path itself is an empty directory - - Other empty directories are represented with the special "emptyDir" string - While these could be replaced with `null`, that would take another mapAttrs + /* + A minimal normalisation of a filesetTree, intended for pretty-printing: + - If all children of a path are recursively included or empty directories, the path itself is also recursively included + - If all children of a path are fully excluded or empty directories, the path itself is an empty directory + - Other empty directories are represented with the special "emptyDir" string + While these could be replaced with `null`, that would take another mapAttrs - Note that this function is partially lazy. + Note that this function is partially lazy. - Type: Path -> filesetTree -> filesetTree (with "emptyDir"'s) + Type: Path -> filesetTree -> filesetTree (with "emptyDir"'s) */ - _normaliseTreeMinimal = path: tree: + _normaliseTreeMinimal = + path: tree: if tree == "directory" || isAttrs tree then let entries = _directoryEntries path tree; - normalisedSubtrees = - mapAttrs (name: _normaliseTreeMinimal (path + "/${name}")) entries; + normalisedSubtrees = mapAttrs (name: _normaliseTreeMinimal (path + "/${name}")) entries; subtreeValues = attrValues normalisedSubtrees; - # If there are no entries, or all entries are empty directories, return "emptyDir". - # After this branch we know that there's at least one file - in if all (value: value == "emptyDir") subtreeValues then + in + # If there are no entries, or all entries are empty directories, return "emptyDir". + # After this branch we know that there's at least one file + if all (value: value == "emptyDir") subtreeValues then "emptyDir" - # If all subtrees are fully included or empty directories - # (both of which are coincidentally represented as strings), return "directory". - # This takes advantage of the fact that empty directories can be represented as included directories. - # Note that the tree == "directory" check allows avoiding recursion - else if tree == "directory" - || all (value: isString value) subtreeValues then + # If all subtrees are fully included or empty directories + # (both of which are coincidentally represented as strings), return "directory". + # This takes advantage of the fact that empty directories can be represented as included directories. + # Note that the tree == "directory" check allows avoiding recursion + else if tree == "directory" || all (value: isString value) subtreeValues then "directory" - # If all subtrees are fully excluded or empty directories, return null. - # This takes advantage of the fact that empty directories can be represented as excluded directories - else if all (value: isNull value || value == "emptyDir") - subtreeValues then + # If all subtrees are fully excluded or empty directories, return null. + # This takes advantage of the fact that empty directories can be represented as excluded directories + else if all (value: isNull value || value == "emptyDir") subtreeValues then null - # Mix of included and excluded entries + # Mix of included and excluded entries else normalisedSubtrees else @@ -278,102 +326,118 @@ in rec { # Trace a filesetTree in a pretty way when the resulting value is evaluated. # This can handle both normal filesetTree's, and ones returned from _normaliseTreeMinimal # Type: Path -> filesetTree (with "emptyDir"'s) -> Null - _printMinimalTree = base: tree: + _printMinimalTree = + base: tree: let - treeSuffix = tree: + treeSuffix = + tree: if isAttrs tree then "" else if tree == "directory" then " (all files in directory)" else - # This does "leak" the file type strings of the internal representation, - # but this is the main reason these file type strings even are in the representation! - # TODO: Consider removing that information from the internal representation for performance. - # The file types can still be printed by querying them only during tracing + # This does "leak" the file type strings of the internal representation, + # but this is the main reason these file type strings even are in the representation! + # TODO: Consider removing that information from the internal representation for performance. + # The file types can still be printed by querying them only during tracing " (${tree})"; # Only for attribute set trees - traceTreeAttrs = prevLine: indent: tree: - foldl' (prevLine: name: + traceTreeAttrs = + prevLine: indent: tree: + foldl' ( + prevLine: name: let subtree = tree.${name}; # Evaluating this prints the line for this subtree thisLine = trace "${indent}- ${name}${treeSuffix subtree}" prevLine; - in if subtree == null || subtree == "emptyDir" then - # Don't print anything at all if this subtree is empty + in + if subtree == null || subtree == "emptyDir" then + # Don't print anything at all if this subtree is empty prevLine else if isAttrs subtree then - # A directory with explicit entries - # Do print this node, but also recurse + # A directory with explicit entries + # Do print this node, but also recurse traceTreeAttrs thisLine "${indent} " subtree else - # Either a file, or a recursively included directory - # Do print this node but no further recursion needed - thisLine) prevLine (attrNames tree); + # Either a file, or a recursively included directory + # Do print this node but no further recursion needed + thisLine + ) prevLine (attrNames tree); # Evaluating this will print the first line - firstLine = if tree == null || tree == "emptyDir" then - trace "(empty)" null - else - trace "${toString base}${treeSuffix tree}" null; - in if isAttrs tree then traceTreeAttrs firstLine "" tree else firstLine; + firstLine = + if tree == null || tree == "emptyDir" then + trace "(empty)" null + else + trace "${toString base}${treeSuffix tree}" null; + in + if isAttrs tree then traceTreeAttrs firstLine "" tree else firstLine; # Pretty-print a file set in a pretty way when the resulting value is evaluated # Type: fileset -> Null - _printFileset = fileset: + _printFileset = + fileset: if fileset._internalIsEmptyWithoutBase then trace "(empty)" null else - _printMinimalTree fileset._internalBase - (_normaliseTreeMinimal fileset._internalBase fileset._internalTree); + _printMinimalTree fileset._internalBase ( + _normaliseTreeMinimal fileset._internalBase fileset._internalTree + ); # Turn a fileset into a source filter function suitable for `builtins.path` # Only directories recursively containing at least one files are recursed into # Type: fileset -> (String -> String -> Bool) - _toSourceFilter = fileset: + _toSourceFilter = + fileset: let # Simplify the tree, necessary to make sure all empty directories are null # which has the effect that they aren't included in the result tree = _normaliseTreeFilter fileset._internalBase fileset._internalTree; # The base path as a string with a single trailing slash - baseString = if fileset._internalBaseComponents == [ ] then - # Need to handle the filesystem root specially - "/" - else - "/" + concatStringsSep "/" fileset._internalBaseComponents + "/"; + baseString = + if fileset._internalBaseComponents == [ ] then + # Need to handle the filesystem root specially + "/" + else + "/" + concatStringsSep "/" fileset._internalBaseComponents + "/"; baseLength = stringLength baseString; # Check whether a list of path components under the base path exists in the tree. # This function is called often, so it should be fast. # Type: [ String ] -> Bool - inTree = components: + inTree = + components: let - recurse = index: localTree: + recurse = + index: localTree: if isAttrs localTree then - # We have an attribute set, meaning this is a directory with at least one file + # We have an attribute set, meaning this is a directory with at least one file if index >= length components then - # The path may have no more components though, meaning the filter is running on the directory itself, - # so we always include it, again because there's at least one file in it. + # The path may have no more components though, meaning the filter is running on the directory itself, + # so we always include it, again because there's at least one file in it. true else - # If we do have more components, the filter runs on some entry inside this directory, so we need to recurse - # We do +2 because builtins.split is an interleaved list of the inbetweens and the matches + # If we do have more components, the filter runs on some entry inside this directory, so we need to recurse + # We do +2 because builtins.split is an interleaved list of the inbetweens and the matches recurse (index + 2) localTree.${elemAt components index} else - # If it's not an attribute set it can only be either null (in which case it's not included) - # or a string ("directory" or "regular", etc.) in which case it's included + # If it's not an attribute set it can only be either null (in which case it's not included) + # or a string ("directory" or "regular", etc.) in which case it's included localTree != null; - in recurse 0 tree; + in + recurse 0 tree; # Filter suited when there's no files empty = _: _: false; # Filter suited when there's some files # This can't be used for when there's no files, because the base directory is always included - nonEmpty = path: type: + nonEmpty = + path: type: let # Add a slash to the path string, turning "/foo" to "/foo/", # making sure to not have any false prefix matches below. @@ -381,47 +445,50 @@ in rec { # but builtins.path doesn't call the filter function on the `path` argument itself, # meaning this function can never receive "/" as an argument pathSlash = path + "/"; - in ( + in + ( # Same as `hasPrefix pathSlash baseString`, but more efficient. # With base /foo/bar we need to include /foo: # hasPrefix "/foo/" "/foo/bar/" if substring 0 (stringLength pathSlash) baseString == pathSlash then true - # Same as `! hasPrefix baseString pathSlash`, but more efficient. - # With base /foo/bar we need to exclude /baz - # ! hasPrefix "/baz/" "/foo/bar/" + # Same as `! hasPrefix baseString pathSlash`, but more efficient. + # With base /foo/bar we need to exclude /baz + # ! hasPrefix "/baz/" "/foo/bar/" else if substring 0 baseLength pathSlash != baseString then false else - # Same as `removePrefix baseString path`, but more efficient. - # From the above code we know that hasPrefix baseString pathSlash holds, so this is safe. - # We don't use pathSlash here because we only needed the trailing slash for the prefix matching. - # With base /foo and path /foo/bar/baz this gives - # inTree (split "/" (removePrefix "/foo/" "/foo/bar/baz")) - # == inTree (split "/" "bar/baz") - # == inTree [ "bar" "baz" ] - inTree (split "/" (substring baseLength (-1) path))) + # Same as `removePrefix baseString path`, but more efficient. + # From the above code we know that hasPrefix baseString pathSlash holds, so this is safe. + # We don't use pathSlash here because we only needed the trailing slash for the prefix matching. + # With base /foo and path /foo/bar/baz this gives + # inTree (split "/" (removePrefix "/foo/" "/foo/bar/baz")) + # == inTree (split "/" "bar/baz") + # == inTree [ "bar" "baz" ] + inTree (split "/" (substring baseLength (-1) path)) + ) # This is a way have an additional check in case the above is true without any significant performance cost && ( # This relies on the fact that Nix only distinguishes path types "directory", "regular", "symlink" and "unknown", # so everything except "unknown" is allowed, seems reasonable to rely on that - type != "unknown" || throw '' + type != "unknown" + || throw '' lib.fileset.toSource: `fileset` contains a file that cannot be added to the store: ${path} This file is neither a regular file nor a symlink, the only file types supported by the Nix store. - Therefore the file set cannot be added to the Nix store as is. Make sure to not include that file to avoid this error.''); - # Special case because the code below assumes that the _internalBase is always included in the result - # which shouldn't be done when we have no files at all in the base - # This also forces the tree before returning the filter, leads to earlier error messages - in if fileset._internalIsEmptyWithoutBase || tree == null then - empty - else - nonEmpty; + Therefore the file set cannot be added to the Nix store as is. Make sure to not include that file to avoid this error.'' + ); + in + # Special case because the code below assumes that the _internalBase is always included in the result + # which shouldn't be done when we have no files at all in the base + # This also forces the tree before returning the filter, leads to earlier error messages + if fileset._internalIsEmptyWithoutBase || tree == null then empty else nonEmpty; # Turn a builtins.filterSource-based source filter on a root path into a file set # containing only files included by the filter. # The filter is lazily called as necessary to determine whether paths are included # Type: Path -> (String -> String -> Bool) -> fileset - _fromSourceFilter = root: sourceFilter: + _fromSourceFilter = + root: sourceFilter: let # During the recursion we need to track both: # - The path value such that we can safely call `readDir` on it @@ -431,7 +498,8 @@ in rec { # this would then require converting it to a path string for every path, # which is a fairly expensive operation # Create a file set from a directory entry - fromDirEntry = path: pathString: type: + fromDirEntry = + path: pathString: type: # The filter needs to run on the path as a string if !sourceFilter pathString type then null @@ -441,13 +509,14 @@ in rec { type; # Create a file set from a directory - fromDir = path: pathString: + fromDir = + path: pathString: mapAttrs - # This looks a bit funny, but we need both the path-based and the path string-based values - (name: fromDirEntry (path + "/${name}") (pathString + "/${name}")) - # We need to readDir on the path value, because reading on a path string - # would be unspecified if there are multiple filesystem roots - (readDir path); + # This looks a bit funny, but we need both the path-based and the path string-based values + (name: fromDirEntry (path + "/${name}") (pathString + "/${name}")) + # We need to readDir on the path value, because reading on a path string + # would be unspecified if there are multiple filesystem roots + (readDir path); rootPathType = pathType root; @@ -456,31 +525,33 @@ in rec { # So instead we use `lib.path.splitRoot` to safely deconstruct the path into its filesystem root and subpath # We don't need the filesystem root though, builtins.path doesn't expose that in any way to the filter. # So we only need the components, which we then turn into a string as one would expect. - rootString = "/" - + concatStringsSep "/" (components (splitRoot root).subpath); - in if rootPathType == "directory" then - # We imitate builtins.path not calling the filter on the root path + rootString = "/" + concatStringsSep "/" (components (splitRoot root).subpath); + in + if rootPathType == "directory" then + # We imitate builtins.path not calling the filter on the root path _create root (fromDir root rootString) else - # Direct files are always included by builtins.path without calling the filter - # But we need to lift up the base path to its parent to satisfy the base path invariant + # Direct files are always included by builtins.path without calling the filter + # But we need to lift up the base path to its parent to satisfy the base path invariant _create (dirOf root) { ${baseNameOf root} = rootPathType; }; # Turns a file set into the list of file paths it includes. # Type: fileset -> [ Path ] - _toList = fileset: + _toList = + fileset: let - recurse = path: tree: + recurse = + path: tree: if isAttrs tree then - concatLists - (mapAttrsToList (name: value: recurse (path + "/${name}") value) tree) + concatLists (mapAttrsToList (name: value: recurse (path + "/${name}") value) tree) else if tree == "directory" then recurse path (readDir path) else if tree == null then [ ] else [ path ]; - in if fileset._internalIsEmptyWithoutBase then + in + if fileset._internalIsEmptyWithoutBase then [ ] else recurse fileset._internalBase fileset._internalTree; @@ -488,48 +559,52 @@ in rec { # Transforms the filesetTree of a file set to a shorter base path, e.g. # _shortenTreeBase [ "foo" ] (_create /foo/bar null) # => { bar = null; } - _shortenTreeBase = targetBaseComponents: fileset: + _shortenTreeBase = + targetBaseComponents: fileset: let - recurse = index: + recurse = + index: # If we haven't reached the required depth yet if index < length fileset._internalBaseComponents then - # Create an attribute set and recurse as the value, this can be lazily evaluated this way - { - ${elemAt fileset._internalBaseComponents index} = recurse (index + 1); - } else - # Otherwise we reached the appropriate depth, here's the original tree + # Create an attribute set and recurse as the value, this can be lazily evaluated this way + { ${elemAt fileset._internalBaseComponents index} = recurse (index + 1); } + else + # Otherwise we reached the appropriate depth, here's the original tree fileset._internalTree; - in recurse (length targetBaseComponents); + in + recurse (length targetBaseComponents); # Transforms the filesetTree of a file set to a longer base path, e.g. # _lengthenTreeBase [ "foo" "bar" ] (_create /foo { bar.baz = "regular"; }) # => { baz = "regular"; } - _lengthenTreeBase = targetBaseComponents: fileset: + _lengthenTreeBase = + targetBaseComponents: fileset: let - recurse = index: tree: + recurse = + index: tree: # If the filesetTree is an attribute set and we haven't reached the required depth yet if isAttrs tree && index < length targetBaseComponents then - # Recurse with the tree under the right component (which might not exist) - recurse (index + 1) - (tree.${elemAt targetBaseComponents index} or null) + # Recurse with the tree under the right component (which might not exist) + recurse (index + 1) (tree.${elemAt targetBaseComponents index} or null) else - # For all values here we can just return the tree itself: - # tree == null -> the result is also null, everything is excluded - # tree == "directory" -> the result is also "directory", - # because the base path is always a directory and everything is included - # isAttrs tree -> the result is `tree` - # because we don't need to recurse any more since `index == length longestBaseComponents` + # For all values here we can just return the tree itself: + # tree == null -> the result is also null, everything is excluded + # tree == "directory" -> the result is also "directory", + # because the base path is always a directory and everything is included + # isAttrs tree -> the result is `tree` + # because we don't need to recurse any more since `index == length longestBaseComponents` tree; - in recurse (length fileset._internalBaseComponents) fileset._internalTree; + in + recurse (length fileset._internalBaseComponents) fileset._internalTree; # Computes the union of a list of filesets. # The filesets must already be coerced and validated to be in the same filesystem root # Type: [ Fileset ] -> Fileset - _unionMany = filesets: + _unionMany = + filesets: let # All filesets that have a base, aka not the ones that are the empty value without a base - filesetsWithBase = - filter (fileset: !fileset._internalIsEmptyWithoutBase) filesets; + filesetsWithBase = filter (fileset: !fileset._internalIsEmptyWithoutBase) filesets; # The first fileset that has a base. # This value is only accessed if there are at all. @@ -542,17 +617,16 @@ in rec { # A list of path components common to all base paths. # Note that commonPrefix can only be fully evaluated, # so this cannot cause a stack overflow due to a build-up of unevaluated thunks. - commonBaseComponents = foldl' - (components: el: commonPrefix components el._internalBaseComponents) - firstWithBase._internalBaseComponents - # We could also not do the `tail` here to avoid a list allocation, - # but then we'd have to pay for a potentially expensive - # but unnecessary `commonPrefix` call - (tail filesetsWithBase); + commonBaseComponents = + foldl' (components: el: commonPrefix components el._internalBaseComponents) + firstWithBase._internalBaseComponents + # We could also not do the `tail` here to avoid a list allocation, + # but then we'd have to pay for a potentially expensive + # but unnecessary `commonPrefix` call + (tail filesetsWithBase); # The common base path assembled from a filesystem root and the common components - commonBase = - append firstWithBase._internalBaseRoot (join commonBaseComponents); + commonBase = append firstWithBase._internalBaseRoot (join commonBaseComponents); # A list of filesetTree's that all have the same base path # This is achieved by nesting the trees into the components they have over the common base path @@ -566,43 +640,43 @@ in rec { # We do not use a fold here because it would cause a thunk build-up # which could cause a stack overflow for a large number of trees resultTree = _unionTrees trees; - # If there's no values with a base, we have no files - in if filesetsWithBase == [ ] then - _emptyWithoutBase - else - _create commonBase resultTree; + in + # If there's no values with a base, we have no files + if filesetsWithBase == [ ] then _emptyWithoutBase else _create commonBase resultTree; # The union of multiple filesetTree's with the same base path. # Later elements are only evaluated if necessary. # Type: [ filesetTree ] -> filesetTree - _unionTrees = trees: + _unionTrees = + trees: let stringIndex = findFirstIndex isString null trees; withoutNull = filter (tree: tree != null) trees; - in if stringIndex != null then - # If there's a string, it's always a fully included tree (dir or file), - # no need to look at other elements + in + if stringIndex != null then + # If there's a string, it's always a fully included tree (dir or file), + # no need to look at other elements elemAt trees stringIndex else if withoutNull == [ ] then - # If all trees are null, then the resulting tree is also null + # If all trees are null, then the resulting tree is also null null else - # The non-null elements have to be attribute sets representing partial trees - # We need to recurse into those + # The non-null elements have to be attribute sets representing partial trees + # We need to recurse into those zipAttrsWith (name: _unionTrees) withoutNull; # Computes the intersection of a list of filesets. # The filesets must already be coerced and validated to be in the same filesystem root # Type: Fileset -> Fileset -> Fileset - _intersection = fileset1: fileset2: + _intersection = + fileset1: fileset2: let # The common base components prefix, e.g. # (/foo/bar, /foo/bar/baz) -> /foo/bar # (/foo/bar, /foo/baz) -> /foo commonBaseComponentsLength = # TODO: Have a `lib.lists.commonPrefixLength` function such that we don't need the list allocation from commonPrefix here - length (commonPrefix fileset1._internalBaseComponents - fileset2._internalBaseComponents); + length (commonPrefix fileset1._internalBaseComponents fileset2._internalBaseComponents); # To be able to intersect filesetTree's together, they need to have the same base path. # Base paths can be intersected by taking the longest one (if any) @@ -610,18 +684,17 @@ in rec { # The fileset with the longest base, if any, e.g. # (/foo/bar, /foo/bar/baz) -> /foo/bar/baz # (/foo/bar, /foo/baz) -> null - longestBaseFileset = if commonBaseComponentsLength - == length fileset1._internalBaseComponents then - # The common prefix is the same as the first path, so the second path is equal or longer - fileset2 - else if commonBaseComponentsLength - == length fileset2._internalBaseComponents then - # The common prefix is the same as the second path, so the first path is longer - fileset1 - else - # The common prefix is neither the first nor the second path - # This means there's no overlap between the two sets - null; + longestBaseFileset = + if commonBaseComponentsLength == length fileset1._internalBaseComponents then + # The common prefix is the same as the first path, so the second path is equal or longer + fileset2 + else if commonBaseComponentsLength == length fileset2._internalBaseComponents then + # The common prefix is the same as the second path, so the first path is longer + fileset1 + else + # The common prefix is neither the first nor the second path + # This means there's no overlap between the two sets + null; # Whether the result should be the empty value without a base resultIsEmptyWithoutBase = @@ -632,14 +705,13 @@ in rec { || longestBaseFileset == null; # Lengthen each fileset's tree to the longest base prefix - tree1 = - _lengthenTreeBase longestBaseFileset._internalBaseComponents fileset1; - tree2 = - _lengthenTreeBase longestBaseFileset._internalBaseComponents fileset2; + tree1 = _lengthenTreeBase longestBaseFileset._internalBaseComponents fileset1; + tree2 = _lengthenTreeBase longestBaseFileset._internalBaseComponents fileset2; # With two filesetTree's with the same base, we can compute their intersection resultTree = _intersectTree tree1 tree2; - in if resultIsEmptyWithoutBase then + in + if resultIsEmptyWithoutBase then _emptyWithoutBase else _create longestBaseFileset._internalBase resultTree; @@ -647,32 +719,32 @@ in rec { # The intersection of two filesetTree's with the same base path # The second element is only evaluated as much as necessary. # Type: filesetTree -> filesetTree -> filesetTree - _intersectTree = lhs: rhs: + _intersectTree = + lhs: rhs: if isAttrs lhs && isAttrs rhs then - # Both sides are attribute sets, we can recurse for the attributes existing on both sides - mapAttrs (name: _intersectTree lhs.${name}) - (builtins.intersectAttrs lhs rhs) + # Both sides are attribute sets, we can recurse for the attributes existing on both sides + mapAttrs (name: _intersectTree lhs.${name}) (builtins.intersectAttrs lhs rhs) else if lhs == null || isString rhs then - # If the lhs is null, the result should also be null - # And if the rhs is the identity element - # (a string, aka it includes everything), then it's also the lhs + # If the lhs is null, the result should also be null + # And if the rhs is the identity element + # (a string, aka it includes everything), then it's also the lhs lhs else - # In all other cases it's the rhs + # In all other cases it's the rhs rhs; # Compute the set difference between two file sets. # The filesets must already be coerced and validated to be in the same filesystem root. # Type: Fileset -> Fileset -> Fileset - _difference = positive: negative: + _difference = + positive: negative: let # The common base components prefix, e.g. # (/foo/bar, /foo/bar/baz) -> /foo/bar # (/foo/bar, /foo/baz) -> /foo commonBaseComponentsLength = # TODO: Have a `lib.lists.commonPrefixLength` function such that we don't need the list allocation from commonPrefix here - length (commonPrefix positive._internalBaseComponents - negative._internalBaseComponents); + length (commonPrefix positive._internalBaseComponents negative._internalBaseComponents); # We need filesetTree's with the same base to be able to compute the difference between them # This here is the filesetTree from the negative file set, but for a base path that matches the positive file set. @@ -683,96 +755,99 @@ in rec { # because under the base path of `/foo/bar`, everything from the negative file set is included # For `difference /foo /bar`, `negativeTreeWithPositiveBase = null` # because under the base path of `/foo`, nothing from the negative file set is included - negativeTreeWithPositiveBase = if commonBaseComponentsLength - == length positive._internalBaseComponents then - # The common prefix is the same as the positive base path, so the second path is equal or longer. - # We need to _shorten_ the negative filesetTree to the same base path as the positive one - # E.g. for `difference /foo /foo/bar` the common prefix is /foo, equal to the positive file set's base - # So we need to shorten the base of the tree for the negative argument from /foo/bar to just /foo - _shortenTreeBase positive._internalBaseComponents negative - else if commonBaseComponentsLength - == length negative._internalBaseComponents then - # The common prefix is the same as the negative base path, so the first path is longer. - # We need to lengthen the negative filesetTree to the same base path as the positive one. - # E.g. for `difference /foo/bar /foo` the common prefix is /foo, equal to the negative file set's base - # So we need to lengthen the base of the tree for the negative argument from /foo to /foo/bar - _lengthenTreeBase positive._internalBaseComponents negative - else - # The common prefix is neither the first nor the second path. - # This means there's no overlap between the two file sets, - # and nothing from the negative argument should get removed from the positive one - # E.g for `difference /foo /bar`, we remove nothing to get the same as `/foo` - null; + negativeTreeWithPositiveBase = + if commonBaseComponentsLength == length positive._internalBaseComponents then + # The common prefix is the same as the positive base path, so the second path is equal or longer. + # We need to _shorten_ the negative filesetTree to the same base path as the positive one + # E.g. for `difference /foo /foo/bar` the common prefix is /foo, equal to the positive file set's base + # So we need to shorten the base of the tree for the negative argument from /foo/bar to just /foo + _shortenTreeBase positive._internalBaseComponents negative + else if commonBaseComponentsLength == length negative._internalBaseComponents then + # The common prefix is the same as the negative base path, so the first path is longer. + # We need to lengthen the negative filesetTree to the same base path as the positive one. + # E.g. for `difference /foo/bar /foo` the common prefix is /foo, equal to the negative file set's base + # So we need to lengthen the base of the tree for the negative argument from /foo to /foo/bar + _lengthenTreeBase positive._internalBaseComponents negative + else + # The common prefix is neither the first nor the second path. + # This means there's no overlap between the two file sets, + # and nothing from the negative argument should get removed from the positive one + # E.g for `difference /foo /bar`, we remove nothing to get the same as `/foo` + null; resultingTree = _differenceTree positive._internalBase positive._internalTree - negativeTreeWithPositiveBase; - # If the first file set is empty, we can never have any files in the result - in if positive._internalIsEmptyWithoutBase then + negativeTreeWithPositiveBase; + in + # If the first file set is empty, we can never have any files in the result + if positive._internalIsEmptyWithoutBase then _emptyWithoutBase - # If the second file set is empty, nothing gets removed, so the result is just the first file set + # If the second file set is empty, nothing gets removed, so the result is just the first file set else if negative._internalIsEmptyWithoutBase then positive else - # We use the positive file set base for the result, - # because only files from the positive side may be included, - # which is what base path is for + # We use the positive file set base for the result, + # because only files from the positive side may be included, + # which is what base path is for _create positive._internalBase resultingTree; # Computes the set difference of two filesetTree's # Type: Path -> filesetTree -> filesetTree - _differenceTree = path: lhs: rhs: + _differenceTree = + path: lhs: rhs: # If the lhs doesn't have any files, or the right hand side includes all files if lhs == null || isString rhs then - # The result will always be empty + # The result will always be empty null - # If the right hand side has no files + # If the right hand side has no files else if rhs == null then - # The result is always the left hand side, because nothing gets removed + # The result is always the left hand side, because nothing gets removed lhs else - # Otherwise we always have two attribute sets to recurse into - mapAttrs (name: lhsValue: - _differenceTree (path + "/${name}") lhsValue (rhs.${name} or null)) - (_directoryEntries path lhs); + # Otherwise we always have two attribute sets to recurse into + mapAttrs (name: lhsValue: _differenceTree (path + "/${name}") lhsValue (rhs.${name} or null)) ( + _directoryEntries path lhs + ); # Filters all files in a path based on a predicate # Type: ({ name, type, ... } -> Bool) -> Path -> FileSet - _fileFilter = predicate: root: + _fileFilter = + predicate: root: let # Check the predicate for a single file # Type: String -> String -> filesetTree - fromFile = name: type: - if predicate { - inherit name type; - hasExt = ext: hasSuffix ".${ext}" name; - - # To ensure forwards compatibility with more arguments being added in the future, - # adding an attribute which can't be deconstructed :) - "lib.fileset.fileFilter: The predicate function passed as the first argument must be able to handle extra attributes for future compatibility. If you're using `{ name, file, hasExt }:`, use `{ name, file, hasExt, ... }:` instead." = - null; - } then + fromFile = + name: type: + if + predicate { + inherit name type; + hasExt = ext: hasSuffix ".${ext}" name; + + # To ensure forwards compatibility with more arguments being added in the future, + # adding an attribute which can't be deconstructed :) + "lib.fileset.fileFilter: The predicate function passed as the first argument must be able to handle extra attributes for future compatibility. If you're using `{ name, file, hasExt }:`, use `{ name, file, hasExt, ... }:` instead." = + null; + } + then type else null; # Check the predicate for all files in a directory # Type: Path -> filesetTree - fromDir = path: - mapAttrs (name: type: - if type == "directory" then - fromDir (path + "/${name}") - else - fromFile name type) (readDir path); + fromDir = + path: + mapAttrs ( + name: type: if type == "directory" then fromDir (path + "/${name}") else fromFile name type + ) (readDir path); rootType = pathType root; - in if rootType == "directory" then + in + if rootType == "directory" then _create root (fromDir root) else - # Single files are turned into a directory containing that file or nothing. - _create (dirOf root) { - ${baseNameOf root} = fromFile (baseNameOf root) rootType; - }; + # Single files are turned into a directory containing that file or nothing. + _create (dirOf root) { ${baseNameOf root} = fromFile (baseNameOf root) rootType; }; # Support for `builtins.fetchGit` with `submodules = true` was introduced in 2.4 # https://github.com/NixOS/nix/commit/55cefd41d63368d4286568e2956afd535cb44018 @@ -788,81 +863,79 @@ in rec { # - The store path must not include files that don't exist in the respective local path. # # Type: Path -> String -> FileSet - _mirrorStorePath = localPath: storePath: + _mirrorStorePath = + localPath: storePath: let - recurse = focusedStorePath: - mapAttrs (name: type: - if type == "directory" then - recurse (focusedStorePath + "/${name}") - else - type) (builtins.readDir focusedStorePath); - in _create localPath (recurse storePath); + recurse = + focusedStorePath: + mapAttrs ( + name: type: if type == "directory" then recurse (focusedStorePath + "/${name}") else type + ) (builtins.readDir focusedStorePath); + in + _create localPath (recurse storePath); # Create a file set from the files included in the result of a fetchGit call # Type: String -> String -> Path -> Attrs -> FileSet - _fromFetchGit = function: argument: path: extraFetchGitAttrs: + _fromFetchGit = + function: argument: path: extraFetchGitAttrs: let # The code path for when isStorePath is true - tryStorePath = if pathExists (path + "/.git") then - # If there is a `.git` directory in the path, - # it means that the path was imported unfiltered into the Nix store. - # This function should throw in such a case, because - # - `fetchGit` doesn't generally work with `.git` directories in store paths - # - Importing the entire path could include Git-tracked files - throw '' - lib.fileset.${function}: The ${argument} (${ - toString path - }) is a store path within a working tree of a Git repository. - This indicates that a source directory was imported into the store using a method such as `import "''${./.}"` or `path:.`. - This function currently does not support such a use case, since it currently relies on `builtins.fetchGit`. - You could make this work by using a fetcher such as `fetchGit` instead of copying the whole repository. - If you can't avoid copying the repo to the store, see https://github.com/NixOS/nix/issues/9292.'' - else - # Otherwise we're going to assume that the path was a Git directory originally, - # but it was fetched using a method that already removed files not tracked by Git, - # such as `builtins.fetchGit`, `pkgs.fetchgit` or others. - # So we can just import the path in its entirety. - _singleton path; + tryStorePath = + if pathExists (path + "/.git") then + # If there is a `.git` directory in the path, + # it means that the path was imported unfiltered into the Nix store. + # This function should throw in such a case, because + # - `fetchGit` doesn't generally work with `.git` directories in store paths + # - Importing the entire path could include Git-tracked files + throw '' + lib.fileset.${function}: The ${argument} (${toString path}) is a store path within a working tree of a Git repository. + This indicates that a source directory was imported into the store using a method such as `import "''${./.}"` or `path:.`. + This function currently does not support such a use case, since it currently relies on `builtins.fetchGit`. + You could make this work by using a fetcher such as `fetchGit` instead of copying the whole repository. + If you can't avoid copying the repo to the store, see https://github.com/NixOS/nix/issues/9292.'' + else + # Otherwise we're going to assume that the path was a Git directory originally, + # but it was fetched using a method that already removed files not tracked by Git, + # such as `builtins.fetchGit`, `pkgs.fetchgit` or others. + # So we can just import the path in its entirety. + _singleton path; # The code path for when isStorePath is false - tryFetchGit = let - # This imports the files unnecessarily, which currently can't be avoided - # because `builtins.fetchGit` is the only function exposing which files are tracked by Git. - # With the [lazy trees PR](https://github.com/NixOS/nix/pull/6530), - # the unnecessarily import could be avoided. - # However a simpler alternative still would be [a builtins.gitLsFiles](https://github.com/NixOS/nix/issues/2944). - fetchResult = fetchGit ({ - url = path; - } - # In older Nix versions, repositories were always assumed to be deep clones, which made `fetchGit` fail for shallow clones - # For newer versions this was fixed, but the `shallow` flag is required. - # The only behavioral difference is that for shallow clones, `fetchGit` doesn't return a `revCount`, - # which we don't need here, so it's fine to always pass it. - # Unfortunately this means older Nix versions get a poor error message for shallow repositories, and there's no good way to improve that. - # Checking for `.git/shallow` doesn't seem worth it, especially since that's more of an implementation detail, - # and would also require more code to handle worktrees where `.git` is a file. - // optionalAttrs (versionAtLeast nixVersion _fetchGitShallowMinver) { - shallow = true; - } // extraFetchGitAttrs); + tryFetchGit = + let + # This imports the files unnecessarily, which currently can't be avoided + # because `builtins.fetchGit` is the only function exposing which files are tracked by Git. + # With the [lazy trees PR](https://github.com/NixOS/nix/pull/6530), + # the unnecessarily import could be avoided. + # However a simpler alternative still would be [a builtins.gitLsFiles](https://github.com/NixOS/nix/issues/2944). + fetchResult = fetchGit ( + { + url = path; + } + # In older Nix versions, repositories were always assumed to be deep clones, which made `fetchGit` fail for shallow clones + # For newer versions this was fixed, but the `shallow` flag is required. + # The only behavioral difference is that for shallow clones, `fetchGit` doesn't return a `revCount`, + # which we don't need here, so it's fine to always pass it. + # Unfortunately this means older Nix versions get a poor error message for shallow repositories, and there's no good way to improve that. + # Checking for `.git/shallow` doesn't seem worth it, especially since that's more of an implementation detail, + # and would also require more code to handle worktrees where `.git` is a file. + // optionalAttrs (versionAtLeast nixVersion _fetchGitShallowMinver) { shallow = true; } + // extraFetchGitAttrs + ); + in # We can identify local working directories by checking for .git, # see https://git-scm.com/docs/gitrepository-layout#_description. # Note that `builtins.fetchGit` _does_ work for bare repositories (where there's no `.git`), # even though `git ls-files` wouldn't return any files in that case. - in if !pathExists (path + "/.git") then - throw "lib.fileset.${function}: Expected the ${argument} (${ - toString path - }) to point to a local working tree of a Git repository, but it's not." - else - _mirrorStorePath path fetchResult.outPath; - in if !isPath path then - throw - "lib.fileset.${function}: Expected the ${argument} to be a path, but it's a ${ - typeOf path - } instead." + if !pathExists (path + "/.git") then + throw "lib.fileset.${function}: Expected the ${argument} (${toString path}) to point to a local working tree of a Git repository, but it's not." + else + _mirrorStorePath path fetchResult.outPath; + in + if !isPath path then + throw "lib.fileset.${function}: Expected the ${argument} to be a path, but it's a ${typeOf path} instead." else if pathType path != "directory" then - throw "lib.fileset.${function}: Expected the ${argument} (${ - toString path - }) to be a directory, but it's a file instead." + throw "lib.fileset.${function}: Expected the ${argument} (${toString path}) to be a directory, but it's a file instead." else if hasStorePathPrefix path then tryStorePath else diff --git a/nix/stdlib/fileset/mock-splitRoot.nix b/nix/stdlib/fileset/mock-splitRoot.nix index 05109f6..96d43a4 100644 --- a/nix/stdlib/fileset/mock-splitRoot.nix +++ b/nix/stdlib/fileset/mock-splitRoot.nix @@ -8,17 +8,22 @@ # } self: super: { path = super.path // { - splitRoot = path: + splitRoot = + path: let parts = super.path.splitRoot path; components = self.path.subpath.components parts.subpath; count = self.length components; - rootIndex = count - - self.lists.findFirstIndex (component: component == "mock-root") - (self.length components) (self.reverseList components); - root = self.path.append parts.root - (self.path.subpath.join (self.take rootIndex components)); + rootIndex = + count + - self.lists.findFirstIndex (component: component == "mock-root") (self.length components) ( + self.reverseList components + ); + root = self.path.append parts.root (self.path.subpath.join (self.take rootIndex components)); subpath = self.path.subpath.join (self.drop rootIndex components); - in { inherit root subpath; }; + in + { + inherit root subpath; + }; }; } diff --git a/nix/stdlib/filesystem.nix b/nix/stdlib/filesystem.nix index 522d1a4..b4d8c5e 100644 --- a/nix/stdlib/filesystem.nix +++ b/nix/stdlib/filesystem.nix @@ -1,5 +1,6 @@ -/* Functions for querying information about the filesystem - without copying any files to the Nix store. +/* + Functions for querying information about the filesystem + without copying any files to the Nix store. */ { lib }: # Tested in lib/tests/filesystem.sh @@ -11,75 +12,84 @@ let inherit (lib.filesystem) pathType; inherit (lib.strings) hasSuffix removeSuffix; -in { - /* The type of a path. The path needs to exist and be accessible. - The result is either "directory" for a directory, "regular" for a regular file, "symlink" for a symlink, or "unknown" for anything else. +in +{ + /* + The type of a path. The path needs to exist and be accessible. + The result is either "directory" for a directory, "regular" for a regular file, "symlink" for a symlink, or "unknown" for anything else. - Type: - pathType :: Path -> String + Type: + pathType :: Path -> String - Example: - pathType /. - => "directory" + Example: + pathType /. + => "directory" - pathType /some/file.nix - => "regular" + pathType /some/file.nix + => "regular" */ - pathType = builtins.readFileType or + pathType = + builtins.readFileType or # Nix <2.14 compatibility shim - (path: - if !pathExists path + ( + path: + if + !pathExists path # Fail irrecoverably to mimic the historic behavior of this function and # the new builtins.readFileType then abort "lib.filesystem.pathType: Path ${toString path} does not exist." - # The filesystem root is the only path where `dirOf / == /` and - # `baseNameOf /` is not valid. We can detect this and directly return - # "directory", since we know the filesystem root can't be anything else. + # The filesystem root is the only path where `dirOf / == /` and + # `baseNameOf /` is not valid. We can detect this and directly return + # "directory", since we know the filesystem root can't be anything else. else if dirOf path == path then "directory" else - (readDir (dirOf path)).${baseNameOf path}); + (readDir (dirOf path)).${baseNameOf path} + ); - /* Whether a path exists and is a directory. + /* + Whether a path exists and is a directory. - Type: - pathIsDirectory :: Path -> Bool + Type: + pathIsDirectory :: Path -> Bool - Example: - pathIsDirectory /. - => true + Example: + pathIsDirectory /. + => true - pathIsDirectory /this/does/not/exist - => false + pathIsDirectory /this/does/not/exist + => false - pathIsDirectory /some/file.nix - => false + pathIsDirectory /some/file.nix + => false */ pathIsDirectory = path: pathExists path && pathType path == "directory"; - /* Whether a path exists and is a regular file, meaning not a symlink or any other special file type. + /* + Whether a path exists and is a regular file, meaning not a symlink or any other special file type. - Type: - pathIsRegularFile :: Path -> Bool + Type: + pathIsRegularFile :: Path -> Bool - Example: - pathIsRegularFile /. - => false + Example: + pathIsRegularFile /. + => false - pathIsRegularFile /this/does/not/exist - => false + pathIsRegularFile /this/does/not/exist + => false - pathIsRegularFile /some/file.nix - => true + pathIsRegularFile /some/file.nix + => true */ pathIsRegularFile = path: pathExists path && pathType path == "regular"; - /* A map of all haskell packages defined in the given path, - identified by having a cabal file with the same name as the - directory itself. + /* + A map of all haskell packages defined in the given path, + identified by having a cabal file with the same name as the + directory itself. - Type: Path -> Map String Path + Type: Path -> Map String Path */ haskellPathsInDir = # The directory within to search @@ -93,15 +103,17 @@ in { value = root + "/${file}"; }) root-files; # Subdirectories of the root with a cabal file. - cabal-subdirs = builtins.filter - ({ name, value, }: builtins.pathExists (value + "/${name}.cabal")) - root-files-with-paths; - in builtins.listToAttrs cabal-subdirs; - /* Find the first directory containing a file matching 'pattern' - upward from a given 'file'. - Returns 'null' if no directories contain a file matching 'pattern'. - - Type: RegExp -> Path -> Nullable { path : Path; matches : [ MatchResults ]; } + cabal-subdirs = builtins.filter ( + { name, value }: builtins.pathExists (value + "/${name}.cabal") + ) root-files-with-paths; + in + builtins.listToAttrs cabal-subdirs; + /* + Find the first directory containing a file matching 'pattern' + upward from a given 'file'. + Returns 'null' if no directories contain a file matching 'pattern'. + + Type: RegExp -> Path -> Nullable { path : Path; matches : [ MatchResults ]; } */ locateDominatingFile = # The pattern to search for @@ -109,162 +121,182 @@ in { # The file to start searching upward from file: let - go = path: + go = + path: let files = builtins.attrNames (builtins.readDir path); - matches = builtins.filter (match: match != null) - (map (builtins.match pattern) files); - in if builtins.length matches != 0 then { - inherit path matches; - } else if path == /. then + matches = builtins.filter (match: match != null) (map (builtins.match pattern) files); + in + if builtins.length matches != 0 then + { inherit path matches; } + else if path == /. then null else go (dirOf path); parent = dirOf file; - isDir = let - base = baseNameOf file; - type = (builtins.readDir parent).${base} or null; - in file == /. || type == "directory"; - in go (if isDir then file else parent); + isDir = + let + base = baseNameOf file; + type = (builtins.readDir parent).${base} or null; + in + file == /. || type == "directory"; + in + go (if isDir then file else parent); - /* Given a directory, return a flattened list of all files within it recursively. + /* + Given a directory, return a flattened list of all files within it recursively. - Type: Path -> [ Path ] + Type: Path -> [ Path ] */ listFilesRecursive = # The path to recursively list dir: - lib.flatten (lib.mapAttrsToList (name: type: - if type == "directory" then - lib.filesystem.listFilesRecursive (dir + "/${name}") - else - dir + "/${name}") (builtins.readDir dir)); - - /* Transform a directory tree containing package files suitable for - `callPackage` into a matching nested attribute set of derivations. - - For a directory tree like this: - - ``` - my-packages - ├── a.nix - ├── b.nix - ├── c - │ ├── my-extra-feature.patch - │ ├── package.nix - │ └── support-definitions.nix - └── my-namespace - ├── d.nix - ├── e.nix - └── f - └── package.nix - ``` - - `packagesFromDirectoryRecursive` will produce an attribute set like this: - - ```nix - # packagesFromDirectoryRecursive { - # callPackage = pkgs.callPackage; - # directory = ./my-packages; - # } - { - a = pkgs.callPackage ./my-packages/a.nix { }; - b = pkgs.callPackage ./my-packages/b.nix { }; - c = pkgs.callPackage ./my-packages/c/package.nix { }; - my-namespace = { - d = pkgs.callPackage ./my-packages/my-namespace/d.nix { }; - e = pkgs.callPackage ./my-packages/my-namespace/e.nix { }; - f = pkgs.callPackage ./my-packages/my-namespace/f/package.nix { }; - }; - } - ``` - - In particular: - - If the input directory contains a `package.nix` file, then - `callPackage /package.nix { }` is returned. - - Otherwise, the input directory's contents are listed and transformed into - an attribute set. - - If a file name has the `.nix` extension, it is turned into attribute - where: - - The attribute name is the file name without the `.nix` extension - - The attribute value is `callPackage { }` - - Other files are ignored. - - Directories are turned into an attribute where: - - The attribute name is the name of the directory - - The attribute value is the result of calling - `packagesFromDirectoryRecursive { ... }` on the directory. - - As a result, directories with no `.nix` files (including empty - directories) will be transformed into empty attribute sets. - - Example: - packagesFromDirectoryRecursive { - inherit (pkgs) callPackage; - directory = ./my-packages; - } - => { ... } - - lib.makeScope pkgs.newScope ( - self: packagesFromDirectoryRecursive { - callPackage = self.callPackage; - directory = ./my-packages; - } - ) - => { ... } - - Type: - packagesFromDirectoryRecursive :: AttrSet -> AttrSet + lib.flatten ( + lib.mapAttrsToList ( + name: type: + if type == "directory" then + lib.filesystem.listFilesRecursive (dir + "/${name}") + else + dir + "/${name}" + ) (builtins.readDir dir) + ); + + /* + Transform a directory tree containing package files suitable for + `callPackage` into a matching nested attribute set of derivations. + + For a directory tree like this: + + ``` + my-packages + ├── a.nix + ├── b.nix + ├── c + │ ├── my-extra-feature.patch + │ ├── package.nix + │ └── support-definitions.nix + └── my-namespace + ├── d.nix + ├── e.nix + └── f + └── package.nix + ``` + + `packagesFromDirectoryRecursive` will produce an attribute set like this: + + ```nix + # packagesFromDirectoryRecursive { + # callPackage = pkgs.callPackage; + # directory = ./my-packages; + # } + { + a = pkgs.callPackage ./my-packages/a.nix { }; + b = pkgs.callPackage ./my-packages/b.nix { }; + c = pkgs.callPackage ./my-packages/c/package.nix { }; + my-namespace = { + d = pkgs.callPackage ./my-packages/my-namespace/d.nix { }; + e = pkgs.callPackage ./my-packages/my-namespace/e.nix { }; + f = pkgs.callPackage ./my-packages/my-namespace/f/package.nix { }; + }; + } + ``` + + In particular: + - If the input directory contains a `package.nix` file, then + `callPackage /package.nix { }` is returned. + - Otherwise, the input directory's contents are listed and transformed into + an attribute set. + - If a file name has the `.nix` extension, it is turned into attribute + where: + - The attribute name is the file name without the `.nix` extension + - The attribute value is `callPackage { }` + - Other files are ignored. + - Directories are turned into an attribute where: + - The attribute name is the name of the directory + - The attribute value is the result of calling + `packagesFromDirectoryRecursive { ... }` on the directory. + + As a result, directories with no `.nix` files (including empty + directories) will be transformed into empty attribute sets. + + Example: + packagesFromDirectoryRecursive { + inherit (pkgs) callPackage; + directory = ./my-packages; + } + => { ... } + + lib.makeScope pkgs.newScope ( + self: packagesFromDirectoryRecursive { + callPackage = self.callPackage; + directory = ./my-packages; + } + ) + => { ... } + + Type: + packagesFromDirectoryRecursive :: AttrSet -> AttrSet */ packagesFromDirectoryRecursive = # Options. { - /* `pkgs.callPackage` - - Type: - Path -> AttrSet -> a - */ - callPackage, - /* The directory to read package files from - - Type: - Path - */ - directory, ... }: + /* + `pkgs.callPackage` + + Type: + Path -> AttrSet -> a + */ + callPackage, + /* + The directory to read package files from + + Type: + Path + */ + directory, + ... + }: let # Determine if a directory entry from `readDir` indicates a package or # directory of packages. - directoryEntryIsPackage = basename: type: - type == "directory" || hasSuffix ".nix" basename; + directoryEntryIsPackage = basename: type: type == "directory" || hasSuffix ".nix" basename; # List directory entries that indicate packages in the given `path`. - packageDirectoryEntries = path: - filterAttrs directoryEntryIsPackage (readDir path); + packageDirectoryEntries = path: filterAttrs directoryEntryIsPackage (readDir path); # Transform a directory entry (a `basename` and `type` pair) into a # package. - directoryEntryToAttrPair = subdirectory: basename: type: - let path = subdirectory + "/${basename}"; - in if type == "regular" then { - name = removeSuffix ".nix" basename; - value = callPackage path { }; - } else if type == "directory" then { - name = basename; - value = packagesFromDirectory path; - } else + directoryEntryToAttrPair = + subdirectory: basename: type: + let + path = subdirectory + "/${basename}"; + in + if type == "regular" then + { + name = removeSuffix ".nix" basename; + value = callPackage path { }; + } + else if type == "directory" then + { + name = basename; + value = packagesFromDirectory path; + } + else throw '' - lib.filesystem.packagesFromDirectoryRecursive: Unsupported file type ${type} at path ${ - toString subdirectory - } + lib.filesystem.packagesFromDirectoryRecursive: Unsupported file type ${type} at path ${toString subdirectory} ''; # Transform a directory into a package (if there's a `package.nix`) or # set of packages (otherwise). - packagesFromDirectory = path: - let defaultPackagePath = path + "/package.nix"; - in if pathExists defaultPackagePath then + packagesFromDirectory = + path: + let + defaultPackagePath = path + "/package.nix"; + in + if pathExists defaultPackagePath then callPackage defaultPackagePath { } else - mapAttrs' (directoryEntryToAttrPair path) - (packageDirectoryEntries path); - in packagesFromDirectory directory; + mapAttrs' (directoryEntryToAttrPair path) (packageDirectoryEntries path); + in + packagesFromDirectory directory; } diff --git a/nix/stdlib/fixed-points.nix b/nix/stdlib/fixed-points.nix index e1a05d2..26ce089 100644 --- a/nix/stdlib/fixed-points.nix +++ b/nix/stdlib/fixed-points.nix @@ -1,229 +1,251 @@ -{ lib, ... }: rec { - /* `fix f` computes the fixed point of the given function `f`. In other words, the return value is `x` in `x = f x`. - - `f` must be a lazy function. - This means that `x` must be a value that can be partially evaluated, - such as an attribute set, a list, or a function. - This way, `f` can use one part of `x` to compute another part. - - **Relation to syntactic recursion** - - This section explains `fix` by refactoring from syntactic recursion to a call of `fix` instead. - - For context, Nix lets you define attributes in terms of other attributes syntactically using the [`rec { }` syntax](https://nixos.org/manual/nix/stable/language/constructs.html#recursive-sets). - - ```nix - nix-repl> rec { - foo = "foo"; - bar = "bar"; - foobar = foo + bar; - } - { bar = "bar"; foo = "foo"; foobar = "foobar"; } - ``` - - This is convenient when constructing a value to pass to a function for example, - but an equivalent effect can be achieved with the `let` binding syntax: - - ```nix - nix-repl> let self = { - foo = "foo"; - bar = "bar"; - foobar = self.foo + self.bar; - }; in self - { bar = "bar"; foo = "foo"; foobar = "foobar"; } - ``` - - But in general you can get more reuse out of `let` bindings by refactoring them to a function. - - ```nix - nix-repl> f = self: { - foo = "foo"; - bar = "bar"; - foobar = self.foo + self.bar; - } - ``` - - This is where `fix` comes in, it contains the syntactic recursion that's not in `f` anymore. - - ```nix - nix-repl> fix = f: - let self = f self; in self; - ``` - - By applying `fix` we get the final result. - - ```nix - nix-repl> fix f - { bar = "bar"; foo = "foo"; foobar = "foobar"; } - ``` - - Such a refactored `f` using `fix` is not useful by itself. - See [`extends`](#function-library-lib.fixedPoints.extends) for an example use case. - There `self` is also often called `final`. - - Type: fix :: (a -> a) -> a - - Example: - fix (self: { foo = "foo"; bar = "bar"; foobar = self.foo + self.bar; }) - => { bar = "bar"; foo = "foo"; foobar = "foobar"; } - - fix (self: [ 1 2 (elemAt self 0 + elemAt self 1) ]) - => [ 1 2 3 ] +{ lib, ... }: +rec { + /* + `fix f` computes the fixed point of the given function `f`. In other words, the return value is `x` in `x = f x`. + + `f` must be a lazy function. + This means that `x` must be a value that can be partially evaluated, + such as an attribute set, a list, or a function. + This way, `f` can use one part of `x` to compute another part. + + **Relation to syntactic recursion** + + This section explains `fix` by refactoring from syntactic recursion to a call of `fix` instead. + + For context, Nix lets you define attributes in terms of other attributes syntactically using the [`rec { }` syntax](https://nixos.org/manual/nix/stable/language/constructs.html#recursive-sets). + + ```nix + nix-repl> rec { + foo = "foo"; + bar = "bar"; + foobar = foo + bar; + } + { bar = "bar"; foo = "foo"; foobar = "foobar"; } + ``` + + This is convenient when constructing a value to pass to a function for example, + but an equivalent effect can be achieved with the `let` binding syntax: + + ```nix + nix-repl> let self = { + foo = "foo"; + bar = "bar"; + foobar = self.foo + self.bar; + }; in self + { bar = "bar"; foo = "foo"; foobar = "foobar"; } + ``` + + But in general you can get more reuse out of `let` bindings by refactoring them to a function. + + ```nix + nix-repl> f = self: { + foo = "foo"; + bar = "bar"; + foobar = self.foo + self.bar; + } + ``` + + This is where `fix` comes in, it contains the syntactic recursion that's not in `f` anymore. + + ```nix + nix-repl> fix = f: + let self = f self; in self; + ``` + + By applying `fix` we get the final result. + + ```nix + nix-repl> fix f + { bar = "bar"; foo = "foo"; foobar = "foobar"; } + ``` + + Such a refactored `f` using `fix` is not useful by itself. + See [`extends`](#function-library-lib.fixedPoints.extends) for an example use case. + There `self` is also often called `final`. + + Type: fix :: (a -> a) -> a + + Example: + fix (self: { foo = "foo"; bar = "bar"; foobar = self.foo + self.bar; }) + => { bar = "bar"; foo = "foo"; foobar = "foobar"; } + + fix (self: [ 1 2 (elemAt self 0 + elemAt self 1) ]) + => [ 1 2 3 ] */ - fix = f: let x = f x; in x; + fix = + f: + let + x = f x; + in + x; - /* A variant of `fix` that records the original recursive attribute set in the - result, in an attribute named `__unfix__`. + /* + A variant of `fix` that records the original recursive attribute set in the + result, in an attribute named `__unfix__`. - This is useful in combination with the `extends` function to - implement deep overriding. + This is useful in combination with the `extends` function to + implement deep overriding. */ - fix' = f: let x = f x // { __unfix__ = f; }; in x; - - /* Return the fixpoint that `f` converges to when called iteratively, starting - with the input `x`. - - ``` - nix-repl> converge (x: x / 2) 16 - 0 - ``` - - Type: (a -> a) -> a -> a + fix' = + f: + let + x = f x // { + __unfix__ = f; + }; + in + x; + + /* + Return the fixpoint that `f` converges to when called iteratively, starting + with the input `x`. + + ``` + nix-repl> converge (x: x / 2) 16 + 0 + ``` + + Type: (a -> a) -> a -> a */ - converge = f: x: let x' = f x; in if x' == x then x else converge f x'; + converge = + f: x: + let + x' = f x; + in + if x' == x then x else converge f x'; - /* Extend a function using an overlay. + /* + Extend a function using an overlay. - Overlays allow modifying and extending fixed-point functions, specifically ones returning attribute sets. - A fixed-point function is a function which is intended to be evaluated by passing the result of itself as the argument. - This is possible due to Nix's lazy evaluation. + Overlays allow modifying and extending fixed-point functions, specifically ones returning attribute sets. + A fixed-point function is a function which is intended to be evaluated by passing the result of itself as the argument. + This is possible due to Nix's lazy evaluation. - A fixed-point function returning an attribute set has the form + A fixed-point function returning an attribute set has the form - ```nix - final: { # attributes } - ``` + ```nix + final: { # attributes } + ``` - where `final` refers to the lazily evaluated attribute set returned by the fixed-point function. + where `final` refers to the lazily evaluated attribute set returned by the fixed-point function. - An overlay to such a fixed-point function has the form + An overlay to such a fixed-point function has the form - ```nix - final: prev: { # attributes } - ``` + ```nix + final: prev: { # attributes } + ``` - where `prev` refers to the result of the original function to `final`, and `final` is the result of the composition of the overlay and the original function. + where `prev` refers to the result of the original function to `final`, and `final` is the result of the composition of the overlay and the original function. - Applying an overlay is done with `extends`: + Applying an overlay is done with `extends`: - ```nix - let - f = final: { # attributes }; - overlay = final: prev: { # attributes }; - in extends overlay f; - ``` + ```nix + let + f = final: { # attributes }; + overlay = final: prev: { # attributes }; + in extends overlay f; + ``` - To get the value of `final`, use `lib.fix`: + To get the value of `final`, use `lib.fix`: - ```nix - let - f = final: { # attributes }; - overlay = final: prev: { # attributes }; - g = extends overlay f; - in fix g - ``` + ```nix + let + f = final: { # attributes }; + overlay = final: prev: { # attributes }; + g = extends overlay f; + in fix g + ``` - :::{.note} - The argument to the given fixed-point function after applying an overlay will *not* refer to its own return value, but rather to the value after evaluating the overlay function. + :::{.note} + The argument to the given fixed-point function after applying an overlay will *not* refer to its own return value, but rather to the value after evaluating the overlay function. - The given fixed-point function is called with a separate argument than if it was evaluated with `lib.fix`. - ::: + The given fixed-point function is called with a separate argument than if it was evaluated with `lib.fix`. + ::: - :::{.example} + :::{.example} - # Extend a fixed-point function with an overlay + # Extend a fixed-point function with an overlay - Define a fixed-point function `f` that expects its own output as the argument `final`: + Define a fixed-point function `f` that expects its own output as the argument `final`: - ```nix-repl - f = final: { - # Constant value a - a = 1; + ```nix-repl + f = final: { + # Constant value a + a = 1; - # b depends on the final value of a, available as final.a - b = final.a + 2; - } - ``` + # b depends on the final value of a, available as final.a + b = final.a + 2; + } + ``` - Evaluate this using [`lib.fix`](#function-library-lib.fixedPoints.fix) to get the final result: + Evaluate this using [`lib.fix`](#function-library-lib.fixedPoints.fix) to get the final result: - ```nix-repl - fix f - => { a = 1; b = 3; } - ``` + ```nix-repl + fix f + => { a = 1; b = 3; } + ``` - An overlay represents a modification or extension of such a fixed-point function. - Here's an example of an overlay: + An overlay represents a modification or extension of such a fixed-point function. + Here's an example of an overlay: - ```nix-repl - overlay = final: prev: { - # Modify the previous value of a, available as prev.a - a = prev.a + 10; + ```nix-repl + overlay = final: prev: { + # Modify the previous value of a, available as prev.a + a = prev.a + 10; - # Extend the attribute set with c, letting it depend on the final values of a and b - c = final.a + final.b; - } - ``` + # Extend the attribute set with c, letting it depend on the final values of a and b + c = final.a + final.b; + } + ``` - Use `extends overlay f` to apply the overlay to the fixed-point function `f`. - This produces a new fixed-point function `g` with the combined behavior of `f` and `overlay`: + Use `extends overlay f` to apply the overlay to the fixed-point function `f`. + This produces a new fixed-point function `g` with the combined behavior of `f` and `overlay`: - ```nix-repl - g = extends overlay f - ``` + ```nix-repl + g = extends overlay f + ``` - The result is a function, so we can't print it directly, but it's the same as: + The result is a function, so we can't print it directly, but it's the same as: - ```nix-repl - g' = final: { - # The constant from f, but changed with the overlay - a = 1 + 10; + ```nix-repl + g' = final: { + # The constant from f, but changed with the overlay + a = 1 + 10; - # Unchanged from f - b = final.a + 2; + # Unchanged from f + b = final.a + 2; - # Extended in the overlay - c = final.a + final.b; - } - ``` + # Extended in the overlay + c = final.a + final.b; + } + ``` - Evaluate this using [`lib.fix`](#function-library-lib.fixedPoints.fix) again to get the final result: + Evaluate this using [`lib.fix`](#function-library-lib.fixedPoints.fix) again to get the final result: - ```nix-repl - fix g - => { a = 11; b = 13; c = 24; } - ``` - ::: + ```nix-repl + fix g + => { a = 11; b = 13; c = 24; } + ``` + ::: - Type: - extends :: (Attrs -> Attrs -> Attrs) # The overlay to apply to the fixed-point function - -> (Attrs -> Attrs) # A fixed-point function - -> (Attrs -> Attrs) # The resulting fixed-point function + Type: + extends :: (Attrs -> Attrs -> Attrs) # The overlay to apply to the fixed-point function + -> (Attrs -> Attrs) # A fixed-point function + -> (Attrs -> Attrs) # The resulting fixed-point function - Example: - f = final: { a = 1; b = final.a + 2; } + Example: + f = final: { a = 1; b = final.a + 2; } - fix f - => { a = 1; b = 3; } + fix f + => { a = 1; b = 3; } - fix (extends (final: prev: { a = prev.a + 10; }) f) - => { a = 11; b = 13; } + fix (extends (final: prev: { a = prev.a + 10; }) f) + => { a = 11; b = 13; } - fix (extends (final: prev: { b = final.a + 5; }) f) - => { a = 1; b = 6; } + fix (extends (final: prev: { b = final.a + 5; }) f) + => { a = 1; b = 6; } - fix (extends (final: prev: { c = final.a + final.b; }) f) - => { a = 1; b = 3; c = 4; } + fix (extends (final: prev: { c = final.a + final.b; }) f) + => { a = 1; b = 3; c = 4; } */ extends = # The overlay to apply to the fixed-point function @@ -232,58 +254,72 @@ f: # Wrap with parenthesis to prevent nixdoc from rendering the `final` argument in the documentation # The result should be thought of as a function, the argument of that function is not an argument to `extends` itself - (final: let prev = f final; in prev // overlay final prev); - - /* Compose two extending functions of the type expected by 'extends' - into one where changes made in the first are available in the - 'super' of the second + ( + final: + let + prev = f final; + in + prev // overlay final prev + ); + + /* + Compose two extending functions of the type expected by 'extends' + into one where changes made in the first are available in the + 'super' of the second */ - composeExtensions = f: g: final: prev: + composeExtensions = + f: g: final: prev: let fApplied = f final prev; prev' = prev // fApplied; - in fApplied // g final prev'; - - /* Compose several extending functions of the type expected by 'extends' into - one where changes made in preceding functions are made available to - subsequent ones. - - ``` - composeManyExtensions : [packageSet -> packageSet -> packageSet] -> packageSet -> packageSet -> packageSet - ^final ^prev ^overrides ^final ^prev ^overrides - ``` + in + fApplied // g final prev'; + + /* + Compose several extending functions of the type expected by 'extends' into + one where changes made in preceding functions are made available to + subsequent ones. + + ``` + composeManyExtensions : [packageSet -> packageSet -> packageSet] -> packageSet -> packageSet -> packageSet + ^final ^prev ^overrides ^final ^prev ^overrides + ``` */ - composeManyExtensions = - lib.foldr (x: y: composeExtensions x y) (final: prev: { }); + composeManyExtensions = lib.foldr (x: y: composeExtensions x y) (final: prev: { }); - /* Create an overridable, recursive attribute set. For example: + /* + Create an overridable, recursive attribute set. For example: - ``` - nix-repl> obj = makeExtensible (self: { }) + ``` + nix-repl> obj = makeExtensible (self: { }) - nix-repl> obj - { __unfix__ = «lambda»; extend = «lambda»; } + nix-repl> obj + { __unfix__ = «lambda»; extend = «lambda»; } - nix-repl> obj = obj.extend (self: super: { foo = "foo"; }) + nix-repl> obj = obj.extend (self: super: { foo = "foo"; }) - nix-repl> obj - { __unfix__ = «lambda»; extend = «lambda»; foo = "foo"; } + nix-repl> obj + { __unfix__ = «lambda»; extend = «lambda»; foo = "foo"; } - nix-repl> obj = obj.extend (self: super: { foo = super.foo + " + "; bar = "bar"; foobar = self.foo + self.bar; }) + nix-repl> obj = obj.extend (self: super: { foo = super.foo + " + "; bar = "bar"; foobar = self.foo + self.bar; }) - nix-repl> obj - { __unfix__ = «lambda»; bar = "bar"; extend = «lambda»; foo = "foo + "; foobar = "foo + bar"; } - ``` + nix-repl> obj + { __unfix__ = «lambda»; bar = "bar"; extend = «lambda»; foo = "foo + "; foobar = "foo + bar"; } + ``` */ makeExtensible = makeExtensibleWithCustomName "extend"; - /* Same as `makeExtensible` but the name of the extending attribute is - customized. + /* + Same as `makeExtensible` but the name of the extending attribute is + customized. */ - makeExtensibleWithCustomName = extenderName: rattrs: - fix' (self: - (rattrs self) // { - ${extenderName} = f: - makeExtensibleWithCustomName extenderName (extends f rattrs); - }); + makeExtensibleWithCustomName = + extenderName: rattrs: + fix' ( + self: + (rattrs self) + // { + ${extenderName} = f: makeExtensibleWithCustomName extenderName (extends f rattrs); + } + ); } diff --git a/nix/stdlib/flake-version-info.nix b/nix/stdlib/flake-version-info.nix index 80b293c..3fa965e 100644 --- a/nix/stdlib/flake-version-info.nix +++ b/nix/stdlib/flake-version-info.nix @@ -12,10 +12,9 @@ finalLib: prevLib: # lib overlay { trivial = prevLib.trivial // { - versionSuffix = - ".${finalLib.substring 0 8 (self.lastModifiedDate or "19700101")}.${ - self.shortRev or "dirty" - }"; + versionSuffix = ".${ + finalLib.substring 0 8 (self.lastModifiedDate or "19700101") + }.${self.shortRev or "dirty"}"; revisionWithDefault = default: self.rev or default; }; } diff --git a/nix/stdlib/flake.nix b/nix/stdlib/flake.nix index 3a62ed9..a21b988 100644 --- a/nix/stdlib/flake.nix +++ b/nix/stdlib/flake.nix @@ -1,7 +1,12 @@ { description = "Library of low-level helper functions for nix expressions."; - outputs = { self }: - let lib0 = import ./.; - in { lib = lib0.extend (import ./flake-version-info.nix self); }; + outputs = + { self }: + let + lib0 = import ./.; + in + { + lib = lib0.extend (import ./flake-version-info.nix self); + }; } diff --git a/nix/stdlib/generators.nix b/nix/stdlib/generators.nix index 644863e..0fbc437 100644 --- a/nix/stdlib/generators.nix +++ b/nix/stdlib/generators.nix @@ -15,56 +15,95 @@ { lib }: let inherit (lib) - addErrorContext assertMsg attrNames concatLists concatMapStringsSep - concatStrings concatStringsSep const elem escape filter flatten foldl + addErrorContext + assertMsg + attrNames + concatLists + concatMapStringsSep + concatStrings + concatStringsSep + const + elem + escape + filter + flatten + foldl functionArgs # Note: not the builtin; considers `__functor` in attrsets. - gvariant hasInfix head id init isAttrs isBool isDerivation isFloat + gvariant + hasInfix + head + id + init + isAttrs + isBool + isDerivation + isFloat isFunction # Note: not the builtin; considers `__functor` in attrsets. - isInt isList isPath isString last length mapAttrs mapAttrsToList optionals - recursiveUpdate replaceStrings reverseList splitString tail toList; + isInt + isList + isPath + isString + last + length + mapAttrs + mapAttrsToList + optionals + recursiveUpdate + replaceStrings + reverseList + splitString + tail + toList + ; inherit (lib.strings) - escapeNixIdentifier floatToString match split toJSON typeOf; + escapeNixIdentifier + floatToString + match + split + toJSON + typeOf + ; ## -- HELPER FUNCTIONS & DEFAULTS -- # Convert a value to a sensible default string representation. # The builtin `toString` function has some strange defaults, # suitable for bash scripts but not much else. - mkValueStringDefault = { }: + mkValueStringDefault = + { }: v: let - err = t: v: - abort ("generators.mkValueStringDefault: " - + "${t} not supported: ${toPretty { } v}"); - in if isInt v then + 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 - # we default to not quoting strings + # we default to not quoting strings 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" - # 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 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 - # same as for lists, might want to replace + # same as for lists, might want to replace 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 - # Floats currently can't be converted to precise strings, - # condition warning on nix version once this isn't a problem anymore - # See https://github.com/NixOS/nix/pull/3480 + # Floats currently can't be converted to precise strings, + # condition warning on nix version once this isn't a problem anymore + # See https://github.com/NixOS/nix/pull/3480 else if isFloat v then floatToString v else @@ -78,7 +117,10 @@ let # # mkKeyValueDefault {} ":" "f:oo" "bar" # > "f\:oo:bar" - mkKeyValueDefault = { mkValueString ? mkValueStringDefault { } }: + mkKeyValueDefault = + { + mkValueString ? mkValueStringDefault { }, + }: sep: k: v: "${escape [ sep ] k}${sep}${mkValueString v}"; @@ -87,15 +129,21 @@ let # Generate a key-value-style config file from an attrset. # # mkKeyValue is the same as in toINI. - toKeyValue = { mkKeyValue ? mkKeyValueDefault { } "=" - , listsAsDuplicateKeys ? false, indent ? "", }: + toKeyValue = + { + mkKeyValue ? mkKeyValueDefault { } "=", + listsAsDuplicateKeys ? false, + indent ? "", + }: let mkLine = k: v: indent + mkKeyValue k v + "\n"; - mkLines = if listsAsDuplicateKeys then - k: v: map (mkLine k) (if isList v then v else [ v ]) - else - k: v: [ (mkLine k v) ]; - in attrs: concatStrings (concatLists (mapAttrsToList mkLines attrs)); + mkLines = + if listsAsDuplicateKeys then + k: v: map (mkLine k) (if isList v then v else [ v ]) + else + k: v: [ (mkLine k v) ]; + in + attrs: concatStrings (concatLists (mapAttrsToList mkLines attrs)); # Generate an INI-style config file from an # attrset of sections to an attrset of key-value pairs. @@ -116,24 +164,36 @@ let # the way sections and key-value strings are generated. # # For more examples see the test cases in ./tests/misc.nix. - toINI = { - # apply transformations (e.g. escapes) to section names - mkSectionName ? (name: escape [ "[" "]" ] name), - # format a setting line from key and value - mkKeyValue ? mkKeyValueDefault { } "=", - # allow lists as values for duplicate keys - listsAsDuplicateKeys ? false, }: + toINI = + { + # apply transformations (e.g. escapes) to section names + mkSectionName ? ( + name: + escape [ + "[" + "]" + ] name + ), + # format a setting line from key and value + mkKeyValue ? mkKeyValueDefault { } "=", + # allow lists as values for duplicate keys + listsAsDuplicateKeys ? false, + }: attrsOfAttrs: let # map function to string for each key val - mapAttrsToStringsSep = sep: mapFn: attrs: + mapAttrsToStringsSep = + sep: mapFn: attrs: concatStringsSep sep (mapAttrsToList mapFn attrs); - mkSection = sectName: sectValues: + mkSection = + sectName: sectValues: '' [${mkSectionName sectName}] - '' + toKeyValue { inherit mkKeyValue listsAsDuplicateKeys; } sectValues; - # map input to ini sections - in mapAttrsToStringsSep "\n" mkSection attrsOfAttrs; + '' + + toKeyValue { inherit mkKeyValue listsAsDuplicateKeys; } sectValues; + in + # map input to ini sections + mapAttrsToStringsSep "\n" mkSection attrsOfAttrs; # Generate an INI-style config file from an attrset # specifying the global section (no header), and an @@ -165,21 +225,32 @@ let # If you don’t need a global section, you can also use # `generators.toINI` directly, which only takes # the part in `sections`. - toINIWithGlobalSection = { - # apply transformations (e.g. escapes) to section names - mkSectionName ? (name: escape [ "[" "]" ] name), - # format a setting line from key and value - mkKeyValue ? mkKeyValueDefault { } "=", - # allow lists as values for duplicate keys - listsAsDuplicateKeys ? false, }: - { globalSection, sections ? { }, }: - (if globalSection == { } then - "" - else - (toKeyValue { inherit mkKeyValue listsAsDuplicateKeys; } globalSection) - + "\n") - + (toINI { inherit mkSectionName mkKeyValue listsAsDuplicateKeys; } - sections); + toINIWithGlobalSection = + { + # apply transformations (e.g. escapes) to section names + mkSectionName ? ( + name: + escape [ + "[" + "]" + ] name + ), + # format a setting line from key and value + mkKeyValue ? mkKeyValueDefault { } "=", + # allow lists as values for duplicate keys + listsAsDuplicateKeys ? false, + }: + { + globalSection, + sections ? { }, + }: + ( + if globalSection == { } then + "" + else + (toKeyValue { inherit mkKeyValue listsAsDuplicateKeys; } globalSection) + "\n" + ) + + (toINI { inherit mkSectionName mkKeyValue listsAsDuplicateKeys; } sections); # Generate a git-config file from an attrset. # @@ -198,146 +269,192 @@ let #> #> [user] #> name = "edolstra" - toGitINI = attrs: + toGitINI = + attrs: let - mkSectionName = name: + mkSectionName = + name: let containsQuote = hasInfix ''"'' name; sections = splitString "." name; section = head sections; subsections = tail sections; subsection = concatStringsSep "." subsections; - in if containsQuote || subsections == [ ] then - name - else - ''${section} "${subsection}"''; + in + if containsQuote || subsections == [ ] then name else ''${section} "${subsection}"''; - mkValueString = v: + mkValueString = + v: let - escapedV = '' - "${ - replaceStrings [ "\n" " " ''"'' "\\" ] [ + escapedV = ''"${ + replaceStrings + [ + "\n" + " " + ''"'' + "\\" + ] + [ "\\n" "\\t" ''\"'' "\\\\" - ] v - }"''; - in mkValueStringDefault { } (if isString v then escapedV else v); + ] + v + }"''; + in + mkValueStringDefault { } (if isString v then escapedV else v); # generation for multiple ini values - mkKeyValue = k: v: - let mkKeyValue = mkKeyValueDefault { inherit mkValueString; } " = " k; - in concatStringsSep "\n" (map (kv: " " + mkKeyValue kv) (toList v)); + mkKeyValue = + k: v: + let + mkKeyValue = mkKeyValueDefault { inherit mkValueString; } " = " k; + in + concatStringsSep "\n" (map (kv: " " + mkKeyValue kv) (toList v)); # converts { a.b.c = 5; } to { "a.b".c = 5; } for toINI - gitFlattenAttrs = let - recurse = path: value: - if isAttrs value && !isDerivation value then - mapAttrsToList (name: value: recurse ([ name ] ++ path) value) value - else if length path > 1 then { - ${concatStringsSep "." (reverseList (tail path))}.${head path} = - value; - } else { - ${head path} = value; - }; - in attrs: foldl recursiveUpdate { } (flatten (recurse [ ] attrs)); + gitFlattenAttrs = + let + recurse = + path: value: + if isAttrs value && !isDerivation value then + mapAttrsToList (name: value: recurse ([ name ] ++ path) value) value + else if length path > 1 then + { ${concatStringsSep "." (reverseList (tail path))}.${head path} = value; } + else + { ${head path} = value; }; + in + attrs: foldl recursiveUpdate { } (flatten (recurse [ ] attrs)); toINI_ = toINI { inherit mkKeyValue mkSectionName; }; - in toINI_ (gitFlattenAttrs attrs); + in + toINI_ (gitFlattenAttrs attrs); # mkKeyValueDefault wrapper that handles dconf INI quirks. # The main differences of the format is that it requires strings to be quoted. - mkDconfKeyValue = - mkKeyValueDefault { mkValueString = v: toString (gvariant.mkValue v); } "="; + mkDconfKeyValue = mkKeyValueDefault { mkValueString = v: toString (gvariant.mkValue v); } "="; # Generates INI in dconf keyfile style. See https://help.gnome.org/admin/system-admin-guide/stable/dconf-keyfiles.html.en # for details. toDconfINI = toINI { mkKeyValue = mkDconfKeyValue; }; - withRecursion = { - # If this option is not null, the given value will stop evaluating at a certain depth - depthLimit, - # If this option is true, an error will be thrown, if a certain given depth is exceeded - throwOnDepthLimit ? true, }: + withRecursion = + { + # If this option is not null, the given value will stop evaluating at a certain depth + depthLimit, + # If this option is true, an error will be thrown, if a certain given depth is exceeded + throwOnDepthLimit ? true, + }: assert isInt depthLimit; let - specialAttrs = [ "__functor" "__functionArgs" "__toString" "__pretty" ]; - stepIntoAttr = evalNext: name: - if elem name specialAttrs then id else evalNext; - transform = depth: + specialAttrs = [ + "__functor" + "__functionArgs" + "__toString" + "__pretty" + ]; + stepIntoAttr = evalNext: name: if elem name specialAttrs then id else evalNext; + transform = + depth: if depthLimit != null && depth > depthLimit then if throwOnDepthLimit then - throw "Exceeded maximum eval-depth limit of ${ - toString depthLimit - } while trying to evaluate with `generators.withRecursion'!" + throw "Exceeded maximum eval-depth limit of ${toString depthLimit} while trying to evaluate with `generators.withRecursion'!" else const "" else id; - mapAny = depth: v: - let evalNext = x: mapAny (depth + 1) (transform (depth + 1) x); - in if isAttrs v then + mapAny = + depth: v: + let + evalNext = x: mapAny (depth + 1) (transform (depth + 1) x); + in + if isAttrs v then mapAttrs (stepIntoAttr evalNext) v else if isList v then map evalNext v else transform (depth + 1) v; - in mapAny 0; + in + mapAny 0; # Pretty print a value, akin to `builtins.trace`. # Should probably be a builtin as well. # The pretty-printed string should be suitable for rendering default values # in the NixOS manual. In particular, it should be as close to a valid Nix expression # as possible. - toPretty = { - /* If this option is true, attrsets like { __pretty = fn; val = …; } - will use fn to convert val to a pretty printed representation. - (This means fn is type Val -> String.) - */ - allowPrettyValues ? false, - # If this option is true, the output is indented with newlines for attribute sets and lists - multiline ? true, - # Initial indentation level - indent ? "", }: + toPretty = + { + /* + If this option is true, attrsets like { __pretty = fn; val = …; } + will use fn to convert val to a pretty printed representation. + (This means fn is type Val -> String.) + */ + allowPrettyValues ? false, + # If this option is true, the output is indented with newlines for attribute sets and lists + multiline ? true, + # Initial indentation level + indent ? "", + }: let - go = indent: v: + go = + indent: v: let - introSpace = if multiline then '' - - ${indent} '' else - " "; - outroSpace = if multiline then '' - - ${indent}'' else - " "; - in if isInt v then + introSpace = + if multiline then + '' + + ${indent} '' + else + " "; + outroSpace = + if multiline then + '' + + ${indent}'' + else + " "; + in + if isInt v then toString v - # toString loses precision on floats, so we use toJSON instead. This isn't perfect - # as the resulting string may not parse back as a float (e.g. 42, 1e-06), but for - # pretty-printing purposes this is acceptable. + # toString loses precision on floats, so we use toJSON instead. This isn't perfect + # as the resulting string may not parse back as a float (e.g. 42, 1e-06), but for + # pretty-printing purposes this is acceptable. else if isFloat v then builtins.toJSON v else if isString v then let lines = filter (v: !isList v) (split "\n" v); - escapeSingleline = escape [ "\\" ''"'' "\${" ]; - escapeMultiline = replaceStrings [ "\${" "''" ] [ "''\${" "'''" ]; - singlelineResult = ''"'' - + concatStringsSep "\\n" (map escapeSingleline lines) + ''"''; - multilineResult = let - escapedLines = map escapeMultiline lines; - # The last line gets a special treatment: if it's empty, '' is on its own line at the "outer" - # indentation level. Otherwise, '' is appended to the last line. - lastLine = last escapedLines; - in "''" + introSpace - + concatStringsSep introSpace (init escapedLines) - + (if lastLine == "" then outroSpace else introSpace + lastLine) - + "''"; - in if multiline && length lines > 1 then - multilineResult - else - singlelineResult + escapeSingleline = escape [ + "\\" + ''"'' + "\${" + ]; + escapeMultiline = + replaceStrings + [ + "\${" + "''" + ] + [ + "''\${" + "'''" + ]; + singlelineResult = ''"'' + concatStringsSep "\\n" (map escapeSingleline lines) + ''"''; + multilineResult = + let + escapedLines = map escapeMultiline lines; + # The last line gets a special treatment: if it's empty, '' is on its own line at the "outer" + # indentation level. Otherwise, '' is appended to the last line. + lastLine = last escapedLines; + in + "''" + + introSpace + + concatStringsSep introSpace (init escapedLines) + + (if lastLine == "" then outroSpace else introSpace + lastLine) + + "''"; + in + if multiline && length lines > 1 then multilineResult else singlelineResult else if true == v then "true" else if false == v then @@ -350,20 +467,17 @@ let if v == [ ] then "[ ]" else - "[" + introSpace - + concatMapStringsSep introSpace (go (indent + " ")) v + outroSpace - + "]" + "[" + introSpace + concatMapStringsSep introSpace (go (indent + " ")) v + outroSpace + "]" else if isFunction v then let fna = functionArgs v; - showFnas = concatStringsSep ", " (mapAttrsToList - (name: hasDefVal: if hasDefVal then name + "?" else name) fna); - in if fna == { } then - "" - else - "" + showFnas = concatStringsSep ", " ( + mapAttrsToList (name: hasDefVal: if hasDefVal then name + "?" else name) fna + ); + in + if fna == { } then "" else "" else if isAttrs v then - # apply pretty values if allowed + # apply pretty values if allowed if allowPrettyValues && v ? __pretty && v ? val then v.__pretty v.val else if v == { } then @@ -371,21 +485,30 @@ let else if v ? type && v.type == "derivation" then "" else - "{" + introSpace + concatStringsSep introSpace (mapAttrsToList - (name: value: + "{" + + introSpace + + concatStringsSep introSpace ( + mapAttrsToList ( + name: value: "${escapeNixIdentifier name} = ${ - addErrorContext "while evaluating an attribute `${name}`" - (go (indent + " ") value) - };") v) + outroSpace + "}" + addErrorContext "while evaluating an attribute `${name}`" (go (indent + " ") value) + };" + ) v + ) + + outroSpace + + "}" else abort "generators.toPretty: should never happen (v = ${v})"; - in go indent; + in + go indent; # PLIST handling - toPlist = { }: + toPlist = + { }: v: let - expr = ind: x: + expr = + ind: x: if x == null then "" else if isBool x then @@ -417,28 +540,40 @@ let item = ind: concatMapStringsSep "\n" (indent ind); - list = ind: x: + list = + ind: x: concatStringsSep "\n" [ (literal ind "") (item ind x) (literal ind "") ]; - attrs = ind: x: + attrs = + ind: x: concatStringsSep "\n" [ (literal ind "") (attr ind x) (literal ind "") ]; - attr = let attrFilter = name: value: name != "_module" && value != null; - in ind: x: - concatStringsSep "\n" (flatten (mapAttrsToList (name: value: - optionals (attrFilter name value) [ - (key " ${ind}" name) - (expr " ${ind}" value) - ]) x)); - in '' + attr = + let + attrFilter = name: value: name != "_module" && value != null; + in + ind: x: + concatStringsSep "\n" ( + flatten ( + mapAttrsToList ( + name: value: + optionals (attrFilter name value) [ + (key " ${ind}" name) + (expr " ${ind}" value) + ] + ) x + ) + ); + in + '' @@ -448,14 +583,14 @@ let # Translate a simple Nix expression to Dhall notation. # Note that integers are translated to Integer and never # the Natural type. - toDhall = { }@args: + toDhall = + { }@args: v: - let concatItems = concatStringsSep ", "; - in if isAttrs v then - "{ ${ - concatItems - (mapAttrsToList (key: value: "${key} = ${toDhall args value}") v) - } }" + let + concatItems = concatStringsSep ", "; + in + if isAttrs v then + "{ ${concatItems (mapAttrsToList (key: value: "${key} = ${toDhall args value}") v)} }" else if isList v then "[ ${concatItems (map (toDhall args) v)} ]" else if isInt v then @@ -469,116 +604,148 @@ let else toJSON v; - /* Translate a simple Nix expression to Lua representation with occasional - Lua-inlines that can be constructed by mkLuaInline function. + /* + Translate a simple Nix expression to Lua representation with occasional + Lua-inlines that can be constructed by mkLuaInline function. - Configuration: - * multiline - by default is true which results in indented block-like view. - * indent - initial indent. - * asBindings - by default generate single value, but with this use attrset to set global vars. + Configuration: + * multiline - by default is true which results in indented block-like view. + * indent - initial indent. + * asBindings - by default generate single value, but with this use attrset to set global vars. - Attention: - Regardless of multiline parameter there is no trailing newline. + Attention: + Regardless of multiline parameter there is no trailing newline. - Example: - generators.toLua {} - { - cmd = [ "typescript-language-server" "--stdio" ]; - settings.workspace.library = mkLuaInline ''vim.api.nvim_get_runtime_file("", true)''; - } - -> + Example: + generators.toLua {} { - ["cmd"] = { - "typescript-language-server", - "--stdio" - }, - ["settings"] = { - ["workspace"] = { - ["library"] = (vim.api.nvim_get_runtime_file("", true)) - } - } + cmd = [ "typescript-language-server" "--stdio" ]; + settings.workspace.library = mkLuaInline ''vim.api.nvim_get_runtime_file("", true)''; } + -> + { + ["cmd"] = { + "typescript-language-server", + "--stdio" + }, + ["settings"] = { + ["workspace"] = { + ["library"] = (vim.api.nvim_get_runtime_file("", true)) + } + } + } - Type: - toLua :: AttrSet -> Any -> String + Type: + toLua :: AttrSet -> Any -> String */ - toLua = { - # If this option is true, the output is indented with newlines for attribute sets and lists - multiline ? true, - # Initial indentation level - indent ? "", - # Interpret as variable bindings - asBindings ? false, }@args: + toLua = + { + # If this option is true, the output is indented with newlines for attribute sets and lists + multiline ? true, + # Initial indentation level + indent ? "", + # Interpret as variable bindings + asBindings ? false, + }@args: v: let innerIndent = "${indent} "; - introSpace = if multiline then '' + introSpace = + if multiline then + '' - ${innerIndent}'' else - " "; - outroSpace = if multiline then '' + ${innerIndent}'' + else + " "; + outroSpace = + if multiline then + '' - ${indent}'' else - " "; + ${indent}'' + else + " "; innerArgs = args // { indent = if asBindings then indent else innerIndent; asBindings = false; }; concatItems = concatStringsSep ",${introSpace}"; - isLuaInline = { _type ? null, ... }: _type == "lua-inline"; - - generatedBindings = assert assertMsg (badVarNames == [ ]) - "Bad Lua var names: ${toPretty { } badVarNames}"; - concatStrings (mapAttrsToList (key: value: '' - ${indent}${key} = ${toLua innerArgs value} - '') v); + isLuaInline = + { + _type ? null, + ... + }: + _type == "lua-inline"; + + generatedBindings = + assert assertMsg (badVarNames == [ ]) "Bad Lua var names: ${toPretty { } badVarNames}"; + concatStrings ( + mapAttrsToList (key: value: '' + ${indent}${key} = ${toLua innerArgs value} + '') v + ); # https://en.wikibooks.org/wiki/Lua_Programming/variable#Variable_names - matchVarName = - match "[[:alpha:]_][[:alnum:]_]*(\\.[[:alpha:]_][[:alnum:]_]*)*"; + matchVarName = match "[[:alpha:]_][[:alnum:]_]*(\\.[[:alpha:]_][[:alnum:]_]*)*"; badVarNames = filter (name: matchVarName name == null) (attrNames v); - in if asBindings then + in + if asBindings then generatedBindings else if v == null then "nil" else if isInt v || isFloat v || isString v || isBool v then toJSON v else if isList v then - (if v == [ ] then - "{}" - else - "{${introSpace}${ - concatItems (map (value: "${toLua innerArgs value}") v) - }${outroSpace}}") + ( + if v == [ ] then + "{}" + else + "{${introSpace}${concatItems (map (value: "${toLua innerArgs value}") v)}${outroSpace}}" + ) else if isAttrs v then - (if isLuaInline v then - "(${v.expr})" - else if v == { } then - "{}" - else if isDerivation v then - ''"${toString v}"'' - else - "{${introSpace}${ - concatItems (mapAttrsToList - (key: value: "[${toJSON key}] = ${toLua innerArgs value}") v) - }${outroSpace}}") + ( + if isLuaInline v then + "(${v.expr})" + else if v == { } then + "{}" + else if isDerivation v then + ''"${toString v}"'' + else + "{${introSpace}${ + concatItems (mapAttrsToList (key: value: "[${toJSON key}] = ${toLua innerArgs value}") v) + }${outroSpace}}" + ) else abort "generators.toLua: type ${typeOf v} is unsupported"; - /* Mark string as Lua expression to be inlined when processed by toLua. + /* + Mark string as Lua expression to be inlined when processed by toLua. - Type: - mkLuaInline :: String -> AttrSet + Type: + mkLuaInline :: String -> AttrSet */ mkLuaInline = expr: { _type = "lua-inline"; inherit expr; }; - # Everything in this attrset is the public interface of the file. -in { - inherit mkDconfKeyValue mkKeyValueDefault mkLuaInline mkValueStringDefault - toDconfINI toDhall toGitINI toINI toINIWithGlobalSection toKeyValue toLua - toPlist toPretty withRecursion; +in +# Everything in this attrset is the public interface of the file. +{ + inherit + mkDconfKeyValue + mkKeyValueDefault + mkLuaInline + mkValueStringDefault + toDconfINI + toDhall + toGitINI + toINI + toINIWithGlobalSection + toKeyValue + toLua + toPlist + toPretty + withRecursion + ; # Generates JSON from an arbitrary (non-function) value. # For more information see the documentation of the builtin. diff --git a/nix/stdlib/gvariant.nix b/nix/stdlib/gvariant.nix index c1dab3e..b48c042 100644 --- a/nix/stdlib/gvariant.nix +++ b/nix/stdlib/gvariant.nix @@ -1,25 +1,29 @@ -/* A partial and basic implementation of GVariant formatted strings. - See [GVariant Format Strings](https://docs.gtk.org/glib/gvariant-format-strings.html) for details. - - :::{.warning} - This API is not considered fully stable and it might therefore - change in backwards incompatible ways without prior notice. - ::: +/* + A partial and basic implementation of GVariant formatted strings. + See [GVariant Format Strings](https://docs.gtk.org/glib/gvariant-format-strings.html) for details. + + :::{.warning} + This API is not considered fully stable and it might therefore + change in backwards incompatible ways without prior notice. + ::: */ # This file is based on https://github.com/nix-community/home-manager # Copyright (c) 2017-2022 Home Manager contributors { lib }: let - inherit (lib) concatMapStringsSep concatStrings escape head replaceStrings; + inherit (lib) + concatMapStringsSep + concatStrings + escape + head + replaceStrings + ; mkPrimitive = t: v: { _type = "gvariant"; type = t; value = v; - __toString = self: - "@${self.type} ${ - toString self.value - }"; # https://docs.gtk.org/glib/gvariant-text.html + __toString = self: "@${self.type} ${toString self.value}"; # https://docs.gtk.org/glib/gvariant-text.html }; type = { @@ -40,22 +44,26 @@ let variant = "v"; }; - /* Check if a value is a GVariant value + /* + Check if a value is a GVariant value - Type: - isGVariant :: Any -> Bool + Type: + isGVariant :: Any -> Bool */ isGVariant = v: v._type or "" == "gvariant"; -in rec { +in +rec { inherit type isGVariant; - /* Returns the GVariant value that most closely matches the given Nix value. - If no GVariant value can be found unambiguously then error is thrown. + /* + Returns the GVariant value that most closely matches the given Nix value. + If no GVariant value can be found unambiguously then error is thrown. - Type: - mkValue :: Any -> gvariant + Type: + mkValue :: Any -> gvariant */ - mkValue = v: + mkValue = + v: if builtins.isBool v then mkBoolean v else if builtins.isFloat v then @@ -69,71 +77,75 @@ in rec { else throw "The GVariant type of ${v} can't be inferred."; - /* Returns the GVariant array from the given type of the elements and a Nix list. + /* + Returns the GVariant array from the given type of the elements and a Nix list. - Type: - mkArray :: [Any] -> gvariant + Type: + mkArray :: [Any] -> gvariant - Example: - # Creating a string array - lib.gvariant.mkArray [ "a" "b" "c" ] + Example: + # Creating a string array + lib.gvariant.mkArray [ "a" "b" "c" ] */ - mkArray = elems: + mkArray = + elems: let - vs = map mkValue (lib.throwIf (elems == [ ]) - "Please create empty array with mkEmptyArray." elems); - elemType = - lib.throwIfNot (lib.all (t: (head vs).type == t) (map (v: v.type) vs)) - "Elements in a list should have same type." (head vs).type; - in mkPrimitive (type.arrayOf elemType) vs // { - __toString = self: - "@${self.type} [${concatMapStringsSep "," toString self.value}]"; + vs = map mkValue (lib.throwIf (elems == [ ]) "Please create empty array with mkEmptyArray." elems); + elemType = lib.throwIfNot (lib.all (t: (head vs).type == t) ( + map (v: v.type) vs + )) "Elements in a list should have same type." (head vs).type; + in + mkPrimitive (type.arrayOf elemType) vs + // { + __toString = self: "@${self.type} [${concatMapStringsSep "," toString self.value}]"; }; - /* Returns the GVariant array from the given empty Nix list. + /* + Returns the GVariant array from the given empty Nix list. - Type: - mkEmptyArray :: gvariant.type -> gvariant + Type: + mkEmptyArray :: gvariant.type -> gvariant - Example: - # Creating an empty string array - lib.gvariant.mkEmptyArray (lib.gvariant.type.string) + Example: + # Creating an empty string array + lib.gvariant.mkEmptyArray (lib.gvariant.type.string) */ - mkEmptyArray = elemType: - mkPrimitive (type.arrayOf elemType) [ ] // { - __toString = self: "@${self.type} []"; - }; + mkEmptyArray = + elemType: mkPrimitive (type.arrayOf elemType) [ ] // { __toString = self: "@${self.type} []"; }; - /* Returns the GVariant variant from the given Nix value. Variants are containers - of different GVariant type. + /* + Returns the GVariant variant from the given Nix value. Variants are containers + of different GVariant type. - Type: - mkVariant :: Any -> gvariant + Type: + mkVariant :: Any -> gvariant - Example: - lib.gvariant.mkArray [ - (lib.gvariant.mkVariant "a string") - (lib.gvariant.mkVariant (lib.gvariant.mkInt32 1)) - ] + Example: + lib.gvariant.mkArray [ + (lib.gvariant.mkVariant "a string") + (lib.gvariant.mkVariant (lib.gvariant.mkInt32 1)) + ] */ - mkVariant = elem: - let gvarElem = mkValue elem; - in mkPrimitive type.variant gvarElem // { - __toString = self: "<${toString self.value}>"; - }; - - /* Returns the GVariant dictionary entry from the given key and value. - - Type: - mkDictionaryEntry :: String -> Any -> gvariant - - Example: - # A dictionary describing an Epiphany’s search provider - [ - (lib.gvariant.mkDictionaryEntry "url" (lib.gvariant.mkVariant "https://duckduckgo.com/?q=%s&t=epiphany")) - (lib.gvariant.mkDictionaryEntry "bang" (lib.gvariant.mkVariant "!d")) - (lib.gvariant.mkDictionaryEntry "name" (lib.gvariant.mkVariant "DuckDuckGo")) - ] + mkVariant = + elem: + let + gvarElem = mkValue elem; + in + mkPrimitive type.variant gvarElem // { __toString = self: "<${toString self.value}>"; }; + + /* + Returns the GVariant dictionary entry from the given key and value. + + Type: + mkDictionaryEntry :: String -> Any -> gvariant + + Example: + # A dictionary describing an Epiphany’s search provider + [ + (lib.gvariant.mkDictionaryEntry "url" (lib.gvariant.mkVariant "https://duckduckgo.com/?q=%s&t=epiphany")) + (lib.gvariant.mkDictionaryEntry "bang" (lib.gvariant.mkVariant "!d")) + (lib.gvariant.mkDictionaryEntry "name" (lib.gvariant.mkVariant "DuckDuckGo")) + ] */ mkDictionaryEntry = # The key of the entry @@ -144,142 +156,163 @@ in rec { name' = mkValue name; value' = mkValue value; dictionaryType = type.dictionaryEntryOf name'.type value'.type; - in mkPrimitive dictionaryType { inherit name value; } // { + in + mkPrimitive dictionaryType { inherit name value; } + // { __toString = self: "@${self.type} {${name'},${value'}}"; }; - /* Returns the GVariant maybe from the given element type. + /* + Returns the GVariant maybe from the given element type. - Type: - mkMaybe :: gvariant.type -> Any -> gvariant + Type: + mkMaybe :: gvariant.type -> Any -> gvariant */ - mkMaybe = elemType: elem: - mkPrimitive (type.maybeOf elemType) elem // { - __toString = self: - if self.value == null then - "@${self.type} nothing" - else - "just ${toString self.value}"; + mkMaybe = + elemType: elem: + mkPrimitive (type.maybeOf elemType) elem + // { + __toString = + self: if self.value == null then "@${self.type} nothing" else "just ${toString self.value}"; }; - /* Returns the GVariant nothing from the given element type. + /* + Returns the GVariant nothing from the given element type. - Type: - mkNothing :: gvariant.type -> gvariant + Type: + mkNothing :: gvariant.type -> gvariant */ mkNothing = elemType: mkMaybe elemType null; - /* Returns the GVariant just from the given Nix value. + /* + Returns the GVariant just from the given Nix value. - Type: - mkJust :: Any -> gvariant + Type: + mkJust :: Any -> gvariant */ - mkJust = elem: let gvarElem = mkValue elem; in mkMaybe gvarElem.type gvarElem; + mkJust = + elem: + let + gvarElem = mkValue elem; + in + mkMaybe gvarElem.type gvarElem; - /* Returns the GVariant tuple from the given Nix list. + /* + Returns the GVariant tuple from the given Nix list. - Type: - mkTuple :: [Any] -> gvariant + Type: + mkTuple :: [Any] -> gvariant */ - mkTuple = elems: + mkTuple = + elems: let gvarElems = map mkValue elems; tupleType = type.tupleOf (map (e: e.type) gvarElems); - in mkPrimitive tupleType gvarElems // { - __toString = self: - "@${self.type} (${concatMapStringsSep "," toString self.value})"; + in + mkPrimitive tupleType gvarElems + // { + __toString = self: "@${self.type} (${concatMapStringsSep "," toString self.value})"; }; - /* Returns the GVariant boolean from the given Nix bool value. + /* + Returns the GVariant boolean from the given Nix bool value. - Type: - mkBoolean :: Bool -> gvariant + Type: + mkBoolean :: Bool -> gvariant */ - mkBoolean = v: - mkPrimitive type.boolean v // { - __toString = self: if self.value then "true" else "false"; - }; + mkBoolean = + v: mkPrimitive type.boolean v // { __toString = self: if self.value then "true" else "false"; }; - /* Returns the GVariant string from the given Nix string value. + /* + Returns the GVariant string from the given Nix string value. - Type: - mkString :: String -> gvariant + Type: + mkString :: String -> gvariant */ - mkString = v: - let sanitize = s: replaceStrings [ "\n" ] [ "\\n" ] (escape [ "'" "\\" ] s); - in mkPrimitive type.string v // { - __toString = self: "'${sanitize self.value}'"; - }; - - /* Returns the GVariant object path from the given Nix string value. - - Type: - mkObjectpath :: String -> gvariant + mkString = + v: + let + sanitize = + s: + replaceStrings [ "\n" ] [ "\\n" ] ( + escape [ + "'" + "\\" + ] s + ); + in + mkPrimitive type.string v // { __toString = self: "'${sanitize self.value}'"; }; + + /* + Returns the GVariant object path from the given Nix string value. + + Type: + mkObjectpath :: String -> gvariant */ - mkObjectpath = v: - mkPrimitive type.string v // { - __toString = self: "objectpath '${escape [ "'" ] self.value}'"; - }; + mkObjectpath = + v: mkPrimitive type.string v // { __toString = self: "objectpath '${escape [ "'" ] self.value}'"; }; - /* Returns the GVariant uchar from the given Nix int value. + /* + Returns the GVariant uchar from the given Nix int value. - Type: - mkUchar :: Int -> gvariant + Type: + mkUchar :: Int -> gvariant */ mkUchar = mkPrimitive type.uchar; - /* Returns the GVariant int16 from the given Nix int value. + /* + Returns the GVariant int16 from the given Nix int value. - Type: - mkInt16 :: Int -> gvariant + Type: + mkInt16 :: Int -> gvariant */ mkInt16 = mkPrimitive type.int16; - /* Returns the GVariant uint16 from the given Nix int value. + /* + Returns the GVariant uint16 from the given Nix int value. - Type: - mkUint16 :: Int -> gvariant + Type: + mkUint16 :: Int -> gvariant */ mkUint16 = mkPrimitive type.uint16; - /* Returns the GVariant int32 from the given Nix int value. + /* + Returns the GVariant int32 from the given Nix int value. - Type: - mkInt32 :: Int -> gvariant + Type: + mkInt32 :: Int -> gvariant */ - mkInt32 = v: - mkPrimitive type.int32 v // { - __toString = self: toString self.value; - }; + mkInt32 = v: mkPrimitive type.int32 v // { __toString = self: toString self.value; }; - /* Returns the GVariant uint32 from the given Nix int value. + /* + Returns the GVariant uint32 from the given Nix int value. - Type: - mkUint32 :: Int -> gvariant + Type: + mkUint32 :: Int -> gvariant */ mkUint32 = mkPrimitive type.uint32; - /* Returns the GVariant int64 from the given Nix int value. + /* + Returns the GVariant int64 from the given Nix int value. - Type: - mkInt64 :: Int -> gvariant + Type: + mkInt64 :: Int -> gvariant */ mkInt64 = mkPrimitive type.int64; - /* Returns the GVariant uint64 from the given Nix int value. + /* + Returns the GVariant uint64 from the given Nix int value. - Type: - mkUint64 :: Int -> gvariant + Type: + mkUint64 :: Int -> gvariant */ mkUint64 = mkPrimitive type.uint64; - /* Returns the GVariant double from the given Nix float value. + /* + Returns the GVariant double from the given Nix float value. - Type: - mkDouble :: Float -> gvariant + Type: + mkDouble :: Float -> gvariant */ - mkDouble = v: - mkPrimitive type.double v // { - __toString = self: toString self.value; - }; + mkDouble = v: mkPrimitive type.double v // { __toString = self: toString self.value; }; } diff --git a/nix/stdlib/kernel.nix b/nix/stdlib/kernel.nix index 3d2a1d2..709e92a 100644 --- a/nix/stdlib/kernel.nix +++ b/nix/stdlib/kernel.nix @@ -1,6 +1,8 @@ { lib }: -let inherit (lib) mkIf versionAtLeast versionOlder; -in { +let + inherit (lib) mkIf versionAtLeast versionOlder; +in +{ # Keeping these around in case we decide to change this horrible implementation :) option = x: x // { optional = true; }; @@ -30,7 +32,6 @@ in { whenAtLeast = ver: mkIf (versionAtLeast version ver); whenOlder = ver: mkIf (versionOlder version ver); # range is (inclusive, exclusive) - whenBetween = verLow: verHigh: - mkIf (versionAtLeast version verLow && versionOlder version verHigh); + whenBetween = verLow: verHigh: mkIf (versionAtLeast version verLow && versionOlder version verHigh); }; } diff --git a/nix/stdlib/licenses.nix b/nix/stdlib/licenses.nix index 2ae40e6..8a90e26 100644 --- a/nix/stdlib/licenses.nix +++ b/nix/stdlib/licenses.nix @@ -1,1341 +1,1324 @@ { lib }: -lib.mapAttrs (lname: lset: - let - defaultLicense = { - shortName = lname; - free = - true; # Most of our licenses are Free, explicitly declare unfree additions as such! - deprecated = false; - }; - - mkLicense = licenseDeclaration: - let - applyDefaults = license: defaultLicense // license; - applySpdx = license: - if license ? spdxId then - license // { - url = "https://spdx.org/licenses/${license.spdxId}.html"; - } - else - license; - applyRedistributable = license: - { - redistributable = license.free; - } // license; - in lib.pipe licenseDeclaration [ - applyDefaults - applySpdx - applyRedistributable - ]; - in mkLicense lset) ({ - # License identifiers from spdx.org where possible. - # If you cannot find your license here, then look for a similar license or - # add it to this list. The URL mentioned above is a good source for inspiration. - - abstyles = { - spdxId = "Abstyles"; - fullName = "Abstyles License"; - }; - - acsl14 = { - fullName = "Anti-Capitalist Software License v1.4"; - url = "https://anticapitalist.software/"; - # restrictions on corporations apply for both use and redistribution - free = false; - redistributable = false; - }; - - activision = { - # https://doomwiki.org/wiki/Raven_source_code_licensing - fullName = "Activision EULA"; - url = "https://www.doomworld.com/eternity/activision_eula.txt"; - free = false; - }; - - afl20 = { - spdxId = "AFL-2.0"; - fullName = "Academic Free License v2.0"; - }; - - afl21 = { - spdxId = "AFL-2.1"; - fullName = "Academic Free License v2.1"; - }; - - afl3 = { - spdxId = "AFL-3.0"; - fullName = "Academic Free License v3.0"; - }; - - agpl3Only = { - spdxId = "AGPL-3.0-only"; - fullName = "GNU Affero General Public License v3.0 only"; - }; - - agpl3Plus = { - spdxId = "AGPL-3.0-or-later"; - fullName = "GNU Affero General Public License v3.0 or later"; - }; - - aladdin = { - spdxId = "Aladdin"; - fullName = "Aladdin Free Public License"; - free = false; - }; - - amazonsl = { - fullName = "Amazon Software License"; - url = "https://aws.amazon.com/asl/"; - free = false; - }; - - amd = { - fullName = "AMD License Agreement"; - url = "https://developer.amd.com/amd-license-agreement/"; - free = false; - }; - - aom = { - fullName = "Alliance for Open Media Patent License 1.0"; - url = "https://aomedia.org/license/patent-license/"; - }; - - apple-psl10 = { - spdxId = "APSL-1.0"; - fullName = "Apple Public Source License 1.0"; - }; - - apple-psl20 = { - spdxId = "APSL-2.0"; - fullName = "Apple Public Source License 2.0"; - }; - - arphicpl = { - spdxId = "Arphic-1999"; - fullName = "Arphic Public License"; - url = "https://www.freedesktop.org/wiki/Arphic_Public_License/"; - }; - - artistic1 = { - spdxId = "Artistic-1.0"; - fullName = "Artistic License 1.0"; - }; - - artistic1-cl8 = { - spdxId = "Artistic-1.0-cl8"; - fullName = "Artistic License 1.0 w/clause 8"; - }; - - artistic2 = { - spdxId = "Artistic-2.0"; - fullName = "Artistic License 2.0"; - }; - - asl20 = { - spdxId = "Apache-2.0"; - fullName = "Apache License 2.0"; - }; - - asl20-llvm = { - spdxId = "Apache-2.0 WITH LLVM-exception"; - fullName = "Apache License 2.0 with LLVM Exceptions"; - }; - - bitstreamVera = { - spdxId = "Bitstream-Vera"; - fullName = "Bitstream Vera Font License"; - }; - - bitTorrent10 = { - spdxId = "BitTorrent-1.0"; - fullName = " BitTorrent Open Source License v1.0"; - }; - - bitTorrent11 = { - spdxId = "BitTorrent-1.1"; - fullName = " BitTorrent Open Source License v1.1"; - }; - - bola11 = { - url = "https://blitiri.com.ar/p/bola/"; - fullName = "Buena Onda License Agreement 1.1"; - }; - - boost = { - spdxId = "BSL-1.0"; - fullName = "Boost Software License 1.0"; - }; - - beerware = { - spdxId = "Beerware"; - fullName = "Beerware License"; - }; - - blueOak100 = { - spdxId = "BlueOak-1.0.0"; - fullName = "Blue Oak Model License 1.0.0"; - }; - - bsd0 = { - spdxId = "0BSD"; - fullName = "BSD Zero Clause License"; - }; - - bsd1 = { - spdxId = "BSD-1-Clause"; - fullName = "BSD 1-Clause License"; - }; - - bsd2 = { - spdxId = "BSD-2-Clause"; - fullName = ''BSD 2-clause "Simplified" License''; - }; - - bsd2Patent = { - spdxId = "BSD-2-Clause-Patent"; - fullName = "BSD-2-Clause Plus Patent License"; - }; - - bsd2WithViews = { - spdxId = "BSD-2-Clause-Views"; - fullName = "BSD 2-Clause with views sentence"; - }; - - bsd3 = { - spdxId = "BSD-3-Clause"; - fullName = ''BSD 3-clause "New" or "Revised" License''; - }; - - bsd3Clear = { - spdxId = "BSD-3-Clause-Clear"; - fullName = "BSD 3-Clause Clear License"; - }; - - bsdOriginal = { - spdxId = "BSD-4-Clause"; - fullName = ''BSD 4-clause "Original" or "Old" License''; - }; - - bsdOriginalShortened = { - spdxId = "BSD-4-Clause-Shortened"; - fullName = "BSD 4 Clause Shortened"; - }; - - bsdOriginalUC = { - spdxId = "BSD-4-Clause-UC"; - fullName = "BSD 4-Clause University of California-Specific"; - }; - - bsdProtection = { - spdxId = "BSD-Protection"; - fullName = "BSD Protection License"; - }; - - bsl11 = { - fullName = "Business Source License 1.1"; - url = "https://mariadb.com/bsl11"; - free = false; - redistributable = true; - }; - - caossl = { - fullName = "Computer Associates Open Source Licence Version 1.0"; - url = "http://jxplorer.org/licence.html"; - }; - - cal10 = { - spdxId = "CAL-1.0"; - fullName = "Cryptographic Autonomy License version 1.0 (CAL-1.0)"; - url = "https://opensource.org/licenses/CAL-1.0"; - }; - - caldera = { - spdxId = "Caldera"; - fullName = "Caldera License"; - url = "http://www.lemis.com/grog/UNIX/ancient-source-all.pdf"; - }; - - capec = { - fullName = "Common Attack Pattern Enumeration and Classification"; - url = "https://capec.mitre.org/about/termsofuse.html"; - }; - - clArtistic = { - spdxId = "ClArtistic"; - fullName = "Clarified Artistic License"; - }; - - cc0 = { - spdxId = "CC0-1.0"; - fullName = "Creative Commons Zero v1.0 Universal"; - }; - - cc-by-nc-nd-30 = { - spdxId = "CC-BY-NC-ND-3.0"; - fullName = - "Creative Commons Attribution Non Commercial No Derivative Works 3.0 Unported"; - free = false; - }; - - cc-by-nc-nd-40 = { - spdxId = "CC-BY-NC-ND-4.0"; - fullName = - "Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International"; - free = false; - }; - - cc-by-nc-sa-20 = { - spdxId = "CC-BY-NC-SA-2.0"; - fullName = "Creative Commons Attribution Non Commercial Share Alike 2.0"; - free = false; - }; - - cc-by-nc-sa-25 = { - spdxId = "CC-BY-NC-SA-2.5"; - fullName = "Creative Commons Attribution Non Commercial Share Alike 2.5"; - free = false; - }; - - cc-by-nc-sa-30 = { - spdxId = "CC-BY-NC-SA-3.0"; - fullName = "Creative Commons Attribution Non Commercial Share Alike 3.0"; - free = false; - }; - - cc-by-nc-sa-40 = { - spdxId = "CC-BY-NC-SA-4.0"; - fullName = "Creative Commons Attribution Non Commercial Share Alike 4.0"; - free = false; - }; - - cc-by-nc-30 = { - spdxId = "CC-BY-NC-3.0"; - fullName = "Creative Commons Attribution Non Commercial 3.0 Unported"; - free = false; - }; - - cc-by-nc-40 = { - spdxId = "CC-BY-NC-4.0"; - fullName = - "Creative Commons Attribution Non Commercial 4.0 International"; - free = false; - }; - - cc-by-nd-30 = { - spdxId = "CC-BY-ND-3.0"; - fullName = "Creative Commons Attribution-No Derivative Works v3.00"; - free = false; - }; - - cc-by-sa-10 = { - spdxId = "CC-BY-SA-1.0"; - fullName = "Creative Commons Attribution Share Alike 1.0"; - }; - - cc-by-sa-20 = { - spdxId = "CC-BY-SA-2.0"; - fullName = "Creative Commons Attribution Share Alike 2.0"; - }; - - cc-by-sa-25 = { - spdxId = "CC-BY-SA-2.5"; - fullName = "Creative Commons Attribution Share Alike 2.5"; - }; - - cc-by-10 = { - spdxId = "CC-BY-1.0"; - fullName = "Creative Commons Attribution 1.0"; - }; - - cc-by-20 = { - spdxId = "CC-BY-2.0"; - fullName = "Creative Commons Attribution 2.0"; - }; - - cc-by-30 = { - spdxId = "CC-BY-3.0"; - fullName = "Creative Commons Attribution 3.0"; - }; - - cc-by-sa-30 = { - spdxId = "CC-BY-SA-3.0"; - fullName = "Creative Commons Attribution Share Alike 3.0"; - }; - - cc-by-40 = { - spdxId = "CC-BY-4.0"; - fullName = "Creative Commons Attribution 4.0"; - }; - - cc-by-sa-40 = { - spdxId = "CC-BY-SA-4.0"; - fullName = "Creative Commons Attribution Share Alike 4.0"; - }; - - cddl = { - spdxId = "CDDL-1.0"; - fullName = "Common Development and Distribution License 1.0"; - }; - - cecill20 = { - spdxId = "CECILL-2.0"; - fullName = "CeCILL Free Software License Agreement v2.0"; - }; - - cecill21 = { - spdxId = "CECILL-2.1"; - fullName = "CeCILL Free Software License Agreement v2.1"; - }; - - cecill-b = { - spdxId = "CECILL-B"; - fullName = "CeCILL-B Free Software License Agreement"; - }; - - cecill-c = { - spdxId = "CECILL-C"; - fullName = "CeCILL-C Free Software License Agreement"; - }; - - cpal10 = { - spdxId = "CPAL-1.0"; - fullName = "Common Public Attribution License 1.0"; - }; - - commons-clause = { - fullName = "Commons Clause License"; - url = "https://commonsclause.com/"; - free = false; - }; - - cpl10 = { - spdxId = "CPL-1.0"; - fullName = "Common Public License 1.0"; - }; - - curl = { - spdxId = "curl"; - fullName = "curl License"; - }; - - doc = { - spdxId = "DOC"; - fullName = "DOC License"; - }; - - drl10 = { - spdxId = "DRL-1.0"; - fullName = "Detection Rule License 1.0"; - }; - - dtoa = { - spdxId = "dtoa"; - fullName = "dtoa License"; - }; - - eapl = { - fullName = "EPSON AVASYS PUBLIC LICENSE"; - url = "https://avasys.jp/hp/menu000000700/hpg000000603.htm"; - free = false; - }; - - ecl20 = { - fullName = "Educational Community License, Version 2.0"; - url = "https://opensource.org/licenses/ECL-2.0"; - shortName = "ECL 2.0"; - spdxId = "ECL-2.0"; - }; - - efl10 = { - spdxId = "EFL-1.0"; - fullName = "Eiffel Forum License v1.0"; - }; - - efl20 = { - spdxId = "EFL-2.0"; - fullName = "Eiffel Forum License v2.0"; - }; - - elastic20 = { - spdxId = "Elastic-2.0"; - fullName = "Elastic License 2.0"; - url = - "https://github.com/elastic/elasticsearch/blob/main/licenses/ELASTIC-LICENSE-2.0.txt"; - free = false; - }; - - epl10 = { - spdxId = "EPL-1.0"; - fullName = "Eclipse Public License 1.0"; - }; - - epl20 = { - spdxId = "EPL-2.0"; - fullName = "Eclipse Public License 2.0"; - }; - - epson = { - fullName = "Seiko Epson Corporation Software License Agreement for Linux"; - url = - "https://download.ebz.epson.net/dsc/du/02/eula/global/LINUX_EN.html"; - free = false; - }; - - eupl11 = { - spdxId = "EUPL-1.1"; - fullName = "European Union Public License 1.1"; - }; - - eupl12 = { - spdxId = "EUPL-1.2"; - fullName = "European Union Public License 1.2"; - }; - - fdl11Only = { - spdxId = "GFDL-1.1-only"; - fullName = "GNU Free Documentation License v1.1 only"; - }; - - fdl11Plus = { - spdxId = "GFDL-1.1-or-later"; - fullName = "GNU Free Documentation License v1.1 or later"; - }; - - fdl12Only = { - spdxId = "GFDL-1.2-only"; - fullName = "GNU Free Documentation License v1.2 only"; - }; - - fdl12Plus = { - spdxId = "GFDL-1.2-or-later"; - fullName = "GNU Free Documentation License v1.2 or later"; - }; - - fdl13Only = { - spdxId = "GFDL-1.3-only"; - fullName = "GNU Free Documentation License v1.3 only"; - }; - - fdl13Plus = { - spdxId = "GFDL-1.3-or-later"; - fullName = "GNU Free Documentation License v1.3 or later"; - }; - - ffsl = { - fullName = "Floodgap Free Software License"; - url = "https://www.floodgap.com/software/ffsl/license.html"; - free = false; - }; - - fraunhofer-fdk = { - fullName = "Fraunhofer FDK AAC Codec Library"; - spdxId = "FDK-AAC"; - }; - - free = { fullName = "Unspecified free software license"; }; - - ftl = { - spdxId = "FTL"; - fullName = "Freetype Project License"; - }; - - g4sl = { - fullName = "Geant4 Software License"; - url = "https://geant4.web.cern.ch/geant4/license/LICENSE.html"; - }; - - geogebra = { - fullName = "GeoGebra Non-Commercial License Agreement"; - url = "https://www.geogebra.org/license"; - free = false; - }; - - generaluser = { - fullName = "GeneralUser GS License v2.0"; - url = - "https://www.schristiancollins.com/generaluser.php"; # license included in sources - }; - - gfl = { - fullName = "GUST Font License"; - url = - "https://www.gust.org.pl/projects/e-foundry/licenses/GUST-FONT-LICENSE.txt"; - }; - - gfsl = { - fullName = "GUST Font Source License"; - url = - "https://www.gust.org.pl/projects/e-foundry/licenses/GUST-FONT-SOURCE-LICENSE.txt"; - }; - - gpl1Only = { - spdxId = "GPL-1.0-only"; - fullName = "GNU General Public License v1.0 only"; - }; - - gpl1Plus = { - spdxId = "GPL-1.0-or-later"; - fullName = "GNU General Public License v1.0 or later"; - }; - - gpl2Only = { - spdxId = "GPL-2.0-only"; - fullName = "GNU General Public License v2.0 only"; - }; - - gpl2Classpath = { - spdxId = "GPL-2.0-with-classpath-exception"; - fullName = - "GNU General Public License v2.0 only (with Classpath exception)"; - }; - - gpl2ClasspathPlus = { - fullName = - "GNU General Public License v2.0 or later (with Classpath exception)"; - url = "https://fedoraproject.org/wiki/Licensing/GPL_Classpath_Exception"; - }; - - gpl2Oss = { - fullName = - "GNU General Public License version 2 only (with OSI approved licenses linking exception)"; - url = "https://www.mysql.com/about/legal/licensing/foss-exception"; - }; - - gpl2Plus = { - spdxId = "GPL-2.0-or-later"; - fullName = "GNU General Public License v2.0 or later"; - }; - - gpl3Only = { - spdxId = "GPL-3.0-only"; - fullName = "GNU General Public License v3.0 only"; - }; - - gpl3Plus = { - spdxId = "GPL-3.0-or-later"; - fullName = "GNU General Public License v3.0 or later"; - }; - - gpl3ClasspathPlus = { - fullName = - "GNU General Public License v3.0 or later (with Classpath exception)"; - url = "https://fedoraproject.org/wiki/Licensing/GPL_Classpath_Exception"; - }; - - giftware = { - spdxId = "Giftware"; - fullName = "Giftware License"; - }; - - hpnd = { - spdxId = "HPND"; - fullName = "Historic Permission Notice and Disclaimer"; - }; - - hpndSellVariant = { - fullName = "Historical Permission Notice and Disclaimer - sell variant"; - spdxId = "HPND-sell-variant"; - }; - - hpndUc = { - spdxId = "HPND-UC"; - fullName = - "Historical Permission Notice and Disclaimer - University of California variant"; - }; - - # Intel's license, seems free - iasl = { - spdxId = "Intel-ACPI"; - fullName = "iASL"; - url = "https://old.calculate-linux.org/packages/licenses/iASL"; - }; - - ijg = { - spdxId = "IJG"; - fullName = "Independent JPEG Group License"; - }; - - imagemagick = { - fullName = "ImageMagick License"; - spdxId = "ImageMagick"; - }; - - imlib2 = { - spdxId = "Imlib2"; - fullName = "Imlib2 License"; - }; - - info-zip = { - spdxId = "Info-ZIP"; - fullName = "Info-ZIP License"; - url = "https://infozip.sourceforge.net/license.html"; - }; - - inria-compcert = { - fullName = - "INRIA Non-Commercial License Agreement for the CompCert verified compiler"; - url = "https://compcert.org/doc/LICENSE.txt"; - free = false; - }; - - inria-icesl = { - fullName = "End User License Agreement for IceSL Software"; - url = "https://icesl.loria.fr/assets/pdf/EULA_IceSL_binary.pdf"; - free = false; - }; - - inria-zelus = { - fullName = - "INRIA Non-Commercial License Agreement for the Zélus compiler"; - url = - "https://github.com/INRIA/zelus/raw/829f2b97cba93b0543a9ca0272269e6b8fdad356/LICENSE"; - free = false; - }; - - ipa = { - spdxId = "IPA"; - fullName = "IPA Font License"; - }; - - ipl10 = { - spdxId = "IPL-1.0"; - fullName = "IBM Public License v1.0"; - }; - - isc = { - spdxId = "ISC"; - fullName = "ISC License"; - }; - - # Proprietary binaries; free to redistribute without modification. - databricks = { - fullName = "Databricks Proprietary License"; - url = "https://pypi.org/project/databricks-connect"; - free = false; - }; - - databricks-dbx = { - fullName = "DataBricks eXtensions aka dbx License"; - url = - "https://github.com/databrickslabs/dbx/blob/743b579a4ac44531f764c6e522dbe5a81a7dc0e4/LICENSE"; - free = false; - redistributable = false; - }; - - fair = { - fullName = "Fair License"; - spdxId = "Fair"; - free = true; - }; - - fairsource09 = { - fullName = "Fair Source License, version 0.9"; - url = "https://fair.io/v0.9.txt"; - free = false; - redistributable = true; - }; - - hl3 = { - fullName = "Hippocratic License v3.0"; - url = "https://firstdonoharm.dev/version/3/0/core.txt"; - free = false; - redistributable = true; - }; - - issl = { - fullName = "Intel Simplified Software License"; - url = - "https://software.intel.com/en-us/license/intel-simplified-software-license"; - free = false; - }; - - knuth = { - fullName = "Knuth CTAN License"; - spdxId = "Knuth-CTAN"; - }; - - lal12 = { - spdxId = "LAL-1.2"; - fullName = "Licence Art Libre 1.2"; - }; - - lal13 = { - spdxId = "LAL-1.3"; - fullName = "Licence Art Libre 1.3"; - }; - - lens = { - fullName = "Lens Terms of Service Agreement"; - url = "https://k8slens.dev/licenses/tos"; - free = false; - }; - - lgpl2Only = { - spdxId = "LGPL-2.0-only"; - fullName = "GNU Library General Public License v2 only"; - }; - - lgpl2Plus = { - spdxId = "LGPL-2.0-or-later"; - fullName = "GNU Library General Public License v2 or later"; - }; - - lgpl21Only = { - spdxId = "LGPL-2.1-only"; - fullName = "GNU Lesser General Public License v2.1 only"; - }; - - lgpl21Plus = { - spdxId = "LGPL-2.1-or-later"; - fullName = "GNU Lesser General Public License v2.1 or later"; - }; - - lgpl3Only = { - spdxId = "LGPL-3.0-only"; - fullName = "GNU Lesser General Public License v3.0 only"; - }; - - lgpl3Plus = { - spdxId = "LGPL-3.0-or-later"; - fullName = "GNU Lesser General Public License v3.0 or later"; - }; - - lgpllr = { - spdxId = "LGPLLR"; - fullName = "Lesser General Public License For Linguistic Resources"; - }; - - libpng = { - spdxId = "Libpng"; - fullName = "libpng License"; - }; - - libpng2 = { - spdxId = "libpng-2.0"; # Used since libpng 1.6.36. - fullName = "PNG Reference Library version 2"; - }; - - libssh2 = { - fullName = "libssh2 License"; - url = "https://www.libssh2.org/license.html"; - }; - - libtiff = { - spdxId = "libtiff"; - fullName = "libtiff License"; - }; - - llgpl21 = { - fullName = - "Lisp LGPL; GNU Lesser General Public License version 2.1 with Franz Inc. preamble for clarification of LGPL terms in context of Lisp"; - url = "https://opensource.franz.com/preamble.html"; - }; - - lppl1 = { - spdxId = "LPPL-1.0"; - fullName = "LaTeX Project Public License v1.0"; - }; - - lppl12 = { - spdxId = "LPPL-1.2"; - fullName = "LaTeX Project Public License v1.2"; - }; - - lppl13a = { - spdxId = "LPPL-1.3a"; - fullName = "LaTeX Project Public License v1.3a"; - }; - - lppl13c = { - spdxId = "LPPL-1.3c"; - fullName = "LaTeX Project Public License v1.3c"; - }; - - lpl-102 = { - spdxId = "LPL-1.02"; - fullName = "Lucent Public License v1.02"; - }; - - miros = { - spdxId = "MirOS"; - fullName = "MirOS License"; - url = "https://opensource.org/licenses/MirOS"; - }; - - # spdx.org does not (yet) differentiate between the X11 and Expat versions - # for details see https://en.wikipedia.org/wiki/MIT_License#Various_versions - mit = { - spdxId = "MIT"; - fullName = "MIT License"; - }; - # https://spdx.org/licenses/MIT-feh.html - mit-feh = { - spdxId = "MIT-feh"; - fullName = "feh License"; - }; - - mitAdvertising = { - spdxId = "MIT-advertising"; - fullName = "Enlightenment License (e16)"; - }; - - mit0 = { - spdxId = "MIT-0"; - fullName = "MIT No Attribution"; - }; - - mpl10 = { - spdxId = "MPL-1.0"; - fullName = "Mozilla Public License 1.0"; - }; - - mpl11 = { - spdxId = "MPL-1.1"; - fullName = "Mozilla Public License 1.1"; - }; - - mpl20 = { - spdxId = "MPL-2.0"; - fullName = "Mozilla Public License 2.0"; - }; - - mplus = { - spdxId = "mplus"; - fullName = "M+ Font License"; - }; - - mspl = { - spdxId = "MS-PL"; - fullName = "Microsoft Public License"; - }; - - mulan-psl2 = { - spdxId = "MulanPSL-2.0"; - fullName = "Mulan Permissive Software License, Version 2"; - url = "https://license.coscl.org.cn/MulanPSL2"; - }; - - nasa13 = { - spdxId = "NASA-1.3"; - fullName = "NASA Open Source Agreement 1.3"; - free = false; - }; - - ncsa = { - spdxId = "NCSA"; - fullName = "University of Illinois/NCSA Open Source License"; - }; - - ncul1 = { - spdxId = "NCUL1"; - fullName = "Netdata Cloud UI License v1.0"; - free = false; - redistributable = true; # Only if used in Netdata products. - url = - "https://raw.githubusercontent.com/netdata/netdata/master/web/gui/v2/LICENSE.md"; - }; - - nistSoftware = { - spdxId = "NIST-Software"; - fullName = "NIST Software License"; - }; - - nlpl = { - spdxId = "NLPL"; - fullName = "No Limit Public License"; - }; - - nposl3 = { - spdxId = "NPOSL-3.0"; - fullName = "Non-Profit Open Software License 3.0"; - }; - - nvidiaCuda = { - shortName = "CUDA EULA"; - fullName = "CUDA Toolkit End User License Agreement (EULA)"; - url = - "https://docs.nvidia.com/cuda/eula/index.html#cuda-toolkit-supplement-license-agreement"; - free = false; - }; - - nvidiaCudaRedist = { - shortName = "CUDA EULA"; - fullName = "CUDA Toolkit End User License Agreement (EULA)"; - url = - "https://docs.nvidia.com/cuda/eula/index.html#cuda-toolkit-supplement-license-agreement"; - free = false; - redistributable = true; - }; - - obsidian = { - fullName = "Obsidian End User Agreement"; - url = "https://obsidian.md/eula"; - free = false; - }; - - ocamlLgplLinkingException = { - spdxId = "OCaml-LGPL-linking-exception"; - fullName = "OCaml LGPL Linking Exception"; - }; - - ocamlpro_nc = { - fullName = "OCamlPro Non Commercial license version 1"; - url = - "https://alt-ergo.ocamlpro.com/http/alt-ergo-2.2.0/OCamlPro-Non-Commercial-License.pdf"; - free = false; - }; - - odbl = { - spdxId = "ODbL-1.0"; - fullName = "Open Data Commons Open Database License v1.0"; - }; - - ofl = { - spdxId = "OFL-1.1"; - fullName = "SIL Open Font License 1.1"; - }; - - oml = { - spdxId = "OML"; - fullName = "Open Market License"; - }; - - openldap = { - spdxId = "OLDAP-2.8"; - fullName = "Open LDAP Public License v2.8"; - }; - - openssl = { - spdxId = "OpenSSL"; - fullName = "OpenSSL License"; - }; - - opubl = { - spdxId = "OPUBL-1.0"; - fullName = "Open Publication License v1.0"; - }; - - osl2 = { - spdxId = "OSL-2.0"; - fullName = "Open Software License 2.0"; - }; - - osl21 = { - spdxId = "OSL-2.1"; - fullName = "Open Software License 2.1"; - }; - - osl3 = { - spdxId = "OSL-3.0"; - fullName = "Open Software License 3.0"; - }; - - parity70 = { - spdxId = "Parity-7.0.0"; - fullName = "Parity Public License 7.0.0"; - url = "https://paritylicense.com/versions/7.0.0.html"; - }; - - php301 = { - spdxId = "PHP-3.01"; - fullName = "PHP License v3.01"; - }; - - postgresql = { - spdxId = "PostgreSQL"; - fullName = "PostgreSQL License"; - }; - - postman = { - fullName = "Postman EULA"; - url = "https://www.getpostman.com/licenses/postman_base_app"; - free = false; - }; - - psfl = { - spdxId = "Python-2.0"; - fullName = "Python Software Foundation License version 2"; - url = "https://docs.python.org/license.html"; - }; - - publicDomain = { fullName = "Public Domain"; }; - - purdueBsd = { - fullName = " Purdue BSD-Style License"; # also know as lsof license - url = "https://enterprise.dejacode.com/licenses/public/purdue-bsd"; - }; - - prosperity30 = { - fullName = "Prosperity-3.0.0"; - free = false; - url = "https://prosperitylicense.com/versions/3.0.0.html"; - }; - - qhull = { - spdxId = "Qhull"; - fullName = "Qhull License"; - }; - - qpl = { - spdxId = "QPL-1.0"; - fullName = "Q Public License 1.0"; - }; - - qwt = { - fullName = "Qwt License, Version 1.0"; - url = "https://qwt.sourceforge.io/qwtlicense.html"; - }; - - ruby = { - spdxId = "Ruby"; - fullName = "Ruby License"; - }; - - sendmail = { - spdxId = "Sendmail"; - fullName = "Sendmail License"; - }; - - sgi-b-20 = { - spdxId = "SGI-B-2.0"; - fullName = "SGI Free Software License B v2.0"; - }; - - # Gentoo seems to treat it as a license: - # https://gitweb.gentoo.org/repo/gentoo.git/tree/licenses/SGMLUG?id=7d999af4a47bf55e53e54713d98d145f935935c1 - sgmlug = { fullName = "SGML UG SGML Parser Materials license"; }; - - sleepycat = { - spdxId = "Sleepycat"; - fullName = "Sleepycat License"; - }; - - smail = { - shortName = "smail"; - fullName = "SMAIL General Public License"; - url = "https://sources.debian.org/copyright/license/debianutils/4.9.1/"; - }; - - smlnj = { - spdxId = "SMLNJ"; - fullName = "Standard ML of New Jersey License"; - }; - - sspl = { - shortName = "SSPL"; - fullName = "Server Side Public License"; - url = "https://www.mongodb.com/licensing/server-side-public-license"; - free = false; - # NOTE Debatable. - # The license a slightly modified AGPL but still considered unfree by the - # OSI for what seem like political reasons - redistributable = - true; # Definitely redistributable though, it's an AGPL derivative - }; - - stk = { - shortName = "stk"; - fullName = "Synthesis Tool Kit 4.3"; - url = "https://github.com/thestk/stk/blob/master/LICENSE"; - }; - - sudo = { - shortName = "sudo"; - fullName = "Sudo License (ISC-style)"; - url = "https://www.sudo.ws/about/license/"; - }; - - sustainableUse = { - shortName = "sustainable"; - fullName = "Sustainable Use License"; - url = "https://github.com/n8n-io/n8n/blob/master/LICENSE.md"; - free = false; - redistributable = - false; # only free to redistribute "for non-commercial purposes" - }; - - tsl = { - shortName = "TSL"; - fullName = "Timescale License Agreegment"; - url = - "https://github.com/timescale/timescaledb/blob/main/tsl/LICENSE-TIMESCALE"; - unfree = true; - }; - - tcltk = { - spdxId = "TCL"; - fullName = "TCL/TK License"; - }; - - ucd = { - fullName = "Unicode Character Database License"; - url = "https://fedoraproject.org/wiki/Licensing:UCD"; - }; - - ufl = { - fullName = "Ubuntu Font License 1.0"; - url = "https://ubuntu.com/legal/font-licence"; - }; - - unfree = { - fullName = "Unfree"; - free = false; - }; - - unfreeRedistributable = { - fullName = "Unfree redistributable"; - free = false; - redistributable = true; - }; - - unfreeRedistributableFirmware = { - fullName = "Unfree redistributable firmware"; - redistributable = true; - # Note: we currently consider these "free" for inclusion in the - # channel and NixOS images. - }; - - unicode-dfs-2015 = { - spdxId = "Unicode-DFS-2015"; - fullName = "Unicode License Agreement - Data Files and Software (2015)"; - }; - - unicode-dfs-2016 = { - spdxId = "Unicode-DFS-2016"; - fullName = "Unicode License Agreement - Data Files and Software (2016)"; - }; - - unlicense = { - spdxId = "Unlicense"; - fullName = "The Unlicense"; - }; - - upl = { - spdxId = "UPL-1.0"; - fullName = "Universal Permissive License"; - url = "https://oss.oracle.com/licenses/upl/"; - }; - - vim = { - spdxId = "Vim"; - fullName = "Vim License"; - }; - - virtualbox-puel = { - fullName = - "Oracle VM VirtualBox Extension Pack Personal Use and Evaluation License (PUEL)"; - url = "https://www.virtualbox.org/wiki/VirtualBox_PUEL"; - free = false; - }; - - vol-sl = { - fullName = "Volatility Software License, Version 1.0"; - url = "https://www.volatilityfoundation.org/license/vsl-v1.0"; - }; - - vsl10 = { - spdxId = "VSL-1.0"; - fullName = "Vovida Software License v1.0"; - }; - - watcom = { - spdxId = "Watcom-1.0"; - fullName = "Sybase Open Watcom Public License 1.0"; - }; - - w3c = { - spdxId = "W3C"; - fullName = "W3C Software Notice and License"; - }; - - wadalab = { - fullName = "Wadalab Font License"; - url = - "https://fedoraproject.org/wiki/Licensing:Wadalab?rd=Licensing/Wadalab"; - }; - - wtfpl = { - spdxId = "WTFPL"; - fullName = "Do What The F*ck You Want To Public License"; - }; - - wxWindows = { - spdxId = "wxWindows"; - fullName = "wxWindows Library Licence, Version 3.1"; - }; - - x11 = { - spdxId = "X11"; - fullName = "X11 License"; - }; - - xfig = { - spdxId = "Xfig"; - fullName = "xfig"; - url = "https://mcj.sourceforge.net/authors.html#xfig"; - }; - - xinetd = { - spdxId = "xinetd"; - fullName = "xinetd License"; - }; - - zlib = { - spdxId = "Zlib"; - fullName = "zlib License"; - }; - - zpl20 = { - spdxId = "ZPL-2.0"; - fullName = "Zope Public License 2.0"; - }; - - zpl21 = { - spdxId = "ZPL-2.1"; - fullName = "Zope Public License 2.1"; - }; - - xskat = { - spdxId = "XSkat"; - fullName = "XSkat License"; - }; - } // { - # TODO: remove legacy aliases - apsl10 = { - # deprecated for consistency with `apple-psl20`; use `apple-psl10` - spdxId = "APSL-1.0"; - fullName = "Apple Public Source License 1.0"; - deprecated = true; - }; - apsl20 = { - # deprecated due to confusion with Apache-2.0; use `apple-psl20` - spdxId = "APSL-2.0"; - fullName = "Apple Public Source License 2.0"; - deprecated = true; - }; - gpl2 = { - spdxId = "GPL-2.0"; - fullName = "GNU General Public License v2.0"; - deprecated = true; - }; - gpl3 = { - spdxId = "GPL-3.0"; - fullName = "GNU General Public License v3.0"; - deprecated = true; - }; - lgpl2 = { - spdxId = "LGPL-2.0"; - fullName = "GNU Library General Public License v2"; - deprecated = true; - }; - lgpl21 = { - spdxId = "LGPL-2.1"; - fullName = "GNU Lesser General Public License v2.1"; - deprecated = true; - }; - lgpl3 = { - spdxId = "LGPL-3.0"; - fullName = "GNU Lesser General Public License v3.0"; - deprecated = true; - }; - }) +lib.mapAttrs + ( + lname: lset: + let + defaultLicense = { + shortName = lname; + free = true; # Most of our licenses are Free, explicitly declare unfree additions as such! + deprecated = false; + }; + + mkLicense = + licenseDeclaration: + let + applyDefaults = license: defaultLicense // license; + applySpdx = + license: + if license ? spdxId then + license // { url = "https://spdx.org/licenses/${license.spdxId}.html"; } + else + license; + applyRedistributable = license: { redistributable = license.free; } // license; + in + lib.pipe licenseDeclaration [ + applyDefaults + applySpdx + applyRedistributable + ]; + in + mkLicense lset + ) + ( + { + # License identifiers from spdx.org where possible. + # If you cannot find your license here, then look for a similar license or + # add it to this list. The URL mentioned above is a good source for inspiration. + + abstyles = { + spdxId = "Abstyles"; + fullName = "Abstyles License"; + }; + + acsl14 = { + fullName = "Anti-Capitalist Software License v1.4"; + url = "https://anticapitalist.software/"; + # restrictions on corporations apply for both use and redistribution + free = false; + redistributable = false; + }; + + activision = { + # https://doomwiki.org/wiki/Raven_source_code_licensing + fullName = "Activision EULA"; + url = "https://www.doomworld.com/eternity/activision_eula.txt"; + free = false; + }; + + afl20 = { + spdxId = "AFL-2.0"; + fullName = "Academic Free License v2.0"; + }; + + afl21 = { + spdxId = "AFL-2.1"; + fullName = "Academic Free License v2.1"; + }; + + afl3 = { + spdxId = "AFL-3.0"; + fullName = "Academic Free License v3.0"; + }; + + agpl3Only = { + spdxId = "AGPL-3.0-only"; + fullName = "GNU Affero General Public License v3.0 only"; + }; + + agpl3Plus = { + spdxId = "AGPL-3.0-or-later"; + fullName = "GNU Affero General Public License v3.0 or later"; + }; + + aladdin = { + spdxId = "Aladdin"; + fullName = "Aladdin Free Public License"; + free = false; + }; + + amazonsl = { + fullName = "Amazon Software License"; + url = "https://aws.amazon.com/asl/"; + free = false; + }; + + amd = { + fullName = "AMD License Agreement"; + url = "https://developer.amd.com/amd-license-agreement/"; + free = false; + }; + + aom = { + fullName = "Alliance for Open Media Patent License 1.0"; + url = "https://aomedia.org/license/patent-license/"; + }; + + apple-psl10 = { + spdxId = "APSL-1.0"; + fullName = "Apple Public Source License 1.0"; + }; + + apple-psl20 = { + spdxId = "APSL-2.0"; + fullName = "Apple Public Source License 2.0"; + }; + + arphicpl = { + spdxId = "Arphic-1999"; + fullName = "Arphic Public License"; + url = "https://www.freedesktop.org/wiki/Arphic_Public_License/"; + }; + + artistic1 = { + spdxId = "Artistic-1.0"; + fullName = "Artistic License 1.0"; + }; + + artistic1-cl8 = { + spdxId = "Artistic-1.0-cl8"; + fullName = "Artistic License 1.0 w/clause 8"; + }; + + artistic2 = { + spdxId = "Artistic-2.0"; + fullName = "Artistic License 2.0"; + }; + + asl20 = { + spdxId = "Apache-2.0"; + fullName = "Apache License 2.0"; + }; + + asl20-llvm = { + spdxId = "Apache-2.0 WITH LLVM-exception"; + fullName = "Apache License 2.0 with LLVM Exceptions"; + }; + + bitstreamVera = { + spdxId = "Bitstream-Vera"; + fullName = "Bitstream Vera Font License"; + }; + + bitTorrent10 = { + spdxId = "BitTorrent-1.0"; + fullName = " BitTorrent Open Source License v1.0"; + }; + + bitTorrent11 = { + spdxId = "BitTorrent-1.1"; + fullName = " BitTorrent Open Source License v1.1"; + }; + + bola11 = { + url = "https://blitiri.com.ar/p/bola/"; + fullName = "Buena Onda License Agreement 1.1"; + }; + + boost = { + spdxId = "BSL-1.0"; + fullName = "Boost Software License 1.0"; + }; + + beerware = { + spdxId = "Beerware"; + fullName = "Beerware License"; + }; + + blueOak100 = { + spdxId = "BlueOak-1.0.0"; + fullName = "Blue Oak Model License 1.0.0"; + }; + + bsd0 = { + spdxId = "0BSD"; + fullName = "BSD Zero Clause License"; + }; + + bsd1 = { + spdxId = "BSD-1-Clause"; + fullName = "BSD 1-Clause License"; + }; + + bsd2 = { + spdxId = "BSD-2-Clause"; + fullName = ''BSD 2-clause "Simplified" License''; + }; + + bsd2Patent = { + spdxId = "BSD-2-Clause-Patent"; + fullName = "BSD-2-Clause Plus Patent License"; + }; + + bsd2WithViews = { + spdxId = "BSD-2-Clause-Views"; + fullName = "BSD 2-Clause with views sentence"; + }; + + bsd3 = { + spdxId = "BSD-3-Clause"; + fullName = ''BSD 3-clause "New" or "Revised" License''; + }; + + bsd3Clear = { + spdxId = "BSD-3-Clause-Clear"; + fullName = "BSD 3-Clause Clear License"; + }; + + bsdOriginal = { + spdxId = "BSD-4-Clause"; + fullName = ''BSD 4-clause "Original" or "Old" License''; + }; + + bsdOriginalShortened = { + spdxId = "BSD-4-Clause-Shortened"; + fullName = "BSD 4 Clause Shortened"; + }; + + bsdOriginalUC = { + spdxId = "BSD-4-Clause-UC"; + fullName = "BSD 4-Clause University of California-Specific"; + }; + + bsdProtection = { + spdxId = "BSD-Protection"; + fullName = "BSD Protection License"; + }; + + bsl11 = { + fullName = "Business Source License 1.1"; + url = "https://mariadb.com/bsl11"; + free = false; + redistributable = true; + }; + + caossl = { + fullName = "Computer Associates Open Source Licence Version 1.0"; + url = "http://jxplorer.org/licence.html"; + }; + + cal10 = { + spdxId = "CAL-1.0"; + fullName = "Cryptographic Autonomy License version 1.0 (CAL-1.0)"; + url = "https://opensource.org/licenses/CAL-1.0"; + }; + + caldera = { + spdxId = "Caldera"; + fullName = "Caldera License"; + url = "http://www.lemis.com/grog/UNIX/ancient-source-all.pdf"; + }; + + capec = { + fullName = "Common Attack Pattern Enumeration and Classification"; + url = "https://capec.mitre.org/about/termsofuse.html"; + }; + + clArtistic = { + spdxId = "ClArtistic"; + fullName = "Clarified Artistic License"; + }; + + cc0 = { + spdxId = "CC0-1.0"; + fullName = "Creative Commons Zero v1.0 Universal"; + }; + + cc-by-nc-nd-30 = { + spdxId = "CC-BY-NC-ND-3.0"; + fullName = "Creative Commons Attribution Non Commercial No Derivative Works 3.0 Unported"; + free = false; + }; + + cc-by-nc-nd-40 = { + spdxId = "CC-BY-NC-ND-4.0"; + fullName = "Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International"; + free = false; + }; + + cc-by-nc-sa-20 = { + spdxId = "CC-BY-NC-SA-2.0"; + fullName = "Creative Commons Attribution Non Commercial Share Alike 2.0"; + free = false; + }; + + cc-by-nc-sa-25 = { + spdxId = "CC-BY-NC-SA-2.5"; + fullName = "Creative Commons Attribution Non Commercial Share Alike 2.5"; + free = false; + }; + + cc-by-nc-sa-30 = { + spdxId = "CC-BY-NC-SA-3.0"; + fullName = "Creative Commons Attribution Non Commercial Share Alike 3.0"; + free = false; + }; + + cc-by-nc-sa-40 = { + spdxId = "CC-BY-NC-SA-4.0"; + fullName = "Creative Commons Attribution Non Commercial Share Alike 4.0"; + free = false; + }; + + cc-by-nc-30 = { + spdxId = "CC-BY-NC-3.0"; + fullName = "Creative Commons Attribution Non Commercial 3.0 Unported"; + free = false; + }; + + cc-by-nc-40 = { + spdxId = "CC-BY-NC-4.0"; + fullName = "Creative Commons Attribution Non Commercial 4.0 International"; + free = false; + }; + + cc-by-nd-30 = { + spdxId = "CC-BY-ND-3.0"; + fullName = "Creative Commons Attribution-No Derivative Works v3.00"; + free = false; + }; + + cc-by-sa-10 = { + spdxId = "CC-BY-SA-1.0"; + fullName = "Creative Commons Attribution Share Alike 1.0"; + }; + + cc-by-sa-20 = { + spdxId = "CC-BY-SA-2.0"; + fullName = "Creative Commons Attribution Share Alike 2.0"; + }; + + cc-by-sa-25 = { + spdxId = "CC-BY-SA-2.5"; + fullName = "Creative Commons Attribution Share Alike 2.5"; + }; + + cc-by-10 = { + spdxId = "CC-BY-1.0"; + fullName = "Creative Commons Attribution 1.0"; + }; + + cc-by-20 = { + spdxId = "CC-BY-2.0"; + fullName = "Creative Commons Attribution 2.0"; + }; + + cc-by-30 = { + spdxId = "CC-BY-3.0"; + fullName = "Creative Commons Attribution 3.0"; + }; + + cc-by-sa-30 = { + spdxId = "CC-BY-SA-3.0"; + fullName = "Creative Commons Attribution Share Alike 3.0"; + }; + + cc-by-40 = { + spdxId = "CC-BY-4.0"; + fullName = "Creative Commons Attribution 4.0"; + }; + + cc-by-sa-40 = { + spdxId = "CC-BY-SA-4.0"; + fullName = "Creative Commons Attribution Share Alike 4.0"; + }; + + cddl = { + spdxId = "CDDL-1.0"; + fullName = "Common Development and Distribution License 1.0"; + }; + + cecill20 = { + spdxId = "CECILL-2.0"; + fullName = "CeCILL Free Software License Agreement v2.0"; + }; + + cecill21 = { + spdxId = "CECILL-2.1"; + fullName = "CeCILL Free Software License Agreement v2.1"; + }; + + cecill-b = { + spdxId = "CECILL-B"; + fullName = "CeCILL-B Free Software License Agreement"; + }; + + cecill-c = { + spdxId = "CECILL-C"; + fullName = "CeCILL-C Free Software License Agreement"; + }; + + cpal10 = { + spdxId = "CPAL-1.0"; + fullName = "Common Public Attribution License 1.0"; + }; + + commons-clause = { + fullName = "Commons Clause License"; + url = "https://commonsclause.com/"; + free = false; + }; + + cpl10 = { + spdxId = "CPL-1.0"; + fullName = "Common Public License 1.0"; + }; + + curl = { + spdxId = "curl"; + fullName = "curl License"; + }; + + doc = { + spdxId = "DOC"; + fullName = "DOC License"; + }; + + drl10 = { + spdxId = "DRL-1.0"; + fullName = "Detection Rule License 1.0"; + }; + + dtoa = { + spdxId = "dtoa"; + fullName = "dtoa License"; + }; + + eapl = { + fullName = "EPSON AVASYS PUBLIC LICENSE"; + url = "https://avasys.jp/hp/menu000000700/hpg000000603.htm"; + free = false; + }; + + ecl20 = { + fullName = "Educational Community License, Version 2.0"; + url = "https://opensource.org/licenses/ECL-2.0"; + shortName = "ECL 2.0"; + spdxId = "ECL-2.0"; + }; + + efl10 = { + spdxId = "EFL-1.0"; + fullName = "Eiffel Forum License v1.0"; + }; + + efl20 = { + spdxId = "EFL-2.0"; + fullName = "Eiffel Forum License v2.0"; + }; + + elastic20 = { + spdxId = "Elastic-2.0"; + fullName = "Elastic License 2.0"; + url = "https://github.com/elastic/elasticsearch/blob/main/licenses/ELASTIC-LICENSE-2.0.txt"; + free = false; + }; + + epl10 = { + spdxId = "EPL-1.0"; + fullName = "Eclipse Public License 1.0"; + }; + + epl20 = { + spdxId = "EPL-2.0"; + fullName = "Eclipse Public License 2.0"; + }; + + epson = { + fullName = "Seiko Epson Corporation Software License Agreement for Linux"; + url = "https://download.ebz.epson.net/dsc/du/02/eula/global/LINUX_EN.html"; + free = false; + }; + + eupl11 = { + spdxId = "EUPL-1.1"; + fullName = "European Union Public License 1.1"; + }; + + eupl12 = { + spdxId = "EUPL-1.2"; + fullName = "European Union Public License 1.2"; + }; + + fdl11Only = { + spdxId = "GFDL-1.1-only"; + fullName = "GNU Free Documentation License v1.1 only"; + }; + + fdl11Plus = { + spdxId = "GFDL-1.1-or-later"; + fullName = "GNU Free Documentation License v1.1 or later"; + }; + + fdl12Only = { + spdxId = "GFDL-1.2-only"; + fullName = "GNU Free Documentation License v1.2 only"; + }; + + fdl12Plus = { + spdxId = "GFDL-1.2-or-later"; + fullName = "GNU Free Documentation License v1.2 or later"; + }; + + fdl13Only = { + spdxId = "GFDL-1.3-only"; + fullName = "GNU Free Documentation License v1.3 only"; + }; + + fdl13Plus = { + spdxId = "GFDL-1.3-or-later"; + fullName = "GNU Free Documentation License v1.3 or later"; + }; + + ffsl = { + fullName = "Floodgap Free Software License"; + url = "https://www.floodgap.com/software/ffsl/license.html"; + free = false; + }; + + fraunhofer-fdk = { + fullName = "Fraunhofer FDK AAC Codec Library"; + spdxId = "FDK-AAC"; + }; + + free = { + fullName = "Unspecified free software license"; + }; + + ftl = { + spdxId = "FTL"; + fullName = "Freetype Project License"; + }; + + g4sl = { + fullName = "Geant4 Software License"; + url = "https://geant4.web.cern.ch/geant4/license/LICENSE.html"; + }; + + geogebra = { + fullName = "GeoGebra Non-Commercial License Agreement"; + url = "https://www.geogebra.org/license"; + free = false; + }; + + generaluser = { + fullName = "GeneralUser GS License v2.0"; + url = "https://www.schristiancollins.com/generaluser.php"; # license included in sources + }; + + gfl = { + fullName = "GUST Font License"; + url = "https://www.gust.org.pl/projects/e-foundry/licenses/GUST-FONT-LICENSE.txt"; + }; + + gfsl = { + fullName = "GUST Font Source License"; + url = "https://www.gust.org.pl/projects/e-foundry/licenses/GUST-FONT-SOURCE-LICENSE.txt"; + }; + + gpl1Only = { + spdxId = "GPL-1.0-only"; + fullName = "GNU General Public License v1.0 only"; + }; + + gpl1Plus = { + spdxId = "GPL-1.0-or-later"; + fullName = "GNU General Public License v1.0 or later"; + }; + + gpl2Only = { + spdxId = "GPL-2.0-only"; + fullName = "GNU General Public License v2.0 only"; + }; + + gpl2Classpath = { + spdxId = "GPL-2.0-with-classpath-exception"; + fullName = "GNU General Public License v2.0 only (with Classpath exception)"; + }; + + gpl2ClasspathPlus = { + fullName = "GNU General Public License v2.0 or later (with Classpath exception)"; + url = "https://fedoraproject.org/wiki/Licensing/GPL_Classpath_Exception"; + }; + + gpl2Oss = { + fullName = "GNU General Public License version 2 only (with OSI approved licenses linking exception)"; + url = "https://www.mysql.com/about/legal/licensing/foss-exception"; + }; + + gpl2Plus = { + spdxId = "GPL-2.0-or-later"; + fullName = "GNU General Public License v2.0 or later"; + }; + + gpl3Only = { + spdxId = "GPL-3.0-only"; + fullName = "GNU General Public License v3.0 only"; + }; + + gpl3Plus = { + spdxId = "GPL-3.0-or-later"; + fullName = "GNU General Public License v3.0 or later"; + }; + + gpl3ClasspathPlus = { + fullName = "GNU General Public License v3.0 or later (with Classpath exception)"; + url = "https://fedoraproject.org/wiki/Licensing/GPL_Classpath_Exception"; + }; + + giftware = { + spdxId = "Giftware"; + fullName = "Giftware License"; + }; + + hpnd = { + spdxId = "HPND"; + fullName = "Historic Permission Notice and Disclaimer"; + }; + + hpndSellVariant = { + fullName = "Historical Permission Notice and Disclaimer - sell variant"; + spdxId = "HPND-sell-variant"; + }; + + hpndUc = { + spdxId = "HPND-UC"; + fullName = "Historical Permission Notice and Disclaimer - University of California variant"; + }; + + # Intel's license, seems free + iasl = { + spdxId = "Intel-ACPI"; + fullName = "iASL"; + url = "https://old.calculate-linux.org/packages/licenses/iASL"; + }; + + ijg = { + spdxId = "IJG"; + fullName = "Independent JPEG Group License"; + }; + + imagemagick = { + fullName = "ImageMagick License"; + spdxId = "ImageMagick"; + }; + + imlib2 = { + spdxId = "Imlib2"; + fullName = "Imlib2 License"; + }; + + info-zip = { + spdxId = "Info-ZIP"; + fullName = "Info-ZIP License"; + url = "https://infozip.sourceforge.net/license.html"; + }; + + inria-compcert = { + fullName = "INRIA Non-Commercial License Agreement for the CompCert verified compiler"; + url = "https://compcert.org/doc/LICENSE.txt"; + free = false; + }; + + inria-icesl = { + fullName = "End User License Agreement for IceSL Software"; + url = "https://icesl.loria.fr/assets/pdf/EULA_IceSL_binary.pdf"; + free = false; + }; + + inria-zelus = { + fullName = "INRIA Non-Commercial License Agreement for the Zélus compiler"; + url = "https://github.com/INRIA/zelus/raw/829f2b97cba93b0543a9ca0272269e6b8fdad356/LICENSE"; + free = false; + }; + + ipa = { + spdxId = "IPA"; + fullName = "IPA Font License"; + }; + + ipl10 = { + spdxId = "IPL-1.0"; + fullName = "IBM Public License v1.0"; + }; + + isc = { + spdxId = "ISC"; + fullName = "ISC License"; + }; + + # Proprietary binaries; free to redistribute without modification. + databricks = { + fullName = "Databricks Proprietary License"; + url = "https://pypi.org/project/databricks-connect"; + free = false; + }; + + databricks-dbx = { + fullName = "DataBricks eXtensions aka dbx License"; + url = "https://github.com/databrickslabs/dbx/blob/743b579a4ac44531f764c6e522dbe5a81a7dc0e4/LICENSE"; + free = false; + redistributable = false; + }; + + fair = { + fullName = "Fair License"; + spdxId = "Fair"; + free = true; + }; + + fairsource09 = { + fullName = "Fair Source License, version 0.9"; + url = "https://fair.io/v0.9.txt"; + free = false; + redistributable = true; + }; + + hl3 = { + fullName = "Hippocratic License v3.0"; + url = "https://firstdonoharm.dev/version/3/0/core.txt"; + free = false; + redistributable = true; + }; + + issl = { + fullName = "Intel Simplified Software License"; + url = "https://software.intel.com/en-us/license/intel-simplified-software-license"; + free = false; + }; + + knuth = { + fullName = "Knuth CTAN License"; + spdxId = "Knuth-CTAN"; + }; + + lal12 = { + spdxId = "LAL-1.2"; + fullName = "Licence Art Libre 1.2"; + }; + + lal13 = { + spdxId = "LAL-1.3"; + fullName = "Licence Art Libre 1.3"; + }; + + lens = { + fullName = "Lens Terms of Service Agreement"; + url = "https://k8slens.dev/licenses/tos"; + free = false; + }; + + lgpl2Only = { + spdxId = "LGPL-2.0-only"; + fullName = "GNU Library General Public License v2 only"; + }; + + lgpl2Plus = { + spdxId = "LGPL-2.0-or-later"; + fullName = "GNU Library General Public License v2 or later"; + }; + + lgpl21Only = { + spdxId = "LGPL-2.1-only"; + fullName = "GNU Lesser General Public License v2.1 only"; + }; + + lgpl21Plus = { + spdxId = "LGPL-2.1-or-later"; + fullName = "GNU Lesser General Public License v2.1 or later"; + }; + + lgpl3Only = { + spdxId = "LGPL-3.0-only"; + fullName = "GNU Lesser General Public License v3.0 only"; + }; + + lgpl3Plus = { + spdxId = "LGPL-3.0-or-later"; + fullName = "GNU Lesser General Public License v3.0 or later"; + }; + + lgpllr = { + spdxId = "LGPLLR"; + fullName = "Lesser General Public License For Linguistic Resources"; + }; + + libpng = { + spdxId = "Libpng"; + fullName = "libpng License"; + }; + + libpng2 = { + spdxId = "libpng-2.0"; # Used since libpng 1.6.36. + fullName = "PNG Reference Library version 2"; + }; + + libssh2 = { + fullName = "libssh2 License"; + url = "https://www.libssh2.org/license.html"; + }; + + libtiff = { + spdxId = "libtiff"; + fullName = "libtiff License"; + }; + + llgpl21 = { + fullName = "Lisp LGPL; GNU Lesser General Public License version 2.1 with Franz Inc. preamble for clarification of LGPL terms in context of Lisp"; + url = "https://opensource.franz.com/preamble.html"; + }; + + lppl1 = { + spdxId = "LPPL-1.0"; + fullName = "LaTeX Project Public License v1.0"; + }; + + lppl12 = { + spdxId = "LPPL-1.2"; + fullName = "LaTeX Project Public License v1.2"; + }; + + lppl13a = { + spdxId = "LPPL-1.3a"; + fullName = "LaTeX Project Public License v1.3a"; + }; + + lppl13c = { + spdxId = "LPPL-1.3c"; + fullName = "LaTeX Project Public License v1.3c"; + }; + + lpl-102 = { + spdxId = "LPL-1.02"; + fullName = "Lucent Public License v1.02"; + }; + + miros = { + spdxId = "MirOS"; + fullName = "MirOS License"; + url = "https://opensource.org/licenses/MirOS"; + }; + + # spdx.org does not (yet) differentiate between the X11 and Expat versions + # for details see https://en.wikipedia.org/wiki/MIT_License#Various_versions + mit = { + spdxId = "MIT"; + fullName = "MIT License"; + }; + # https://spdx.org/licenses/MIT-feh.html + mit-feh = { + spdxId = "MIT-feh"; + fullName = "feh License"; + }; + + mitAdvertising = { + spdxId = "MIT-advertising"; + fullName = "Enlightenment License (e16)"; + }; + + mit0 = { + spdxId = "MIT-0"; + fullName = "MIT No Attribution"; + }; + + mpl10 = { + spdxId = "MPL-1.0"; + fullName = "Mozilla Public License 1.0"; + }; + + mpl11 = { + spdxId = "MPL-1.1"; + fullName = "Mozilla Public License 1.1"; + }; + + mpl20 = { + spdxId = "MPL-2.0"; + fullName = "Mozilla Public License 2.0"; + }; + + mplus = { + spdxId = "mplus"; + fullName = "M+ Font License"; + }; + + mspl = { + spdxId = "MS-PL"; + fullName = "Microsoft Public License"; + }; + + mulan-psl2 = { + spdxId = "MulanPSL-2.0"; + fullName = "Mulan Permissive Software License, Version 2"; + url = "https://license.coscl.org.cn/MulanPSL2"; + }; + + nasa13 = { + spdxId = "NASA-1.3"; + fullName = "NASA Open Source Agreement 1.3"; + free = false; + }; + + ncsa = { + spdxId = "NCSA"; + fullName = "University of Illinois/NCSA Open Source License"; + }; + + ncul1 = { + spdxId = "NCUL1"; + fullName = "Netdata Cloud UI License v1.0"; + free = false; + redistributable = true; # Only if used in Netdata products. + url = "https://raw.githubusercontent.com/netdata/netdata/master/web/gui/v2/LICENSE.md"; + }; + + nistSoftware = { + spdxId = "NIST-Software"; + fullName = "NIST Software License"; + }; + + nlpl = { + spdxId = "NLPL"; + fullName = "No Limit Public License"; + }; + + nposl3 = { + spdxId = "NPOSL-3.0"; + fullName = "Non-Profit Open Software License 3.0"; + }; + + nvidiaCuda = { + shortName = "CUDA EULA"; + fullName = "CUDA Toolkit End User License Agreement (EULA)"; + url = "https://docs.nvidia.com/cuda/eula/index.html#cuda-toolkit-supplement-license-agreement"; + free = false; + }; + + nvidiaCudaRedist = { + shortName = "CUDA EULA"; + fullName = "CUDA Toolkit End User License Agreement (EULA)"; + url = "https://docs.nvidia.com/cuda/eula/index.html#cuda-toolkit-supplement-license-agreement"; + free = false; + redistributable = true; + }; + + obsidian = { + fullName = "Obsidian End User Agreement"; + url = "https://obsidian.md/eula"; + free = false; + }; + + ocamlLgplLinkingException = { + spdxId = "OCaml-LGPL-linking-exception"; + fullName = "OCaml LGPL Linking Exception"; + }; + + ocamlpro_nc = { + fullName = "OCamlPro Non Commercial license version 1"; + url = "https://alt-ergo.ocamlpro.com/http/alt-ergo-2.2.0/OCamlPro-Non-Commercial-License.pdf"; + free = false; + }; + + odbl = { + spdxId = "ODbL-1.0"; + fullName = "Open Data Commons Open Database License v1.0"; + }; + + ofl = { + spdxId = "OFL-1.1"; + fullName = "SIL Open Font License 1.1"; + }; + + oml = { + spdxId = "OML"; + fullName = "Open Market License"; + }; + + openldap = { + spdxId = "OLDAP-2.8"; + fullName = "Open LDAP Public License v2.8"; + }; + + openssl = { + spdxId = "OpenSSL"; + fullName = "OpenSSL License"; + }; + + opubl = { + spdxId = "OPUBL-1.0"; + fullName = "Open Publication License v1.0"; + }; + + osl2 = { + spdxId = "OSL-2.0"; + fullName = "Open Software License 2.0"; + }; + + osl21 = { + spdxId = "OSL-2.1"; + fullName = "Open Software License 2.1"; + }; + + osl3 = { + spdxId = "OSL-3.0"; + fullName = "Open Software License 3.0"; + }; + + parity70 = { + spdxId = "Parity-7.0.0"; + fullName = "Parity Public License 7.0.0"; + url = "https://paritylicense.com/versions/7.0.0.html"; + }; + + php301 = { + spdxId = "PHP-3.01"; + fullName = "PHP License v3.01"; + }; + + postgresql = { + spdxId = "PostgreSQL"; + fullName = "PostgreSQL License"; + }; + + postman = { + fullName = "Postman EULA"; + url = "https://www.getpostman.com/licenses/postman_base_app"; + free = false; + }; + + psfl = { + spdxId = "Python-2.0"; + fullName = "Python Software Foundation License version 2"; + url = "https://docs.python.org/license.html"; + }; + + publicDomain = { + fullName = "Public Domain"; + }; + + purdueBsd = { + fullName = " Purdue BSD-Style License"; # also know as lsof license + url = "https://enterprise.dejacode.com/licenses/public/purdue-bsd"; + }; + + prosperity30 = { + fullName = "Prosperity-3.0.0"; + free = false; + url = "https://prosperitylicense.com/versions/3.0.0.html"; + }; + + qhull = { + spdxId = "Qhull"; + fullName = "Qhull License"; + }; + + qpl = { + spdxId = "QPL-1.0"; + fullName = "Q Public License 1.0"; + }; + + qwt = { + fullName = "Qwt License, Version 1.0"; + url = "https://qwt.sourceforge.io/qwtlicense.html"; + }; + + ruby = { + spdxId = "Ruby"; + fullName = "Ruby License"; + }; + + sendmail = { + spdxId = "Sendmail"; + fullName = "Sendmail License"; + }; + + sgi-b-20 = { + spdxId = "SGI-B-2.0"; + fullName = "SGI Free Software License B v2.0"; + }; + + # Gentoo seems to treat it as a license: + # https://gitweb.gentoo.org/repo/gentoo.git/tree/licenses/SGMLUG?id=7d999af4a47bf55e53e54713d98d145f935935c1 + sgmlug = { + fullName = "SGML UG SGML Parser Materials license"; + }; + + sleepycat = { + spdxId = "Sleepycat"; + fullName = "Sleepycat License"; + }; + + smail = { + shortName = "smail"; + fullName = "SMAIL General Public License"; + url = "https://sources.debian.org/copyright/license/debianutils/4.9.1/"; + }; + + smlnj = { + spdxId = "SMLNJ"; + fullName = "Standard ML of New Jersey License"; + }; + + sspl = { + shortName = "SSPL"; + fullName = "Server Side Public License"; + url = "https://www.mongodb.com/licensing/server-side-public-license"; + free = false; + # NOTE Debatable. + # The license a slightly modified AGPL but still considered unfree by the + # OSI for what seem like political reasons + redistributable = true; # Definitely redistributable though, it's an AGPL derivative + }; + + stk = { + shortName = "stk"; + fullName = "Synthesis Tool Kit 4.3"; + url = "https://github.com/thestk/stk/blob/master/LICENSE"; + }; + + sudo = { + shortName = "sudo"; + fullName = "Sudo License (ISC-style)"; + url = "https://www.sudo.ws/about/license/"; + }; + + sustainableUse = { + shortName = "sustainable"; + fullName = "Sustainable Use License"; + url = "https://github.com/n8n-io/n8n/blob/master/LICENSE.md"; + free = false; + redistributable = false; # only free to redistribute "for non-commercial purposes" + }; + + tsl = { + shortName = "TSL"; + fullName = "Timescale License Agreegment"; + url = "https://github.com/timescale/timescaledb/blob/main/tsl/LICENSE-TIMESCALE"; + unfree = true; + }; + + tcltk = { + spdxId = "TCL"; + fullName = "TCL/TK License"; + }; + + ucd = { + fullName = "Unicode Character Database License"; + url = "https://fedoraproject.org/wiki/Licensing:UCD"; + }; + + ufl = { + fullName = "Ubuntu Font License 1.0"; + url = "https://ubuntu.com/legal/font-licence"; + }; + + unfree = { + fullName = "Unfree"; + free = false; + }; + + unfreeRedistributable = { + fullName = "Unfree redistributable"; + free = false; + redistributable = true; + }; + + unfreeRedistributableFirmware = { + fullName = "Unfree redistributable firmware"; + redistributable = true; + # Note: we currently consider these "free" for inclusion in the + # channel and NixOS images. + }; + + unicode-dfs-2015 = { + spdxId = "Unicode-DFS-2015"; + fullName = "Unicode License Agreement - Data Files and Software (2015)"; + }; + + unicode-dfs-2016 = { + spdxId = "Unicode-DFS-2016"; + fullName = "Unicode License Agreement - Data Files and Software (2016)"; + }; + + unlicense = { + spdxId = "Unlicense"; + fullName = "The Unlicense"; + }; + + upl = { + spdxId = "UPL-1.0"; + fullName = "Universal Permissive License"; + url = "https://oss.oracle.com/licenses/upl/"; + }; + + vim = { + spdxId = "Vim"; + fullName = "Vim License"; + }; + + virtualbox-puel = { + fullName = "Oracle VM VirtualBox Extension Pack Personal Use and Evaluation License (PUEL)"; + url = "https://www.virtualbox.org/wiki/VirtualBox_PUEL"; + free = false; + }; + + vol-sl = { + fullName = "Volatility Software License, Version 1.0"; + url = "https://www.volatilityfoundation.org/license/vsl-v1.0"; + }; + + vsl10 = { + spdxId = "VSL-1.0"; + fullName = "Vovida Software License v1.0"; + }; + + watcom = { + spdxId = "Watcom-1.0"; + fullName = "Sybase Open Watcom Public License 1.0"; + }; + + w3c = { + spdxId = "W3C"; + fullName = "W3C Software Notice and License"; + }; + + wadalab = { + fullName = "Wadalab Font License"; + url = "https://fedoraproject.org/wiki/Licensing:Wadalab?rd=Licensing/Wadalab"; + }; + + wtfpl = { + spdxId = "WTFPL"; + fullName = "Do What The F*ck You Want To Public License"; + }; + + wxWindows = { + spdxId = "wxWindows"; + fullName = "wxWindows Library Licence, Version 3.1"; + }; + + x11 = { + spdxId = "X11"; + fullName = "X11 License"; + }; + + xfig = { + spdxId = "Xfig"; + fullName = "xfig"; + url = "https://mcj.sourceforge.net/authors.html#xfig"; + }; + + xinetd = { + spdxId = "xinetd"; + fullName = "xinetd License"; + }; + + zlib = { + spdxId = "Zlib"; + fullName = "zlib License"; + }; + + zpl20 = { + spdxId = "ZPL-2.0"; + fullName = "Zope Public License 2.0"; + }; + + zpl21 = { + spdxId = "ZPL-2.1"; + fullName = "Zope Public License 2.1"; + }; + + xskat = { + spdxId = "XSkat"; + fullName = "XSkat License"; + }; + } + // { + # TODO: remove legacy aliases + apsl10 = { + # deprecated for consistency with `apple-psl20`; use `apple-psl10` + spdxId = "APSL-1.0"; + fullName = "Apple Public Source License 1.0"; + deprecated = true; + }; + apsl20 = { + # deprecated due to confusion with Apache-2.0; use `apple-psl20` + spdxId = "APSL-2.0"; + fullName = "Apple Public Source License 2.0"; + deprecated = true; + }; + gpl2 = { + spdxId = "GPL-2.0"; + fullName = "GNU General Public License v2.0"; + deprecated = true; + }; + gpl3 = { + spdxId = "GPL-3.0"; + fullName = "GNU General Public License v3.0"; + deprecated = true; + }; + lgpl2 = { + spdxId = "LGPL-2.0"; + fullName = "GNU Library General Public License v2"; + deprecated = true; + }; + lgpl21 = { + spdxId = "LGPL-2.1"; + fullName = "GNU Lesser General Public License v2.1"; + deprecated = true; + }; + lgpl3 = { + spdxId = "LGPL-3.0"; + fullName = "GNU Lesser General Public License v3.0"; + deprecated = true; + }; + } + ) diff --git a/nix/stdlib/lists.nix b/nix/stdlib/lists.nix index 547206b..ce352c3 100644 --- a/nix/stdlib/lists.nix +++ b/nix/stdlib/lists.nix @@ -1,537 +1,584 @@ -/* * - General list operations. +/* + * + General list operations. */ { lib }: let inherit (lib.strings) toInt; - inherit (lib.trivial) compare min id warn pipe; + inherit (lib.trivial) + compare + min + id + warn + pipe + ; inherit (lib.attrsets) mapAttrs; -in rec { +in +rec { inherit (builtins) - head tail length isList elemAt concatLists filter elem genList map; - - /* * - Create a list consisting of a single element. `singleton x` is - sometimes more convenient with respect to indentation than `[x]` - when x spans multiple lines. - - # Inputs - - `x` - - : 1\. Function argument - - # Type - - ``` - singleton :: a -> [a] - ``` - - # Examples - :::{.example} - ## `lib.lists.singleton` usage example - - ```nix - singleton "foo" - => [ "foo" ] - ``` - - ::: + head + tail + length + isList + elemAt + concatLists + filter + elem + genList + map + ; + + /* + * + Create a list consisting of a single element. `singleton x` is + sometimes more convenient with respect to indentation than `[x]` + when x spans multiple lines. + + # Inputs + + `x` + + : 1\. Function argument + + # Type + + ``` + singleton :: a -> [a] + ``` + + # Examples + :::{.example} + ## `lib.lists.singleton` usage example + + ```nix + singleton "foo" + => [ "foo" ] + ``` + + ::: */ singleton = x: [ x ]; - /* * - Apply the function to each element in the list. - Same as `map`, but arguments flipped. + /* + * + Apply the function to each element in the list. + Same as `map`, but arguments flipped. - # Inputs + # Inputs - `xs` + `xs` - : 1\. Function argument + : 1\. Function argument - `f` + `f` - : 2\. Function argument + : 2\. Function argument - # Type + # Type - ``` - forEach :: [a] -> (a -> b) -> [b] - ``` + ``` + forEach :: [a] -> (a -> b) -> [b] + ``` - # Examples - :::{.example} - ## `lib.lists.forEach` usage example + # Examples + :::{.example} + ## `lib.lists.forEach` usage example - ```nix - forEach [ 1 2 ] (x: - toString x - ) - => [ "1" "2" ] - ``` + ```nix + forEach [ 1 2 ] (x: + toString x + ) + => [ "1" "2" ] + ``` - ::: + ::: */ forEach = xs: f: map f xs; - /* * - “right fold” a binary function `op` between successive elements of - `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))`. + /* + * + “right fold” a binary function `op` between successive elements of + `list` with `nul` as the starting value, i.e., + `foldr op nul [x_1 x_2 ... x_n] == op x_1 (op x_2 ... (op x_n nul))`. - # Inputs + # Inputs - `op` + `op` - : 1\. Function argument + : 1\. Function argument - `nul` + `nul` - : 2\. Function argument + : 2\. Function argument - `list` + `list` - : 3\. Function argument + : 3\. Function argument - # Type + # Type - ``` - foldr :: (a -> b -> b) -> b -> [a] -> b - ``` + ``` + foldr :: (a -> b -> b) -> b -> [a] -> b + ``` - # Examples - :::{.example} - ## `lib.lists.foldr` usage example + # Examples + :::{.example} + ## `lib.lists.foldr` usage example - ```nix - concat = foldr (a: b: a + b) "z" - concat [ "a" "b" "c" ] - => "abcz" - # different types - strange = foldr (int: str: toString (int + 1) + str) "a" - strange [ 1 2 3 4 ] - => "2345a" - ``` + ```nix + concat = foldr (a: b: a + b) "z" + concat [ "a" "b" "c" ] + => "abcz" + # different types + strange = foldr (int: str: toString (int + 1) + str) "a" + strange [ 1 2 3 4 ] + => "2345a" + ``` - ::: + ::: */ - foldr = op: nul: list: + foldr = + op: nul: list: let len = length list; fold' = n: if n == len then nul else op (elemAt list n) (fold' (n + 1)); - in fold' 0; + in + fold' 0; - /* * - `fold` is an alias of `foldr` for historic reasons + /* + * + `fold` is an alias of `foldr` for historic reasons */ # FIXME(Profpatsch): deprecate? fold = foldr; - /* * - “left fold”, like `foldr`, but from the left: + /* + * + “left fold”, like `foldr`, but from the left: - `foldl op nul [x_1 x_2 ... x_n] == op (... (op (op nul x_1) x_2) ... x_n)`. + `foldl op nul [x_1 x_2 ... x_n] == op (... (op (op nul x_1) x_2) ... x_n)`. - # Inputs + # Inputs - `op` + `op` - : 1\. Function argument + : 1\. Function argument - `nul` + `nul` - : 2\. Function argument + : 2\. Function argument - `list` + `list` - : 3\. Function argument + : 3\. Function argument - # Type + # Type - ``` - foldl :: (b -> a -> b) -> b -> [a] -> b - ``` + ``` + foldl :: (b -> a -> b) -> b -> [a] -> b + ``` - # Examples - :::{.example} - ## `lib.lists.foldl` usage example + # Examples + :::{.example} + ## `lib.lists.foldl` usage example - ```nix - lconcat = foldl (a: b: a + b) "z" - lconcat [ "a" "b" "c" ] - => "zabc" - # different types - lstrange = foldl (str: int: str + toString (int + 1)) "a" - lstrange [ 1 2 3 4 ] - => "a2345" - ``` + ```nix + lconcat = foldl (a: b: a + b) "z" + lconcat [ "a" "b" "c" ] + => "zabc" + # different types + lstrange = foldl (str: int: str + toString (int + 1)) "a" + lstrange [ 1 2 3 4 ] + => "a2345" + ``` - ::: + ::: */ - foldl = op: nul: list: + foldl = + op: nul: list: let foldl' = n: if n == -1 then nul else op (foldl' (n - 1)) (elemAt list n); - in foldl' (length list - 1); + in + foldl' (length list - 1); - /* * - Reduce a list by applying a binary operator from left to right, - starting with an initial accumulator. + /* + * + Reduce a list by applying a binary operator from left to right, + starting with an initial accumulator. - Before each application of the operator, the accumulator value is evaluated. - This behavior makes this function stricter than [`foldl`](#function-library-lib.lists.foldl). + Before each application of the operator, the accumulator value is evaluated. + This behavior makes this function stricter than [`foldl`](#function-library-lib.lists.foldl). - Unlike [`builtins.foldl'`](https://nixos.org/manual/nix/unstable/language/builtins.html#builtins-foldl'), - the initial accumulator argument is evaluated before the first iteration. + Unlike [`builtins.foldl'`](https://nixos.org/manual/nix/unstable/language/builtins.html#builtins-foldl'), + the initial accumulator argument is evaluated before the first iteration. - A call like + A call like - ```nix - foldl' op acc₀ [ x₀ x₁ x₂ ... xₙ₋₁ xₙ ] - ``` + ```nix + foldl' op acc₀ [ x₀ x₁ x₂ ... xₙ₋₁ xₙ ] + ``` - is (denotationally) equivalent to the following, - but with the added benefit that `foldl'` itself will never overflow the stack. + is (denotationally) equivalent to the following, + but with the added benefit that `foldl'` itself will never overflow the stack. - ```nix - let - acc₁ = builtins.seq acc₀ (op acc₀ x₀ ); - acc₂ = builtins.seq acc₁ (op acc₁ x₁ ); - acc₃ = builtins.seq acc₂ (op acc₂ x₂ ); - ... - accₙ = builtins.seq accₙ₋₁ (op accₙ₋₁ xₙ₋₁); - accₙ₊₁ = builtins.seq accₙ (op accₙ xₙ ); - in - accₙ₊₁ + ```nix + let + acc₁ = builtins.seq acc₀ (op acc₀ x₀ ); + acc₂ = builtins.seq acc₁ (op acc₁ x₁ ); + acc₃ = builtins.seq acc₂ (op acc₂ x₂ ); + ... + accₙ = builtins.seq accₙ₋₁ (op accₙ₋₁ xₙ₋₁); + accₙ₊₁ = builtins.seq accₙ (op accₙ xₙ ); + in + accₙ₊₁ - # Or ignoring builtins.seq - op (op (... (op (op (op acc₀ x₀) x₁) x₂) ...) xₙ₋₁) xₙ - ``` + # Or ignoring builtins.seq + op (op (... (op (op (op acc₀ x₀) x₁) x₂) ...) xₙ₋₁) xₙ + ``` - # Inputs + # Inputs - `op` + `op` - : The binary operation to run, where the two arguments are: + : The binary operation to run, where the two arguments are: - 1. `acc`: The current accumulator value: Either the initial one for the first iteration, or the result of the previous iteration - 2. `x`: The corresponding list element for this iteration + 1. `acc`: The current accumulator value: Either the initial one for the first iteration, or the result of the previous iteration + 2. `x`: The corresponding list element for this iteration - `acc` + `acc` - : The initial accumulator value. + : The initial accumulator value. - The accumulator value is evaluated in any case before the first iteration starts. + The accumulator value is evaluated in any case before the first iteration starts. - To avoid evaluation even before the `list` argument is given an eta expansion can be used: + To avoid evaluation even before the `list` argument is given an eta expansion can be used: - ```nix - list: lib.foldl' op acc list - ``` + ```nix + list: lib.foldl' op acc list + ``` - `list` + `list` - : The list to fold + : The list to fold - # Type + # Type - ``` - foldl' :: (acc -> x -> acc) -> acc -> [x] -> acc - ``` + ``` + foldl' :: (acc -> x -> acc) -> acc -> [x] -> acc + ``` - # Examples - :::{.example} - ## `lib.lists.foldl'` usage example + # Examples + :::{.example} + ## `lib.lists.foldl'` usage example - ```nix - foldl' (acc: x: acc + x) 0 [1 2 3] - => 6 - ``` + ```nix + foldl' (acc: x: acc + x) 0 [1 2 3] + => 6 + ``` - ::: + ::: */ - foldl' = op: acc: + foldl' = + op: acc: # The builtin `foldl'` is a bit lazier than one might expect. # See https://github.com/NixOS/nix/pull/7158. # In particular, the initial accumulator value is not forced before the first iteration starts. builtins.seq acc (builtins.foldl' op acc); - /* * - Map with index starting from 0 + /* + * + Map with index starting from 0 - # Inputs + # Inputs - `f` + `f` - : 1\. Function argument + : 1\. Function argument - `list` + `list` - : 2\. Function argument + : 2\. Function argument - # Type + # Type - ``` - imap0 :: (int -> a -> b) -> [a] -> [b] - ``` + ``` + imap0 :: (int -> a -> b) -> [a] -> [b] + ``` - # Examples - :::{.example} - ## `lib.lists.imap0` usage example + # Examples + :::{.example} + ## `lib.lists.imap0` usage example - ```nix - imap0 (i: v: "${v}-${toString i}") ["a" "b"] - => [ "a-0" "b-1" ] - ``` + ```nix + imap0 (i: v: "${v}-${toString i}") ["a" "b"] + => [ "a-0" "b-1" ] + ``` - ::: + ::: */ imap0 = f: list: genList (n: f n (elemAt list n)) (length list); - /* * - Map with index starting from 1 + /* + * + Map with index starting from 1 - # Inputs + # Inputs - `f` + `f` - : 1\. Function argument + : 1\. Function argument - `list` + `list` - : 2\. Function argument + : 2\. Function argument - # Type + # Type - ``` - imap1 :: (int -> a -> b) -> [a] -> [b] - ``` + ``` + imap1 :: (int -> a -> b) -> [a] -> [b] + ``` - # Examples - :::{.example} - ## `lib.lists.imap1` usage example + # Examples + :::{.example} + ## `lib.lists.imap1` usage example - ```nix - imap1 (i: v: "${v}-${toString i}") ["a" "b"] - => [ "a-1" "b-2" ] - ``` + ```nix + imap1 (i: v: "${v}-${toString i}") ["a" "b"] + => [ "a-1" "b-2" ] + ``` - ::: + ::: */ imap1 = f: list: genList (n: f (n + 1) (elemAt list n)) (length list); - /* * - Filter a list for elements that satisfy a predicate function. - The predicate function is called with both the index and value for each element. - It must return `true`/`false` to include/exclude a given element in the result. - This function is strict in the result of the predicate function for each element. - This function has O(n) complexity. + /* + * + Filter a list for elements that satisfy a predicate function. + The predicate function is called with both the index and value for each element. + It must return `true`/`false` to include/exclude a given element in the result. + This function is strict in the result of the predicate function for each element. + This function has O(n) complexity. - Also see [`builtins.filter`](https://nixos.org/manual/nix/stable/language/builtins.html#builtins-filter) (available as `lib.lists.filter`), - which can be used instead when the index isn't needed. + Also see [`builtins.filter`](https://nixos.org/manual/nix/stable/language/builtins.html#builtins-filter) (available as `lib.lists.filter`), + which can be used instead when the index isn't needed. - # Inputs + # Inputs - `ipred` + `ipred` - : The predicate function, it takes two arguments: - - 1. (int): the index of the element. - - 2. (a): the value of the element. + : The predicate function, it takes two arguments: + - 1. (int): the index of the element. + - 2. (a): the value of the element. - It must return `true`/`false` to include/exclude a given element from the result. + It must return `true`/`false` to include/exclude a given element from the result. - `list` + `list` - : The list to filter using the predicate. + : The list to filter using the predicate. - # Type - ``` - ifilter0 :: (int -> a -> bool) -> [a] -> [a] - ``` + # Type + ``` + ifilter0 :: (int -> a -> bool) -> [a] -> [a] + ``` - # Examples - :::{.example} - ## `lib.lists.ifilter0` usage example + # Examples + :::{.example} + ## `lib.lists.ifilter0` usage example - ```nix - ifilter0 (i: v: i == 0 || v > 2) [ 1 2 3 ] - => [ 1 3 ] - ``` - ::: + ```nix + ifilter0 (i: v: i == 0 || v > 2) [ 1 2 3 ] + => [ 1 3 ] + ``` + ::: */ - ifilter0 = ipred: input: - map (idx: elemAt input idx) (filter (idx: ipred idx (elemAt input idx)) - (genList (x: x) (length input))); + ifilter0 = + ipred: input: + map (idx: elemAt input idx) ( + filter (idx: ipred idx (elemAt input idx)) (genList (x: x) (length input)) + ); - /* * - Map and concatenate the result. + /* + * + Map and concatenate the result. - # Type + # Type - ``` - concatMap :: (a -> [b]) -> [a] -> [b] - ``` + ``` + concatMap :: (a -> [b]) -> [a] -> [b] + ``` - # Examples - :::{.example} - ## `lib.lists.concatMap` usage example + # Examples + :::{.example} + ## `lib.lists.concatMap` usage example - ```nix - concatMap (x: [x] ++ ["z"]) ["a" "b"] - => [ "a" "z" "b" "z" ] - ``` + ```nix + concatMap (x: [x] ++ ["z"]) ["a" "b"] + => [ "a" "z" "b" "z" ] + ``` - ::: + ::: */ concatMap = builtins.concatMap; - /* * - Flatten the argument into a single list; that is, nested lists are - spliced into the top-level lists. + /* + * + Flatten the argument into a single list; that is, nested lists are + spliced into the top-level lists. - # Inputs + # Inputs - `x` + `x` - : 1\. Function argument + : 1\. Function argument - # Examples - :::{.example} - ## `lib.lists.flatten` usage example + # Examples + :::{.example} + ## `lib.lists.flatten` usage example - ```nix - flatten [1 [2 [3] 4] 5] - => [1 2 3 4 5] - flatten 1 - => [1] - ``` + ```nix + flatten [1 [2 [3] 4] 5] + => [1 2 3 4 5] + flatten 1 + => [1] + ``` - ::: + ::: */ flatten = 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` - : Element to remove from `list` + : Element to remove from `list` - `list` + `list` - : The list + : The list - # Type + # Type - ``` - remove :: a -> [a] -> [a] - ``` + ``` + remove :: a -> [a] -> [a] + ``` - # Examples - :::{.example} - ## `lib.lists.remove` usage example + # Examples + :::{.example} + ## `lib.lists.remove` usage example - ```nix - remove 3 [ 1 3 4 3 ] - => [ 1 4 ] - ``` + ```nix + remove 3 [ 1 3 4 3 ] + => [ 1 4 ] + ``` - ::: + ::: */ remove = e: filter (x: x != e); - /* * - Find the sole element in the list matching the specified - predicate. + /* + * + Find the sole element in the list matching the specified + predicate. - Returns `default` if no such element exists, or - `multiple` if there are multiple matching elements. + Returns `default` if no such element exists, or + `multiple` if there are multiple matching elements. - # Inputs + # Inputs - `pred` + `pred` - : Predicate + : Predicate - `default` + `default` - : Default value to return if element was not found. + : Default value to return if element was not found. - `multiple` + `multiple` - : Default value to return if more than one element was found + : Default value to return if more than one element was found - `list` + `list` - : Input list + : Input list - # Type + # Type - ``` - findSingle :: (a -> bool) -> a -> a -> [a] -> a - ``` + ``` + findSingle :: (a -> bool) -> a -> a -> [a] -> a + ``` - # Examples - :::{.example} - ## `lib.lists.findSingle` usage example + # Examples + :::{.example} + ## `lib.lists.findSingle` usage example - ```nix - findSingle (x: x == 3) "none" "multiple" [ 1 3 3 ] - => "multiple" - findSingle (x: x == 3) "none" "multiple" [ 1 3 ] - => 3 - findSingle (x: x == 3) "none" "multiple" [ 1 9 ] - => "none" - ``` + ```nix + findSingle (x: x == 3) "none" "multiple" [ 1 3 3 ] + => "multiple" + findSingle (x: x == 3) "none" "multiple" [ 1 3 ] + => 3 + findSingle (x: x == 3) "none" "multiple" [ 1 9 ] + => "none" + ``` - ::: + ::: */ - findSingle = pred: default: multiple: list: + findSingle = + pred: default: multiple: list: let found = filter pred list; len = length found; - in if len == 0 then default else if len != 1 then multiple else head found; + in + if len == 0 then + default + else if len != 1 then + multiple + else + head found; - /* * - Find the first index in the list matching the specified - predicate or return `default` if no such element exists. + /* + * + Find the first index in the list matching the specified + predicate or return `default` if no such element exists. - # Inputs + # Inputs - `pred` + `pred` - : Predicate + : Predicate - `default` + `default` - : Default value to return + : Default value to return - `list` + `list` - : Input list + : Input list - # Type + # Type - ``` - findFirstIndex :: (a -> Bool) -> b -> [a] -> (Int | b) - ``` + ``` + findFirstIndex :: (a -> Bool) -> b -> [a] -> (Int | b) + ``` - # Examples - :::{.example} - ## `lib.lists.findFirstIndex` usage example + # Examples + :::{.example} + ## `lib.lists.findFirstIndex` usage example - ```nix - findFirstIndex (x: x > 3) null [ 0 6 4 ] - => 1 - findFirstIndex (x: x > 9) null [ 0 6 4 ] - => null - ``` + ```nix + findFirstIndex (x: x > 3) null [ 0 6 4 ] + => 1 + findFirstIndex (x: x > 9) null [ 0 6 4 ] + => null + ``` - ::: + ::: */ - findFirstIndex = pred: default: list: + findFirstIndex = + pred: default: list: let # A naive recursive implementation would be much simpler, but # would also overflow the evaluator stack. We use `foldl'` as a workaround @@ -545,1022 +592,1092 @@ in rec { # - if index >= 0 then pred (elemAt list index) and all elements before (elemAt list index) didn't satisfy pred # # We start with index -1 and the 0'th element of the list, which satisfies the invariant - resultIndex = foldl' (index: el: + resultIndex = foldl' ( + index: el: if index < 0 then - # No match yet before the current index, we need to check the element + # No match yet before the current index, we need to check the element if pred el then - # We have a match! Turn it into the actual index to prevent future iterations from modifying it + # We have a match! Turn it into the actual index to prevent future iterations from modifying it -index - 1 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 else - # There's already a match, propagate the index without evaluating anything - index) (-1) list; - in if resultIndex < 0 then default else resultIndex; + # There's already a match, propagate the index without evaluating anything + index + ) (-1) list; + in + if resultIndex < 0 then default else resultIndex; - /* * - Find the first element in the list matching the specified - predicate or return `default` if no such element exists. + /* + * + Find the first element in the list matching the specified + predicate or return `default` if no such element exists. - # Inputs + # Inputs - `pred` + `pred` - : Predicate + : Predicate - `default` + `default` - : Default value to return + : Default value to return - `list` + `list` - : Input list + : Input list - # Type + # Type - ``` - findFirst :: (a -> bool) -> a -> [a] -> a - ``` + ``` + findFirst :: (a -> bool) -> a -> [a] -> a + ``` - # Examples - :::{.example} - ## `lib.lists.findFirst` usage example + # Examples + :::{.example} + ## `lib.lists.findFirst` usage example - ```nix - findFirst (x: x > 3) 7 [ 1 6 4 ] - => 6 - findFirst (x: x > 9) 7 [ 1 6 4 ] - => 7 - ``` + ```nix + findFirst (x: x > 3) 7 [ 1 6 4 ] + => 6 + findFirst (x: x > 9) 7 [ 1 6 4 ] + => 7 + ``` - ::: + ::: */ - findFirst = pred: default: list: - let index = findFirstIndex pred null list; - in if index == null then default else elemAt list index; + findFirst = + pred: default: list: + let + index = findFirstIndex pred null list; + in + if index == null then default else elemAt list index; - /* * - Return true if function `pred` returns true for at least one - element of `list`. + /* + * + Return true if function `pred` returns true for at least one + element of `list`. - # Inputs + # Inputs - `pred` + `pred` - : Predicate + : Predicate - `list` + `list` - : Input list + : Input list - # Type + # Type - ``` - any :: (a -> bool) -> [a] -> bool - ``` + ``` + any :: (a -> bool) -> [a] -> bool + ``` - # Examples - :::{.example} - ## `lib.lists.any` usage example + # Examples + :::{.example} + ## `lib.lists.any` usage example - ```nix - any isString [ 1 "a" { } ] - => true - any isString [ 1 { } ] - => false - ``` + ```nix + any isString [ 1 "a" { } ] + => true + any isString [ 1 { } ] + => false + ``` - ::: + ::: */ any = builtins.any; - /* * - Return true if function `pred` returns true for all elements of - `list`. + /* + * + Return true if function `pred` returns true for all elements of + `list`. - # Inputs + # Inputs - `pred` + `pred` - : Predicate + : Predicate - `list` + `list` - : Input list + : Input list - # Type + # Type - ``` - all :: (a -> bool) -> [a] -> bool - ``` + ``` + all :: (a -> bool) -> [a] -> bool + ``` - # Examples - :::{.example} - ## `lib.lists.all` usage example + # Examples + :::{.example} + ## `lib.lists.all` usage example - ```nix - all (x: x < 3) [ 1 2 ] - => true - all (x: x < 3) [ 1 2 3 ] - => false - ``` + ```nix + all (x: x < 3) [ 1 2 ] + => true + all (x: x < 3) [ 1 2 3 ] + => false + ``` - ::: + ::: */ all = builtins.all; - /* * - Count how many elements of `list` match the supplied predicate - function. + /* + * + Count how many elements of `list` match the supplied predicate + function. - # Inputs + # Inputs - `pred` + `pred` - : Predicate + : Predicate - # Type + # Type - ``` - count :: (a -> bool) -> [a] -> int - ``` + ``` + count :: (a -> bool) -> [a] -> int + ``` - # Examples - :::{.example} - ## `lib.lists.count` usage example + # Examples + :::{.example} + ## `lib.lists.count` usage example - ```nix - count (x: x == 3) [ 3 2 3 4 6 ] - => 2 - ``` + ```nix + count (x: x == 3) [ 3 2 3 4 6 ] + => 2 + ``` - ::: + ::: */ count = pred: foldl' (c: x: if pred x then c + 1 else c) 0; - /* * - Return a singleton list or an empty list, depending on a boolean - value. Useful when building lists with optional elements - (e.g. `++ optional (system == "i686-linux") firefox`). + /* + * + Return a singleton list or an empty list, depending on a boolean + value. Useful when building lists with optional elements + (e.g. `++ optional (system == "i686-linux") firefox`). - # Inputs + # Inputs - `cond` + `cond` - : 1\. Function argument + : 1\. Function argument - `elem` + `elem` - : 2\. Function argument + : 2\. Function argument - # Type + # Type - ``` - optional :: bool -> a -> [a] - ``` + ``` + optional :: bool -> a -> [a] + ``` - # Examples - :::{.example} - ## `lib.lists.optional` usage example + # Examples + :::{.example} + ## `lib.lists.optional` usage example - ```nix - optional true "foo" - => [ "foo" ] - optional false "foo" - => [ ] - ``` + ```nix + optional true "foo" + => [ "foo" ] + optional false "foo" + => [ ] + ``` - ::: + ::: */ 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. - # Inputs + # Inputs - `cond` + `cond` - : Condition + : Condition - `elems` + `elems` - : List to return if condition is true + : List to return if condition is true - # Type + # Type - ``` - optionals :: bool -> [a] -> [a] - ``` + ``` + optionals :: bool -> [a] -> [a] + ``` - # Examples - :::{.example} - ## `lib.lists.optionals` usage example + # Examples + :::{.example} + ## `lib.lists.optionals` usage example - ```nix - optionals true [ 2 3 ] - => [ 2 3 ] - optionals false [ 2 3 ] - => [ ] - ``` + ```nix + optionals true [ 2 3 ] + => [ 2 3 ] + optionals false [ 2 3 ] + => [ ] + ``` - ::: + ::: */ optionals = cond: elems: if cond then elems else [ ]; - /* * - If argument is a list, return it; else, wrap it in a singleton - list. If you're using this, you should almost certainly - reconsider if there isn't a more "well-typed" approach. + /* + * + If argument is a list, return it; else, wrap it in a singleton + list. If you're using this, you should almost certainly + reconsider if there isn't a more "well-typed" approach. - # Inputs + # Inputs - `x` + `x` - : 1\. Function argument + : 1\. Function argument - # Examples - :::{.example} - ## `lib.lists.toList` usage example + # Examples + :::{.example} + ## `lib.lists.toList` usage example - ```nix - toList [ 1 2 ] - => [ 1 2 ] - toList "hi" - => [ "hi "] - ``` + ```nix + toList [ 1 2 ] + => [ 1 2 ] + toList "hi" + => [ "hi "] + ``` - ::: + ::: */ 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`. - # Inputs + # Inputs - `first` + `first` - : First integer in the range + : First integer in the range - `last` + `last` - : Last integer in the range + : Last integer in the range - # Type + # Type - ``` - range :: int -> int -> [int] - ``` + ``` + range :: int -> int -> [int] + ``` - # Examples - :::{.example} - ## `lib.lists.range` usage example + # Examples + :::{.example} + ## `lib.lists.range` usage example - ```nix - range 2 4 - => [ 2 3 4 ] - range 3 2 - => [ ] - ``` + ```nix + range 2 4 + => [ 2 3 4 ] + range 3 2 + => [ ] + ``` - ::: + ::: */ - range = first: last: - if first > last then [ ] else genList (n: first + n) (last - first + 1); + range = first: last: if first > last then [ ] else genList (n: first + n) (last - first + 1); - /* * - Return a list with `n` copies of an element. + /* + * + Return a list with `n` copies of an element. - # Inputs + # Inputs - `n` + `n` - : 1\. Function argument + : 1\. Function argument - `elem` + `elem` - : 2\. Function argument + : 2\. Function argument - # Type + # Type - ``` - replicate :: int -> a -> [a] - ``` + ``` + replicate :: int -> a -> [a] + ``` - # Examples - :::{.example} - ## `lib.lists.replicate` usage example + # Examples + :::{.example} + ## `lib.lists.replicate` usage example - ```nix - replicate 3 "a" - => [ "a" "a" "a" ] - replicate 2 true - => [ true true ] - ``` + ```nix + replicate 3 "a" + => [ "a" "a" "a" ] + replicate 2 true + => [ true true ] + ``` - ::: + ::: */ replicate = n: elem: genList (_: elem) n; - /* * - Splits the elements of a list in two lists, `right` and - `wrong`, depending on the evaluation of a predicate. + /* + * + Splits the elements of a list in two lists, `right` and + `wrong`, depending on the evaluation of a predicate. - # Inputs + # Inputs - `pred` + `pred` - : Predicate + : Predicate - `list` + `list` - : Input list + : Input list - # Type + # Type - ``` - (a -> bool) -> [a] -> { right :: [a]; wrong :: [a]; } - ``` + ``` + (a -> bool) -> [a] -> { right :: [a]; wrong :: [a]; } + ``` - # Examples - :::{.example} - ## `lib.lists.partition` usage example + # Examples + :::{.example} + ## `lib.lists.partition` usage example - ```nix - partition (x: x > 2) [ 5 1 2 3 4 ] - => { right = [ 5 3 4 ]; wrong = [ 1 2 ]; } - ``` + ```nix + partition (x: x > 2) [ 5 1 2 3 4 ] + => { right = [ 5 3 4 ]; wrong = [ 1 2 ]; } + ``` - ::: + ::: */ partition = builtins.partition; - /* * - Splits the elements of a list into many lists, using the return value of a predicate. - Predicate should return a string which becomes keys of attrset `groupBy` returns. - `groupBy'` allows to customise the combining function and initial value + /* + * + Splits the elements of a list into many lists, using the return value of a predicate. + Predicate should return a string which becomes keys of attrset `groupBy` returns. + `groupBy'` allows to customise the combining function and initial value - # Inputs + # Inputs - `op` + `op` - : 1\. Function argument + : 1\. Function argument - `nul` + `nul` - : 2\. Function argument + : 2\. Function argument - `pred` + `pred` - : 3\. Function argument + : 3\. Function argument - `lst` + `lst` - : 4\. Function argument + : 4\. Function argument - # Examples - :::{.example} - ## `lib.lists.groupBy'` usage example + # Examples + :::{.example} + ## `lib.lists.groupBy'` usage example - ```nix - groupBy (x: boolToString (x > 2)) [ 5 1 2 3 4 ] - => { true = [ 5 3 4 ]; false = [ 1 2 ]; } - groupBy (x: x.name) [ {name = "icewm"; script = "icewm &";} - {name = "xfce"; script = "xfce4-session &";} - {name = "icewm"; script = "icewmbg &";} - {name = "mate"; script = "gnome-session &";} - ] - => { icewm = [ { name = "icewm"; script = "icewm &"; } - { name = "icewm"; script = "icewmbg &"; } ]; - mate = [ { name = "mate"; script = "gnome-session &"; } ]; - xfce = [ { name = "xfce"; script = "xfce4-session &"; } ]; - } + ```nix + groupBy (x: boolToString (x > 2)) [ 5 1 2 3 4 ] + => { true = [ 5 3 4 ]; false = [ 1 2 ]; } + groupBy (x: x.name) [ {name = "icewm"; script = "icewm &";} + {name = "xfce"; script = "xfce4-session &";} + {name = "icewm"; script = "icewmbg &";} + {name = "mate"; script = "gnome-session &";} + ] + => { icewm = [ { name = "icewm"; script = "icewm &"; } + { name = "icewm"; script = "icewmbg &"; } ]; + mate = [ { name = "mate"; script = "gnome-session &"; } ]; + xfce = [ { name = "xfce"; script = "xfce4-session &"; } ]; + } - groupBy' builtins.add 0 (x: boolToString (x > 2)) [ 5 1 2 3 4 ] - => { true = 12; false = 3; } - ``` + groupBy' builtins.add 0 (x: boolToString (x > 2)) [ 5 1 2 3 4 ] + => { true = 12; false = 3; } + ``` - ::: + ::: */ - groupBy' = op: nul: pred: lst: + groupBy' = + op: nul: pred: lst: mapAttrs (name: foldl op nul) (groupBy pred lst); - groupBy = builtins.groupBy or (pred: - foldl' - (r: e: let key = pred e; in r // { ${key} = (r.${key} or [ ]) ++ [ e ]; }) - { }); + groupBy = + builtins.groupBy or ( + pred: + foldl' ( + r: e: + let + key = pred e; + in + r // { ${key} = (r.${key} or [ ]) ++ [ e ]; } + ) { } + ); - /* * - Merges two lists of the same size together. If the sizes aren't the same - the merging stops at the shortest. How both lists are merged is defined - by the first argument. + /* + * + Merges two lists of the same size together. If the sizes aren't the same + the merging stops at the shortest. How both lists are merged is defined + by the first argument. - # Inputs + # Inputs - `f` + `f` - : Function to zip elements of both lists + : Function to zip elements of both lists - `fst` + `fst` - : First list + : First list - `snd` + `snd` - : Second list + : Second list - # Type + # Type - ``` - zipListsWith :: (a -> b -> c) -> [a] -> [b] -> [c] - ``` + ``` + zipListsWith :: (a -> b -> c) -> [a] -> [b] -> [c] + ``` - # Examples - :::{.example} - ## `lib.lists.zipListsWith` usage example + # Examples + :::{.example} + ## `lib.lists.zipListsWith` usage example - ```nix - zipListsWith (a: b: a + b) ["h" "l"] ["e" "o"] - => ["he" "lo"] - ``` + ```nix + zipListsWith (a: b: a + b) ["h" "l"] ["e" "o"] + => ["he" "lo"] + ``` - ::: + ::: */ - zipListsWith = f: fst: snd: - genList (n: f (elemAt fst n) (elemAt snd n)) - (min (length fst) (length snd)); + zipListsWith = + f: fst: snd: + genList (n: f (elemAt fst n) (elemAt snd n)) (min (length fst) (length snd)); - /* * - Merges two lists of the same size together. If the sizes aren't the same - the merging stops at the shortest. + /* + * + Merges two lists of the same size together. If the sizes aren't the same + the merging stops at the shortest. - # Inputs + # Inputs - `fst` + `fst` - : First list + : First list - `snd` + `snd` - : Second list + : Second list - # Type + # Type - ``` - zipLists :: [a] -> [b] -> [{ fst :: a; snd :: b; }] - ``` + ``` + zipLists :: [a] -> [b] -> [{ fst :: a; snd :: b; }] + ``` - # Examples - :::{.example} - ## `lib.lists.zipLists` usage example + # Examples + :::{.example} + ## `lib.lists.zipLists` usage example - ```nix - zipLists [ 1 2 ] [ "a" "b" ] - => [ { fst = 1; snd = "a"; } { fst = 2; snd = "b"; } ] - ``` + ```nix + zipLists [ 1 2 ] [ "a" "b" ] + => [ { fst = 1; snd = "a"; } { fst = 2; snd = "b"; } ] + ``` - ::: + ::: */ zipLists = zipListsWith (fst: snd: { inherit fst snd; }); - /* * - Reverse the order of the elements of a list. + /* + * + Reverse the order of the elements of a list. - # Inputs + # Inputs - `xs` + `xs` - : 1\. Function argument + : 1\. Function argument - # Type + # Type - ``` - reverseList :: [a] -> [a] - ``` + ``` + reverseList :: [a] -> [a] + ``` - # Examples - :::{.example} - ## `lib.lists.reverseList` usage example + # Examples + :::{.example} + ## `lib.lists.reverseList` usage example - ```nix - reverseList [ "b" "o" "j" ] - => [ "j" "o" "b" ] - ``` + ```nix + reverseList [ "b" "o" "j" ] + => [ "j" "o" "b" ] + ``` - ::: + ::: */ - reverseList = xs: let l = length xs; in genList (n: elemAt xs (l - n - 1)) l; + reverseList = + xs: + let + l = length xs; + in + genList (n: elemAt xs (l - n - 1)) l; - /* * - Depth-First Search (DFS) for lists `list != []`. + /* + * + Depth-First Search (DFS) for lists `list != []`. - `before a b == true` means that `b` depends on `a` (there's an - edge from `b` to `a`). + `before a b == true` means that `b` depends on `a` (there's an + edge from `b` to `a`). - # Inputs + # Inputs - `stopOnCycles` + `stopOnCycles` - : 1\. Function argument + : 1\. Function argument - `before` + `before` - : 2\. Function argument + : 2\. Function argument - `list` + `list` - : 3\. Function argument + : 3\. Function argument - # Examples - :::{.example} - ## `lib.lists.listDfs` usage example + # Examples + :::{.example} + ## `lib.lists.listDfs` usage example - ```nix - listDfs true hasPrefix [ "/home/user" "other" "/" "/home" ] - == { minimal = "/"; # minimal element - visited = [ "/home/user" ]; # seen elements (in reverse order) - rest = [ "/home" "other" ]; # everything else - } + ```nix + listDfs true hasPrefix [ "/home/user" "other" "/" "/home" ] + == { minimal = "/"; # minimal element + visited = [ "/home/user" ]; # seen elements (in reverse order) + rest = [ "/home" "other" ]; # everything else + } - listDfs true hasPrefix [ "/home/user" "other" "/" "/home" "/" ] - == { cycle = "/"; # cycle encountered at this element - loops = [ "/" ]; # and continues to these elements - visited = [ "/" "/home/user" ]; # elements leading to the cycle (in reverse order) - rest = [ "/home" "other" ]; # everything else - ``` + listDfs true hasPrefix [ "/home/user" "other" "/" "/home" "/" ] + == { cycle = "/"; # cycle encountered at this element + loops = [ "/" ]; # and continues to these elements + visited = [ "/" "/home/user" ]; # elements leading to the cycle (in reverse order) + rest = [ "/home" "other" ]; # everything else + ``` - ::: + ::: */ - listDfs = stopOnCycles: before: list: + listDfs = + stopOnCycles: before: list: let - dfs' = us: visited: rest: + dfs' = + us: visited: rest: let c = filter (x: before x us) visited; b = partition (x: before x us) rest; - in if stopOnCycles && (length c > 0) then { - cycle = us; - loops = c; - inherit visited rest; - } else if length b.right == 0 then # nothing is before us - { - minimal = us; - inherit visited rest; - } else # grab the first one before us and continue + in + if stopOnCycles && (length c > 0) then + { + cycle = us; + loops = c; + inherit visited rest; + } + else if length b.right == 0 then # nothing is before us + { + minimal = us; + inherit visited rest; + } + # grab the first one before us and continue + else dfs' (head b.right) ([ us ] ++ visited) (tail b.right ++ b.wrong); - in dfs' (head list) [ ] (tail list); + in + dfs' (head list) [ ] (tail list); - /* * - Sort a list based on a partial ordering using DFS. This - implementation is O(N^2), if your ordering is linear, use `sort` - instead. + /* + * + Sort a list based on a partial ordering using DFS. This + implementation is O(N^2), if your ordering is linear, use `sort` + instead. - `before a b == true` means that `b` should be after `a` - in the result. + `before a b == true` means that `b` should be after `a` + in the result. - # Inputs + # Inputs - `before` + `before` - : 1\. Function argument + : 1\. Function argument - `list` + `list` - : 2\. Function argument + : 2\. Function argument - # Examples - :::{.example} - ## `lib.lists.toposort` usage example + # Examples + :::{.example} + ## `lib.lists.toposort` usage example - ```nix - toposort hasPrefix [ "/home/user" "other" "/" "/home" ] - == { result = [ "/" "/home" "/home/user" "other" ]; } + ```nix + toposort hasPrefix [ "/home/user" "other" "/" "/home" ] + == { result = [ "/" "/home" "/home/user" "other" ]; } - toposort hasPrefix [ "/home/user" "other" "/" "/home" "/" ] - == { cycle = [ "/home/user" "/" "/" ]; # path leading to a cycle - loops = [ "/" ]; } # loops back to these elements + toposort hasPrefix [ "/home/user" "other" "/" "/home" "/" ] + == { cycle = [ "/home/user" "/" "/" ]; # path leading to a cycle + loops = [ "/" ]; } # loops back to these elements - toposort hasPrefix [ "other" "/home/user" "/home" "/" ] - == { result = [ "other" "/" "/home" "/home/user" ]; } + toposort hasPrefix [ "other" "/home/user" "/home" "/" ] + == { result = [ "other" "/" "/home" "/home/user" ]; } - toposort (a: b: a < b) [ 3 2 1 ] == { result = [ 1 2 3 ]; } - ``` + toposort (a: b: a < b) [ 3 2 1 ] == { result = [ 1 2 3 ]; } + ``` - ::: + ::: */ - toposort = before: list: + toposort = + before: list: let dfsthis = listDfs true before list; toporest = toposort before (dfsthis.visited ++ dfsthis.rest); - in if length list < 2 then # finish - { - result = list; - } else if dfsthis - ? cycle then # there's a cycle, starting from the current vertex, return it - { - cycle = reverseList ([ dfsthis.cycle ] ++ dfsthis.visited); - inherit (dfsthis) loops; - } else if toporest - ? cycle then # there's a cycle somewhere else in the graph, return it + in + if length list < 2 then # finish + { result = list; } + else if dfsthis ? cycle then # there's a cycle, starting from the current vertex, return it + { + cycle = reverseList ([ dfsthis.cycle ] ++ dfsthis.visited); + inherit (dfsthis) loops; + } + else if toporest ? cycle then # there's a cycle somewhere else in the graph, return it toporest - # Slow, but short. Can be made a bit faster with an explicit stack. - else # there are no cycles - { - result = [ dfsthis.minimal ] ++ toporest.result; - }; + # Slow, but short. Can be made a bit faster with an explicit stack. + # there are no cycles + else + { result = [ dfsthis.minimal ] ++ toporest.result; }; - /* * - Sort a list based on a comparator function which compares two - elements and returns true if the first argument is strictly below - the second argument. The returned list is sorted in an increasing - order. The implementation does a quick-sort. + /* + * + Sort a list based on a comparator function which compares two + elements and returns true if the first argument is strictly below + the second argument. The returned list is sorted in an increasing + order. The implementation does a quick-sort. - See also [`sortOn`](#function-library-lib.lists.sortOn), which applies the - default comparison on a function-derived property, and may be more efficient. + See also [`sortOn`](#function-library-lib.lists.sortOn), which applies the + default comparison on a function-derived property, and may be more efficient. - # Inputs + # Inputs - `comparator` + `comparator` - : 1\. Function argument + : 1\. Function argument - `list` + `list` - : 2\. Function argument + : 2\. Function argument - # Type + # Type - ``` - sort :: (a -> a -> Bool) -> [a] -> [a] - ``` + ``` + sort :: (a -> a -> Bool) -> [a] -> [a] + ``` - # Examples - :::{.example} - ## `lib.lists.sort` usage example + # Examples + :::{.example} + ## `lib.lists.sort` usage example - ```nix - sort (p: q: p < q) [ 5 3 7 ] - => [ 3 5 7 ] - ``` + ```nix + sort (p: q: p < q) [ 5 3 7 ] + => [ 3 5 7 ] + ``` - ::: + ::: */ sort = builtins.sort; - /* * - Sort a list based on the default comparison of a derived property `b`. + /* + * + Sort a list based on the default comparison of a derived property `b`. - The items are returned in `b`-increasing order. + The items are returned in `b`-increasing order. - **Performance**: + **Performance**: - The passed function `f` is only evaluated once per item, - unlike an unprepared [`sort`](#function-library-lib.lists.sort) using - `f p < f q`. + The passed function `f` is only evaluated once per item, + unlike an unprepared [`sort`](#function-library-lib.lists.sort) using + `f p < f q`. - **Laws**: - ```nix - sortOn f == sort (p: q: f p < f q) - ``` + **Laws**: + ```nix + sortOn f == sort (p: q: f p < f q) + ``` - # Inputs + # Inputs - `f` + `f` - : 1\. Function argument + : 1\. Function argument - `list` + `list` - : 2\. Function argument + : 2\. Function argument - # Type + # Type - ``` - sortOn :: (a -> b) -> [a] -> [a], for comparable b - ``` + ``` + sortOn :: (a -> b) -> [a] -> [a], for comparable b + ``` - # Examples - :::{.example} - ## `lib.lists.sortOn` usage example + # Examples + :::{.example} + ## `lib.lists.sortOn` usage example - ```nix - sortOn stringLength [ "aa" "b" "cccc" ] - => [ "b" "aa" "cccc" ] - ``` + ```nix + sortOn stringLength [ "aa" "b" "cccc" ] + => [ "b" "aa" "cccc" ] + ``` - ::: + ::: */ - sortOn = f: list: + sortOn = + f: list: let # Heterogenous list as pair may be ugly, but requires minimal allocations. - pairs = map (x: [ (f x) x ]) list; - in map (x: builtins.elemAt x 1) (sort - # Compare the first element of the pairs - # Do not factor out the `<`, to avoid calls in hot code; duplicate instead. - (a: b: head a < head b) pairs); + pairs = map (x: [ + (f x) + x + ]) list; + in + map (x: builtins.elemAt x 1) ( + sort + # Compare the first element of the pairs + # Do not factor out the `<`, to avoid calls in hot code; duplicate instead. + (a: b: head a < head b) + pairs + ); - /* * - Compare two lists element-by-element. + /* + * + Compare two lists element-by-element. - # Inputs + # Inputs - `cmp` + `cmp` - : 1\. Function argument + : 1\. Function argument - `a` + `a` - : 2\. Function argument + : 2\. Function argument - `b` + `b` - : 3\. Function argument + : 3\. Function argument - # Examples - :::{.example} - ## `lib.lists.compareLists` usage example + # Examples + :::{.example} + ## `lib.lists.compareLists` usage example - ```nix - compareLists compare [] [] - => 0 - compareLists compare [] [ "a" ] - => -1 - compareLists compare [ "a" ] [] - => 1 - compareLists compare [ "a" "b" ] [ "a" "c" ] - => -1 - ``` + ```nix + compareLists compare [] [] + => 0 + compareLists compare [] [ "a" ] + => -1 + compareLists compare [ "a" ] [] + => 1 + compareLists compare [ "a" "b" ] [ "a" "c" ] + => -1 + ``` - ::: + ::: */ - compareLists = cmp: a: b: + compareLists = + cmp: a: b: if a == [ ] then if b == [ ] then 0 else -1 else if b == [ ] then 1 else - let rel = cmp (head a) (head b); - in if rel == 0 then compareLists cmp (tail a) (tail b) else rel; + let + rel = cmp (head a) (head b); + in + if rel == 0 then compareLists cmp (tail a) (tail b) else rel; - /* * - Sort list using "Natural sorting". - Numeric portions of strings are sorted in numeric order. + /* + * + Sort list using "Natural sorting". + Numeric portions of strings are sorted in numeric order. - # Inputs + # Inputs - `lst` + `lst` - : 1\. Function argument + : 1\. Function argument - # Examples - :::{.example} - ## `lib.lists.naturalSort` usage example + # Examples + :::{.example} + ## `lib.lists.naturalSort` usage example - ```nix - naturalSort ["disk11" "disk8" "disk100" "disk9"] - => ["disk8" "disk9" "disk11" "disk100"] - naturalSort ["10.46.133.149" "10.5.16.62" "10.54.16.25"] - => ["10.5.16.62" "10.46.133.149" "10.54.16.25"] - naturalSort ["v0.2" "v0.15" "v0.0.9"] - => [ "v0.0.9" "v0.2" "v0.15" ] - ``` + ```nix + naturalSort ["disk11" "disk8" "disk100" "disk9"] + => ["disk8" "disk9" "disk11" "disk100"] + naturalSort ["10.46.133.149" "10.5.16.62" "10.54.16.25"] + => ["10.5.16.62" "10.46.133.149" "10.54.16.25"] + naturalSort ["v0.2" "v0.15" "v0.0.9"] + => [ "v0.0.9" "v0.2" "v0.15" ] + ``` - ::: + ::: */ - naturalSort = lst: + naturalSort = + lst: let - vectorise = s: - map (x: if isList x then toInt (head x) else x) - (builtins.split "(0|[1-9][0-9]*)" s); - prepared = map (x: [ (vectorise x) x ]) - lst; # remember vectorised version for O(n) regex splits + 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 less = a: b: (compareLists compare (head a) (head b)) < 0; - in map (x: elemAt x 1) (sort less prepared); + in + map (x: elemAt x 1) (sort less prepared); - /* * - Return the first (at most) N elements of a list. + /* + * + Return the first (at most) N elements of a list. - # Inputs + # Inputs - `count` + `count` - : Number of elements to take + : Number of elements to take - `list` + `list` - : Input list + : Input list - # Type + # Type - ``` - take :: int -> [a] -> [a] - ``` + ``` + take :: int -> [a] -> [a] + ``` - # Examples - :::{.example} - ## `lib.lists.take` usage example + # Examples + :::{.example} + ## `lib.lists.take` usage example - ```nix - take 2 [ "a" "b" "c" "d" ] - => [ "a" "b" ] - take 2 [ ] - => [ ] - ``` + ```nix + take 2 [ "a" "b" "c" "d" ] + => [ "a" "b" ] + take 2 [ ] + => [ ] + ``` - ::: + ::: */ take = 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` - : Number of elements to drop + : Number of elements to drop - `list` + `list` - : Input list + : Input list - # Type + # Type - ``` - drop :: int -> [a] -> [a] - ``` + ``` + drop :: int -> [a] -> [a] + ``` - # Examples - :::{.example} - ## `lib.lists.drop` usage example + # Examples + :::{.example} + ## `lib.lists.drop` usage example - ```nix - drop 2 [ "a" "b" "c" "d" ] - => [ "c" "d" ] - drop 2 [ ] - => [ ] - ``` + ```nix + drop 2 [ "a" "b" "c" "d" ] + => [ "c" "d" ] + drop 2 [ ] + => [ ] + ``` - ::: + ::: */ drop = count: list: sublist count (length list) list; - /* * - 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` - : 1\. Function argument + : 1\. Function argument - `list2` + `list2` - : 2\. Function argument + : 2\. Function argument - # Type + # Type - ``` - hasPrefix :: [a] -> [a] -> bool - ``` + ``` + hasPrefix :: [a] -> [a] -> bool + ``` - # Examples - :::{.example} - ## `lib.lists.hasPrefix` usage example + # Examples + :::{.example} + ## `lib.lists.hasPrefix` usage example - ```nix - hasPrefix [ 1 2 ] [ 1 2 3 4 ] - => true - hasPrefix [ 0 1 ] [ 1 2 3 4 ] - => false - ``` + ```nix + hasPrefix [ 1 2 ] [ 1 2 3 4 ] + => true + hasPrefix [ 0 1 ] [ 1 2 3 4 ] + => false + ``` - ::: + ::: */ hasPrefix = list1: list2: take (length list1) list2 == list1; - /* * - Remove the first list as a prefix from the second list. - Error if the first list isn't a prefix of the second list. + /* + * + Remove the first list as a prefix from the second list. + Error if the first list isn't a prefix of the second list. - # Inputs + # Inputs - `list1` + `list1` - : 1\. Function argument + : 1\. Function argument - `list2` + `list2` - : 2\. Function argument + : 2\. Function argument - # Type + # Type - ``` - removePrefix :: [a] -> [a] -> [a] - ``` + ``` + removePrefix :: [a] -> [a] -> [a] + ``` - # Examples - :::{.example} - ## `lib.lists.removePrefix` usage example + # Examples + :::{.example} + ## `lib.lists.removePrefix` usage example - ```nix - removePrefix [ 1 2 ] [ 1 2 3 4 ] - => [ 3 4 ] - removePrefix [ 0 1 ] [ 1 2 3 4 ] - => - ``` + ```nix + removePrefix [ 1 2 ] [ 1 2 3 4 ] + => [ 3 4 ] + removePrefix [ 0 1 ] [ 1 2 3 4 ] + => + ``` - ::: + ::: */ - removePrefix = list1: list2: + removePrefix = + list1: list2: if hasPrefix list1 list2 then drop (length list1) list2 else - throw - "lib.lists.removePrefix: First argument is not a list prefix of the second argument"; + throw "lib.lists.removePrefix: First argument is not a list prefix of the second argument"; - /* * - Return a list consisting of at most `count` elements of `list`, - starting at index `start`. + /* + * + Return a list consisting of at most `count` elements of `list`, + starting at index `start`. - # Inputs + # Inputs - `start` + `start` - : Index at which to start the sublist + : Index at which to start the sublist - `count` + `count` - : Number of elements to take + : Number of elements to take - `list` + `list` - : Input list + : Input list - # Type + # Type - ``` - sublist :: int -> int -> [a] -> [a] - ``` + ``` + sublist :: int -> int -> [a] -> [a] + ``` - # Examples - :::{.example} - ## `lib.lists.sublist` usage example + # Examples + :::{.example} + ## `lib.lists.sublist` usage example - ```nix - sublist 1 3 [ "a" "b" "c" "d" "e" ] - => [ "b" "c" "d" ] - sublist 1 3 [ ] - => [ ] - ``` + ```nix + sublist 1 3 [ "a" "b" "c" "d" "e" ] + => [ "b" "c" "d" ] + sublist 1 3 [ ] + => [ ] + ``` - ::: + ::: */ - sublist = start: count: list: - let len = length list; - in genList (n: elemAt list (n + start)) (if start >= len then - 0 - else if start + count > len then - len - start - else - count); + sublist = + start: count: list: + let + len = length list; + in + genList (n: elemAt list (n + start)) ( + if start >= len then + 0 + else if start + count > len then + len - start + else + count + ); - /* * - The common prefix of two lists. + /* + * + The common prefix of two lists. - # Inputs + # Inputs - `list1` + `list1` - : 1\. Function argument + : 1\. Function argument - `list2` + `list2` - : 2\. Function argument + : 2\. Function argument - # Type + # Type - ``` - commonPrefix :: [a] -> [a] -> [a] - ``` + ``` + commonPrefix :: [a] -> [a] -> [a] + ``` - # Examples - :::{.example} - ## `lib.lists.commonPrefix` usage example + # Examples + :::{.example} + ## `lib.lists.commonPrefix` usage example - ```nix - commonPrefix [ 1 2 3 4 5 6 ] [ 1 2 4 8 ] - => [ 1 2 ] - commonPrefix [ 1 2 3 ] [ 1 2 3 4 5 ] - => [ 1 2 3 ] - commonPrefix [ 1 2 3 ] [ 4 5 6 ] - => [ ] - ``` + ```nix + commonPrefix [ 1 2 3 4 5 6 ] [ 1 2 4 8 ] + => [ 1 2 ] + commonPrefix [ 1 2 3 ] [ 1 2 3 4 5 ] + => [ 1 2 3 ] + commonPrefix [ 1 2 3 ] [ 4 5 6 ] + => [ ] + ``` - ::: + ::: */ - commonPrefix = list1: list2: + commonPrefix = + list1: list2: let # Zip the lists together into a list of booleans whether each element matches matchings = zipListsWith (fst: snd: fst != snd) list1 list2; @@ -1569,91 +1686,97 @@ in rec { # If all elements match, we fall back to the length of the zipped list, # which is the same as the length of the smaller list. commonPrefixLength = findFirstIndex id (length matchings) matchings; - in take commonPrefixLength list1; + in + take commonPrefixLength list1; - /* * - Return the last element of a list. + /* + * + Return the last element of a list. - This function throws an error if the list is empty. + This function throws an error if the list is empty. - # Inputs + # Inputs - `list` + `list` - : 1\. Function argument + : 1\. Function argument - # Type + # Type - ``` - last :: [a] -> a - ``` + ``` + last :: [a] -> a + ``` - # Examples - :::{.example} - ## `lib.lists.last` usage example + # Examples + :::{.example} + ## `lib.lists.last` usage example - ```nix - last [ 1 2 3 ] - => 3 - ``` + ```nix + last [ 1 2 3 ] + => 3 + ``` - ::: + ::: */ - last = list: + last = + list: assert lib.assertMsg (list != [ ]) "lists.last: list must not be empty!"; elemAt list (length list - 1); - /* * - Return all elements but the last. + /* + * + Return all elements but the last. - This function throws an error if the list is empty. + This function throws an error if the list is empty. - # Inputs + # Inputs - `list` + `list` - : 1\. Function argument + : 1\. Function argument - # Type + # Type - ``` - init :: [a] -> [a] - ``` + ``` + init :: [a] -> [a] + ``` - # Examples - :::{.example} - ## `lib.lists.init` usage example + # Examples + :::{.example} + ## `lib.lists.init` usage example - ```nix - init [ 1 2 3 ] - => [ 1 2 ] - ``` + ```nix + init [ 1 2 3 ] + => [ 1 2 ] + ``` - ::: + ::: */ - init = list: + init = + list: assert lib.assertMsg (list != [ ]) "lists.init: list must not be empty!"; take (length list - 1) list; - /* * - Return the image of the cross product of some lists by a function. + /* + * + Return the image of the cross product of some lists by a function. - # Examples - :::{.example} - ## `lib.lists.crossLists` usage example + # Examples + :::{.example} + ## `lib.lists.crossLists` usage example - ```nix - crossLists (x: y: "${toString x}${toString y}") [[1 2] [3 4]] - => [ "13" "14" "23" "24" ] - ``` + ```nix + crossLists (x: y: "${toString x}${toString y}") [[1 2] [3 4]] + => [ "13" "14" "23" "24" ] + ``` - The following function call is equivalent to the one deprecated above: + The following function call is equivalent to the one deprecated above: - ```nix - mapCartesianProduct (x: "${toString x.a}${toString x.b}") { a = [1 2]; b = [3 4]; } - => [ "13" "14" "23" "24" ] - ``` - ::: + ```nix + mapCartesianProduct (x: "${toString x.a}${toString x.b}") { a = [1 2]; b = [3 4]; } + => [ "13" "14" "23" "24" ] + ``` + ::: */ crossLists = warn '' lib.crossLists is deprecated, use lib.mapCartesianProduct instead. @@ -1669,133 +1792,138 @@ in rec { [ 4 5 5 6 ] '' (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` - : Input list + : Input list - # Type + # Type - ``` - unique :: [a] -> [a] - ``` + ``` + unique :: [a] -> [a] + ``` - # Examples - :::{.example} - ## `lib.lists.unique` usage example + # Examples + :::{.example} + ## `lib.lists.unique` usage example - ```nix - unique [ 3 2 3 4 ] - => [ 3 2 4 ] - ``` + ```nix + unique [ 3 2 3 4 ] + => [ 3 2 4 ] + ``` - ::: + ::: */ 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` - : 1\. Function argument + : 1\. Function argument - # Type + # Type - ``` - allUnique :: [a] -> bool - ``` + ``` + allUnique :: [a] -> bool + ``` - # Examples - :::{.example} - ## `lib.lists.allUnique` usage example + # Examples + :::{.example} + ## `lib.lists.allUnique` usage example - ```nix - allUnique [ 3 2 3 4 ] - => false - allUnique [ 3 2 4 1 ] - => true - ``` + ```nix + allUnique [ 3 2 3 4 ] + => false + allUnique [ 3 2 4 1 ] + => true + ``` - ::: + ::: */ allUnique = list: (length (unique list) == length list); - /* * - Intersects list 'list1' and another list (`list2`). + /* + * + Intersects list 'list1' and another list (`list2`). - O(nm) complexity. + O(nm) complexity. - # Inputs + # Inputs - `list1` + `list1` - : First list + : First list - `list2` + `list2` - : Second list + : Second list - # Examples - :::{.example} - ## `lib.lists.intersectLists` usage example + # Examples + :::{.example} + ## `lib.lists.intersectLists` usage example - ```nix - intersectLists [ 1 2 3 ] [ 6 3 2 ] - => [ 3 2 ] - ``` + ```nix + intersectLists [ 1 2 3 ] [ 6 3 2 ] + => [ 3 2 ] + ``` - ::: + ::: */ intersectLists = e: filter (x: elem x e); - /* * - Subtracts list 'e' from another list (`list2`). + /* + * + Subtracts list 'e' from another list (`list2`). - O(nm) complexity. + O(nm) complexity. - # Inputs + # Inputs - `e` + `e` - : First list + : First list - `list2` + `list2` - : Second list + : Second list - # Examples - :::{.example} - ## `lib.lists.subtractLists` usage example + # Examples + :::{.example} + ## `lib.lists.subtractLists` usage example - ```nix - subtractLists [ 3 2 ] [ 1 2 3 4 5 3 ] - => [ 1 4 5 ] - ``` + ```nix + subtractLists [ 3 2 ] [ 1 2 3 4 5 3 ] + => [ 1 4 5 ] + ``` - ::: + ::: */ subtractLists = e: filter (x: !(elem x e)); - /* * - Test if two lists have no common element. - It should be slightly more efficient than (intersectLists a b == []) + /* + * + Test if two lists have no common element. + It should be slightly more efficient than (intersectLists a b == []) - # Inputs + # Inputs - `a` + `a` - : 1\. Function argument + : 1\. Function argument - `b` + `b` - : 2\. Function argument + : 2\. Function argument */ mutuallyExclusive = a: b: length a == 0 || !(any (x: elem x a) b); } diff --git a/nix/stdlib/meta.nix b/nix/stdlib/meta.nix index c096467..b1f1c4f 100644 --- a/nix/stdlib/meta.nix +++ b/nix/stdlib/meta.nix @@ -1,87 +1,106 @@ -/* Some functions for manipulating meta attributes, as well as the - name attribute. +/* + Some functions for manipulating meta attributes, as well as the + name attribute. */ { lib }: let - inherit (lib) matchAttrs any all isDerivation getBin assertMsg; + inherit (lib) + matchAttrs + any + all + isDerivation + getBin + assertMsg + ; inherit (builtins) isString match typeOf; -in rec { - /* Add to or override the meta attributes of the given - derivation. - - Example: - addMetaAttrs {description = "Bla blah";} somePkg +in +rec { + /* + Add to or override the meta attributes of the given + derivation. + + Example: + addMetaAttrs {description = "Bla blah";} somePkg */ - addMetaAttrs = newAttrs: drv: - drv // { - meta = (drv.meta or { }) // newAttrs; - }; + addMetaAttrs = newAttrs: drv: drv // { meta = (drv.meta or { }) // newAttrs; }; # Disable Hydra builds of given derivation. dontDistribute = drv: addMetaAttrs { hydraPlatforms = [ ]; } drv; - /* Change the symbolic name of a package for presentation purposes - (i.e., so that nix-env users can tell them apart). + /* + Change the symbolic name of a package for presentation purposes + (i.e., so that nix-env users can tell them apart). */ 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. - Example: - updateName (oldName: oldName + "-experimental") somePkg + Example: + updateName (oldName: oldName + "-experimental") somePkg */ updateName = updater: drv: drv // { name = updater (drv.name); }; - /* Append a suffix to the name of a package (before the version - part). + /* + Append a suffix to the name of a package (before the version + part). */ - appendToName = suffix: - updateName (name: - let x = builtins.parseDrvName name; - in "${x.name}-${suffix}-${x.version}"); + appendToName = + suffix: + updateName ( + name: + let + x = builtins.parseDrvName name; + in + "${x.name}-${suffix}-${x.version}" + ); # Apply a function to each derivation and only to derivations in an attrset. - 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; # Set the nix-env priority of the package. setPrio = priority: addMetaAttrs { inherit priority; }; - /* Decrease the nix-env priority of the package, i.e., other - versions/variants of the package will be preferred. + /* + Decrease the nix-env priority of the package, i.e., other + versions/variants of the package will be preferred. */ lowPrio = setPrio 10; # Apply lowPrio to an attrset with derivations lowPrioSet = set: mapDerivationAttrset lowPrio set; - /* Increase the nix-env priority of the package, i.e., this - version/variant of the package will be preferred. + /* + Increase the nix-env priority of the package, i.e., this + version/variant of the package will be preferred. */ hiPrio = setPrio (-10); # Apply hiPrio to an attrset with derivations hiPrioSet = set: mapDerivationAttrset hiPrio set; - /* Check to see if a platform is matched by the given `meta.platforms` - element. + /* + Check to see if a platform is matched by the given `meta.platforms` + element. - A `meta.platform` pattern is either + A `meta.platform` pattern is either - 1. (legacy) a system string. + 1. (legacy) a system string. - 2. (modern) a pattern for the entire platform structure (see `lib.systems.inspect.platformPatterns`). + 2. (modern) a pattern for the entire platform structure (see `lib.systems.inspect.platformPatterns`). - 3. (modern) a pattern for the platform `parsed` field (see `lib.systems.inspect.patterns`). + 3. (modern) a pattern for the platform `parsed` field (see `lib.systems.inspect.patterns`). - We can inject these into a pattern for the whole of a structured platform, - and then match that. + We can inject these into a pattern for the whole of a structured platform, + and then match that. - Example: - lib.meta.platformMatch { system = "aarch64-darwin"; } "aarch64-darwin" - => true + Example: + lib.meta.platformMatch { system = "aarch64-darwin"; } "aarch64-darwin" + => true */ - platformMatch = platform: elem: + platformMatch = + platform: elem: ( # Check with simple string comparison if elem was a string. # @@ -95,91 +114,106 @@ in rec { else matchAttrs ( # Normalize platform attrset. - if elem ? parsed then elem else { parsed = elem; }) platform); + if elem ? parsed then elem else { parsed = elem; } + ) platform + ); - /* Check if a package is available on a given platform. + /* + Check if a package is available on a given platform. - A package is available on a platform if both + A package is available on a platform if both - 1. One of `meta.platforms` pattern matches the given - platform, or `meta.platforms` is not present. + 1. One of `meta.platforms` pattern matches the given + platform, or `meta.platforms` is not present. - 2. None of `meta.badPlatforms` pattern matches the given platform. + 2. None of `meta.badPlatforms` pattern matches the given platform. - Example: - lib.meta.availableOn { system = "aarch64-darwin"; } pkg.zsh - => true + Example: + lib.meta.availableOn { system = "aarch64-darwin"; } pkg.zsh + => true */ - availableOn = platform: pkg: + availableOn = + platform: pkg: ((!pkg ? meta.platforms) || any (platformMatch platform) pkg.meta.platforms) && all (elem: !platformMatch platform elem) (pkg.meta.badPlatforms or [ ]); - /* Get the corresponding attribute in lib.licenses - from the SPDX ID. - For SPDX IDs, see - https://spdx.org/licenses - - Type: - getLicenseFromSpdxId :: str -> AttrSet - - Example: - lib.getLicenseFromSpdxId "MIT" == lib.licenses.mit - => true - lib.getLicenseFromSpdxId "mIt" == lib.licenses.mit - => true - lib.getLicenseFromSpdxId "MY LICENSE" - => trace: warning: getLicenseFromSpdxId: No license matches the given SPDX ID: MY LICENSE - => { shortName = "MY LICENSE"; } + /* + Get the corresponding attribute in lib.licenses + from the SPDX ID. + For SPDX IDs, see + https://spdx.org/licenses + + Type: + getLicenseFromSpdxId :: str -> AttrSet + + Example: + lib.getLicenseFromSpdxId "MIT" == lib.licenses.mit + => true + lib.getLicenseFromSpdxId "mIt" == lib.licenses.mit + => true + lib.getLicenseFromSpdxId "MY LICENSE" + => trace: warning: getLicenseFromSpdxId: No license matches the given SPDX ID: MY LICENSE + => { shortName = "MY LICENSE"; } */ - getLicenseFromSpdxId = let - spdxLicenses = - lib.mapAttrs (id: ls: assert lib.length ls == 1; builtins.head ls) - (lib.groupBy (l: lib.toLower l.spdxId) - (lib.filter (l: l ? spdxId) (lib.attrValues lib.licenses))); - in licstr: - spdxLicenses.${lib.toLower licstr} or (lib.warn - "getLicenseFromSpdxId: No license matches the given SPDX ID: ${licstr}" { + getLicenseFromSpdxId = + let + spdxLicenses = + lib.mapAttrs + ( + id: ls: + assert lib.length ls == 1; + builtins.head ls + ) + (lib.groupBy (l: lib.toLower l.spdxId) (lib.filter (l: l ? spdxId) (lib.attrValues lib.licenses))); + in + licstr: + spdxLicenses.${lib.toLower licstr} + or (lib.warn "getLicenseFromSpdxId: No license matches the given SPDX ID: ${licstr}" { shortName = licstr; }); - /* 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 - Type: getExe :: package -> string + Type: getExe :: package -> string - Example: - getExe pkgs.hello - => "/nix/store/g124820p9hlv4lj8qplzxw1c44dxaw1k-hello-2.12/bin/hello" - getExe pkgs.mustache-go - => "/nix/store/am9ml4f4ywvivxnkiaqwr0hyxka1xjsf-mustache-go-1.3.0/bin/mustache" + Example: + getExe pkgs.hello + => "/nix/store/g124820p9hlv4lj8qplzxw1c44dxaw1k-hello-2.12/bin/hello" + getExe pkgs.mustache-go + => "/nix/store/am9ml4f4ywvivxnkiaqwr0hyxka1xjsf-mustache-go-1.3.0/bin/mustache" */ - getExe = x: - getExe' x (x.meta.mainProgram or ( - # This could be turned into an error when 23.05 is at end of life - lib.warn '' - getExe: Package ${ - lib.strings.escapeNixIdentifier x.meta.name or x.pname or x.name - } does not have the meta.mainProgram attribute. We'll assume that the main program has the same name for now, but this behavior is deprecated, because it leads to surprising errors when the assumption does not hold. If the package has a main program, please set `meta.mainProgram` in its definition to make this warning go away. Otherwise, if the package does not have a main program, or if you don't control its definition, use getExe' to specify the name to the program, such as lib.getExe' foo "bar".'' - lib.getName x)); - - /* Get the path of a program of a derivation. - - Type: getExe' :: derivation -> string -> string - Example: - getExe' pkgs.hello "hello" - => "/nix/store/g124820p9hlv4lj8qplzxw1c44dxaw1k-hello-2.12/bin/hello" - getExe' pkgs.imagemagick "convert" - => "/nix/store/5rs48jamq7k6sal98ymj9l4k2bnwq515-imagemagick-7.1.1-15/bin/convert" + getExe = + x: + getExe' x ( + x.meta.mainProgram or ( + # This could be turned into an error when 23.05 is at end of life + lib.warn + ''getExe: Package ${ + lib.strings.escapeNixIdentifier x.meta.name or x.pname or x.name + } does not have the meta.mainProgram attribute. We'll assume that the main program has the same name for now, but this behavior is deprecated, because it leads to surprising errors when the assumption does not hold. If the package has a main program, please set `meta.mainProgram` in its definition to make this warning go away. Otherwise, if the package does not have a main program, or if you don't control its definition, use getExe' to specify the name to the program, such as lib.getExe' foo "bar".'' + lib.getName + x + ) + ); + + /* + Get the path of a program of a derivation. + + Type: getExe' :: derivation -> string -> string + Example: + getExe' pkgs.hello "hello" + => "/nix/store/g124820p9hlv4lj8qplzxw1c44dxaw1k-hello-2.12/bin/hello" + getExe' pkgs.imagemagick "convert" + => "/nix/store/5rs48jamq7k6sal98ymj9l4k2bnwq515-imagemagick-7.1.1-15/bin/convert" */ - getExe' = x: y: + getExe' = + x: y: assert assertMsg (isDerivation x) - "lib.meta.getExe': The first argument is of type ${ - typeOf x - }, but it should be a derivation instead."; + "lib.meta.getExe': The first argument is of type ${typeOf x}, but it should be a derivation instead."; assert assertMsg (isString y) - "lib.meta.getExe': The second argument is of type ${ - typeOf y - }, but it should be a string instead."; - assert assertMsg (match ".*/.*" y == null) '' - lib.meta.getExe': The second argument "${y}" is a nested path with a "/" character, but it should just be the name of the executable instead.''; + "lib.meta.getExe': The second argument is of type ${typeOf y}, but it should be a string instead."; + assert assertMsg (match ".*/.*" y == null) + ''lib.meta.getExe': The second argument "${y}" is a nested path with a "/" character, but it should just be the name of the executable instead.''; "${getBin x}/bin/${y}"; } diff --git a/nix/stdlib/modules.nix b/nix/stdlib/modules.nix index 284786d..e3bc4c9 100644 --- a/nix/stdlib/modules.nix +++ b/nix/stdlib/modules.nix @@ -1,59 +1,109 @@ { lib }: let inherit (lib) - all any attrByPath attrNames catAttrs concatLists concatMap concatStringsSep - elem filter foldl' getAttrFromPath head id imap1 isAttrs isBool isFunction - isList isString length mapAttrs mapAttrsToList mapAttrsRecursiveCond min - optional optionalAttrs optionalString recursiveUpdate reverseList sort - setAttrByPath types warnIf zipAttrsWith; + all + any + attrByPath + attrNames + catAttrs + concatLists + concatMap + concatStringsSep + elem + filter + foldl' + getAttrFromPath + head + id + imap1 + isAttrs + isBool + isFunction + isList + isString + length + mapAttrs + mapAttrsToList + mapAttrsRecursiveCond + min + optional + optionalAttrs + optionalString + recursiveUpdate + reverseList + sort + setAttrByPath + types + warnIf + zipAttrsWith + ; inherit (lib.options) - isOption mkOption showDefs showFiles showOption unknownModule; + isOption + mkOption + showDefs + showFiles + showOption + unknownModule + ; inherit (lib.strings) isConvertibleWithToString; - showDeclPrefix = loc: decl: prefix: - " - option(s) with prefix `${ - showOption (loc ++ [ prefix ]) - }' in module `${decl._file}'"; - showRawDecls = loc: decls: - concatStringsSep "\n" (sort (a: b: a < b) - (concatMap (decl: map (showDeclPrefix loc decl) (attrNames decl.options)) - decls)); - - /* See https://nixos.org/manual/nixpkgs/unstable/#module-system-lib-evalModules - or file://./../doc/module-system/module-system.chapter.md - - !!! Please think twice before adding to this argument list! The more - that is specified here instead of in the modules themselves the harder - it is to transparently move a set of modules to be a submodule of another - config (as the proper arguments need to be replicated at each call to - evalModules) and the less declarative the module set is. + showDeclPrefix = + loc: decl: prefix: + " - option(s) with prefix `${showOption (loc ++ [ prefix ])}' in module `${decl._file}'"; + showRawDecls = + loc: decls: + concatStringsSep "\n" ( + sort (a: b: a < b) (concatMap (decl: map (showDeclPrefix loc decl) (attrNames decl.options)) decls) + ); + + /* + See https://nixos.org/manual/nixpkgs/unstable/#module-system-lib-evalModules + or file://./../doc/module-system/module-system.chapter.md + + !!! Please think twice before adding to this argument list! The more + that is specified here instead of in the modules themselves the harder + it is to transparently move a set of modules to be a submodule of another + config (as the proper arguments need to be replicated at each call to + evalModules) and the less declarative the module set is. */ - evalModules = evalModulesArgs@{ modules, prefix ? [ ], - # This should only be used for special arguments that need to be evaluated - # when resolving module structure (like in imports). For everything else, - # there's _module.args. If specialArgs.modulesPath is defined it will be - # used as the base path for disabledModules. - specialArgs ? { }, - # `class`: - # A nominal type for modules. When set and non-null, this adds a check to - # make sure that only compatible modules are imported. - class ? null, - # This would be remove in the future, Prefer _module.args option instead. - args ? { }, - # This would be remove in the future, Prefer _module.check option instead. - check ? true, }: + evalModules = + evalModulesArgs@{ + modules, + prefix ? [ ], + # This should only be used for special arguments that need to be evaluated + # when resolving module structure (like in imports). For everything else, + # there's _module.args. If specialArgs.modulesPath is defined it will be + # used as the base path for disabledModules. + specialArgs ? { }, + # `class`: + # A nominal type for modules. When set and non-null, this adds a check to + # make sure that only compatible modules are imported. + class ? null, + # This would be remove in the future, Prefer _module.args option instead. + args ? { }, + # This would be remove in the future, Prefer _module.check option instead. + check ? true, + }: let - withWarnings = x: + withWarnings = + x: lib.warnIf (evalModulesArgs ? args) - "The args argument to evalModules is deprecated. Please set config._module.args instead." - lib.warnIf (evalModulesArgs ? check) - "The check argument to evalModules is deprecated. Please set config._module.check instead." - x; + "The args argument to evalModules is deprecated. Please set config._module.args instead." + lib.warnIf + (evalModulesArgs ? check) + "The check argument to evalModules is deprecated. Please set config._module.check instead." + x; legacyModules = - optional (evalModulesArgs ? args) { config = { _module.args = args; }; } + optional (evalModulesArgs ? args) { + config = { + _module.args = args; + }; + } ++ optional (evalModulesArgs ? check) { - config = { _module.check = mkDefault check; }; + config = { + _module.check = mkDefault check; + }; }; regularModules = modules ++ legacyModules; @@ -141,8 +191,7 @@ let type = types.bool; internal = true; default = true; - description = - "Whether to check whether all option definitions have matching declarations."; + description = "Whether to check whether all option definitions have matching declarations."; }; _module.freeformType = mkOption { @@ -180,87 +229,111 @@ let }; }; - merged = let - collected = collectModules class (specialArgs.modulesPath or "") - (regularModules ++ [ internalModule ]) - ({ inherit lib options config specialArgs; } // specialArgs); - in mergeModules prefix (reverseList collected); + merged = + let + collected = + collectModules class (specialArgs.modulesPath or "") (regularModules ++ [ internalModule ]) + ( + { + inherit + lib + options + config + specialArgs + ; + } + // specialArgs + ); + in + mergeModules prefix (reverseList collected); options = merged.matchedOptions; - config = let - # For definitions that have an associated option - declaredConfig = - mapAttrsRecursiveCond (v: !isOption v) (_: v: v.value) options; - - # If freeformType is set, this is for definitions that don't have an associated option - freeformConfig = let - defs = map (def: { - file = def.file; - value = setAttrByPath def.prefix def.value; - }) merged.unmatchedDefns; - in if defs == [ ] then - { } - else - declaredConfig._module.freeformType.merge prefix defs; - in if declaredConfig._module.freeformType == null then - declaredConfig + config = + let + # For definitions that have an associated option + declaredConfig = mapAttrsRecursiveCond (v: !isOption v) (_: v: v.value) options; + + # If freeformType is set, this is for definitions that don't have an associated option + freeformConfig = + let + defs = map (def: { + file = def.file; + value = setAttrByPath def.prefix def.value; + }) merged.unmatchedDefns; + in + if defs == [ ] then { } else declaredConfig._module.freeformType.merge prefix defs; + in + if declaredConfig._module.freeformType == null then + declaredConfig # Because all definitions that had an associated option ended in # declaredConfig, freeformConfig can only contain the non-option # paths, meaning recursiveUpdate will never override any value - else - recursiveUpdate freeformConfig declaredConfig; + else + recursiveUpdate freeformConfig declaredConfig; - checkUnmatched = if config._module.check && config._module.freeformType - == null && merged.unmatchedDefns != [ ] then - let - firstDef = head merged.unmatchedDefns; - baseMsg = let - optText = showOption (prefix ++ firstDef.prefix); - defText = builtins.addErrorContext - "while evaluating the error message for definitions for `${optText}', which is an option that does not exist" - (builtins.addErrorContext - "while evaluating a definition from `${firstDef.file}'" - (showDefs [ firstDef ])); - in "The option `${optText}' does not exist. Definition values:${defText}"; - in if attrNames options == [ - "_module" - ] - # No options were declared at all (`_module` is built in) - # but we do have unmatched definitions, and no freeformType (earlier conditions) - then - let optionName = showOption prefix; - in if optionName == "" then - throw '' - ${baseMsg} + checkUnmatched = + if config._module.check && config._module.freeformType == null && merged.unmatchedDefns != [ ] then + let + firstDef = head merged.unmatchedDefns; + baseMsg = + let + optText = showOption (prefix ++ firstDef.prefix); + defText = + builtins.addErrorContext + "while evaluating the error message for definitions for `${optText}', which is an option that does not exist" + ( + builtins.addErrorContext "while evaluating a definition from `${firstDef.file}'" (showDefs [ + firstDef + ]) + ); + in + "The option `${optText}' does not exist. Definition values:${defText}"; + in + if + attrNames options == [ "_module" ] + # No options were declared at all (`_module` is built in) + # but we do have unmatched definitions, and no freeformType (earlier conditions) + then + let + optionName = showOption prefix; + in + if optionName == "" then + throw '' + ${baseMsg} + + It seems as if you're trying to declare an option by placing it into `config' rather than `options'! + '' + else + throw '' + ${baseMsg} - It seems as if you're trying to declare an option by placing it into `config' rather than `options'! - '' + However there are no options defined in `${showOption prefix}'. Are you sure you've + declared your options properly? This can happen if you e.g. declared your options in `types.submodule' + under `config' rather than `options'. + '' else - throw '' - ${baseMsg} - - However there are no options defined in `${ - showOption prefix - }'. Are you sure you've - declared your options properly? This can happen if you e.g. declared your options in `types.submodule' - under `config' rather than `options'. - '' + throw baseMsg else - throw baseMsg - else - null; + null; checked = builtins.seq checkUnmatched; extendModules = - extendArgs@{ modules ? [ ], specialArgs ? { }, prefix ? [ ], }: - evalModules (evalModulesArgs // { - inherit class; - modules = regularModules ++ modules; - specialArgs = evalModulesArgs.specialArgs or { } // specialArgs; - prefix = extendArgs.prefix or evalModulesArgs.prefix or [ ]; - }); + extendArgs@{ + modules ? [ ], + specialArgs ? { }, + prefix ? [ ], + }: + evalModules ( + evalModulesArgs + // { + inherit class; + modules = regularModules ++ modules; + specialArgs = evalModulesArgs.specialArgs or { } // specialArgs; + prefix = extendArgs.prefix or evalModulesArgs.prefix or [ ]; + } + ); type = lib.types.submoduleWith { inherit modules specialArgs class; }; @@ -272,29 +345,30 @@ let inherit extendModules type; class = class; }; - in result; + in + result; # collectModules :: (class: String) -> (modulesPath: String) -> (modules: [ Module ]) -> (args: Attrs) -> [ Module ] # # Collects all modules recursively through `import` statements, filtering out # all modules in disabledModules. - collectModules = class: + collectModules = + class: let # Like unifyModuleSyntax, but also imports paths and calls functions if necessary - loadModule = args: fallbackFile: fallbackKey: m: + loadModule = + args: fallbackFile: fallbackKey: m: if isFunction m then - unifyModuleSyntax fallbackFile fallbackKey - (applyModuleArgs fallbackKey m args) + unifyModuleSyntax fallbackFile fallbackKey (applyModuleArgs fallbackKey m args) else if isAttrs m then if m._type or "module" == "module" then unifyModuleSyntax fallbackFile fallbackKey m else if m._type == "if" || m._type == "override" then loadModule args fallbackFile fallbackKey { config = m; } else - throw ("Could not load a value as a module, because it is of type ${ - lib.strings.escapeNixString m._type - }" + lib.optionalString (fallbackFile != unknownModule) - ", in file ${toString fallbackFile}." + throw ( + "Could not load a value as a module, because it is of type ${lib.strings.escapeNixString m._type}" + + lib.optionalString (fallbackFile != unknownModule) ", in file ${toString fallbackFile}." + lib.optionalString (m._type == "configuration") '' If you do intend to import this configuration, please only import the modules that make up the configuration. You may have to create a `let` binding, file or attribute to give yourself access to the relevant modules. While loading a configuration into the module system is a very sensible idea, it can not be done cleanly in practice.'' @@ -302,120 +376,126 @@ let ) else if isList m then let - defs = [{ - file = fallbackFile; - value = m; - }]; - in throw - "Module imports can't be nested lists. Perhaps you meant to remove one level of lists? Definitions: ${ - showDefs defs - }" + defs = [ + { + file = fallbackFile; + value = m; + } + ]; + in + throw "Module imports can't be nested lists. Perhaps you meant to remove one level of lists? Definitions: ${showDefs defs}" else - unifyModuleSyntax (toString m) (toString m) - (applyModuleArgsIfFunction (toString m) (import m) args); - - checkModule = if class != null then - m: - if m._class != null -> m._class == class then - m + unifyModuleSyntax (toString m) (toString m) ( + applyModuleArgsIfFunction (toString m) (import m) args + ); + + checkModule = + if class != null then + m: + if m._class != null -> m._class == class then + m + else + throw "The module ${m._file or m.key} was imported into ${class} instead of ${m._class}." else - throw "The module ${ - m._file or m.key - } was imported into ${class} instead of ${m._class}." - else - m: m; - - /* Collects all modules recursively into the form - - { - disabled = [ ]; - # All modules of the main module list - modules = [ - { - key = ; - module = ; - # All modules imported by the module for key1 - modules = [ - { - key = ; - module = ; - # All modules imported by the module for key1-1 - modules = [ ... ]; - } - ... - ]; - } - ... - ]; - } + m: m; + + /* + Collects all modules recursively into the form + + { + disabled = [ ]; + # All modules of the main module list + modules = [ + { + key = ; + module = ; + # All modules imported by the module for key1 + modules = [ + { + key = ; + module = ; + # All modules imported by the module for key1-1 + modules = [ ... ]; + } + ... + ]; + } + ... + ]; + } */ - collectStructuredModules = let - collectResults = modules: { - disabled = concatLists (catAttrs "disabled" modules); - inherit modules; - }; - in parentFile: parentKey: initialModules: args: - collectResults (imap1 (n: x: + collectStructuredModules = let - module = checkModule - (loadModule args parentFile "${parentKey}:anon-${toString n}" x); - collectedImports = - collectStructuredModules module._file module.key module.imports - args; - in { - key = module.key; - module = module; - modules = collectedImports.modules; - disabled = (if module.disabledModules != [ ] then [{ - file = module._file; - disabled = module.disabledModules; - }] else - [ ]) ++ collectedImports.disabled; - }) initialModules); + collectResults = modules: { + disabled = concatLists (catAttrs "disabled" modules); + inherit modules; + }; + in + parentFile: parentKey: initialModules: args: + collectResults ( + imap1 ( + n: x: + let + module = checkModule (loadModule args parentFile "${parentKey}:anon-${toString n}" x); + collectedImports = collectStructuredModules module._file module.key module.imports args; + in + { + key = module.key; + module = module; + modules = collectedImports.modules; + disabled = + ( + if module.disabledModules != [ ] then + [ + { + file = module._file; + disabled = module.disabledModules; + } + ] + else + [ ] + ) + ++ collectedImports.disabled; + } + ) initialModules + ); # filterModules :: String -> { disabled, modules } -> [ Module ] # # Filters a structure as emitted by collectStructuredModules by removing all disabled # modules recursively. It returns the final list of unique-by-key modules - filterModules = modulesPath: - { disabled, modules, }: + filterModules = + modulesPath: + { disabled, modules }: let - moduleKey = file: m: + moduleKey = + file: m: if isString m then - if builtins.substring 0 1 m == "/" then - m - else - toString modulesPath + "/" + m + if builtins.substring 0 1 m == "/" then m else toString modulesPath + "/" + m else if isConvertibleWithToString m then if m ? key && m.key != toString m then - throw - "Module `${file}` contains a disabledModules item that is an attribute set that can be converted to a string (${ - toString m - }) but also has a `.key` attribute (${m.key}) with a different value. This makes it ambiguous which module should be disabled." + throw "Module `${file}` contains a disabledModules item that is an attribute set that can be converted to a string (${toString m}) but also has a `.key` attribute (${m.key}) with a different value. This makes it ambiguous which module should be disabled." else toString m else if m ? key then m.key else if isAttrs m then - throw - "Module `${file}` contains a disabledModules item that is an attribute set, presumably a module, that does not have a `key` attribute. This means that the module system doesn't have any means to identify the module that should be disabled. Make sure that you've put the correct value in disabledModules: a string path relative to modulesPath, a path value, or an attribute set with a `key` attribute." + throw "Module `${file}` contains a disabledModules item that is an attribute set, presumably a module, that does not have a `key` attribute. This means that the module system doesn't have any means to identify the module that should be disabled. Make sure that you've put the correct value in disabledModules: a string path relative to modulesPath, a path value, or an attribute set with a `key` attribute." else - throw - "Each disabledModules item must be a path, string, or a attribute set with a key attribute, or a value supported by toString. However, one of the disabledModules items in `${ - toString file - }` is none of that, but is of type ${builtins.typeOf m}."; - - disabledKeys = - concatMap ({ file, disabled, }: map (moduleKey file) disabled) - disabled; + throw "Each disabledModules item must be a path, string, or a attribute set with a key attribute, or a value supported by toString. However, one of the disabledModules items in `${toString file}` is none of that, but is of type ${builtins.typeOf m}."; + + disabledKeys = concatMap ({ file, disabled }: map (moduleKey file) disabled) disabled; keyFilter = filter (attrs: !elem attrs.key disabledKeys); - in map (attrs: attrs.module) (builtins.genericClosure { - startSet = keyFilter modules; - operator = attrs: keyFilter attrs.modules; - }); - in modulesPath: initialModules: args: - filterModules modulesPath - (collectStructuredModules unknownModule "" initialModules args); + in + map (attrs: attrs.module) ( + builtins.genericClosure { + startSet = keyFilter modules; + operator = attrs: keyFilter attrs.modules; + } + ); + in + modulesPath: initialModules: args: + filterModules modulesPath (collectStructuredModules unknownModule "" initialModules args); # Wrap a module with a default location for reporting errors. setDefaultModuleLocation = file: m: { @@ -423,19 +503,33 @@ let imports = [ m ]; }; - /* Massage a module into canonical form, that is, a set consisting - of ‘options’, ‘config’ and ‘imports’ attributes. + /* + Massage a module into canonical form, that is, a set consisting + of ‘options’, ‘config’ and ‘imports’ attributes. */ - unifyModuleSyntax = file: key: m: + unifyModuleSyntax = + file: key: m: let - addMeta = config: - if m ? meta then mkMerge [ config { meta = m.meta; } ] else config; - addFreeformType = config: + addMeta = + config: + if m ? meta then + mkMerge [ + config + { meta = m.meta; } + ] + else + config; + addFreeformType = + config: if m ? freeformType then - mkMerge [ config { _module.freeformType = m.freeformType; } ] + mkMerge [ + config + { _module.freeformType = m.freeformType; } + ] else config; - in if m ? config || m ? options then + in + if m ? config || m ? options then let badAttrs = removeAttrs m [ "_class" @@ -448,48 +542,59 @@ let "meta" "freeformType" ]; - in if badAttrs != { } then - throw "Module `${key}' has an unsupported attribute `${ - head (attrNames badAttrs) - }'. This is caused by introducing a top-level `config' or `options' attribute. Add configuration attributes immediately on the top level instead, or move all of them (namely: ${ - toString (attrNames badAttrs) - }) into the explicit `config' attribute." - else { - _file = toString m._file or file; - _class = m._class or null; - key = toString m.key or key; - disabledModules = m.disabledModules or [ ]; - imports = m.imports or [ ]; - options = m.options or { }; - config = addFreeformType (addMeta (m.config or { })); - } + in + if badAttrs != { } then + throw "Module `${key}' has an unsupported attribute `${head (attrNames badAttrs)}'. This is caused by introducing a top-level `config' or `options' attribute. Add configuration attributes immediately on the top level instead, or move all of them (namely: ${toString (attrNames badAttrs)}) into the explicit `config' attribute." + else + { + _file = toString m._file or file; + _class = m._class or null; + key = toString m.key or key; + disabledModules = m.disabledModules or [ ]; + imports = m.imports or [ ]; + options = m.options or { }; + config = addFreeformType (addMeta (m.config or { })); + } else - # shorthand syntax - lib.throwIfNot (isAttrs m) - "module ${file} (${key}) does not look like a module." { + # shorthand syntax + lib.throwIfNot (isAttrs m) "module ${file} (${key}) does not look like a module." { _file = toString m._file or file; _class = m._class or null; key = toString m.key or key; disabledModules = m.disabledModules or [ ]; imports = m.require or [ ] ++ m.imports or [ ]; options = { }; - config = addFreeformType (removeAttrs m [ - "_class" - "_file" - "key" - "disabledModules" - "require" - "imports" - "freeformType" - ]); + config = addFreeformType ( + removeAttrs m [ + "_class" + "_file" + "key" + "disabledModules" + "require" + "imports" + "freeformType" + ] + ); }; - applyModuleArgsIfFunction = key: f: - args@{ config, options, lib, ... }: + applyModuleArgsIfFunction = + key: f: + args@{ + config, + options, + lib, + ... + }: if isFunction f then applyModuleArgs key f args else f; - applyModuleArgs = key: f: - args@{ config, options, lib, ... }: + applyModuleArgs = + key: f: + args@{ + config, + options, + lib, + ... + }: let # Module arguments are resolved in a strict manner when attribute set # deconstruction is used. As the arguments are now defined with the @@ -503,108 +608,135 @@ let # a module will resolve strictly the attributes used as argument but # not their values. The values are forwarding the result of the # evaluation of the option. - context = name: - ''while evaluating the module argument `${name}' in "${key}":''; - extraArgs = builtins.mapAttrs (name: _: - builtins.addErrorContext (context name) - (args.${name} or config._module.args.${name})) (lib.functionArgs f); - # Note: we append in the opposite order such that we can add an error - # context on the explicit arguments of "args" too. This update - # operator is used to make the "args@{ ... }: with args.lib;" notation - # works. - in f (args // extraArgs); - - /* Merge a list of modules. This will recurse over the option - declarations in all modules, combining them into a single set. - At the same time, for each option declaration, it will merge the - corresponding option definitions in all machines, returning them - in the ‘value’ attribute of each option. - - This returns a set like - { - # A recursive set of options along with their final values - matchedOptions = { - foo = { _type = "option"; value = "option value of foo"; ... }; - bar.baz = { _type = "option"; value = "option value of bar.baz"; ... }; - ... - }; - # A list of definitions that weren't matched by any option - unmatchedDefns = [ - { file = "file.nix"; prefix = [ "qux" ]; value = "qux"; } - ... - ]; - } + context = name: ''while evaluating the module argument `${name}' in "${key}":''; + extraArgs = builtins.mapAttrs ( + name: _: builtins.addErrorContext (context name) (args.${name} or config._module.args.${name}) + ) (lib.functionArgs f); + in + # Note: we append in the opposite order such that we can add an error + # context on the explicit arguments of "args" too. This update + # operator is used to make the "args@{ ... }: with args.lib;" notation + # works. + f (args // extraArgs); + + /* + Merge a list of modules. This will recurse over the option + declarations in all modules, combining them into a single set. + At the same time, for each option declaration, it will merge the + corresponding option definitions in all machines, returning them + in the ‘value’ attribute of each option. + + This returns a set like + { + # A recursive set of options along with their final values + matchedOptions = { + foo = { _type = "option"; value = "option value of foo"; ... }; + bar.baz = { _type = "option"; value = "option value of bar.baz"; ... }; + ... + }; + # A list of definitions that weren't matched by any option + unmatchedDefns = [ + { file = "file.nix"; prefix = [ "qux" ]; value = "qux"; } + ... + ]; + } */ - mergeModules = prefix: modules: - mergeModules' prefix modules (concatMap (m: - map (config: { - file = m._file; - inherit config; - }) (pushDownProperties m.config)) modules); - - mergeModules' = prefix: modules: configs: + mergeModules = + prefix: modules: + mergeModules' prefix modules ( + concatMap ( + m: + map (config: { + file = m._file; + inherit config; + }) (pushDownProperties m.config) + ) modules + ); + + mergeModules' = + prefix: modules: configs: let # an attrset 'name' => list of submodules that declare ‘name’. - declsByName = zipAttrsWith (n: concatLists) (map (module: - let subtree = module.options; - in if !(builtins.isAttrs subtree) then - throw '' - An option declaration for `${ - builtins.concatStringsSep "." prefix - }' has type - `${builtins.typeOf subtree}' rather than an attribute set. - Did you mean to define this outside of `options'? - '' - else - mapAttrs (n: option: [{ - inherit (module) _file; - pos = builtins.unsafeGetAttrPos n subtree; - options = option; - }]) subtree) modules); + declsByName = zipAttrsWith (n: concatLists) ( + map ( + module: + let + subtree = module.options; + in + if !(builtins.isAttrs subtree) then + throw '' + An option declaration for `${builtins.concatStringsSep "." prefix}' has type + `${builtins.typeOf subtree}' rather than an attribute set. + Did you mean to define this outside of `options'? + '' + else + mapAttrs (n: option: [ + { + inherit (module) _file; + pos = builtins.unsafeGetAttrPos n subtree; + options = option; + } + ]) subtree + ) modules + ); # The root of any module definition must be an attrset. - checkedConfigs = assert lib.all (c: - # TODO: I have my doubts that this error would occur when option definitions are not matched. - # The implementation of this check used to be tied to a superficially similar check for - # options, so maybe that's why this is here. - isAttrs c.config || throw '' - In module `${c.file}', you're trying to define a value of type `${ - builtins.typeOf c.config - }' - rather than an attribute set for the option - `${builtins.concatStringsSep "." prefix}'! - - This usually happens if `${ - builtins.concatStringsSep "." prefix - }' has option - definitions inside that are not matched. Please check how to properly define - this option by e.g. referring to `man 5 configuration.nix'! - '') configs; + checkedConfigs = + assert lib.all ( + c: + # TODO: I have my doubts that this error would occur when option definitions are not matched. + # The implementation of this check used to be tied to a superficially similar check for + # options, so maybe that's why this is here. + isAttrs c.config + || throw '' + In module `${c.file}', you're trying to define a value of type `${builtins.typeOf c.config}' + rather than an attribute set for the option + `${builtins.concatStringsSep "." prefix}'! + + This usually happens if `${builtins.concatStringsSep "." prefix}' has option + definitions inside that are not matched. Please check how to properly define + this option by e.g. referring to `man 5 configuration.nix'! + '' + ) configs; configs; # an attrset 'name' => list of submodules that define ‘name’. - pushedDownDefinitionsByName = zipAttrsWith (n: concatLists) (map (module: - mapAttrs (n: value: - map (config: { - inherit (module) file; - inherit config; - }) (pushDownProperties value)) module.config) checkedConfigs); + pushedDownDefinitionsByName = zipAttrsWith (n: concatLists) ( + map ( + module: + mapAttrs ( + n: value: + map (config: { + inherit (module) file; + inherit config; + }) (pushDownProperties value) + ) module.config + ) checkedConfigs + ); # extract the definitions for each loc - rawDefinitionsByName = zipAttrsWith (n: concatLists) (map (module: - mapAttrs (n: value: [{ - inherit (module) file; - inherit value; - }]) module.config) checkedConfigs); + rawDefinitionsByName = zipAttrsWith (n: concatLists) ( + map ( + module: + mapAttrs (n: value: [ + { + inherit (module) file; + inherit value; + } + ]) module.config + ) checkedConfigs + ); # Convert an option tree decl to a submodule option decl - optionTreeToOption = decl: + optionTreeToOption = + decl: if isOption decl.options then decl else - decl // { + decl + // { options = mkOption { type = types.submoduleWith { - modules = [{ options = decl.options; }]; + modules = [ { options = decl.options; } ]; # `null` is not intended for use by modules. It is an internal # value that means "whatever the user has declared elsewhere". # This might become obsolete with https://github.com/NixOS/nixpkgs/issues/162398 @@ -613,23 +745,30 @@ let }; }; - resultsByName = mapAttrs (name: decls: + resultsByName = mapAttrs ( + name: decls: # We're descending into attribute ‘name’. let loc = prefix ++ [ name ]; defns = pushedDownDefinitionsByName.${name} or [ ]; defns' = rawDefinitionsByName.${name} or [ ]; - optionDecls = filter (m: - m.options ? _type && (m.options._type == "option" - || throwDeclarationTypeError loc m.options._type m._file)) decls; - in if length optionDecls == length decls then - let opt = fixupOptionType loc (mergeOptionDecls loc decls); - in { + optionDecls = filter ( + m: + m.options ? _type + && (m.options._type == "option" || throwDeclarationTypeError loc m.options._type m._file) + ) decls; + in + if length optionDecls == length decls then + let + opt = fixupOptionType loc (mergeOptionDecls loc decls); + in + { matchedOptions = evalOptionValue loc opt defns'; unmatchedDefns = [ ]; } else if optionDecls != [ ] then - if all (x: x.options.type.name or null == "submodule") optionDecls + if + all (x: x.options.type.name or null == "submodule") optionDecls # Raw options can only be merged into submodules. Merging into # attrsets might be nice, but ambiguous. Suppose we have # attrset as a `attrsOf submodule`. User declares option @@ -641,24 +780,24 @@ let # All of the above are merely syntax sugar though. then let - opt = fixupOptionType loc - (mergeOptionDecls loc (map optionTreeToOption decls)); - in { + opt = fixupOptionType loc (mergeOptionDecls loc (map optionTreeToOption decls)); + in + { matchedOptions = evalOptionValue loc opt defns'; unmatchedDefns = [ ]; } else - let nonOptions = filter (m: !isOption m.options) decls; - in throw '' - The option `${showOption loc}' in module `${ - (lib.head optionDecls)._file - }' would be a parent of the following options, but its type `${ - (lib.head - optionDecls).options.type.description or "" + let + nonOptions = filter (m: !isOption m.options) decls; + in + throw '' + The option `${showOption loc}' in module `${(lib.head optionDecls)._file}' would be a parent of the following options, but its type `${ + (lib.head optionDecls).options.type.description or "" }' does not support nested options. ${showRawDecls loc nonOptions}'' else - mergeModules' loc decls defns) declsByName; + mergeModules' loc decls defns + ) declsByName; matchedOptions = mapAttrs (n: v: v.matchedOptions) resultsByName; @@ -668,150 +807,177 @@ let mapAttrs (n: v: v.unmatchedDefns) resultsByName # Plus the definitions for the current prefix that don't have a matching option // removeAttrs rawDefinitionsByName (attrNames matchedOptions); - in { + in + { inherit matchedOptions; # Transforms unmatchedDefnsByName into a list of definitions - unmatchedDefns = if configs == [ ] then - # When no config values exist, there can be no unmatched config, so - # we short circuit and avoid evaluating more _options_ than necessary. - [ ] - else - concatLists (mapAttrsToList (name: defs: - map (def: - def // { - # Set this so we know when the definition first left unmatched territory - prefix = [ name ] ++ (def.prefix or [ ]); - }) defs) unmatchedDefnsByName); + unmatchedDefns = + if configs == [ ] then + # When no config values exist, there can be no unmatched config, so + # we short circuit and avoid evaluating more _options_ than necessary. + [ ] + else + concatLists ( + mapAttrsToList ( + name: defs: + map ( + def: + def + // { + # Set this so we know when the definition first left unmatched territory + prefix = [ name ] ++ (def.prefix or [ ]); + } + ) defs + ) unmatchedDefnsByName + ); }; - throwDeclarationTypeError = loc: actualTag: file: + throwDeclarationTypeError = + loc: actualTag: file: let name = lib.strings.escapeNixIdentifier (lib.lists.last loc); path = showOption loc; depth = length loc; - paragraphs = [ - "In module ${file}: expected an option declaration at option path `${path}` but got an attribute set with type ${actualTag}" - ] ++ optional (actualTag == "option-type") '' - When declaring an option, you must wrap the type in a `mkOption` call. It should look somewhat like: - ${comment} - ${name} = lib.mkOption { - description = ...; - type = ; - ... - }; - ''; + paragraphs = + [ + "In module ${file}: expected an option declaration at option path `${path}` but got an attribute set with type ${actualTag}" + ] + ++ optional (actualTag == "option-type") '' + When declaring an option, you must wrap the type in a `mkOption` call. It should look somewhat like: + ${comment} + ${name} = lib.mkOption { + description = ...; + type = ; + ... + }; + ''; # Ideally we'd know the exact syntax they used, but short of that, # we can only reliably repeat the last. However, we repeat the # full path in a non-misleading way here, in case they overlook # the start of the message. Examples attract attention. comment = optionalString (depth > 1) "\n # ${showOption loc}"; - in throw (concatStringsSep "\n\n" paragraphs); + in + throw (concatStringsSep "\n\n" paragraphs); - /* Merge multiple option declarations into a single declaration. In - general, there should be only one declaration of each option. - The exception is the ‘options’ attribute, which specifies - sub-options. These can be specified multiple times to allow one - module to add sub-options to an option declared somewhere else - (e.g. multiple modules define sub-options for ‘fileSystems’). + /* + Merge multiple option declarations into a single declaration. In + general, there should be only one declaration of each option. + The exception is the ‘options’ attribute, which specifies + sub-options. These can be specified multiple times to allow one + module to add sub-options to an option declared somewhere else + (e.g. multiple modules define sub-options for ‘fileSystems’). - 'loc' is the list of attribute names where the option is located. + 'loc' is the list of attribute names where the option is located. - 'opts' is a list of modules. Each module has an options attribute which - correspond to the definition of 'loc' in 'opt.file'. + 'opts' is a list of modules. Each module has an options attribute which + correspond to the definition of 'loc' in 'opt.file'. */ - mergeOptionDecls = loc: opts: - foldl' (res: opt: - let - t = res.type; - t' = opt.options.type; - mergedType = t.typeMerge t'.functor; - typesMergeable = mergedType != null; - typeSet = if (bothHave "type") && typesMergeable then { - type = mergedType; - } else - { }; - bothHave = k: opt.options ? ${k} && res ? ${k}; - in if bothHave "default" || bothHave "example" || bothHave "description" - || bothHave "apply" || (bothHave "type" && (!typesMergeable)) then - throw "The option `${ - showOption loc - }' in `${opt._file}' is already declared in ${ - showFiles res.declarations - }." - else + mergeOptionDecls = + loc: opts: + foldl' + ( + res: opt: let - getSubModules = opt.options.type.getSubModules or null; - submodules = if getSubModules != null then - map (setDefaultModuleLocation opt._file) getSubModules - ++ res.options - else - res.options; - in opt.options // res // { - declarations = res.declarations ++ [ opt._file ]; - # In the case of modules that are generated dynamically, we won't - # have exact declaration lines; fall back to just the file being - # evaluated. - declarationPositions = res.declarationPositions - ++ (if opt.pos != null then - [ opt.pos ] - else [{ - file = opt._file; - line = null; - column = null; - }]); - options = submodules; - } // typeSet) { - inherit loc; - declarations = [ ]; - declarationPositions = [ ]; - options = [ ]; - } opts; - - /* Merge all the definitions of an option to produce the final - config value. + t = res.type; + t' = opt.options.type; + mergedType = t.typeMerge t'.functor; + typesMergeable = mergedType != null; + typeSet = if (bothHave "type") && typesMergeable then { type = mergedType; } else { }; + bothHave = k: opt.options ? ${k} && res ? ${k}; + in + if + bothHave "default" + || bothHave "example" + || bothHave "description" + || bothHave "apply" + || (bothHave "type" && (!typesMergeable)) + then + throw "The option `${showOption loc}' in `${opt._file}' is already declared in ${showFiles res.declarations}." + else + let + getSubModules = opt.options.type.getSubModules or null; + submodules = + if getSubModules != null then + map (setDefaultModuleLocation opt._file) getSubModules ++ res.options + else + res.options; + in + opt.options + // res + // { + declarations = res.declarations ++ [ opt._file ]; + # In the case of modules that are generated dynamically, we won't + # have exact declaration lines; fall back to just the file being + # evaluated. + declarationPositions = + res.declarationPositions + ++ ( + if opt.pos != null then + [ opt.pos ] + else + [ + { + file = opt._file; + line = null; + column = null; + } + ] + ); + options = submodules; + } + // typeSet + ) + { + inherit loc; + declarations = [ ]; + declarationPositions = [ ]; + options = [ ]; + } + opts; + + /* + Merge all the definitions of an option to produce the final + config value. */ - evalOptionValue = loc: opt: defs: + evalOptionValue = + loc: opt: defs: let # Add in the default value for this option, if any. - defs' = (optional (opt ? default) { - file = head opt.declarations; - value = mkOptionDefault opt.default; - }) ++ defs; + defs' = + (optional (opt ? default) { + file = head opt.declarations; + value = mkOptionDefault opt.default; + }) + ++ defs; # Handle properties, check types, and merge everything together. - res = if opt.readOnly or false && length defs' > 1 then - let - # For a better error message, evaluate all readOnly definitions as - # if they were the only definition. - separateDefs = map (def: - def // { - value = (mergeDefinitions loc opt.type [ def ]).mergedValue; - }) defs'; - in throw "The option `${ - showOption loc - }' is read-only, but it's set multiple times. Definition values:${ - showDefs separateDefs - }" - else - mergeDefinitions loc opt.type defs'; + res = + if opt.readOnly or false && length defs' > 1 then + let + # For a better error message, evaluate all readOnly definitions as + # if they were the only definition. + separateDefs = map ( + def: def // { value = (mergeDefinitions loc opt.type [ def ]).mergedValue; } + ) defs'; + in + throw "The option `${showOption loc}' is read-only, but it's set multiple times. Definition values:${showDefs separateDefs}" + else + mergeDefinitions loc opt.type defs'; # Apply the 'apply' function to the merged value. This allows options to # yield a value computed from the definitions - value = - if opt ? apply then opt.apply res.mergedValue else res.mergedValue; - - warnDeprecation = warnIf (opt.type.deprecationMessage != null) - "The type `types.${opt.type.name}' of option `${ - showOption loc - }' defined in ${ - showFiles opt.declarations - } is deprecated. ${opt.type.deprecationMessage}"; - in warnDeprecation opt // { - value = builtins.addErrorContext - "while evaluating the option `${showOption loc}':" value; + value = if opt ? apply then opt.apply res.mergedValue else res.mergedValue; + + warnDeprecation = + warnIf (opt.type.deprecationMessage != null) + "The type `types.${opt.type.name}' of option `${showOption loc}' defined in ${showFiles opt.declarations} is deprecated. ${opt.type.deprecationMessage}"; + in + warnDeprecation opt + // { + value = builtins.addErrorContext "while evaluating the option `${showOption loc}':" value; inherit (res.defsFinal') highestPrio; definitions = map (def: def.value) res.defsFinal; files = map (def: def.file) res.defsFinal; @@ -823,91 +989,101 @@ let # Merge definitions of a value of a given type. mergeDefinitions = loc: type: defs: rec { - defsFinal' = let - # Process mkMerge and mkIf properties. - defs' = concatMap (m: - map (value: { - inherit (m) file; - inherit value; - }) (builtins.addErrorContext - "while evaluating definitions from `${m.file}':" - (dischargeProperties m.value))) defs; - - # Process mkOverride properties. - defs'' = filterOverrides' defs'; - - # Sort mkOrder properties. - defs''' = - # Avoid sorting if we don't have to. - if any (def: def.value._type or "" == "order") defs''.values then - sortProperties defs''.values - else - defs''.values; - in { - values = defs'''; - inherit (defs'') highestPrio; - }; + defsFinal' = + let + # Process mkMerge and mkIf properties. + defs' = concatMap ( + m: + map + (value: { + inherit (m) file; + inherit value; + }) + ( + builtins.addErrorContext "while evaluating definitions from `${m.file}':" ( + dischargeProperties m.value + ) + ) + ) defs; + + # Process mkOverride properties. + defs'' = filterOverrides' defs'; + + # Sort mkOrder properties. + defs''' = + # Avoid sorting if we don't have to. + if any (def: def.value._type or "" == "order") defs''.values then + sortProperties defs''.values + else + defs''.values; + in + { + values = defs'''; + inherit (defs'') highestPrio; + }; defsFinal = defsFinal'.values; # Type-check the remaining definitions, and merge them. Or throw if no definitions. - mergedValue = if isDefined then - if all (def: type.check def.value) defsFinal then - type.merge loc defsFinal + mergedValue = + if isDefined then + if all (def: type.check def.value) defsFinal then + type.merge loc defsFinal + else + let + allInvalid = filter (def: !type.check def.value) defsFinal; + in + throw "A definition for option `${showOption loc}' is not of type `${type.description}'. Definition values:${showDefs allInvalid}" else - let allInvalid = filter (def: !type.check def.value) defsFinal; - in throw "A definition for option `${ - showOption loc - }' is not of type `${type.description}'. Definition values:${ - showDefs allInvalid - }" - else - # (nixos-option detects this specific error message and gives it special - # handling. If changed here, please change it there too.) - throw "The option `${showOption loc}' is used but not defined."; + # (nixos-option detects this specific error message and gives it special + # handling. If changed here, please change it there too.) + throw "The option `${showOption loc}' is used but not defined."; isDefined = defsFinal != [ ]; optionalValue = if isDefined then { value = mergedValue; } else { }; }; - /* Given a config set, expand mkMerge properties, and push down the - other properties into the children. The result is a list of - config sets that do not have properties at top-level. For - example, + /* + Given a config set, expand mkMerge properties, and push down the + other properties into the children. The result is a list of + config sets that do not have properties at top-level. For + example, - mkMerge [ { boot = set1; } (mkIf cond { boot = set2; services = set3; }) ] + mkMerge [ { boot = set1; } (mkIf cond { boot = set2; services = set3; }) ] - is transformed into + is transformed into - [ { boot = set1; } { boot = mkIf cond set2; services = mkIf cond set3; } ]. + [ { boot = set1; } { boot = mkIf cond set2; services = mkIf cond set3; } ]. - This transform is the critical step that allows mkIf conditions - to refer to the full configuration without creating an infinite - recursion. + This transform is the critical step that allows mkIf conditions + to refer to the full configuration without creating an infinite + recursion. */ - pushDownProperties = cfg: + pushDownProperties = + cfg: if cfg._type or "" == "merge" then concatMap pushDownProperties cfg.contents else if cfg._type or "" == "if" then - map (mapAttrs (n: v: mkIf cfg.condition v)) - (pushDownProperties cfg.content) + map (mapAttrs (n: v: mkIf cfg.condition v)) (pushDownProperties cfg.content) else if cfg._type or "" == "override" then - map (mapAttrs (n: v: mkOverride cfg.priority v)) - (pushDownProperties cfg.content) - else # FIXME: handle mkOrder? + map (mapAttrs (n: v: mkOverride cfg.priority v)) (pushDownProperties cfg.content) + # FIXME: handle mkOrder? + else [ cfg ]; - /* Given a config value, expand mkMerge properties, and discharge - any mkIf conditions. That is, this is the place where mkIf - conditions are actually evaluated. The result is a list of - config values. For example, ‘mkIf false x’ yields ‘[]’, - ‘mkIf true x’ yields ‘[x]’, and + /* + Given a config value, expand mkMerge properties, and discharge + any mkIf conditions. That is, this is the place where mkIf + conditions are actually evaluated. The result is a list of + config values. For example, ‘mkIf false x’ yields ‘[]’, + ‘mkIf true x’ yields ‘[x]’, and - mkMerge [ 1 (mkIf true 2) (mkIf true (mkIf false 3)) ] + mkMerge [ 1 (mkIf true 2) (mkIf true (mkIf false 3)) ] - yields ‘[ 1 2 ]’. + yields ‘[ 1 2 ]’. */ - dischargeProperties = def: + dischargeProperties = + def: if def._type or "" == "merge" then concatMap dischargeProperties def.contents else if def._type or "" == "if" then @@ -918,103 +1094,117 @@ let else [ def ]; - /* Given a list of config values, process the mkOverride properties, - that is, return the values that have the highest (that is, - numerically lowest) priority, and strip the mkOverride - properties. For example, + /* + Given a list of config values, process the mkOverride properties, + that is, return the values that have the highest (that is, + numerically lowest) priority, and strip the mkOverride + properties. For example, - [ { file = "/1"; value = mkOverride 10 "a"; } - { file = "/2"; value = mkOverride 20 "b"; } - { file = "/3"; value = "z"; } - { file = "/4"; value = mkOverride 10 "d"; } - ] + [ { file = "/1"; value = mkOverride 10 "a"; } + { file = "/2"; value = mkOverride 20 "b"; } + { file = "/3"; value = "z"; } + { file = "/4"; value = mkOverride 10 "d"; } + ] - yields + yields - [ { file = "/1"; value = "a"; } - { file = "/4"; value = "d"; } - ] + [ { file = "/1"; value = "a"; } + { file = "/4"; value = "d"; } + ] - Note that "z" has the default priority 100. + Note that "z" has the default priority 100. */ filterOverrides = defs: (filterOverrides' defs).values; - filterOverrides' = defs: + filterOverrides' = + defs: let - getPrio = def: - if def.value._type or "" == "override" then - def.value.priority - else - defaultOverridePriority; + getPrio = + def: if def.value._type or "" == "override" then def.value.priority else defaultOverridePriority; highestPrio = foldl' (prio: def: min (getPrio def) prio) 9999 defs; - strip = def: - if def.value._type or "" == "override" then - def // { value = def.value.content; } - else - def; - in { - values = concatMap - (def: if getPrio def == highestPrio then [ (strip def) ] else [ ]) defs; + strip = + def: if def.value._type or "" == "override" then def // { value = def.value.content; } else def; + in + { + values = concatMap (def: if getPrio def == highestPrio then [ (strip def) ] else [ ]) defs; inherit highestPrio; }; - /* Sort a list of properties. The sort priority of a property is - defaultOrderPriority by default, but can be overridden by wrapping the property - using mkOrder. + /* + Sort a list of properties. The sort priority of a property is + defaultOrderPriority by default, but can be overridden by wrapping the property + using mkOrder. */ - sortProperties = defs: + sortProperties = + defs: let - strip = def: + strip = + def: if def.value._type or "" == "order" then - def // { + def + // { value = def.value.content; inherit (def.value) priority; } else def; defs' = map strip defs; - compare = a: b: - (a.priority or defaultOrderPriority) - < (b.priority or defaultOrderPriority); - in sort compare defs'; + compare = a: b: (a.priority or defaultOrderPriority) < (b.priority or defaultOrderPriority); + in + sort compare defs'; # This calls substSubModules, whose entire purpose is only to ensure that # option declarations in submodules have accurate position information. # TODO: Merge this into mergeOptionDecls - fixupOptionType = loc: opt: + fixupOptionType = + loc: opt: if opt.type.getSubModules or null == null then opt // { type = opt.type or types.unspecified; } else - opt // { + opt + // { type = opt.type.substSubModules opt.options; options = [ ]; }; - /* Merge an option's definitions in a way that preserves the priority of the - individual attributes in the option value. + /* + Merge an option's definitions in a way that preserves the priority of the + individual attributes in the option value. - This does not account for all option semantics, such as readOnly. + This does not account for all option semantics, such as readOnly. - Type: - option -> attrsOf { highestPrio, value } + Type: + option -> attrsOf { highestPrio, value } */ - mergeAttrDefinitionsWithPrio = opt: + mergeAttrDefinitionsWithPrio = + opt: let - defsByAttr = lib.zipAttrs (lib.concatLists (lib.concatMap - ({ value, ... }@def: - map - (lib.mapAttrsToList (k: value: { ${k} = def // { inherit value; }; })) - (pushDownProperties value)) opt.definitionsWithLocations)); - in assert opt.type.name == "attrsOf" || opt.type.name == "lazyAttrsOf"; - lib.mapAttrs (k: v: + defsByAttr = lib.zipAttrs ( + lib.concatLists ( + lib.concatMap ( + { value, ... }@def: + map (lib.mapAttrsToList ( + k: value: { + ${k} = def // { + inherit value; + }; + } + )) (pushDownProperties value) + ) opt.definitionsWithLocations + ) + ); + in + assert opt.type.name == "attrsOf" || opt.type.name == "lazyAttrsOf"; + lib.mapAttrs ( + k: v: let - merging = - lib.mergeDefinitions (opt.loc ++ [ k ]) opt.type.nestedTypes.elemType - v; - in { + merging = lib.mergeDefinitions (opt.loc ++ [ k ]) opt.type.nestedTypes.elemType v; + in + { value = merging.mergedValue; inherit (merging.defsFinal') highestPrio; - }) defsByAttr; + } + ) defsByAttr; # Properties. @@ -1023,13 +1213,16 @@ let inherit condition content; }; - mkAssert = assertion: message: content: - mkIf (if assertion then - true - else - throw '' + mkAssert = + assertion: message: content: + mkIf ( + if assertion then + true + else + throw '' - Failed assertion: ${message}'') content; + Failed assertion: ${message}'' + ) content; mkMerge = contents: { _type = "merge"; @@ -1042,21 +1235,18 @@ let }; mkOptionDefault = mkOverride 1500; # priority of option defaults - mkDefault = mkOverride - 1000; # used in config sections of non-user modules to set a default + mkDefault = mkOverride 1000; # used in config sections of non-user modules to set a default defaultOverridePriority = 100; - mkImageMediaOverride = mkOverride - 60; # image media profiles can be derived by inclusion into host config, hence needing to override host config, but do allow user to mkForce + mkImageMediaOverride = mkOverride 60; # image media profiles can be derived by inclusion into host config, hence needing to override host config, but do allow user to mkForce mkForce = mkOverride 50; mkVMOverride = mkOverride 10; # used by ‘nixos-rebuild build-vm’ - defaultPriority = lib.warnIf (lib.isInOldestRelease 2305) - "lib.modules.defaultPriority is deprecated, please use lib.modules.defaultOverridePriority instead." - defaultOverridePriority; + defaultPriority = + lib.warnIf (lib.isInOldestRelease 2305) + "lib.modules.defaultPriority is deprecated, please use lib.modules.defaultOverridePriority instead." + defaultOverridePriority; - mkFixStrictness = lib.warn - "lib.mkFixStrictness has no effect and will be removed. It returns its argument unmodified, so you can just remove any calls." - id; + mkFixStrictness = lib.warn "lib.mkFixStrictness has no effect and will be removed. It returns its argument unmodified, so you can just remove any calls." id; mkOrder = priority: content: { _type = "order"; @@ -1087,187 +1277,206 @@ let # mkDefault properties of the previous option. # mkAliasDefinitions = mkAliasAndWrapDefinitions id; - mkAliasAndWrapDefinitions = wrap: option: - mkAliasIfDef option (wrap (mkMerge option.definitions)); + mkAliasAndWrapDefinitions = wrap: option: mkAliasIfDef option (wrap (mkMerge option.definitions)); # Similar to mkAliasAndWrapDefinitions but copies over the priority from the # option as well. # # If a priority is not set, it assumes a priority of defaultOverridePriority. - mkAliasAndWrapDefsWithPriority = wrap: option: + mkAliasAndWrapDefsWithPriority = + wrap: option: let prio = option.highestPrio or defaultOverridePriority; defsWithPrio = map (mkOverride prio) option.definitions; - in mkAliasIfDef option (wrap (mkMerge defsWithPrio)); + in + mkAliasIfDef option (wrap (mkMerge defsWithPrio)); mkAliasIfDef = option: mkIf (isOption option && option.isDefined); # Compatibility. - fixMergeModules = modules: args: + fixMergeModules = + modules: args: evalModules { inherit modules args; check = false; }; - /* Return a module that causes a warning to be shown if the - specified option is defined. For example, + /* + Return a module that causes a warning to be shown if the + specified option is defined. For example, - mkRemovedOptionModule [ "boot" "loader" "grub" "bootDevice" ] "" + mkRemovedOptionModule [ "boot" "loader" "grub" "bootDevice" ] "" - causes a assertion if the user defines boot.loader.grub.bootDevice. + causes a assertion if the user defines boot.loader.grub.bootDevice. - replacementInstructions is a string that provides instructions on - how to achieve the same functionality without the removed option, - or alternatively a reasoning why the functionality is not needed. - replacementInstructions SHOULD be provided! + replacementInstructions is a string that provides instructions on + how to achieve the same functionality without the removed option, + or alternatively a reasoning why the functionality is not needed. + replacementInstructions SHOULD be provided! */ - mkRemovedOptionModule = optionName: replacementInstructions: - { options, ... }: { + mkRemovedOptionModule = + optionName: replacementInstructions: + { options, ... }: + { options = setAttrByPath optionName (mkOption { visible = false; - apply = x: - throw "The option `${ - showOption optionName - }' can no longer be used since it's been removed. ${replacementInstructions}"; + apply = + x: + throw "The option `${showOption optionName}' can no longer be used since it's been removed. ${replacementInstructions}"; }); - config.assertions = let opt = getAttrFromPath optionName options; - in [{ - assertion = !opt.isDefined; - message = '' - The option definition `${showOption optionName}' in ${ - showFiles opt.files - } no longer has any effect; please remove it. - ${replacementInstructions} - ''; - }]; + config.assertions = + let + opt = getAttrFromPath optionName options; + in + [ + { + assertion = !opt.isDefined; + message = '' + The option definition `${showOption optionName}' in ${showFiles opt.files} no longer has any effect; please remove it. + ${replacementInstructions} + ''; + } + ]; }; - /* Return a module that causes a warning to be shown if the - specified "from" option is defined; the defined value is however - forwarded to the "to" option. This can be used to rename options - while providing backward compatibility. For example, + /* + Return a module that causes a warning to be shown if the + specified "from" option is defined; the defined value is however + forwarded to the "to" option. This can be used to rename options + while providing backward compatibility. For example, - mkRenamedOptionModule [ "boot" "copyKernels" ] [ "boot" "loader" "grub" "copyKernels" ] + mkRenamedOptionModule [ "boot" "copyKernels" ] [ "boot" "loader" "grub" "copyKernels" ] - forwards any definitions of boot.copyKernels to - boot.loader.grub.copyKernels while printing a warning. + forwards any definitions of boot.copyKernels to + boot.loader.grub.copyKernels while printing a warning. - This also copies over the priority from the aliased option to the - non-aliased option. + This also copies over the priority from the aliased option to the + non-aliased option. */ - mkRenamedOptionModule = from: to: + mkRenamedOptionModule = + from: to: doRename { inherit from to; visible = false; warn = true; - use = builtins.trace - "Obsolete option `${showOption from}' is used. It was renamed to `${ - showOption to - }'."; + use = builtins.trace "Obsolete option `${showOption from}' is used. It was renamed to `${showOption to}'."; }; - mkRenamedOptionModuleWith = { - # Old option path as list of strings. - from, - # New option path as list of strings. - to, - /* Release number of the first release that contains the rename, ignoring backports. - Set it to the upcoming release, matching the nixpkgs/.version file. - */ - sinceRelease, }: + mkRenamedOptionModuleWith = + { + # Old option path as list of strings. + from, + # New option path as list of strings. + to, + /* + Release number of the first release that contains the rename, ignoring backports. + Set it to the upcoming release, matching the nixpkgs/.version file. + */ + sinceRelease, + }: doRename { inherit from to; visible = false; warn = lib.isInOldestRelease sinceRelease; - use = lib.warnIf (lib.isInOldestRelease sinceRelease) - "Obsolete option `${showOption from}' is used. It was renamed to `${ - showOption to - }'."; + use = lib.warnIf (lib.isInOldestRelease sinceRelease) "Obsolete option `${showOption from}' is used. It was renamed to `${showOption to}'."; }; - /* Return a module that causes a warning to be shown if any of the "from" - option is defined; the defined values can be used in the "mergeFn" to set - the "to" value. - This function can be used to merge multiple options into one that has a - different type. - - "mergeFn" takes the module "config" as a parameter and must return a value - of "to" option type. - - mkMergedOptionModule - [ [ "a" "b" "c" ] - [ "d" "e" "f" ] ] - [ "x" "y" "z" ] - (config: - let value = p: getAttrFromPath p config; - in - if (value [ "a" "b" "c" ]) == true then "foo" - else if (value [ "d" "e" "f" ]) == true then "bar" - else "baz") - - - options.a.b.c is a removed boolean option - - options.d.e.f is a removed boolean option - - options.x.y.z is a new str option that combines a.b.c and d.e.f - functionality - - This show a warning if any a.b.c or d.e.f is set, and set the value of - x.y.z to the result of the merge function + /* + Return a module that causes a warning to be shown if any of the "from" + option is defined; the defined values can be used in the "mergeFn" to set + the "to" value. + This function can be used to merge multiple options into one that has a + different type. + + "mergeFn" takes the module "config" as a parameter and must return a value + of "to" option type. + + mkMergedOptionModule + [ [ "a" "b" "c" ] + [ "d" "e" "f" ] ] + [ "x" "y" "z" ] + (config: + let value = p: getAttrFromPath p config; + in + if (value [ "a" "b" "c" ]) == true then "foo" + else if (value [ "d" "e" "f" ]) == true then "bar" + else "baz") + + - options.a.b.c is a removed boolean option + - options.d.e.f is a removed boolean option + - options.x.y.z is a new str option that combines a.b.c and d.e.f + functionality + + This show a warning if any a.b.c or d.e.f is set, and set the value of + x.y.z to the result of the merge function */ - mkMergedOptionModule = from: to: mergeFn: - { config, options, ... }: { - options = foldl' recursiveUpdate { } (map (path: - setAttrByPath path (mkOption { - visible = false; - # To use the value in mergeFn without triggering errors - default = "_mkMergedOptionModule"; - })) from); - - config = { - warnings = filter (x: x != "") (map (f: - let - val = getAttrFromPath f config; - opt = getAttrFromPath f options; - in optionalString (val != "_mkMergedOptionModule") - "The option `${showOption f}' defined in ${ - showFiles opt.files - } has been changed to `${ - showOption to - }' that has a different type. Please read `${ - showOption to - }' documentation and update your configuration accordingly.") from); - } // setAttrByPath to (mkMerge (optional - (any (f: (getAttrFromPath f config) != "_mkMergedOptionModule") from) - (mergeFn config))); + mkMergedOptionModule = + from: to: mergeFn: + { config, options, ... }: + { + options = foldl' recursiveUpdate { } ( + map ( + path: + setAttrByPath path (mkOption { + visible = false; + # To use the value in mergeFn without triggering errors + default = "_mkMergedOptionModule"; + }) + ) from + ); + + config = + { + warnings = filter (x: x != "") ( + map ( + f: + let + val = getAttrFromPath f config; + opt = getAttrFromPath f options; + in + optionalString (val != "_mkMergedOptionModule") + "The option `${showOption f}' defined in ${showFiles opt.files} has been changed to `${showOption to}' that has a different type. Please read `${showOption to}' documentation and update your configuration accordingly." + ) from + ); + } + // setAttrByPath to ( + mkMerge ( + optional (any (f: (getAttrFromPath f config) != "_mkMergedOptionModule") from) (mergeFn config) + ) + ); }; - /* Single "from" version of mkMergedOptionModule. - Return a module that causes a warning to be shown if the "from" option is - defined; the defined value can be used in the "mergeFn" to set the "to" - value. - This function can be used to change an option into another that has a - different type. - - "mergeFn" takes the module "config" as a parameter and must return a value of - "to" option type. - - mkChangedOptionModule [ "a" "b" "c" ] [ "x" "y" "z" ] - (config: - let value = getAttrFromPath [ "a" "b" "c" ] config; - in - if value > 100 then "high" - else "normal") - - - options.a.b.c is a removed int option - - options.x.y.z is a new str option that supersedes a.b.c - - This show a warning if a.b.c is set, and set the value of x.y.z to the - result of the change function + /* + Single "from" version of mkMergedOptionModule. + Return a module that causes a warning to be shown if the "from" option is + defined; the defined value can be used in the "mergeFn" to set the "to" + value. + This function can be used to change an option into another that has a + different type. + + "mergeFn" takes the module "config" as a parameter and must return a value of + "to" option type. + + mkChangedOptionModule [ "a" "b" "c" ] [ "x" "y" "z" ] + (config: + let value = getAttrFromPath [ "a" "b" "c" ] config; + in + if value > 100 then "high" + else "normal") + + - options.a.b.c is a removed int option + - options.x.y.z is a new str option that supersedes a.b.c + + This show a warning if a.b.c is set, and set the value of x.y.z to the + result of the change function */ - mkChangedOptionModule = from: to: changeFn: + mkChangedOptionModule = + from: to: changeFn: mkMergedOptionModule [ from ] to changeFn; # Like ‘mkRenamedOptionModule’, but doesn't show a warning. - mkAliasOptionModule = from: to: + mkAliasOptionModule = + from: to: doRename { inherit from to; visible = true; @@ -1275,20 +1484,22 @@ let use = id; }; - /* Transitional version of mkAliasOptionModule that uses MD docs. + /* + Transitional version of mkAliasOptionModule that uses MD docs. - This function is no longer necessary and merely an alias of `mkAliasOptionModule`. + This function is no longer necessary and merely an alias of `mkAliasOptionModule`. */ mkAliasOptionModuleMD = mkAliasOptionModule; - /* mkDerivedConfig : Option a -> (a -> Definition b) -> Definition b + /* + mkDerivedConfig : Option a -> (a -> Definition b) -> Definition b - Create config definitions with the same priority as the definition of another option. - This should be used for option definitions where one option sets the value of another as a convenience. - For instance a config file could be set with a `text` or `source` option, where text translates to a `source` - value using `mkDerivedConfig options.text (pkgs.writeText "filename.conf")`. + Create config definitions with the same priority as the definition of another option. + This should be used for option definitions where one option sets the value of another as a convenience. + For instance a config file could be set with a `text` or `source` option, where text translates to a `source` + value using `mkDerivedConfig options.text (pkgs.writeText "filename.conf")`. - It takes care of setting the right priority using `mkOverride`. + It takes care of setting the right priority using `mkOverride`. */ # TODO: make the module system error message include information about `opt` in # error messages about conflicts. E.g. introduce a variation of `mkOverride` which @@ -1296,146 +1507,200 @@ let # to all messages that report option locations "this value was derived from # which was defined in ". It can provide a trace of options that contributed # to definitions. - mkDerivedConfig = opt: f: - mkOverride (opt.highestPrio or defaultOverridePriority) (f opt.value); + mkDerivedConfig = opt: f: mkOverride (opt.highestPrio or defaultOverridePriority) (f opt.value); - /* Return a module that help declares an option that has been renamed. - When a value is defined for the old option, it is forwarded to the `to` option. + /* + Return a module that help declares an option that has been renamed. + When a value is defined for the old option, it is forwarded to the `to` option. */ - doRename = { - # List of strings representing the attribute path of the old option. - from, - # List of strings representing the attribute path of the new option. - to, - # Boolean, whether the old option is to be included in documentation. - visible, - # Whether to warn when a value is defined for the old option. - # NOTE: This requires the NixOS assertions module to be imported, so - # - this generally does not work in submodules - # - this may or may not work outside NixOS - warn, - # A function that is applied to the option value, to form the value - # of the old `from` option. - # - # For example, the identity function can be passed, to return the option value unchanged. - # ```nix - # use = x: x; - # ``` - # - # To add a warning, you can pass the partially applied `warn` function. - # ```nix - # use = lib.warn "Obsolete option `${opt.old}' is used. Use `${opt.to}' instead."; - # ``` - use, - # Legacy option, enabled by default: whether to preserve the priority of definitions in `old`. - withPriority ? true, - # A boolean that defines the `mkIf` condition for `to`. - # If the condition evaluates to `true`, and the `to` path points into an - # `attrsOf (submodule ...)`, then `doRename` would cause an empty module to - # be created, even if the `from` option is undefined. - # By setting this to an expression that may return `false`, you can inhibit - # this undesired behavior. - # - # Example: - # - # ```nix - # { config, lib, ... }: - # let - # inherit (lib) mkOption mkEnableOption types doRename; - # in - # { - # options = { - # - # # Old service - # services.foo.enable = mkEnableOption "foo"; - # - # # New multi-instance service - # services.foos = mkOption { - # type = types.attrsOf (types.submodule …); - # }; - # }; - # imports = [ - # (doRename { - # from = [ "services" "foo" "bar" ]; - # to = [ "services" "foos" "" "bar" ]; - # visible = true; - # warn = false; - # use = x: x; - # withPriority = true; - # # Only define services.foos."" if needed. (It's not just about `bar`) - # condition = config.services.foo.enable; - # }) - # ]; - # } - # ``` - condition ? true, }: + doRename = + { + # List of strings representing the attribute path of the old option. + from, + # List of strings representing the attribute path of the new option. + to, + # Boolean, whether the old option is to be included in documentation. + visible, + # Whether to warn when a value is defined for the old option. + # NOTE: This requires the NixOS assertions module to be imported, so + # - this generally does not work in submodules + # - this may or may not work outside NixOS + warn, + # A function that is applied to the option value, to form the value + # of the old `from` option. + # + # For example, the identity function can be passed, to return the option value unchanged. + # ```nix + # use = x: x; + # ``` + # + # To add a warning, you can pass the partially applied `warn` function. + # ```nix + # use = lib.warn "Obsolete option `${opt.old}' is used. Use `${opt.to}' instead."; + # ``` + use, + # Legacy option, enabled by default: whether to preserve the priority of definitions in `old`. + withPriority ? true, + # A boolean that defines the `mkIf` condition for `to`. + # If the condition evaluates to `true`, and the `to` path points into an + # `attrsOf (submodule ...)`, then `doRename` would cause an empty module to + # be created, even if the `from` option is undefined. + # By setting this to an expression that may return `false`, you can inhibit + # this undesired behavior. + # + # Example: + # + # ```nix + # { config, lib, ... }: + # let + # inherit (lib) mkOption mkEnableOption types doRename; + # in + # { + # options = { + # + # # Old service + # services.foo.enable = mkEnableOption "foo"; + # + # # New multi-instance service + # services.foos = mkOption { + # type = types.attrsOf (types.submodule …); + # }; + # }; + # imports = [ + # (doRename { + # from = [ "services" "foo" "bar" ]; + # to = [ "services" "foos" "" "bar" ]; + # visible = true; + # warn = false; + # use = x: x; + # withPriority = true; + # # Only define services.foos."" if needed. (It's not just about `bar`) + # condition = config.services.foo.enable; + # }) + # ]; + # } + # ``` + condition ? true, + }: { config, options, ... }: let fromOpt = getAttrFromPath from options; - toOf = attrByPath to - (abort "Renaming error: option `${showOption to}' does not exist."); - toType = let opt = attrByPath to { } options; - in opt.type or (types.submodule { }); - in { - options = setAttrByPath from (mkOption { - inherit visible; - description = "Alias of {option}`${showOption to}`."; - apply = x: use (toOf config); - } // optionalAttrs (toType != null) { type = toType; }); + toOf = attrByPath to (abort "Renaming error: option `${showOption to}' does not exist."); + toType = + let + opt = attrByPath to { } options; + in + opt.type or (types.submodule { }); + in + { + options = setAttrByPath from ( + mkOption { + inherit visible; + description = "Alias of {option}`${showOption to}`."; + apply = x: use (toOf config); + } + // optionalAttrs (toType != null) { type = toType; } + ); config = mkIf condition (mkMerge [ (optionalAttrs (options ? warnings) { - warnings = optional (warn && fromOpt.isDefined) - "The option `${showOption from}' defined in ${ - showFiles fromOpt.files - } has been renamed to `${showOption to}'."; + warnings = + optional (warn && fromOpt.isDefined) + "The option `${showOption from}' defined in ${showFiles fromOpt.files} has been renamed to `${showOption to}'."; }) - (if withPriority then - mkAliasAndWrapDefsWithPriority (setAttrByPath to) fromOpt - else - mkAliasAndWrapDefinitions (setAttrByPath to) fromOpt) + ( + if withPriority then + mkAliasAndWrapDefsWithPriority (setAttrByPath to) fromOpt + else + mkAliasAndWrapDefinitions (setAttrByPath to) fromOpt + ) ]); }; - /* Use this function to import a JSON file as NixOS configuration. + /* + Use this function to import a JSON file as NixOS configuration. - modules.importJSON :: path -> attrs + modules.importJSON :: path -> attrs */ importJSON = file: { _file = file; config = lib.importJSON file; }; - /* Use this function to import a TOML file as NixOS configuration. + /* + Use this function to import a TOML file as NixOS configuration. - modules.importTOML :: path -> attrs + modules.importTOML :: path -> attrs */ importTOML = file: { _file = file; config = lib.importTOML file; }; - private = lib.mapAttrs (k: - lib.warn - "External use of `lib.modules.${k}` 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/.") { - inherit applyModuleArgsIfFunction dischargeProperties mergeModules - mergeModules' pushDownProperties unifyModuleSyntax; - collectModules = collectModules null; - }; -in private // { + private = + lib.mapAttrs + ( + k: + lib.warn "External use of `lib.modules.${k}` 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/." + ) + { + inherit + applyModuleArgsIfFunction + dischargeProperties + mergeModules + mergeModules' + pushDownProperties + unifyModuleSyntax + ; + collectModules = collectModules null; + }; +in +private +// { # NOTE: not all of these functions are necessarily public interfaces; some # are just needed by types.nix, but are not meant to be consumed # externally. - inherit defaultOrderPriority defaultOverridePriority defaultPriority doRename - evalModules evalOptionValue # for use by lib.types - filterOverrides filterOverrides' fixMergeModules + inherit + defaultOrderPriority + defaultOverridePriority + defaultPriority + doRename + evalModules + evalOptionValue # for use by lib.types + filterOverrides + filterOverrides' + fixMergeModules fixupOptionType # should be private? - importJSON importTOML mergeDefinitions mergeAttrDefinitionsWithPrio + importJSON + importTOML + mergeDefinitions + mergeAttrDefinitionsWithPrio mergeOptionDecls # should be private? - mkAfter mkAliasAndWrapDefinitions mkAliasAndWrapDefsWithPriority - mkAliasDefinitions mkAliasIfDef mkAliasOptionModule mkAliasOptionModuleMD - mkAssert mkBefore mkChangedOptionModule mkDefault mkDerivedConfig - mkFixStrictness mkForce mkIf mkImageMediaOverride mkMerge - mkMergedOptionModule mkOptionDefault mkOrder mkOverride - mkRemovedOptionModule mkRenamedOptionModule mkRenamedOptionModuleWith - mkVMOverride setDefaultModuleLocation sortProperties; + mkAfter + mkAliasAndWrapDefinitions + mkAliasAndWrapDefsWithPriority + mkAliasDefinitions + mkAliasIfDef + mkAliasOptionModule + mkAliasOptionModuleMD + mkAssert + mkBefore + mkChangedOptionModule + mkDefault + mkDerivedConfig + mkFixStrictness + mkForce + mkIf + mkImageMediaOverride + mkMerge + mkMergedOptionModule + mkOptionDefault + mkOrder + mkOverride + mkRemovedOptionModule + mkRenamedOptionModule + mkRenamedOptionModuleWith + mkVMOverride + setDefaultModuleLocation + sortProperties + ; } diff --git a/nix/stdlib/options.nix b/nix/stdlib/options.nix index 57ce8af..96b78e9 100644 --- a/nix/stdlib/options.nix +++ b/nix/stdlib/options.nix @@ -2,9 +2,28 @@ { lib }: let inherit (lib) - all collect concatLists concatMap concatMapStringsSep filter foldl' head - tail isAttrs isBool isDerivation isFunction isInt isList isString length - mapAttrs optional optionals take; + all + collect + concatLists + concatMap + concatMapStringsSep + filter + foldl' + head + tail + isAttrs + isBool + isDerivation + isFunction + isInt + isList + isString + length + mapAttrs + optional + optionals + take + ; inherit (lib.attrsets) attrByPath optionalAttrs; inherit (lib.strings) concatMapStrings concatStringsSep; inherit (lib.types) mkOptionType; @@ -12,56 +31,60 @@ let prioritySuggestion = '' Use `lib.mkForce value` or `lib.mkDefault value` to change the priority on any of these definitions. ''; -in rec { - /* Returns true when the given argument is an option +in +rec { + /* + Returns true when the given argument is an option - Type: isOption :: a -> bool + Type: isOption :: a -> bool - Example: - isOption 1 // => false - isOption (mkOption {}) // => true + Example: + isOption 1 // => false + isOption (mkOption {}) // => true */ isOption = lib.isType "option"; - /* Creates an Option attribute set. mkOption accepts an attribute set with the following keys: + /* + Creates an Option attribute set. mkOption accepts an attribute set with the following keys: - All keys default to `null` when not given. + All keys default to `null` when not given. - Example: - mkOption { } // => { _type = "option"; } - mkOption { default = "foo"; } // => { _type = "option"; default = "foo"; } + Example: + mkOption { } // => { _type = "option"; } + mkOption { default = "foo"; } // => { _type = "option"; default = "foo"; } */ - mkOption = { - # Default value used when no definition is given in the configuration. - default ? null, - # Textual representation of the default, for the manual. - defaultText ? null, - # Example value used in the manual. - example ? null, - # String describing the option. - description ? null, - # Related packages used in the manual (see `genRelatedPackages` in ../nixos/lib/make-options-doc/default.nix). - relatedPackages ? null, - # Option type, providing type-checking and value merging. - type ? null, - # Function that converts the option value to something else. - apply ? null, - # Whether the option is for NixOS developers only. - internal ? null, - # Whether the option shows up in the manual. Default: true. Use false to hide the option and any sub-options from submodules. Use "shallow" to hide only sub-options. - visible ? null, - # Whether the option can be set only once - readOnly ? null, }@attrs: - attrs // { - _type = "option"; - }; - - /* Creates an Option attribute set for a boolean value option i.e an - option to be toggled on or off: - - Example: - mkEnableOption "foo" - => { _type = "option"; default = false; description = "Whether to enable foo."; example = true; type = { ... }; } + mkOption = + { + # Default value used when no definition is given in the configuration. + default ? null, + # Textual representation of the default, for the manual. + defaultText ? null, + # Example value used in the manual. + example ? null, + # String describing the option. + description ? null, + # Related packages used in the manual (see `genRelatedPackages` in ../nixos/lib/make-options-doc/default.nix). + relatedPackages ? null, + # Option type, providing type-checking and value merging. + type ? null, + # Function that converts the option value to something else. + apply ? null, + # Whether the option is for NixOS developers only. + internal ? null, + # Whether the option shows up in the manual. Default: true. Use false to hide the option and any sub-options from submodules. Use "shallow" to hide only sub-options. + visible ? null, + # Whether the option can be set only once + readOnly ? null, + }@attrs: + attrs // { _type = "option"; }; + + /* + Creates an Option attribute set for a boolean value option i.e an + option to be toggled on or off: + + Example: + mkEnableOption "foo" + => { _type = "option"; default = false; description = "Whether to enable foo."; example = true; type = { ... }; } */ mkEnableOption = # Name for the created option @@ -73,83 +96,84 @@ in rec { type = lib.types.bool; }; - /* Creates an Option attribute set for an option that specifies the - package a module should use for some purpose. - - The package is specified in the third argument under `default` as a list of strings - representing its attribute path in nixpkgs (or another package set). - Because of this, you need to pass nixpkgs itself (usually `pkgs` in a module; - alternatively to nixpkgs itself, another package set) as the first argument. - - If you pass another package set you should set the `pkgsText` option. - This option is used to display the expression for the package set. It is `"pkgs"` by default. - If your expression is complex you should parenthesize it, as the `pkgsText` argument - is usually immediately followed by an attribute lookup (`.`). - - The second argument may be either a string or a list of strings. - It provides the display name of the package in the description of the generated option - (using only the last element if the passed value is a list) - and serves as the fallback value for the `default` argument. - - To include extra information in the description, pass `extraDescription` to - append arbitrary text to the generated description. - - You can also pass an `example` value, either a literal string or an attribute path. - - The `default` argument can be omitted if the provided name is - an attribute of pkgs (if `name` is a string) or a valid attribute path in pkgs (if `name` is a list). - You can also set `default` to just a string in which case it is interpreted as an attribute name - (a singleton attribute path, if you will). - - If you wish to explicitly provide no default, pass `null` as `default`. - - If you want users to be able to set no package, pass `nullable = true`. - In this mode a `default = null` will not be interpreted as no default and is interpreted literally. - - Type: mkPackageOption :: pkgs -> (string|[string]) -> { nullable? :: bool, default? :: string|[string], example? :: null|string|[string], extraDescription? :: string, pkgsText? :: string } -> option - - Example: - mkPackageOption pkgs "hello" { } - => { ...; default = pkgs.hello; defaultText = literalExpression "pkgs.hello"; description = "The hello package to use."; type = package; } - - Example: - mkPackageOption pkgs "GHC" { - default = [ "ghc" ]; - example = "pkgs.haskell.packages.ghc92.ghc.withPackages (hkgs: [ hkgs.primes ])"; - } - => { ...; default = pkgs.ghc; defaultText = literalExpression "pkgs.ghc"; description = "The GHC package to use."; example = literalExpression "pkgs.haskell.packages.ghc92.ghc.withPackages (hkgs: [ hkgs.primes ])"; type = package; } - - Example: - mkPackageOption pkgs [ "python3Packages" "pytorch" ] { - extraDescription = "This is an example and doesn't actually do anything."; - } - => { ...; default = pkgs.python3Packages.pytorch; defaultText = literalExpression "pkgs.python3Packages.pytorch"; description = "The pytorch package to use. This is an example and doesn't actually do anything."; type = package; } - - Example: - mkPackageOption pkgs "nushell" { - nullable = true; - } - => { ...; default = pkgs.nushell; defaultText = literalExpression "pkgs.nushell"; description = "The nushell package to use."; type = nullOr package; } - - Example: - mkPackageOption pkgs "coreutils" { - default = null; - } - => { ...; description = "The coreutils package to use."; type = package; } - - Example: - mkPackageOption pkgs "dbus" { - nullable = true; - default = null; - } - => { ...; default = null; description = "The dbus package to use."; type = nullOr package; } - - Example: - mkPackageOption pkgs.javaPackages "OpenJFX" { - default = "openjfx20"; - pkgsText = "pkgs.javaPackages"; - } - => { ...; default = pkgs.javaPackages.openjfx20; defaultText = literalExpression "pkgs.javaPackages.openjfx20"; description = "The OpenJFX package to use."; type = package; } + /* + Creates an Option attribute set for an option that specifies the + package a module should use for some purpose. + + The package is specified in the third argument under `default` as a list of strings + representing its attribute path in nixpkgs (or another package set). + Because of this, you need to pass nixpkgs itself (usually `pkgs` in a module; + alternatively to nixpkgs itself, another package set) as the first argument. + + If you pass another package set you should set the `pkgsText` option. + This option is used to display the expression for the package set. It is `"pkgs"` by default. + If your expression is complex you should parenthesize it, as the `pkgsText` argument + is usually immediately followed by an attribute lookup (`.`). + + The second argument may be either a string or a list of strings. + It provides the display name of the package in the description of the generated option + (using only the last element if the passed value is a list) + and serves as the fallback value for the `default` argument. + + To include extra information in the description, pass `extraDescription` to + append arbitrary text to the generated description. + + You can also pass an `example` value, either a literal string or an attribute path. + + The `default` argument can be omitted if the provided name is + an attribute of pkgs (if `name` is a string) or a valid attribute path in pkgs (if `name` is a list). + You can also set `default` to just a string in which case it is interpreted as an attribute name + (a singleton attribute path, if you will). + + If you wish to explicitly provide no default, pass `null` as `default`. + + If you want users to be able to set no package, pass `nullable = true`. + In this mode a `default = null` will not be interpreted as no default and is interpreted literally. + + Type: mkPackageOption :: pkgs -> (string|[string]) -> { nullable? :: bool, default? :: string|[string], example? :: null|string|[string], extraDescription? :: string, pkgsText? :: string } -> option + + Example: + mkPackageOption pkgs "hello" { } + => { ...; default = pkgs.hello; defaultText = literalExpression "pkgs.hello"; description = "The hello package to use."; type = package; } + + Example: + mkPackageOption pkgs "GHC" { + default = [ "ghc" ]; + example = "pkgs.haskell.packages.ghc92.ghc.withPackages (hkgs: [ hkgs.primes ])"; + } + => { ...; default = pkgs.ghc; defaultText = literalExpression "pkgs.ghc"; description = "The GHC package to use."; example = literalExpression "pkgs.haskell.packages.ghc92.ghc.withPackages (hkgs: [ hkgs.primes ])"; type = package; } + + Example: + mkPackageOption pkgs [ "python3Packages" "pytorch" ] { + extraDescription = "This is an example and doesn't actually do anything."; + } + => { ...; default = pkgs.python3Packages.pytorch; defaultText = literalExpression "pkgs.python3Packages.pytorch"; description = "The pytorch package to use. This is an example and doesn't actually do anything."; type = package; } + + Example: + mkPackageOption pkgs "nushell" { + nullable = true; + } + => { ...; default = pkgs.nushell; defaultText = literalExpression "pkgs.nushell"; description = "The nushell package to use."; type = nullOr package; } + + Example: + mkPackageOption pkgs "coreutils" { + default = null; + } + => { ...; description = "The coreutils package to use."; type = package; } + + Example: + mkPackageOption pkgs "dbus" { + nullable = true; + default = null; + } + => { ...; default = null; description = "The dbus package to use."; type = nullOr package; } + + Example: + mkPackageOption pkgs.javaPackages "OpenJFX" { + default = "openjfx20"; + pkgsText = "pkgs.javaPackages"; + } + => { ...; default = pkgs.javaPackages.openjfx20; defaultText = literalExpression "pkgs.javaPackages.openjfx20"; description = "The OpenJFX package to use."; type = package; } */ mkPackageOption = # Package set (an instantiation of nixpkgs such as pkgs in modules or another package set) @@ -157,68 +181,82 @@ in rec { # Name for the package, shown in option description name: { - # Whether the package can be null, for example to disable installing a package altogether (defaults to false) - nullable ? false, - # The attribute path where the default package is located (may be omitted, in which case it is copied from `name`) - default ? name, - # A string or an attribute path to use as an example (may be omitted) - example ? null, - # Additional text to include in the option description (may be omitted) - extraDescription ? "", - # Representation of the package set passed as pkgs (defaults to `"pkgs"`) - pkgsText ? "pkgs", }: + # Whether the package can be null, for example to disable installing a package altogether (defaults to false) + nullable ? false, + # The attribute path where the default package is located (may be omitted, in which case it is copied from `name`) + default ? name, + # A string or an attribute path to use as an example (may be omitted) + example ? null, + # Additional text to include in the option description (may be omitted) + extraDescription ? "", + # Representation of the package set passed as pkgs (defaults to `"pkgs"`) + pkgsText ? "pkgs", + }: let name' = if isList name then last name else name; default' = if isList default then default else [ default ]; defaultText = concatStringsSep "." default'; - defaultValue = attrByPath default' - (throw "${defaultText} cannot be found in ${pkgsText}") pkgs; - defaults = if default != null then { - default = defaultValue; - defaultText = literalExpression ("${pkgsText}." + defaultText); - } else - optionalAttrs nullable { default = null; }; - in mkOption (defaults // { - description = "The ${name'} package to use." - + (if extraDescription == "" then "" else " ") + extraDescription; - type = with lib.types; (if nullable then nullOr else lib.id) package; - } // optionalAttrs (example != null) { - example = literalExpression (if isList example then - "${pkgsText}." + concatStringsSep "." example - else - example); - }); - - /* Alias of mkPackageOption. Previously used to create options with markdown - documentation, which is no longer required. + defaultValue = attrByPath default' (throw "${defaultText} cannot be found in ${pkgsText}") pkgs; + defaults = + if default != null then + { + default = defaultValue; + defaultText = literalExpression ("${pkgsText}." + defaultText); + } + else + optionalAttrs nullable { default = null; }; + in + mkOption ( + defaults + // { + description = + "The ${name'} package to use." + (if extraDescription == "" then "" else " ") + extraDescription; + type = with lib.types; (if nullable then nullOr else lib.id) package; + } + // optionalAttrs (example != null) { + example = literalExpression ( + if isList example then "${pkgsText}." + concatStringsSep "." example else example + ); + } + ); + + /* + Alias of mkPackageOption. Previously used to create options with markdown + documentation, which is no longer required. */ mkPackageOptionMD = mkPackageOption; - /* This option accepts anything, but it does not produce any result. + /* + This option accepts anything, but it does not produce any result. - This is useful for sharing a module across different module sets - without having to implement similar features as long as the - values of the options are not accessed. + This is useful for sharing a module across different module sets + without having to implement similar features as long as the + values of the options are not accessed. */ - mkSinkUndeclaredOptions = attrs: - mkOption ({ - internal = true; - visible = false; - default = false; - description = "Sink for option definitions."; - type = mkOptionType { - name = "sink"; - check = x: true; - merge = loc: defs: false; - }; - apply = x: - throw - "Option value is not readable because the option is not declared."; - } // attrs); - - mergeDefaultOption = loc: defs: - let list = getValues defs; - in if length list == 1 then + mkSinkUndeclaredOptions = + attrs: + mkOption ( + { + internal = true; + visible = false; + default = false; + description = "Sink for option definitions."; + type = mkOptionType { + name = "sink"; + check = x: true; + merge = loc: defs: false; + }; + apply = x: throw "Option value is not readable because the option is not declared."; + } + // attrs + ); + + mergeDefaultOption = + loc: defs: + let + list = getValues defs; + in + if length list == 1 then head list else if all isFunction list then x: mergeDefaultOption loc (map (f: f x) list) @@ -233,77 +271,85 @@ in rec { 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 - }"; + throw "Cannot merge definitions of `${showOption loc}'. Definition values:${showDefs defs}"; - /* Require a single definition. + /* + Require a single definition. - WARNING: Does not perform nested checks, as this does not run the merge function! + WARNING: Does not perform nested checks, as this does not run the merge function! */ mergeOneOption = mergeUniqueOption { message = ""; }; - /* Require a single definition. + /* + Require a single definition. - NOTE: When the type is not checked completely by check, pass a merge function for further checking (of sub-attributes, etc). + NOTE: When the type is not checked completely by check, pass a merge function for further checking (of sub-attributes, etc). */ - mergeUniqueOption = args@{ message, - # WARNING: the default merge function assumes that the definition is a valid (option) value. You MUST pass a merge function if the return value needs to be - # - type checked beyond what .check does (which should be very litte; only on the value head; not attribute values, etc) - # - if you want attribute values to be checked, or list items - # - if you want coercedTo-like behavior to work - merge ? loc: defs: (head defs).value, }: + mergeUniqueOption = + args@{ + message, + # WARNING: the default merge function assumes that the definition is a valid (option) value. You MUST pass a merge function if the return value needs to be + # - type checked beyond what .check does (which should be very litte; only on the value head; not attribute values, etc) + # - if you want attribute values to be checked, or list items + # - if you want coercedTo-like behavior to work + merge ? loc: defs: (head defs).value, + }: loc: defs: if length defs == 1 then merge loc defs else assert length defs > 1; throw '' - The option `${ - showOption loc - }' is defined multiple times while it's expected to be unique. + The option `${showOption loc}' is defined multiple times while it's expected to be unique. ${message} Definition values:${showDefs defs} ${prioritySuggestion}''; # "Merge" option definitions by checking that they all have the same value. - mergeEqualOption = loc: defs: + mergeEqualOption = + loc: defs: if defs == [ ] then abort "This case should never happen." - # Return early if we only have one element - # This also makes it work for functions, because the foldl' below would try - # to compare the first element with itself, which is false for functions + # Return early if we only have one element + # This also makes it work for functions, because the foldl' below would try + # to compare the first element with itself, which is false for functions else if length defs == 1 then (head defs).value else - (foldl' (first: def: + (foldl' ( + first: def: if def.value != first.value then throw '' The option `${showOption loc}' has conflicting definition values:${ - showDefs [ first def ] + showDefs [ + first + def + ] } ${prioritySuggestion}'' else - first) (head defs) (tail defs)).value; + first + ) (head defs) (tail defs)).value; - /* Extracts values of all "value" keys of the given list. + /* + Extracts values of all "value" keys of the given list. - Type: getValues :: [ { value :: a; } ] -> [a] + Type: getValues :: [ { value :: a; } ] -> [a] - Example: - getValues [ { value = 1; } { value = 2; } ] // => [ 1 2 ] - getValues [ ] // => [ ] + Example: + getValues [ { value = 1; } { value = 2; } ] // => [ 1 2 ] + getValues [ ] // => [ ] */ getValues = map (x: x.value); - /* Extracts values of all "file" keys of the given list + /* + Extracts values of all "file" keys of the given list - Type: getFiles :: [ { file :: a; } ] -> [a] + Type: getFiles :: [ { file :: a; } ] -> [a] - Example: - getFiles [ { file = "file1"; } { file = "file2"; } ] // => [ "file1" "file2" ] - getFiles [ ] // => [ ] + Example: + getFiles [ { file = "file1"; } { file = "file2"; } ] // => [ "file1" "file2" ] + getFiles [ ] // => [ ] */ getFiles = map (x: x.file); @@ -311,135 +357,154 @@ in rec { # the set generated with filterOptionSets. optionAttrSetToDocList = optionAttrSetToDocList' [ ]; - optionAttrSetToDocList' = _: options: - concatMap (opt: + optionAttrSetToDocList' = + _: options: + concatMap ( + opt: let name = showOption opt.loc; - docOption = { - loc = opt.loc; - inherit name; - description = opt.description or null; - declarations = filter (x: x != unknownModule) opt.declarations; - internal = opt.internal or false; - visible = if (opt ? visible && opt.visible == "shallow") then - true - else - opt.visible or true; - readOnly = opt.readOnly or false; - type = opt.type.description or "unspecified"; - } // optionalAttrs (opt ? example) { - example = builtins.addErrorContext - "while evaluating the example of option `${name}`" - (renderOptionValue opt.example); - } // optionalAttrs (opt ? defaultText || opt ? default) { - default = builtins.addErrorContext "while evaluating the ${ + docOption = + { + loc = opt.loc; + inherit name; + description = opt.description or null; + declarations = filter (x: x != unknownModule) opt.declarations; + internal = opt.internal or false; + visible = if (opt ? visible && opt.visible == "shallow") then true else opt.visible or true; + readOnly = opt.readOnly or false; + type = opt.type.description or "unspecified"; + } + // optionalAttrs (opt ? example) { + example = builtins.addErrorContext "while evaluating the example of option `${name}`" ( + renderOptionValue opt.example + ); + } + // optionalAttrs (opt ? defaultText || opt ? default) { + default = builtins.addErrorContext "while evaluating the ${ if opt ? defaultText then "defaultText" else "default value" - } of option `${name}`" - (renderOptionValue (opt.defaultText or opt.default)); - } // optionalAttrs - (opt ? relatedPackages && opt.relatedPackages != null) { + } of option `${name}`" (renderOptionValue (opt.defaultText or opt.default)); + } + // optionalAttrs (opt ? relatedPackages && opt.relatedPackages != null) { inherit (opt) relatedPackages; }; - subOptions = let ss = opt.type.getSubOptions opt.loc; - in if ss != { } then optionAttrSetToDocList' opt.loc ss else [ ]; - subOptionsVisible = docOption.visible && opt.visible or null - != "shallow"; - # To find infinite recursion in NixOS option docs: - # builtins.trace opt.loc - in [ docOption ] ++ optionals subOptionsVisible subOptions) - (collect isOption options); - - /* This function recursively removes all derivation attributes from - `x` except for the `name` attribute. - - This is to make the generation of `options.xml` much more - efficient: the XML representation of derivations is very large - (on the order of megabytes) and is not actually used by the - manual generator. - - This function was made obsolete by renderOptionValue and is kept for - compatibility with out-of-tree code. + subOptions = + let + ss = opt.type.getSubOptions opt.loc; + in + if ss != { } then optionAttrSetToDocList' opt.loc ss else [ ]; + subOptionsVisible = docOption.visible && opt.visible or null != "shallow"; + in + # To find infinite recursion in NixOS option docs: + # builtins.trace opt.loc + [ docOption ] ++ optionals subOptionsVisible subOptions + ) (collect isOption options); + + /* + This function recursively removes all derivation attributes from + `x` except for the `name` attribute. + + This is to make the generation of `options.xml` much more + efficient: the XML representation of derivations is very large + (on the order of megabytes) and is not actually used by the + manual generator. + + This function was made obsolete by renderOptionValue and is kept for + compatibility with out-of-tree code. */ - scrubOptionValue = x: - if isDerivation x then { - type = "derivation"; - drvPath = x.name; - outPath = x.name; - name = x.name; - } else if isList x then + scrubOptionValue = + x: + if isDerivation x then + { + type = "derivation"; + drvPath = x.name; + outPath = x.name; + name = x.name; + } + else if isList x then map scrubOptionValue x else if isAttrs x then mapAttrs (n: v: scrubOptionValue v) (removeAttrs x [ "_args" ]) else x; - /* Ensures that the given option value (default or example) is a `_type`d string - by rendering Nix values to `literalExpression`s. + /* + Ensures that the given option value (default or example) is a `_type`d string + by rendering Nix values to `literalExpression`s. */ - renderOptionValue = v: + renderOptionValue = + v: if v ? _type && v ? text then v else - literalExpression (lib.generators.toPretty { - multiline = true; - allowPrettyValues = true; - } v); - - /* For use in the `defaultText` and `example` option attributes. Causes the - given string to be rendered verbatim in the documentation as Nix code. This - is necessary for complex values, e.g. functions, or values that depend on - other values or packages. + literalExpression ( + lib.generators.toPretty { + multiline = true; + allowPrettyValues = true; + } v + ); + + /* + For use in the `defaultText` and `example` option attributes. Causes the + given string to be rendered verbatim in the documentation as Nix code. This + is necessary for complex values, e.g. functions, or values that depend on + other values or packages. */ - literalExpression = text: + literalExpression = + text: if !isString text then throw "literalExpression expects a string." - else { - _type = "literalExpression"; - inherit text; - }; + 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; - /* Transition marker for documentation that's already migrated to markdown - syntax. Has been a no-op for some while and been removed from nixpkgs. - Kept here to alert downstream users who may not be aware of the migration's - completion that it should be removed from modules. + /* + Transition marker for documentation that's already migrated to markdown + syntax. Has been a no-op for some while and been removed from nixpkgs. + Kept here to alert downstream users who may not be aware of the migration's + completion that it should be removed from modules. */ - mdDoc = lib.warn - "lib.mdDoc will be removed from nixpkgs in 24.11. Option descriptions are now in Markdown by default; you can remove any remaining uses of lib.mdDoc."; + mdDoc = lib.warn "lib.mdDoc will be removed from nixpkgs in 24.11. Option descriptions are now in Markdown by default; you can remove any remaining uses of lib.mdDoc."; - /* For use in the `defaultText` and `example` option attributes. Causes the - given MD text to be inserted verbatim in the documentation, for when - a `literalExpression` would be too hard to read. + /* + For use in the `defaultText` and `example` option attributes. Causes the + given MD text to be inserted verbatim in the documentation, for when + a `literalExpression` would be too hard to read. */ - literalMD = text: + literalMD = + text: if !isString text then throw "literalMD expects a string." - else { - _type = "literalMD"; - inherit text; - }; + else + { + _type = "literalMD"; + inherit text; + }; # Helper functions. - /* Convert an option, described as a list of the option parts to a - human-readable version. + /* + Convert an option, described as a list of the option parts to a + human-readable version. - Example: - (showOption ["foo" "bar" "baz"]) == "foo.bar.baz" - (showOption ["foo" "bar.baz" "tux"]) == "foo.\"bar.baz\".tux" - (showOption ["windowManager" "2bwm" "enable"]) == "windowManager.\"2bwm\".enable" + Example: + (showOption ["foo" "bar" "baz"]) == "foo.bar.baz" + (showOption ["foo" "bar.baz" "tux"]) == "foo.\"bar.baz\".tux" + (showOption ["windowManager" "2bwm" "enable"]) == "windowManager.\"2bwm\".enable" - Placeholders will not be quoted as they are not actual values: - (showOption ["foo" "*" "bar"]) == "foo.*.bar" - (showOption ["foo" "" "bar"]) == "foo..bar" + Placeholders will not be quoted as they are not actual values: + (showOption ["foo" "*" "bar"]) == "foo.*.bar" + (showOption ["foo" "" "bar"]) == "foo..bar" */ - showOption = parts: + showOption = + parts: let - escapeOptionPart = part: + escapeOptionPart = + part: let # We assume that these are "special values" and not real configuration data. # If it is real configuration data, it is rendered incorrectly. @@ -448,39 +513,44 @@ in rec { "*" # listOf (submodule {}) "" # functionTo ]; - in if builtins.elem part specialIdentifiers then - part - else - lib.strings.escapeNixIdentifier part; - in (concatStringsSep ".") (map escapeOptionPart parts); + in + if builtins.elem part specialIdentifiers then part else lib.strings.escapeNixIdentifier part; + in + (concatStringsSep ".") (map escapeOptionPart parts); showFiles = files: concatStringsSep " and " (map (f: "`${f}'") files); - showDefs = defs: - concatMapStrings (def: + showDefs = + defs: + concatMapStrings ( + def: let # Pretty print the value for display, if successful - prettyEval = builtins.tryEval (lib.generators.toPretty { } - (lib.generators.withRecursion { - depthLimit = 10; - throwOnDepthLimit = false; - } def.value)); + prettyEval = builtins.tryEval ( + lib.generators.toPretty { } ( + lib.generators.withRecursion { + depthLimit = 10; + throwOnDepthLimit = false; + } def.value + ) + ); # Split it into its lines lines = filter (v: !isList v) (builtins.split "\n" prettyEval.value); # Only display the first 5 lines, and indent them for better visibility - value = concatStringsSep "\n " - (take 5 lines ++ optional (length lines > 5) "..."); + value = concatStringsSep "\n " (take 5 lines ++ optional (length lines > 5) "..."); result = # Don't print any value if evaluating the value strictly fails if !prettyEval.success then "" - # Put it on a new line if it consists of multiple + # Put it on a new line if it consists of multiple else if length lines > 1 then ":\n " + value else ": " + value; - in '' + in + '' - - In `${def.file}'${result}'') defs; + - In `${def.file}'${result}'' + ) defs; showOptionWithDefLocs = opt: '' ${showOption opt.loc}, with values defined in: diff --git a/nix/stdlib/path/default.nix b/nix/stdlib/path/default.nix index 00c9618..ca8dfeb 100644 --- a/nix/stdlib/path/default.nix +++ b/nix/stdlib/path/default.nix @@ -2,10 +2,27 @@ # See ./README.md for internal docs { lib }: let - inherit (builtins) isString isPath split match typeOf storeDir; + inherit (builtins) + isString + isPath + split + match + typeOf + storeDir + ; inherit (lib.lists) - length head last genList elemAt all concatMap foldl' take drop; + length + head + last + genList + elemAt + all + concatMap + foldl' + take + drop + ; listHasPrefix = lib.lists.hasPrefix; @@ -16,26 +33,24 @@ let inherit (lib.path.subpath) isValid; # Return the reason why a subpath is invalid, or `null` if it's valid - subpathInvalidReason = value: + subpathInvalidReason = + value: if !isString value then - "The given value is of type ${ - builtins.typeOf value - }, but a string was expected" + "The given value is of type ${builtins.typeOf value}, but a string was expected" else if value == "" then "The given string is empty" else if substring 0 1 value == "/" then - '' - The given string "${value}" starts with a `/`, representing an absolute path'' - # We don't support ".." components, see ./path.md#parent-directory + ''The given string "${value}" starts with a `/`, representing an absolute path'' + # We don't support ".." components, see ./path.md#parent-directory else if match "(.*/)?\\.\\.(/.*)?" value != null then - '' - The given string "${value}" contains a `..` component, which is not allowed in subpaths'' + ''The given string "${value}" contains a `..` component, which is not allowed in subpaths'' else null; # Split and normalise a relative path string into its components. # Error for ".." components and doesn't include "." components - splitRelPath = path: + splitRelPath = + path: let # Split the string into its parts using regex for efficiency. This regex # matches patterns like "/", "/./", "/././", with arbitrarily many "/"s @@ -64,26 +79,31 @@ let # We can now know the length of the result by removing the number of # skipped parts from the total number componentCount = partCount - skipEnd - skipStart; - # Special case of a single "." path component. Such a case leaves a - # componentCount of -1 due to the skipStart/skipEnd not verifying that - # they don't refer to the same character - in if path == "." then + in + # Special case of a single "." path component. Such a case leaves a + # componentCount of -1 due to the skipStart/skipEnd not verifying that + # they don't refer to the same character + if path == "." then [ ] - # Generate the result list directly. This is more efficient than a - # combination of `filter`, `init` and `tail`, because here we don't - # allocate any intermediate lists + # Generate the result list directly. This is more efficient than a + # combination of `filter`, `init` and `tail`, because here we don't + # allocate any intermediate lists else - genList (index: + genList ( + index: # To get to the element we need to add the number of parts we skip and # multiply by two due to the interleaved layout of `parts` - elemAt parts ((skipStart + index) * 2)) componentCount; + elemAt parts ((skipStart + index) * 2) + ) componentCount; # Join relative path components together - joinRelPath = components: + joinRelPath = + components: # Always return relative paths with `./` as a prefix (./path.md#leading-dots-for-relative-paths) - "./" + - # An empty string is not a valid relative path, so we need to return a `.` when we have no components - (if components == [ ] then "." else concatStringsSep "/" components); + "./" + + + # An empty string is not a valid relative path, so we need to return a `.` when we have no components + (if components == [ ] then "." else concatStringsSep "/" components); # Type: Path -> { root :: Path, components :: [ String ] } # @@ -93,15 +113,20 @@ let # # This is similar to `splitString "/" (toString path)` but safer # because it can distinguish different filesystem roots - deconstructPath = let - recurse = components: base: - # If the parent of a path is the path itself, then it's a filesystem root - if base == dirOf base then { - root = base; - inherit components; - } else - recurse ([ (baseNameOf base) ] ++ components) (dirOf base); - in recurse [ ]; + deconstructPath = + let + recurse = + components: base: + # If the parent of a path is the path itself, then it's a filesystem root + if base == dirOf base then + { + root = base; + inherit components; + } + else + recurse ([ (baseNameOf base) ] ++ components) (dirOf base); + in + recurse [ ]; # The components of the store directory, typically [ "nix" "store" ] storeDirComponents = splitRelPath ("./" + storeDir); @@ -112,7 +137,8 @@ let # # Whether path components have a store path as a prefix, according to # https://nixos.org/manual/nix/stable/store/store-path.html#store-path. - componentsHaveStorePathPrefix = components: + componentsHaveStorePathPrefix = + components: # path starts with the store directory (typically /nix/store) listHasPrefix storeDirComponents components # is not the store directory itself, meaning there's at least one extra component @@ -124,48 +150,50 @@ let # Similar reasoning applies to the validity of the name part. # We care more about discerning store path-ness on realistic values. Making it airtight would be fragile and slow. && match ".{32}-.+" (elemAt components storeDirLength) != null; - # No rec! Add dependencies on this file at the top. -in { - /* Append a subpath string to a path. - - Like `path + ("/" + string)` but safer, because it errors instead of returning potentially surprising results. - More specifically, it checks that the first argument is a [path value type](https://nixos.org/manual/nix/stable/language/values.html#type-path"), - and that the second argument is a [valid subpath string](#function-library-lib.path.subpath.isValid). - - Laws: - - - Not influenced by subpath [normalisation](#function-library-lib.path.subpath.normalise): - - append p s == append p (subpath.normalise s) - - Type: - append :: Path -> String -> Path - - Example: - append /foo "bar/baz" - => /foo/bar/baz - - # subpaths don't need to be normalised - append /foo "./bar//baz/./" - => /foo/bar/baz - - # can append to root directory - append /. "foo/bar" - => /foo/bar - - # first argument needs to be a path value type - append "/foo" "bar" - => - - # second argument needs to be a valid subpath string - append /foo /bar - => - append /foo "" - => - append /foo "/bar" - => - append /foo "../bar" - => +in +# No rec! Add dependencies on this file at the top. +{ + /* + Append a subpath string to a path. + + Like `path + ("/" + string)` but safer, because it errors instead of returning potentially surprising results. + More specifically, it checks that the first argument is a [path value type](https://nixos.org/manual/nix/stable/language/values.html#type-path"), + and that the second argument is a [valid subpath string](#function-library-lib.path.subpath.isValid). + + Laws: + + - Not influenced by subpath [normalisation](#function-library-lib.path.subpath.normalise): + + append p s == append p (subpath.normalise s) + + Type: + append :: Path -> String -> Path + + Example: + append /foo "bar/baz" + => /foo/bar/baz + + # subpaths don't need to be normalised + append /foo "./bar//baz/./" + => /foo/bar/baz + + # can append to root directory + append /. "foo/bar" + => /foo/bar + + # first argument needs to be a path value type + append "/foo" "bar" + => + + # second argument needs to be a valid subpath string + append /foo /bar + => + append /foo "" + => + append /foo "/bar" + => + append /foo "../bar" + => */ append = # The absolute path to append to @@ -173,319 +201,309 @@ in { # The subpath string to append subpath: assert assertMsg (isPath path) - "lib.path.append: The first argument is of type ${ - builtins.typeOf path - }, but a path was expected"; + "lib.path.append: The first argument is of type ${builtins.typeOf path}, but a path was expected"; assert assertMsg (isValid subpath) '' lib.path.append: Second argument is not a valid subpath string: ${subpathInvalidReason subpath}''; path + ("/" + subpath); - /* Whether the first path is a component-wise prefix of the second path. + /* + Whether the first path is a component-wise prefix of the second path. - Laws: + Laws: - - `hasPrefix p q` is only true if [`q == append p s`](#function-library-lib.path.append) for some [subpath](#function-library-lib.path.subpath.isValid) `s`. + - `hasPrefix p q` is only true if [`q == append p s`](#function-library-lib.path.append) for some [subpath](#function-library-lib.path.subpath.isValid) `s`. - - `hasPrefix` is a [non-strict partial order](https://en.wikipedia.org/wiki/Partially_ordered_set#Non-strict_partial_order) over the set of all path values. + - `hasPrefix` is a [non-strict partial order](https://en.wikipedia.org/wiki/Partially_ordered_set#Non-strict_partial_order) over the set of all path values. - Type: - hasPrefix :: Path -> Path -> Bool + Type: + hasPrefix :: Path -> Path -> Bool - Example: - hasPrefix /foo /foo/bar - => true - hasPrefix /foo /foo - => true - hasPrefix /foo/bar /foo - => false - hasPrefix /. /foo - => true + Example: + hasPrefix /foo /foo/bar + => true + hasPrefix /foo /foo + => true + hasPrefix /foo/bar /foo + => false + hasPrefix /. /foo + => true */ - hasPrefix = path1: + hasPrefix = + path1: assert assertMsg (isPath path1) - "lib.path.hasPrefix: First argument is of type ${ - typeOf path1 - }, but a path was expected"; - let path1Deconstructed = deconstructPath path1; - in path2: + "lib.path.hasPrefix: First argument is of type ${typeOf path1}, but a path was expected"; + let + path1Deconstructed = deconstructPath path1; + in + path2: assert assertMsg (isPath path2) - "lib.path.hasPrefix: Second argument is of type ${ - typeOf path2 - }, but a path was expected"; - let path2Deconstructed = deconstructPath path2; - in assert assertMsg (path1Deconstructed.root == path2Deconstructed.root) '' + "lib.path.hasPrefix: Second argument is of type ${typeOf path2}, but a path was expected"; + let + path2Deconstructed = deconstructPath path2; + in + assert assertMsg (path1Deconstructed.root == path2Deconstructed.root) '' lib.path.hasPrefix: Filesystem roots must be the same for both paths, but paths with different roots were given: - first argument: "${toString path1}" with root "${ - toString path1Deconstructed.root - }" - second argument: "${toString path2}" with root "${ - toString path2Deconstructed.root - }"''; + first argument: "${toString path1}" with root "${toString path1Deconstructed.root}" + second argument: "${toString path2}" with root "${toString path2Deconstructed.root}"''; take (length path1Deconstructed.components) path2Deconstructed.components == path1Deconstructed.components; - /* Remove the first path as a component-wise prefix from the second path. - The result is a [normalised subpath string](#function-library-lib.path.subpath.normalise). + /* + Remove the first path as a component-wise prefix from the second path. + The result is a [normalised subpath string](#function-library-lib.path.subpath.normalise). - Laws: + Laws: - - Inverts [`append`](#function-library-lib.path.append) for [normalised subpath string](#function-library-lib.path.subpath.normalise): + - Inverts [`append`](#function-library-lib.path.append) for [normalised subpath string](#function-library-lib.path.subpath.normalise): - removePrefix p (append p s) == subpath.normalise s + removePrefix p (append p s) == subpath.normalise s - Type: - removePrefix :: Path -> Path -> String + Type: + removePrefix :: Path -> Path -> String - Example: - removePrefix /foo /foo/bar/baz - => "./bar/baz" - removePrefix /foo /foo - => "./." - removePrefix /foo/bar /foo - => - removePrefix /. /foo - => "./foo" + Example: + removePrefix /foo /foo/bar/baz + => "./bar/baz" + removePrefix /foo /foo + => "./." + removePrefix /foo/bar /foo + => + removePrefix /. /foo + => "./foo" */ - removePrefix = path1: + removePrefix = + path1: assert assertMsg (isPath path1) - "lib.path.removePrefix: First argument is of type ${ - typeOf path1 - }, but a path was expected."; + "lib.path.removePrefix: First argument is of type ${typeOf path1}, but a path was expected."; let path1Deconstructed = deconstructPath path1; path1Length = length path1Deconstructed.components; - in path2: + in + path2: assert assertMsg (isPath path2) - "lib.path.removePrefix: Second argument is of type ${ - typeOf path2 - }, but a path was expected."; + "lib.path.removePrefix: Second argument is of type ${typeOf path2}, but a path was expected."; let path2Deconstructed = deconstructPath path2; - success = take path1Length path2Deconstructed.components - == path1Deconstructed.components; - components = if success then - drop path1Length path2Deconstructed.components - else - throw '' - lib.path.removePrefix: The first path argument "${ - toString path1 - }" is not a component-wise prefix of the second path argument "${ - toString path2 - }".''; - in assert assertMsg (path1Deconstructed.root == path2Deconstructed.root) '' + success = take path1Length path2Deconstructed.components == path1Deconstructed.components; + components = + if success then + drop path1Length path2Deconstructed.components + else + throw ''lib.path.removePrefix: The first path argument "${toString path1}" is not a component-wise prefix of the second path argument "${toString path2}".''; + in + assert assertMsg (path1Deconstructed.root == path2Deconstructed.root) '' lib.path.removePrefix: Filesystem roots must be the same for both paths, but paths with different roots were given: - first argument: "${toString path1}" with root "${ - toString path1Deconstructed.root - }" - second argument: "${toString path2}" with root "${ - toString path2Deconstructed.root - }"''; + first argument: "${toString path1}" with root "${toString path1Deconstructed.root}" + second argument: "${toString path2}" with root "${toString path2Deconstructed.root}"''; joinRelPath components; - /* Split the filesystem root from a [path](https://nixos.org/manual/nix/stable/language/values.html#type-path). - The result is an attribute set with these attributes: - - `root`: The filesystem root of the path, meaning that this directory has no parent directory. - - `subpath`: The [normalised subpath string](#function-library-lib.path.subpath.normalise) that when [appended](#function-library-lib.path.append) to `root` returns the original path. + /* + Split the filesystem root from a [path](https://nixos.org/manual/nix/stable/language/values.html#type-path). + The result is an attribute set with these attributes: + - `root`: The filesystem root of the path, meaning that this directory has no parent directory. + - `subpath`: The [normalised subpath string](#function-library-lib.path.subpath.normalise) that when [appended](#function-library-lib.path.append) to `root` returns the original path. - Laws: - - [Appending](#function-library-lib.path.append) the `root` and `subpath` gives the original path: + Laws: + - [Appending](#function-library-lib.path.append) the `root` and `subpath` gives the original path: - p == - append - (splitRoot p).root - (splitRoot p).subpath + p == + append + (splitRoot p).root + (splitRoot p).subpath - - Trying to get the parent directory of `root` using [`readDir`](https://nixos.org/manual/nix/stable/language/builtins.html#builtins-readDir) returns `root` itself: + - Trying to get the parent directory of `root` using [`readDir`](https://nixos.org/manual/nix/stable/language/builtins.html#builtins-readDir) returns `root` itself: - dirOf (splitRoot p).root == (splitRoot p).root + dirOf (splitRoot p).root == (splitRoot p).root - Type: - splitRoot :: Path -> { root :: Path, subpath :: String } + Type: + splitRoot :: Path -> { root :: Path, subpath :: String } - Example: - splitRoot /foo/bar - => { root = /.; subpath = "./foo/bar"; } + Example: + splitRoot /foo/bar + => { root = /.; subpath = "./foo/bar"; } - splitRoot /. - => { root = /.; subpath = "./."; } + splitRoot /. + => { root = /.; subpath = "./."; } - # Nix neutralises `..` path components for all path values automatically - splitRoot /foo/../bar - => { root = /.; subpath = "./bar"; } + # Nix neutralises `..` path components for all path values automatically + splitRoot /foo/../bar + => { root = /.; subpath = "./bar"; } - splitRoot "/foo/bar" - => + splitRoot "/foo/bar" + => */ splitRoot = # The path to split the root off of path: - assert assertMsg (isPath path) "lib.path.splitRoot: Argument is of type ${ - typeOf path - }, but a path was expected"; - let deconstructed = deconstructPath path; - in { + assert assertMsg (isPath path) + "lib.path.splitRoot: Argument is of type ${typeOf path}, but a path was expected"; + let + deconstructed = deconstructPath path; + in + { root = deconstructed.root; subpath = joinRelPath deconstructed.components; }; - /* Whether a [path](https://nixos.org/manual/nix/stable/language/values.html#type-path) - has a [store path](https://nixos.org/manual/nix/stable/store/store-path.html#store-path) - as a prefix. + /* + Whether a [path](https://nixos.org/manual/nix/stable/language/values.html#type-path) + has a [store path](https://nixos.org/manual/nix/stable/store/store-path.html#store-path) + as a prefix. - :::{.note} - As with all functions of this `lib.path` library, it does not work on paths in strings, - which is how you'd typically get store paths. + :::{.note} + As with all functions of this `lib.path` library, it does not work on paths in strings, + which is how you'd typically get store paths. - Instead, this function only handles path values themselves, - which occur when Nix files in the store use relative path expressions. - ::: + Instead, this function only handles path values themselves, + which occur when Nix files in the store use relative path expressions. + ::: - Type: - hasStorePathPrefix :: Path -> Bool + Type: + hasStorePathPrefix :: Path -> Bool - Example: - # Subpaths of derivation outputs have a store path as a prefix - hasStorePathPrefix /nix/store/nvl9ic0pj1fpyln3zaqrf4cclbqdfn1j-foo/bar/baz - => true + Example: + # Subpaths of derivation outputs have a store path as a prefix + hasStorePathPrefix /nix/store/nvl9ic0pj1fpyln3zaqrf4cclbqdfn1j-foo/bar/baz + => true - # The store directory itself is not a store path - hasStorePathPrefix /nix/store - => false + # The store directory itself is not a store path + hasStorePathPrefix /nix/store + => false - # Derivation outputs are store paths themselves - hasStorePathPrefix /nix/store/nvl9ic0pj1fpyln3zaqrf4cclbqdfn1j-foo - => true + # Derivation outputs are store paths themselves + hasStorePathPrefix /nix/store/nvl9ic0pj1fpyln3zaqrf4cclbqdfn1j-foo + => true - # Paths outside the Nix store don't have a store path prefix - hasStorePathPrefix /home/user - => false + # Paths outside the Nix store don't have a store path prefix + hasStorePathPrefix /home/user + => false - # Not all paths under the Nix store are store paths - hasStorePathPrefix /nix/store/.links/10gg8k3rmbw8p7gszarbk7qyd9jwxhcfq9i6s5i0qikx8alkk4hq - => false + # Not all paths under the Nix store are store paths + hasStorePathPrefix /nix/store/.links/10gg8k3rmbw8p7gszarbk7qyd9jwxhcfq9i6s5i0qikx8alkk4hq + => false - # Store derivations are also store paths themselves - hasStorePathPrefix /nix/store/nvl9ic0pj1fpyln3zaqrf4cclbqdfn1j-foo.drv - => true + # Store derivations are also store paths themselves + hasStorePathPrefix /nix/store/nvl9ic0pj1fpyln3zaqrf4cclbqdfn1j-foo.drv + => true */ - hasStorePathPrefix = path: - let deconstructed = deconstructPath path; - in assert assertMsg (isPath path) - "lib.path.hasStorePathPrefix: Argument is of type ${ - typeOf path - }, but a path was expected"; + hasStorePathPrefix = + path: + let + deconstructed = deconstructPath path; + in + assert assertMsg (isPath path) + "lib.path.hasStorePathPrefix: Argument is of type ${typeOf path}, but a path was expected"; assert assertMsg # This function likely breaks or needs adjustment if used with other filesystem roots, if they ever get implemented. # Let's try to error nicely in such a case, though it's unclear how an implementation would work even and whether this could be detected. # See also https://github.com/NixOS/nix/pull/6530#discussion_r1422843117 (deconstructed.root == /. && toString deconstructed.root == "/") - "lib.path.hasStorePathPrefix: Argument has a filesystem root (${ - toString deconstructed.root - }) that's not /, which is currently not supported."; + "lib.path.hasStorePathPrefix: Argument has a filesystem root (${toString deconstructed.root}) that's not /, which is currently not supported."; componentsHaveStorePathPrefix deconstructed.components; - /* Whether a value is a valid subpath string. + /* + Whether a value is a valid subpath string. - A subpath string points to a specific file or directory within an absolute base directory. - It is a stricter form of a relative path that excludes `..` components, since those could escape the base directory. + A subpath string points to a specific file or directory within an absolute base directory. + It is a stricter form of a relative path that excludes `..` components, since those could escape the base directory. - - The value is a string. + - The value is a string. - - The string is not empty. + - The string is not empty. - - The string doesn't start with a `/`. + - The string doesn't start with a `/`. - - The string doesn't contain any `..` path components. + - The string doesn't contain any `..` path components. - Type: - subpath.isValid :: String -> Bool + Type: + subpath.isValid :: String -> Bool - Example: - # Not a string - subpath.isValid null - => false + Example: + # Not a string + subpath.isValid null + => false - # Empty string - subpath.isValid "" - => false + # Empty string + subpath.isValid "" + => false - # Absolute path - subpath.isValid "/foo" - => false + # Absolute path + subpath.isValid "/foo" + => false - # Contains a `..` path component - subpath.isValid "../foo" - => false + # Contains a `..` path component + subpath.isValid "../foo" + => false - # Valid subpath - subpath.isValid "foo/bar" - => true + # Valid subpath + subpath.isValid "foo/bar" + => true - # Doesn't need to be normalised - subpath.isValid "./foo//bar/" - => true + # Doesn't need to be normalised + subpath.isValid "./foo//bar/" + => true */ subpath.isValid = # The value to check - value: - subpathInvalidReason value == null; + value: subpathInvalidReason value == null; - /* Join subpath strings together using `/`, returning a normalised subpath string. + /* + Join subpath strings together using `/`, returning a normalised subpath string. - Like `concatStringsSep "/"` but safer, specifically: + Like `concatStringsSep "/"` but safer, specifically: - - All elements must be [valid subpath strings](#function-library-lib.path.subpath.isValid). + - All elements must be [valid subpath strings](#function-library-lib.path.subpath.isValid). - - The result gets [normalised](#function-library-lib.path.subpath.normalise). + - The result gets [normalised](#function-library-lib.path.subpath.normalise). - - The edge case of an empty list gets properly handled by returning the neutral subpath `"./."`. + - The edge case of an empty list gets properly handled by returning the neutral subpath `"./."`. - Laws: + Laws: - - Associativity: + - Associativity: - subpath.join [ x (subpath.join [ y z ]) ] == subpath.join [ (subpath.join [ x y ]) z ] + subpath.join [ x (subpath.join [ y z ]) ] == subpath.join [ (subpath.join [ x y ]) z ] - - Identity - `"./."` is the neutral element for normalised paths: + - Identity - `"./."` is the neutral element for normalised paths: - subpath.join [ ] == "./." - subpath.join [ (subpath.normalise p) "./." ] == subpath.normalise p - subpath.join [ "./." (subpath.normalise p) ] == subpath.normalise p + subpath.join [ ] == "./." + subpath.join [ (subpath.normalise p) "./." ] == subpath.normalise p + subpath.join [ "./." (subpath.normalise p) ] == subpath.normalise p - - Normalisation - the result is [normalised](#function-library-lib.path.subpath.normalise): + - Normalisation - the result is [normalised](#function-library-lib.path.subpath.normalise): - subpath.join ps == subpath.normalise (subpath.join ps) + subpath.join ps == subpath.normalise (subpath.join ps) - - For non-empty lists, the implementation is equivalent to [normalising](#function-library-lib.path.subpath.normalise) the result of `concatStringsSep "/"`. - Note that the above laws can be derived from this one: + - For non-empty lists, the implementation is equivalent to [normalising](#function-library-lib.path.subpath.normalise) the result of `concatStringsSep "/"`. + Note that the above laws can be derived from this one: - ps != [] -> subpath.join ps == subpath.normalise (concatStringsSep "/" ps) + ps != [] -> subpath.join ps == subpath.normalise (concatStringsSep "/" ps) - Type: - subpath.join :: [ String ] -> String + Type: + subpath.join :: [ String ] -> String - Example: - subpath.join [ "foo" "bar/baz" ] - => "./foo/bar/baz" + Example: + subpath.join [ "foo" "bar/baz" ] + => "./foo/bar/baz" - # normalise the result - subpath.join [ "./foo" "." "bar//./baz/" ] - => "./foo/bar/baz" + # normalise the result + subpath.join [ "./foo" "." "bar//./baz/" ] + => "./foo/bar/baz" - # passing an empty list results in the current directory - subpath.join [ ] - => "./." + # passing an empty list results in the current directory + subpath.join [ ] + => "./." - # elements must be valid subpath strings - subpath.join [ /foo ] - => - subpath.join [ "" ] - => - subpath.join [ "/foo" ] - => - subpath.join [ "../foo" ] - => + # elements must be valid subpath strings + subpath.join [ /foo ] + => + subpath.join [ "" ] + => + subpath.join [ "/foo" ] + => + subpath.join [ "../foo" ] + => */ subpath.join = # The list of subpaths to join together @@ -494,41 +512,42 @@ in { if all isValid subpaths then joinRelPath (concatMap splitRelPath subpaths) else - # Otherwise we take our time to gather more info for a better error message - # Strictly go through each path, throwing on the first invalid one - # Tracks the list index in the fold accumulator - foldl' (i: path: + # Otherwise we take our time to gather more info for a better error message + # Strictly go through each path, throwing on the first invalid one + # Tracks the list index in the fold accumulator + foldl' ( + i: path: if isValid path then i + 1 else throw '' - lib.path.subpath.join: Element at index ${ - toString i - } is not a valid subpath string: - ${subpathInvalidReason path}'') 0 subpaths; + lib.path.subpath.join: Element at index ${toString i} is not a valid subpath string: + ${subpathInvalidReason path}'' + ) 0 subpaths; - /* Split [a subpath](#function-library-lib.path.subpath.isValid) into its path component strings. - Throw an error if the subpath isn't valid. - Note that the returned path components are also [valid subpath strings](#function-library-lib.path.subpath.isValid), though they are intentionally not [normalised](#function-library-lib.path.subpath.normalise). + /* + Split [a subpath](#function-library-lib.path.subpath.isValid) into its path component strings. + Throw an error if the subpath isn't valid. + Note that the returned path components are also [valid subpath strings](#function-library-lib.path.subpath.isValid), though they are intentionally not [normalised](#function-library-lib.path.subpath.normalise). - Laws: + Laws: - - Splitting a subpath into components and [joining](#function-library-lib.path.subpath.join) the components gives the same subpath but [normalised](#function-library-lib.path.subpath.normalise): + - Splitting a subpath into components and [joining](#function-library-lib.path.subpath.join) the components gives the same subpath but [normalised](#function-library-lib.path.subpath.normalise): - subpath.join (subpath.components s) == subpath.normalise s + subpath.join (subpath.components s) == subpath.normalise s - Type: - subpath.components :: String -> [ String ] + Type: + subpath.components :: String -> [ String ] - Example: - subpath.components "." - => [ ] + Example: + subpath.components "." + => [ ] - subpath.components "./foo//bar/./baz/" - => [ "foo" "bar" "baz" ] + subpath.components "./foo//bar/./baz/" + => [ "foo" "bar" "baz" ] - subpath.components "/foo" - => + subpath.components "/foo" + => */ subpath.components = # The subpath string to split into components @@ -538,77 +557,78 @@ in { ${subpathInvalidReason subpath}''; splitRelPath subpath; - /* Normalise a subpath. Throw an error if the subpath isn't [valid](#function-library-lib.path.subpath.isValid). + /* + Normalise a subpath. Throw an error if the subpath isn't [valid](#function-library-lib.path.subpath.isValid). - - Limit repeating `/` to a single one. + - Limit repeating `/` to a single one. - - Remove redundant `.` components. + - Remove redundant `.` components. - - Remove trailing `/` and `/.`. + - Remove trailing `/` and `/.`. - - Add leading `./`. + - Add leading `./`. - Laws: + Laws: - - Idempotency - normalising multiple times gives the same result: + - Idempotency - normalising multiple times gives the same result: - subpath.normalise (subpath.normalise p) == subpath.normalise p + subpath.normalise (subpath.normalise p) == subpath.normalise p - - Uniqueness - there's only a single normalisation for the paths that lead to the same file system node: + - Uniqueness - there's only a single normalisation for the paths that lead to the same file system node: - subpath.normalise p != subpath.normalise q -> $(realpath ${p}) != $(realpath ${q}) + subpath.normalise p != subpath.normalise q -> $(realpath ${p}) != $(realpath ${q}) - - Don't change the result when [appended](#function-library-lib.path.append) to a Nix path value: + - Don't change the result when [appended](#function-library-lib.path.append) to a Nix path value: - append base p == append base (subpath.normalise p) + append base p == append base (subpath.normalise p) - - Don't change the path according to `realpath`: + - Don't change the path according to `realpath`: - $(realpath ${p}) == $(realpath ${subpath.normalise p}) + $(realpath ${p}) == $(realpath ${subpath.normalise p}) - - Only error on [invalid subpaths](#function-library-lib.path.subpath.isValid): + - Only error on [invalid subpaths](#function-library-lib.path.subpath.isValid): - builtins.tryEval (subpath.normalise p)).success == subpath.isValid p + builtins.tryEval (subpath.normalise p)).success == subpath.isValid p - Type: - subpath.normalise :: String -> String + Type: + subpath.normalise :: String -> String - Example: - # limit repeating `/` to a single one - subpath.normalise "foo//bar" - => "./foo/bar" + Example: + # limit repeating `/` to a single one + subpath.normalise "foo//bar" + => "./foo/bar" - # remove redundant `.` components - subpath.normalise "foo/./bar" - => "./foo/bar" + # remove redundant `.` components + subpath.normalise "foo/./bar" + => "./foo/bar" - # add leading `./` - subpath.normalise "foo/bar" - => "./foo/bar" + # add leading `./` + subpath.normalise "foo/bar" + => "./foo/bar" - # remove trailing `/` - subpath.normalise "foo/bar/" - => "./foo/bar" + # remove trailing `/` + subpath.normalise "foo/bar/" + => "./foo/bar" - # remove trailing `/.` - subpath.normalise "foo/bar/." - => "./foo/bar" + # remove trailing `/.` + subpath.normalise "foo/bar/." + => "./foo/bar" - # Return the current directory as `./.` - subpath.normalise "." - => "./." + # Return the current directory as `./.` + subpath.normalise "." + => "./." - # error on `..` path components - subpath.normalise "foo/../bar" - => + # error on `..` path components + subpath.normalise "foo/../bar" + => - # error on empty string - subpath.normalise "" - => + # error on empty string + subpath.normalise "" + => - # error on absolute path - subpath.normalise "/foo" - => + # error on absolute path + subpath.normalise "/foo" + => */ subpath.normalise = # The subpath string to normalise diff --git a/nix/stdlib/source-types.nix b/nix/stdlib/source-types.nix index c4dc1be..7c00893 100644 --- a/nix/stdlib/source-types.nix +++ b/nix/stdlib/source-types.nix @@ -4,8 +4,11 @@ let shortName = tname; isSource = false; }; -in lib.mapAttrs (tname: tset: defaultSourceType tname // tset) { - fromSource = { isSource = true; }; +in +lib.mapAttrs (tname: tset: defaultSourceType tname // tset) { + fromSource = { + isSource = true; + }; binaryNativeCode = { }; diff --git a/nix/stdlib/sources.nix b/nix/stdlib/sources.nix index 9502c4c..667d0c1 100644 --- a/nix/stdlib/sources.nix +++ b/nix/stdlib/sources.nix @@ -3,118 +3,159 @@ # Tested in lib/tests/sources.sh let inherit (builtins) match split storeDir; - inherit (lib) boolToString filter isString readFile; + inherit (lib) + boolToString + filter + isString + readFile + ; inherit (lib.filesystem) pathIsRegularFile; - /* A basic filter for `cleanSourceWith` that removes - directories of version control system, backup files (*~) - and some generated files. + /* + A basic filter for `cleanSourceWith` that removes + directories of version control system, backup files (*~) + and some generated files. */ - cleanSourceFilter = name: type: - let baseName = baseNameOf (toString name); - in !( + cleanSourceFilter = + name: type: + let + baseName = baseNameOf (toString name); + in + !( # Filter out version control software files/directories - (baseName == ".git" || type == "directory" - && (baseName == ".svn" || baseName == "CVS" || baseName == ".hg")) || - # Filter out editor backup / swap files. - lib.hasSuffix "~" baseName || match "^\\.sw[a-z]$" baseName != null - || match "^\\..*\\.sw[a-z]$" baseName != null || - # Filter out generates files. - lib.hasSuffix ".o" baseName || lib.hasSuffix ".so" baseName || - # Filter out nix-build result symlinks - (type == "symlink" && lib.hasPrefix "result" baseName) || - # Filter out sockets and other types of files we can't have in the store. - (type == "unknown")); + ( + baseName == ".git" + || type == "directory" && (baseName == ".svn" || baseName == "CVS" || baseName == ".hg") + ) + || + # Filter out editor backup / swap files. + lib.hasSuffix "~" baseName + || match "^\\.sw[a-z]$" baseName != null + || match "^\\..*\\.sw[a-z]$" baseName != null + || + # Filter out generates files. + lib.hasSuffix ".o" baseName + || lib.hasSuffix ".so" baseName + || + # Filter out nix-build result symlinks + (type == "symlink" && lib.hasPrefix "result" baseName) + || + # Filter out sockets and other types of files we can't have in the store. + (type == "unknown") + ); - /* Filters a source tree removing version control files and directories using cleanSourceFilter. + /* + Filters a source tree removing version control files and directories using cleanSourceFilter. - Example: - cleanSource ./. + Example: + cleanSource ./. */ - cleanSource = src: + cleanSource = + src: cleanSourceWith { filter = cleanSourceFilter; inherit src; }; - /* Like `builtins.filterSource`, except it will compose with itself, - allowing you to chain multiple calls together without any - intermediate copies being put in the nix store. + /* + Like `builtins.filterSource`, except it will compose with itself, + allowing you to chain multiple calls together without any + intermediate copies being put in the nix store. - Example: - lib.cleanSourceWith { - filter = f; - src = lib.cleanSourceWith { - filter = g; - src = ./.; - }; - } - # Succeeds! + Example: + lib.cleanSourceWith { + filter = f; + src = lib.cleanSourceWith { + filter = g; + src = ./.; + }; + } + # Succeeds! - builtins.filterSource f (builtins.filterSource g ./.) - # Fails! + builtins.filterSource f (builtins.filterSource g ./.) + # Fails! */ - cleanSourceWith = { - # A path or cleanSourceWith result to filter and/or rename. - src, - # Optional with default value: constant true (include everything) - # The function will be combined with the && operator such - # that src.filter is called lazily. - # For implementing a filter, see - # https://nixos.org/nix/manual/#builtin-filterSource - # Type: A function (path -> type -> bool) - filter ? _path: _type: true, - # Optional name to use as part of the store path. - # This defaults to `src.name` or otherwise `"source"`. - name ? null, }: - let orig = toSourceAttributes src; - in fromSourceAttributes { + cleanSourceWith = + { + # A path or cleanSourceWith result to filter and/or rename. + src, + # Optional with default value: constant true (include everything) + # The function will be combined with the && operator such + # that src.filter is called lazily. + # For implementing a filter, see + # https://nixos.org/nix/manual/#builtin-filterSource + # Type: A function (path -> type -> bool) + filter ? _path: _type: true, + # Optional name to use as part of the store path. + # This defaults to `src.name` or otherwise `"source"`. + name ? null, + }: + let + orig = toSourceAttributes src; + in + fromSourceAttributes { inherit (orig) origSrc; filter = path: type: filter path type && orig.filter path type; name = if name != null then name else orig.name; }; - /* Add logging to a source, for troubleshooting the filtering behavior. - Type: - sources.trace :: sourceLike -> Source + /* + Add logging to a source, for troubleshooting the filtering behavior. + Type: + sources.trace :: sourceLike -> Source */ trace = # Source to debug. The returned source will behave like this source, but also log its filter invocations. src: - let attrs = toSourceAttributes src; - in fromSourceAttributes (attrs // { - filter = path: type: - let r = attrs.filter path type; - in builtins.trace "${attrs.name}.filter ${path} = ${boolToString r}" r; - }) // { - satisfiesSubpathInvariant = src ? satisfiesSubpathInvariant - && src.satisfiesSubpathInvariant; + let + attrs = toSourceAttributes src; + in + fromSourceAttributes ( + attrs + // { + filter = + path: type: + let + r = attrs.filter path type; + in + builtins.trace "${attrs.name}.filter ${path} = ${boolToString r}" r; + } + ) + // { + satisfiesSubpathInvariant = src ? satisfiesSubpathInvariant && src.satisfiesSubpathInvariant; }; - /* Filter sources by a list of regular expressions. + /* + Filter sources by a list of regular expressions. - Example: src = sourceByRegex ./my-subproject [".*\.py$" "^database.sql$"] + Example: src = sourceByRegex ./my-subproject [".*\.py$" "^database.sql$"] */ - sourceByRegex = src: regexes: + sourceByRegex = + src: regexes: let isFiltered = src ? _isLibCleanSourceWith; origSrc = if isFiltered then src.origSrc else src; - in lib.cleanSourceWith { - filter = path: type: - let relPath = lib.removePrefix (toString origSrc + "/") (toString path); - in lib.any (re: match re relPath != null) regexes; + in + lib.cleanSourceWith { + filter = + path: type: + let + relPath = lib.removePrefix (toString origSrc + "/") (toString path); + in + lib.any (re: match re relPath != null) regexes; inherit src; }; - /* Get all files ending with the specified suffices from the given - source directory or its descendants, omitting files that do not match - any suffix. The result of the example below will include files like - `./dir/module.c` and `./dir/subdir/doc.xml` if present. + /* + Get all files ending with the specified suffices from the given + source directory or its descendants, omitting files that do not match + any suffix. The result of the example below will include files like + `./dir/module.c` and `./dir/subdir/doc.xml` if present. - Type: sourceLike -> [String] -> Source + Type: sourceLike -> [String] -> Source - Example: - sourceFilesBySuffices ./. [ ".xml" ".c" ] + Example: + sourceFilesBySuffices ./. [ ".xml" ".c" ] */ sourceFilesBySuffices = # Path or source containing the files to be returned @@ -122,20 +163,28 @@ let # A list of file suffix strings exts: let - filter = name: type: - let base = baseNameOf (toString name); - in type == "directory" || lib.any (ext: lib.hasSuffix ext base) exts; - in cleanSourceWith { inherit filter src; }; + filter = + name: type: + let + base = baseNameOf (toString name); + in + type == "directory" || lib.any (ext: lib.hasSuffix ext base) exts; + in + cleanSourceWith { inherit filter src; }; pathIsGitRepo = path: (_commitIdFromGitRepoOrError path) ? value; - /* Get the commit id of a git repo. + /* + Get the commit id of a git repo. - Example: commitIdFromGitRepo + Example: commitIdFromGitRepo */ - commitIdFromGitRepo = path: - let commitIdOrError = _commitIdFromGitRepoOrError path; - in commitIdOrError.value or (throw commitIdOrError.error); + commitIdFromGitRepo = + path: + let + commitIdOrError = _commitIdFromGitRepoOrError path; + in + commitIdOrError.value or (throw commitIdOrError.error); # Get the commit id of a git repo. @@ -143,69 +192,70 @@ let # Example: commitIdFromGitRepo # not exported, used for commitIdFromGitRepo - _commitIdFromGitRepoOrError = let - readCommitFromFile = file: path: - let - fileName = path + "/${file}"; - packedRefsName = path + "/packed-refs"; - absolutePath = base: path: - if lib.hasPrefix "/" path then - path + _commitIdFromGitRepoOrError = + let + readCommitFromFile = + file: path: + let + fileName = path + "/${file}"; + packedRefsName = path + "/packed-refs"; + absolutePath = + base: path: if lib.hasPrefix "/" path then path else toString (/. + "${base}/${path}"); + in + if + pathIsRegularFile path + # Resolve git worktrees. See gitrepository-layout(5) + then + let + m = match "^gitdir: (.*)$" (lib.fileContents path); + in + if m == null then + { error = "File contains no gitdir reference: " + path; } else - toString (/. + "${base}/${path}"); - in if pathIsRegularFile path - # Resolve git worktrees. See gitrepository-layout(5) - then - let m = match "^gitdir: (.*)$" (lib.fileContents path); - in if m == null then { - error = "File contains no gitdir reference: " + path; - } else + let + gitDir = absolutePath (dirOf path) (lib.head m); + commonDir'' = + if pathIsRegularFile "${gitDir}/commondir" then lib.fileContents "${gitDir}/commondir" else gitDir; + commonDir' = lib.removeSuffix "/" commonDir''; + commonDir = absolutePath gitDir commonDir'; + refFile = lib.removePrefix "${commonDir}/" "${gitDir}/${file}"; + in + readCommitFromFile refFile commonDir + else if + pathIsRegularFile fileName + # Sometimes git stores the commitId directly in the file but + # sometimes it stores something like: «ref: refs/heads/branch-name» + then let - gitDir = absolutePath (dirOf path) (lib.head m); - commonDir'' = if pathIsRegularFile "${gitDir}/commondir" then - lib.fileContents "${gitDir}/commondir" - else - gitDir; - commonDir' = lib.removeSuffix "/" commonDir''; - commonDir = absolutePath gitDir commonDir'; - refFile = lib.removePrefix "${commonDir}/" "${gitDir}/${file}"; - in readCommitFromFile refFile commonDir - else if pathIsRegularFile fileName - # Sometimes git stores the commitId directly in the file but - # sometimes it stores something like: «ref: refs/heads/branch-name» - then - let - fileContent = lib.fileContents fileName; - matchRef = match "^ref: (.*)$" fileContent; - in if matchRef == null then { - value = fileContent; - } else - readCommitFromFile (lib.head matchRef) path - else if pathIsRegularFile packedRefsName - # Sometimes, the file isn't there at all and has been packed away in the - # packed-refs file, so we have to grep through it: - then - let - fileContent = readFile packedRefsName; - matchRef = match "([a-z0-9]+) ${file}"; - isRef = s: isString s && (matchRef s) != null; - # there is a bug in libstdc++ leading to stackoverflow for long strings: - # https://github.com/NixOS/nix/issues/2147#issuecomment-659868795 - refs = filter isRef (split "\n" fileContent); - in if refs == [ ] then { - error = "Could not find " + file + " in " + packedRefsName; - } else { - value = lib.head (matchRef (lib.head refs)); - } - else { - error = "Not a .git directory: " + toString path; - }; - in readCommitFromFile "HEAD"; + fileContent = lib.fileContents fileName; + matchRef = match "^ref: (.*)$" fileContent; + in + if matchRef == null then { value = fileContent; } else readCommitFromFile (lib.head matchRef) path + else if + pathIsRegularFile packedRefsName + # Sometimes, the file isn't there at all and has been packed away in the + # packed-refs file, so we have to grep through it: + then + let + fileContent = readFile packedRefsName; + matchRef = match "([a-z0-9]+) ${file}"; + isRef = s: isString s && (matchRef s) != null; + # there is a bug in libstdc++ leading to stackoverflow for long strings: + # https://github.com/NixOS/nix/issues/2147#issuecomment-659868795 + refs = filter isRef (split "\n" fileContent); + in + if refs == [ ] then + { error = "Could not find " + file + " in " + packedRefsName; } + else + { value = lib.head (matchRef (lib.head refs)); } + else + { error = "Not a .git directory: " + toString path; }; + in + readCommitFromFile "HEAD"; pathHasContext = builtins.hasContext or (lib.hasPrefix storeDir); - canCleanSource = src: - src ? _isLibCleanSourceWith || !(pathHasContext (toString src)); + canCleanSource = src: src ? _isLibCleanSourceWith || !(pathHasContext (toString src)); # -------------------------------------------------------------------------- # # Internal functions @@ -218,9 +268,12 @@ let # like class of objects in the wild. # (Existing ones being: paths, strings, sources and x//{outPath}) # So instead of exposing internals, we build a library of combinator functions. - toSourceAttributes = src: - let isFiltered = src ? _isLibCleanSourceWith; - in { + toSourceAttributes = + src: + let + isFiltered = src ? _isLibCleanSourceWith; + in + { # The original path origSrc = if isFiltered then src.origSrc else src; filter = if isFiltered then src.filter else _: _: true; @@ -230,28 +283,46 @@ let # fromSourceAttributes : SourceAttrs -> Source # # Inverse of toSourceAttributes for Source objects. - fromSourceAttributes = { origSrc, filter, name, }: { - _isLibCleanSourceWith = true; - inherit origSrc filter name; - outPath = builtins.path { - inherit filter name; - path = origSrc; + fromSourceAttributes = + { + origSrc, + filter, + name, + }: + { + _isLibCleanSourceWith = true; + inherit origSrc filter name; + outPath = builtins.path { + inherit filter name; + path = origSrc; + }; }; - }; -in { - pathType = lib.warnIf (lib.isInOldestRelease 2305) - "lib.sources.pathType has been moved to lib.filesystem.pathType." - lib.filesystem.pathType; +in +{ + pathType = lib.warnIf (lib.isInOldestRelease + 2305 + ) "lib.sources.pathType has been moved to lib.filesystem.pathType." lib.filesystem.pathType; - pathIsDirectory = lib.warnIf (lib.isInOldestRelease 2305) - "lib.sources.pathIsDirectory has been moved to lib.filesystem.pathIsDirectory." - lib.filesystem.pathIsDirectory; + pathIsDirectory = + lib.warnIf (lib.isInOldestRelease 2305) + "lib.sources.pathIsDirectory has been moved to lib.filesystem.pathIsDirectory." + lib.filesystem.pathIsDirectory; - pathIsRegularFile = lib.warnIf (lib.isInOldestRelease 2305) - "lib.sources.pathIsRegularFile has been moved to lib.filesystem.pathIsRegularFile." - lib.filesystem.pathIsRegularFile; + pathIsRegularFile = + lib.warnIf (lib.isInOldestRelease 2305) + "lib.sources.pathIsRegularFile has been moved to lib.filesystem.pathIsRegularFile." + lib.filesystem.pathIsRegularFile; - inherit pathIsGitRepo commitIdFromGitRepo cleanSource cleanSourceWith - cleanSourceFilter pathHasContext canCleanSource sourceByRegex - sourceFilesBySuffices trace; + inherit + pathIsGitRepo + commitIdFromGitRepo + cleanSource + cleanSourceWith + cleanSourceFilter + pathHasContext + canCleanSource + sourceByRegex + sourceFilesBySuffices + trace + ; } diff --git a/nix/stdlib/strings-with-deps.nix b/nix/stdlib/strings-with-deps.nix index 6ffc5d3..ab3d904 100644 --- a/nix/stdlib/strings-with-deps.nix +++ b/nix/stdlib/strings-with-deps.nix @@ -1,77 +1,101 @@ { lib }: -/* Usage: +/* + Usage: - You define you custom builder script by adding all build steps to a list. - for example: - builder = writeScript "fsg-4.4-builder" - (textClosure [doUnpack addInputs preBuild doMake installPhase doForceShare]); + You define you custom builder script by adding all build steps to a list. + for example: + builder = writeScript "fsg-4.4-builder" + (textClosure [doUnpack addInputs preBuild doMake installPhase doForceShare]); - a step is defined by noDepEntry, fullDepEntry or packEntry. - To ensure that prerequisite are met those are added before the task itself by - textClosureDupList. Duplicated items are removed again. + a step is defined by noDepEntry, fullDepEntry or packEntry. + To ensure that prerequisite are met those are added before the task itself by + textClosureDupList. Duplicated items are removed again. - See trace/nixpkgs/trunk/pkgs/top-level/builder-defs.nix for some predefined build steps + See trace/nixpkgs/trunk/pkgs/top-level/builder-defs.nix for some predefined build steps - Attention: + Attention: - let - pkgs = (import ) {}; - in let - inherit (pkgs.stringsWithDeps) fullDepEntry packEntry noDepEntry textClosureMap; - inherit (pkgs.lib) id; + let + pkgs = (import ) {}; + in let + inherit (pkgs.stringsWithDeps) fullDepEntry packEntry noDepEntry textClosureMap; + inherit (pkgs.lib) id; - nameA = noDepEntry "Text a"; - nameB = fullDepEntry "Text b" ["nameA"]; - nameC = fullDepEntry "Text c" ["nameA"]; + nameA = noDepEntry "Text a"; + nameB = fullDepEntry "Text b" ["nameA"]; + nameC = fullDepEntry "Text c" ["nameA"]; - stages = { - nameHeader = noDepEntry "#! /bin/sh \n"; - inherit nameA nameB nameC; - }; - in - textClosureMap id stages - [ "nameHeader" "nameA" "nameB" "nameC" - nameC # <- added twice. add a dep entry if you know that it will be added once only [1] - "nameB" # <- this will not be added again because the attr name (reference) is used - ] + stages = { + nameHeader = noDepEntry "#! /bin/sh \n"; + inherit nameA nameB nameC; + }; + in + textClosureMap id stages + [ "nameHeader" "nameA" "nameB" "nameC" + nameC # <- added twice. add a dep entry if you know that it will be added once only [1] + "nameB" # <- this will not be added again because the attr name (reference) is used + ] - # result: Str("#! /bin/sh \n\nText a\nText b\nText c\nText c",[]) + # result: Str("#! /bin/sh \n\nText a\nText b\nText c\nText c",[]) - [1] maybe this behaviour should be removed to keep things simple (?) + [1] maybe this behaviour should be removed to keep things simple (?) */ -let inherit (lib) concatStringsSep head isAttrs listToAttrs tail; -in rec { - /* !!! The interface of this function is kind of messed up, since - it's way too overloaded and almost but not quite computes a - topological sort of the depstrings. +let + inherit (lib) + concatStringsSep + head + isAttrs + listToAttrs + tail + ; +in +rec { + /* + !!! The interface of this function is kind of messed up, since + it's way too overloaded and almost but not quite computes a + topological sort of the depstrings. */ - textClosureList = predefined: arg: + textClosureList = + predefined: arg: let - f = done: todo: - if todo == [ ] then { - result = [ ]; - inherit done; - } else - let entry = head todo; - in if isAttrs entry then + f = + done: todo: + if todo == [ ] then + { + result = [ ]; + inherit done; + } + else + let + entry = head todo; + in + if isAttrs entry then let x = f done entry.deps; y = f x.done (tail todo); - in { + in + { result = x.result ++ [ entry.text ] ++ y.result; done = y.done; } else if done ? ${entry} then f done (tail todo) else - f (done // listToAttrs [{ - name = entry; - value = 1; - }]) ([ predefined.${entry} ] ++ tail todo); - in (f { } arg).result; + f ( + done + // listToAttrs [ + { + name = entry; + value = 1; + } + ] + ) ([ predefined.${entry} ] ++ tail todo); + in + (f { } arg).result; - textClosureMap = f: predefined: names: + textClosureMap = + f: predefined: names: concatStringsSep "\n" (map f (textClosureList predefined names)); noDepEntry = text: { diff --git a/nix/stdlib/strings.nix b/nix/stdlib/strings.nix index dc53a3d..34cde03 100644 --- a/nix/stdlib/strings.nix +++ b/nix/stdlib/strings.nix @@ -6,51 +6,77 @@ let inherit (lib.trivial) warnIf; asciiTable = import ./ascii-table.nix; -in rec { +in +rec { inherit (builtins) - compareVersions elem elemAt filter fromJSON genList head isInt isList - isAttrs isPath isString match parseDrvName readFile replaceStrings split - storeDir stringLength substring tail toJSON typeOf - unsafeDiscardStringContext; + compareVersions + elem + elemAt + filter + fromJSON + genList + head + isInt + isList + isAttrs + isPath + isString + match + parseDrvName + readFile + replaceStrings + split + storeDir + stringLength + substring + tail + toJSON + typeOf + unsafeDiscardStringContext + ; + + /* + Concatenate a list of strings. + + Type: concatStrings :: [string] -> string - /* Concatenate a list of strings. - - Type: concatStrings :: [string] -> string - - Example: - concatStrings ["foo" "bar"] - => "foobar" + Example: + concatStrings ["foo" "bar"] + => "foobar" */ concatStrings = builtins.concatStringsSep ""; - /* Map a function over a list and concatenate the resulting strings. + /* + Map a function over a list and concatenate the resulting strings. - Type: concatMapStrings :: (a -> string) -> [a] -> string + Type: concatMapStrings :: (a -> string) -> [a] -> string - Example: - concatMapStrings (x: "a" + x) ["foo" "bar"] - => "afooabar" + Example: + concatMapStrings (x: "a" + x) ["foo" "bar"] + => "afooabar" */ concatMapStrings = f: list: concatStrings (map f list); - /* Like `concatMapStrings` except that the f functions also gets the - position as a parameter. + /* + Like `concatMapStrings` except that the f functions also gets the + position as a parameter. - Type: concatImapStrings :: (int -> a -> string) -> [a] -> string + Type: concatImapStrings :: (int -> a -> string) -> [a] -> string - Example: - concatImapStrings (pos: x: "${toString pos}-${x}") ["foo" "bar"] - => "1-foo2-bar" + Example: + concatImapStrings (pos: x: "${toString pos}-${x}") ["foo" "bar"] + => "1-foo2-bar" */ concatImapStrings = f: list: concatStrings (lib.imap1 f list); - /* Place an element between each element of a list + /* + Place an element between each element of a list - Type: intersperse :: a -> [a] -> [a] + Type: intersperse :: a -> [a] -> [a] - Example: - intersperse "/" ["usr" "local" "bin"] - => ["usr" "/" "local" "/" "bin"]. + Example: + intersperse "/" ["usr" "local" "bin"] + => ["usr" "/" "local" "/" "bin"]. */ intersperse = # Separator to add between elements @@ -60,27 +86,34 @@ in rec { if list == [ ] || length list == 1 then list else - tail (lib.concatMap (x: [ separator x ]) list); + tail ( + lib.concatMap (x: [ + separator + x + ]) list + ); - /* Concatenate a list of strings with a separator between each element + /* + Concatenate a list of strings with a separator between each element - Type: concatStringsSep :: string -> [string] -> string + Type: concatStringsSep :: string -> [string] -> string - Example: - concatStringsSep "/" ["usr" "local" "bin"] - => "usr/local/bin" + Example: + concatStringsSep "/" ["usr" "local" "bin"] + => "usr/local/bin" */ concatStringsSep = builtins.concatStringsSep; - /* Maps a function over a list of strings and then concatenates the - result with the specified separator interspersed between - elements. + /* + Maps a function over a list of strings and then concatenates the + result with the specified separator interspersed between + elements. - Type: concatMapStringsSep :: string -> (a -> string) -> [a] -> string + Type: concatMapStringsSep :: string -> (a -> string) -> [a] -> string - Example: - concatMapStringsSep "-" (x: toUpper x) ["foo" "bar" "baz"] - => "FOO-BAR-BAZ" + Example: + concatMapStringsSep "-" (x: toUpper x) ["foo" "bar" "baz"] + => "FOO-BAR-BAZ" */ concatMapStringsSep = # Separator to add between elements @@ -91,14 +124,15 @@ in rec { list: concatStringsSep sep (map f list); - /* Same as `concatMapStringsSep`, but the mapping function - additionally receives the position of its argument. + /* + Same as `concatMapStringsSep`, but the mapping function + additionally receives the position of its argument. - Type: concatIMapStringsSep :: string -> (int -> a -> string) -> [a] -> string + Type: concatIMapStringsSep :: string -> (int -> a -> string) -> [a] -> string - Example: - concatImapStringsSep "-" (pos: x: toString (x / pos)) [ 6 6 6 ] - => "6-3-2" + Example: + concatImapStringsSep "-" (pos: x: toString (x / pos)) [ 6 6 6 ] + => "6-3-2" */ concatImapStringsSep = # Separator to add between elements @@ -109,59 +143,62 @@ in rec { list: concatStringsSep sep (lib.imap1 f list); - /* Concatenate a list of strings, adding a newline at the end of each one. - Defined as `concatMapStrings (s: s + "\n")`. + /* + Concatenate a list of strings, adding a newline at the end of each one. + Defined as `concatMapStrings (s: s + "\n")`. - Type: concatLines :: [string] -> string + Type: concatLines :: [string] -> string - Example: - concatLines [ "foo" "bar" ] - => "foo\nbar\n" + Example: + concatLines [ "foo" "bar" ] + => "foo\nbar\n" */ concatLines = concatMapStrings (s: s + "\n"); - /* Replicate a string n times, - and concatenate the parts into a new string. + /* + Replicate a string n times, + and concatenate the parts into a new string. - Type: replicate :: int -> string -> string + Type: replicate :: int -> string -> string - Example: - replicate 3 "v" - => "vvv" - replicate 5 "hello" - => "hellohellohellohellohello" + Example: + replicate 3 "v" + => "vvv" + replicate 5 "hello" + => "hellohellohellohellohello" */ replicate = n: s: concatStrings (lib.lists.replicate n s); - /* Construct a Unix-style, colon-separated search path consisting of - the given `subDir` appended to each of the given paths. + /* + Construct a Unix-style, colon-separated search path consisting of + the given `subDir` appended to each of the given paths. - Type: makeSearchPath :: string -> [string] -> string + Type: makeSearchPath :: string -> [string] -> string - Example: - makeSearchPath "bin" ["/root" "/usr" "/usr/local"] - => "/root/bin:/usr/bin:/usr/local/bin" - makeSearchPath "bin" [""] - => "/bin" + Example: + makeSearchPath "bin" ["/root" "/usr" "/usr/local"] + => "/root/bin:/usr/bin:/usr/local/bin" + makeSearchPath "bin" [""] + => "/bin" */ makeSearchPath = # Directory name to append subDir: # List of base paths paths: - concatStringsSep ":" - (map (path: path + "/" + subDir) (filter (x: x != null) paths)); + concatStringsSep ":" (map (path: path + "/" + subDir) (filter (x: x != null) paths)); - /* Construct a Unix-style search path by appending the given - `subDir` to the specified `output` of each of the packages. If no - output by the given name is found, fallback to `.out` and then to - the default. + /* + Construct a Unix-style search path by appending the given + `subDir` to the specified `output` of each of the packages. If no + output by the given name is found, fallback to `.out` and then to + the default. - Type: string -> string -> [package] -> string + Type: string -> string -> [package] -> string - Example: - makeSearchPathOutput "dev" "bin" [ pkgs.openssl pkgs.zlib ] - => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r-dev/bin:/nix/store/wwh7mhwh269sfjkm6k5665b5kgp7jrk2-zlib-1.2.8/bin" + Example: + makeSearchPathOutput "dev" "bin" [ pkgs.openssl pkgs.zlib ] + => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r-dev/bin:/nix/store/wwh7mhwh269sfjkm6k5665b5kgp7jrk2-zlib-1.2.8/bin" */ makeSearchPathOutput = # Package output to use @@ -172,68 +209,74 @@ in rec { pkgs: makeSearchPath subDir (map (lib.getOutput output) pkgs); - /* Construct a library search path (such as RPATH) containing the - libraries for a set of packages + /* + Construct a library search path (such as RPATH) containing the + libraries for a set of packages - Example: - makeLibraryPath [ "/usr" "/usr/local" ] - => "/usr/lib:/usr/local/lib" - pkgs = import { } - makeLibraryPath [ pkgs.openssl pkgs.zlib ] - => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r/lib:/nix/store/wwh7mhwh269sfjkm6k5665b5kgp7jrk2-zlib-1.2.8/lib" + Example: + makeLibraryPath [ "/usr" "/usr/local" ] + => "/usr/lib:/usr/local/lib" + pkgs = import { } + makeLibraryPath [ pkgs.openssl pkgs.zlib ] + => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r/lib:/nix/store/wwh7mhwh269sfjkm6k5665b5kgp7jrk2-zlib-1.2.8/lib" */ makeLibraryPath = makeSearchPathOutput "lib" "lib"; - /* Construct an include search path (such as C_INCLUDE_PATH) containing the - header files for a set of packages or paths. + /* + Construct an include search path (such as C_INCLUDE_PATH) containing the + header files for a set of packages or paths. - Example: - makeIncludePath [ "/usr" "/usr/local" ] - => "/usr/include:/usr/local/include" - pkgs = import { } - makeIncludePath [ pkgs.openssl pkgs.zlib ] - => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r-dev/include:/nix/store/wwh7mhwh269sfjkm6k5665b5kgp7jrk2-zlib-1.2.8-dev/include" + Example: + makeIncludePath [ "/usr" "/usr/local" ] + => "/usr/include:/usr/local/include" + pkgs = import { } + makeIncludePath [ pkgs.openssl pkgs.zlib ] + => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r-dev/include:/nix/store/wwh7mhwh269sfjkm6k5665b5kgp7jrk2-zlib-1.2.8-dev/include" */ makeIncludePath = makeSearchPathOutput "dev" "include"; - /* Construct a binary search path (such as $PATH) containing the - binaries for a set of packages. + /* + Construct a binary search path (such as $PATH) containing the + binaries for a set of packages. - Example: - makeBinPath ["/root" "/usr" "/usr/local"] - => "/root/bin:/usr/bin:/usr/local/bin" + Example: + makeBinPath ["/root" "/usr" "/usr/local"] + => "/root/bin:/usr/bin:/usr/local/bin" */ makeBinPath = makeSearchPathOutput "bin" "bin"; - /* Normalize path, removing extraneous /s + /* + Normalize path, removing extraneous /s - Type: normalizePath :: string -> string + Type: normalizePath :: string -> string - Example: - normalizePath "/a//b///c/" - => "/a/b/c/" + Example: + normalizePath "/a//b///c/" + => "/a/b/c/" */ - normalizePath = s: - warnIf (isPath s) '' - lib.strings.normalizePath: The argument (${ - toString s - }) is a path value, but only strings are supported. - Path values are always normalised in Nix, so there's no need to call this function on them. - This function also copies the path to the Nix store and returns the store path, the same as "''${path}" will, which may not be what you want. - This behavior is deprecated and will throw an error in the future.'' - (builtins.foldl' (x: y: if y == "/" && hasSuffix "/" x then x else x + y) "" - (stringToCharacters s)); - - /* Depending on the boolean `cond', return either the given string - or the empty string. Useful to concatenate against a bigger string. - - Type: optionalString :: bool -> string -> string - - Example: - optionalString true "some-string" - => "some-string" - optionalString false "some-string" - => "" + normalizePath = + s: + warnIf (isPath s) + '' + lib.strings.normalizePath: The argument (${toString s}) is a path value, but only strings are supported. + Path values are always normalised in Nix, so there's no need to call this function on them. + This function also copies the path to the Nix store and returns the store path, the same as "''${path}" will, which may not be what you want. + This behavior is deprecated and will throw an error in the future.'' + ( + builtins.foldl' (x: y: if y == "/" && hasSuffix "/" x then x else x + y) "" (stringToCharacters s) + ); + + /* + Depending on the boolean `cond', return either the given string + or the empty string. Useful to concatenate against a bigger string. + + Type: optionalString :: bool -> string -> string + + Example: + optionalString true "some-string" + => "some-string" + optionalString false "some-string" + => "" */ optionalString = # Condition @@ -242,15 +285,16 @@ in rec { string: if cond then string else ""; - /* Determine whether a string has given prefix. + /* + Determine whether a string has given prefix. - Type: hasPrefix :: string -> string -> bool + Type: hasPrefix :: string -> string -> bool - Example: - hasPrefix "foo" "foobar" - => true - hasPrefix "foo" "barfoo" - => false + Example: + hasPrefix "foo" "foobar" + => true + hasPrefix "foo" "barfoo" + => false */ hasPrefix = # Prefix to check for @@ -259,25 +303,25 @@ in rec { str: # Before 23.05, paths would be copied to the store before converting them # to strings and comparing. This was surprising and confusing. - warnIf (isPath pref) '' - lib.strings.hasPrefix: The first argument (${ - toString pref - }) is a path value, but only strings are supported. - There is almost certainly a bug in the calling code, since this function always returns `false` in such a case. - This function also copies the path to the Nix store, which may not be what you want. - This behavior is deprecated and will throw an error in the future. - You might want to use `lib.path.hasPrefix` instead, which correctly supports paths.'' - (substring 0 (stringLength pref) str == pref); - - /* Determine whether a string has given suffix. - - Type: hasSuffix :: string -> string -> bool - - Example: - hasSuffix "foo" "foobar" - => false - hasSuffix "foo" "barfoo" - => true + warnIf (isPath pref) + '' + lib.strings.hasPrefix: The first argument (${toString pref}) is a path value, but only strings are supported. + There is almost certainly a bug in the calling code, since this function always returns `false` in such a case. + This function also copies the path to the Nix store, which may not be what you want. + This behavior is deprecated and will throw an error in the future. + You might want to use `lib.path.hasPrefix` instead, which correctly supports paths.'' + (substring 0 (stringLength pref) str == pref); + + /* + Determine whether a string has given suffix. + + Type: hasSuffix :: string -> string -> bool + + Example: + hasSuffix "foo" "foobar" + => false + hasSuffix "foo" "barfoo" + => true */ hasSuffix = # Suffix to check for @@ -287,72 +331,74 @@ in rec { let lenContent = stringLength content; lenSuffix = stringLength suffix; - # Before 23.05, paths would be copied to the store before converting them - # to strings and comparing. This was surprising and confusing. - in warnIf (isPath suffix) '' - lib.strings.hasSuffix: The first argument (${ - toString suffix - }) is a path value, but only strings are supported. - There is almost certainly a bug in the calling code, since this function always returns `false` in such a case. - This function also copies the path to the Nix store, which may not be what you want. - This behavior is deprecated and will throw an error in the future.'' - (lenContent >= lenSuffix - && substring (lenContent - lenSuffix) lenContent content == suffix); - - /* Determine whether a string contains the given infix - - Type: hasInfix :: string -> string -> bool - - Example: - hasInfix "bc" "abcd" - => true - hasInfix "ab" "abcd" - => true - hasInfix "cd" "abcd" - => true - hasInfix "foo" "abcd" - => false - */ - hasInfix = infix: content: + in # Before 23.05, paths would be copied to the store before converting them # to strings and comparing. This was surprising and confusing. - warnIf (isPath infix) '' - lib.strings.hasInfix: The first argument (${ - toString infix - }) is a path value, but only strings are supported. - There is almost certainly a bug in the calling code, since this function always returns `false` in such a case. - This function also copies the path to the Nix store, which may not be what you want. - This behavior is deprecated and will throw an error in the future.'' - (builtins.match ".*${escapeRegex infix}.*" "${content}" != null); - - /* Convert a string to a list of characters (i.e. singleton strings). - This allows you to, e.g., map a function over each character. However, - note that this will likely be horribly inefficient; Nix is not a - general purpose programming language. Complex string manipulations - should, if appropriate, be done in a derivation. - Also note that Nix treats strings as a list of bytes and thus doesn't - handle unicode. - - Type: stringToCharacters :: string -> [string] - - Example: - stringToCharacters "" - => [ ] - stringToCharacters "abc" - => [ "a" "b" "c" ] - stringToCharacters "🦄" - => [ "�" "�" "�" "�" ] + warnIf (isPath suffix) + '' + lib.strings.hasSuffix: The first argument (${toString suffix}) is a path value, but only strings are supported. + There is almost certainly a bug in the calling code, since this function always returns `false` in such a case. + This function also copies the path to the Nix store, which may not be what you want. + This behavior is deprecated and will throw an error in the future.'' + (lenContent >= lenSuffix && substring (lenContent - lenSuffix) lenContent content == suffix); + + /* + Determine whether a string contains the given infix + + Type: hasInfix :: string -> string -> bool + + Example: + hasInfix "bc" "abcd" + => true + hasInfix "ab" "abcd" + => true + hasInfix "cd" "abcd" + => true + hasInfix "foo" "abcd" + => false + */ + hasInfix = + infix: content: + # Before 23.05, paths would be copied to the store before converting them + # to strings and comparing. This was surprising and confusing. + warnIf (isPath infix) + '' + lib.strings.hasInfix: The first argument (${toString infix}) is a path value, but only strings are supported. + There is almost certainly a bug in the calling code, since this function always returns `false` in such a case. + This function also copies the path to the Nix store, which may not be what you want. + This behavior is deprecated and will throw an error in the future.'' + (builtins.match ".*${escapeRegex infix}.*" "${content}" != null); + + /* + Convert a string to a list of characters (i.e. singleton strings). + This allows you to, e.g., map a function over each character. However, + note that this will likely be horribly inefficient; Nix is not a + general purpose programming language. Complex string manipulations + should, if appropriate, be done in a derivation. + Also note that Nix treats strings as a list of bytes and thus doesn't + handle unicode. + + Type: stringToCharacters :: string -> [string] + + Example: + stringToCharacters "" + => [ ] + stringToCharacters "abc" + => [ "a" "b" "c" ] + stringToCharacters "🦄" + => [ "�" "�" "�" "�" ] */ stringToCharacters = s: genList (p: substring p 1 s) (stringLength s); - /* Manipulate a string character by character and replace them by - strings before concatenating the results. + /* + Manipulate a string character by character and replace them by + strings before concatenating the results. - Type: stringAsChars :: (string -> string) -> string -> string + Type: stringAsChars :: (string -> string) -> string -> string - Example: - stringAsChars (x: if x == "a" then "i" else x) "nax" - => "nix" + Example: + stringAsChars (x: if x == "a" then "i" else x) "nax" + => "nix" */ stringAsChars = # Function to map over each individual character @@ -361,327 +407,353 @@ in rec { s: concatStrings (map f (stringToCharacters s)); - /* Convert char to ascii value, must be in printable range + /* + Convert char to ascii value, must be in printable range - Type: charToInt :: string -> int + Type: charToInt :: string -> int - Example: - charToInt "A" - => 65 - charToInt "(" - => 40 + Example: + charToInt "A" + => 65 + charToInt "(" + => 40 */ charToInt = c: builtins.getAttr c asciiTable; - /* Escape occurrence of the elements of `list` in `string` by - prefixing it with a backslash. + /* + Escape occurrence of the elements of `list` in `string` by + prefixing it with a backslash. - Type: escape :: [string] -> string -> string + Type: escape :: [string] -> string -> string - Example: - escape ["(" ")"] "(foo)" - => "\\(foo\\)" + Example: + escape ["(" ")"] "(foo)" + => "\\(foo\\)" */ escape = list: replaceStrings list (map (c: "\\${c}") list); - /* Escape occurrence of the element of `list` in `string` by - converting to its ASCII value and prefixing it with \\x. - Only works for printable ascii characters. + /* + Escape occurrence of the element of `list` in `string` by + converting to its ASCII value and prefixing it with \\x. + Only works for printable ascii characters. - Type: escapeC = [string] -> string -> string + Type: escapeC = [string] -> string -> string - Example: - escapeC [" "] "foo bar" - => "foo\\x20bar" + Example: + escapeC [" "] "foo bar" + => "foo\\x20bar" */ - escapeC = list: - replaceStrings list - (map (c: "\\x${toLower (lib.toHexString (charToInt c))}") list); + escapeC = list: replaceStrings list (map (c: "\\x${toLower (lib.toHexString (charToInt c))}") list); - /* Escape the string so it can be safely placed inside a URL - query. + /* + Escape the string so it can be safely placed inside a URL + query. - Type: escapeURL :: string -> string + Type: escapeURL :: string -> string - Example: - escapeURL "foo/bar baz" - => "foo%2Fbar%20baz" - */ - escapeURL = let - unreserved = [ - "A" - "B" - "C" - "D" - "E" - "F" - "G" - "H" - "I" - "J" - "K" - "L" - "M" - "N" - "O" - "P" - "Q" - "R" - "S" - "T" - "U" - "V" - "W" - "X" - "Y" - "Z" - "a" - "b" - "c" - "d" - "e" - "f" - "g" - "h" - "i" - "j" - "k" - "l" - "m" - "n" - "o" - "p" - "q" - "r" - "s" - "t" - "u" - "v" - "w" - "x" - "y" - "z" - "0" - "1" - "2" - "3" - "4" - "5" - "6" - "7" - "8" - "9" - "-" - "_" - "." - "~" - ]; - toEscape = builtins.removeAttrs asciiTable unreserved; - in replaceStrings (builtins.attrNames toEscape) - (lib.mapAttrsToList (_: c: "%${fixedWidthString 2 "0" (lib.toHexString c)}") - toEscape); - - /* Quote string to be used safely within the Bourne shell. - - Type: escapeShellArg :: string -> string - - Example: - escapeShellArg "esc'ape\nme" - => "'esc'\\''ape\nme'" + Example: + escapeURL "foo/bar baz" + => "foo%2Fbar%20baz" */ - escapeShellArg = arg: - "'${replaceStrings [ "'" ] [ "'\\''" ] (toString arg)}'"; + escapeURL = + let + unreserved = [ + "A" + "B" + "C" + "D" + "E" + "F" + "G" + "H" + "I" + "J" + "K" + "L" + "M" + "N" + "O" + "P" + "Q" + "R" + "S" + "T" + "U" + "V" + "W" + "X" + "Y" + "Z" + "a" + "b" + "c" + "d" + "e" + "f" + "g" + "h" + "i" + "j" + "k" + "l" + "m" + "n" + "o" + "p" + "q" + "r" + "s" + "t" + "u" + "v" + "w" + "x" + "y" + "z" + "0" + "1" + "2" + "3" + "4" + "5" + "6" + "7" + "8" + "9" + "-" + "_" + "." + "~" + ]; + toEscape = builtins.removeAttrs asciiTable unreserved; + in + replaceStrings (builtins.attrNames toEscape) ( + lib.mapAttrsToList (_: c: "%${fixedWidthString 2 "0" (lib.toHexString c)}") toEscape + ); - /* Quote all arguments to be safely passed to the Bourne shell. + /* + Quote string to be used safely within the Bourne shell. - Type: escapeShellArgs :: [string] -> string + Type: escapeShellArg :: string -> string - Example: - escapeShellArgs ["one" "two three" "four'five"] - => "'one' 'two three' 'four'\\''five'" + Example: + escapeShellArg "esc'ape\nme" + => "'esc'\\''ape\nme'" */ - escapeShellArgs = concatMapStringsSep " " escapeShellArg; + escapeShellArg = arg: "'${replaceStrings [ "'" ] [ "'\\''" ] (toString arg)}'"; - /* Test whether the given name is a valid POSIX shell variable name. + /* + Quote all arguments to be safely passed to the Bourne shell. - Type: string -> bool + Type: escapeShellArgs :: [string] -> string - Example: - isValidPosixName "foo_bar000" - => true - isValidPosixName "0-bad.jpg" - => false + Example: + escapeShellArgs ["one" "two three" "four'five"] + => "'one' 'two three' 'four'\\''five'" */ - isValidPosixName = name: match "[a-zA-Z_][a-zA-Z0-9_]*" name != null; - - /* Translate a Nix value into a shell variable declaration, with proper escaping. + escapeShellArgs = concatMapStringsSep " " escapeShellArg; - The value can be a string (mapped to a regular variable), a list of strings - (mapped to a Bash-style array) or an attribute set of strings (mapped to a - Bash-style associative array). Note that "string" includes string-coercible - values like paths or derivations. + /* + Test whether the given name is a valid POSIX shell variable name. - Strings are translated into POSIX sh-compatible code; lists and attribute sets - assume a shell that understands Bash syntax (e.g. Bash or ZSH). + Type: string -> bool - Type: string -> (string | listOf string | attrsOf string) -> string + Example: + isValidPosixName "foo_bar000" + => true + isValidPosixName "0-bad.jpg" + => false + */ + isValidPosixName = name: match "[a-zA-Z_][a-zA-Z0-9_]*" name != null; - Example: - '' - ${toShellVar "foo" "some string"} - [[ "$foo" == "some string" ]] - '' - */ - toShellVar = name: value: - lib.throwIfNot (isValidPosixName name) - "toShellVar: ${name} is not a valid shell variable name" - (if isAttrs value && !isStringLike value then - "declare -A ${name}=(${ - concatStringsSep " " - (lib.mapAttrsToList (n: v: "[${escapeShellArg n}]=${escapeShellArg v}") - value) - })" - else if isList value then - "declare -a ${name}=(${escapeShellArgs value})" - else - "${name}=${escapeShellArg value}"); + /* + Translate a Nix value into a shell variable declaration, with proper escaping. + + The value can be a string (mapped to a regular variable), a list of strings + (mapped to a Bash-style array) or an attribute set of strings (mapped to a + Bash-style associative array). Note that "string" includes string-coercible + values like paths or derivations. + + Strings are translated into POSIX sh-compatible code; lists and attribute sets + assume a shell that understands Bash syntax (e.g. Bash or ZSH). + + Type: string -> (string | listOf string | attrsOf string) -> string + + Example: + '' + ${toShellVar "foo" "some string"} + [[ "$foo" == "some string" ]] + '' + */ + toShellVar = + name: value: + lib.throwIfNot (isValidPosixName name) "toShellVar: ${name} is not a valid shell variable name" ( + if isAttrs value && !isStringLike value then + "declare -A ${name}=(${ + concatStringsSep " " (lib.mapAttrsToList (n: v: "[${escapeShellArg n}]=${escapeShellArg v}") value) + })" + else if isList value then + "declare -a ${name}=(${escapeShellArgs value})" + else + "${name}=${escapeShellArg value}" + ); - /* Translate an attribute set into corresponding shell variable declarations - using `toShellVar`. + /* + Translate an attribute set into corresponding shell variable declarations + using `toShellVar`. - Type: attrsOf (string | listOf string | attrsOf string) -> string + Type: attrsOf (string | listOf string | attrsOf string) -> string - Example: - let - foo = "value"; - bar = foo; - in '' - ${toShellVars { inherit foo bar; }} - [[ "$foo" == "$bar" ]] - '' + Example: + let + foo = "value"; + bar = foo; + in '' + ${toShellVars { inherit foo bar; }} + [[ "$foo" == "$bar" ]] + '' */ - toShellVars = vars: - concatStringsSep "\n" (lib.mapAttrsToList toShellVar vars); + toShellVars = vars: concatStringsSep "\n" (lib.mapAttrsToList toShellVar vars); - /* Turn a string into a Nix expression representing that string + /* + Turn a string into a Nix expression representing that string - Type: string -> string + Type: string -> string - Example: - escapeNixString "hello\${}\n" - => "\"hello\\\${}\\n\"" + Example: + escapeNixString "hello\${}\n" + => "\"hello\\\${}\\n\"" */ escapeNixString = s: escape [ "$" ] (toJSON s); - /* Turn a string into an exact regular expression + /* + Turn a string into an exact regular expression - Type: string -> string + Type: string -> string - Example: - escapeRegex "[^a-z]*" - => "\\[\\^a-z]\\*" + Example: + escapeRegex "[^a-z]*" + => "\\[\\^a-z]\\*" */ escapeRegex = escape (stringToCharacters "\\[{()^$?*+|."); - /* Quotes a string if it can't be used as an identifier directly. + /* + Quotes a string if it can't be used as an identifier directly. - Type: string -> string + Type: string -> string - Example: - escapeNixIdentifier "hello" - => "hello" - escapeNixIdentifier "0abc" - => "\"0abc\"" + Example: + escapeNixIdentifier "hello" + => "hello" + escapeNixIdentifier "0abc" + => "\"0abc\"" */ - escapeNixIdentifier = s: + escapeNixIdentifier = + s: # Regex from https://github.com/NixOS/nix/blob/d048577909e383439c2549e849c5c2f2016c997e/src/libexpr/lexer.l#L91 if match "[a-zA-Z_][a-zA-Z0-9_'-]*" s != null then s else escapeNixString s; - /* Escapes a string such that it is safe to include verbatim in an XML - document. - - Type: string -> string - - Example: - escapeXML ''"test" 'test' < & >'' - => ""test" 'test' < & >" - */ - escapeXML = builtins.replaceStrings [ ''"'' "'" "<" ">" "&" ] [ - """ - "'" - "<" - ">" - "&" - ]; + /* + Escapes a string such that it is safe to include verbatim in an XML + document. + + Type: string -> string + + Example: + escapeXML ''"test" 'test' < & >'' + => ""test" 'test' < & >" + */ + escapeXML = + builtins.replaceStrings + [ + ''"'' + "'" + "<" + ">" + "&" + ] + [ + """ + "'" + "<" + ">" + "&" + ]; # warning added 12-12-2022 - replaceChars = - lib.warn "lib.replaceChars is a deprecated alias of lib.replaceStrings." - builtins.replaceStrings; + replaceChars = lib.warn "lib.replaceChars is a deprecated alias of lib.replaceStrings." builtins.replaceStrings; # Case conversion utilities. lowerChars = stringToCharacters "abcdefghijklmnopqrstuvwxyz"; upperChars = stringToCharacters "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - /* Converts an ASCII string to lower-case. + /* + Converts an ASCII string to lower-case. - Type: toLower :: string -> string + Type: toLower :: string -> string - Example: - toLower "HOME" - => "home" + Example: + toLower "HOME" + => "home" */ toLower = replaceStrings upperChars lowerChars; - /* Converts an ASCII string to upper-case. + /* + Converts an ASCII string to upper-case. - Type: toUpper :: string -> string + Type: toUpper :: string -> string - Example: - toUpper "home" - => "HOME" + Example: + toUpper "home" + => "HOME" */ toUpper = replaceStrings lowerChars upperChars; - /* Appends string context from another string. This is an implementation - detail of Nix and should be used carefully. + /* + Appends string context from another string. This is an implementation + detail of Nix and should be used carefully. - Strings in Nix carry an invisible `context` which is a list of strings - representing store paths. If the string is later used in a derivation - attribute, the derivation will properly populate the inputDrvs and - inputSrcs. + Strings in Nix carry an invisible `context` which is a list of strings + representing store paths. If the string is later used in a derivation + attribute, the derivation will properly populate the inputDrvs and + inputSrcs. - Example: - pkgs = import { }; - addContextFrom pkgs.coreutils "bar" - => "bar" + Example: + pkgs = import { }; + addContextFrom pkgs.coreutils "bar" + => "bar" */ addContextFrom = a: b: substring 0 0 a + b; - /* Cut a string with a separator and produces a list of strings which - were separated by this separator. + /* + Cut a string with a separator and produces a list of strings which + were separated by this separator. - Example: - splitString "." "foo.bar.baz" - => [ "foo" "bar" "baz" ] - splitString "/" "/usr/local/bin" - => [ "" "usr" "local" "bin" ] + Example: + splitString "." "foo.bar.baz" + => [ "foo" "bar" "baz" ] + splitString "/" "/usr/local/bin" + => [ "" "usr" "local" "bin" ] */ - splitString = sep: s: + splitString = + sep: s: let - splits = builtins.filter builtins.isString - (builtins.split (escapeRegex (toString sep)) (toString s)); - in map (addContextFrom s) splits; + splits = builtins.filter builtins.isString ( + builtins.split (escapeRegex (toString sep)) (toString s) + ); + in + map (addContextFrom s) splits; - /* Return a string without the specified prefix, if the prefix matches. + /* + Return a string without the specified prefix, if the prefix matches. - Type: string -> string -> string + Type: string -> string -> string - Example: - removePrefix "foo." "foo.bar.baz" - => "bar.baz" - removePrefix "xxx" "foo.bar.baz" - => "foo.bar.baz" + Example: + removePrefix "foo." "foo.bar.baz" + => "bar.baz" + removePrefix "xxx" "foo.bar.baz" + => "foo.bar.baz" */ removePrefix = # Prefix to remove if it matches @@ -690,29 +762,33 @@ in rec { str: # Before 23.05, paths would be copied to the store before converting them # to strings and comparing. This was surprising and confusing. - warnIf (isPath prefix) '' - lib.strings.removePrefix: The first argument (${ - toString prefix - }) is a path value, but only strings are supported. - There is almost certainly a bug in the calling code, since this function never removes any prefix in such a case. - This function also copies the path to the Nix store, which may not be what you want. - This behavior is deprecated and will throw an error in the future.'' - (let preLen = stringLength prefix; - in if substring 0 preLen str == prefix then - # -1 will take the string until the end - substring preLen (-1) str - else - str); + warnIf (isPath prefix) + '' + lib.strings.removePrefix: The first argument (${toString prefix}) is a path value, but only strings are supported. + There is almost certainly a bug in the calling code, since this function never removes any prefix in such a case. + This function also copies the path to the Nix store, which may not be what you want. + This behavior is deprecated and will throw an error in the future.'' + ( + let + preLen = stringLength prefix; + in + if substring 0 preLen str == prefix then + # -1 will take the string until the end + substring preLen (-1) str + else + str + ); - /* Return a string without the specified suffix, if the suffix matches. + /* + Return a string without the specified suffix, if the suffix matches. - Type: string -> string -> string + Type: string -> string -> string - Example: - removeSuffix "front" "homefront" - => "home" - removeSuffix "xxx" "homefront" - => "homefront" + Example: + removeSuffix "front" "homefront" + => "home" + removeSuffix "xxx" "homefront" + => "homefront" */ removeSuffix = # Suffix to remove if it matches @@ -721,502 +797,571 @@ in rec { str: # Before 23.05, paths would be copied to the store before converting them # to strings and comparing. This was surprising and confusing. - warnIf (isPath suffix) '' - lib.strings.removeSuffix: The first argument (${ - toString suffix - }) is a path value, but only strings are supported. - There is almost certainly a bug in the calling code, since this function never removes any suffix in such a case. - This function also copies the path to the Nix store, which may not be what you want. - This behavior is deprecated and will throw an error in the future.'' - (let - sufLen = stringLength suffix; - sLen = stringLength str; - in if sufLen <= sLen && suffix == substring (sLen - sufLen) sufLen str then - substring 0 (sLen - sufLen) str - else - str); + warnIf (isPath suffix) + '' + lib.strings.removeSuffix: The first argument (${toString suffix}) is a path value, but only strings are supported. + There is almost certainly a bug in the calling code, since this function never removes any suffix in such a case. + This function also copies the path to the Nix store, which may not be what you want. + This behavior is deprecated and will throw an error in the future.'' + ( + let + sufLen = stringLength suffix; + sLen = stringLength str; + in + if sufLen <= sLen && suffix == substring (sLen - sufLen) sufLen str then + substring 0 (sLen - sufLen) str + else + str + ); - /* Return true if string v1 denotes a version older than v2. + /* + Return true if string v1 denotes a version older than v2. - Example: - versionOlder "1.1" "1.2" - => true - versionOlder "1.1" "1.1" - => false + Example: + versionOlder "1.1" "1.2" + => true + versionOlder "1.1" "1.1" + => false */ versionOlder = v1: v2: compareVersions v2 v1 == 1; - /* Return true if string v1 denotes a version equal to or newer than v2. + /* + Return true if string v1 denotes a version equal to or newer than v2. - Example: - versionAtLeast "1.1" "1.0" - => true - versionAtLeast "1.1" "1.1" - => true - versionAtLeast "1.1" "1.2" - => false + Example: + versionAtLeast "1.1" "1.0" + => true + versionAtLeast "1.1" "1.1" + => true + versionAtLeast "1.1" "1.2" + => false */ versionAtLeast = v1: v2: !versionOlder v1 v2; - /* This function takes an argument that's either a derivation or a - derivation's "name" attribute and extracts the name part from that - argument. + /* + This function takes an argument that's either a derivation or a + derivation's "name" attribute and extracts the name part from that + argument. - Example: - getName "youtube-dl-2016.01.01" - => "youtube-dl" - getName pkgs.youtube-dl - => "youtube-dl" - */ - getName = let parse = drv: (parseDrvName drv).name; - in x: if isString x then parse x else x.pname or (parse x.name); - - /* This function takes an argument that's either a derivation or a - derivation's "name" attribute and extracts the version part from that - argument. - - Example: - getVersion "youtube-dl-2016.01.01" - => "2016.01.01" - getVersion pkgs.youtube-dl - => "2016.01.01" + Example: + getName "youtube-dl-2016.01.01" + => "youtube-dl" + getName pkgs.youtube-dl + => "youtube-dl" */ - getVersion = let parse = drv: (parseDrvName drv).version; - in x: if isString x then parse x else x.version or (parse x.name); - - /* Extract name with version from URL. Ask for separator which is - supposed to start extension. - - Example: - nameFromURL "https://nixos.org/releases/nix/nix-1.7/nix-1.7-x86_64-linux.tar.bz2" "-" - => "nix" - nameFromURL "https://nixos.org/releases/nix/nix-1.7/nix-1.7-x86_64-linux.tar.bz2" "_" - => "nix-1.7-x86" - */ - nameFromURL = url: sep: + getName = + let + parse = drv: (parseDrvName drv).name; + in + x: if isString x then parse x else x.pname or (parse x.name); + + /* + This function takes an argument that's either a derivation or a + derivation's "name" attribute and extracts the version part from that + argument. + + Example: + getVersion "youtube-dl-2016.01.01" + => "2016.01.01" + getVersion pkgs.youtube-dl + => "2016.01.01" + */ + getVersion = + let + parse = drv: (parseDrvName drv).version; + in + x: if isString x then parse x else x.version or (parse x.name); + + /* + Extract name with version from URL. Ask for separator which is + supposed to start extension. + + Example: + nameFromURL "https://nixos.org/releases/nix/nix-1.7/nix-1.7-x86_64-linux.tar.bz2" "-" + => "nix" + nameFromURL "https://nixos.org/releases/nix/nix-1.7/nix-1.7-x86_64-linux.tar.bz2" "_" + => "nix-1.7-x86" + */ + nameFromURL = + url: sep: let components = splitString "/" url; filename = lib.last components; name = head (splitString sep filename); - in assert name != filename; name; + in + assert name != filename; + name; - /* Create a "-D:=" string that can be passed to typical - CMake invocations. + /* + Create a "-D:=" string that can be passed to typical + CMake invocations. - Type: cmakeOptionType :: string -> string -> string -> string + Type: cmakeOptionType :: string -> string -> string -> string - @param feature The feature to be set - @param type The type of the feature to be set, as described in - https://cmake.org/cmake/help/latest/command/set.html - the possible values (case insensitive) are: - BOOL FILEPATH PATH STRING INTERNAL - @param value The desired value + @param feature The feature to be set + @param type The type of the feature to be set, as described in + https://cmake.org/cmake/help/latest/command/set.html + the possible values (case insensitive) are: + BOOL FILEPATH PATH STRING INTERNAL + @param value The desired value - Example: - cmakeOptionType "string" "ENGINE" "sdl2" - => "-DENGINE:STRING=sdl2" + Example: + cmakeOptionType "string" "ENGINE" "sdl2" + => "-DENGINE:STRING=sdl2" */ cmakeOptionType = - let types = [ "BOOL" "FILEPATH" "PATH" "STRING" "INTERNAL" ]; - in type: feature: value: + let + types = [ + "BOOL" + "FILEPATH" + "PATH" + "STRING" + "INTERNAL" + ]; + in + type: feature: value: assert (elem (toUpper type) types); assert (isString feature); assert (isString value); "-D${feature}:${toUpper type}=${value}"; - /* Create a -D={TRUE,FALSE} string that can be passed to typical - CMake invocations. + /* + Create a -D={TRUE,FALSE} string that can be passed to typical + CMake invocations. - Type: cmakeBool :: string -> bool -> string + Type: cmakeBool :: string -> bool -> string - @param condition The condition to be made true or false - @param flag The controlling flag of the condition + @param condition The condition to be made true or false + @param flag The controlling flag of the condition - Example: - cmakeBool "ENABLE_STATIC_LIBS" false - => "-DENABLESTATIC_LIBS:BOOL=FALSE" + Example: + cmakeBool "ENABLE_STATIC_LIBS" false + => "-DENABLESTATIC_LIBS:BOOL=FALSE" */ - cmakeBool = condition: flag: + cmakeBool = + condition: flag: assert (lib.isString condition); assert (lib.isBool flag); cmakeOptionType "bool" condition (lib.toUpper (lib.boolToString flag)); - /* Create a -D:STRING= string that can be passed to typical - CMake invocations. - This is the most typical usage, so it deserves a special case. + /* + Create a -D:STRING= string that can be passed to typical + CMake invocations. + This is the most typical usage, so it deserves a special case. - Type: cmakeFeature :: string -> string -> string + Type: cmakeFeature :: string -> string -> string - @param condition The condition to be made true or false - @param flag The controlling flag of the condition + @param condition The condition to be made true or false + @param flag The controlling flag of the condition - Example: - cmakeFeature "MODULES" "badblock" - => "-DMODULES:STRING=badblock" + Example: + cmakeFeature "MODULES" "badblock" + => "-DMODULES:STRING=badblock" */ - cmakeFeature = feature: value: + cmakeFeature = + feature: value: assert (lib.isString feature); assert (lib.isString value); cmakeOptionType "string" feature value; - /* Create a -D= string that can be passed to typical Meson - invocations. + /* + Create a -D= string that can be passed to typical Meson + invocations. - Type: mesonOption :: string -> string -> string + Type: mesonOption :: string -> string -> string - @param feature The feature to be set - @param value The desired value + @param feature The feature to be set + @param value The desired value - Example: - mesonOption "engine" "opengl" - => "-Dengine=opengl" + Example: + mesonOption "engine" "opengl" + => "-Dengine=opengl" */ - mesonOption = feature: value: + mesonOption = + feature: value: assert (lib.isString feature); assert (lib.isString value); "-D${feature}=${value}"; - /* Create a -D={true,false} string that can be passed to typical - Meson invocations. + /* + Create a -D={true,false} string that can be passed to typical + Meson invocations. - Type: mesonBool :: string -> bool -> string + Type: mesonBool :: string -> bool -> string - @param condition The condition to be made true or false - @param flag The controlling flag of the condition + @param condition The condition to be made true or false + @param flag The controlling flag of the condition - Example: - mesonBool "hardened" true - => "-Dhardened=true" - mesonBool "static" false - => "-Dstatic=false" + Example: + mesonBool "hardened" true + => "-Dhardened=true" + mesonBool "static" false + => "-Dstatic=false" */ - mesonBool = condition: flag: + mesonBool = + condition: flag: assert (lib.isString condition); assert (lib.isBool flag); mesonOption condition (lib.boolToString flag); - /* Create a -D={enabled,disabled} string that can be passed to - typical Meson invocations. + /* + Create a -D={enabled,disabled} string that can be passed to + typical Meson invocations. - Type: mesonEnable :: string -> bool -> string + Type: mesonEnable :: string -> bool -> string - @param feature The feature to be enabled or disabled - @param flag The controlling flag + @param feature The feature to be enabled or disabled + @param flag The controlling flag - Example: - mesonEnable "docs" true - => "-Ddocs=enabled" - mesonEnable "savage" false - => "-Dsavage=disabled" + Example: + mesonEnable "docs" true + => "-Ddocs=enabled" + mesonEnable "savage" false + => "-Dsavage=disabled" */ - mesonEnable = feature: flag: + mesonEnable = + feature: flag: assert (lib.isString feature); assert (lib.isBool flag); mesonOption feature (if flag then "enabled" else "disabled"); - /* Create an --{enable,disable}- string that can be passed to - standard GNU Autoconf scripts. + /* + Create an --{enable,disable}- string that can be passed to + standard GNU Autoconf scripts. - Example: - enableFeature true "shared" - => "--enable-shared" - enableFeature false "shared" - => "--disable-shared" + Example: + enableFeature true "shared" + => "--enable-shared" + enableFeature false "shared" + => "--disable-shared" */ - enableFeature = flag: feature: + enableFeature = + flag: feature: assert lib.isBool flag; assert lib.isString feature; # e.g. passing openssl instead of "openssl" "--${if flag then "enable" else "disable"}-${feature}"; - /* Create an --{enable-=,disable-} string that can be passed to - standard GNU Autoconf scripts. + /* + Create an --{enable-=,disable-} string that can be passed to + standard GNU Autoconf scripts. - Example: - enableFeatureAs true "shared" "foo" - => "--enable-shared=foo" - enableFeatureAs false "shared" (throw "ignored") - => "--disable-shared" + Example: + enableFeatureAs true "shared" "foo" + => "--enable-shared=foo" + enableFeatureAs false "shared" (throw "ignored") + => "--disable-shared" */ - enableFeatureAs = flag: feature: value: + enableFeatureAs = + flag: feature: value: enableFeature flag feature + optionalString flag "=${value}"; - /* Create an --{with,without}- string that can be passed to - standard GNU Autoconf scripts. + /* + Create an --{with,without}- string that can be passed to + standard GNU Autoconf scripts. - Example: - withFeature true "shared" - => "--with-shared" - withFeature false "shared" - => "--without-shared" + Example: + withFeature true "shared" + => "--with-shared" + withFeature false "shared" + => "--without-shared" */ - withFeature = flag: feature: + withFeature = + flag: feature: assert isString feature; # e.g. passing openssl instead of "openssl" "--${if flag then "with" else "without"}-${feature}"; - /* Create an --{with-=,without-} string that can be passed to - standard GNU Autoconf scripts. + /* + Create an --{with-=,without-} string that can be passed to + standard GNU Autoconf scripts. - Example: - withFeatureAs true "shared" "foo" - => "--with-shared=foo" - withFeatureAs false "shared" (throw "ignored") - => "--without-shared" + Example: + withFeatureAs true "shared" "foo" + => "--with-shared=foo" + withFeatureAs false "shared" (throw "ignored") + => "--without-shared" */ - withFeatureAs = flag: feature: value: + withFeatureAs = + flag: feature: value: withFeature flag feature + optionalString flag "=${value}"; - /* Create a fixed width string with additional prefix to match - required width. + /* + Create a fixed width string with additional prefix to match + required width. - This function will fail if the input string is longer than the - requested length. + This function will fail if the input string is longer than the + requested length. - Type: fixedWidthString :: int -> string -> string -> string + Type: fixedWidthString :: int -> string -> string -> string - Example: - fixedWidthString 5 "0" (toString 15) - => "00015" + Example: + fixedWidthString 5 "0" (toString 15) + => "00015" */ - fixedWidthString = width: filler: str: + fixedWidthString = + width: filler: str: let strw = lib.stringLength str; reqWidth = width - (lib.stringLength filler); - in assert lib.assertMsg (strw <= width) - "fixedWidthString: requested string length (${ - toString width - }) must not be shorter than actual length (${toString strw})"; - if strw == width then - str - else - filler + fixedWidthString reqWidth filler str; + in + assert lib.assertMsg (strw <= width) + "fixedWidthString: requested string length (${toString width}) must not be shorter than actual length (${toString strw})"; + if strw == width then str else filler + fixedWidthString reqWidth filler str; - /* Format a number adding leading zeroes up to fixed width. + /* + Format a number adding leading zeroes up to fixed width. - Example: - fixedWidthNumber 5 15 - => "00015" + Example: + fixedWidthNumber 5 15 + => "00015" */ fixedWidthNumber = width: n: fixedWidthString width "0" (toString n); - /* Convert a float to a string, but emit a warning when precision is lost - during the conversion + /* + Convert a float to a string, but emit a warning when precision is lost + during the conversion - Example: - floatToString 0.000001 - => "0.000001" - floatToString 0.0000001 - => trace: warning: Imprecise conversion from float to string 0.000000 - "0.000000" + Example: + floatToString 0.000001 + => "0.000001" + floatToString 0.0000001 + => trace: warning: Imprecise conversion from float to string 0.000000 + "0.000000" */ - floatToString = float: + floatToString = + float: let result = toString float; precise = float == fromJSON result; - in lib.warnIf (!precise) - "Imprecise conversion from float to string ${result}" result; + in + lib.warnIf (!precise) "Imprecise conversion from float to string ${result}" result; - /* Soft-deprecated function. While the original implementation is available as - isConvertibleWithToString, consider using isStringLike instead, if suitable. + /* + Soft-deprecated function. While the original implementation is available as + isConvertibleWithToString, consider using isStringLike instead, if suitable. */ - isCoercibleToString = lib.warnIf (lib.isInOldestRelease 2305) - "lib.strings.isCoercibleToString is deprecated in favor of either isStringLike or isConvertibleWithToString. Only use the latter if it needs to return true for null, numbers, booleans and list of similarly coercibles." - isConvertibleWithToString; + isCoercibleToString = + lib.warnIf (lib.isInOldestRelease 2305) + "lib.strings.isCoercibleToString is deprecated in favor of either isStringLike or isConvertibleWithToString. Only use the latter if it needs to return true for null, numbers, booleans and list of similarly coercibles." + isConvertibleWithToString; - /* Check whether a list or other value can be passed to toString. + /* + Check whether a list or other value can be passed to toString. - Many types of value are coercible to string this way, including int, float, - null, bool, list of similarly coercible values. + Many types of value are coercible to string this way, including int, float, + null, bool, list of similarly coercible values. */ - isConvertibleWithToString = let types = [ "null" "int" "float" "bool" ]; - in x: - isStringLike x || elem (typeOf x) types - || (isList x && lib.all isConvertibleWithToString x); + isConvertibleWithToString = + let + types = [ + "null" + "int" + "float" + "bool" + ]; + in + x: isStringLike x || elem (typeOf x) types || (isList x && lib.all isConvertibleWithToString x); - /* Check whether a value can be coerced to a string. - The value must be a string, path, or attribute set. + /* + Check whether a value can be coerced to a string. + The value must be a string, path, or attribute set. - String-like values can be used without explicit conversion in - string interpolations and in most functions that expect a string. + String-like values can be used without explicit conversion in + string interpolations and in most functions that expect a string. */ isStringLike = x: isString x || isPath x || x ? outPath || x ? __toString; - /* Check whether a value is a store path. - - Example: - isStorePath "/nix/store/d945ibfx9x185xf04b890y4f9g3cbb63-python-2.7.11/bin/python" - => false - isStorePath "/nix/store/d945ibfx9x185xf04b890y4f9g3cbb63-python-2.7.11" - => true - isStorePath pkgs.python - => true - isStorePath [] || isStorePath 42 || isStorePath {} || … - => false - */ - isStorePath = x: + /* + Check whether a value is a store path. + + Example: + isStorePath "/nix/store/d945ibfx9x185xf04b890y4f9g3cbb63-python-2.7.11/bin/python" + => false + isStorePath "/nix/store/d945ibfx9x185xf04b890y4f9g3cbb63-python-2.7.11" + => true + isStorePath pkgs.python + => true + isStorePath [] || isStorePath 42 || isStorePath {} || … + => false + */ + isStorePath = + x: if isStringLike x then - let str = toString x; in substring 0 1 str == "/" && dirOf str == storeDir + let + str = toString x; + in + substring 0 1 str == "/" && dirOf str == storeDir else false; - /* Parse a string as an int. Does not support parsing of integers with preceding zero due to - ambiguity between zero-padded and octal numbers. See toIntBase10. + /* + Parse a string as an int. Does not support parsing of integers with preceding zero due to + ambiguity between zero-padded and octal numbers. See toIntBase10. - Type: string -> int + Type: string -> int - Example: + Example: - toInt "1337" - => 1337 + toInt "1337" + => 1337 - toInt "-4" - => -4 + toInt "-4" + => -4 - toInt " 123 " - => 123 + toInt " 123 " + => 123 - toInt "00024" - => error: Ambiguity in interpretation of 00024 between octal and zero padded integer. + toInt "00024" + => error: Ambiguity in interpretation of 00024 between octal and zero padded integer. - toInt "3.14" - => error: floating point JSON numbers are not supported + toInt "3.14" + => error: floating point JSON numbers are not supported */ - toInt = let - matchStripInput = match "[[:space:]]*(-?[[:digit:]]+)[[:space:]]*"; - matchLeadingZero = match "0[[:digit:]]+"; - in str: - let - # RegEx: Match any leading whitespace, possibly a '-', one or more digits, - # and finally match any trailing whitespace. - strippedInput = matchStripInput str; + toInt = + let + matchStripInput = match "[[:space:]]*(-?[[:digit:]]+)[[:space:]]*"; + matchLeadingZero = match "0[[:digit:]]+"; + in + str: + let + # RegEx: Match any leading whitespace, possibly a '-', one or more digits, + # and finally match any trailing whitespace. + strippedInput = matchStripInput str; - # RegEx: Match a leading '0' then one or more digits. - isLeadingZero = matchLeadingZero (head strippedInput) == [ ]; + # RegEx: Match a leading '0' then one or more digits. + isLeadingZero = matchLeadingZero (head strippedInput) == [ ]; - # Attempt to parse input - parsedInput = fromJSON (head strippedInput); + # Attempt to parse input + parsedInput = fromJSON (head strippedInput); - generalError = "toInt: Could not convert ${escapeNixString str} to int."; + generalError = "toInt: Could not convert ${escapeNixString str} to int."; + in # Error on presence of non digit characters. - in if strippedInput == null then - throw generalError + if strippedInput == null then + throw generalError # Error on presence of leading zero/octal ambiguity. - else if isLeadingZero then - throw "toInt: Ambiguity in interpretation of ${ - escapeNixString str - } between octal and zero padded integer." + else if isLeadingZero then + throw "toInt: Ambiguity in interpretation of ${escapeNixString str} between octal and zero padded integer." # Error if parse function fails. - else if !isInt parsedInput then - throw generalError + else if !isInt parsedInput then + throw generalError # Return result. - else - parsedInput; + else + parsedInput; - /* Parse a string as a base 10 int. This supports parsing of zero-padded integers. + /* + Parse a string as a base 10 int. This supports parsing of zero-padded integers. - Type: string -> int + Type: string -> int - Example: - toIntBase10 "1337" - => 1337 + Example: + toIntBase10 "1337" + => 1337 - toIntBase10 "-4" - => -4 + toIntBase10 "-4" + => -4 - toIntBase10 " 123 " - => 123 + toIntBase10 " 123 " + => 123 - toIntBase10 "00024" - => 24 + toIntBase10 "00024" + => 24 - toIntBase10 "3.14" - => error: floating point JSON numbers are not supported + toIntBase10 "3.14" + => error: floating point JSON numbers are not supported */ - toIntBase10 = let - matchStripInput = match "[[:space:]]*0*(-?[[:digit:]]+)[[:space:]]*"; - matchZero = match "0+"; - in str: - let - # RegEx: Match any leading whitespace, then match any zero padding, - # capture possibly a '-' followed by one or more digits, - # and finally match any trailing whitespace. - strippedInput = matchStripInput str; + toIntBase10 = + let + matchStripInput = match "[[:space:]]*0*(-?[[:digit:]]+)[[:space:]]*"; + matchZero = match "0+"; + in + str: + let + # RegEx: Match any leading whitespace, then match any zero padding, + # capture possibly a '-' followed by one or more digits, + # and finally match any trailing whitespace. + strippedInput = matchStripInput str; - # RegEx: Match at least one '0'. - isZero = matchZero (head strippedInput) == [ ]; + # RegEx: Match at least one '0'. + isZero = matchZero (head strippedInput) == [ ]; - # Attempt to parse input - parsedInput = fromJSON (head strippedInput); + # Attempt to parse input + parsedInput = fromJSON (head strippedInput); - generalError = - "toIntBase10: Could not convert ${escapeNixString str} to int."; + generalError = "toIntBase10: Could not convert ${escapeNixString str} to int."; + in # Error on presence of non digit characters. - in if strippedInput == null then - throw generalError + if strippedInput == null then + throw generalError # In the special case zero-padded zero (00000), return early. - else if isZero then - 0 + else if isZero then + 0 # Error if parse function fails. - else if !isInt parsedInput then - throw generalError + else if !isInt parsedInput then + throw generalError # Return result. - else - parsedInput; - - /* Read a list of paths from `file`, relative to the `rootPath`. - Lines beginning with `#` are treated as comments and ignored. - Whitespace is significant. - - NOTE: This function is not performant and should be avoided. - - Example: - readPathsFromFile /prefix - ./pkgs/development/libraries/qt-5/5.4/qtbase/series - => [ "/prefix/dlopen-resolv.patch" "/prefix/tzdir.patch" - "/prefix/dlopen-libXcursor.patch" "/prefix/dlopen-openssl.patch" - "/prefix/dlopen-dbus.patch" "/prefix/xdg-config-dirs.patch" - "/prefix/nix-profiles-library-paths.patch" - "/prefix/compose-search-path.patch" ] - */ - readPathsFromFile = - lib.warn "lib.readPathsFromFile is deprecated, use a list instead." - (rootPath: file: - let - lines = lib.splitString "\n" (readFile file); - removeComments = - lib.filter (line: line != "" && !(lib.hasPrefix "#" line)); - relativePaths = removeComments lines; - absolutePaths = map (path: rootPath + "/${path}") relativePaths; - in absolutePaths); + else + parsedInput; + + /* + Read a list of paths from `file`, relative to the `rootPath`. + Lines beginning with `#` are treated as comments and ignored. + Whitespace is significant. + + NOTE: This function is not performant and should be avoided. + + Example: + readPathsFromFile /prefix + ./pkgs/development/libraries/qt-5/5.4/qtbase/series + => [ "/prefix/dlopen-resolv.patch" "/prefix/tzdir.patch" + "/prefix/dlopen-libXcursor.patch" "/prefix/dlopen-openssl.patch" + "/prefix/dlopen-dbus.patch" "/prefix/xdg-config-dirs.patch" + "/prefix/nix-profiles-library-paths.patch" + "/prefix/compose-search-path.patch" ] + */ + readPathsFromFile = lib.warn "lib.readPathsFromFile is deprecated, use a list instead." ( + rootPath: file: + let + lines = lib.splitString "\n" (readFile file); + removeComments = lib.filter (line: line != "" && !(lib.hasPrefix "#" line)); + relativePaths = removeComments lines; + absolutePaths = map (path: rootPath + "/${path}") relativePaths; + in + absolutePaths + ); - /* Read the contents of a file removing the trailing \n + /* + Read the contents of a file removing the trailing \n - Type: fileContents :: path -> string + Type: fileContents :: path -> string - Example: - $ echo "1.0" > ./version + Example: + $ echo "1.0" > ./version - fileContents ./version - => "1.0" + fileContents ./version + => "1.0" */ fileContents = file: removeSuffix "\n" (readFile file); - /* Creates a valid derivation name from a potentially invalid one. + /* + Creates a valid derivation name from a potentially invalid one. - Type: sanitizeDerivationName :: String -> String + Type: sanitizeDerivationName :: String -> String - Example: - sanitizeDerivationName "../hello.bar # foo" - => "-hello.bar-foo" - sanitizeDerivationName "" - => "unknown" - sanitizeDerivationName pkgs.hello - => "-nix-store-2g75chlbpxlrqn15zlby2dfh8hr9qwbk-hello-2.10" + Example: + sanitizeDerivationName "../hello.bar # foo" + => "-hello.bar-foo" + sanitizeDerivationName "" + => "unknown" + sanitizeDerivationName pkgs.hello + => "-nix-store-2g75chlbpxlrqn15zlby2dfh8hr9qwbk-hello-2.10" */ sanitizeDerivationName = - let okRegex = match "[[:alnum:]+_?=-][[:alnum:]+._?=-]*"; - in string: + let + okRegex = match "[[:alnum:]+_?=-][[:alnum:]+._?=-]*"; + in + string: # First detect the common case of already valid strings, to speed those up if stringLength string <= 207 && okRegex string != null then unsafeDiscardStringContext string @@ -1239,148 +1384,165 @@ in rec { (x: if stringLength x == 0 then "unknown" else x) ]; - /* Computes the Levenshtein distance between two strings. - Complexity O(n*m) where n and m are the lengths of the strings. - Algorithm adjusted from https://stackoverflow.com/a/9750974/6605742 + /* + Computes the Levenshtein distance between two strings. + Complexity O(n*m) where n and m are the lengths of the strings. + Algorithm adjusted from https://stackoverflow.com/a/9750974/6605742 - Type: levenshtein :: string -> string -> int + Type: levenshtein :: string -> string -> int - Example: - levenshtein "foo" "foo" - => 0 - levenshtein "book" "hook" - => 1 - levenshtein "hello" "Heyo" - => 3 - */ - levenshtein = a: b: + Example: + levenshtein "foo" "foo" + => 0 + levenshtein "book" "hook" + => 1 + levenshtein "hello" "Heyo" + => 3 + */ + levenshtein = + a: b: let # Two dimensional array with dimensions (stringLength a + 1, stringLength b + 1) - arr = lib.genList (i: lib.genList (j: dist i j) (stringLength b + 1)) - (stringLength a + 1); + arr = lib.genList (i: lib.genList (j: dist i j) (stringLength b + 1)) (stringLength a + 1); d = x: y: lib.elemAt (lib.elemAt arr x) y; - dist = i: j: - let c = if substring (i - 1) 1 a == substring (j - 1) 1 b then 0 else 1; - in if j == 0 then + dist = + i: j: + let + c = if substring (i - 1) 1 a == substring (j - 1) 1 b then 0 else 1; + in + if j == 0 then i else if i == 0 then j else - lib.min (lib.min (d (i - 1) j + 1) (d i (j - 1) + 1)) - (d (i - 1) (j - 1) + c); - in d (stringLength a) (stringLength b); + lib.min (lib.min (d (i - 1) j + 1) (d i (j - 1) + 1)) (d (i - 1) (j - 1) + c); + in + d (stringLength a) (stringLength b); # Returns the length of the prefix common to both strings. - commonPrefixLength = a: b: + commonPrefixLength = + a: b: let m = lib.min (stringLength a) (stringLength b); - go = i: + go = + i: if i >= m then m else if substring i 1 a == substring i 1 b then go (i + 1) else i; - in go 0; + in + go 0; # Returns the length of the suffix common to both strings. - commonSuffixLength = a: b: + commonSuffixLength = + a: b: let m = lib.min (stringLength a) (stringLength b); - go = i: + go = + i: if i >= m then m - else if substring (stringLength a - i - 1) 1 a - == substring (stringLength b - i - 1) 1 b then + else if substring (stringLength a - i - 1) 1 a == substring (stringLength b - i - 1) 1 b then go (i + 1) else i; - in go 0; - - /* Returns whether the levenshtein distance between two strings is at most some value - Complexity is O(min(n,m)) for k <= 2 and O(n*m) otherwise - - Type: levenshteinAtMost :: int -> string -> string -> bool - - Example: - levenshteinAtMost 0 "foo" "foo" - => true - levenshteinAtMost 1 "foo" "boa" - => false - levenshteinAtMost 2 "foo" "boa" - => true - levenshteinAtMost 2 "This is a sentence" "this is a sentense." - => false - levenshteinAtMost 3 "This is a sentence" "this is a sentense." - => true - */ - levenshteinAtMost = let - infixDifferAtMost1 = x: y: stringLength x <= 1 && stringLength y <= 1; - - # This function takes two strings stripped by their common pre and suffix, - # and returns whether they differ by at most two by Levenshtein distance. - # Because of this stripping, if they do indeed differ by at most two edits, - # we know that those edits were (if at all) done at the start or the end, - # while the middle has to have stayed the same. This fact is used in the - # implementation. - infixDifferAtMost2 = x: y: - let - xlen = stringLength x; - ylen = stringLength y; - # This function is only called with |x| >= |y| and |x| - |y| <= 2, so - # diff is one of 0, 1 or 2 - diff = xlen - ylen; - - # Infix of x and y, stripped by the left and right most character - xinfix = substring 1 (xlen - 2) x; - yinfix = substring 1 (ylen - 2) y; - - # x and y but a character deleted at the left or right - xdelr = substring 0 (xlen - 1) x; - xdell = substring 1 (xlen - 1) x; - ydelr = substring 0 (ylen - 1) y; - ydell = substring 1 (ylen - 1) y; + in + go 0; + + /* + Returns whether the levenshtein distance between two strings is at most some value + Complexity is O(min(n,m)) for k <= 2 and O(n*m) otherwise + + Type: levenshteinAtMost :: int -> string -> string -> bool + + Example: + levenshteinAtMost 0 "foo" "foo" + => true + levenshteinAtMost 1 "foo" "boa" + => false + levenshteinAtMost 2 "foo" "boa" + => true + levenshteinAtMost 2 "This is a sentence" "this is a sentense." + => false + levenshteinAtMost 3 "This is a sentence" "this is a sentense." + => true + */ + levenshteinAtMost = + let + infixDifferAtMost1 = x: y: stringLength x <= 1 && stringLength y <= 1; + + # This function takes two strings stripped by their common pre and suffix, + # and returns whether they differ by at most two by Levenshtein distance. + # Because of this stripping, if they do indeed differ by at most two edits, + # we know that those edits were (if at all) done at the start or the end, + # while the middle has to have stayed the same. This fact is used in the + # implementation. + infixDifferAtMost2 = + x: y: + let + xlen = stringLength x; + ylen = stringLength y; + # This function is only called with |x| >= |y| and |x| - |y| <= 2, so + # diff is one of 0, 1 or 2 + diff = xlen - ylen; + + # Infix of x and y, stripped by the left and right most character + xinfix = substring 1 (xlen - 2) x; + yinfix = substring 1 (ylen - 2) y; + + # x and y but a character deleted at the left or right + xdelr = substring 0 (xlen - 1) x; + xdell = substring 1 (xlen - 1) x; + ydelr = substring 0 (ylen - 1) y; + ydell = substring 1 (ylen - 1) y; + in # A length difference of 2 can only be gotten with 2 delete edits, # which have to have happened at the start and end of x # Example: "abcdef" -> "bcde" - in if diff == 2 then - xinfix == y + if diff == 2 then + xinfix == y # A length difference of 1 can only be gotten with a deletion on the # right and a replacement on the left or vice versa. # Example: "abcdef" -> "bcdez" or "zbcde" - else if diff == 1 then - xinfix == ydelr || xinfix == ydell + else if diff == 1 then + xinfix == ydelr || xinfix == ydell # No length difference can either happen through replacements on both # sides, or a deletion on the left and an insertion on the right or # vice versa # Example: "abcdef" -> "zbcdez" or "bcdefz" or "zabcde" - else - xinfix == yinfix || xdelr == ydell || xdell == ydelr; - in k: - if k <= 0 then - a: b: a == b - else - let - f = a: b: - let - alen = stringLength a; - blen = stringLength b; - prelen = commonPrefixLength a b; - suflen = commonSuffixLength a b; - presuflen = prelen + suflen; - ainfix = substring prelen (alen - presuflen) a; - binfix = substring prelen (blen - presuflen) b; + else + xinfix == yinfix || xdelr == ydell || xdell == ydelr; + in + k: + if k <= 0 then + a: b: a == b + else + let + f = + a: b: + let + alen = stringLength a; + blen = stringLength b; + prelen = commonPrefixLength a b; + suflen = commonSuffixLength a b; + presuflen = prelen + suflen; + ainfix = substring prelen (alen - presuflen) a; + binfix = substring prelen (blen - presuflen) b; + in # Make a be the bigger string - in if alen < blen then - f b a + if alen < blen then + f b a # If a has over k more characters than b, even with k deletes on a, b can't be reached - else if alen - blen > k then - false - else if k == 1 then - infixDifferAtMost1 ainfix binfix - else if k == 2 then - infixDifferAtMost2 ainfix binfix - else - levenshtein ainfix binfix <= k; - in f; + else if alen - blen > k then + false + else if k == 1 then + infixDifferAtMost1 ainfix binfix + else if k == 2 then + infixDifferAtMost2 ainfix binfix + else + levenshtein ainfix binfix <= k; + in + f; } diff --git a/nix/stdlib/systems/architectures.nix b/nix/stdlib/systems/architectures.nix index 80e1a3d..ca20b05 100644 --- a/nix/stdlib/systems/architectures.nix +++ b/nix/stdlib/systems/architectures.nix @@ -1,46 +1,232 @@ -{ lib }: rec { +{ lib }: +rec { # gcc.arch to its features (as in /proc/cpuinfo) features = { # x86_64 Generic # Spec: https://gitlab.com/x86-psABIs/x86-64-ABI/ default = [ ]; x86-64 = [ ]; - x86-64-v2 = [ "sse3" "ssse3" "sse4_1" "sse4_2" ]; - x86-64-v3 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "avx" "avx2" "fma" ]; - x86-64-v4 = - [ "sse3" "ssse3" "sse4_1" "sse4_2" "avx" "avx2" "avx512" "fma" ]; + x86-64-v2 = [ + "sse3" + "ssse3" + "sse4_1" + "sse4_2" + ]; + x86-64-v3 = [ + "sse3" + "ssse3" + "sse4_1" + "sse4_2" + "avx" + "avx2" + "fma" + ]; + x86-64-v4 = [ + "sse3" + "ssse3" + "sse4_1" + "sse4_2" + "avx" + "avx2" + "avx512" + "fma" + ]; # x86_64 Intel - nehalem = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" ]; - westmere = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" ]; - sandybridge = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" ]; - ivybridge = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" ]; - haswell = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "fma" ]; - broadwell = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "fma" ]; - skylake = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "fma" ]; - skylake-avx512 = - [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "avx512" "fma" ]; - cannonlake = - [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "avx512" "fma" ]; - icelake-client = - [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "avx512" "fma" ]; - icelake-server = - [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "avx512" "fma" ]; - cascadelake = - [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "avx512" "fma" ]; - cooperlake = - [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "avx512" "fma" ]; - tigerlake = - [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "avx512" "fma" ]; - alderlake = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "fma" ]; + nehalem = [ + "sse3" + "ssse3" + "sse4_1" + "sse4_2" + "aes" + ]; + westmere = [ + "sse3" + "ssse3" + "sse4_1" + "sse4_2" + "aes" + ]; + sandybridge = [ + "sse3" + "ssse3" + "sse4_1" + "sse4_2" + "aes" + "avx" + ]; + ivybridge = [ + "sse3" + "ssse3" + "sse4_1" + "sse4_2" + "aes" + "avx" + ]; + haswell = [ + "sse3" + "ssse3" + "sse4_1" + "sse4_2" + "aes" + "avx" + "avx2" + "fma" + ]; + broadwell = [ + "sse3" + "ssse3" + "sse4_1" + "sse4_2" + "aes" + "avx" + "avx2" + "fma" + ]; + skylake = [ + "sse3" + "ssse3" + "sse4_1" + "sse4_2" + "aes" + "avx" + "avx2" + "fma" + ]; + skylake-avx512 = [ + "sse3" + "ssse3" + "sse4_1" + "sse4_2" + "aes" + "avx" + "avx2" + "avx512" + "fma" + ]; + cannonlake = [ + "sse3" + "ssse3" + "sse4_1" + "sse4_2" + "aes" + "avx" + "avx2" + "avx512" + "fma" + ]; + icelake-client = [ + "sse3" + "ssse3" + "sse4_1" + "sse4_2" + "aes" + "avx" + "avx2" + "avx512" + "fma" + ]; + icelake-server = [ + "sse3" + "ssse3" + "sse4_1" + "sse4_2" + "aes" + "avx" + "avx2" + "avx512" + "fma" + ]; + cascadelake = [ + "sse3" + "ssse3" + "sse4_1" + "sse4_2" + "aes" + "avx" + "avx2" + "avx512" + "fma" + ]; + cooperlake = [ + "sse3" + "ssse3" + "sse4_1" + "sse4_2" + "aes" + "avx" + "avx2" + "avx512" + "fma" + ]; + tigerlake = [ + "sse3" + "ssse3" + "sse4_1" + "sse4_2" + "aes" + "avx" + "avx2" + "avx512" + "fma" + ]; + alderlake = [ + "sse3" + "ssse3" + "sse4_1" + "sse4_2" + "aes" + "avx" + "avx2" + "fma" + ]; # x86_64 AMD - btver1 = [ "sse3" "ssse3" "sse4_1" "sse4_2" ]; - btver2 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" ]; - bdver1 = - [ "sse3" "ssse3" "sse4_1" "sse4_2" "sse4a" "aes" "avx" "fma" "fma4" ]; - bdver2 = - [ "sse3" "ssse3" "sse4_1" "sse4_2" "sse4a" "aes" "avx" "fma" "fma4" ]; - bdver3 = - [ "sse3" "ssse3" "sse4_1" "sse4_2" "sse4a" "aes" "avx" "fma" "fma4" ]; + btver1 = [ + "sse3" + "ssse3" + "sse4_1" + "sse4_2" + ]; + btver2 = [ + "sse3" + "ssse3" + "sse4_1" + "sse4_2" + "aes" + "avx" + ]; + bdver1 = [ + "sse3" + "ssse3" + "sse4_1" + "sse4_2" + "sse4a" + "aes" + "avx" + "fma" + "fma4" + ]; + bdver2 = [ + "sse3" + "ssse3" + "sse4_1" + "sse4_2" + "sse4a" + "aes" + "avx" + "fma" + "fma4" + ]; + bdver3 = [ + "sse3" + "ssse3" + "sse4_1" + "sse4_2" + "sse4a" + "aes" + "avx" + "fma" + "fma4" + ]; bdver4 = [ "sse3" "ssse3" @@ -53,12 +239,39 @@ "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" ]; + 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" @@ -96,13 +309,25 @@ sandybridge = [ "westmere" ] ++ inferiors.westmere; 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; 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; icelake-client = [ "cannonlake" ] ++ inferiors.cannonlake; icelake-server = [ "icelake-client" ] ++ inferiors.icelake-client; @@ -140,12 +365,17 @@ # https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html # https://en.wikichip.org/wiki/amd/microarchitectures/zen # https://en.wikichip.org/wiki/intel/microarchitectures/skylake - znver1 = [ "skylake" ] - ++ inferiors.skylake; # Includes haswell and x86-64-v3 + znver1 = [ "skylake" ] ++ inferiors.skylake; # Includes haswell and x86-64-v3 znver2 = [ "znver1" ] ++ inferiors.znver1; znver3 = [ "znver2" ] ++ inferiors.znver2; - znver4 = lib.unique - ([ "znver3" "x86-64-v4" ] ++ inferiors.znver3 ++ inferiors.x86-64-v4); + znver4 = lib.unique ( + [ + "znver3" + "x86-64-v4" + ] + ++ inferiors.znver3 + ++ inferiors.x86-64-v4 + ); # other armv5te = [ ]; @@ -157,8 +387,10 @@ }; predicates = - let featureSupport = feature: x: builtins.elem feature features.${x} or [ ]; - in { + let + featureSupport = feature: x: builtins.elem feature features.${x} or [ ]; + in + { sse3Support = featureSupport "sse3"; ssse3Support = featureSupport "ssse3"; sse4_1Support = featureSupport "sse4_1"; diff --git a/nix/stdlib/systems/default.nix b/nix/stdlib/systems/default.nix index 047b62c..d81394c 100644 --- a/nix/stdlib/systems/default.nix +++ b/nix/stdlib/systems/default.nix @@ -1,8 +1,21 @@ { lib }: let inherit (lib) - any filterAttrs foldl hasInfix isFunction isList isString mapAttrs optional - optionalAttrs optionalString removeSuffix replaceStrings toUpper; + any + filterAttrs + foldl + hasInfix + isFunction + isList + isString + mapAttrs + optional + optionalAttrs + optionalString + removeSuffix + replaceStrings + toUpper + ; inherit (lib.strings) toJSON; @@ -13,30 +26,35 @@ let examples = import ./examples.nix { inherit lib; }; architectures = import ./architectures.nix { inherit lib; }; - /* * - Elaborated systems contain functions, which means that they don't satisfy - `==` for a lack of reflexivity. + /* + * + Elaborated systems contain functions, which means that they don't satisfy + `==` for a lack of reflexivity. - They might *appear* to satisfy `==` reflexivity when the same exact value is - compared to itself, because object identity is used as an "optimization"; - compare the value with a reconstruction of itself, e.g. with `f == a: f a`, - or perhaps calling `elaborate` twice, and one will see reflexivity fail as described. + They might *appear* to satisfy `==` reflexivity when the same exact value is + compared to itself, because object identity is used as an "optimization"; + compare the value with a reconstruction of itself, e.g. with `f == a: f a`, + or perhaps calling `elaborate` twice, and one will see reflexivity fail as described. - Hence a custom equality test. + Hence a custom equality test. - Note that this does not canonicalize the systems, so you'll want to make sure - both arguments have been `elaborate`-d. + Note that this does not canonicalize the systems, so you'll want to make sure + both arguments have been `elaborate`-d. */ - equals = let removeFunctions = a: filterAttrs (_: v: !isFunction v) a; - in a: b: removeFunctions a == removeFunctions b; - - /* * - List of all Nix system doubles the nixpkgs flake will expose the package set - for. All systems listed here must be supported by nixpkgs as `localSystem`. - - :::{.warning} - This attribute is considered experimental and is subject to change. - ::: + equals = + let + removeFunctions = a: filterAttrs (_: v: !isFunction v) a; + in + a: b: removeFunctions a == removeFunctions b; + + /* + * + List of all Nix system doubles the nixpkgs flake will expose the package set + for. All systems listed here must be supported by nixpkgs as `localSystem`. + + :::{.warning} + This attribute is considered experimental and is subject to change. + ::: */ flakeExposed = import ./flake-systems.nix { }; @@ -46,295 +64,329 @@ let # `parsed` is inferred from args, both because there are two options with one # clearly preferred, and to prevent cycles. A simpler fixed point where the RHS # always just used `final.*` would fail on both counts. - elaborate = args': + elaborate = + args': let args = if isString args' then { system = args'; } else args'; # TODO: deprecate args.rustc in favour of args.rust after 23.05 is EOL. rust = args.rust or args.rustc or { }; - final = { - # Prefer to parse `config` as it is strictly more informative. - parsed = parse.mkSystemFromString - (if args ? config then args.config else args.system); - # Either of these can be losslessly-extracted from `parsed` iff parsing succeeds. - system = parse.doubleFromSystem final.parsed; - config = parse.tripleFromSystem final.parsed; - # Determine whether we can execute binaries built for the provided platform. - canExecute = platform: - final.isAndroid == platform.isAndroid - && parse.isCompatible final.parsed.cpu platform.parsed.cpu - && final.parsed.kernel == platform.parsed.kernel; - isCompatible = _: - throw - "2022-05-23: isCompatible has been removed in favor of canExecute, refer to the 22.11 changelog for details"; - # Derived meta-data - libc = if final.isDarwin then - "libSystem" - else if final.isMinGW then - "msvcrt" - else if final.isWasi then - "wasilibc" - else if final.isRedox then - "relibc" - else if final.isMusl then - "musl" - else if final.isUClibc then - "uclibc" - else if final.isAndroid then - "bionic" - else if final.isLinux - # default - then - "glibc" - else if final.isFreeBSD then - "fblibc" - else if final.isNetBSD then - "nblibc" - else if final.isAvr then - "avrlibc" - else if final.isGhcjs then - null - else if final.isNone then - "newlib" - # TODO(@Ericson2314) think more about other operating systems - else - "native/impure"; - # Choose what linker we wish to use by default. Someday we might also - # choose the C compiler, runtime library, C++ standard library, etc. in - # this way, nice and orthogonally, and deprecate `useLLVM`. But due to - # the monolithic GCC build we cannot actually make those choices - # independently, so we are just doing `linker` and keeping `useLLVM` for - # now. - linker = if final.useLLVM or false then - "lld" - else if final.isDarwin then - "cctools" - # "bfd" and "gold" both come from GNU binutils. The existence of Gold - # is why we use the more obscure "bfd" and not "binutils" for this - # choice. - else - "bfd"; - # The standard lib directory name that non-nixpkgs binaries distributed - # for this platform normally assume. - libDir = if final.isLinux then - if final.isx86_64 || final.isMips64 || final.isPower64 then - "lib64" - else - "lib" - else - null; - extensions = optionalAttrs final.hasSharedLibraries { - sharedLibrary = if final.isDarwin then - ".dylib" - else if final.isWindows then - ".dll" - else - ".so"; - } // { - staticLibrary = if final.isWindows then ".lib" else ".a"; - library = if final.isStatic then - final.extensions.staticLibrary - else - final.extensions.sharedLibrary; - executable = if final.isWindows then ".exe" else ""; - }; - # Misc boolean options - useAndroidPrebuilt = false; - useiOSPrebuilt = false; - - # Output from uname - uname = { - # uname -s - system = { - linux = "Linux"; - windows = "Windows"; - darwin = "Darwin"; - netbsd = "NetBSD"; - freebsd = "FreeBSD"; - openbsd = "OpenBSD"; - wasi = "Wasi"; - redox = "Redox"; - genode = "Genode"; - }.${final.parsed.kernel.name} or null; - - # uname -m - processor = if final.isPower64 then - "ppc64${optionalString final.isLittleEndian "le"}" - else if final.isPower then - "ppc${optionalString final.isLittleEndian "le"}" - else if final.isMips64 then - "mips64" # endianness is *not* included on mips64 - else - final.parsed.cpu.name; - - # uname -r - release = null; - }; + final = + { + # Prefer to parse `config` as it is strictly more informative. + parsed = parse.mkSystemFromString (if args ? config then args.config else args.system); + # Either of these can be losslessly-extracted from `parsed` iff parsing succeeds. + system = parse.doubleFromSystem final.parsed; + config = parse.tripleFromSystem final.parsed; + # Determine whether we can execute binaries built for the provided platform. + canExecute = + platform: + final.isAndroid == platform.isAndroid + && parse.isCompatible final.parsed.cpu platform.parsed.cpu + && final.parsed.kernel == platform.parsed.kernel; + isCompatible = + _: + throw "2022-05-23: isCompatible has been removed in favor of canExecute, refer to the 22.11 changelog for details"; + # Derived meta-data + libc = + if final.isDarwin then + "libSystem" + else if final.isMinGW then + "msvcrt" + else if final.isWasi then + "wasilibc" + else if final.isRedox then + "relibc" + else if final.isMusl then + "musl" + else if final.isUClibc then + "uclibc" + else if final.isAndroid then + "bionic" + else if + final.isLinux + # default + then + "glibc" + else if final.isFreeBSD then + "fblibc" + else if final.isNetBSD then + "nblibc" + else if final.isAvr then + "avrlibc" + else if final.isGhcjs then + null + else if final.isNone then + "newlib" + # TODO(@Ericson2314) think more about other operating systems + else + "native/impure"; + # Choose what linker we wish to use by default. Someday we might also + # choose the C compiler, runtime library, C++ standard library, etc. in + # this way, nice and orthogonally, and deprecate `useLLVM`. But due to + # the monolithic GCC build we cannot actually make those choices + # independently, so we are just doing `linker` and keeping `useLLVM` for + # now. + linker = + if final.useLLVM or false then + "lld" + else if final.isDarwin then + "cctools" + # "bfd" and "gold" both come from GNU binutils. The existence of Gold + # is why we use the more obscure "bfd" and not "binutils" for this + # choice. + else + "bfd"; + # The standard lib directory name that non-nixpkgs binaries distributed + # for this platform normally assume. + libDir = + if final.isLinux then + if final.isx86_64 || final.isMips64 || final.isPower64 then "lib64" else "lib" + else + null; + extensions = + optionalAttrs final.hasSharedLibraries { + sharedLibrary = + if final.isDarwin then + ".dylib" + else if final.isWindows then + ".dll" + else + ".so"; + } + // { + staticLibrary = if final.isWindows then ".lib" else ".a"; + library = if final.isStatic then final.extensions.staticLibrary else final.extensions.sharedLibrary; + executable = if final.isWindows then ".exe" else ""; + }; + # Misc boolean options + useAndroidPrebuilt = false; + useiOSPrebuilt = false; + + # Output from uname + uname = { + # uname -s + system = + { + linux = "Linux"; + windows = "Windows"; + darwin = "Darwin"; + netbsd = "NetBSD"; + freebsd = "FreeBSD"; + openbsd = "OpenBSD"; + wasi = "Wasi"; + redox = "Redox"; + genode = "Genode"; + } + .${final.parsed.kernel.name} or null; + + # uname -m + processor = + if final.isPower64 then + "ppc64${optionalString final.isLittleEndian "le"}" + else if final.isPower then + "ppc${optionalString final.isLittleEndian "le"}" + else if final.isMips64 then + "mips64" # endianness is *not* included on mips64 + else + final.parsed.cpu.name; - # It is important that hasSharedLibraries==false when the platform has no - # dynamic library loader. Various tools (including the gcc build system) - # have knowledge of which platforms are incapable of dynamic linking, and - # will still build on/for those platforms with --enable-shared, but simply - # omit any `.so` build products such as libgcc_s.so. When that happens, - # it causes hard-to-troubleshoot build failures. - hasSharedLibraries = with final; - (isAndroid || isGnu || isMusl # Linux (allows multiple libcs) - || isDarwin || isSunOS || isOpenBSD || isFreeBSD || isNetBSD # BSDs - || isCygwin || isMinGW # Windows - ) && !isStatic; - - # The difference between `isStatic` and `hasSharedLibraries` is mainly the - # addition of the `staticMarker` (see make-derivation.nix). Some - # platforms, like embedded machines without a libc (e.g. arm-none-eabi) - # don't support dynamic linking, but don't get the `staticMarker`. - # `pkgsStatic` sets `isStatic=true`, so `pkgsStatic.hostPlatform` always - # has the `staticMarker`. - isStatic = final.isWasm || final.isRedox; - - # Just a guess, based on `system` - inherit ({ - linux-kernel = args.linux-kernel or { }; - gcc = args.gcc or { }; - } // platforms.select final) - linux-kernel gcc; - - # TODO: remove after 23.05 is EOL, with an error pointing to the rust.* attrs. - rustc = args.rustc or { }; - - linuxArch = if final.isAarch32 then - "arm" - else if final.isAarch64 then - "arm64" - else if final.isx86_32 then - "i386" - else if final.isx86_64 then - "x86_64" - # linux kernel does not distinguish microblaze/microblazeel - else if final.isMicroBlaze then - "microblaze" - else if final.isMips32 then - "mips" - else if final.isMips64 then - "mips" # linux kernel does not distinguish mips32/mips64 - else if final.isPower then - "powerpc" - else if final.isRiscV then - "riscv" - else if final.isS390 then - "s390" - else if final.isLoongArch64 then - "loongarch" - else - final.parsed.cpu.name; - - # https://source.denx.de/u-boot/u-boot/-/blob/9bfb567e5f1bfe7de8eb41f8c6d00f49d2b9a426/common/image.c#L81-106 - ubootArch = if final.isx86_32 then - "x86" # not i386 - else if final.isMips64 then - "mips64" # uboot *does* distinguish between mips32/mips64 - else - final.linuxArch; # other cases appear to agree with linuxArch - - qemuArch = if final.isAarch32 then - "arm" - else if final.isS390 && !final.isS390x then - null - else if final.isx86_64 then - "x86_64" - else if final.isx86 then - "i386" - else if final.isMips64n32 then - "mipsn32${optionalString final.isLittleEndian "el"}" - else if final.isMips64 then - "mips64${optionalString final.isLittleEndian "el"}" - else - final.uname.processor; - - # Name used by UEFI for architectures. - efiArch = if final.isx86_32 then - "ia32" - else if final.isx86_64 then - "x64" - else if final.isAarch32 then - "arm" - else if final.isAarch64 then - "aa64" - else - final.parsed.cpu.name; - - darwinArch = { - armv7a = "armv7"; - aarch64 = "arm64"; - }.${final.parsed.cpu.name} or final.parsed.cpu.name; - - darwinPlatform = if final.isMacOS then - "macos" - else if final.isiOS then - "ios" - else - null; - # The canonical name for this attribute is darwinSdkVersion, but some - # platforms define the old name "sdkVer". - darwinSdkVersion = - final.sdkVer or (if final.isAarch64 then "11.0" else "10.12"); - darwinMinVersion = final.darwinSdkVersion; - darwinMinVersionVariable = if final.isMacOS then - "MACOSX_DEPLOYMENT_TARGET" - else if final.isiOS then - "IPHONEOS_DEPLOYMENT_TARGET" - else - null; - } // (let - selectEmulator = pkgs: + # uname -r + release = null; + }; + + # It is important that hasSharedLibraries==false when the platform has no + # dynamic library loader. Various tools (including the gcc build system) + # have knowledge of which platforms are incapable of dynamic linking, and + # will still build on/for those platforms with --enable-shared, but simply + # omit any `.so` build products such as libgcc_s.so. When that happens, + # it causes hard-to-troubleshoot build failures. + hasSharedLibraries = + with final; + ( + isAndroid + || isGnu + || isMusl # Linux (allows multiple libcs) + || isDarwin + || isSunOS + || isOpenBSD + || isFreeBSD + || isNetBSD # BSDs + || isCygwin + || isMinGW # Windows + ) + && !isStatic; + + # The difference between `isStatic` and `hasSharedLibraries` is mainly the + # addition of the `staticMarker` (see make-derivation.nix). Some + # platforms, like embedded machines without a libc (e.g. arm-none-eabi) + # don't support dynamic linking, but don't get the `staticMarker`. + # `pkgsStatic` sets `isStatic=true`, so `pkgsStatic.hostPlatform` always + # has the `staticMarker`. + isStatic = final.isWasm || final.isRedox; + + # Just a guess, based on `system` + inherit + ( + { + linux-kernel = args.linux-kernel or { }; + gcc = args.gcc or { }; + } + // platforms.select final + ) + linux-kernel + gcc + ; + + # TODO: remove after 23.05 is EOL, with an error pointing to the rust.* attrs. + rustc = args.rustc or { }; + + linuxArch = + if final.isAarch32 then + "arm" + else if final.isAarch64 then + "arm64" + else if final.isx86_32 then + "i386" + else if final.isx86_64 then + "x86_64" + # linux kernel does not distinguish microblaze/microblazeel + else if final.isMicroBlaze then + "microblaze" + else if final.isMips32 then + "mips" + else if final.isMips64 then + "mips" # linux kernel does not distinguish mips32/mips64 + else if final.isPower then + "powerpc" + else if final.isRiscV then + "riscv" + else if final.isS390 then + "s390" + else if final.isLoongArch64 then + "loongarch" + else + final.parsed.cpu.name; + + # https://source.denx.de/u-boot/u-boot/-/blob/9bfb567e5f1bfe7de8eb41f8c6d00f49d2b9a426/common/image.c#L81-106 + ubootArch = + if final.isx86_32 then + "x86" # not i386 + else if final.isMips64 then + "mips64" # uboot *does* distinguish between mips32/mips64 + else + final.linuxArch; # other cases appear to agree with linuxArch + + qemuArch = + if final.isAarch32 then + "arm" + else if final.isS390 && !final.isS390x then + null + else if final.isx86_64 then + "x86_64" + else if final.isx86 then + "i386" + else if final.isMips64n32 then + "mipsn32${optionalString final.isLittleEndian "el"}" + else if final.isMips64 then + "mips64${optionalString final.isLittleEndian "el"}" + else + final.uname.processor; + + # Name used by UEFI for architectures. + efiArch = + if final.isx86_32 then + "ia32" + else if final.isx86_64 then + "x64" + else if final.isAarch32 then + "arm" + else if final.isAarch64 then + "aa64" + else + final.parsed.cpu.name; + + darwinArch = + { + armv7a = "armv7"; + aarch64 = "arm64"; + } + .${final.parsed.cpu.name} or final.parsed.cpu.name; + + darwinPlatform = + if final.isMacOS then + "macos" + else if final.isiOS then + "ios" + else + null; + # The canonical name for this attribute is darwinSdkVersion, but some + # platforms define the old name "sdkVer". + darwinSdkVersion = final.sdkVer or (if final.isAarch64 then "11.0" else "10.12"); + darwinMinVersion = final.darwinSdkVersion; + darwinMinVersionVariable = + if final.isMacOS then + "MACOSX_DEPLOYMENT_TARGET" + else if final.isiOS then + "IPHONEOS_DEPLOYMENT_TARGET" + else + null; + } + // ( let - qemu-user = pkgs.qemu.override { - smartcardSupport = false; - spiceSupport = false; - openGLSupport = false; - virglSupport = false; - vncSupport = false; - gtkSupport = false; - sdlSupport = false; - alsaSupport = false; - pulseSupport = false; - pipewireSupport = false; - jackSupport = false; - smbdSupport = false; - seccompSupport = false; - tpmSupport = false; - capstoneSupport = false; - enableDocs = false; - hostCpuTargets = [ "${final.qemuArch}-linux-user" ]; - }; - wine = (pkgs.winePackagesFor - "wine${toString final.parsed.cpu.bits}").minimal; - in if pkgs.stdenv.hostPlatform.canExecute final then - ''${pkgs.runtimeShell} -c '"$@"' --'' - else if final.isWindows then - "${wine}/bin/wine${ - optionalString (final.parsed.cpu.bits == 64) "64" - }" - else if final.isLinux && pkgs.stdenv.hostPlatform.isLinux - && final.qemuArch != null then - "${qemu-user}/bin/qemu-${final.qemuArch}" - else if final.isWasi then - "${pkgs.wasmtime}/bin/wasmtime" - else if final.isMmix then - "${pkgs.mmixware}/bin/mmix" - else - null; - in { - emulatorAvailable = pkgs: (selectEmulator pkgs) != null; - - emulator = pkgs: - if (final.emulatorAvailable pkgs) then - selectEmulator pkgs - else - throw "Don't know how to run ${final.config} executables."; - }) // mapAttrs (n: v: v final.parsed) inspect.predicates - // mapAttrs (n: v: v final.gcc.arch or "default") - architectures.predicates // args // { + selectEmulator = + pkgs: + let + qemu-user = pkgs.qemu.override { + smartcardSupport = false; + spiceSupport = false; + openGLSupport = false; + virglSupport = false; + vncSupport = false; + gtkSupport = false; + sdlSupport = false; + alsaSupport = false; + pulseSupport = false; + pipewireSupport = false; + jackSupport = false; + smbdSupport = false; + seccompSupport = false; + tpmSupport = false; + capstoneSupport = false; + enableDocs = false; + hostCpuTargets = [ "${final.qemuArch}-linux-user" ]; + }; + wine = (pkgs.winePackagesFor "wine${toString final.parsed.cpu.bits}").minimal; + in + if pkgs.stdenv.hostPlatform.canExecute final then + ''${pkgs.runtimeShell} -c '"$@"' --'' + else if final.isWindows then + "${wine}/bin/wine${optionalString (final.parsed.cpu.bits == 64) "64"}" + else if final.isLinux && pkgs.stdenv.hostPlatform.isLinux && final.qemuArch != null then + "${qemu-user}/bin/qemu-${final.qemuArch}" + else if final.isWasi then + "${pkgs.wasmtime}/bin/wasmtime" + else if final.isMmix then + "${pkgs.mmixware}/bin/mmix" + else + null; + in + { + emulatorAvailable = pkgs: (selectEmulator pkgs) != null; + + emulator = + pkgs: + if (final.emulatorAvailable pkgs) then + selectEmulator pkgs + else + throw "Don't know how to run ${final.config} executables."; + } + ) + // mapAttrs (n: v: v final.parsed) inspect.predicates + // mapAttrs (n: v: v final.gcc.arch or "default") architectures.predicates + // args + // { rust = rust // { # Once args.rustc.platform.target-family is deprecated and # removed, there will no longer be any need to modify any @@ -343,85 +395,97 @@ let # /after/. platform = rust.platform or { } // { # https://doc.rust-lang.org/reference/conditional-compilation.html#target_arch - arch = if rust ? platform then - rust.platform.arch - else if final.isAarch32 then - "arm" - else if final.isMips64 then - "mips64" # never add "el" suffix - else if final.isPower64 then - "powerpc64" # never add "le" suffix - else - final.parsed.cpu.name; + arch = + if rust ? platform then + rust.platform.arch + else if final.isAarch32 then + "arm" + else if final.isMips64 then + "mips64" # never add "el" suffix + else if final.isPower64 then + "powerpc64" # never add "le" suffix + else + final.parsed.cpu.name; # https://doc.rust-lang.org/reference/conditional-compilation.html#target_os - os = if rust ? platform then - rust.platform.os or "none" - else if final.isDarwin then - "macos" - else - final.parsed.kernel.name; + os = + if rust ? platform then + rust.platform.os or "none" + else if final.isDarwin then + "macos" + else + final.parsed.kernel.name; # https://doc.rust-lang.org/reference/conditional-compilation.html#target_family - target-family = if args ? rust.platform.target-family then - args.rust.platform.target-family - else if args ? rustc.platform.target-family then - ( - # Since https://github.com/rust-lang/rust/pull/84072 - # `target-family` is a list instead of single value. - let f = args.rustc.platform.target-family; - in if isList f then f else [ f ]) - else - optional final.isUnix "unix" - ++ optional final.isWindows "windows"; + target-family = + if args ? rust.platform.target-family then + args.rust.platform.target-family + else if args ? rustc.platform.target-family then + ( + # Since https://github.com/rust-lang/rust/pull/84072 + # `target-family` is a list instead of single value. + let + f = args.rustc.platform.target-family; + in + if isList f then f else [ f ] + ) + else + optional final.isUnix "unix" ++ optional final.isWindows "windows"; # https://doc.rust-lang.org/reference/conditional-compilation.html#target_vendor - vendor = let inherit (final.parsed) vendor; - in rust.platform.vendor or { - "w64" = "pc"; - }.${vendor.name} or vendor.name; + vendor = + let + inherit (final.parsed) vendor; + in + rust.platform.vendor or { + "w64" = "pc"; + } + .${vendor.name} or vendor.name; }; # The name of the rust target, even if it is custom. Adjustments are # because rust has slightly different naming conventions than we do. - rustcTarget = let - inherit (final.parsed) cpu kernel abi; - cpu_ = rust.platform.arch or { - "armv7a" = "armv7"; - "armv7l" = "armv7"; - "armv6l" = "arm"; - "armv5tel" = "armv5te"; - "riscv64" = "riscv64gc"; - }.${cpu.name} or cpu.name; - vendor_ = final.rust.platform.vendor; + rustcTarget = + let + inherit (final.parsed) cpu kernel abi; + cpu_ = + rust.platform.arch or { + "armv7a" = "armv7"; + "armv7l" = "armv7"; + "armv6l" = "arm"; + "armv5tel" = "armv5te"; + "riscv64" = "riscv64gc"; + } + .${cpu.name} or cpu.name; + vendor_ = final.rust.platform.vendor; + in # TODO: deprecate args.rustc in favour of args.rust after 23.05 is EOL. - in args.rust.rustcTarget or args.rustc.config or "${cpu_}-${vendor_}-${kernel.name}${ - optionalString (abi.name != "unknown") "-${abi.name}" - }"; + args.rust.rustcTarget or args.rustc.config + or "${cpu_}-${vendor_}-${kernel.name}${optionalString (abi.name != "unknown") "-${abi.name}"}"; # The name of the rust target if it is standard, or the json file # containing the custom target spec. - rustcTargetSpec = rust.rustcTargetSpec or (if rust ? platform then - builtins.toFile (final.rust.rustcTarget + ".json") - (toJSON rust.platform) - else - final.rust.rustcTarget); + rustcTargetSpec = + rust.rustcTargetSpec or ( + if rust ? platform then + builtins.toFile (final.rust.rustcTarget + ".json") (toJSON rust.platform) + else + final.rust.rustcTarget + ); # The name of the rust target if it is standard, or the # basename of the file containing the custom target spec, # without the .json extension. # # This is the name used by Cargo for target subdirectories. - cargoShortTarget = - removeSuffix ".json" (baseNameOf "${final.rust.rustcTargetSpec}"); + cargoShortTarget = removeSuffix ".json" (baseNameOf "${final.rust.rustcTargetSpec}"); # When used as part of an environment variable name, triples are # uppercased and have all hyphens replaced by underscores: # # https://github.com/rust-lang/cargo/pull/9169 # https://github.com/rust-lang/cargo/issues/8285#issuecomment-634202431 - cargoEnvVarTarget = replaceStrings [ "-" ] [ "_" ] - (toUpper final.rust.cargoShortTarget); + cargoEnvVarTarget = replaceStrings [ "-" ] [ "_" ] (toUpper final.rust.cargoShortTarget); # True if the target is no_std # https://github.com/rust-lang/rust/blob/2e44c17c12cec45b6a682b1e53a04ac5b5fcc9d2/src/bootstrap/config.rs#L415-L421 @@ -433,14 +497,24 @@ let ]; }; }; - in assert final.useAndroidPrebuilt -> final.isAndroid; - assert foldl (pass: - { assertion, message, }: - if assertion final then pass else throw message) true - (final.parsed.abi.assertions or [ ]); + in + assert final.useAndroidPrebuilt -> final.isAndroid; + assert foldl (pass: { assertion, message }: if assertion final then pass else throw message) true ( + final.parsed.abi.assertions or [ ] + ); final; - # Everything in this attrset is the public interface of the file. -in { - inherit architectures doubles elaborate equals examples flakeExposed inspect - parse platforms; +in +# Everything in this attrset is the public interface of the file. +{ + inherit + architectures + doubles + elaborate + equals + examples + flakeExposed + inspect + parse + platforms + ; } diff --git a/nix/stdlib/systems/doubles.nix b/nix/stdlib/systems/doubles.nix index 97d54ef..320d468 100644 --- a/nix/stdlib/systems/doubles.nix +++ b/nix/stdlib/systems/doubles.nix @@ -113,7 +113,8 @@ let allParsed = map parse.mkSystemFromString all; filterDoubles = f: map parse.doubleFromSystem (lists.filter f allParsed); -in { +in +{ inherit all; none = [ ]; @@ -147,28 +148,35 @@ in { darwin = filterDoubles predicates.isDarwin; freebsd = filterDoubles predicates.isFreeBSD; # Should be better, but MinGW is unclear. - gnu = filterDoubles (matchAttrs { - kernel = parse.kernels.linux; - abi = parse.abis.gnu; - }) ++ filterDoubles (matchAttrs { - kernel = parse.kernels.linux; - abi = parse.abis.gnueabi; - }) ++ filterDoubles (matchAttrs { - kernel = parse.kernels.linux; - abi = parse.abis.gnueabihf; - }) ++ filterDoubles (matchAttrs { - kernel = parse.kernels.linux; - abi = parse.abis.gnuabin32; - }) ++ filterDoubles (matchAttrs { - kernel = parse.kernels.linux; - abi = parse.abis.gnuabi64; - }) ++ filterDoubles (matchAttrs { - kernel = parse.kernels.linux; - abi = parse.abis.gnuabielfv1; - }) ++ filterDoubles (matchAttrs { - kernel = parse.kernels.linux; - abi = parse.abis.gnuabielfv2; - }); + gnu = + filterDoubles (matchAttrs { + kernel = parse.kernels.linux; + abi = parse.abis.gnu; + }) + ++ filterDoubles (matchAttrs { + kernel = parse.kernels.linux; + abi = parse.abis.gnueabi; + }) + ++ filterDoubles (matchAttrs { + kernel = parse.kernels.linux; + abi = parse.abis.gnueabihf; + }) + ++ filterDoubles (matchAttrs { + kernel = parse.kernels.linux; + abi = parse.abis.gnuabin32; + }) + ++ filterDoubles (matchAttrs { + kernel = parse.kernels.linux; + abi = parse.abis.gnuabi64; + }) + ++ filterDoubles (matchAttrs { + kernel = parse.kernels.linux; + abi = parse.abis.gnuabielfv1; + }) + ++ filterDoubles (matchAttrs { + kernel = parse.kernels.linux; + abi = parse.abis.gnuabielfv2; + }); illumos = filterDoubles predicates.isSunOS; linux = filterDoubles predicates.isLinux; netbsd = filterDoubles predicates.isNetBSD; diff --git a/nix/stdlib/systems/examples.nix b/nix/stdlib/systems/examples.nix index 2ef6d47..4880faf 100644 --- a/nix/stdlib/systems/examples.nix +++ b/nix/stdlib/systems/examples.nix @@ -6,17 +6,26 @@ let platforms = import ./platforms.nix { inherit lib; }; riscv = bits: { config = "riscv${bits}-unknown-linux-gnu"; }; -in rec { +in +rec { # # Linux # - powernv = { config = "powerpc64le-unknown-linux-gnu"; }; - musl-power = { config = "powerpc64le-unknown-linux-musl"; }; + powernv = { + config = "powerpc64le-unknown-linux-gnu"; + }; + musl-power = { + config = "powerpc64le-unknown-linux-musl"; + }; - ppc64 = { config = "powerpc64-unknown-linux-gnuabielfv2"; }; + ppc64 = { + config = "powerpc64-unknown-linux-gnuabielfv2"; + }; ppc64-musl = { config = "powerpc64-unknown-linux-musl"; - gcc = { abi = "elfv2"; }; + gcc = { + abi = "elfv2"; + }; }; sheevaplug = { @@ -39,9 +48,13 @@ in rec { config = "armv7l-unknown-linux-gnueabihf"; } // platforms.zero-sugar; - armv7l-hf-multiplatform = { config = "armv7l-unknown-linux-gnueabihf"; }; + armv7l-hf-multiplatform = { + config = "armv7l-unknown-linux-gnueabihf"; + }; - aarch64-multiplatform = { config = "aarch64-unknown-linux-gnu"; }; + aarch64-multiplatform = { + config = "aarch64-unknown-linux-gnu"; + }; armv7a-android-prebuilt = { config = "armv7a-unknown-linux-androideabi"; @@ -104,17 +117,28 @@ in rec { config = "mips64el-unknown-linux-gnuabi64"; } // platforms.gcc_mips64r2_64; - muslpi = raspberryPi // { config = "armv6l-unknown-linux-musleabihf"; }; + muslpi = raspberryPi // { + config = "armv6l-unknown-linux-musleabihf"; + }; - aarch64-multiplatform-musl = { config = "aarch64-unknown-linux-musl"; }; + aarch64-multiplatform-musl = { + config = "aarch64-unknown-linux-musl"; + }; - gnu64 = { config = "x86_64-unknown-linux-gnu"; }; - gnu64_simplekernel = gnu64 - // platforms.pc_simplekernel; # see test/cross/default.nix - gnu32 = { config = "i686-unknown-linux-gnu"; }; + gnu64 = { + config = "x86_64-unknown-linux-gnu"; + }; + gnu64_simplekernel = gnu64 // platforms.pc_simplekernel; # see test/cross/default.nix + gnu32 = { + config = "i686-unknown-linux-gnu"; + }; - musl64 = { config = "x86_64-unknown-linux-musl"; }; - musl32 = { config = "i686-unknown-linux-musl"; }; + musl64 = { + config = "x86_64-unknown-linux-musl"; + }; + musl32 = { + config = "i686-unknown-linux-musl"; + }; riscv64 = riscv "64"; riscv32 = riscv "32"; @@ -139,7 +163,9 @@ in rec { libc = "newlib"; }; - loongarch64-linux = { config = "loongarch64-unknown-linux-gnu"; }; + loongarch64-linux = { + config = "loongarch64-unknown-linux-gnu"; + }; mmix = { config = "mmix-unknown-mmixware"; @@ -156,7 +182,9 @@ in rec { libc = "newlib"; }; - avr = { config = "avr"; }; + avr = { + config = "avr"; + }; vc4 = { config = "vc4-elf"; @@ -168,11 +196,17 @@ in rec { libc = "newlib"; }; - m68k = { config = "m68k-unknown-linux-gnu"; }; + m68k = { + config = "m68k-unknown-linux-gnu"; + }; - s390 = { config = "s390-unknown-linux-gnu"; }; + s390 = { + config = "s390-unknown-linux-gnu"; + }; - s390x = { config = "s390x-unknown-linux-gnu"; }; + s390x = { + config = "s390x-unknown-linux-gnu"; + }; arm-embedded = { config = "arm-none-eabi"; @@ -317,7 +351,9 @@ in rec { useLLVM = true; }; - x86_64-netbsd = { config = "x86_64-unknown-netbsd"; }; + x86_64-netbsd = { + config = "x86_64-unknown-netbsd"; + }; # this is broken and never worked fully x86_64-netbsd-llvm = { diff --git a/nix/stdlib/systems/flake-systems.nix b/nix/stdlib/systems/flake-systems.nix index b42759c..a1daf1a 100644 --- a/nix/stdlib/systems/flake-systems.nix +++ b/nix/stdlib/systems/flake-systems.nix @@ -4,7 +4,8 @@ # reasonably well are included. # # [RFC 46]: https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md -{ }: [ +{ }: +[ # Tier 1 "x86_64-linux" # Tier 2 diff --git a/nix/stdlib/systems/inspect.nix b/nix/stdlib/systems/inspect.nix index 1ce3ee4..51e8405 100644 --- a/nix/stdlib/systems/inspect.nix +++ b/nix/stdlib/systems/inspect.nix @@ -1,17 +1,31 @@ { lib }: let inherit (lib) - any attrValues concatMap filter hasPrefix isList mapAttrs matchAttrs - recursiveUpdateUntil toList; + any + attrValues + concatMap + filter + hasPrefix + isList + mapAttrs + matchAttrs + recursiveUpdateUntil + toList + ; inherit (lib.strings) toJSON; inherit (lib.systems.parse) - kernels kernelFamilies significantBytes cpuTypes execFormats; + kernels + kernelFamilies + significantBytes + cpuTypes + execFormats + ; - abis = - mapAttrs (_: abi: removeAttrs abi [ "assertions" ]) lib.systems.parse.abis; -in rec { + abis = mapAttrs (_: abi: removeAttrs abi [ "assertions" ]) lib.systems.parse.abis; +in +rec { # these patterns are to be matched against {host,build,target}Platform.parsed patterns = rec { # The patterns below are lists in sum-of-products form. @@ -22,7 +36,9 @@ in rec { # `lib.attrsets.matchAttrs`, which requires a match on *all* attributes of # the product. - isi686 = { cpu = cpuTypes.i686; }; + isi686 = { + cpu = cpuTypes.i686; + }; isx86_32 = { cpu = { family = "x86"; @@ -35,7 +51,11 @@ in rec { bits = 64; }; }; - isPower = { cpu = { family = "power"; }; }; + isPower = { + cpu = { + family = "power"; + }; + }; isPower64 = { cpu = { family = "power"; @@ -46,33 +66,61 @@ in rec { # so it sometimes causes issues in certain packages that makes the wrong # assumption on the used ABI. isAbiElfv2 = [ - { abi = { abi = "elfv2"; }; } { - abi = { name = "musl"; }; + abi = { + abi = "elfv2"; + }; + } + { + abi = { + name = "musl"; + }; cpu = { family = "power"; bits = 64; }; } ]; - isx86 = { cpu = { family = "x86"; }; }; + isx86 = { + cpu = { + family = "x86"; + }; + }; isAarch32 = { cpu = { family = "arm"; bits = 32; }; }; - isArmv7 = map ({ arch, ... }: { cpu = { inherit arch; }; }) - (filter (cpu: hasPrefix "armv7" cpu.arch or "") (attrValues cpuTypes)); + isArmv7 = map ( + { arch, ... }: + { + cpu = { + inherit arch; + }; + } + ) (filter (cpu: hasPrefix "armv7" cpu.arch or "") (attrValues cpuTypes)); isAarch64 = { cpu = { family = "arm"; bits = 64; }; }; - isAarch = { cpu = { family = "arm"; }; }; - isMicroBlaze = { cpu = { family = "microblaze"; }; }; - isMips = { cpu = { family = "mips"; }; }; + isAarch = { + cpu = { + family = "arm"; + }; + }; + isMicroBlaze = { + cpu = { + family = "microblaze"; + }; + }; + isMips = { + cpu = { + family = "mips"; + }; + }; isMips32 = { cpu = { family = "mips"; @@ -90,17 +138,29 @@ in rec { family = "mips"; bits = 64; }; - abi = { abi = "n32"; }; + abi = { + abi = "n32"; + }; }; isMips64n64 = { cpu = { family = "mips"; bits = 64; }; - abi = { abi = "64"; }; + abi = { + abi = "64"; + }; + }; + isMmix = { + cpu = { + family = "mmix"; + }; + }; + isRiscV = { + cpu = { + family = "riscv"; + }; }; - isMmix = { cpu = { family = "mmix"; }; }; - isRiscV = { cpu = { family = "riscv"; }; }; isRiscV32 = { cpu = { family = "riscv"; @@ -113,22 +173,62 @@ in rec { bits = 64; }; }; - isRx = { cpu = { family = "rx"; }; }; - isSparc = { cpu = { family = "sparc"; }; }; + 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"; }; }; + 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"; @@ -141,35 +241,100 @@ in rec { bits = 64; }; }; - isJavaScript = { cpu = cpuTypes.javascript; }; + isJavaScript = { + cpu = cpuTypes.javascript; + }; - is32bit = { cpu = { bits = 32; }; }; - is64bit = { cpu = { bits = 64; }; }; - isILP32 = [{ + is32bit = { cpu = { - family = "wasm"; bits = 32; }; - }] ++ map (a: { abi = { abi = a; }; }) [ "n32" "ilp32" "x32" ]; - isBigEndian = { cpu = { significantByte = significantBytes.bigEndian; }; }; + }; + 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; }; + cpu = { + significantByte = significantBytes.littleEndian; + }; }; - isBSD = { kernel = { families = { inherit (kernelFamilies) bsd; }; }; }; + isBSD = { + kernel = { + families = { + inherit (kernelFamilies) bsd; + }; + }; + }; isDarwin = { - kernel = { families = { inherit (kernelFamilies) darwin; }; }; + kernel = { + families = { + inherit (kernelFamilies) darwin; + }; + }; }; - isUnix = [ isBSD isDarwin isLinux isSunOS isCygwin isRedox ]; + isUnix = [ + isBSD + isDarwin + isLinux + isSunOS + isCygwin + isRedox + ]; - isMacOS = { kernel = kernels.macos; }; - isiOS = { kernel = kernels.ios; }; - isLinux = { kernel = kernels.linux; }; - isSunOS = { kernel = kernels.solaris; }; - isFreeBSD = { kernel = { name = "freebsd"; }; }; - isNetBSD = { kernel = kernels.netbsd; }; - isOpenBSD = { kernel = kernels.openbsd; }; - isWindows = { kernel = kernels.windows; }; + isMacOS = { + kernel = kernels.macos; + }; + isiOS = { + kernel = kernels.ios; + }; + isLinux = { + kernel = kernels.linux; + }; + isSunOS = { + kernel = kernels.solaris; + }; + isFreeBSD = { + kernel = { + name = "freebsd"; + }; + }; + isNetBSD = { + kernel = kernels.netbsd; + }; + isOpenBSD = { + kernel = kernels.openbsd; + }; + isWindows = { + kernel = kernels.windows; + }; isCygwin = { kernel = kernels.windows; abi = abis.cygnus; @@ -178,14 +343,28 @@ in rec { 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; }; + isWasi = { + kernel = kernels.wasi; + }; + isRedox = { + kernel = kernels.redox; + }; + isGhcjs = { + kernel = kernels.ghcjs; + }; + isGenode = { + kernel = kernels.genode; + }; + isNone = { + kernel = kernels.none; + }; - isAndroid = [ { abi = abis.android; } { abi = abis.androideabi; } ]; - isGnu = with abis; + isAndroid = [ + { abi = abis.android; } + { abi = abis.androideabi; } + ]; + isGnu = + with abis; map (a: { abi = a; }) [ gnuabi64 gnuabin32 @@ -195,10 +374,22 @@ in rec { gnuabielfv1 gnuabielfv2 ]; - isMusl = with abis; - map (a: { abi = a; }) [ musl musleabi musleabihf muslabin32 muslabi64 ]; - isUClibc = with abis; - map (a: { abi = a; }) [ uclibc uclibceabi uclibceabihf ]; + isMusl = + with abis; + map (a: { abi = a; }) [ + musl + musleabi + musleabihf + muslabin32 + muslabi64 + ]; + isUClibc = + with abis; + map (a: { abi = a; }) [ + uclibc + uclibceabi + uclibceabihf + ]; isEfi = [ { @@ -219,36 +410,56 @@ in rec { version = "8"; }; } - { cpu = { family = "riscv"; }; } - { cpu = { family = "x86"; }; } + { + cpu = { + family = "riscv"; + }; + } + { + cpu = { + family = "x86"; + }; + } ]; - isElf = { kernel.execFormat = execFormats.elf; }; - isMacho = { kernel.execFormat = execFormats.macho; }; + isElf = { + kernel.execFormat = execFormats.elf; + }; + isMacho = { + kernel.execFormat = execFormats.macho; + }; }; # given two patterns, return a pattern which is their logical AND. # Since a pattern is a list-of-disjuncts, this needs to - patternLogicalAnd = pat1_: pat2_: + patternLogicalAnd = + pat1_: pat2_: let # patterns can be either a list or a (bare) singleton; turn # them into singletons for uniform handling pat1 = toList pat1_; pat2 = toList pat2_; - in concatMap (attr1: - map (attr2: - recursiveUpdateUntil (path: subattr1: subattr2: - if (builtins.intersectAttrs subattr1 subattr2) == { } || subattr1 - == subattr2 then + in + concatMap ( + attr1: + map ( + attr2: + recursiveUpdateUntil ( + path: subattr1: subattr2: + if (builtins.intersectAttrs subattr1 subattr2) == { } || subattr1 == subattr2 then true else throw '' pattern conflict at path ${toString path}: ${toJSON subattr1} ${toJSON subattr2} - '') attr1 attr2) pat2) pat1; + '' + ) attr1 attr2 + ) pat2 + ) pat1; - matchAnyAttrs = patterns: + matchAnyAttrs = + patterns: if isList patterns then attrs: any (pattern: matchAttrs pattern attrs) patterns else @@ -262,6 +473,8 @@ in rec { # apply only to the `parsed` field. platformPatterns = mapAttrs (_: p: { parsed = { }; } // p) { - isStatic = { isStatic = true; }; + isStatic = { + isStatic = true; + }; }; } diff --git a/nix/stdlib/systems/parse.nix b/nix/stdlib/systems/parse.nix index d9907b0..b59cf30 100644 --- a/nix/stdlib/systems/parse.nix +++ b/nix/stdlib/systems/parse.nix @@ -17,29 +17,62 @@ { lib }: let inherit (lib) - all any attrValues elem elemAt hasPrefix id length mapAttrs mergeOneOption - optionalString splitString versionAtLeast; + all + any + attrValues + elem + elemAt + hasPrefix + id + length + mapAttrs + mergeOneOption + optionalString + splitString + versionAtLeast + ; inherit (lib.strings) match; inherit (lib.systems.inspect.predicates) - isAarch32 isBigEndian isDarwin isLinux isPower64 isWindows; + isAarch32 + isBigEndian + isDarwin + isLinux + isPower64 + isWindows + ; inherit (lib.types) - enum float isType mkOptionType number setType string types; - - setTypes = type: - mapAttrs (name: value: + enum + float + isType + mkOptionType + number + setType + string + types + ; + + setTypes = + type: + mapAttrs ( + name: 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 # regex `e?abi.*$` when determining the validity of a triple. In # other words, `i386-linuxabichickenlips` is a valid triple. - removeAbiSuffix = x: - let found = match "(.*)e?abi.*" x; - in if found == null then x else elemAt found 0; -in rec { + removeAbiSuffix = + x: + let + found = match "(.*)e?abi.*" x; + in + if found == null then x else elemAt found 0; +in +rec { ################################################################################ types.openSignificantByte = mkOptionType { @@ -58,7 +91,13 @@ in rec { ################################################################################ # Reasonable power of 2 - types.bitWidth = enum [ 8 16 32 64 128 ]; + types.bitWidth = enum [ + 8 + 16 + 32 + 64 + 128 + ]; ################################################################################ @@ -66,299 +105,304 @@ in rec { name = "cpu-type"; description = "instruction set architecture name and information"; merge = mergeOneOption; - check = x: - types.bitWidth.check x.bits && (if 8 < x.bits then - types.significantByte.check x.significantByte - else - !(x ? significantByte)); + check = + x: + types.bitWidth.check x.bits + && (if 8 < x.bits then types.significantByte.check x.significantByte else !(x ? significantByte)); }; types.cpuType = enum (attrValues cpuTypes); - cpuTypes = let inherit (significantBytes) bigEndian littleEndian; - in setTypes types.openCpuType { - arm = { - bits = 32; - significantByte = littleEndian; - family = "arm"; - }; - armv5tel = { - bits = 32; - significantByte = littleEndian; - 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"; - }; + cpuTypes = + let + inherit (significantBytes) bigEndian littleEndian; + in + setTypes types.openCpuType { + arm = { + bits = 32; + significantByte = littleEndian; + family = "arm"; + }; + armv5tel = { + bits = 32; + significantByte = littleEndian; + 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"; - }; - 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"; - }; + i386 = { + bits = 32; + significantByte = littleEndian; + family = "x86"; + 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"; - }; - microblazeel = { - bits = 32; - significantByte = littleEndian; - family = "microblaze"; - }; + microblaze = { + bits = 32; + significantByte = bigEndian; + family = "microblaze"; + }; + microblazeel = { + bits = 32; + significantByte = littleEndian; + family = "microblaze"; + }; - mips = { - bits = 32; - significantByte = bigEndian; - family = "mips"; - }; - mipsel = { - bits = 32; - significantByte = littleEndian; - family = "mips"; - }; - mips64 = { - bits = 64; - significantByte = bigEndian; - family = "mips"; - }; - mips64el = { - bits = 64; - significantByte = littleEndian; - family = "mips"; - }; + mips = { + bits = 32; + significantByte = bigEndian; + 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"; - }; - powerpc64 = { - bits = 64; - significantByte = bigEndian; - family = "power"; - }; - powerpc64le = { - bits = 64; - significantByte = littleEndian; - family = "power"; - }; - powerpcle = { - bits = 32; - significantByte = littleEndian; - family = "power"; - }; + powerpc = { + bits = 32; + significantByte = bigEndian; + 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"; - }; - riscv64 = { - bits = 64; - significantByte = littleEndian; - family = "riscv"; - }; + riscv32 = { + bits = 32; + significantByte = littleEndian; + family = "riscv"; + }; + riscv64 = { + bits = 64; + significantByte = littleEndian; + family = "riscv"; + }; - s390 = { - bits = 32; - significantByte = bigEndian; - family = "s390"; - }; - s390x = { - bits = 64; - significantByte = bigEndian; - family = "s390"; - }; + s390 = { + bits = 32; + significantByte = bigEndian; + family = "s390"; + }; + s390x = { + bits = 64; + significantByte = bigEndian; + family = "s390"; + }; - sparc = { - bits = 32; - significantByte = bigEndian; - family = "sparc"; - }; - sparc64 = { - bits = 64; - significantByte = bigEndian; - family = "sparc"; - }; + sparc = { + bits = 32; + significantByte = bigEndian; + family = "sparc"; + }; + sparc64 = { + bits = 64; + significantByte = bigEndian; + family = "sparc"; + }; - wasm32 = { - bits = 32; - significantByte = littleEndian; - family = "wasm"; - }; - wasm64 = { - bits = 64; - significantByte = littleEndian; - family = "wasm"; - }; + wasm32 = { + 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"; - }; - msp430 = { - bits = 16; - significantByte = littleEndian; - family = "msp430"; - }; - avr = { - bits = 8; - family = "avr"; - }; + rx = { + bits = 32; + 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. - gnuNetBSDDefaultExecFormat = cpu: - if (cpu.family == "arm" && cpu.bits == 32) - || (cpu.family == "sparc" && cpu.bits == 32) - || (cpu.family == "m68k" && cpu.bits == 32) - || (cpu.family == "x86" && cpu.bits == 32) then + gnuNetBSDDefaultExecFormat = + cpu: + if + (cpu.family == "arm" && cpu.bits == 32) + || (cpu.family == "sparc" && cpu.bits == 32) + || (cpu.family == "m68k" && cpu.bits == 32) + || (cpu.family == "x86" && cpu.bits == 32) + then execFormats.aout else execFormats.elf; @@ -379,7 +423,8 @@ in rec { # 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 # and B respectively can't be executed at the same time. - isCompatible = with cpuTypes; + isCompatible = + with cpuTypes; a: b: any id [ # x86 @@ -496,95 +541,115 @@ in rec { name = "kernel"; description = "kernel name and information"; merge = mergeOneOption; - check = x: - types.execFormat.check x.execFormat - && all types.kernelFamily.check (attrValues x.families); + check = + x: types.execFormat.check x.execFormat && all types.kernelFamily.check (attrValues x.families); }; types.kernel = enum (attrValues kernels); - kernels = let - inherit (execFormats) elf pe wasm unknown macho; - inherit (kernelFamilies) bsd darwin; - in setTypes types.openKernel { - # TODO(@Ericson2314): Don't want to mass-rebuild yet to keeping 'darwin' as - # the normalized name for macOS. - macos = { - execFormat = macho; - families = { inherit darwin; }; - name = "darwin"; - }; - ios = { - execFormat = macho; - families = { inherit darwin; }; - }; - # A tricky thing about FreeBSD is that there is no stable ABI across - # versions. That means that putting in the version as part of the - # config string is paramount. - freebsd12 = { - execFormat = elf; - families = { inherit bsd; }; - name = "freebsd"; - version = 12; - }; - freebsd13 = { - execFormat = elf; - families = { inherit bsd; }; - name = "freebsd"; - version = 13; - }; - 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 = { }; + kernels = + let + inherit (execFormats) + elf + pe + wasm + unknown + macho + ; + inherit (kernelFamilies) bsd darwin; + in + setTypes types.openKernel { + # TODO(@Ericson2314): Don't want to mass-rebuild yet to keeping 'darwin' as + # the normalized name for macOS. + macos = { + execFormat = macho; + families = { + inherit darwin; + }; + name = "darwin"; + }; + ios = { + execFormat = macho; + families = { + inherit darwin; + }; + }; + # A tricky thing about FreeBSD is that there is no stable ABI across + # versions. That means that putting in the version as part of the + # config string is paramount. + freebsd12 = { + execFormat = elf; + families = { + inherit bsd; + }; + name = "freebsd"; + version = 12; + }; + freebsd13 = { + execFormat = elf; + families = { + inherit bsd; + }; + name = "freebsd"; + version = 13; + }; + 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 = kernels.macos; + watchos = kernels.ios; + tvos = kernels.ios; + win32 = kernels.windows; }; - } // { - # aliases - # 'darwin' is the kernel for all of them. We choose macOS by default. - darwin = kernels.macos; - watchos = kernels.ios; - tvos = kernels.ios; - win32 = kernels.windows; - }; ################################################################################ @@ -603,24 +668,34 @@ in rec { # Note: eabi is specific to ARM and PowerPC. # On PowerPC, this corresponds to PPCEABI. # On ARM, this corresponds to ARMEABI. - eabi = { float = "soft"; }; - eabihf = { float = "hard"; }; + eabi = { + float = "soft"; + }; + eabihf = { + float = "hard"; + }; # Other architectures should use ELF in embedded situations. elf = { }; androideabi = { }; android = { - assertions = [{ - assertion = platform: !platform.isAarch32; - message = '' - The "android" ABI is not for 32-bit ARM. Use "androideabi" instead. - ''; - }]; + assertions = [ + { + assertion = platform: !platform.isAarch32; + message = '' + The "android" ABI is not for 32-bit ARM. Use "androideabi" instead. + ''; + } + ]; }; - gnueabi = { float = "soft"; }; - gnueabihf = { float = "hard"; }; + gnueabi = { + float = "soft"; + }; + gnueabihf = { + float = "hard"; + }; gnu = { assertions = [ { @@ -637,24 +712,44 @@ in rec { } ]; }; - gnuabi64 = { abi = "64"; }; - muslabi64 = { abi = "64"; }; + gnuabi64 = { + abi = "64"; + }; + muslabi64 = { + abi = "64"; + }; # 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: # https://www.linux-mips.org/pub/linux/mips/doc/ABI/MIPS-N32-ABI-Handbook.pdf - gnuabin32 = { abi = "n32"; }; - muslabin32 = { abi = "n32"; }; + gnuabin32 = { + abi = "n32"; + }; + muslabin32 = { + abi = "n32"; + }; - gnuabielfv2 = { abi = "elfv2"; }; - gnuabielfv1 = { abi = "elfv1"; }; + gnuabielfv2 = { + abi = "elfv2"; + }; + gnuabielfv1 = { + abi = "elfv1"; + }; - musleabi = { float = "soft"; }; - musleabihf = { float = "hard"; }; + musleabi = { + float = "soft"; + }; + musleabihf = { + float = "hard"; + }; musl = { }; - uclibceabi = { float = "soft"; }; - uclibceabihf = { float = "hard"; }; + uclibceabi = { + float = "soft"; + }; + uclibceabihf = { + float = "hard"; + }; uclibc = { }; unknown = { }; @@ -664,73 +759,111 @@ in rec { types.parsedPlatform = mkOptionType { 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; - check = { cpu, vendor, kernel, abi, }: - types.cpuType.check cpu && types.vendor.check vendor - && types.kernel.check kernel && types.abi.check abi; + check = + { + cpu, + vendor, + kernel, + abi, + }: + types.cpuType.check cpu + && types.vendor.check vendor + && types.kernel.check kernel + && types.abi.check abi; }; isSystem = isType "system"; - mkSystem = components: + mkSystem = + components: assert types.parsedPlatform.check components; setType "system" components; - mkSkeletonFromList = l: + mkSkeletonFromList = + l: { - "1" = if elemAt l 0 == "avr" then { - cpu = elemAt l 0; - kernel = "none"; - abi = "unknown"; - } else - throw "Target specification with 1 components is ambiguous"; + "1" = + if elemAt l 0 == "avr" then + { + cpu = elemAt l 0; + kernel = "none"; + abi = "unknown"; + } + else + throw "Target specification with 1 components is ambiguous"; "2" = # We only do 2-part hacks for things Nix already supports - if elemAt l 1 == "cygwin" then { - cpu = elemAt l 0; - kernel = "windows"; - abi = "cygnus"; - } + if elemAt l 1 == "cygwin" then + { + cpu = elemAt l 0; + kernel = "windows"; + abi = "cygnus"; + } # 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 # minGW. So it's easier to make gnu* the default for the MinGW, but # hack-in MSVC for the non-MinGW case right here. - else if elemAt l 1 == "windows" then { - cpu = elemAt l 0; - kernel = "windows"; - 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; - }; + else if elemAt l 1 == "windows" then + { + cpu = elemAt l 0; + kernel = "windows"; + 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" = # cpu-kernel-environment - if elemAt l 1 == "linux" - || elem (elemAt l 2) [ "eabi" "eabihf" "elf" "gnu" ] then { - cpu = elemAt l 0; - kernel = elemAt l 1; - abi = elemAt l 2; - vendor = "unknown"; - } + if + elemAt l 1 == "linux" + || elem (elemAt l 2) [ + "eabi" + "eabihf" + "elf" + "gnu" + ] + then + { + cpu = elemAt l 0; + kernel = elemAt l 1; + abi = elemAt l 2; + vendor = "unknown"; + } # cpu-vendor-os - else if elemAt l 1 == "apple" - || elem (elemAt l 2) [ "wasi" "redox" "mmixware" "ghcjs" "mingw32" ] - || hasPrefix "freebsd" (elemAt l 2) || hasPrefix "netbsd" (elemAt l 2) - || hasPrefix "genode" (elemAt l 2) then { - cpu = elemAt l 0; - vendor = elemAt l 1; - kernel = if elemAt l 2 == "mingw32" then - "windows" # autotools breaks on -gnu for window - else - elemAt l 2; - } else + else if + elemAt l 1 == "apple" + || elem (elemAt l 2) [ + "wasi" + "redox" + "mmixware" + "ghcjs" + "mingw32" + ] + || hasPrefix "freebsd" (elemAt l 2) + || hasPrefix "netbsd" (elemAt l 2) + || hasPrefix "genode" (elemAt l 2) + then + { + cpu = elemAt l 0; + vendor = elemAt l 1; + kernel = + if elemAt l 2 == "mingw32" then + "windows" # autotools breaks on -gnu for window + else + elemAt l 2; + } + else throw "Target specification with 3 components is ambiguous"; "4" = { cpu = elemAt l 0; @@ -738,16 +871,25 @@ in rec { kernel = elemAt l 2; abi = elemAt l 3; }; - }.${toString (length l)} or (throw - "system string has invalid number of hyphen-separated components"); + } + .${toString (length l)} + or (throw "system string has invalid number of hyphen-separated components"); # This should revert the job done by config.guess from the gcc compiler. - mkSystemFromSkeleton = { cpu, - # Optional, but fallback too complex for here. - # Inferred below instead. - vendor ? assert false; null, kernel, - # Also inferred below - abi ? assert false; null, }@args: + mkSystemFromSkeleton = + { + cpu, + # Optional, but fallback too complex for here. + # Inferred below instead. + vendor ? + assert false; + null, + kernel, + # Also inferred below + abi ? + assert false; + null, + }@args: let getCpu = name: cpuTypes.${name} or (throw "Unknown CPU type: ${name}"); getVendor = name: vendors.${name} or (throw "Unknown vendor: ${name}"); @@ -756,44 +898,50 @@ in rec { parsed = { cpu = getCpu args.cpu; - vendor = if args ? vendor then - getVendor args.vendor - else if isDarwin parsed then - vendors.apple - else if isWindows parsed then - vendors.pc - 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 = if args ? abi then - getAbi args.abi - else if isLinux parsed || isWindows parsed then - if isAarch32 parsed then - if versionAtLeast (parsed.cpu.version or "0") "6" then - abis.gnueabihf + vendor = + if args ? vendor then + getVendor args.vendor + else if isDarwin parsed then + vendors.apple + else if isWindows parsed then + vendors.pc + 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 = + if args ? abi then + getAbi args.abi + else if isLinux parsed || isWindows parsed then + if isAarch32 parsed then + if versionAtLeast (parsed.cpu.version or "0") "6" then abis.gnueabihf else abis.gnueabi + # Default ppc64 BE to ELFv2 + else if isPower64 parsed && isBigEndian parsed then + abis.gnuabielfv2 else - abis.gnueabi - # Default ppc64 BE to ELFv2 - else if isPower64 parsed && isBigEndian parsed then - abis.gnuabielfv2 + abis.gnu else - abis.gnu - else - abis.unknown; + abis.unknown; }; - in mkSystem parsed; + in + mkSystem parsed; - mkSystemFromString = s: - mkSystemFromSkeleton (mkSkeletonFromList (splitString "-" s)); + mkSystemFromString = s: mkSystemFromSkeleton (mkSkeletonFromList (splitString "-" s)); kernelName = kernel: kernel.name + toString (kernel.version or ""); - doubleFromSystem = { cpu, kernel, abi, ... }: + doubleFromSystem = + { + cpu, + kernel, + abi, + ... + }: if abi == abis.cygnus then "${cpu.name}-cygwin" else if kernel.families ? darwin then @@ -801,16 +949,22 @@ in rec { else "${cpu.name}-${kernelName kernel}"; - tripleFromSystem = { cpu, vendor, kernel, abi, ... }@sys: + tripleFromSystem = + { + cpu, + vendor, + kernel, + abi, + ... + }@sys: assert isSystem sys; let - optExecFormat = optionalString (kernel.name == "netbsd" - && gnuNetBSDDefaultExecFormat cpu != kernel.execFormat) - kernel.execFormat.name; + optExecFormat = optionalString ( + kernel.name == "netbsd" && gnuNetBSDDefaultExecFormat cpu != kernel.execFormat + ) kernel.execFormat.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}"; ################################################################################ } diff --git a/nix/stdlib/systems/platforms.nix b/nix/stdlib/systems/platforms.nix index 778dac2..19abe66 100644 --- a/nix/stdlib/systems/platforms.nix +++ b/nix/stdlib/systems/platforms.nix @@ -4,7 +4,8 @@ # required (see types.parsedPlatform in lib/systems/parse.nix). This # file takes an already-valid platform and further elaborates it with # optional fields; currently these are: linux-kernel, gcc, and rustc. -{ lib }: rec { +{ lib }: +rec { pc = { linux-kernel = { name = "pc"; @@ -16,8 +17,7 @@ }; }; - pc_simplekernel = - lib.recursiveUpdate pc { linux-kernel.autoModules = false; }; + pc_simplekernel = lib.recursiveUpdate pc { linux-kernel.autoModules = false; }; powernv = { linux-kernel = { @@ -64,7 +64,9 @@ # TODO reenable once manual-config's config actually builds a .dtb and this is checked to be working #DTB = true; }; - gcc = { arch = "armv5te"; }; + gcc = { + arch = "armv5te"; + }; }; sheevaplug = { @@ -175,7 +177,9 @@ target = "uImage"; DTB = true; # Beyond 3.10 }; - gcc = { arch = "armv5te"; }; + gcc = { + arch = "armv5te"; + }; }; raspberrypi = { @@ -203,7 +207,11 @@ raspberrypi2 = armv7l-hf-multiplatform; # Nvidia Bluefield 2 (w. crypto support) - bluefield2 = { gcc = { arch = "armv8-a+fp+simd+crc+crypto"; }; }; + bluefield2 = { + gcc = { + arch = "armv8-a+fp+simd+crc+crypto"; + }; + }; zero-gravitas = { linux-kernel = { @@ -374,7 +382,9 @@ ''; target = "Image"; }; - gcc = { arch = "armv8-a"; }; + gcc = { + arch = "armv8-a"; + }; }; apple-m1 = { @@ -389,7 +399,9 @@ ## ben_nanonote = { - linux-kernel = { name = "ben_nanonote"; }; + linux-kernel = { + name = "ben_nanonote"; + }; gcc = { arch = "mips32"; float = "soft"; @@ -561,14 +573,17 @@ # This function takes a minimally-valid "platform" and returns an # attrset containing zero or more additional attrs which should be # included in the platform in order to further elaborate it. - select = platform: + select = + platform: # x86 if platform.isx86 then pc - # ARM + # ARM else if platform.isAarch32 then - let version = platform.parsed.cpu.version or null; - in if version == null then + let + version = platform.parsed.cpu.version or null; + in + if version == null then pc else if lib.versionOlder version "6" then sheevaplug diff --git a/nix/stdlib/trivial.nix b/nix/stdlib/trivial.nix index a3c7b71..d8c09bd 100644 --- a/nix/stdlib/trivial.nix +++ b/nix/stdlib/trivial.nix @@ -1,115 +1,128 @@ { lib }: let inherit (lib.trivial) - isFunction isInt functionArgs pathExists release setFunctionArgs - toBaseDigits version versionSuffix warn; -in { + isFunction + isInt + functionArgs + pathExists + release + setFunctionArgs + toBaseDigits + version + versionSuffix + warn + ; +in +{ ## Simple (higher order) functions - /* * - The identity function - For when you need a function that does “nothing”. + /* + * + The identity function + For when you need a function that does “nothing”. - # Inputs + # Inputs - `x` + `x` - : The value to return + : The value to return - # Type + # Type - ``` - id :: a -> a - ``` + ``` + id :: a -> a + ``` */ id = x: x; - /* * - The constant function + /* + * + The constant function - Ignores the second argument. If called with only one argument, - constructs a function that always returns a static value. + Ignores the second argument. If called with only one argument, + constructs a function that always returns a static value. - # Inputs + # Inputs - `x` + `x` - : Value to return + : Value to return - `y` + `y` - : Value to ignore + : Value to ignore - # Type + # Type - ``` - const :: a -> b -> a - ``` + ``` + const :: a -> b -> a + ``` - # Examples - :::{.example} - ## `lib.trivial.const` usage example + # Examples + :::{.example} + ## `lib.trivial.const` usage example - ```nix - let f = const 5; in f 10 - => 5 - ``` + ```nix + let f = const 5; in f 10 + => 5 + ``` - ::: + ::: */ const = 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. - # Inputs + # Inputs - `value` + `value` - : Value to start piping. + : Value to start piping. - `fns` + `fns` - : List of functions to apply sequentially. + : List of functions to apply sequentially. - # Type + # Type - ``` - pipe :: a -> [] -> - ``` + ``` + pipe :: a -> [] -> + ``` - # Examples - :::{.example} - ## `lib.trivial.pipe` usage example + # Examples + :::{.example} + ## `lib.trivial.pipe` usage example - ```nix - pipe 2 [ - (x: x + 2) # 2 + 2 = 4 - (x: x * 2) # 4 * 2 = 8 - ] - => 8 + ```nix + pipe 2 [ + (x: x + 2) # 2 + 2 = 4 + (x: x * 2) # 4 * 2 = 8 + ] + => 8 - # ideal to do text transformations - pipe [ "a/b" "a/c" ] [ + # ideal to do text transformations + pipe [ "a/b" "a/c" ] [ - # create the cp command - (map (file: ''cp "${src}/${file}" $out\n'')) + # create the cp command + (map (file: ''cp "${src}/${file}" $out\n'')) - # concatenate all commands into one string - lib.concatStrings + # concatenate all commands into one string + lib.concatStrings - # make that string into a nix derivation - (pkgs.runCommand "copy-to-out" {}) + # make that string into a nix derivation + (pkgs.runCommand "copy-to-out" {}) - ] - => + ] + => - The output type of each function has to be the input type - of the next function, and the last function returns the - final value. - ``` + The output type of each function has to be the input type + of the next function, and the last function returns the + final value. + ``` - ::: + ::: */ pipe = builtins.foldl' (x: f: f x); @@ -120,489 +133,543 @@ in { ## Named versions corresponding to some builtin operators. - /* * - Concatenate two lists + /* + * + Concatenate two lists - # Inputs + # Inputs - `x` + `x` - : 1\. Function argument + : 1\. Function argument - `y` + `y` - : 2\. Function argument + : 2\. Function argument - # Type + # Type - ``` - concat :: [a] -> [a] -> [a] - ``` + ``` + concat :: [a] -> [a] -> [a] + ``` - # Examples - :::{.example} - ## `lib.trivial.concat` usage example + # Examples + :::{.example} + ## `lib.trivial.concat` usage example - ```nix - concat [ 1 2 ] [ 3 4 ] - => [ 1 2 3 4 ] - ``` + ```nix + concat [ 1 2 ] [ 3 4 ] + => [ 1 2 3 4 ] + ``` - ::: + ::: */ concat = x: y: x ++ y; - /* * - boolean “or” + /* + * + boolean “or” - # Inputs + # Inputs - `x` + `x` - : 1\. Function argument + : 1\. Function argument - `y` + `y` - : 2\. Function argument + : 2\. Function argument */ or = x: y: x || y; - /* * - boolean “and” + /* + * + boolean “and” - # Inputs + # Inputs - `x` + `x` - : 1\. Function argument + : 1\. Function argument - `y` + `y` - : 2\. Function argument + : 2\. Function argument */ and = x: y: x && y; - /* * - boolean “exclusive or” + /* + * + boolean “exclusive or” - # Inputs + # Inputs - `x` + `x` - : 1\. Function argument + : 1\. Function argument - `y` + `y` - : 2\. Function argument + : 2\. Function argument */ # We explicitly invert the arguments purely as a type assertion. # This is invariant under XOR, so it does not affect the result. xor = x: y: (!x) != (!y); - /* * - bitwise “not” + /* + * + bitwise “not” */ bitNot = builtins.sub (-1); - /* * - Convert a boolean to a string. + /* + * + Convert a boolean to a string. - This function uses the strings "true" and "false" to represent - boolean values. Calling `toString` on a bool instead returns "1" - and "" (sic!). + This function uses the strings "true" and "false" to represent + boolean values. Calling `toString` on a bool instead returns "1" + and "" (sic!). - # Inputs + # Inputs - `b` + `b` - : 1\. Function argument + : 1\. Function argument - # Type + # Type - ``` - boolToString :: bool -> string - ``` + ``` + boolToString :: bool -> string + ``` */ boolToString = b: if b then "true" else "false"; - /* * - Merge two attribute sets shallowly, right side trumps left + /* + * + Merge two attribute sets shallowly, right side trumps left - mergeAttrs :: attrs -> attrs -> attrs + mergeAttrs :: attrs -> attrs -> attrs - # Inputs + # Inputs - `x` + `x` - : Left attribute set + : Left attribute set - `y` + `y` - : Right attribute set (higher precedence for equal keys) + : Right attribute set (higher precedence for equal keys) - # Examples - :::{.example} - ## `lib.trivial.mergeAttrs` usage example + # Examples + :::{.example} + ## `lib.trivial.mergeAttrs` usage example - ```nix - mergeAttrs { a = 1; b = 2; } { b = 3; c = 4; } - => { a = 1; b = 3; c = 4; } - ``` + ```nix + mergeAttrs { a = 1; b = 2; } { b = 3; c = 4; } + => { a = 1; b = 3; c = 4; } + ``` - ::: + ::: */ mergeAttrs = 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` - : 1\. Function argument + : 1\. Function argument - `a` + `a` - : 2\. Function argument + : 2\. Function argument - `b` + `b` - : 3\. Function argument + : 3\. Function argument - # Type + # Type - ``` - flip :: (a -> b -> c) -> (b -> a -> c) - ``` + ``` + flip :: (a -> b -> c) -> (b -> a -> c) + ``` - # Examples - :::{.example} - ## `lib.trivial.flip` usage example + # Examples + :::{.example} + ## `lib.trivial.flip` usage example - ```nix - flip concat [1] [2] - => [ 2 1 ] - ``` + ```nix + flip concat [1] [2] + => [ 2 1 ] + ``` - ::: + ::: */ - flip = f: a: b: f b a; + flip = + f: a: b: + f b a; - /* * - Apply function if the supplied argument is non-null. + /* + * + Apply function if the supplied argument is non-null. - # Inputs + # Inputs - `f` + `f` - : Function to call + : Function to call - `a` + `a` - : Argument to check for null before passing it to `f` + : Argument to check for null before passing it to `f` - # Examples - :::{.example} - ## `lib.trivial.mapNullable` usage example + # Examples + :::{.example} + ## `lib.trivial.mapNullable` usage example - ```nix - mapNullable (x: x+1) null - => null - mapNullable (x: x+1) 22 - => 23 - ``` + ```nix + mapNullable (x: x+1) null + => null + mapNullable (x: x+1) 22 + => 23 + ``` - ::: + ::: */ mapNullable = f: a: if a == null then a else f a; # Pull in some builtins not included elsewhere. inherit (builtins) - pathExists readFile isBool isInt isFloat add sub lessThan seq deepSeq - genericClosure bitAnd bitOr bitXor; + pathExists + readFile + isBool + isInt + isFloat + add + sub + lessThan + seq + deepSeq + genericClosure + bitAnd + bitOr + bitXor + ; ## nixpkgs version strings - /* * - Returns the current full nixpkgs version number. + /* + * + Returns the current full nixpkgs version number. */ version = release + versionSuffix; - /* * - Returns the current nixpkgs release number as string. + /* + * + Returns the current nixpkgs release number as string. */ release = lib.strings.fileContents ../.version; - /* * - The latest release that is supported, at the time of release branch-off, - if applicable. + /* + * + The latest release that is supported, at the time of release branch-off, + if applicable. - Ideally, out-of-tree modules should be able to evaluate cleanly with all - supported Nixpkgs versions (master, release and old release until EOL). - So if possible, deprecation warnings should take effect only when all - out-of-tree expressions/libs/modules can upgrade to the new way without - losing support for supported Nixpkgs versions. + Ideally, out-of-tree modules should be able to evaluate cleanly with all + supported Nixpkgs versions (master, release and old release until EOL). + So if possible, deprecation warnings should take effect only when all + out-of-tree expressions/libs/modules can upgrade to the new way without + losing support for supported Nixpkgs versions. - This release number allows deprecation warnings to be implemented such that - they take effect as soon as the oldest release reaches end of life. + This release number allows deprecation warnings to be implemented such that + they take effect as soon as the oldest release reaches end of life. */ oldestSupportedRelease = # Update on master only. Do not backport. 2311; - /* * - Whether a feature is supported in all supported releases (at the time of - release branch-off, if applicable). See `oldestSupportedRelease`. + /* + * + Whether a feature is supported in all supported releases (at the time of + release branch-off, if applicable). See `oldestSupportedRelease`. - # Inputs + # Inputs - `release` + `release` - : Release number of feature introduction as an integer, e.g. 2111 for 21.11. - Set it to the upcoming release, matching the nixpkgs/.version file. + : Release number of feature introduction as an integer, e.g. 2111 for 21.11. + Set it to the upcoming release, matching the nixpkgs/.version file. */ isInOldestRelease = release: release <= lib.trivial.oldestSupportedRelease; - /* * - Returns the current nixpkgs release code name. + /* + * + Returns the current nixpkgs release code name. - On each release the first letter is bumped and a new animal is chosen - starting with that new letter. + On each release the first letter is bumped and a new animal is chosen + starting with that new letter. */ codeName = "Uakari"; - /* * - Returns the current nixpkgs version suffix as string. + /* + * + Returns the current nixpkgs version suffix as string. */ - versionSuffix = let suffixFile = ../../.version-suffix; - in if pathExists suffixFile then - lib.strings.fileContents suffixFile - else - "pre-git"; + versionSuffix = + let + suffixFile = ../../.version-suffix; + in + if pathExists suffixFile then lib.strings.fileContents suffixFile else "pre-git"; - /* * - Attempts to return the the current revision of nixpkgs and - returns the supplied default value otherwise. + /* + * + Attempts to return the the current revision of nixpkgs and + returns the supplied default value otherwise. - # Inputs + # Inputs - `default` + `default` - : Default value to return if revision can not be determined + : Default value to return if revision can not be determined - # Type + # Type - ``` - revisionWithDefault :: string -> string - ``` + ``` + revisionWithDefault :: string -> string + ``` */ - revisionWithDefault = default: + revisionWithDefault = + default: let revisionFile = "${toString ./..}/.git-revision"; gitRepo = "${toString ./..}/.git"; - in if lib.pathIsGitRepo gitRepo then + in + if lib.pathIsGitRepo gitRepo then lib.commitIdFromGitRepo gitRepo else if lib.pathExists revisionFile then lib.fileContents revisionFile else default; - nixpkgsVersion = - warn "lib.nixpkgsVersion is a deprecated alias of lib.version." version; + nixpkgsVersion = warn "lib.nixpkgsVersion is a deprecated alias of lib.version." version; - /* * - Determine whether the function is being called from inside a Nix - shell. + /* + * + Determine whether the function is being called from inside a Nix + shell. - # Type + # Type - ``` - inNixShell :: bool - ``` + ``` + inNixShell :: bool + ``` */ inNixShell = builtins.getEnv "IN_NIX_SHELL" != ""; - /* * - Determine whether the function is being called from inside pure-eval mode - by seeing whether `builtins` contains `currentSystem`. If not, we must be in - pure-eval mode. + /* + * + Determine whether the function is being called from inside pure-eval mode + by seeing whether `builtins` contains `currentSystem`. If not, we must be in + pure-eval mode. - # Type + # Type - ``` - inPureEvalMode :: bool - ``` + ``` + inPureEvalMode :: bool + ``` */ inPureEvalMode = !builtins ? currentSystem; ## Integer operations - /* * - Return minimum of two numbers. + /* + * + Return minimum of two numbers. - # Inputs + # Inputs - `x` + `x` - : 1\. Function argument + : 1\. Function argument - `y` + `y` - : 2\. Function argument + : 2\. Function argument */ min = x: y: if x < y then x else y; - /* * - Return maximum of two numbers. + /* + * + Return maximum of two numbers. - # Inputs + # Inputs - `x` + `x` - : 1\. Function argument + : 1\. Function argument - `y` + `y` - : 2\. Function argument + : 2\. Function argument */ max = x: y: if x > y then x else y; - /* * - Integer modulus + /* + * + Integer modulus - # Inputs + # Inputs - `base` + `base` - : 1\. Function argument + : 1\. Function argument - `int` + `int` - : 2\. Function argument + : 2\. Function argument - # Examples - :::{.example} - ## `lib.trivial.mod` usage example + # Examples + :::{.example} + ## `lib.trivial.mod` usage example - ```nix - mod 11 10 - => 1 - mod 1 10 - => 1 - ``` + ```nix + mod 11 10 + => 1 + mod 1 10 + => 1 + ``` - ::: + ::: */ mod = base: int: base - (int * (builtins.div base int)); ## Comparisons - /* * - C-style comparisons + /* + * + C-style comparisons - a < b, compare a b => -1 - a == b, compare a b => 0 - a > b, compare a b => 1 + a < b, compare a b => -1 + a == b, compare a b => 0 + a > b, compare a b => 1 - # Inputs + # Inputs - `a` + `a` - : 1\. Function argument + : 1\. Function argument - `b` + `b` - : 2\. Function argument + : 2\. Function argument */ - compare = a: b: if a < b then -1 else if a > b then 1 else 0; + compare = + a: b: + if a < b then + -1 + else if a > b then + 1 + else + 0; - /* * - Split type into two subtypes by predicate `p`, take all elements - of the first subtype to be less than all the elements of the - second subtype, compare elements of a single subtype with `yes` - and `no` respectively. + /* + * + Split type into two subtypes by predicate `p`, take all elements + of the first subtype to be less than all the elements of the + second subtype, compare elements of a single subtype with `yes` + and `no` respectively. - # Inputs + # Inputs - `p` + `p` - : Predicate + : Predicate - `yes` + `yes` - : Comparison function if predicate holds for both values + : Comparison function if predicate holds for both values - `no` + `no` - : Comparison function if predicate holds for neither value + : Comparison function if predicate holds for neither value - `a` + `a` - : First value to compare + : First value to compare - `b` + `b` - : Second value to compare + : Second value to compare - # Type + # Type - ``` - (a -> bool) -> (a -> a -> int) -> (a -> a -> int) -> (a -> a -> int) - ``` + ``` + (a -> bool) -> (a -> a -> int) -> (a -> a -> int) -> (a -> a -> int) + ``` - # Examples - :::{.example} - ## `lib.trivial.splitByAndCompare` usage example + # Examples + :::{.example} + ## `lib.trivial.splitByAndCompare` usage example - ```nix - let cmp = splitByAndCompare (hasPrefix "foo") compare compare; in + ```nix + let cmp = splitByAndCompare (hasPrefix "foo") compare compare; in - cmp "a" "z" => -1 - cmp "fooa" "fooz" => -1 + cmp "a" "z" => -1 + cmp "fooa" "fooz" => -1 - cmp "f" "a" => 1 - cmp "fooa" "a" => -1 - # while - compare "fooa" "a" => 1 - ``` + cmp "f" "a" => 1 + cmp "fooa" "a" => -1 + # while + compare "fooa" "a" => 1 + ``` - ::: + ::: */ - splitByAndCompare = p: yes: no: a: b: - if p a then if p b then yes a b else -1 else if p b then 1 else no a b; + splitByAndCompare = + p: yes: no: a: b: + if p a then + if p b then yes a b else -1 + else if p b then + 1 + else + no a b; - /* * - Reads a JSON file. + /* + * + Reads a JSON file. - # Inputs + # Inputs - `path` + `path` - : 1\. Function argument + : 1\. Function argument - # Type + # Type - ``` - importJSON :: path -> any - ``` + ``` + importJSON :: path -> any + ``` */ importJSON = path: builtins.fromJSON (builtins.readFile path); - /* * - Reads a TOML file. + /* + * + Reads a TOML file. - # Inputs + # Inputs - `path` + `path` - : 1\. Function argument + : 1\. Function argument - # Type + # Type - ``` - importTOML :: path -> any - ``` + ``` + importTOML :: path -> any + ``` */ importTOML = path: builtins.fromTOML (builtins.readFile path); @@ -621,193 +688,206 @@ in { # TODO: figure out a clever way to integrate location information from # something like __unsafeGetAttrPos. - /* * - Print a warning before returning the second argument. This function behaves - like `builtins.trace`, but requires a string message and formats it as a - warning, including the `warning: ` prefix. + /* + * + Print a warning before returning the second argument. This function behaves + like `builtins.trace`, but requires a string message and formats it as a + warning, including the `warning: ` prefix. - To get a call stack trace and abort evaluation, set the environment variable - `NIX_ABORT_ON_WARN=true` and set the Nix options `--option pure-eval false --show-trace` + To get a call stack trace and abort evaluation, set the environment variable + `NIX_ABORT_ON_WARN=true` and set the Nix options `--option pure-eval false --show-trace` - # Inputs + # Inputs - `msg` + `msg` - : Warning message to print. + : Warning message to print. - `val` + `val` - : Value to return as-is. + : Value to return as-is. - # Type + # Type - ``` - string -> a -> a - ``` + ``` + string -> a -> a + ``` */ warn = - if lib.elem (builtins.getEnv "NIX_ABORT_ON_WARN") [ "1" "true" "yes" ] then + if + lib.elem (builtins.getEnv "NIX_ABORT_ON_WARN") [ + "1" + "true" + "yes" + ] + then msg: - builtins.trace "warning: ${msg}" (abort - "NIX_ABORT_ON_WARN=true; warnings are treated as unrecoverable errors.") + builtins.trace "warning: ${msg}" ( + abort "NIX_ABORT_ON_WARN=true; warnings are treated as unrecoverable errors." + ) else msg: builtins.trace "warning: ${msg}"; - /* * - Like warn, but only warn when the first argument is `true`. + /* + * + Like warn, but only warn when the first argument is `true`. - # Inputs + # Inputs - `cond` + `cond` - : 1\. Function argument + : 1\. Function argument - `msg` + `msg` - : 2\. Function argument + : 2\. Function argument - `val` + `val` - : Value to return as-is. + : Value to return as-is. - # Type + # Type - ``` - bool -> string -> a -> a - ``` + ``` + bool -> string -> a -> a + ``` */ warnIf = cond: msg: if cond then warn msg else x: x; - /* * - Like warnIf, but negated (warn if the first argument is `false`). + /* + * + Like warnIf, but negated (warn if the first argument is `false`). - # Inputs + # Inputs - `cond` + `cond` - : 1\. Function argument + : 1\. Function argument - `msg` + `msg` - : 2\. Function argument + : 2\. Function argument - `val` + `val` - : Value to return as-is. + : Value to return as-is. - # Type + # Type - ``` - bool -> string -> a -> a - ``` + ``` + bool -> string -> a -> a + ``` */ warnIfNot = cond: msg: if cond then x: x else warn msg; - /* * - Like the `assert b; e` expression, but with a custom error message and - without the semicolon. + /* + * + Like the `assert b; e` expression, but with a custom error message and + without the semicolon. - If true, return the identity function, `r: r`. + If true, return the identity function, `r: r`. - If false, throw the error message. + If false, throw the error message. - Calls can be juxtaposed using function application, as `(r: r) a = a`, so - `(r: r) (r: r) a = a`, and so forth. + Calls can be juxtaposed using function application, as `(r: r) a = a`, so + `(r: r) (r: r) a = a`, and so forth. - # Inputs + # Inputs - `cond` + `cond` - : 1\. Function argument + : 1\. Function argument - `msg` + `msg` - : 2\. Function argument + : 2\. Function argument - # Type + # Type - ``` - bool -> string -> a -> a - ``` + ``` + bool -> string -> a -> a + ``` - # Examples - :::{.example} - ## `lib.trivial.throwIfNot` usage example + # Examples + :::{.example} + ## `lib.trivial.throwIfNot` usage example - ```nix - throwIfNot (lib.isList overlays) "The overlays argument to nixpkgs must be a list." - lib.foldr (x: throwIfNot (lib.isFunction x) "All overlays passed to nixpkgs must be functions.") (r: r) overlays - pkgs - ``` + ```nix + throwIfNot (lib.isList overlays) "The overlays argument to nixpkgs must be a list." + lib.foldr (x: throwIfNot (lib.isFunction x) "All overlays passed to nixpkgs must be functions.") (r: r) overlays + pkgs + ``` - ::: + ::: */ throwIfNot = cond: msg: if cond then x: x else throw msg; - /* * - 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` - : 1\. Function argument + : 1\. Function argument - `msg` + `msg` - : 2\. Function argument + : 2\. Function argument - # Type + # Type - ``` - bool -> string -> a -> a - ``` + ``` + bool -> string -> a -> a + ``` */ throwIf = cond: msg: if cond then throw msg else x: x; - /* * - 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` - : 1\. Function argument + : 1\. Function argument - `valid` + `valid` - : 2\. Function argument + : 2\. Function argument - `given` + `given` - : 3\. Function argument + : 3\. Function argument - # Type + # Type - ``` - String -> List ComparableVal -> List ComparableVal -> a -> a - ``` + ``` + String -> List ComparableVal -> List ComparableVal -> a -> a + ``` - # Examples - :::{.example} - ## `lib.trivial.checkListOfEnum` usage example + # Examples + :::{.example} + ## `lib.trivial.checkListOfEnum` usage example - ```nix - let colorVariants = ["bright" "dark" "black"] - in checkListOfEnum "color variants" [ "standard" "light" "dark" ] colorVariants; - => - error: color variants: bright, black unexpected; valid ones: standard, light, dark - ``` + ```nix + let colorVariants = ["bright" "dark" "black"] + in checkListOfEnum "color variants" [ "standard" "light" "dark" ] colorVariants; + => + error: color variants: bright, black unexpected; valid ones: standard, light, dark + ``` - ::: + ::: */ - checkListOfEnum = msg: valid: given: - let unexpected = lib.subtractLists valid given; - in lib.throwIfNot (unexpected == [ ]) "${msg}: ${ - builtins.concatStringsSep ", " (builtins.map builtins.toString unexpected) - } unexpected; valid ones: ${ - builtins.concatStringsSep ", " (builtins.map builtins.toString valid) - }"; + checkListOfEnum = + msg: valid: given: + let + unexpected = lib.subtractLists valid given; + in + lib.throwIfNot (unexpected == [ ]) + "${msg}: ${builtins.concatStringsSep ", " (builtins.map builtins.toString unexpected)} unexpected; valid ones: ${builtins.concatStringsSep ", " (builtins.map builtins.toString valid)}"; info = msg: builtins.trace "INFO: ${msg}"; @@ -815,26 +895,27 @@ in { ## Function annotations - /* * - Add metadata about expected function arguments to a function. - The metadata should match the format given by - builtins.functionArgs, i.e. a set from expected argument to a bool - representing whether that argument has a default or not. - setFunctionArgs : (a → b) → Map String Bool → (a → b) + /* + * + Add metadata about expected function arguments to a function. + The metadata should match the format given by + builtins.functionArgs, i.e. a set from expected argument to a bool + representing whether that argument has a default or not. + setFunctionArgs : (a → b) → Map String Bool → (a → b) - This function is necessary because you can't dynamically create a - function of the { a, b ? foo, ... }: format, but some facilities - like callPackage expect to be able to query expected arguments. + This function is necessary because you can't dynamically create a + function of the { a, b ? foo, ... }: format, but some facilities + like callPackage expect to be able to query expected arguments. - # Inputs + # Inputs - `f` + `f` - : 1\. Function argument + : 1\. Function argument - `args` + `args` - : 2\. Function argument + : 2\. Function argument */ setFunctionArgs = f: args: { # TODO: Should we add call-time "type" checking like built in? @@ -842,165 +923,180 @@ in { __functionArgs = args; }; - /* * - Extract the expected function arguments from a function. - This works both with nix-native { a, b ? foo, ... }: style - functions and functions with args set with 'setFunctionArgs'. It - has the same return type and semantics as builtins.functionArgs. - setFunctionArgs : (a → b) → Map String Bool. + /* + * + Extract the expected function arguments from a function. + This works both with nix-native { a, b ? foo, ... }: style + functions and functions with args set with 'setFunctionArgs'. It + has the same return type and semantics as builtins.functionArgs. + setFunctionArgs : (a → b) → Map String Bool. - # Inputs + # Inputs - `f` + `f` - : 1\. Function argument + : 1\. Function argument */ - functionArgs = f: + functionArgs = + f: if f ? __functor then f.__functionArgs or (functionArgs (f.__functor f)) else builtins.functionArgs f; - /* * - Check whether something is a function or something - annotated with function args. + /* + * + Check whether something is a function or something + annotated with function args. - # Inputs + # Inputs - `f` + `f` - : 1\. Function argument + : 1\. Function argument */ - isFunction = f: - builtins.isFunction f || (f ? __functor && isFunction (f.__functor f)); + isFunction = f: builtins.isFunction f || (f ? __functor && isFunction (f.__functor f)); - /* * - `mirrorFunctionArgs f g` creates a new function `g'` with the same behavior as `g` (`g' x == g x`) - but its function arguments mirroring `f` (`lib.functionArgs g' == lib.functionArgs f`). + /* + * + `mirrorFunctionArgs f g` creates a new function `g'` with the same behavior as `g` (`g' x == g x`) + but its function arguments mirroring `f` (`lib.functionArgs g' == lib.functionArgs f`). - # Inputs + # Inputs - `f` + `f` - : Function to provide the argument metadata + : Function to provide the argument metadata - `g` + `g` - : Function to set the argument metadata to + : Function to set the argument metadata to - # Type + # Type - ``` - mirrorFunctionArgs :: (a -> b) -> (a -> c) -> (a -> c) - ``` + ``` + mirrorFunctionArgs :: (a -> b) -> (a -> c) -> (a -> c) + ``` - # Examples - :::{.example} - ## `lib.trivial.mirrorFunctionArgs` usage example + # Examples + :::{.example} + ## `lib.trivial.mirrorFunctionArgs` usage example - ```nix - addab = {a, b}: a + b - addab { a = 2; b = 4; } - => 6 - lib.functionArgs addab - => { a = false; b = false; } - addab1 = attrs: addab attrs + 1 - addab1 { a = 2; b = 4; } - => 7 - lib.functionArgs addab1 - => { } - addab1' = lib.mirrorFunctionArgs addab addab1 - addab1' { a = 2; b = 4; } - => 7 - lib.functionArgs addab1' - => { a = false; b = false; } - ``` + ```nix + addab = {a, b}: a + b + addab { a = 2; b = 4; } + => 6 + lib.functionArgs addab + => { a = false; b = false; } + addab1 = attrs: addab attrs + 1 + addab1 { a = 2; b = 4; } + => 7 + lib.functionArgs addab1 + => { } + addab1' = lib.mirrorFunctionArgs addab addab1 + addab1' { a = 2; b = 4; } + => 7 + lib.functionArgs addab1' + => { a = false; b = false; } + ``` - ::: + ::: */ - mirrorFunctionArgs = f: - let fArgs = functionArgs f; - in g: setFunctionArgs g fArgs; + mirrorFunctionArgs = + f: + let + fArgs = functionArgs f; + in + g: setFunctionArgs g fArgs; - /* * - Turns any non-callable values into constant functions. - Returns callable values as is. + /* + * + Turns any non-callable values into constant functions. + Returns callable values as is. - # Inputs + # Inputs - `v` + `v` - : Any value + : Any value - # Examples - :::{.example} - ## `lib.trivial.toFunction` usage example + # Examples + :::{.example} + ## `lib.trivial.toFunction` usage example - ```nix - nix-repl> lib.toFunction 1 2 - 1 + ```nix + nix-repl> lib.toFunction 1 2 + 1 - nix-repl> lib.toFunction (x: x + 1) 2 - 3 - ``` + nix-repl> lib.toFunction (x: x + 1) 2 + 3 + ``` - ::: + ::: */ toFunction = v: if isFunction v then v else k: v; - /* * - Convert the given positive integer to a string of its hexadecimal - representation. For example: + /* + * + Convert the given positive integer to a string of its hexadecimal + representation. For example: - toHexString 0 => "0" + toHexString 0 => "0" - toHexString 16 => "10" + toHexString 16 => "10" - toHexString 250 => "FA" + toHexString 250 => "FA" */ - toHexString = let - hexDigits = { - "10" = "A"; - "11" = "B"; - "12" = "C"; - "13" = "D"; - "14" = "E"; - "15" = "F"; - }; - toHexDigit = d: if d < 10 then toString d else hexDigits.${toString d}; - in i: lib.concatMapStrings toHexDigit (toBaseDigits 16 i); + toHexString = + let + hexDigits = { + "10" = "A"; + "11" = "B"; + "12" = "C"; + "13" = "D"; + "14" = "E"; + "15" = "F"; + }; + toHexDigit = d: if d < 10 then toString d else hexDigits.${toString d}; + in + i: lib.concatMapStrings toHexDigit (toBaseDigits 16 i); - /* * - `toBaseDigits base i` converts the positive integer i to a list of its - digits in the given base. For example: + /* + * + `toBaseDigits base i` converts the positive integer i to a list of its + digits in the given base. For example: - toBaseDigits 10 123 => [ 1 2 3 ] + toBaseDigits 10 123 => [ 1 2 3 ] - toBaseDigits 2 6 => [ 1 1 0 ] + toBaseDigits 2 6 => [ 1 1 0 ] - toBaseDigits 16 250 => [ 15 10 ] + toBaseDigits 16 250 => [ 15 10 ] - # Inputs + # Inputs - `base` + `base` - : 1\. Function argument + : 1\. Function argument - `i` + `i` - : 2\. Function argument + : 2\. Function argument */ - toBaseDigits = base: i: + toBaseDigits = + base: i: let - go = i: + go = + i: if i < base then [ i ] else let r = i - ((i / base) * base); q = (i - r) / base; - in [ r ] ++ go q; - in assert (isInt base); + in + [ r ] ++ go q; + in + assert (isInt base); assert (isInt i); assert (base >= 2); assert (i >= 0); diff --git a/nix/stdlib/types.nix b/nix/stdlib/types.nix index 0bf48a4..6568934 100644 --- a/nix/stdlib/types.nix +++ b/nix/stdlib/types.nix @@ -3,27 +3,69 @@ { lib }: let inherit (lib) - elem flip isAttrs isBool isDerivation isFloat isFunction isInt isList - isString isStorePath throwIf toDerivation toList; + elem + flip + isAttrs + isBool + isDerivation + isFloat + isFunction + isInt + isList + isString + isStorePath + throwIf + toDerivation + toList + ; inherit (lib.lists) - all concatLists count elemAt filter foldl' head imap1 last length tail; + all + concatLists + count + elemAt + filter + foldl' + head + imap1 + last + length + tail + ; inherit (lib.attrsets) - attrNames filterAttrs hasAttr mapAttrs optionalAttrs zipAttrsWith; + attrNames + filterAttrs + hasAttr + mapAttrs + optionalAttrs + zipAttrsWith + ; inherit (lib.options) - getFiles getValues mergeDefaultOption mergeEqualOption mergeOneOption - mergeUniqueOption showFiles showOption; + getFiles + getValues + mergeDefaultOption + mergeEqualOption + mergeOneOption + mergeUniqueOption + showFiles + showOption + ; inherit (lib.strings) - concatMapStringsSep concatStringsSep escapeNixString hasInfix isStringLike; + concatMapStringsSep + concatStringsSep + escapeNixString + hasInfix + isStringLike + ; inherit (lib.trivial) boolToString; inherit (lib.modules) mergeDefinitions fixupOptionType mergeOptionDecls; - inAttrPosSuffix = v: name: - let pos = builtins.unsafeGetAttrPos name v; - in if pos == null then - "" - else - " at ${pos.file}:${toString pos.line}:${toString pos.column}"; + inAttrPosSuffix = + v: name: + let + pos = builtins.unsafeGetAttrPos name v; + in + if pos == null then "" else " at ${pos.file}:${toString pos.line}:${toString pos.column}"; outer_types = rec { __attrsFailEvaluation = true; @@ -33,24 +75,23 @@ let # Default type merging function # takes two type functors and return the merged type - defaultTypeMerge = f: f': + defaultTypeMerge = + f: f': let wrapped = f.wrapped.typeMerge f'.wrapped.functor; payload = f.binOp f.payload f'.payload; - # cannot merge different types - in if f.name != f'.name then + in + # cannot merge different types + if f.name != f'.name then null - # simple types - else if (f.wrapped == null && f'.wrapped == null) - && (f.payload == null && f'.payload == null) then + # simple types + else if (f.wrapped == null && f'.wrapped == null) && (f.payload == null && f'.payload == null) then f.type - # composed types - else if (f.wrapped != null && f'.wrapped != null) - && (wrapped != null) then + # composed types + else if (f.wrapped != null && f'.wrapped != null) && (wrapped != null) then f.type wrapped - # value types - else if (f.payload != null && f'.payload != null) - && (payload != null) then + # value types + else if (f.payload != null && f'.payload != null) && (payload != null) then f.type payload else null; @@ -65,84 +106,98 @@ let }; isOptionType = isType "option-type"; - mkOptionType = { - # Human-readable representation of the type, should be equivalent to - # the type function name. - name, - # Description of the type, defined recursively by embedding the wrapped type if any. - description ? null, - # A hint for whether or not this description needs parentheses. Possible values: - # - "noun": a noun phrase - # Example description: "positive integer", - # - "conjunction": a phrase with a potentially ambiguous "or" connective - # Example description: "int or string" - # - "composite": a phrase with an "of" connective - # Example description: "list of string" - # - "nonRestrictiveClause": a noun followed by a comma and a clause - # Example description: "positive integer, meaning >0" - # See the `optionDescriptionPhrase` function. - descriptionClass ? null, - # DO NOT USE WITHOUT KNOWING WHAT YOU ARE DOING! - # Function applied to each definition that must return false when a definition - # does not match the type. It should not check more than the root of the value, - # because checking nested values reduces laziness, leading to unnecessary - # infinite recursions in the module system. - # Further checks of nested values should be performed by throwing in - # the merge function. - # Strict and deep type checking can be performed by calling lib.deepSeq on - # the merged value. - # - # See https://github.com/NixOS/nixpkgs/pull/6794 that introduced this change, - # https://github.com/NixOS/nixpkgs/pull/173568 and - # https://github.com/NixOS/nixpkgs/pull/168295 that attempted to revert this, - # https://github.com/NixOS/nixpkgs/issues/191124 and - # https://github.com/NixOS/nixos-search/issues/391 for what happens if you ignore - # this disclaimer. - check ? (x: true), - # Merge a list of definitions together into a single value. - # This function is called with two arguments: the location of - # the option in the configuration as a list of strings - # (e.g. ["boot" "loader "grub" "enable"]), and a list of - # definition values and locations (e.g. [ { file = "/foo.nix"; - # value = 1; } { file = "/bar.nix"; value = 2 } ]). - merge ? mergeDefaultOption, - # Whether this type has a value representing nothingness. If it does, - # this should be a value of the form { value = ; } - # If it doesn't, this should be {} - # This may be used when a value is required for `mkIf false`. This allows the extra laziness in e.g. `lazyAttrsOf`. - emptyValue ? { }, - # Return a flat attrset of sub-options. Used to generate - # documentation. - getSubOptions ? prefix: { }, - # List of modules if any, or null if none. - getSubModules ? null, - # Function for building the same option type with a different list of - # modules. - substSubModules ? m: null, - # Function that merge type declarations. - # internal, takes a functor as argument and returns the merged type. - # returning null means the type is not mergeable - typeMerge ? defaultTypeMerge functor, - # The type functor. - # internal, representation of the type as an attribute set. - # name: name of the type - # type: type function. - # wrapped: the type wrapped in case of compound types. - # payload: values of the type, two payloads of the same type must be - # combinable with the binOp binary operation. - # binOp: binary operation that merge two payloads of the same type. - functor ? defaultFunctor name, - # The deprecation message to display when this type is used by an option - # If null, the type isn't deprecated - deprecationMessage ? null, - # The types that occur in the definition of this type. This is used to - # issue deprecation warnings recursively. Can also be used to reuse - # nested types - nestedTypes ? { }, }: { + mkOptionType = + { + # Human-readable representation of the type, should be equivalent to + # the type function name. + name, + # Description of the type, defined recursively by embedding the wrapped type if any. + description ? null, + # A hint for whether or not this description needs parentheses. Possible values: + # - "noun": a noun phrase + # Example description: "positive integer", + # - "conjunction": a phrase with a potentially ambiguous "or" connective + # Example description: "int or string" + # - "composite": a phrase with an "of" connective + # Example description: "list of string" + # - "nonRestrictiveClause": a noun followed by a comma and a clause + # Example description: "positive integer, meaning >0" + # See the `optionDescriptionPhrase` function. + descriptionClass ? null, + # DO NOT USE WITHOUT KNOWING WHAT YOU ARE DOING! + # Function applied to each definition that must return false when a definition + # does not match the type. It should not check more than the root of the value, + # because checking nested values reduces laziness, leading to unnecessary + # infinite recursions in the module system. + # Further checks of nested values should be performed by throwing in + # the merge function. + # Strict and deep type checking can be performed by calling lib.deepSeq on + # the merged value. + # + # See https://github.com/NixOS/nixpkgs/pull/6794 that introduced this change, + # https://github.com/NixOS/nixpkgs/pull/173568 and + # https://github.com/NixOS/nixpkgs/pull/168295 that attempted to revert this, + # https://github.com/NixOS/nixpkgs/issues/191124 and + # https://github.com/NixOS/nixos-search/issues/391 for what happens if you ignore + # this disclaimer. + check ? (x: true), + # Merge a list of definitions together into a single value. + # This function is called with two arguments: the location of + # the option in the configuration as a list of strings + # (e.g. ["boot" "loader "grub" "enable"]), and a list of + # definition values and locations (e.g. [ { file = "/foo.nix"; + # value = 1; } { file = "/bar.nix"; value = 2 } ]). + merge ? mergeDefaultOption, + # Whether this type has a value representing nothingness. If it does, + # this should be a value of the form { value = ; } + # If it doesn't, this should be {} + # This may be used when a value is required for `mkIf false`. This allows the extra laziness in e.g. `lazyAttrsOf`. + emptyValue ? { }, + # Return a flat attrset of sub-options. Used to generate + # documentation. + getSubOptions ? prefix: { }, + # List of modules if any, or null if none. + getSubModules ? null, + # Function for building the same option type with a different list of + # modules. + substSubModules ? m: null, + # Function that merge type declarations. + # internal, takes a functor as argument and returns the merged type. + # returning null means the type is not mergeable + typeMerge ? defaultTypeMerge functor, + # The type functor. + # internal, representation of the type as an attribute set. + # name: name of the type + # type: type function. + # wrapped: the type wrapped in case of compound types. + # payload: values of the type, two payloads of the same type must be + # combinable with the binOp binary operation. + # binOp: binary operation that merge two payloads of the same type. + functor ? defaultFunctor name, + # The deprecation message to display when this type is used by an option + # If null, the type isn't deprecated + deprecationMessage ? null, + # The types that occur in the definition of this type. This is used to + # issue deprecation warnings recursively. Can also be used to reuse + # nested types + nestedTypes ? { }, + }: + { _type = "option-type"; - inherit name check merge emptyValue getSubOptions getSubModules - substSubModules typeMerge functor deprecationMessage nestedTypes - descriptionClass; + inherit + name + check + merge + emptyValue + getSubOptions + getSubModules + substSubModules + typeMerge + functor + deprecationMessage + nestedTypes + descriptionClass + ; description = if description == null then name else description; }; @@ -166,11 +221,9 @@ let # # The description of the `optionType`, with parentheses if there may be an # ambiguity. - optionDescriptionPhrase = unparenthesize: t: - if unparenthesize (t.descriptionClass or null) then - t.description - else - "(${t.description})"; + optionDescriptionPhrase = + unparenthesize: t: + if unparenthesize (t.descriptionClass or null) then t.description else "(${t.description})"; # When adding new types don't forget to document them in # nixos/doc/manual/development/option-types.xml! @@ -188,49 +241,48 @@ let description = "anything"; descriptionClass = "noun"; check = value: true; - merge = loc: defs: + merge = + loc: defs: let - getType = value: - if isAttrs value && isStringLike value then - "stringCoercibleSet" - else - builtins.typeOf value; + getType = + value: if isAttrs value && isStringLike value then "stringCoercibleSet" else builtins.typeOf value; # Returns the common type of all definitions, throws an error if they # don't have the same type - commonType = foldl' (type: def: + commonType = foldl' ( + type: def: if getType def.value == type then type else - throw "The option `${ - showOption loc - }' has conflicting option types in ${ - showFiles (getFiles defs) - }") (getType (head defs).value) defs; - - mergeFunction = { - # Recursively merge attribute sets - set = (attrsOf anything).merge; - # Safe and deterministic behavior for lists is to only accept one definition - # listOf only used to apply mkIf and co. - list = if length defs > 1 then - throw "The option `${ - showOption loc - }' has conflicting definitions, in ${ - showFiles (getFiles defs) - }." - else - (listOf anything).merge; - # This is the type of packages, only accept a single definition - stringCoercibleSet = mergeOneOption; - lambda = loc: defs: arg: - anything.merge (loc ++ [ "" ]) (map (def: { - file = def.file; - value = def.value arg; - }) defs); - # Otherwise fall back to only allowing all equal definitions - }.${commonType} or mergeEqualOption; - in mergeFunction loc defs; + throw "The option `${showOption loc}' has conflicting option types in ${showFiles (getFiles defs)}" + ) (getType (head defs).value) defs; + + mergeFunction = + { + # Recursively merge attribute sets + set = (attrsOf anything).merge; + # Safe and deterministic behavior for lists is to only accept one definition + # listOf only used to apply mkIf and co. + list = + if length defs > 1 then + throw "The option `${showOption loc}' has conflicting definitions, in ${showFiles (getFiles defs)}." + else + (listOf anything).merge; + # This is the type of packages, only accept a single definition + stringCoercibleSet = mergeOneOption; + lambda = + loc: defs: arg: + anything.merge (loc ++ [ "" ]) ( + map (def: { + file = def.file; + value = def.value arg; + }) defs + ); + # Otherwise fall back to only allowing all equal definitions + } + .${commonType} or mergeEqualOption; + in + mergeFunction loc defs; }; unspecified = mkOptionType { @@ -252,11 +304,14 @@ let description = "boolean (merged using or)"; descriptionClass = "noun"; check = isBool; - merge = loc: defs: - foldl' (result: def: + merge = + loc: defs: + foldl' ( + result: def: # Under the assumption that .check always runs before merge, we can assume that all defs.*.value # have been forced, and therefore we assume we don't introduce order-dependent strictness here - result || def.value) false defs; + result || def.value + ) false defs; }; int = mkOptionType { @@ -268,70 +323,76 @@ let }; # Specialized subdomains of int - ints = let - betweenDesc = lowest: highest: - "${toString lowest} and ${toString highest} (both inclusive)"; - between = lowest: highest: - assert lib.assertMsg (lowest <= highest) - "ints.between: lowest must be smaller than highest"; - addCheck int (x: x >= lowest && x <= highest) // { - name = "intBetween"; - description = "integer between ${betweenDesc lowest highest}"; + ints = + let + betweenDesc = lowest: highest: "${toString lowest} and ${toString highest} (both inclusive)"; + between = + lowest: highest: + assert lib.assertMsg (lowest <= highest) "ints.between: lowest must be smaller than highest"; + addCheck int (x: x >= lowest && x <= highest) + // { + name = "intBetween"; + description = "integer between ${betweenDesc lowest highest}"; + }; + ign = + lowest: highest: name: docStart: + between lowest highest + // { + inherit name; + description = docStart + "; between ${betweenDesc lowest highest}"; + }; + unsign = + bit: range: ign 0 (range - 1) "unsignedInt${toString bit}" "${toString bit} bit unsigned integer"; + sign = + bit: range: + ign (0 - (range / 2)) ( + range / 2 - 1 + ) "signedInt${toString bit}" "${toString bit} bit signed integer"; + in + { + # TODO: Deduplicate with docs in nixos/doc/manual/development/option-types.section.md + /* + * + An int with a fixed range. + + # Example + :::{.example} + ## `lib.types.ints.between` usage example + + ```nix + (ints.between 0 100).check (-1) + => false + (ints.between 0 100).check (101) + => false + (ints.between 0 0).check 0 + => true + ``` + + ::: + */ + inherit between; + + unsigned = addCheck types.int (x: x >= 0) // { + name = "unsignedInt"; + description = "unsigned integer, meaning >=0"; + descriptionClass = "nonRestrictiveClause"; }; - ign = lowest: highest: name: docStart: - between lowest highest // { - inherit name; - description = docStart + "; between ${betweenDesc lowest highest}"; + positive = addCheck types.int (x: x > 0) // { + name = "positiveInt"; + description = "positive integer, meaning >0"; + descriptionClass = "nonRestrictiveClause"; }; - unsign = bit: range: - ign 0 (range - 1) "unsignedInt${toString bit}" - "${toString bit} bit unsigned integer"; - sign = bit: range: - ign (0 - (range / 2)) (range / 2 - 1) "signedInt${toString bit}" - "${toString bit} bit signed integer"; - in { - # TODO: Deduplicate with docs in nixos/doc/manual/development/option-types.section.md - /* * - An int with a fixed range. - - # Example - :::{.example} - ## `lib.types.ints.between` usage example - - ```nix - (ints.between 0 100).check (-1) - => false - (ints.between 0 100).check (101) - => false - (ints.between 0 0).check 0 - => true - ``` - - ::: - */ - inherit between; - - unsigned = addCheck types.int (x: x >= 0) // { - name = "unsignedInt"; - description = "unsigned integer, meaning >=0"; - descriptionClass = "nonRestrictiveClause"; - }; - positive = addCheck types.int (x: x > 0) // { - name = "positiveInt"; - description = "positive integer, meaning >0"; - descriptionClass = "nonRestrictiveClause"; + u8 = unsign 8 256; + u16 = unsign 16 65536; + # the biggest int Nix accepts is 2^63 - 1 (9223372036854775808) + # the smallest int Nix accepts is -2^63 (-9223372036854775807) + u32 = unsign 32 4294967296; + # u64 = unsign 64 18446744073709551616; + + s8 = sign 8 256; + s16 = sign 16 65536; + s32 = sign 32 4294967296; }; - u8 = unsign 8 256; - u16 = unsign 16 65536; - # the biggest int Nix accepts is 2^63 - 1 (9223372036854775808) - # the smallest int Nix accepts is -2^63 (-9223372036854775807) - u32 = unsign 32 4294967296; - # u64 = unsign 64 18446744073709551616; - - s8 = sign 8 256; - s16 = sign 16 65536; - s32 = sign 32 4294967296; - }; # Alias of u16 for a port number port = ints.u16; @@ -346,34 +407,32 @@ let number = either int float; - numbers = let - betweenDesc = lowest: highest: - "${builtins.toJSON lowest} and ${ - builtins.toJSON highest - } (both inclusive)"; - in { - between = lowest: highest: - assert lib.assertMsg (lowest <= highest) - "numbers.between: lowest must be smaller than highest"; - addCheck number (x: x >= lowest && x <= highest) // { - name = "numberBetween"; - description = "integer or floating point number between ${ - betweenDesc lowest highest - }"; - }; + numbers = + let + betweenDesc = + lowest: highest: "${builtins.toJSON lowest} and ${builtins.toJSON highest} (both inclusive)"; + in + { + between = + lowest: highest: + assert lib.assertMsg (lowest <= highest) "numbers.between: lowest must be smaller than highest"; + addCheck number (x: x >= lowest && x <= highest) + // { + name = "numberBetween"; + description = "integer or floating point number between ${betweenDesc lowest highest}"; + }; - nonnegative = addCheck number (x: x >= 0) // { - name = "numberNonnegative"; - description = - "nonnegative integer or floating point number, meaning >=0"; - descriptionClass = "nonRestrictiveClause"; - }; - positive = addCheck number (x: x > 0) // { - name = "numberPositive"; - description = "positive integer or floating point number, meaning >0"; - descriptionClass = "nonRestrictiveClause"; + nonnegative = addCheck number (x: x >= 0) // { + name = "numberNonnegative"; + description = "nonnegative integer or floating point number, meaning >=0"; + descriptionClass = "nonRestrictiveClause"; + }; + positive = addCheck number (x: x > 0) // { + name = "numberPositive"; + description = "positive integer or floating point number, meaning >0"; + descriptionClass = "nonRestrictiveClause"; + }; }; - }; str = mkOptionType { name = "str"; @@ -387,29 +446,38 @@ let name = "nonEmptyStr"; description = "non-empty string"; descriptionClass = "noun"; - check = x: - str.check x && builtins.match '' - [ - ]*'' x == null; + check = + x: + str.check x + && + builtins.match '' + [ + ]*'' x == null; inherit (str) merge; }; # Allow a newline character at the end and trim it in the merge function. - singleLineStr = let - inherit (strMatching '' - [^ - ]* - ?'') - check merge; - in mkOptionType { - name = "singleLineStr"; - description = "(optionally newline-terminated) single-line string"; - descriptionClass = "noun"; - inherit check; - merge = loc: defs: lib.removeSuffix "\n" (merge loc defs); - }; + singleLineStr = + let + inherit + (strMatching '' + [^ + ]* + ?'') + check + merge + ; + in + mkOptionType { + name = "singleLineStr"; + description = "(optionally newline-terminated) single-line string"; + descriptionClass = "noun"; + inherit check; + merge = loc: defs: lib.removeSuffix "\n" (merge loc defs); + }; - strMatching = pattern: + strMatching = + pattern: mkOptionType { name = "strMatching ${escapeNixString pattern}"; description = "string matching the pattern ${pattern}"; @@ -420,13 +488,15 @@ let # Merge multiple definitions by concatenating them (with the given # separator between the values). - separatedString = sep: + separatedString = + sep: mkOptionType rec { name = "separatedString"; - description = if sep == "" then - "Concatenated string" # for types.string. - else - "strings concatenated with ${builtins.toJSON sep}"; + description = + if sep == "" then + "Concatenated string" # for types.string. + else + "strings concatenated with ${builtins.toJSON sep}"; descriptionClass = "noun"; check = isString; merge = loc: defs: concatStringsSep sep (getValues defs); @@ -443,16 +513,19 @@ let # Deprecated; should not be used because it quietly concatenates # strings, which is usually not what you want. # We use a lib.warn because `deprecationMessage` doesn't trigger in nested types such as `attrsOf string` - string = lib.warn - "The type `types.string` is deprecated. See https://github.com/NixOS/nixpkgs/pull/66346 for better alternative types." - (separatedString "" // { name = "string"; }); - - passwdEntry = entryType: - addCheck entryType (str: !(hasInfix ":" str || hasInfix "\n" str)) // { + string = + lib.warn + "The type `types.string` is deprecated. See https://github.com/NixOS/nixpkgs/pull/66346 for better alternative types." + (separatedString "" // { name = "string"; }); + + passwdEntry = + entryType: + addCheck entryType (str: !(hasInfix ":" str || hasInfix "\n" str)) + // { name = "passwdEntry ${entryType.name}"; description = "${ - optionDescriptionPhrase (class: class == "noun") entryType - }, not containing newlines or colons"; + optionDescriptionPhrase (class: class == "noun") entryType + }, not containing newlines or colons"; descriptionClass = "nonRestrictiveClause"; }; @@ -461,7 +534,9 @@ let description = "attribute set"; check = isAttrs; merge = loc: foldl' (res: def: res // def.value) { }; - emptyValue = { value = { }; }; + emptyValue = { + value = { }; + }; }; # A package is a top-level store path (/nix/store/hash-name). This includes: @@ -476,10 +551,12 @@ let name = "package"; descriptionClass = "noun"; check = x: isDerivation x || isStorePath x; - merge = loc: defs: - let res = mergeOneOption loc defs; - in if builtins.isPath res - || (builtins.isString res && !builtins.hasContext res) then + merge = + loc: defs: + let + res = mergeOneOption loc defs; + in + if builtins.isPath res || (builtins.isString res && !builtins.hasContext res) then toDerivation res else res; @@ -489,13 +566,14 @@ let check = x: isDerivation x && hasAttr "shellPath" x; }; - pkgs = addCheck (unique { - message = "A Nixpkgs pkgs set can not be merged with another pkgs set."; - } attrs // { - name = "pkgs"; - descriptionClass = "noun"; - description = "Nixpkgs package set"; - }) (x: (x._type or null) == "pkgs"); + pkgs = addCheck ( + unique { message = "A Nixpkgs pkgs set can not be merged with another pkgs set."; } attrs + // { + name = "pkgs"; + descriptionClass = "noun"; + description = "Nixpkgs package set"; + } + ) (x: (x._type or null) == "pkgs"); path = mkOptionType { name = "path"; @@ -508,74 +586,98 @@ let name = "pathInStore"; description = "path in the Nix store"; descriptionClass = "noun"; - check = x: - isStringLike x - && builtins.match "${builtins.storeDir}/[^.].*" (toString x) != null; + check = x: isStringLike x && builtins.match "${builtins.storeDir}/[^.].*" (toString x) != null; merge = mergeEqualOption; }; - listOf = elemType: + listOf = + elemType: mkOptionType rec { name = "listOf"; description = "list of ${ - optionDescriptionPhrase - (class: class == "noun" || class == "composite") elemType - }"; + optionDescriptionPhrase (class: class == "noun" || class == "composite") elemType + }"; descriptionClass = "composite"; check = isList; - merge = loc: defs: - map (x: x.value) (filter (x: x ? value) (concatLists (imap1 (n: def: - imap1 (m: def': - (mergeDefinitions - (loc ++ [ "[definition ${toString n}-entry ${toString m}]" ]) - elemType [{ - inherit (def) file; - value = def'; - }]).optionalValue) def.value) defs))); - emptyValue = { value = [ ]; }; + merge = + loc: defs: + map (x: x.value) ( + filter (x: x ? value) ( + concatLists ( + imap1 ( + n: def: + imap1 ( + m: def': + (mergeDefinitions (loc ++ [ "[definition ${toString n}-entry ${toString m}]" ]) elemType [ + { + inherit (def) file; + value = def'; + } + ]).optionalValue + ) def.value + ) defs + ) + ) + ); + emptyValue = { + value = [ ]; + }; getSubOptions = prefix: elemType.getSubOptions (prefix ++ [ "*" ]); getSubModules = elemType.getSubModules; substSubModules = m: listOf (elemType.substSubModules m); - functor = (defaultFunctor name) // { wrapped = elemType; }; + functor = (defaultFunctor name) // { + wrapped = elemType; + }; nestedTypes.elemType = elemType; }; - nonEmptyListOf = elemType: - let list = addCheck (types.listOf elemType) (l: l != [ ]); - in list // { - description = "non-empty ${ - optionDescriptionPhrase (class: class == "noun") list - }"; + nonEmptyListOf = + elemType: + let + list = addCheck (types.listOf elemType) (l: l != [ ]); + in + list + // { + description = "non-empty ${optionDescriptionPhrase (class: class == "noun") list}"; emptyValue = { }; # no .value attr, meaning unset substSubModules = m: nonEmptyListOf (elemType.substSubModules m); }; - attrsOf = elemType: + attrsOf = + elemType: mkOptionType rec { name = "attrsOf"; description = "attribute set of ${ - optionDescriptionPhrase - (class: class == "noun" || class == "composite") elemType - }"; + optionDescriptionPhrase (class: class == "noun" || class == "composite") elemType + }"; descriptionClass = "composite"; check = isAttrs; - merge = loc: defs: - mapAttrs (n: v: v.value) (filterAttrs (n: v: v ? value) - (zipAttrsWith (name: defs: - (mergeDefinitions (loc ++ [ name ]) elemType - defs).optionalValue) - # Push down position info. - (map (def: - mapAttrs (n: v: { - inherit (def) file; - value = v; - }) def.value) defs))); - emptyValue = { value = { }; }; - getSubOptions = prefix: - elemType.getSubOptions (prefix ++ [ "" ]); + merge = + loc: defs: + mapAttrs (n: v: v.value) ( + filterAttrs (n: v: v ? value) ( + zipAttrsWith (name: defs: (mergeDefinitions (loc ++ [ name ]) elemType defs).optionalValue) + # Push down position info. + ( + map ( + def: + mapAttrs (n: v: { + inherit (def) file; + value = v; + }) def.value + ) defs + ) + ) + ); + emptyValue = { + value = { }; + }; + getSubOptions = prefix: elemType.getSubOptions (prefix ++ [ "" ]); getSubModules = elemType.getSubModules; substSubModules = m: attrsOf (elemType.substSubModules m); - functor = (defaultFunctor name) // { wrapped = elemType; }; + functor = (defaultFunctor name) // { + wrapped = elemType; + }; nestedTypes.elemType = elemType; }; @@ -584,148 +686,181 @@ let # `foo.attr = mkIf false 10`, then `foo ? attr == true`, whereas with # attrsOf it would correctly be `false`. Accessing `foo.attr` would throw an # error that it's not defined. Use only if conditional definitions don't make sense. - lazyAttrsOf = elemType: + lazyAttrsOf = + elemType: mkOptionType rec { name = "lazyAttrsOf"; description = "lazy attribute set of ${ - optionDescriptionPhrase - (class: class == "noun" || class == "composite") elemType - }"; + optionDescriptionPhrase (class: class == "noun" || class == "composite") elemType + }"; descriptionClass = "composite"; check = isAttrs; - merge = loc: defs: - zipAttrsWith (name: defs: - let - merged = mergeDefinitions (loc ++ [ name ]) elemType defs; + merge = + loc: defs: + zipAttrsWith + ( + name: defs: + let + merged = mergeDefinitions (loc ++ [ name ]) elemType defs; + in # mergedValue will trigger an appropriate error when accessed - in merged.optionalValue.value or elemType.emptyValue.value or merged.mergedValue) - # Push down position info. - (map (def: - mapAttrs (n: v: { - inherit (def) file; - value = v; - }) def.value) defs); - emptyValue = { value = { }; }; - getSubOptions = prefix: - elemType.getSubOptions (prefix ++ [ "" ]); + merged.optionalValue.value or elemType.emptyValue.value or merged.mergedValue + ) + # Push down position info. + ( + map ( + def: + mapAttrs (n: v: { + inherit (def) file; + value = v; + }) def.value + ) defs + ); + emptyValue = { + value = { }; + }; + getSubOptions = prefix: elemType.getSubOptions (prefix ++ [ "" ]); getSubModules = elemType.getSubModules; substSubModules = m: lazyAttrsOf (elemType.substSubModules m); - functor = (defaultFunctor name) // { wrapped = elemType; }; + functor = (defaultFunctor name) // { + wrapped = elemType; + }; nestedTypes.elemType = elemType; }; # TODO: deprecate this in the future: - loaOf = elemType: - types.attrsOf elemType // { + loaOf = + elemType: + types.attrsOf elemType + // { name = "loaOf"; - deprecationMessage = "Mixing lists with attribute values is no longer" + deprecationMessage = + "Mixing lists with attribute values is no longer" + " possible; please use `types.attrsOf` instead. See" + " https://github.com/NixOS/nixpkgs/issues/1800 for the motivation."; nestedTypes.elemType = elemType; }; - attrTag = tags: - let tags_ = tags; - in let - tags = mapAttrs (n: opt: - builtins.addErrorContext "while checking that attrTag tag ${ - lib.strings.escapeNixIdentifier n - } is an option with a type${inAttrPosSuffix tags_ n}" - (throwIf (opt._type or null != "option") - "In attrTag, each tag value must be an option, but tag ${ - lib.strings.escapeNixIdentifier n - } ${ - if opt ? _type then - if opt._type == "option-type" then - "was a bare type, not wrapped in mkOption." - else - "was of type ${lib.strings.escapeNixString opt._type}." - else - "was not." - }" opt // { - declarations = opt.declarations or (let - pos = builtins.unsafeGetAttrPos n tags_; - in if pos == null then [ ] else [ pos.file ]); - declarationPositions = opt.declarationPositions or (let - pos = builtins.unsafeGetAttrPos n tags_; - in if pos == null then [ ] else [ pos ]); - })) tags_; - choicesStr = concatMapStringsSep ", " lib.strings.escapeNixIdentifier - (attrNames tags); - in mkOptionType { + attrTag = + tags: + let + tags_ = tags; + in + let + tags = mapAttrs ( + n: opt: + builtins.addErrorContext + "while checking that attrTag tag ${lib.strings.escapeNixIdentifier n} is an option with a type${inAttrPosSuffix tags_ n}" + ( + throwIf (opt._type or null != "option") + "In attrTag, each tag value must be an option, but tag ${lib.strings.escapeNixIdentifier n} ${ + if opt ? _type then + if opt._type == "option-type" then + "was a bare type, not wrapped in mkOption." + else + "was of type ${lib.strings.escapeNixString opt._type}." + else + "was not." + }" + opt + // { + declarations = + opt.declarations or ( + let + pos = builtins.unsafeGetAttrPos n tags_; + in + if pos == null then [ ] else [ pos.file ] + ); + declarationPositions = + opt.declarationPositions or ( + let + pos = builtins.unsafeGetAttrPos n tags_; + in + if pos == null then [ ] else [ pos ] + ); + } + ) + ) tags_; + choicesStr = concatMapStringsSep ", " lib.strings.escapeNixIdentifier (attrNames tags); + in + mkOptionType { name = "attrTag"; description = "attribute-tagged union"; descriptionClass = "noun"; - getSubOptions = prefix: + getSubOptions = + prefix: mapAttrs (tagName: tagOption: { "${lib.showOption prefix}" = tagOption // { loc = prefix ++ [ tagName ]; }; }) tags; - check = v: - isAttrs v && length (attrNames v) == 1 && tags - ? ${head (attrNames v)}; - merge = loc: defs: + check = v: isAttrs v && length (attrNames v) == 1 && tags ? ${head (attrNames v)}; + merge = + loc: defs: let choice = head (attrNames (head defs).value); - checkedValueDefs = map (def: + checkedValueDefs = map ( + def: assert (length (attrNames def.value)) == 1; if (head (attrNames def.value)) != choice then - throw "The option `${ - showOption loc - }` is defined both as `${choice}` and `${ - head (attrNames def.value) - }`, in ${showFiles (getFiles defs)}." - else { - inherit (def) file; - value = def.value.${choice}; - }) defs; - in if tags ? ${choice} then { - ${choice} = - (lib.modules.evalOptionValue (loc ++ [ choice ]) tags.${choice} - checkedValueDefs).value; - } else - throw "The option `${showOption loc}` is defined as ${ - lib.strings.escapeNixIdentifier choice - }, but ${ - lib.strings.escapeNixIdentifier choice - } is not among the valid choices (${choicesStr}). Value ${choice} was defined in ${ - showFiles (getFiles defs) - }."; + throw "The option `${showOption loc}` is defined both as `${choice}` and `${head (attrNames def.value)}`, in ${showFiles (getFiles defs)}." + else + { + inherit (def) file; + value = def.value.${choice}; + } + ) defs; + in + if tags ? ${choice} then + { + ${choice} = (lib.modules.evalOptionValue (loc ++ [ choice ]) tags.${choice} checkedValueDefs).value; + } + else + throw "The option `${showOption loc}` is defined as ${lib.strings.escapeNixIdentifier choice}, but ${lib.strings.escapeNixIdentifier choice} is not among the valid choices (${choicesStr}). Value ${choice} was defined in ${showFiles (getFiles defs)}."; nestedTypes = tags; functor = defaultFunctor "attrTag" // { type = { tags, ... }: types.attrTag tags; - payload = { inherit tags; }; - binOp = let - # Add metadata in the format that submodules work with - wrapOptionDecl = option: { - options = option; - _file = ""; - pos = null; - }; - in a: b: { - tags = a.tags // b.tags // mapAttrs (tagName: bOpt: - lib.mergeOptionDecls - # FIXME: loc is not accurate; should include prefix - # Fortunately, it's only used for error messages, where a "relative" location is kinda ok. - # It is also returned though, but use of the attribute seems rare? - [ tagName ] [ - (wrapOptionDecl a.tags.${tagName}) - (wrapOptionDecl bOpt) - ] // { - # mergeOptionDecls is not idempotent in these attrs: - declarations = a.tags.${tagName}.declarations - ++ bOpt.declarations; - declarationPositions = a.tags.${tagName}.declarationPositions - ++ bOpt.declarationPositions; - }) (builtins.intersectAttrs a.tags b.tags); + payload = { + inherit tags; }; + binOp = + let + # Add metadata in the format that submodules work with + wrapOptionDecl = option: { + options = option; + _file = ""; + pos = null; + }; + in + a: b: { + tags = + a.tags + // b.tags + // mapAttrs ( + tagName: bOpt: + lib.mergeOptionDecls + # FIXME: loc is not accurate; should include prefix + # Fortunately, it's only used for error messages, where a "relative" location is kinda ok. + # It is also returned though, but use of the attribute seems rare? + [ tagName ] + [ + (wrapOptionDecl a.tags.${tagName}) + (wrapOptionDecl bOpt) + ] + // { + # mergeOptionDecls is not idempotent in these attrs: + declarations = a.tags.${tagName}.declarations ++ bOpt.declarations; + declarationPositions = a.tags.${tagName}.declarationPositions ++ bOpt.declarationPositions; + } + ) (builtins.intersectAttrs a.tags b.tags); + }; }; }; uniq = unique { message = ""; }; - unique = { message }: + unique = + { message }: type: mkOptionType rec { name = "unique"; @@ -738,65 +873,74 @@ let getSubOptions = type.getSubOptions; getSubModules = type.getSubModules; substSubModules = m: uniq (type.substSubModules m); - functor = (defaultFunctor name) // { wrapped = type; }; + functor = (defaultFunctor name) // { + wrapped = type; + }; nestedTypes.elemType = type; }; # Null or value of ... - nullOr = elemType: + nullOr = + elemType: mkOptionType rec { name = "nullOr"; description = "null or ${ - optionDescriptionPhrase - (class: class == "noun" || class == "conjunction") elemType - }"; + optionDescriptionPhrase (class: class == "noun" || class == "conjunction") elemType + }"; descriptionClass = "conjunction"; check = x: x == null || elemType.check x; - merge = loc: defs: - let nrNulls = count (def: def.value == null) defs; - in if nrNulls == length defs then + merge = + loc: defs: + let + nrNulls = count (def: def.value == null) defs; + in + if nrNulls == length defs then null else if nrNulls != 0 then - throw "The option `${ - showOption loc - }` is defined both null and not null, in ${ - showFiles (getFiles defs) - }." + throw "The option `${showOption loc}` is defined both null and not null, in ${showFiles (getFiles defs)}." else elemType.merge loc defs; - emptyValue = { value = null; }; + emptyValue = { + value = null; + }; getSubOptions = elemType.getSubOptions; getSubModules = elemType.getSubModules; substSubModules = m: nullOr (elemType.substSubModules m); - functor = (defaultFunctor name) // { wrapped = elemType; }; + functor = (defaultFunctor name) // { + wrapped = elemType; + }; nestedTypes.elemType = elemType; }; - functionTo = elemType: + functionTo = + elemType: mkOptionType { name = "functionTo"; description = "function that evaluates to a(n) ${ - optionDescriptionPhrase - (class: class == "noun" || class == "composite") elemType - }"; + optionDescriptionPhrase (class: class == "noun" || class == "composite") elemType + }"; descriptionClass = "composite"; check = isFunction; - merge = loc: defs: fnArgs: - (mergeDefinitions (loc ++ [ "" ]) elemType (map - (fn: { + merge = + loc: defs: fnArgs: + (mergeDefinitions (loc ++ [ "" ]) elemType ( + map (fn: { inherit (fn) file; value = fn.value fnArgs; - }) defs)).mergedValue; - getSubOptions = prefix: - elemType.getSubOptions (prefix ++ [ "" ]); + }) defs + )).mergedValue; + getSubOptions = prefix: elemType.getSubOptions (prefix ++ [ "" ]); getSubModules = elemType.getSubModules; substSubModules = m: functionTo (elemType.substSubModules m); - functor = (defaultFunctor "functionTo") // { wrapped = elemType; }; + functor = (defaultFunctor "functionTo") // { + wrapped = elemType; + }; nestedTypes.elemType = elemType; }; # A submodule (like typed attribute set). See NixOS manual. - submodule = modules: + submodule = + modules: submoduleWith { shorthandOnlyDefinesConfig = true; modules = toList modules; @@ -808,27 +952,30 @@ let # A module to be imported in some other part of the configuration. # `staticModules`' options will be added to the documentation, unlike # options declared via `config`. - deferredModuleWith = attrs@{ staticModules ? [ ] }: + deferredModuleWith = + attrs@{ + staticModules ? [ ], + }: mkOptionType { name = "deferredModule"; description = "module"; descriptionClass = "noun"; check = x: isAttrs x || isFunction x || path.check x; merge = loc: defs: { - imports = staticModules ++ map (def: - lib.setDefaultModuleLocation - "${def.file}, via option ${showOption loc}" def.value) defs; + imports = + staticModules + ++ map ( + def: lib.setDefaultModuleLocation "${def.file}, via option ${showOption loc}" def.value + ) defs; }; - inherit (submoduleWith { modules = staticModules; }) - getSubOptions getSubModules; - substSubModules = m: - deferredModuleWith (attrs // { staticModules = m; }); + inherit (submoduleWith { modules = staticModules; }) getSubOptions getSubModules; + substSubModules = m: deferredModuleWith (attrs // { staticModules = m; }); functor = defaultFunctor "deferredModuleWith" // { type = types.deferredModuleWith; - payload = { inherit staticModules; }; - binOp = lhs: rhs: { - staticModules = lhs.staticModules ++ rhs.staticModules; + payload = { + inherit staticModules; }; + binOp = lhs: rhs: { staticModules = lhs.staticModules ++ rhs.staticModules; }; }; }; @@ -838,81 +985,100 @@ let description = "optionType"; descriptionClass = "noun"; check = value: value._type or null == "option-type"; - merge = loc: defs: + merge = + loc: defs: if length defs == 1 then (head defs).value else let # Prepares the type definitions for mergeOptionDecls, which # annotates submodules types with file locations - optionModules = map ({ value, file, }: { - _file = file; - # There's no way to merge types directly from the module system, - # but we can cheat a bit by just declaring an option with the type - options = lib.mkOption { type = value; }; - }) defs; + optionModules = map ( + { value, file }: + { + _file = file; + # There's no way to merge types directly from the module system, + # but we can cheat a bit by just declaring an option with the type + options = lib.mkOption { type = value; }; + } + ) defs; # Merges all the types into a single one, including submodule merging. # This also propagates file information to all submodules - mergedOption = - fixupOptionType loc (mergeOptionDecls loc optionModules); - in mergedOption.type; + mergedOption = fixupOptionType loc (mergeOptionDecls loc optionModules); + in + mergedOption.type; }; - submoduleWith = { modules, specialArgs ? { } - , shorthandOnlyDefinesConfig ? false, description ? null, class ? null, + submoduleWith = + { + modules, + specialArgs ? { }, + shorthandOnlyDefinesConfig ? false, + description ? null, + class ? null, }@attrs: let inherit (lib.modules) evalModules; - allModules = defs: - map ({ value, file, }: - if isAttrs value && shorthandOnlyDefinesConfig then { - _file = file; - config = value; - } else { - _file = file; - imports = [ value ]; - }) defs; + allModules = + defs: + map ( + { value, file }: + if isAttrs value && shorthandOnlyDefinesConfig then + { + _file = file; + config = value; + } + else + { + _file = file; + imports = [ value ]; + } + ) defs; base = evalModules { inherit class specialArgs; - modules = [{ - # This is a work-around for the fact that some sub-modules, - # such as the one included in an attribute set, expects an "args" - # attribute to be given to the sub-module. As the option - # evaluation does not have any specific attribute name yet, we - # provide a default for the documentation and the freeform type. - # - # This is necessary as some option declaration might use the - # "name" attribute given as argument of the submodule and use it - # as the default of option declarations. - # - # We use lookalike unicode single angle quotation marks because - # of the docbook transformation the options receive. In all uses - # > and < wouldn't be encoded correctly so the encoded values - # would be used, and use of `<` and `>` would break the XML document. - # It shouldn't cause an issue since this is cosmetic for the manual. - _module.args.name = lib.mkOptionDefault "‹name›"; - }] ++ modules; + modules = [ + { + # This is a work-around for the fact that some sub-modules, + # such as the one included in an attribute set, expects an "args" + # attribute to be given to the sub-module. As the option + # evaluation does not have any specific attribute name yet, we + # provide a default for the documentation and the freeform type. + # + # This is necessary as some option declaration might use the + # "name" attribute given as argument of the submodule and use it + # as the default of option declarations. + # + # We use lookalike unicode single angle quotation marks because + # of the docbook transformation the options receive. In all uses + # > and < wouldn't be encoded correctly so the encoded values + # would be used, and use of `<` and `>` would break the XML document. + # It shouldn't cause an issue since this is cosmetic for the manual. + _module.args.name = lib.mkOptionDefault "‹name›"; + } + ] ++ modules; }; freeformType = base._module.freeformType; name = "submodule"; - in mkOptionType { + in + mkOptionType { inherit name; - description = if description != null then - description - else - freeformType.description or name; + description = if description != null then description else freeformType.description or name; check = x: isAttrs x || isFunction x || path.check x; - merge = loc: defs: + merge = + loc: defs: (base.extendModules { - modules = [{ _module.args.name = last loc; }] ++ allModules defs; + modules = [ { _module.args.name = last loc; } ] ++ allModules defs; prefix = loc; }).config; - emptyValue = { value = { }; }; - getSubOptions = prefix: + emptyValue = { + value = { }; + }; + getSubOptions = + prefix: (base.extendModules { inherit prefix; }).options // optionalAttrs (freeformType != null) { # Expose the sub options of the freeform type. Note that the option @@ -922,14 +1088,17 @@ let }; getSubModules = modules; substSubModules = m: submoduleWith (attrs // { modules = m; }); - nestedTypes = lib.optionalAttrs (freeformType != null) { - freeformType = freeformType; - }; + nestedTypes = lib.optionalAttrs (freeformType != null) { freeformType = freeformType; }; functor = defaultFunctor name // { type = types.submoduleWith; payload = { - inherit modules class specialArgs shorthandOnlyDefinesConfig - description; + inherit + modules + class + specialArgs + shorthandOnlyDefinesConfig + description + ; }; binOp = lhs: rhs: { class = @@ -942,50 +1111,45 @@ let else if lhs.class or null == rhs.class then lhs.class or null else - throw '' - A submoduleWith option is declared multiple times with conflicting class values "${ - toString lhs.class - }" and "${toString rhs.class}".''; + throw ''A submoduleWith option is declared multiple times with conflicting class values "${toString lhs.class}" and "${toString rhs.class}".''; modules = lhs.modules ++ rhs.modules; - specialArgs = let - intersecting = - builtins.intersectAttrs lhs.specialArgs rhs.specialArgs; - in if intersecting == { } then - lhs.specialArgs // rhs.specialArgs - else - throw '' - A submoduleWith option is declared multiple times with the same specialArgs "${ - toString (attrNames intersecting) - }"''; + specialArgs = + let + intersecting = builtins.intersectAttrs lhs.specialArgs rhs.specialArgs; + in + if intersecting == { } then + lhs.specialArgs // rhs.specialArgs + else + throw ''A submoduleWith option is declared multiple times with the same specialArgs "${toString (attrNames intersecting)}"''; shorthandOnlyDefinesConfig = if lhs.shorthandOnlyDefinesConfig == null then rhs.shorthandOnlyDefinesConfig else if rhs.shorthandOnlyDefinesConfig == null then lhs.shorthandOnlyDefinesConfig - else if lhs.shorthandOnlyDefinesConfig - == rhs.shorthandOnlyDefinesConfig then + else if lhs.shorthandOnlyDefinesConfig == rhs.shorthandOnlyDefinesConfig then lhs.shorthandOnlyDefinesConfig else - throw - "A submoduleWith option is declared multiple times with conflicting shorthandOnlyDefinesConfig values"; - description = if lhs.description == null then - rhs.description - else if rhs.description == null then - lhs.description - else if lhs.description == rhs.description then - lhs.description - else - throw - "A submoduleWith option is declared multiple times with conflicting descriptions"; + throw "A submoduleWith option is declared multiple times with conflicting shorthandOnlyDefinesConfig values"; + description = + if lhs.description == null then + rhs.description + else if rhs.description == null then + lhs.description + else if lhs.description == rhs.description then + lhs.description + else + throw "A submoduleWith option is declared multiple times with conflicting descriptions"; }; }; }; # A value from a set of allowed ones. - enum = values: + enum = + values: let inherit (lib.lists) unique; - show = v: + show = + v: if builtins.isString v then ''"${v}"'' else if builtins.isInt v then @@ -994,7 +1158,8 @@ let boolToString v else "<${builtins.typeOf v}>"; - in mkOptionType rec { + in + mkOptionType rec { name = "enum"; description = # Length 0 or 1 enums may occur in a design pattern with type merging @@ -1007,8 +1172,7 @@ let "value ${show (builtins.head values)} (singular enum)" else "one of ${concatMapStringsSep ", " show values}"; - descriptionClass = - if builtins.length values < 2 then "noun" else "conjunction"; + descriptionClass = if builtins.length values < 2 then "noun" else "conjunction"; check = flip elem values; merge = mergeEqualOption; functor = (defaultFunctor name) // { @@ -1018,94 +1182,96 @@ let }; # Either value of type `t1` or `t2`. - either = t1: t2: + either = + t1: t2: mkOptionType rec { name = "either"; description = if t1.descriptionClass or null == "nonRestrictiveClause" then - # Plain, but add comma + # Plain, but add comma "${t1.description}, or ${ - optionDescriptionPhrase - (class: class == "noun" || class == "conjunction") t2 + optionDescriptionPhrase (class: class == "noun" || class == "conjunction") t2 }" else - "${ - optionDescriptionPhrase - (class: class == "noun" || class == "conjunction") t1 - } or ${ - optionDescriptionPhrase (class: - class == "noun" || class == "conjunction" || class - == "composite") t2 + "${optionDescriptionPhrase (class: class == "noun" || class == "conjunction") t1} or ${ + optionDescriptionPhrase ( + class: class == "noun" || class == "conjunction" || class == "composite" + ) t2 }"; descriptionClass = "conjunction"; check = x: t1.check x || t2.check x; - merge = loc: defs: - let defList = map (d: d.value) defs; - in if all (x: t1.check x) defList then + merge = + loc: defs: + let + defList = map (d: d.value) defs; + in + if all (x: t1.check x) defList then t1.merge loc defs else if all (x: t2.check x) defList then t2.merge loc defs else mergeOneOption loc defs; - typeMerge = f': + typeMerge = + f': let mt1 = t1.typeMerge (elemAt f'.wrapped 0).functor; mt2 = t2.typeMerge (elemAt f'.wrapped 1).functor; - in if (name == f'.name) && (mt1 != null) && (mt2 != null) then - functor.type mt1 mt2 - else - null; - functor = (defaultFunctor name) // { wrapped = [ t1 t2 ]; }; + in + if (name == f'.name) && (mt1 != null) && (mt2 != null) then functor.type mt1 mt2 else null; + functor = (defaultFunctor name) // { + wrapped = [ + t1 + t2 + ]; + }; nestedTypes.left = t1; nestedTypes.right = t2; }; # Any of the types in the given list - oneOf = ts: + oneOf = + ts: let - head' = if ts == [ ] then - throw "types.oneOf needs to get at least one type in its argument" - else - head ts; + head' = + if ts == [ ] then throw "types.oneOf needs to get at least one type in its argument" else head ts; tail' = tail ts; - in foldl' either head' tail'; + in + foldl' either head' tail'; # Either value of type `coercedType` or `finalType`, the former is # converted to `finalType` using `coerceFunc`. - coercedTo = coercedType: coerceFunc: finalType: - assert lib.assertMsg (coercedType.getSubModules == null) - "coercedTo: coercedType must not have submodules (it’s a ${coercedType.description})"; + coercedTo = + coercedType: coerceFunc: finalType: + assert lib.assertMsg ( + coercedType.getSubModules == null + ) "coercedTo: coercedType must not have submodules (it’s a ${coercedType.description})"; mkOptionType rec { name = "coercedTo"; - description = - "${optionDescriptionPhrase (class: class == "noun") finalType} or ${ - optionDescriptionPhrase (class: class == "noun") coercedType - } convertible to it"; - check = x: - (coercedType.check x && finalType.check (coerceFunc x)) - || finalType.check x; - merge = loc: defs: + description = "${optionDescriptionPhrase (class: class == "noun") finalType} or ${ + optionDescriptionPhrase (class: class == "noun") coercedType + } convertible to it"; + check = x: (coercedType.check x && finalType.check (coerceFunc x)) || finalType.check x; + merge = + loc: defs: let - coerceVal = val: - if coercedType.check val then coerceFunc val else val; - in finalType.merge loc - (map (def: def // { value = coerceVal def.value; }) defs); + coerceVal = val: if coercedType.check val then coerceFunc val else val; + in + finalType.merge loc (map (def: def // { value = coerceVal def.value; }) defs); emptyValue = finalType.emptyValue; getSubOptions = finalType.getSubOptions; getSubModules = finalType.getSubModules; - substSubModules = m: - coercedTo coercedType coerceFunc (finalType.substSubModules m); + substSubModules = m: coercedTo coercedType coerceFunc (finalType.substSubModules m); typeMerge = t1: t2: null; - functor = (defaultFunctor name) // { wrapped = finalType; }; + functor = (defaultFunctor name) // { + wrapped = finalType; + }; nestedTypes.coercedType = coercedType; nestedTypes.finalType = finalType; }; # Augment the given type with an additional type check function. - addCheck = elemType: check: - elemType // { - check = x: elemType.check x && check x; - }; + addCheck = elemType: check: elemType // { check = x: elemType.check x && check x; }; }; }; -in outer_types // outer_types.types +in +outer_types // outer_types.types diff --git a/nix/stdlib/versions.nix b/nix/stdlib/versions.nix index 98294c1..8f672e3 100644 --- a/nix/stdlib/versions.nix +++ b/nix/stdlib/versions.nix @@ -1,61 +1,69 @@ # Version string functions. -{ lib }: rec { - /* Break a version string into its component parts. +{ lib }: +rec { + /* + Break a version string into its component parts. - Example: - splitVersion "1.2.3" - => ["1" "2" "3"] + Example: + splitVersion "1.2.3" + => ["1" "2" "3"] */ splitVersion = builtins.splitVersion; - /* Get the major version string from a string. + /* + Get the major version string from a string. - Example: - major "1.2.3" - => "1" + Example: + major "1.2.3" + => "1" */ major = v: builtins.elemAt (splitVersion v) 0; - /* Get the minor version string from a string. + /* + Get the minor version string from a string. - Example: - minor "1.2.3" - => "2" + Example: + minor "1.2.3" + => "2" */ minor = v: builtins.elemAt (splitVersion v) 1; - /* Get the patch version string from a string. + /* + Get the patch version string from a string. - Example: - patch "1.2.3" - => "3" + Example: + patch "1.2.3" + => "3" */ patch = v: builtins.elemAt (splitVersion v) 2; - /* Get string of the first two parts (major and minor) - of a version string. + /* + Get string of the first two parts (major and minor) + of a version string. - Example: - majorMinor "1.2.3" - => "1.2" + Example: + majorMinor "1.2.3" + => "1.2" */ majorMinor = v: builtins.concatStringsSep "." (lib.take 2 (splitVersion v)); - /* Pad a version string with zeros to match the given number of components. + /* + Pad a version string with zeros to match the given number of components. - Example: - pad 3 "1.2" - => "1.2.0" - pad 3 "1.3-rc1" - => "1.3.0-rc1" - pad 3 "1.2.3.4" - => "1.2.3" + Example: + pad 3 "1.2" + => "1.2.0" + pad 3 "1.3-rc1" + => "1.3.0-rc1" + pad 3 "1.2.3.4" + => "1.2.3" */ - pad = n: version: + pad = + n: version: let numericVersion = lib.head (lib.splitString "-" version); versionSuffix = lib.removePrefix numericVersion version; - in lib.concatStringsSep "." - (lib.take n (lib.splitVersion numericVersion ++ lib.genList (_: "0") n)) + in + lib.concatStringsSep "." (lib.take n (lib.splitVersion numericVersion ++ lib.genList (_: "0") n)) + versionSuffix; } From d2f30a07b2f29cd4c70ee035703f3fa8de7ba27b Mon Sep 17 00:00:00 2001 From: tcmal Date: Thu, 16 May 2024 15:53:24 +0100 Subject: [PATCH 3/4] update readme --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 521111c..47747d0 100644 --- a/README.md +++ b/README.md @@ -18,9 +18,9 @@ This directory uses `npins`, as using flakes requires constantly running `nix fl ## Testing -Tests are stored in `extra/tests/`. You can run them all with `nix-build -E '(import ./extra).checks.tests` (or `.stdlib`, `.auxlib`). +Tests are stored in `extra/tests/`. You can run them all with `nix-build -E '(import ./extra {}).checks.tests` (or `.stdlib`, `.auxlib`). -You should also check your formatting with `nix-build -E '(import ./extra).checks.formatting`. +You should also check your formatting with `nix-build -E '(import ./extra {}).checks.formatting`. ## Documentation @@ -28,4 +28,4 @@ Reference documentation for library functions is written above each function as These comments are processed using [nixdoc](https://github.com/nix-community/nixdoc), although currently we aren't doing much with the output. The nixdoc README describes the [comment format](https://github.com/nix-community/nixdoc#comment-format). -You can build the documentation with `nix-build -E '(import ./extra).packages.docs'`. +You can build the documentation with `nix-build -E '(import ./extra {}).packages.docs'`. From e60323be5451a07876a63a9aabc9b434a9de39bc Mon Sep 17 00:00:00 2001 From: tcmal Date: Thu, 16 May 2024 16:09:11 +0100 Subject: [PATCH 4/4] fix errors in tests --- extra/tests/formatting.nix | 7 ++++--- extra/tests/stdlib/modules.sh | 16 ++++++++-------- .../stdlib/modules/declaration-positions.nix | 16 ++++++++-------- 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/extra/tests/formatting.nix b/extra/tests/formatting.nix index caabd94..3b9493f 100644 --- a/extra/tests/formatting.nix +++ b/extra/tests/formatting.nix @@ -1,9 +1,10 @@ { - auxlib, + libSrc, runCommand, nixfmt, }: runCommand "aux-lib-formatting" { buildInputs = [ nixfmt ]; } '' - find ${auxlib} -iname '*.nix' -type f -print0 | xargs -0 -i nixfmt -c {} - touch $out + find ${libSrc} -iname '*.nix' -type f -print0 | xargs -0 -i nixfmt -c {} + mkdir $out + touch $out/formatted '' diff --git a/extra/tests/stdlib/modules.sh b/extra/tests/stdlib/modules.sh index 7eb87dd..f0b9b84 100755 --- a/extra/tests/stdlib/modules.sh +++ b/extra/tests/stdlib/modules.sh @@ -495,20 +495,20 @@ checkConfigOutput '^"pear\\npear"$' config.twice.raw ./merge-module-with-key.nix # Declaration positions # Line should be present for direct options -checkConfigOutput '^8$' options.imported.line8.declarationPositions.0.line ./declaration-positions.nix -checkConfigOutput '/declaration-positions.nix"$' options.imported.line8.declarationPositions.0.file ./declaration-positions.nix +checkConfigOutput '^14$' options.imported.line14.declarationPositions.0.line ./declaration-positions.nix +checkConfigOutput '/declaration-positions.nix"$' options.imported.line14.declarationPositions.0.file ./declaration-positions.nix # Generated options may not have line numbers but they will at least get the # right file -checkConfigOutput '/declaration-positions.nix"$' options.generated.line14.declarationPositions.0.file ./declaration-positions.nix -checkConfigOutput '^null$' options.generated.line14.declarationPositions.0.line ./declaration-positions.nix +checkConfigOutput '/declaration-positions.nix"$' options.generated.line19.declarationPositions.0.file ./declaration-positions.nix +checkConfigOutput '^null$' options.generated.line19.declarationPositions.0.line ./declaration-positions.nix # Submodules don't break it -checkConfigOutput '^28$' config.submoduleLine24.submodDeclLine28.0.line ./declaration-positions.nix -checkConfigOutput '/declaration-positions.nix"$' config.submoduleLine24.submodDeclLine28.0.file ./declaration-positions.nix +checkConfigOutput '^37$' config.submoduleLine30.submodDeclLine37.0.line ./declaration-positions.nix +checkConfigOutput '/declaration-positions.nix"$' config.submoduleLine30.submodDeclLine37.0.file ./declaration-positions.nix # New options under freeform submodules get collected into the parent submodule # (consistent with .declarations behaviour, but weird; notably appears in system.build) -checkConfigOutput '^24$' options.submoduleLine24.declarationPositions.0.line ./declaration-positions.nix +checkConfigOutput '^30$' options.submoduleLine30.declarationPositions.0.line ./declaration-positions.nix # nested options work -checkConfigOutput '^22$' options.nested.nestedLine22.declarationPositions.0.line ./declaration-positions.nix +checkConfigOutput '^28$' options.nested.nestedLine28.declarationPositions.0.line ./declaration-positions.nix cat <