From 408dcfc0d3898fb855c1038d24c09c82fe58c379 Mon Sep 17 00:00:00 2001 From: Sandro Date: Mon, 5 Sep 2022 15:42:10 +0200 Subject: [PATCH 001/116] Improve experimental-features error wording --- src/libutil/experimental-features.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libutil/experimental-features.cc b/src/libutil/experimental-features.cc index fa79cca6b..d8b0d30fa 100644 --- a/src/libutil/experimental-features.cc +++ b/src/libutil/experimental-features.cc @@ -52,7 +52,7 @@ std::set parseFeatures(const std::set & rawFea } MissingExperimentalFeature::MissingExperimentalFeature(ExperimentalFeature feature) - : Error("experimental Nix feature '%1%' is disabled; use '--extra-experimental-features %1%' to override", showExperimentalFeature(feature)) + : Error("experimental Nix feature '%1%' is disabled; pass '--extra-experimental-features %1%' as an argument to enable it", showExperimentalFeature(feature)) , missingFeature(feature) {} From 481e4082bf96f5613693785e97a9a5cec7c09940 Mon Sep 17 00:00:00 2001 From: Sandro Date: Wed, 9 Nov 2022 12:03:53 +0100 Subject: [PATCH 002/116] Update src/libutil/experimental-features.cc Co-authored-by: Valentin Gagarin --- src/libutil/experimental-features.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libutil/experimental-features.cc b/src/libutil/experimental-features.cc index d8b0d30fa..052988b34 100644 --- a/src/libutil/experimental-features.cc +++ b/src/libutil/experimental-features.cc @@ -52,7 +52,7 @@ std::set parseFeatures(const std::set & rawFea } MissingExperimentalFeature::MissingExperimentalFeature(ExperimentalFeature feature) - : Error("experimental Nix feature '%1%' is disabled; pass '--extra-experimental-features %1%' as an argument to enable it", showExperimentalFeature(feature)) + : Error("experimental Nix feature '%1%' is disabled; add '--extra-experimental-features %1%' to enable it", showExperimentalFeature(feature)) , missingFeature(feature) {} From c74248c56e6e5558603be6c6eda43eb439772f20 Mon Sep 17 00:00:00 2001 From: Michael Lohmann Date: Thu, 12 Jan 2023 15:45:19 +0100 Subject: [PATCH 003/116] install-multi-user: ignore profile_target backups that have no change If there was a prior nix installation that created this backup file and then you tried to install it again, it would stop to tell you there is this file. But if the file and its backup are identical in content, there is no harm in continuing and in a later step overwriting the existing backup file with the identical one. This is just a convenience feature. --- scripts/install-multi-user.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/scripts/install-multi-user.sh b/scripts/install-multi-user.sh index f149ea0d7..b2f2a9e45 100644 --- a/scripts/install-multi-user.sh +++ b/scripts/install-multi-user.sh @@ -445,6 +445,14 @@ EOF # a row for different files. if [ -e "$profile_target$PROFILE_BACKUP_SUFFIX" ]; then # this backup process first released in Nix 2.1 + + if diff -q "$profile_target$PROFILE_BACKUP_SUFFIX" "$profile_target" > /dev/null; then + # a backup file for the rc-file exist, but they are identical, + # so we can safely ignore it and overwrite it with the same + # content later + continue + fi + failure < Date: Wed, 19 Apr 2023 16:32:47 -0700 Subject: [PATCH 004/116] Mention `$DRV_PATH` in post-build-hook docs --- doc/manual/src/advanced-topics/post-build-hook.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/manual/src/advanced-topics/post-build-hook.md b/doc/manual/src/advanced-topics/post-build-hook.md index 1479cc3a4..17b67111c 100644 --- a/doc/manual/src/advanced-topics/post-build-hook.md +++ b/doc/manual/src/advanced-topics/post-build-hook.md @@ -69,6 +69,8 @@ exec nix copy --to "s3://example-nix-cache" $OUT_PATHS > store sign`. Nix guarantees the paths will not contain any spaces, > however a store path might contain glob characters. The `set -f` > disables globbing in the shell. +> If you want to upload the .drv file too, the `$DRV_PATH` variable +> is also defined for the script and works just like `$OUT_PATHS`. Then make sure the hook program is executable by the `root` user: From f11445952fcf0f5fd3f827997591d43a41f2074a Mon Sep 17 00:00:00 2001 From: Michal Sojka Date: Sun, 11 Jun 2023 21:36:56 +0200 Subject: [PATCH 005/116] Document builtins.fetchTree Co-authored-by: Valentin Gagarin Supersedes #6740 --- src/libexpr/primops/fetchTree.cc | 43 ++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index 579a45f92..3b048c446 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -198,10 +198,49 @@ static void prim_fetchTree(EvalState & state, const PosIdx pos, Value * * args, fetchTree(state, pos, args, v, std::nullopt, FetchTreeParams { .allowNameArgument = false }); } -// FIXME: document static RegisterPrimOp primop_fetchTree({ .name = "fetchTree", - .arity = 1, + .args = {"input"}, + .doc = R"( + Fetch a source tree or a plain file using one of the supported backends. + *input* can be an attribute set representation of [flake reference](@docroot@/command-ref/new-cli/nix3-flake.md#flake-references) or a URL. + The input should be "locked", that is, it should contain a commit hash or content hash unless impure evaluation (`--impure`) is allowed. + + Here are some examples of how to use `fetchTree`: + + - Fetch a GitHub repository: + + ```nix + builtins.fetchTree { + type = "github"; + owner = "NixOS"; + repo = "nixpkgs"; + rev = "ae2e6b3958682513d28f7d633734571fb18285dd"; + } + ``` + + This evaluates to attribute set: + + ``` + { + lastModified = 1686503798; + lastModifiedDate = "20230611171638"; + narHash = "sha256-rA9RqKP9OlBrgGCPvfd5HVAXDOy8k2SmPtB/ijShNXc="; + outPath = "/nix/store/l5m6qlvfs9sdw14ja3qbzpglcjlb6j1x-source"; + rev = "ae2e6b3958682513d28f7d633734571fb18285dd"; + shortRev = "ae2e6b3"; + } + ``` + - Fetch a single file from a URL: + + ```nix + builtins.fetchTree "https://example.com/" + ``` + + > **Note** + > + > This function requires the [`flakes` experimental feature](@docroot@/contributing/experimental-features.md#xp-feature-flakes) to be enabled. + )", .fun = prim_fetchTree }); From feb01b22ed2954232455b5c8346cc5ed09c21f81 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Mon, 17 Jul 2023 11:28:12 +0200 Subject: [PATCH 006/116] add links to store API documentation --- src/libstore/store-api.hh | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh index 14a862eef..43aa00637 100644 --- a/src/libstore/store-api.hh +++ b/src/libstore/store-api.hh @@ -136,19 +136,22 @@ struct StoreConfig : public Config Setting priority{this, 0, "priority", R"( - Priority of this store when used as a substituter. A lower value means a higher priority. + Priority of this store when used as a [substituter](@docroot@/command-ref/conf-file.md#conf-substituters). + A lower value means a higher priority. )"}; Setting wantMassQuery{this, false, "want-mass-query", R"( - Whether this store (when used as a substituter) can be - queried efficiently for path validity. + Whether this store can be queried efficiently for path validity when used as a [substituter](@docroot@/command-ref/conf-file.md#conf-substituters). )"}; Setting systemFeatures{this, getDefaultSystemFeatures(), "system-features", - "Optional features that the system this store builds on implements (like \"kvm\")."}; + R"( + Optional [system features](@docroot@/command-ref/conf-file.md#conf-system-features) available on the system this store uses to build derivations. + Example: `"kvm"` + )" }; }; class Store : public std::enable_shared_from_this, public virtual StoreConfig From 493ddf617f0b2c406dc35026b6d481a69cf1d934 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Mon, 17 Jul 2023 11:28:27 +0200 Subject: [PATCH 007/116] reformat `system-features` setting documentation --- src/libstore/globals.hh | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index d4b8fb1f9..879588375 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -689,19 +689,21 @@ public: getDefaultSystemFeatures(), "system-features", R"( - A set of system “features” supported by this machine, e.g. `kvm`. - Derivations can express a dependency on such features through the - derivation attribute `requiredSystemFeatures`. For example, the - attribute + A set of system “features” supported by this machine. + Derivations can express a dependency on such features through the derivation attribute `requiredSystemFeatures`. + + For example, the attribute requiredSystemFeatures = [ "kvm" ]; - ensures that the derivation can only be built on a machine with the - `kvm` feature. + ensures that the derivation can only be built on a machine with the `kvm` feature. - This setting by default includes `kvm` if `/dev/kvm` is accessible, - and the pseudo-features `nixos-test`, `benchmark` and `big-parallel` - that are used in Nixpkgs to route builds to specific machines. + This setting by default includes + - `kvm` if `/dev/kvm` is accessible + - historical pseudo-features for backwards compatibility, used in Nixpkgs to route Hydra builds to specific machines + - `nixos-test` + - `benchmark` + - `big-parallel` )", {}, false}; Setting substituters{ From 5f37ebcf83c856b08a63778e07bfc5c84ea4a5ec Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Wed, 19 Jul 2023 11:57:20 +0200 Subject: [PATCH 008/116] document all special system features and their behavior --- .../src/language/advanced-attributes.md | 12 ++++++ doc/manual/src/release-notes/rl-2.12.md | 14 +------ src/libstore/globals.hh | 39 +++++++++++++------ 3 files changed, 41 insertions(+), 24 deletions(-) diff --git a/doc/manual/src/language/advanced-attributes.md b/doc/manual/src/language/advanced-attributes.md index 307971434..2961f9dcc 100644 --- a/doc/manual/src/language/advanced-attributes.md +++ b/doc/manual/src/language/advanced-attributes.md @@ -345,3 +345,15 @@ Derivations can declare some infrequently used optional attributes. This is useful, for example, when generating self-contained filesystem images with their own embedded Nix store: hashes found inside such an image refer to the embedded store and not to the host's Nix store. + +- [`requiredSystemFeatures`]{#adv-attr-requiredSystemFeatures}\ + + If a derivation has the `requiredSystemFeatures` attribute, then Nix will only build it on a machine that has the corresponding features set in its [`system-features` configuration](@docroot@/command-ref/conf-file.md#conf-system-features). + + For example, setting + + ```nix + requiredSystemFeatures = [ "kvm" ]; + ``` + + ensures that the derivation can only be built on a machine with the `kvm` feature. diff --git a/doc/manual/src/release-notes/rl-2.12.md b/doc/manual/src/release-notes/rl-2.12.md index e2045d7bf..57d092e01 100644 --- a/doc/manual/src/release-notes/rl-2.12.md +++ b/doc/manual/src/release-notes/rl-2.12.md @@ -2,20 +2,8 @@ * On Linux, Nix can now run builds in a user namespace where they run as root (UID 0) and have 65,536 UIDs available. - - This is primarily useful for running containers such as `systemd-nspawn` - inside a Nix build. For an example, see [`tests/systemd-nspawn/nix`][nspawn]. - [nspawn]: https://github.com/NixOS/nix/blob/67bcb99700a0da1395fa063d7c6586740b304598/tests/systemd-nspawn.nix. - - A build can enable this by setting the derivation attribute: - - ``` - requiredSystemFeatures = [ "uid-range" ]; - ``` - - The `uid-range` [system feature] requires the [`auto-allocate-uids`] - setting to be enabled. + This can be used by requiring `uid-range` [system feature] in derivations. [system feature]: ../command-ref/conf-file.md#conf-system-features diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index 879588375..9f852dc7b 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -337,7 +337,7 @@ public: users in `build-users-group`. UIDs are allocated starting at 872415232 (0x34000000) on Linux and 56930 on macOS. - )"}; + )", {}, true, Xp::AutoAllocateUids}; Setting startId{this, #if __linux__ @@ -690,20 +690,37 @@ public: "system-features", R"( A set of system “features” supported by this machine. - Derivations can express a dependency on such features through the derivation attribute `requiredSystemFeatures`. - For example, the attribute + This complements the [`system`](#conf-system) and [`extra-platforms`](#conf-extra-platforms) configuration options and the corresponding [`system`](@docroot@/language/derivations.md#attr-system) attribute on derivations. - requiredSystemFeatures = [ "kvm" ]; + Derivations can require system features in the derivation attribute [`requiredSystemFeatures`](@docroot@/language/advanced-attributes.md#adv-attr-requiredSystemFeatures). - ensures that the derivation can only be built on a machine with the `kvm` feature. + System features are generally user-defined, but the following have special treatment: - This setting by default includes - - `kvm` if `/dev/kvm` is accessible - - historical pseudo-features for backwards compatibility, used in Nixpkgs to route Hydra builds to specific machines - - `nixos-test` - - `benchmark` - - `big-parallel` + - `kvm` + + Set by default if `/dev/kvm` is accessible. + + - `nixos-test`, `benchmark`, `big-parallel` + + These historical pseudo-features are always enabled for backwards compatibility, used in Nixpkgs to route Hydra builds to specific machines. + + - `ca-derivations` + + Set by default if the [`ca-derivations` experimental feature](@docroot@/contributing/experimental-features.md#xp-feature-ca-derivations) is enabled. + + - `recursive-nix` + + Set by default if the [`recursive-nix` experimental feature](@docroot@/contributing/experimental-features.md#xp-feature-recursive-nix) is enabled. + + - `uid-range` + + On Linux, Nix can run builds in a user namespace where they run as root (UID 0) and have 65,536 UIDs available. + This is primarily useful for running containers such as `systemd-nspawn` inside a Nix build. For an example, see [`tests/systemd-nspawn/nix`][nspawn]. + + [nspawn]: https://github.com/NixOS/nix/blob/67bcb99700a0da1395fa063d7c6586740b304598/tests/systemd-nspawn.nix. + + Set by default on Linux if the [`auto-allocate-uids`](#conf-auto-allocate-uids) setting is enabled. )", {}, false}; Setting substituters{ From 2fa90e5824635998b445b6862bb49469e8fc75de Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Wed, 19 Jul 2023 13:59:18 +0200 Subject: [PATCH 009/116] add more details on CA derivations --- doc/manual/src/language/advanced-attributes.md | 2 ++ src/libstore/globals.hh | 16 +++++++++------- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/doc/manual/src/language/advanced-attributes.md b/doc/manual/src/language/advanced-attributes.md index 2961f9dcc..5292a2ffc 100644 --- a/doc/manual/src/language/advanced-attributes.md +++ b/doc/manual/src/language/advanced-attributes.md @@ -229,6 +229,8 @@ Derivations can declare some infrequently used optional attributes. [`outputHashAlgo`](#adv-attr-outputHashAlgo) like for *fixed-output derivations* (see above). + It also implicitly requires that the machine to build the derivation must have the `ca-derivations` [system feature](@docroot@/command-ref/conf-file.md#conf-system-features). + - [`passAsFile`]{#adv-attr-passAsFile}\ A list of names of attributes that should be passed via files rather than environment variables. For example, if you have diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index 9f852dc7b..c08a1a943 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -693,25 +693,27 @@ public: This complements the [`system`](#conf-system) and [`extra-platforms`](#conf-extra-platforms) configuration options and the corresponding [`system`](@docroot@/language/derivations.md#attr-system) attribute on derivations. - Derivations can require system features in the derivation attribute [`requiredSystemFeatures`](@docroot@/language/advanced-attributes.md#adv-attr-requiredSystemFeatures). + A derivation can require system features in the [`requiredSystemFeatures` attribute](@docroot@/language/advanced-attributes.md#adv-attr-requiredSystemFeatures), and the machine to build the derivation must have them. - System features are generally user-defined, but the following have special treatment: + System features are user-defined, but Nix sets the following defaults: - `kvm` - Set by default if `/dev/kvm` is accessible. + Included by default if `/dev/kvm` is accessible. - `nixos-test`, `benchmark`, `big-parallel` - These historical pseudo-features are always enabled for backwards compatibility, used in Nixpkgs to route Hydra builds to specific machines. + These historical pseudo-features are always enabled for backwards compatibility, as they are used in Nixpkgs to route Hydra builds to specific machines. - `ca-derivations` - Set by default if the [`ca-derivations` experimental feature](@docroot@/contributing/experimental-features.md#xp-feature-ca-derivations) is enabled. + Included by default if the [`ca-derivations` experimental feature](@docroot@/contributing/experimental-features.md#xp-feature-ca-derivations) is enabled. + + This system feature is implicitly required by derivations with the [`__contentAddressed` attribute](@docroot@/language/advanced-attributes.md#adv-attr-__contentAddressed). - `recursive-nix` - Set by default if the [`recursive-nix` experimental feature](@docroot@/contributing/experimental-features.md#xp-feature-recursive-nix) is enabled. + Included by default if the [`recursive-nix` experimental feature](@docroot@/contributing/experimental-features.md#xp-feature-recursive-nix) is enabled. - `uid-range` @@ -720,7 +722,7 @@ public: [nspawn]: https://github.com/NixOS/nix/blob/67bcb99700a0da1395fa063d7c6586740b304598/tests/systemd-nspawn.nix. - Set by default on Linux if the [`auto-allocate-uids`](#conf-auto-allocate-uids) setting is enabled. + Included by default on Linux if the [`auto-allocate-uids`](#conf-auto-allocate-uids) setting is enabled. )", {}, false}; Setting substituters{ From 52248b1c2786f7aa98109d47ee12f527bd202b12 Mon Sep 17 00:00:00 2001 From: Tom Bereknyei Date: Sat, 19 Aug 2023 17:03:31 -0400 Subject: [PATCH 010/116] feat: notation to refer to no attribute search prefix An attrPath prefix of "." indicates no need to try default attrPath prefixes. For example 1nixpkgs#legacyPackages.x86_64-linux.ERROR` searches through ``` trying flake output attribute 'packages.x86_64-linux.legacyPackages.x86_64-linux.ERROR' using cached attrset attribute '' trying flake output attribute 'legacyPackages.x86_64-linux.legacyPackages.x86_64-linux.ERROR' using cached attrset attribute 'legacyPackages.x86_64-linux' trying flake output attribute 'legacyPackages.x86_64-linux.ERROR' using cached attrset attribute 'legacyPackages.x86_64-linux' ``` And there is no way to specify that one does not want the automatic search behavior. Now one can specify `nixpkgs#.legacyPackages.x86_64-linux.ERROR` to only refer to the rooted attribute path without any default injection of attribute search path or system. --- src/libcmd/installable-flake.cc | 5 +++++ src/libcmd/installables.cc | 12 ++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/libcmd/installable-flake.cc b/src/libcmd/installable-flake.cc index 4074da06d..2f428cb7e 100644 --- a/src/libcmd/installable-flake.cc +++ b/src/libcmd/installable-flake.cc @@ -28,6 +28,11 @@ namespace nix { std::vector InstallableFlake::getActualAttrPaths() { std::vector res; + if (attrPaths.size() == 1 && attrPaths.front().starts_with(".")){ + attrPaths.front().erase(0,1); + res.push_back(attrPaths.front()); + return res; + } for (auto & prefix : prefixes) res.push_back(prefix + *attrPaths.begin()); diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index eb1903084..01c01441c 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -301,6 +301,11 @@ void completeFlakeRefWithFragment( completionType = ctAttrs; auto fragment = prefix.substr(hash + 1); + std::string prefixRoot = ""; + if (fragment.starts_with(".")){ + fragment = fragment.substr(1); + prefixRoot = "."; + } auto flakeRefS = std::string(prefix.substr(0, hash)); auto flakeRef = parseFlakeRef(expandTilde(flakeRefS), absPath(".")); @@ -309,6 +314,9 @@ void completeFlakeRefWithFragment( auto root = evalCache->getRoot(); + if (prefixRoot == "."){ + attrPathPrefixes.clear(); + } /* Complete 'fragment' relative to all the attrpath prefixes as well as the root of the flake. */ @@ -333,7 +341,7 @@ void completeFlakeRefWithFragment( auto attrPath2 = (*attr)->getAttrPath(attr2); /* Strip the attrpath prefix. */ attrPath2.erase(attrPath2.begin(), attrPath2.begin() + attrPathPrefix.size()); - completions->add(flakeRefS + "#" + concatStringsSep(".", evalState->symbols.resolve(attrPath2))); + completions->add(flakeRefS + "#" + prefixRoot + concatStringsSep(".", evalState->symbols.resolve(attrPath2))); } } } @@ -344,7 +352,7 @@ void completeFlakeRefWithFragment( for (auto & attrPath : defaultFlakeAttrPaths) { auto attr = root->findAlongAttrPath(parseAttrPath(*evalState, attrPath)); if (!attr) continue; - completions->add(flakeRefS + "#"); + completions->add(flakeRefS + "#" + prefixRoot); } } } From c609be4072b03dc3eabcb2ac9916593847fd3b19 Mon Sep 17 00:00:00 2001 From: Tom Bereknyei Date: Sat, 19 Aug 2023 17:19:52 -0400 Subject: [PATCH 011/116] doc: explain the . attrPath prefix notation --- doc/manual/src/release-notes/rl-next.md | 2 ++ src/nix/nix.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/doc/manual/src/release-notes/rl-next.md b/doc/manual/src/release-notes/rl-next.md index 7ddb5ca00..56222a21b 100644 --- a/doc/manual/src/release-notes/rl-next.md +++ b/doc/manual/src/release-notes/rl-next.md @@ -22,3 +22,5 @@ - Introduce a new [`outputOf`](@docroot@/language/builtins.md#builtins-outputOf) builtin. It is part of the [`dynamic-derivations`](@docroot@/contributing/experimental-features.md#xp-feature-dynamic-derivations) experimental feature. + +- Introduce new flake installable syntax `flakeref#.attrPath` where the "." prefix denotes no searching of default attribute prefixes like `packages.` or `legacyPackages.`. diff --git a/src/nix/nix.md b/src/nix/nix.md index e0f459d6b..6e7e8a649 100644 --- a/src/nix/nix.md +++ b/src/nix/nix.md @@ -132,6 +132,8 @@ subcommands, these are `packages.`*system*, attributes `packages.x86_64-linux.hello`, `legacyPackages.x86_64-linux.hello` and `hello`. +If *attrpath* begins with `.` then no prefixes or defaults are attempted. This allows the form *flakeref*[`#.`*attrpath*], such as `github:NixOS/nixpkgs#.lib.fakeSha256` to avoid a search of `packages.*system*.lib.fakeSha256` + ### Store path Example: `/nix/store/v5sv61sszx301i0x6xysaqzla09nksnd-hello-2.10` From 696eb79b150011629d86addd10f79c943b2f16b2 Mon Sep 17 00:00:00 2001 From: Tom Bereknyei Date: Sun, 27 Aug 2023 04:42:52 -0400 Subject: [PATCH 012/116] test: test behavior of .-prefixed attrPaths --- tests/flakes/absolute-attr-paths.sh | 17 +++++++++++++++++ tests/local.mk | 1 + 2 files changed, 18 insertions(+) create mode 100644 tests/flakes/absolute-attr-paths.sh diff --git a/tests/flakes/absolute-attr-paths.sh b/tests/flakes/absolute-attr-paths.sh new file mode 100644 index 000000000..491adceb7 --- /dev/null +++ b/tests/flakes/absolute-attr-paths.sh @@ -0,0 +1,17 @@ +source ./common.sh + +flake1Dir=$TEST_ROOT/flake1 + +mkdir -p $flake1Dir +cat > $flake1Dir/flake.nix < Date: Mon, 28 Aug 2023 20:51:44 +0200 Subject: [PATCH 013/116] do not change existing release notes --- doc/manual/src/release-notes/rl-2.12.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/doc/manual/src/release-notes/rl-2.12.md b/doc/manual/src/release-notes/rl-2.12.md index 57d092e01..e1e3efe1a 100644 --- a/doc/manual/src/release-notes/rl-2.12.md +++ b/doc/manual/src/release-notes/rl-2.12.md @@ -2,8 +2,19 @@ * On Linux, Nix can now run builds in a user namespace where they run as root (UID 0) and have 65,536 UIDs available. + This is primarily useful for running containers such as `systemd-nspawn` + inside a Nix build. For an example, see [`tests/systemd-nspawn/nix`][nspawn]. - This can be used by requiring `uid-range` [system feature] in derivations. + [nspawn]: https://github.com/NixOS/nix/blob/67bcb99700a0da1395fa063d7c6586740b304598/tests/systemd-nspawn.nix. + + A build can enable this by setting the derivation attribute: + + ``` + requiredSystemFeatures = [ "uid-range" ]; + ``` + + The `uid-range` [system feature] requires the [`auto-allocate-uids`] + setting to be enabled. [system feature]: ../command-ref/conf-file.md#conf-system-features From 4c50f5d130fc3adc3b048923e267d936df5b6250 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 3 Sep 2023 13:44:32 +0200 Subject: [PATCH 014/116] traces: Do not print unknown location Solves 1/3 of the infinite recursion at unknown location meme. See #8879 for ensuring we always have a trace (for stack overflows) We might want to re-add this for finding missing location info *while hacking on that problem only*. --- src/libutil/error.cc | 12 ++++-------- src/libutil/error.hh | 7 +++++++ tests/lang/eval-fail-fromTOML-timestamps.err.exp | 2 -- tests/lang/eval-fail-hashfile-missing.err.exp | 4 ---- tests/lang/eval-fail-set-override.err.exp | 2 -- tests/lang/eval-fail-substring.err.exp | 2 -- tests/lang/eval-fail-to-path.err.exp | 2 -- 7 files changed, 11 insertions(+), 20 deletions(-) diff --git a/src/libutil/error.cc b/src/libutil/error.cc index 4a1b346ef..3917fffc3 100644 --- a/src/libutil/error.cc +++ b/src/libutil/error.cc @@ -203,8 +203,6 @@ std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool s std::ostringstream oss; - auto noSource = ANSI_ITALIC " (source not available)" ANSI_NORMAL "\n"; - /* * Traces * ------ @@ -320,7 +318,7 @@ std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool s oss << "\n" << "… " << trace.hint.str() << "\n"; - if (trace.pos) { + if (trace.pos && *trace.pos) { count++; oss << "\n" << ellipsisIndent << ANSI_BLUE << "at " ANSI_WARNING << *trace.pos << ANSI_NORMAL << ":"; @@ -329,8 +327,7 @@ std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool s oss << "\n"; printCodeLines(oss, "", *trace.pos, *loc); oss << "\n"; - } else - oss << noSource; + } } } oss << "\n" << prefix; @@ -338,15 +335,14 @@ std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool s oss << einfo.msg << "\n"; - if (einfo.errPos) { + if (einfo.errPos && *einfo.errPos) { oss << "\n" << ANSI_BLUE << "at " ANSI_WARNING << *einfo.errPos << ANSI_NORMAL << ":"; if (auto loc = einfo.errPos->getCodeLines()) { oss << "\n"; printCodeLines(oss, "", *einfo.errPos, *loc); oss << "\n"; - } else - oss << noSource; + } } auto suggestions = einfo.suggestions.trim(); diff --git a/src/libutil/error.hh b/src/libutil/error.hh index be5c5e252..c04dcbd77 100644 --- a/src/libutil/error.hh +++ b/src/libutil/error.hh @@ -70,6 +70,13 @@ struct AbstractPos uint32_t line = 0; uint32_t column = 0; + /** + * An AbstractPos may be a "null object", representing an unknown position. + * + * Return true if this position is known. + */ + inline operator bool() const { return line != 0; }; + /** * Return the contents of the source file. */ diff --git a/tests/lang/eval-fail-fromTOML-timestamps.err.exp b/tests/lang/eval-fail-fromTOML-timestamps.err.exp index f6bd19f5a..5b60d253d 100644 --- a/tests/lang/eval-fail-fromTOML-timestamps.err.exp +++ b/tests/lang/eval-fail-fromTOML-timestamps.err.exp @@ -8,5 +8,3 @@ error: 2| key = "value" error: while parsing a TOML string: Dates and times are not supported - - at «none»:0: (source not available) diff --git a/tests/lang/eval-fail-hashfile-missing.err.exp b/tests/lang/eval-fail-hashfile-missing.err.exp index 8e77dec1e..6d38608c0 100644 --- a/tests/lang/eval-fail-hashfile-missing.err.exp +++ b/tests/lang/eval-fail-hashfile-missing.err.exp @@ -10,10 +10,6 @@ error: … while evaluating the first argument passed to builtins.toString - at «none»:0: (source not available) - … while calling the 'hashFile' builtin - at «none»:0: (source not available) - error: opening file '/pwd/lang/this-file-is-definitely-not-there-7392097': No such file or directory diff --git a/tests/lang/eval-fail-set-override.err.exp b/tests/lang/eval-fail-set-override.err.exp index beb29d678..71481683d 100644 --- a/tests/lang/eval-fail-set-override.err.exp +++ b/tests/lang/eval-fail-set-override.err.exp @@ -1,6 +1,4 @@ error: … while evaluating the `__overrides` attribute - at «none»:0: (source not available) - error: value is an integer while a set was expected diff --git a/tests/lang/eval-fail-substring.err.exp b/tests/lang/eval-fail-substring.err.exp index dc26a00bd..5c58be29a 100644 --- a/tests/lang/eval-fail-substring.err.exp +++ b/tests/lang/eval-fail-substring.err.exp @@ -8,5 +8,3 @@ error: 2| error: negative start position in 'substring' - - at «none»:0: (source not available) diff --git a/tests/lang/eval-fail-to-path.err.exp b/tests/lang/eval-fail-to-path.err.exp index 43ed2bdfc..4ffa2cf6d 100644 --- a/tests/lang/eval-fail-to-path.err.exp +++ b/tests/lang/eval-fail-to-path.err.exp @@ -9,6 +9,4 @@ error: … while evaluating the first argument passed to builtins.toPath - at «none»:0: (source not available) - error: string 'foo/bar' doesn't represent an absolute path From f1aeeea32b113ac5ae9518221015a945625fa112 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 3 Sep 2023 14:00:55 +0200 Subject: [PATCH 015/116] traces: DRY printPosMaybe --- src/libutil/error.cc | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/src/libutil/error.cc b/src/libutil/error.cc index 3917fffc3..5b604c328 100644 --- a/src/libutil/error.cc +++ b/src/libutil/error.cc @@ -155,6 +155,25 @@ static std::string indent(std::string_view indentFirst, std::string_view indentR return res; } +/** + * Print a position, if it is known. + * + * @return true if a position was printed. + */ +static bool printPosMaybe(std::ostream & oss, std::string_view indent, const std::shared_ptr & pos) { + bool hasPos = pos && *pos; + if (hasPos) { + oss << "\n" << indent << ANSI_BLUE << "at " ANSI_WARNING << *pos << ANSI_NORMAL << ":"; + + if (auto loc = pos->getCodeLines()) { + oss << "\n"; + printCodeLines(oss, "", *pos, *loc); + oss << "\n"; + } + } + return hasPos; +} + std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool showTrace) { std::string prefix; @@ -318,32 +337,15 @@ std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool s oss << "\n" << "… " << trace.hint.str() << "\n"; - if (trace.pos && *trace.pos) { + if (printPosMaybe(oss, ellipsisIndent, trace.pos)) count++; - - oss << "\n" << ellipsisIndent << ANSI_BLUE << "at " ANSI_WARNING << *trace.pos << ANSI_NORMAL << ":"; - - if (auto loc = trace.pos->getCodeLines()) { - oss << "\n"; - printCodeLines(oss, "", *trace.pos, *loc); - oss << "\n"; - } - } } oss << "\n" << prefix; } oss << einfo.msg << "\n"; - if (einfo.errPos && *einfo.errPos) { - oss << "\n" << ANSI_BLUE << "at " ANSI_WARNING << *einfo.errPos << ANSI_NORMAL << ":"; - - if (auto loc = einfo.errPos->getCodeLines()) { - oss << "\n"; - printCodeLines(oss, "", *einfo.errPos, *loc); - oss << "\n"; - } - } + printPosMaybe(oss, "", einfo.errPos); auto suggestions = einfo.suggestions.trim(); if (!suggestions.suggestions.empty()) { From 477bc617bba5617d1d1da3ce039b5bd296a3e56e Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 3 Sep 2023 14:08:03 +0200 Subject: [PATCH 016/116] traces: Add _NIX_DEVELOPER_SHOW_UNKNOWN_LOCATIONS --- src/libutil/error.cc | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/libutil/error.cc b/src/libutil/error.cc index 5b604c328..dd9612471 100644 --- a/src/libutil/error.cc +++ b/src/libutil/error.cc @@ -155,6 +155,15 @@ static std::string indent(std::string_view indentFirst, std::string_view indentR return res; } +/** + * A development aid for finding missing positions, to improve error messages. Example use: + * + * NIX_DEVELOPER_SHOW_UNKNOWN_LOCATIONS=1 _NIX_TEST_ACCEPT=1 make tests/lang.sh.test + * git diff -U20 tests + * + */ +static bool printUnknownLocations = getEnv("_NIX_DEVELOPER_SHOW_UNKNOWN_LOCATIONS").has_value(); + /** * Print a position, if it is known. * @@ -170,6 +179,8 @@ static bool printPosMaybe(std::ostream & oss, std::string_view indent, const std printCodeLines(oss, "", *pos, *loc); oss << "\n"; } + } else if (printUnknownLocations) { + oss << "\n" << indent << ANSI_BLUE << "at " ANSI_RED << "UNKNOWN LOCATION" << ANSI_NORMAL << "\n"; } return hasPos; } From 7ff43435f9b23c9e0d445427cc37b147c84b056f Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 4 Sep 2023 18:15:32 -0400 Subject: [PATCH 017/116] Unit test some worker protocol serializers Continue with the characterization testing idioms begun in c70484454f52a3bba994ac0c155b4a6f35a5013c, but this time for unit tests. Co-authored-by: Andreas Rammhold --- Makefile | 1 + doc/manual/src/contributing/testing.md | 73 ++++++++- flake.nix | 1 + mk/programs.mk | 2 +- src/libstore/tests/worker-protocol.cc | 139 ++++++++++++++++++ src/libstore/worker-protocol-impl.hh | 16 ++ src/libstore/worker-protocol.hh | 4 + .../worker-protocol/content-address.bin | Bin 0 -> 208 bytes .../libstore/worker-protocol/derived-path.bin | Bin 0 -> 120 bytes .../libstore/worker-protocol/store-path.bin | Bin 0 -> 120 bytes .../libstore/worker-protocol/string.bin | Bin 0 -> 88 bytes 11 files changed, 228 insertions(+), 8 deletions(-) create mode 100644 src/libstore/tests/worker-protocol.cc create mode 100644 unit-test-data/libstore/worker-protocol/content-address.bin create mode 100644 unit-test-data/libstore/worker-protocol/derived-path.bin create mode 100644 unit-test-data/libstore/worker-protocol/store-path.bin create mode 100644 unit-test-data/libstore/worker-protocol/string.bin diff --git a/Makefile b/Makefile index 31b54b93d..2eabeb077 100644 --- a/Makefile +++ b/Makefile @@ -22,6 +22,7 @@ makefiles = \ -include Makefile.config ifeq ($(tests), yes) +UNIT_TEST_ENV = _NIX_TEST_UNIT_DATA=unit-test-data makefiles += \ src/libutil/tests/local.mk \ src/libstore/tests/local.mk \ diff --git a/doc/manual/src/contributing/testing.md b/doc/manual/src/contributing/testing.md index cd94d5cfb..329b34575 100644 --- a/doc/manual/src/contributing/testing.md +++ b/doc/manual/src/contributing/testing.md @@ -2,14 +2,70 @@ ## Unit-tests -The unit-tests for each Nix library (`libexpr`, `libstore`, etc..) are defined -under `src/{library_name}/tests` using the -[googletest](https://google.github.io/googletest/) and -[rapidcheck](https://github.com/emil-e/rapidcheck) frameworks. +The unit tests are defined using the [googletest] and [rapidcheck] frameworks. + +[googletest]: https://google.github.io/googletest/ +[rapidcheck]: https://github.com/emil-e/rapidcheck + +### Source and header layout + +> An example of some files, demonstrating much of what is described below +> +> ``` +> src +> ├── libexpr +> │ ├── value/context.hh +> │ ├── value/context.cc +> │ │ +> │ … +> └── tests +> │ ├── value/context.hh +> │ ├── value/context.cc +> │ │ +> │ … +> │ +> ├── unit-test-data +> │ ├── libstore +> │ │ ├── worker-protocol/content-address.bin +> │ │ … +> │ … +> … +> ``` + +The unit tests for each Nix library (`libnixexpr`, `libnixstore`, etc..) live inside a directory `src/${library_shortname}/tests` within the directory for the library (`src/${library_shortname}`). + +The data is in `unit-test-data`, with one subdir per library, with the same name as where the code goes. +For example, `libnixstore` code is in `src/libstore`, and its test data is in `unit-test-data/libstore`. +The path to the `unit-test-data` directory is passed to the unit test executable with the environment variable `_NIX_TEST_UNIT_DATA`. + +> **Note** +> Due to the way googletest works, downstream unit test executables will actually include and re-run upstream library tests. +> Therefore it is important that the same value for `_NIX_TEST_UNIT_DATA` be used with the tests for each library. +> That is why we have the test data nested within a single `unit-test-data` directory. + +### Running tests You can run the whole testsuite with `make check`, or the tests for a specific component with `make libfoo-tests_RUN`. Finer-grained filtering is also possible using the [--gtest_filter](https://google.github.io/googletest/advanced.html#running-a-subset-of-the-tests) command-line option, or the `GTEST_FILTER` environment variable. +### Characterization testing + +See [below](#characterization-testing-1) for a broader discussion of characterization testing. + +Like with the functional characterization, `_NIX_TEST_ACCEPT=1` is also used. +For example: +```shell-session +$ _NIX_TEST_ACCEPT=1 make libstore-tests-exe_RUN +... +[ SKIPPED ] WorkerProtoTest.string_read +[ SKIPPED ] WorkerProtoTest.string_write +[ SKIPPED ] WorkerProtoTest.storePath_read +[ SKIPPED ] WorkerProtoTest.storePath_write +... +``` +will regenerate the "golden master" expected result for the `libnixstore` characterization tests. +The characterization tests will mark themselves "skipped" since they regenerated the expected result instead of actually testing anything. + ## Functional tests The functional tests reside under the `tests` directory and are listed in `tests/local.mk`. @@ -124,9 +180,12 @@ This technique is to include the exact output/behavior of a former version of Ni For example, this technique is used for the language tests, to check both the printed final value if evaluation was successful, and any errors and warnings encountered. It is frequently useful to regenerate the expected output. -To do that, rerun the failed test with `_NIX_TEST_ACCEPT=1`. -(At least, this is the convention we've used for `tests/lang.sh`. -If we add more characterization testing we should always strive to be consistent.) +To do that, rerun the failed test(s) with `_NIX_TEST_ACCEPT=1`. +For example: +```bash +_NIX_TEST_ACCEPT=1 make tests/lang.sh.test +``` +This convention is shared with the [characterization unit tests](#characterization-testing-1) too. An interesting situation to document is the case when these tests are "overfitted". The language tests are, again, an example of this. diff --git a/flake.nix b/flake.nix index cf7c1fa12..ec6ab48bd 100644 --- a/flake.nix +++ b/flake.nix @@ -75,6 +75,7 @@ ./precompiled-headers.h ./src ./tests + ./unit-test-data ./COPYING ./scripts/local.mk (fileset.fileFilter (f: lib.strings.hasPrefix "nix-profile" f.name) ./scripts) diff --git a/mk/programs.mk b/mk/programs.mk index 1ee1d3fa5..a88d9d949 100644 --- a/mk/programs.mk +++ b/mk/programs.mk @@ -87,6 +87,6 @@ define build-program # Phony target to run this program (typically as a dependency of 'check'). .PHONY: $(1)_RUN $(1)_RUN: $$($(1)_PATH) - $(trace-test) $$($(1)_PATH) + $(trace-test) $$(UNIT_TEST_ENV) $$($(1)_PATH) endef diff --git a/src/libstore/tests/worker-protocol.cc b/src/libstore/tests/worker-protocol.cc new file mode 100644 index 000000000..4a6ccf7c0 --- /dev/null +++ b/src/libstore/tests/worker-protocol.cc @@ -0,0 +1,139 @@ +#include + +#include +#include + +#include "worker-protocol.hh" +#include "worker-protocol-impl.hh" +#include "derived-path.hh" +#include "tests/libstore.hh" + +namespace nix { + +class WorkerProtoTest : public LibStoreTest +{ +public: + Path unitTestData = getEnv("_NIX_TEST_UNIT_DATA").value() + "/libstore/worker-protocol"; + + bool testAccept() { + return getEnv("_NIX_TEST_ACCEPT") == "1"; + } + + Path goldenMaster(std::string_view testStem) { + return unitTestData + "/" + testStem + ".bin"; + } + + /** + * Golden test for `T` reading + */ + template + void readTest(PathView testStem, T value) + { + if (testAccept()) + { + GTEST_SKIP() << "Cannot read golden master because another test is also updating it"; + } + else + { + auto expected = readFile(goldenMaster(testStem)); + + T got = ({ + StringSource from { expected }; + WorkerProto::Serialise::read( + *store, + WorkerProto::ReadConn { .from = from }); + }); + + ASSERT_EQ(got, value); + } + } + + /** + * Golden test for `T` write + */ + template + void writeTest(PathView testStem, const T & value) + { + auto file = goldenMaster(testStem); + + StringSink to; + WorkerProto::write( + *store, + WorkerProto::WriteConn { .to = to }, + value); + + if (testAccept()) + { + createDirs(dirOf(file)); + writeFile(file, to.s); + GTEST_SKIP() << "Updating golden master"; + } + else + { + auto expected = readFile(file); + ASSERT_EQ(to.s, expected); + } + } +}; + +#define CHARACTERIZATION_TEST(NAME, STEM, VALUE) \ + TEST_F(WorkerProtoTest, NAME ## _read) { \ + readTest(STEM, VALUE); \ + } \ + TEST_F(WorkerProtoTest, NAME ## _write) { \ + writeTest(STEM, VALUE); \ + } + +CHARACTERIZATION_TEST( + string, + "string", + (std::tuple { + "", + "hi", + "white rabbit", + "大白兔", + "oh no \0\0\0 what was that!", + })) + +CHARACTERIZATION_TEST( + storePath, + "store-path", + (std::tuple { + StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo" }, + StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo-bar" }, + })) + +CHARACTERIZATION_TEST( + contentAddress, + "content-address", + (std::tuple { + ContentAddress { + .method = TextIngestionMethod {}, + .hash = hashString(HashType::htSHA256, "Derive(...)"), + }, + ContentAddress { + .method = FileIngestionMethod::Flat, + .hash = hashString(HashType::htSHA1, "blob blob..."), + }, + ContentAddress { + .method = FileIngestionMethod::Recursive, + .hash = hashString(HashType::htSHA256, "(...)"), + }, + })) + +CHARACTERIZATION_TEST( + derivedPath, + "derived-path", + (std::tuple { + DerivedPath::Opaque { + .path = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo" }, + }, + DerivedPath::Built { + .drvPath = makeConstantStorePathRef(StorePath { + "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar.drv", + }), + .outputs = OutputsSpec::Names { "x", "y" }, + }, + })) + +} diff --git a/src/libstore/worker-protocol-impl.hh b/src/libstore/worker-protocol-impl.hh index d3d2792ff..4f797f95a 100644 --- a/src/libstore/worker-protocol-impl.hh +++ b/src/libstore/worker-protocol-impl.hh @@ -75,4 +75,20 @@ void WorkerProto::Serialise>::write(const Store & store, WorkerPr } } +template +std::tuple WorkerProto::Serialise>::read(const Store & store, WorkerProto::ReadConn conn) +{ + return std::tuple { + WorkerProto::Serialise::read(store, conn)..., + }; +} + +template +void WorkerProto::Serialise>::write(const Store & store, WorkerProto::WriteConn conn, const std::tuple & res) +{ + std::apply([&](const Us &... args) { + (WorkerProto::Serialise::write(store, conn, args), ...); + }, res); +} + } diff --git a/src/libstore/worker-protocol.hh b/src/libstore/worker-protocol.hh index ff762c924..70a5bddb9 100644 --- a/src/libstore/worker-protocol.hh +++ b/src/libstore/worker-protocol.hh @@ -28,6 +28,8 @@ class Store; struct Source; // items being serialised +class StorePath; +struct ContentAddress; struct DerivedPath; struct DrvOutput; struct Realisation; @@ -220,6 +222,8 @@ template MAKE_WORKER_PROTO(std::vector); template MAKE_WORKER_PROTO(std::set); +template +MAKE_WORKER_PROTO(std::tuple); template #define X_ std::map diff --git a/unit-test-data/libstore/worker-protocol/content-address.bin b/unit-test-data/libstore/worker-protocol/content-address.bin new file mode 100644 index 0000000000000000000000000000000000000000..8f14bcdb3e50cc72d87106ce159a6fd7b9f75713 GIT binary patch literal 208 zcmY+;K@P$o5QSmy;|7WYrYjRqQfMm$+LWPzDW_MfG4bucKks(>Y#V56lkFOiEzi24 zUMdC5VUCa86OJ&gL0BF^B{HBQEY%f|I<6v7#q+l_PBirI5N~-md%37WE|g33QTE2k zc~-WF&R}7Oxc@o)T?ojpD!+*5=xpO;%IoCKSV5FMh={>xePK|8<${>E)*huNHZ(pX literal 0 HcmV?d00001 diff --git a/unit-test-data/libstore/worker-protocol/derived-path.bin b/unit-test-data/libstore/worker-protocol/derived-path.bin new file mode 100644 index 0000000000000000000000000000000000000000..bb1a81ac6d096cddf510a457e7506e6a81ee66a3 GIT binary patch literal 120 zcmdOAfB^lx%nJSDlKi4n{dB`}^NdR4LR_?NT7Eu*IT`wr5{vXwipmr#bSfDDBbg*z literal 0 HcmV?d00001 diff --git a/unit-test-data/libstore/worker-protocol/store-path.bin b/unit-test-data/libstore/worker-protocol/store-path.bin new file mode 100644 index 0000000000000000000000000000000000000000..3fc05f2981d3398ab30ec34125c903cdcefa8729 GIT binary patch literal 120 tcmdOAfB^lx%nJSDlKi4n{dB`}^NdR4LR_?NT7Eu*F?srQlM;)-IsqP{BM|@q literal 0 HcmV?d00001 diff --git a/unit-test-data/libstore/worker-protocol/string.bin b/unit-test-data/libstore/worker-protocol/string.bin new file mode 100644 index 0000000000000000000000000000000000000000..aa7b5a604745b735473ec34283177e69320776e4 GIT binary patch literal 88 zcmZQzfB+^aoskJ)@Id+H8JQ)i3Pp)YNtq=eAx^0H( Date: Tue, 12 Sep 2023 13:21:55 +0200 Subject: [PATCH 018/116] libexpr: Add nrExprs to NIX_SHOW_STATS --- src/libexpr/eval.cc | 1 + src/libexpr/nixexpr.hh | 4 ++++ src/libexpr/parser.y | 1 + 3 files changed, 6 insertions(+) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 6d445fd96..afa864730 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -2506,6 +2506,7 @@ void EvalState::printStats() {"elements", nrValuesInEnvs}, {"bytes", bEnvs}, }; + topObj["nrExprs"] = Expr::nrExprs; topObj["list"] = { {"elements", nrListElems}, {"bytes", bLists}, diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index 5ca3d1fa6..b80c07f46 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -155,6 +155,10 @@ std::string showAttrPath(const SymbolTable & symbols, const AttrPath & attrPath) struct Expr { + static unsigned long nrExprs; + inline Expr() { + nrExprs++; + } virtual ~Expr() { }; virtual void show(const SymbolTable & symbols, std::ostream & str) const; virtual void bindVars(EvalState & es, const std::shared_ptr & env); diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 792f51fde..a3d3c7041 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -653,6 +653,7 @@ formal namespace nix { +unsigned long Expr::nrExprs = 0; Expr * EvalState::parse( char * text, From bf8deb4991286a44e1b6cc2142721a594276dd12 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 12 Sep 2023 13:45:45 +0200 Subject: [PATCH 019/116] Expr: remove redundant int and float fields --- src/libexpr/nixexpr.cc | 4 ++-- src/libexpr/nixexpr.hh | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index 4566a1388..22be8e68c 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -76,12 +76,12 @@ void Expr::show(const SymbolTable & symbols, std::ostream & str) const void ExprInt::show(const SymbolTable & symbols, std::ostream & str) const { - str << n; + str << v.integer; } void ExprFloat::show(const SymbolTable & symbols, std::ostream & str) const { - str << nf; + str << v.fpoint; } void ExprString::show(const SymbolTable & symbols, std::ostream & str) const diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index b80c07f46..17b2d1136 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -175,18 +175,16 @@ struct Expr struct ExprInt : Expr { - NixInt n; Value v; - ExprInt(NixInt n) : n(n) { v.mkInt(n); }; + ExprInt(NixInt n) { v.mkInt(n); }; Value * maybeThunk(EvalState & state, Env & env) override; COMMON_METHODS }; struct ExprFloat : Expr { - NixFloat nf; Value v; - ExprFloat(NixFloat nf) : nf(nf) { v.mkFloat(nf); }; + ExprFloat(NixFloat nf) { v.mkFloat(nf); }; Value * maybeThunk(EvalState & state, Env & env) override; COMMON_METHODS }; From e44d2a6bbef113b3d8f162f75bd5c94e0101075f Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 30 Aug 2023 22:57:59 -0400 Subject: [PATCH 020/116] Add FreeBSD and NetBSD cross to Nix's flake --- flake.nix | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/flake.nix b/flake.nix index dfb2dc1f8..871f0d7ba 100644 --- a/flake.nix +++ b/flake.nix @@ -25,8 +25,11 @@ linuxSystems = linux32BitSystems ++ linux64BitSystems; darwinSystems = [ "x86_64-darwin" "aarch64-darwin" ]; systems = linuxSystems ++ darwinSystems; - - crossSystems = [ "armv6l-linux" "armv7l-linux" ]; + + crossSystems = [ + "armv6l-linux" "armv7l-linux" + "x86_64-freebsd13" "x86_64-netbsd" + ]; stdenvs = [ "gccStdenv" "clangStdenv" "clang11Stdenv" "stdenv" "libcxxStdenv" "ccacheStdenv" ]; @@ -94,7 +97,14 @@ nixpkgsFor = forAllSystems (system: let make-pkgs = crossSystem: stdenv: import nixpkgs { - inherit system crossSystem; + localSystem = { + inherit system; + }; + crossSystem = if crossSystem == null then null else { + system = crossSystem; + } // lib.optionalAttrs (crossSystem == "x86_64-freebsd13") { + useLLVM = true; + }; overlays = [ (overlayFor (p: p.${stdenv})) ]; From 28850ee90095e337bf47fbe03a0d937cb98784e2 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 30 Aug 2023 23:27:53 -0400 Subject: [PATCH 021/116] Make dev shells work for cross Need to get tools from right package set. Could build clang tools but I don't want to wait :D. --- flake.nix | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index 871f0d7ba..24d9520ea 100644 --- a/flake.nix +++ b/flake.nix @@ -750,7 +750,11 @@ outputs = [ "out" "dev" "doc" ]; nativeBuildInputs = nativeBuildDeps - ++ (lib.optionals stdenv.cc.isClang [ pkgs.bear pkgs.clang-tools ]); + ++ lib.optional stdenv.cc.isClang pkgs.buildPackages.bear + ++ lib.optional + (stdenv.cc.isClang && stdenv.hostPlatform == stdenv.buildPlatform) + pkgs.buildPackages.clang-tools + ; buildInputs = buildDeps ++ propagatedDeps ++ awsDeps ++ checkDeps ++ internalApiDocsDeps; From 564392b57bc44c407303e4eaf6138d61a8ea50b0 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 30 Aug 2023 23:29:17 -0400 Subject: [PATCH 022/116] Make libsodium an unconditional dependency The configure script will not tolerate it being missing. --- flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index 24d9520ea..50a226347 100644 --- a/flake.nix +++ b/flake.nix @@ -190,9 +190,9 @@ libarchive boost lowdown-nix + libsodium ] ++ lib.optionals stdenv.isLinux [libseccomp] - ++ lib.optional (stdenv.isLinux || stdenv.isDarwin) libsodium ++ lib.optional stdenv.hostPlatform.isx86_64 libcpuid; checkDeps = [ From 0db251e4ad07338375fd59134fb467e9ebc4176a Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 30 Aug 2023 23:43:48 -0400 Subject: [PATCH 023/116] Do not build docs in cross devShell Coppied from the main build; we really should deduplicate this more. --- flake.nix | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index 50a226347..36937057d 100644 --- a/flake.nix +++ b/flake.nix @@ -743,6 +743,9 @@ devShells = let makeShell = pkgs: stdenv: + let + canRunInstalled = stdenv.buildPlatform.canExecute stdenv.hostPlatform; + in with commonDeps { inherit pkgs; }; stdenv.mkDerivation { name = "nix"; @@ -760,7 +763,8 @@ ++ awsDeps ++ checkDeps ++ internalApiDocsDeps; configureFlags = configureFlags - ++ testConfigureFlags ++ internalApiDocsConfigureFlags; + ++ testConfigureFlags ++ internalApiDocsConfigureFlags + ++ lib.optional (!canRunInstalled) "--disable-doc-gen"; enableParallelBuilding = true; From 7f76d7f038fbb5cb7982cf9aa951b9730566e275 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 30 Aug 2023 23:44:23 -0400 Subject: [PATCH 024/116] Rename an identifier of ours called `stdout` This is a reserved identifier on NetBSD --- it is replaced by a macro on that platform --- and so we cannot use it. --- src/libmain/shared.cc | 6 +++--- src/libmain/shared.hh | 7 ++++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index 56f47a4ac..9c2ad039a 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -379,9 +379,9 @@ RunPager::RunPager() }); pid.setKillSignal(SIGINT); - stdout = fcntl(STDOUT_FILENO, F_DUPFD_CLOEXEC, 0); + std_out = fcntl(STDOUT_FILENO, F_DUPFD_CLOEXEC, 0); if (dup2(toPager.writeSide.get(), STDOUT_FILENO) == -1) - throw SysError("dupping stdout"); + throw SysError("dupping standard output"); } @@ -390,7 +390,7 @@ RunPager::~RunPager() try { if (pid != -1) { std::cout.flush(); - dup2(stdout, STDOUT_FILENO); + dup2(std_out, STDOUT_FILENO); pid.wait(); } } catch (...) { diff --git a/src/libmain/shared.hh b/src/libmain/shared.hh index 7a9e83c6c..9415be78a 100644 --- a/src/libmain/shared.hh +++ b/src/libmain/shared.hh @@ -85,8 +85,9 @@ struct LegacyArgs : public MixCommonArgs void showManPage(const std::string & name); /** - * The constructor of this class starts a pager if stdout is a - * terminal and $PAGER is set. Stdout is redirected to the pager. + * The constructor of this class starts a pager if standard output is a + * terminal and $PAGER is set. Standard output is redirected to the + * pager. */ class RunPager { @@ -96,7 +97,7 @@ public: private: Pid pid; - int stdout; + int std_out; }; extern volatile ::sig_atomic_t blockInt; From c18911602eb4260d59acf8c17f1c3b4c7fcf7cee Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 31 Aug 2023 00:07:49 -0400 Subject: [PATCH 025/116] Fix `boehmgc-coroutine-sp-fallback.diff` for FreeBSD Our FreeBSD headers have `pthread_getattr_np`, but we get a link-time error that is missing. The good news is that there is another similar function which does exist, and the upstream project elsewhere does just the [fallback code] we need. As the fallback code indicates, the two functions are not identical however as the other one needs explicit initialization. NetBSD supports both in fact, and its [manpage] is therefore a good resource on what the differences are. [fallback code]: https://github.com/ivmai/bdwgc/blob/07a6d0ee8889bca5eaeadc13cabadc363725d216/os_dep.c#L1266-L1272 [manpage]: https://man.netbsd.org/pthread_attr_get_np.3 --- boehmgc-coroutine-sp-fallback.diff | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/boehmgc-coroutine-sp-fallback.diff b/boehmgc-coroutine-sp-fallback.diff index 5066d8278..2afbe9671 100644 --- a/boehmgc-coroutine-sp-fallback.diff +++ b/boehmgc-coroutine-sp-fallback.diff @@ -59,12 +59,18 @@ index b5d71e62..aed7b0bf 100644 GC_bool found_me = FALSE; size_t nthreads = 0; int i; -@@ -851,6 +853,31 @@ GC_INNER void GC_push_all_stacks(void) +@@ -851,6 +853,37 @@ GC_INNER void GC_push_all_stacks(void) hi = p->altstack + p->altstack_size; /* FIXME: Need to scan the normal stack too, but how ? */ /* FIXME: Assume stack grows down */ + } else { -+ if (pthread_getattr_np(p->id, &pattr)) { ++#ifdef HAVE_PTHREAD_ATTR_GET_NP ++ if (!pthread_attr_init(&pattr) ++ || !pthread_attr_get_np(p->id, &pattr)) ++#else /* HAVE_PTHREAD_GETATTR_NP */ ++ if (pthread_getattr_np(p->id, &pattr)) ++#endif ++ { + ABORT("GC_push_all_stacks: pthread_getattr_np failed!"); + } + if (pthread_attr_getstacksize(&pattr, &stack_limit)) { From 954890a42f143896d5892066f7b5a0f2b120fbfe Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Thu, 21 Sep 2023 02:52:05 +0200 Subject: [PATCH 026/116] add information on release cycle and backports --- doc/manual/src/release-notes/release-notes.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/manual/src/release-notes/release-notes.md b/doc/manual/src/release-notes/release-notes.md index b05d5ee0a..dbda0babd 100644 --- a/doc/manual/src/release-notes/release-notes.md +++ b/doc/manual/src/release-notes/release-notes.md @@ -1 +1,7 @@ # Nix Release Notes + +Nix has a release cycle of roughly 6 weeks. +Notable changes and additions are announced in the release notes for each version. + +Bugfixes can be backported on request to the Nix version used in the latest NixOS release, which is announced in the [NixOS release notes](https://nixos.org/manual/nixos/stable/release-notes.html#ch-release-notes). + From 8e25450ff4bbf2b2c6951ebc78fd1bdec7097660 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Thu, 21 Sep 2023 02:53:11 +0200 Subject: [PATCH 027/116] refer to nix.dev for installation instructions there are currently multiple places with installation instructions that all have to be updated when a change to any of them is accepted. this reduces the number of places by one, and directs beginners to the maintained and curated resource for Nix learning materials. --- README.md | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 85b0902b1..1440239bc 100644 --- a/README.md +++ b/README.md @@ -7,16 +7,11 @@ Nix is a powerful package manager for Linux and other Unix systems that makes pa management reliable and reproducible. Please refer to the [Nix manual](https://nixos.org/nix/manual) for more details. -## Installation +## Installation and first steps -On Linux and macOS the easiest way to install Nix is to run the following shell command -(as a user other than root): +Visit [nix.dev](https://nix.dev) for installation instructions and beginner tutorials. -```console -$ curl -L https://nixos.org/nix/install | sh -``` - -Information on additional installation methods is available on the [Nix download page](https://nixos.org/download.html). +Full reference documentation can be found in the [Nix manual](https://nixos.org/nix/manual). ## Building And Developing From 1b560ea502cf0308b829e4b3425307f80fc3528a Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Thu, 21 Sep 2023 23:23:14 +0200 Subject: [PATCH 028/116] more detail on backports Co-authored-by: John Ericson --- doc/manual/src/release-notes/release-notes.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/doc/manual/src/release-notes/release-notes.md b/doc/manual/src/release-notes/release-notes.md index dbda0babd..1fa2f5580 100644 --- a/doc/manual/src/release-notes/release-notes.md +++ b/doc/manual/src/release-notes/release-notes.md @@ -3,5 +3,9 @@ Nix has a release cycle of roughly 6 weeks. Notable changes and additions are announced in the release notes for each version. -Bugfixes can be backported on request to the Nix version used in the latest NixOS release, which is announced in the [NixOS release notes](https://nixos.org/manual/nixos/stable/release-notes.html#ch-release-notes). +Bugfixes can be backported on request to previous Nix releases. +We typically backport only as far back as the Nix version used in the latest NixOS release, which is announced in the [NixOS release notes](https://nixos.org/manual/nixos/stable/release-notes.html#ch-release-notes). + +Backports never skip releases (if a feature is backported to `x.y`, it must also be available in `x.(y+1)`; +This ensures that upgrading from an older version with backports is still safe and no backported functionality will go missing. From f264d9ff0862277523a207c7afaed4894a40dc11 Mon Sep 17 00:00:00 2001 From: Gerg-L Date: Thu, 21 Sep 2023 21:00:35 -0400 Subject: [PATCH 029/116] flake: complete update to 23.05 --- flake.lock | 12 ++++++------ flake.nix | 3 +-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/flake.lock b/flake.lock index 75b6ae6a7..56df9c3fb 100644 --- a/flake.lock +++ b/flake.lock @@ -34,16 +34,16 @@ }, "nixpkgs": { "locked": { - "lastModified": 1695124524, - "narHash": "sha256-trXDytVCqf3KryQQQrHOZKUabu1/lB8/ndOAuZKQrOE=", - "owner": "edolstra", + "lastModified": 1695283060, + "narHash": "sha256-CJz71xhCLlRkdFUSQEL0pIAAfcnWFXMzd9vXhPrnrEg=", + "owner": "NixOS", "repo": "nixpkgs", - "rev": "a3d30b525535e3158221abc1a957ce798ab159fe", + "rev": "31ed632c692e6a36cfc18083b88ece892f863ed4", "type": "github" }, "original": { - "owner": "edolstra", - "ref": "fix-aws-sdk-cpp", + "owner": "NixOS", + "ref": "nixos-23.05-small", "repo": "nixpkgs", "type": "github" } diff --git a/flake.nix b/flake.nix index dfb2dc1f8..a7680a759 100644 --- a/flake.nix +++ b/flake.nix @@ -1,8 +1,7 @@ { description = "The purely functional package manager"; - #inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.05-small"; - inputs.nixpkgs.url = "github:edolstra/nixpkgs/fix-aws-sdk-cpp"; + inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.05-small"; inputs.nixpkgs-regression.url = "github:NixOS/nixpkgs/215d4d0fd80ca5163643b03a33fde804a29cc1e2"; inputs.lowdown-src = { url = "github:kristapsdz/lowdown"; flake = false; }; inputs.flake-compat = { url = "github:edolstra/flake-compat"; flake = false; }; From d8cebae939932f2a5b458b5d92c82cd1a025dc0a Mon Sep 17 00:00:00 2001 From: Rasmus Rendal Date: Sat, 4 Jun 2022 12:07:08 +0200 Subject: [PATCH 030/116] Add a test for flake paths with spaces in them --- tests/flakes/common.sh | 10 +- tests/flakes/flakes.sh | 302 +++++++++++++++++++++-------------------- 2 files changed, 157 insertions(+), 155 deletions(-) diff --git a/tests/flakes/common.sh b/tests/flakes/common.sh index 427abcdde..8aed296e6 100644 --- a/tests/flakes/common.sh +++ b/tests/flakes/common.sh @@ -61,10 +61,10 @@ createGitRepo() { local repo="$1" local extraArgs="${2-}" - rm -rf $repo $repo.tmp - mkdir -p $repo + rm -rf "$repo" "$repo".tmp + mkdir -p "$repo" - git -C $repo init $extraArgs - git -C $repo config user.email "foobar@example.com" - git -C $repo config user.name "Foobar" + git -C "$repo" init $extraArgs + git -C "$repo" config user.email "foobar@example.com" + git -C "$repo" config user.name "Foobar" } diff --git a/tests/flakes/flakes.sh b/tests/flakes/flakes.sh index 128f759ea..70de28628 100644 --- a/tests/flakes/flakes.sh +++ b/tests/flakes/flakes.sh @@ -6,27 +6,29 @@ clearStore rm -rf $TEST_HOME/.cache $TEST_HOME/.config flake1Dir=$TEST_ROOT/flake1 -flake2Dir=$TEST_ROOT/flake2 -flake3Dir=$TEST_ROOT/flake3 +flake2Dir=$TEST_ROOT/flake\ 2 +percentEncodedFlake2Dir=$TEST_ROOT/flake%202 +flake3Dir=$TEST_ROOT/flake%20 +percentEncodedFlake3Dir=$TEST_ROOT/flake%2520 flake5Dir=$TEST_ROOT/flake5 flake7Dir=$TEST_ROOT/flake7 nonFlakeDir=$TEST_ROOT/nonFlake badFlakeDir=$TEST_ROOT/badFlake flakeGitBare=$TEST_ROOT/flakeGitBare -for repo in $flake1Dir $flake2Dir $flake3Dir $flake7Dir $nonFlakeDir; do +for repo in "$flake1Dir" "$flake2Dir" "$flake3Dir" "$flake7Dir" "$nonFlakeDir"; do # Give one repo a non-main initial branch. extraArgs= - if [[ $repo == $flake2Dir ]]; then + if [[ "$repo" == "$flake2Dir" ]]; then extraArgs="--initial-branch=main" fi createGitRepo "$repo" "$extraArgs" done -createSimpleGitFlake $flake1Dir +createSimpleGitFlake "$flake1Dir" -cat > $flake2Dir/flake.nix < "$flake2Dir/flake.nix" < $flake2Dir/flake.nix < $flake3Dir/flake.nix < "$flake3Dir/flake.nix" < $flake3Dir/flake.nix < $flake3Dir/default.nix < "$flake3Dir/default.nix" < $nonFlakeDir/README.md < "$nonFlakeDir/README.md" < $flake1Dir/foo -git -C $flake1Dir add $flake1Dir/foo +echo foo > "$flake1Dir/foo" +git -C "$flake1Dir" add $flake1Dir/foo [[ $(nix flake metadata flake1 --json --refresh | jq -r .dirtyRevision) == "$hash1-dirty" ]] -echo -n '# foo' >> $flake1Dir/flake.nix -flake1OriginalCommit=$(git -C $flake1Dir rev-parse HEAD) -git -C $flake1Dir commit -a -m 'Foo' -flake1NewCommit=$(git -C $flake1Dir rev-parse HEAD) +echo -n '# foo' >> "$flake1Dir/flake.nix" +flake1OriginalCommit=$(git -C "$flake1Dir" rev-parse HEAD) +git -C "$flake1Dir" commit -a -m 'Foo' +flake1NewCommit=$(git -C "$flake1Dir" rev-parse HEAD) hash2=$(nix flake metadata flake1 --json --refresh | jq -r .revision) [[ $(nix flake metadata flake1 --json --refresh | jq -r .dirtyRevision) == "null" ]] [[ $hash1 != $hash2 ]] # Test 'nix build' on a flake. -nix build -o $TEST_ROOT/result flake1#foo -[[ -e $TEST_ROOT/result/hello ]] +nix build -o "$TEST_ROOT/result" flake1#foo +[[ -e "$TEST_ROOT/result/hello" ]] # Test packages.default. -nix build -o $TEST_ROOT/result flake1 -[[ -e $TEST_ROOT/result/hello ]] +nix build -o "$TEST_ROOT/result" flake1 +[[ -e "$TEST_ROOT/result/hello" ]] -nix build -o $TEST_ROOT/result $flake1Dir -nix build -o $TEST_ROOT/result git+file://$flake1Dir +nix build -o "$TEST_ROOT/result" "$flake1Dir" +nix build -o "$TEST_ROOT/result" "git+file://$flake1Dir" # Check that store symlinks inside a flake are not interpreted as flakes. -nix build -o $flake1Dir/result git+file://$flake1Dir -nix path-info $flake1Dir/result +nix build -o "$flake1Dir/result" "git+file://$flake1Dir" +nix path-info "$flake1Dir/result" # 'getFlake' on an unlocked flakeref should fail in pure mode, but # succeed in impure mode. -(! nix build -o $TEST_ROOT/result --expr "(builtins.getFlake \"$flake1Dir\").packages.$system.default") -nix build -o $TEST_ROOT/result --expr "(builtins.getFlake \"$flake1Dir\").packages.$system.default" --impure +(! nix build -o "$TEST_ROOT/result" --expr "(builtins.getFlake \"$flake1Dir\").packages.$system.default") +nix build -o "$TEST_ROOT/result" --expr "(builtins.getFlake \"$flake1Dir\").packages.$system.default" --impure # 'getFlake' on a locked flakeref should succeed even in pure mode. -nix build -o $TEST_ROOT/result --expr "(builtins.getFlake \"git+file://$flake1Dir?rev=$hash2\").packages.$system.default" +nix build -o "$TEST_ROOT/result" --expr "(builtins.getFlake \"git+file://$flake1Dir?rev=$hash2\").packages.$system.default" # Building a flake with an unlocked dependency should fail in pure mode. -(! nix build -o $TEST_ROOT/result flake2#bar --no-registries) -(! nix build -o $TEST_ROOT/result flake2#bar --no-use-registries) +(! nix build -o "$TEST_ROOT/result" flake2#bar --no-registries) +(! nix build -o "$TEST_ROOT/result" flake2#bar --no-use-registries) (! nix eval --expr "builtins.getFlake \"$flake2Dir\"") # But should succeed in impure mode. -(! nix build -o $TEST_ROOT/result flake2#bar --impure) -nix build -o $TEST_ROOT/result flake2#bar --impure --no-write-lock-file +(! nix build -o "$TEST_ROOT/result" flake2#bar --impure) +nix build -o "$TEST_ROOT/result" flake2#bar --impure --no-write-lock-file nix eval --expr "builtins.getFlake \"$flake2Dir\"" --impure # Building a local flake with an unlocked dependency should fail with --no-update-lock-file. -expect 1 nix build -o $TEST_ROOT/result $flake2Dir#bar --no-update-lock-file 2>&1 | grep 'requires lock file changes' +expect 1 nix build -o "$TEST_ROOT/result" "$flake2Dir#bar" --no-update-lock-file 2>&1 | grep 'requires lock file changes' # But it should succeed without that flag. -nix build -o $TEST_ROOT/result $flake2Dir#bar --no-write-lock-file -expect 1 nix build -o $TEST_ROOT/result $flake2Dir#bar --no-update-lock-file 2>&1 | grep 'requires lock file changes' -nix build -o $TEST_ROOT/result $flake2Dir#bar --commit-lock-file -[[ -e $flake2Dir/flake.lock ]] -[[ -z $(git -C $flake2Dir diff main || echo failed) ]] +nix build -o "$TEST_ROOT/result" "$flake2Dir#bar" --no-write-lock-file +expect 1 nix build -o "$TEST_ROOT/result" "$flake2Dir#bar" --no-update-lock-file 2>&1 | grep 'requires lock file changes' +nix build -o "$TEST_ROOT/result" "$flake2Dir#bar" --commit-lock-file +[[ -e "$flake2Dir/flake.lock" ]] +[[ -z $(git -C "$flake2Dir" diff main || echo failed) ]] # Rerunning the build should not change the lockfile. -nix build -o $TEST_ROOT/result $flake2Dir#bar -[[ -z $(git -C $flake2Dir diff main || echo failed) ]] +nix build -o "$TEST_ROOT/result" "$flake2Dir#bar" +[[ -z $(git -C "$flake2Dir" diff main || echo failed) ]] # Building with a lockfile should not require a fetch of the registry. -nix build -o $TEST_ROOT/result --flake-registry file:///no-registry.json $flake2Dir#bar --refresh -nix build -o $TEST_ROOT/result --no-registries $flake2Dir#bar --refresh -nix build -o $TEST_ROOT/result --no-use-registries $flake2Dir#bar --refresh +nix build -o "$TEST_ROOT/result" --flake-registry file:///no-registry.json "$flake2Dir#bar" --refresh +nix build -o "$TEST_ROOT/result" --no-registries "$flake2Dir#bar" --refresh +nix build -o "$TEST_ROOT/result" --no-use-registries "$flake2Dir#bar" --refresh # Updating the flake should not change the lockfile. -nix flake lock $flake2Dir -[[ -z $(git -C $flake2Dir diff main || echo failed) ]] +nix flake lock "$flake2Dir" +[[ -z $(git -C "$flake2Dir" diff main || echo failed) ]] # Now we should be able to build the flake in pure mode. -nix build -o $TEST_ROOT/result flake2#bar +nix build -o "$TEST_ROOT/result" flake2#bar # Or without a registry. -nix build -o $TEST_ROOT/result --no-registries git+file://$flake2Dir#bar --refresh -nix build -o $TEST_ROOT/result --no-use-registries git+file://$flake2Dir#bar --refresh +nix build -o "$TEST_ROOT/result" --no-registries "git+file://$percentEncodedFlake2Dir#bar" --refresh +nix build -o "$TEST_ROOT/result" --no-use-registries "git+file://$percentEncodedFlake2Dir#bar" --refresh # Test whether indirect dependencies work. -nix build -o $TEST_ROOT/result $flake3Dir#xyzzy -git -C $flake3Dir add flake.lock +nix build -o "$TEST_ROOT/result" "$flake3Dir#xyzzy" +git -C "$flake3Dir" add flake.lock # Add dependency to flake3. -rm $flake3Dir/flake.nix +rm "$flake3Dir/flake.nix" -cat > $flake3Dir/flake.nix < "$flake3Dir/flake.nix" < $flake3Dir/flake.nix < $flake3Dir/flake.nix < "$flake3Dir/flake.nix" < $flake3Dir/flake.nix < $flake3Dir/flake.nix < "$flake3Dir/flake.nix" < $flake3Dir/flake.nix < $flake3Dir/flake.nix < "$flake3Dir/flake.nix" < $flake3Dir/flake.nix < $flake3Dir/flake.nix < "$flake3Dir/flake.nix" < $flake3Dir/flake.nix < $flake3Dir/flake.nix < "$flake3Dir/flake.nix" < $flake3Dir/flake.nix < $flake3Dir/flake.nix < "$flake3Dir/flake.nix" < $flake3Dir/flake.nix < $flake3Dir/flake.nix < "$flake3Dir/flake.nix" < $flake3Dir/flake.nix <' -A x) = 123 ]] [[ $(NIX_PATH=flake3=flake:flake3 nix-instantiate --eval '' -A x) = 123 ]] # Test alternate lockfile paths. -nix flake lock $flake2Dir --output-lock-file $TEST_ROOT/flake2.lock -cmp $flake2Dir/flake.lock $TEST_ROOT/flake2.lock >/dev/null # lockfiles should be identical, since we're referencing flake2's original one +nix flake lock "$flake2Dir" --output-lock-file $TEST_ROOT/flake2.lock +cmp "$flake2Dir/flake.lock" $TEST_ROOT/flake2.lock >/dev/null # lockfiles should be identical, since we're referencing flake2's original one -nix flake lock $flake2Dir --output-lock-file $TEST_ROOT/flake2-overridden.lock --override-input flake1 git+file://$flake1Dir?rev=$flake1OriginalCommit -expectStderr 1 cmp $flake2Dir/flake.lock $TEST_ROOT/flake2-overridden.lock -nix flake metadata $flake2Dir --reference-lock-file $TEST_ROOT/flake2-overridden.lock | grepQuiet $flake1OriginalCommit +nix flake lock "$flake2Dir" --output-lock-file $TEST_ROOT/flake2-overridden.lock --override-input flake1 git+file://$flake1Dir?rev=$flake1OriginalCommit +expectStderr 1 cmp "$flake2Dir/flake.lock" $TEST_ROOT/flake2-overridden.lock +nix flake metadata "$flake2Dir" --reference-lock-file $TEST_ROOT/flake2-overridden.lock | grepQuiet $flake1OriginalCommit # reference-lock-file can only be used if allow-dirty is set. -expectStderr 1 nix flake metadata $flake2Dir --no-allow-dirty --reference-lock-file $TEST_ROOT/flake2-overridden.lock +expectStderr 1 nix flake metadata "$flake2Dir" --no-allow-dirty --reference-lock-file $TEST_ROOT/flake2-overridden.lock From 50e61f579cfc8cf64031845f3e73f82593ad2ff3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Wed, 31 May 2023 10:36:43 +0200 Subject: [PATCH 031/116] Allow special characters in flake paths Support using nix flakes in paths with spaces or abitrary unicode characters. This introduces the convention that the path part of the URL should be percent-encoded when dealing with `path:` urls and not when using filepaths (following the convention of firefox). Co-authored-by: Rendal --- src/libexpr/flake/flakeref.cc | 85 +++++++++++++++++++---------------- src/libutil/tests/url.cc | 9 ++++ src/libutil/url.cc | 2 +- 3 files changed, 57 insertions(+), 39 deletions(-) diff --git a/src/libexpr/flake/flakeref.cc b/src/libexpr/flake/flakeref.cc index e1bce90bc..2155c5f12 100644 --- a/src/libexpr/flake/flakeref.cc +++ b/src/libexpr/flake/flakeref.cc @@ -77,14 +77,6 @@ std::pair parseFlakeRefWithFragment( { using namespace fetchers; - static std::string fnRegex = "[0-9a-zA-Z-._~!$&'\"()*+,;=]+"; - - static std::regex pathUrlRegex( - "(/?" + fnRegex + "(?:/" + fnRegex + ")*/?)" - + "(?:\\?(" + queryRegex + "))?" - + "(?:#(" + queryRegex + "))?", - std::regex::ECMAScript); - static std::regex flakeRegex( "((" + flakeIdRegexS + ")(?:/(?:" + refAndOrRevRegex + "))?)" + "(?:#(" + queryRegex + "))?", @@ -92,26 +84,23 @@ std::pair parseFlakeRefWithFragment( std::smatch match; - /* Check if 'url' is a flake ID. This is an abbreviated syntax for - 'flake:?ref=&rev='. */ - - if (std::regex_match(url, match, flakeRegex)) { - auto parsedURL = ParsedURL{ - .url = url, - .base = "flake:" + match.str(1), - .scheme = "flake", - .authority = "", - .path = match[1], - }; - - return std::make_pair( - FlakeRef(Input::fromURL(parsedURL, isFlake), ""), - percentDecode(match.str(6))); - } - - else if (std::regex_match(url, match, pathUrlRegex)) { - std::string path = match[1]; - std::string fragment = percentDecode(match.str(3)); + auto parsePathFlakeRef = [&]() { + std::string path = url; + std::string fragment = ""; + std::map query = {}; + auto pathEnd = url.find_first_of("#?"); + auto fragmentStart = pathEnd; + if (pathEnd != std::string::npos && url[pathEnd] == '?') + fragmentStart = url.find("#"); + if (pathEnd != std::string::npos) { + path = url.substr(0, pathEnd); + } + if (fragmentStart != std::string::npos) { + fragment = percentDecode(url.substr(fragmentStart+1)); + } + if (fragmentStart != std::string::npos && pathEnd != std::string::npos) { + query = decodeQuery(url.substr(pathEnd+1, fragmentStart)); + } if (baseDir) { /* Check if 'url' is a path (either absolute or relative @@ -163,7 +152,7 @@ std::pair parseFlakeRefWithFragment( .scheme = "git+file", .authority = "", .path = flakeRoot, - .query = decodeQuery(match[2]), + .query = query, }; if (subdir != "") { @@ -188,7 +177,6 @@ std::pair parseFlakeRefWithFragment( } else { if (!hasPrefix(path, "/")) throw BadURL("flake reference '%s' is not an absolute path", url); - auto query = decodeQuery(match[2]); path = canonPath(path + "/" + getOr(query, "dir", "")); } @@ -197,19 +185,40 @@ std::pair parseFlakeRefWithFragment( attrs.insert_or_assign("path", path); return std::make_pair(FlakeRef(Input::fromAttrs(std::move(attrs)), ""), fragment); + }; + + /* Check if 'url' is a flake ID. This is an abbreviated syntax for + 'flake:?ref=&rev='. */ + + if (std::regex_match(url, match, flakeRegex)) { + auto parsedURL = ParsedURL{ + .url = url, + .base = "flake:" + match.str(1), + .scheme = "flake", + .authority = "", + .path = match[1], + }; + + return std::make_pair( + FlakeRef(Input::fromURL(parsedURL), ""), + percentDecode(match.str(6))); } else { - auto parsedURL = parseURL(url); - std::string fragment; - std::swap(fragment, parsedURL.fragment); + try { + auto parsedURL = parseURL(url); + std::string fragment; + std::swap(fragment, parsedURL.fragment); - auto input = Input::fromURL(parsedURL, isFlake); - input.parent = baseDir; + auto input = Input::fromURL(parsedURL, isFlake); + input.parent = baseDir; - return std::make_pair( - FlakeRef(std::move(input), getOr(parsedURL.query, "dir", "")), - fragment); + return std::make_pair( + FlakeRef(std::move(input), getOr(parsedURL.query, "dir", "")), + fragment); + } catch (BadURL &) { + return parsePathFlakeRef(); + } } } diff --git a/src/libutil/tests/url.cc b/src/libutil/tests/url.cc index a908631e6..a678dad20 100644 --- a/src/libutil/tests/url.cc +++ b/src/libutil/tests/url.cc @@ -335,4 +335,13 @@ namespace nix { ASSERT_EQ(d, s); } + TEST(percentEncode, yen) { + // https://en.wikipedia.org/wiki/Percent-encoding#Character_data + std::string s = reinterpret_cast(u8"円"); + std::string e = "%E5%86%86"; + + ASSERT_EQ(percentEncode(s), e); + ASSERT_EQ(percentDecode(e), s); + } + } diff --git a/src/libutil/url.cc b/src/libutil/url.cc index a8f7d39fd..e700c8eaf 100644 --- a/src/libutil/url.cc +++ b/src/libutil/url.cc @@ -103,7 +103,7 @@ std::string percentEncode(std::string_view s, std::string_view keep) || keep.find(c) != std::string::npos) res += c; else - res += fmt("%%%02X", (unsigned int) c); + res += fmt("%%%02X", c & 0xFF); return res; } From e8113747e1abf7fa6225f7fcd31ca9e71e03cfbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Thu, 20 Apr 2023 06:51:47 +0200 Subject: [PATCH 032/116] Split the parseFlakeRefWithFragment function Was starting to be very complex and hard to follow. Now the different cases should be easier to understand. --- src/libexpr/flake/flakeref.cc | 279 +++++++++++++++++++--------------- 1 file changed, 155 insertions(+), 124 deletions(-) diff --git a/src/libexpr/flake/flakeref.cc b/src/libexpr/flake/flakeref.cc index 2155c5f12..f7ef19b3f 100644 --- a/src/libexpr/flake/flakeref.cc +++ b/src/libexpr/flake/flakeref.cc @@ -69,127 +69,130 @@ std::optional maybeParseFlakeRef( } } -std::pair parseFlakeRefWithFragment( +std::pair parsePathFlakeRefWithFragment( const std::string & url, const std::optional & baseDir, bool allowMissing, bool isFlake) { - using namespace fetchers; + std::string path = url; + std::string fragment = ""; + std::map query; + auto pathEnd = url.find_first_of("#?"); + auto fragmentStart = pathEnd; + if (pathEnd != std::string::npos && url[pathEnd] == '?') { + fragmentStart = url.find("#"); + } + if (pathEnd != std::string::npos) { + path = url.substr(0, pathEnd); + } + if (fragmentStart != std::string::npos) { + fragment = percentDecode(url.substr(fragmentStart+1)); + } + if (pathEnd != std::string::npos && fragmentStart != std::string::npos) { + query = decodeQuery(url.substr(pathEnd+1, fragmentStart)); + } + + if (baseDir) { + /* Check if 'url' is a path (either absolute or relative + to 'baseDir'). If so, search upward to the root of the + repo (i.e. the directory containing .git). */ + + path = absPath(path, baseDir); + + if (isFlake) { + + if (!allowMissing && !pathExists(path + "/flake.nix")){ + notice("path '%s' does not contain a 'flake.nix', searching up",path); + + // Save device to detect filesystem boundary + dev_t device = lstat(path).st_dev; + bool found = false; + while (path != "/") { + if (pathExists(path + "/flake.nix")) { + found = true; + break; + } else if (pathExists(path + "/.git")) + throw Error("path '%s' is not part of a flake (neither it nor its parent directories contain a 'flake.nix' file)", path); + else { + if (lstat(path).st_dev != device) + throw Error("unable to find a flake before encountering filesystem boundary at '%s'", path); + } + path = dirOf(path); + } + if (!found) + throw BadURL("could not find a flake.nix file"); + } + + if (!S_ISDIR(lstat(path).st_mode)) + throw BadURL("path '%s' is not a flake (because it's not a directory)", path); + + if (!allowMissing && !pathExists(path + "/flake.nix")) + throw BadURL("path '%s' is not a flake (because it doesn't contain a 'flake.nix' file)", path); + + auto flakeRoot = path; + std::string subdir; + + while (flakeRoot != "/") { + if (pathExists(flakeRoot + "/.git")) { + auto base = std::string("git+file://") + flakeRoot; + + auto parsedURL = ParsedURL{ + .url = base, // FIXME + .base = base, + .scheme = "git+file", + .authority = "", + .path = flakeRoot, + .query = query, + }; + + if (subdir != "") { + if (parsedURL.query.count("dir")) + throw Error("flake URL '%s' has an inconsistent 'dir' parameter", url); + parsedURL.query.insert_or_assign("dir", subdir); + } + + if (pathExists(flakeRoot + "/.git/shallow")) + parsedURL.query.insert_or_assign("shallow", "1"); + + return std::make_pair( + FlakeRef(fetchers::Input::fromURL(parsedURL), getOr(parsedURL.query, "dir", "")), + fragment); + } + + subdir = std::string(baseNameOf(flakeRoot)) + (subdir.empty() ? "" : "/" + subdir); + flakeRoot = dirOf(flakeRoot); + } + } + + } else { + if (!hasPrefix(path, "/")) + throw BadURL("flake reference '%s' is not an absolute path", url); + path = canonPath(path + "/" + getOr(query, "dir", "")); + } + + fetchers::Attrs attrs; + attrs.insert_or_assign("type", "path"); + attrs.insert_or_assign("path", path); + + return std::make_pair(FlakeRef(fetchers::Input::fromAttrs(std::move(attrs)), ""), fragment); +}; + + +/* Check if 'url' is a flake ID. This is an abbreviated syntax for + 'flake:?ref=&rev='. */ +std::optional> parseFlakeIdRef( + const std::string & url, + bool isFlake +) +{ + std::smatch match; static std::regex flakeRegex( "((" + flakeIdRegexS + ")(?:/(?:" + refAndOrRevRegex + "))?)" + "(?:#(" + queryRegex + "))?", std::regex::ECMAScript); - std::smatch match; - - auto parsePathFlakeRef = [&]() { - std::string path = url; - std::string fragment = ""; - std::map query = {}; - auto pathEnd = url.find_first_of("#?"); - auto fragmentStart = pathEnd; - if (pathEnd != std::string::npos && url[pathEnd] == '?') - fragmentStart = url.find("#"); - if (pathEnd != std::string::npos) { - path = url.substr(0, pathEnd); - } - if (fragmentStart != std::string::npos) { - fragment = percentDecode(url.substr(fragmentStart+1)); - } - if (fragmentStart != std::string::npos && pathEnd != std::string::npos) { - query = decodeQuery(url.substr(pathEnd+1, fragmentStart)); - } - - if (baseDir) { - /* Check if 'url' is a path (either absolute or relative - to 'baseDir'). If so, search upward to the root of the - repo (i.e. the directory containing .git). */ - - path = absPath(path, baseDir); - - if (isFlake) { - - if (!allowMissing && !pathExists(path + "/flake.nix")){ - notice("path '%s' does not contain a 'flake.nix', searching up",path); - - // Save device to detect filesystem boundary - dev_t device = lstat(path).st_dev; - bool found = false; - while (path != "/") { - if (pathExists(path + "/flake.nix")) { - found = true; - break; - } else if (pathExists(path + "/.git")) - throw Error("path '%s' is not part of a flake (neither it nor its parent directories contain a 'flake.nix' file)", path); - else { - if (lstat(path).st_dev != device) - throw Error("unable to find a flake before encountering filesystem boundary at '%s'", path); - } - path = dirOf(path); - } - if (!found) - throw BadURL("could not find a flake.nix file"); - } - - if (!S_ISDIR(lstat(path).st_mode)) - throw BadURL("path '%s' is not a flake (because it's not a directory)", path); - - if (!allowMissing && !pathExists(path + "/flake.nix")) - throw BadURL("path '%s' is not a flake (because it doesn't contain a 'flake.nix' file)", path); - - auto flakeRoot = path; - std::string subdir; - - while (flakeRoot != "/") { - if (pathExists(flakeRoot + "/.git")) { - auto base = std::string("git+file://") + flakeRoot; - - auto parsedURL = ParsedURL{ - .url = base, // FIXME - .base = base, - .scheme = "git+file", - .authority = "", - .path = flakeRoot, - .query = query, - }; - - if (subdir != "") { - if (parsedURL.query.count("dir")) - throw Error("flake URL '%s' has an inconsistent 'dir' parameter", url); - parsedURL.query.insert_or_assign("dir", subdir); - } - - if (pathExists(flakeRoot + "/.git/shallow")) - parsedURL.query.insert_or_assign("shallow", "1"); - - return std::make_pair( - FlakeRef(Input::fromURL(parsedURL, isFlake), getOr(parsedURL.query, "dir", "")), - fragment); - } - - subdir = std::string(baseNameOf(flakeRoot)) + (subdir.empty() ? "" : "/" + subdir); - flakeRoot = dirOf(flakeRoot); - } - } - - } else { - if (!hasPrefix(path, "/")) - throw BadURL("flake reference '%s' is not an absolute path", url); - path = canonPath(path + "/" + getOr(query, "dir", "")); - } - - fetchers::Attrs attrs; - attrs.insert_or_assign("type", "path"); - attrs.insert_or_assign("path", path); - - return std::make_pair(FlakeRef(Input::fromAttrs(std::move(attrs)), ""), fragment); - }; - - /* Check if 'url' is a flake ID. This is an abbreviated syntax for - 'flake:?ref=&rev='. */ - if (std::regex_match(url, match, flakeRegex)) { auto parsedURL = ParsedURL{ .url = url, @@ -200,25 +203,53 @@ std::pair parseFlakeRefWithFragment( }; return std::make_pair( - FlakeRef(Input::fromURL(parsedURL), ""), + FlakeRef(fetchers::Input::fromURL(parsedURL, isFlake), ""), percentDecode(match.str(6))); } - else { - try { - auto parsedURL = parseURL(url); - std::string fragment; - std::swap(fragment, parsedURL.fragment); + return {}; +} - auto input = Input::fromURL(parsedURL, isFlake); - input.parent = baseDir; +std::optional> parseURLFlakeRef( + const std::string & url, + const std::optional & baseDir, + bool isFlake +) +{ + ParsedURL parsedURL; + try { + parsedURL = parseURL(url); + } catch (BadURL &) { + return std::nullopt; + } - return std::make_pair( - FlakeRef(std::move(input), getOr(parsedURL.query, "dir", "")), - fragment); - } catch (BadURL &) { - return parsePathFlakeRef(); - } + std::string fragment; + std::swap(fragment, parsedURL.fragment); + + auto input = fetchers::Input::fromURL(parsedURL, isFlake); + input.parent = baseDir; + + return std::make_pair( + FlakeRef(std::move(input), getOr(parsedURL.query, "dir", "")), + fragment); +} + +std::pair parseFlakeRefWithFragment( + const std::string & url, + const std::optional & baseDir, + bool allowMissing, + bool isFlake) +{ + using namespace fetchers; + + std::smatch match; + + if (auto res = parseFlakeIdRef(url, isFlake)) { + return *res; + } else if (auto res = parseURLFlakeRef(url, baseDir, isFlake)) { + return *res; + } else { + return parsePathFlakeRefWithFragment(url, baseDir, allowMissing, isFlake); } } From 34115076969f20be935ae8eaf579928ac07981e3 Mon Sep 17 00:00:00 2001 From: Rasmus Rendal Date: Tue, 14 Jun 2022 17:38:48 +0200 Subject: [PATCH 033/116] Document the percent-encoding mechanism --- doc/manual/src/release-notes/rl-next.md | 4 ++++ src/nix/flake.md | 12 ++++++++++++ 2 files changed, 16 insertions(+) diff --git a/doc/manual/src/release-notes/rl-next.md b/doc/manual/src/release-notes/rl-next.md index c869b5e2f..2467e7ccc 100644 --- a/doc/manual/src/release-notes/rl-next.md +++ b/doc/manual/src/release-notes/rl-next.md @@ -1 +1,5 @@ # Release X.Y (202?-??-??) + +- [URL flake references](@docroot@/command-ref/new-cli/nix3-flake.md#flake-references) now support [percent-encoded](https://datatracker.ietf.org/doc/html/rfc3986#section-2.1) characters. + +- [Path-like flake references](@docroot@/command-ref/new-cli/nix3-flake.md#path-like-syntax) now accept arbitrary unicode characters (except `#` and `?`). diff --git a/src/nix/flake.md b/src/nix/flake.md index 92f477917..1c512b315 100644 --- a/src/nix/flake.md +++ b/src/nix/flake.md @@ -67,6 +67,13 @@ inputs.nixpkgs = { }; ``` +Following [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986#section-2.1), +characters outside of the allowed range (i.e. are neither [reserved +character](https://datatracker.ietf.org/doc/html/rfc3986#section-2.2) or +[unreserved +characters](https://datatracker.ietf.org/doc/html/rfc3986#section-2.3)) must be +percent-encoded. + ### Examples Here are some examples of flake references in their URL-like representation: @@ -103,10 +110,15 @@ The semantic of such a path is as follows: 2. The filesystem root (/), or 3. A folder on a different mount point. +Contrary to URL-like reference, path-like flake references can contain +arbitrary unicode characters (except `#` and `?`). + ### Examples * `.`: The flake to which the current directory belongs to. * `/home/alice/src/patchelf`: A flake in some other directory. +* `./../sub directory/with Ûñî©ôδ€`: A flake in another relative directory that + has Unicode characters in its name. ## Flake reference attributes From 39ba81a4eb7404f9a12cf0768da51c0e0e26b588 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 7 Sep 2023 22:52:57 -0400 Subject: [PATCH 034/116] Improve internal API docs for two file hashing functions Co-Authored-By: Matthew Bauer Co-Authored-By: Carlo Nucera --- src/libutil/hash.hh | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/libutil/hash.hh b/src/libutil/hash.hh index ae3ee40f4..c3aa5cd81 100644 --- a/src/libutil/hash.hh +++ b/src/libutil/hash.hh @@ -145,13 +145,17 @@ std::string printHash16or32(const Hash & hash); Hash hashString(HashType ht, std::string_view s); /** - * Compute the hash of the given file. + * Compute the hash of the given file, hashing its contents directly. + * + * (Metadata, such as the executable permission bit, is ignored.) */ Hash hashFile(HashType ht, const Path & path); /** - * Compute the hash of the given path. The hash is defined as - * (essentially) hashString(ht, dumpPath(path)). + * Compute the hash of the given path, serializing as a Nix Archive and + * then hashing that. + * + * The hash is defined as (essentially) hashString(ht, dumpPath(path)). */ typedef std::pair HashResult; HashResult hashPath(HashType ht, const Path & path, From 9d6114313b1b06a020bed230cfe7f05e6075b875 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 7 Sep 2023 22:31:19 -0400 Subject: [PATCH 035/116] Move `ParseSink` to its own header We will soon add a new implemenation so the one for NARs in `archive.cc` isn't the only one. Co-Authored-By: Matthew Bauer Co-Authored-By: Carlo Nucera --- src/libutil/archive.hh | 17 +---------------- src/libutil/fs-sink.hh | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 16 deletions(-) create mode 100644 src/libutil/fs-sink.hh diff --git a/src/libutil/archive.hh b/src/libutil/archive.hh index 2cf164a41..3530783c1 100644 --- a/src/libutil/archive.hh +++ b/src/libutil/archive.hh @@ -3,6 +3,7 @@ #include "types.hh" #include "serialise.hh" +#include "fs-sink.hh" namespace nix { @@ -72,22 +73,6 @@ time_t dumpPathAndGetMtime(const Path & path, Sink & sink, */ void dumpString(std::string_view s, Sink & sink); -/** - * \todo Fix this API, it sucks. - */ -struct ParseSink -{ - virtual void createDirectory(const Path & path) { }; - - virtual void createRegularFile(const Path & path) { }; - virtual void closeRegularFile() { }; - virtual void isExecutable() { }; - virtual void preallocateContents(uint64_t size) { }; - virtual void receiveContents(std::string_view data) { }; - - virtual void createSymlink(const Path & path, const std::string & target) { }; -}; - /** * If the NAR archive contains a single file at top-level, then save * the contents of the file to `s`. Otherwise barf. diff --git a/src/libutil/fs-sink.hh b/src/libutil/fs-sink.hh new file mode 100644 index 000000000..ed6484e61 --- /dev/null +++ b/src/libutil/fs-sink.hh @@ -0,0 +1,25 @@ +#pragma once +///@file + +#include "types.hh" +#include "serialise.hh" + +namespace nix { + +/** + * \todo Fix this API, it sucks. + */ +struct ParseSink +{ + virtual void createDirectory(const Path & path) { }; + + virtual void createRegularFile(const Path & path) { }; + virtual void closeRegularFile() { }; + virtual void isExecutable() { }; + virtual void preallocateContents(uint64_t size) { }; + virtual void receiveContents(std::string_view data) { }; + + virtual void createSymlink(const Path & path, const std::string & target) { }; +}; + +} From 8a416e819c9727c2e9efd70f1d2a4b0bfda11663 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 7 Sep 2023 22:49:01 -0400 Subject: [PATCH 036/116] Move `RestoreSink` to `fs-sink.cc` Co-Authored-By: Matthew Bauer Co-Authored-By: Carlo Nucera --- src/libutil/archive.cc | 67 ---------------------------------- src/libutil/fs-sink.cc | 82 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 67 deletions(-) create mode 100644 src/libutil/fs-sink.cc diff --git a/src/libutil/archive.cc b/src/libutil/archive.cc index 268a798d9..0f0cae7c0 100644 --- a/src/libutil/archive.cc +++ b/src/libutil/archive.cc @@ -27,8 +27,6 @@ struct ArchiveSettings : Config #endif "use-case-hack", "Whether to enable a Darwin-specific hack for dealing with file name collisions."}; - Setting preallocateContents{this, false, "preallocate-contents", - "Whether to preallocate files when writing objects with known size."}; }; static ArchiveSettings archiveSettings; @@ -302,71 +300,6 @@ void parseDump(ParseSink & sink, Source & source) } -struct RestoreSink : ParseSink -{ - Path dstPath; - AutoCloseFD fd; - - void createDirectory(const Path & path) override - { - Path p = dstPath + path; - if (mkdir(p.c_str(), 0777) == -1) - throw SysError("creating directory '%1%'", p); - }; - - void createRegularFile(const Path & path) override - { - Path p = dstPath + path; - fd = open(p.c_str(), O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, 0666); - if (!fd) throw SysError("creating file '%1%'", p); - } - - void closeRegularFile() override - { - /* Call close explicitly to make sure the error is checked */ - fd.close(); - } - - void isExecutable() override - { - struct stat st; - if (fstat(fd.get(), &st) == -1) - throw SysError("fstat"); - if (fchmod(fd.get(), st.st_mode | (S_IXUSR | S_IXGRP | S_IXOTH)) == -1) - throw SysError("fchmod"); - } - - void preallocateContents(uint64_t len) override - { - if (!archiveSettings.preallocateContents) - return; - -#if HAVE_POSIX_FALLOCATE - if (len) { - errno = posix_fallocate(fd.get(), 0, len); - /* Note that EINVAL may indicate that the underlying - filesystem doesn't support preallocation (e.g. on - OpenSolaris). Since preallocation is just an - optimisation, ignore it. */ - if (errno && errno != EINVAL && errno != EOPNOTSUPP && errno != ENOSYS) - throw SysError("preallocating file of %1% bytes", len); - } -#endif - } - - void receiveContents(std::string_view data) override - { - writeFull(fd.get(), data); - } - - void createSymlink(const Path & path, const std::string & target) override - { - Path p = dstPath + path; - nix::createSymlink(target, p); - } -}; - - void restorePath(const Path & path, Source & source) { RestoreSink sink; diff --git a/src/libutil/fs-sink.cc b/src/libutil/fs-sink.cc new file mode 100644 index 000000000..fcffd3216 --- /dev/null +++ b/src/libutil/fs-sink.cc @@ -0,0 +1,82 @@ +#include + +#include "config.hh" +#include "fs-sink.hh" + +namespace nix { + +struct RestoreSinkSettings : Config +{ + Setting preallocateContents{this, false, "preallocate-contents", + "Whether to preallocate files when writing objects with known size."}; +}; + +static RestoreSinkSettings restoreSinkSettings; + +static GlobalConfig::Register r1(&restoreSinkSettings); + +struct RestoreSink : ParseSink +{ + Path dstPath; + AutoCloseFD fd; + + void createDirectory(const Path & path) override + { + Path p = dstPath + path; + if (mkdir(p.c_str(), 0777) == -1) + throw SysError("creating directory '%1%'", p); + }; + + void createRegularFile(const Path & path) override + { + Path p = dstPath + path; + fd = open(p.c_str(), O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, 0666); + if (!fd) throw SysError("creating file '%1%'", p); + } + + void closeRegularFile() override + { + /* Call close explicitly to make sure the error is checked */ + fd.close(); + } + + void isExecutable() override + { + struct stat st; + if (fstat(fd.get(), &st) == -1) + throw SysError("fstat"); + if (fchmod(fd.get(), st.st_mode | (S_IXUSR | S_IXGRP | S_IXOTH)) == -1) + throw SysError("fchmod"); + } + + void preallocateContents(uint64_t len) override + { + if (!archiveSettings.preallocateContents) + return; + +#if HAVE_POSIX_FALLOCATE + if (len) { + errno = posix_fallocate(fd.get(), 0, len); + /* Note that EINVAL may indicate that the underlying + filesystem doesn't support preallocation (e.g. on + OpenSolaris). Since preallocation is just an + optimisation, ignore it. */ + if (errno && errno != EINVAL && errno != EOPNOTSUPP && errno != ENOSYS) + throw SysError("preallocating file of %1% bytes", len); + } +#endif + } + + void receiveContents(std::string_view data) override + { + writeFull(fd.get(), data); + } + + void createSymlink(const Path & path, const std::string & target) override + { + Path p = dstPath + path; + nix::createSymlink(target, p); + } +}; + +} From f2e201fbdb93925b9588f78a03d67fcd91c74b65 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 7 Sep 2023 22:49:01 -0400 Subject: [PATCH 037/116] Expose `RestoreSink` in header (`fs-sink.hh`) Co-Authored-By: Matthew Bauer Co-Authored-By: Carlo Nucera --- src/libutil/fs-sink.cc | 117 ++++++++++++++++++++--------------------- src/libutil/fs-sink.hh | 17 ++++++ 2 files changed, 73 insertions(+), 61 deletions(-) diff --git a/src/libutil/fs-sink.cc b/src/libutil/fs-sink.cc index fcffd3216..a08a723a4 100644 --- a/src/libutil/fs-sink.cc +++ b/src/libutil/fs-sink.cc @@ -15,68 +15,63 @@ static RestoreSinkSettings restoreSinkSettings; static GlobalConfig::Register r1(&restoreSinkSettings); -struct RestoreSink : ParseSink + +void RestoreSink::createDirectory(const Path & path) { - Path dstPath; - AutoCloseFD fd; - - void createDirectory(const Path & path) override - { - Path p = dstPath + path; - if (mkdir(p.c_str(), 0777) == -1) - throw SysError("creating directory '%1%'", p); - }; - - void createRegularFile(const Path & path) override - { - Path p = dstPath + path; - fd = open(p.c_str(), O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, 0666); - if (!fd) throw SysError("creating file '%1%'", p); - } - - void closeRegularFile() override - { - /* Call close explicitly to make sure the error is checked */ - fd.close(); - } - - void isExecutable() override - { - struct stat st; - if (fstat(fd.get(), &st) == -1) - throw SysError("fstat"); - if (fchmod(fd.get(), st.st_mode | (S_IXUSR | S_IXGRP | S_IXOTH)) == -1) - throw SysError("fchmod"); - } - - void preallocateContents(uint64_t len) override - { - if (!archiveSettings.preallocateContents) - return; - -#if HAVE_POSIX_FALLOCATE - if (len) { - errno = posix_fallocate(fd.get(), 0, len); - /* Note that EINVAL may indicate that the underlying - filesystem doesn't support preallocation (e.g. on - OpenSolaris). Since preallocation is just an - optimisation, ignore it. */ - if (errno && errno != EINVAL && errno != EOPNOTSUPP && errno != ENOSYS) - throw SysError("preallocating file of %1% bytes", len); - } -#endif - } - - void receiveContents(std::string_view data) override - { - writeFull(fd.get(), data); - } - - void createSymlink(const Path & path, const std::string & target) override - { - Path p = dstPath + path; - nix::createSymlink(target, p); - } + Path p = dstPath + path; + if (mkdir(p.c_str(), 0777) == -1) + throw SysError("creating directory '%1%'", p); }; +void RestoreSink::createRegularFile(const Path & path) +{ + Path p = dstPath + path; + fd = open(p.c_str(), O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, 0666); + if (!fd) throw SysError("creating file '%1%'", p); +} + +void RestoreSink::closeRegularFile() +{ + /* Call close explicitly to make sure the error is checked */ + fd.close(); +} + +void RestoreSink::isExecutable() +{ + struct stat st; + if (fstat(fd.get(), &st) == -1) + throw SysError("fstat"); + if (fchmod(fd.get(), st.st_mode | (S_IXUSR | S_IXGRP | S_IXOTH)) == -1) + throw SysError("fchmod"); +} + +void RestoreSink::preallocateContents(uint64_t len) +{ + if (!restoreSinkSettings.preallocateContents) + return; + +#if HAVE_POSIX_FALLOCATE + if (len) { + errno = posix_fallocate(fd.get(), 0, len); + /* Note that EINVAL may indicate that the underlying + filesystem doesn't support preallocation (e.g. on + OpenSolaris). Since preallocation is just an + optimisation, ignore it. */ + if (errno && errno != EINVAL && errno != EOPNOTSUPP && errno != ENOSYS) + throw SysError("preallocating file of %1% bytes", len); + } +#endif +} + +void RestoreSink::receiveContents(std::string_view data) +{ + writeFull(fd.get(), data); +} + +void RestoreSink::createSymlink(const Path & path, const std::string & target) +{ + Path p = dstPath + path; + nix::createSymlink(target, p); +} + } diff --git a/src/libutil/fs-sink.hh b/src/libutil/fs-sink.hh index ed6484e61..6837e2fc4 100644 --- a/src/libutil/fs-sink.hh +++ b/src/libutil/fs-sink.hh @@ -22,4 +22,21 @@ struct ParseSink virtual void createSymlink(const Path & path, const std::string & target) { }; }; +struct RestoreSink : ParseSink +{ + Path dstPath; + AutoCloseFD fd; + + + void createDirectory(const Path & path) override; + + void createRegularFile(const Path & path) override; + void closeRegularFile() override; + void isExecutable() override; + void preallocateContents(uint64_t size) override; + void receiveContents(std::string_view data) override; + + void createSymlink(const Path & path, const std::string & target) override; +}; + } From 694810ba34a74226eba22daf5c6264b9d81e2926 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 22 Sep 2023 23:54:04 -0400 Subject: [PATCH 038/116] doc: `showOptions`: Move union to caller `showOptions` itself doesn't care, so it shouldn't take two separate arguments. --- doc/manual/generate-manpage.nix | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/doc/manual/generate-manpage.nix b/doc/manual/generate-manpage.nix index 87891fa7a..b85c488c9 100644 --- a/doc/manual/generate-manpage.nix +++ b/doc/manual/generate-manpage.nix @@ -78,16 +78,15 @@ let maybeOptions = optionalString (details.flags != {}) '' # Options - ${showOptions details.flags toplevel.flags} + ${showOptions (details.flags // toplevel.flags)} > **Note** > > See [`man nix.conf`](@docroot@/command-ref/conf-file.md#command-line-flags) for overriding configuration settings with command line flags. ''; - showOptions = options: commonOptions: + showOptions = allOptions: let - allOptions = options // commonOptions; showCategory = cat: '' ${optionalString (cat != "") "**${cat}:**"} From 9c640c122931aa6515e1f3c80cb4d415a1352cc7 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sat, 23 Sep 2023 00:28:16 -0400 Subject: [PATCH 039/116] doc: `showOptions`: Simplify code with `builtins.groupBy` This makes grouping options by category much nicer. No behavior should be changed. --- doc/manual/generate-manpage.nix | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/doc/manual/generate-manpage.nix b/doc/manual/generate-manpage.nix index b85c488c9..e18810594 100644 --- a/doc/manual/generate-manpage.nix +++ b/doc/manual/generate-manpage.nix @@ -1,8 +1,8 @@ let inherit (builtins) - attrNames attrValues fromJSON listToAttrs mapAttrs + attrNames attrValues fromJSON listToAttrs mapAttrs groupBy concatStringsSep concatMap length lessThan replaceStrings sort; - inherit (import ./utils.nix) concatStrings optionalString filterAttrs trim squash unique showSettings; + inherit (import ./utils.nix) attrsToList concatStrings optionalString filterAttrs trim squash unique showSettings; in commandDump: @@ -87,12 +87,11 @@ let showOptions = allOptions: let - showCategory = cat: '' + showCategory = cat: opts: '' ${optionalString (cat != "") "**${cat}:**"} - ${listOptions (filterAttrs (n: v: v.category == cat) allOptions)} + ${concatStringsSep "\n" (attrValues (mapAttrs showOption opts))} ''; - listOptions = opts: concatStringsSep "\n" (attrValues (mapAttrs showOption opts)); showOption = name: option: let shortName = optionalString @@ -106,8 +105,12 @@ let ${option.description} ''; - categories = sort lessThan (unique (map (cmd: cmd.category) (attrValues allOptions))); - in concatStrings (map showCategory categories); + categories = mapAttrs + (_: listToAttrs) + (groupBy + (cmd: cmd.value.category) + (attrsToList allOptions)); + in concatStrings (attrValues (mapAttrs showCategory categories)); in squash result; appendName = filename: name: (if filename == "nix" then "nix3" else filename) + "-" + name; From 1d9fd3a6f8ac53be4ba7d409defe291c0dbdf97a Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 22 Sep 2023 23:55:06 -0400 Subject: [PATCH 040/116] manual / manpages: Adjust option filter filtering, move from C++ to Nix Behavior change: Before we only showed uption if the command-specific options were non-empty. But that is somewhat odd since we also show common options. Now, we do everything based on the union of both sorts of options (with hidden-categories filtered, as before). Implementation change: The JSON dumping once again includes all options; the filtering of hidden categories is done in the Nix instead. This is better separation of "content" vs "presentation", and prepare the way for the HTML manual vs manpages / `--help` doing different things. --- doc/manual/generate-manpage.nix | 8 ++++++-- src/libutil/args.cc | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/doc/manual/generate-manpage.nix b/doc/manual/generate-manpage.nix index e18810594..b41730bc2 100644 --- a/doc/manual/generate-manpage.nix +++ b/doc/manual/generate-manpage.nix @@ -75,10 +75,14 @@ let (details ? doc) (replaceStrings ["@stores@"] [storeDocs] details.doc); - maybeOptions = optionalString (details.flags != {}) '' + maybeOptions = let + allVisibleOptions = filterAttrs + (_: o: ! o.hiddenCategory) + (details.flags // toplevel.flags); + in optionalString (allVisibleOptions != {}) '' # Options - ${showOptions (details.flags // toplevel.flags)} + ${showOptions allVisibleOptions} > **Note** > diff --git a/src/libutil/args.cc b/src/libutil/args.cc index 8db293762..e410c7eec 100644 --- a/src/libutil/args.cc +++ b/src/libutil/args.cc @@ -236,7 +236,7 @@ nlohmann::json Args::toJSON() for (auto & [name, flag] : longFlags) { auto j = nlohmann::json::object(); - if (hiddenCategories.count(flag->category)) continue; + j["hiddenCategory"] = hiddenCategories.count(flag->category) > 0; if (flag->aliases.count(name)) continue; if (flag->shortName) j["shortName"] = std::string(1, flag->shortName); From 9f93972c4d73abdcde789112229209af2cbda520 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sat, 23 Sep 2023 00:35:03 -0400 Subject: [PATCH 041/116] manual / manpages: Make option category names a proper subheader Before they were an "ad-hoc" header with bold and a colon; now they are a proper subheader. For the man pages, this doesn't make much of a difference, but it will help more on for the HTML manual, where things can be restyled. Again, good separation of content vs presentation. --- doc/manual/generate-manpage.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/generate-manpage.nix b/doc/manual/generate-manpage.nix index b41730bc2..21a4802e5 100644 --- a/doc/manual/generate-manpage.nix +++ b/doc/manual/generate-manpage.nix @@ -92,7 +92,7 @@ let showOptions = allOptions: let showCategory = cat: opts: '' - ${optionalString (cat != "") "**${cat}:**"} + ${optionalString (cat != "") "## ${cat}"} ${concatStringsSep "\n" (attrValues (mapAttrs showOption opts))} ''; From e4b83fbfe2e08ddd2ebd409c26f2dc4c11ee1d4a Mon Sep 17 00:00:00 2001 From: Yingchi Long Date: Sun, 24 Sep 2023 14:54:41 +0800 Subject: [PATCH 042/116] libexpr: const rvalue reference -> value for nix::Expr nodes --- src/libexpr/nixexpr.hh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index 5ca3d1fa6..944977e50 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -238,7 +238,7 @@ struct ExprSelect : Expr PosIdx pos; Expr * e, * def; AttrPath attrPath; - ExprSelect(const PosIdx & pos, Expr * e, const AttrPath && attrPath, Expr * def) : pos(pos), e(e), def(def), attrPath(std::move(attrPath)) { }; + ExprSelect(const PosIdx & pos, Expr * e, AttrPath attrPath, Expr * def) : pos(pos), e(e), def(def), attrPath(std::move(attrPath)) { }; ExprSelect(const PosIdx & pos, Expr * e, Symbol name) : pos(pos), e(e), def(0) { attrPath.push_back(AttrName(name)); }; PosIdx getPos() const override { return pos; } COMMON_METHODS @@ -248,7 +248,7 @@ struct ExprOpHasAttr : Expr { Expr * e; AttrPath attrPath; - ExprOpHasAttr(Expr * e, const AttrPath && attrPath) : e(e), attrPath(std::move(attrPath)) { }; + ExprOpHasAttr(Expr * e, AttrPath attrPath) : e(e), attrPath(std::move(attrPath)) { }; PosIdx getPos() const override { return e->getPos(); } COMMON_METHODS }; From 89e5e68799132bc2f1d2551002dab84b63fde9ac Mon Sep 17 00:00:00 2001 From: Maximilian Bosch Date: Sun, 24 Sep 2023 20:28:34 +0200 Subject: [PATCH 043/116] doc/hacking: fix `make` target to build the docs (#9033) Was confused why `make html` didn't work while working on #9032, but then I realized that after this section was written, the target was renamed to `manual-html` in 6910f5dcb6784360589b860b8f80487a5c97fe08. --- doc/manual/src/contributing/hacking.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/src/contributing/hacking.md b/doc/manual/src/contributing/hacking.md index 22f629c6f..a2446b53a 100644 --- a/doc/manual/src/contributing/hacking.md +++ b/doc/manual/src/contributing/hacking.md @@ -228,7 +228,7 @@ This happens late in the process, so `nix build` is not suitable for iterating. To build the manual incrementally, run: ```console -make html -j $NIX_BUILD_CORES +make manual-html -j $NIX_BUILD_CORES ``` In order to reflect changes to the [Makefile], clear all generated files before re-building: From b3433099d4ef3fbc2ef86f97cf19dcd543bd05e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= <7226587+thufschmitt@users.noreply.github.com> Date: Mon, 25 Sep 2023 09:56:49 +0200 Subject: [PATCH 044/116] Apply suggestions from code review Co-authored-by: Valentin Gagarin --- src/nix/flake.md | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/nix/flake.md b/src/nix/flake.md index 1c512b315..939269c6a 100644 --- a/src/nix/flake.md +++ b/src/nix/flake.md @@ -68,11 +68,9 @@ inputs.nixpkgs = { ``` Following [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986#section-2.1), -characters outside of the allowed range (i.e. are neither [reserved -character](https://datatracker.ietf.org/doc/html/rfc3986#section-2.2) or -[unreserved -characters](https://datatracker.ietf.org/doc/html/rfc3986#section-2.3)) must be -percent-encoded. +characters outside of the allowed range (i.e. neither [reserved characters](https://datatracker.ietf.org/doc/html/rfc3986#section-2.2) +nor [unreserved characters](https://datatracker.ietf.org/doc/html/rfc3986#section-2.3)) +must be percent-encoded. ### Examples @@ -110,8 +108,7 @@ The semantic of such a path is as follows: 2. The filesystem root (/), or 3. A folder on a different mount point. -Contrary to URL-like reference, path-like flake references can contain -arbitrary unicode characters (except `#` and `?`). +Contrary to URL-like references, path-like flake references can contain arbitrary unicode characters (except `#` and `?`). ### Examples From 4606a07bb62c650d2261f2d8d5e93e547acf0af6 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 25 Sep 2023 08:20:39 -0400 Subject: [PATCH 045/116] generate-manpage.nix: Add comment explaining one bit --- doc/manual/generate-manpage.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/manual/generate-manpage.nix b/doc/manual/generate-manpage.nix index 21a4802e5..41725aed6 100644 --- a/doc/manual/generate-manpage.nix +++ b/doc/manual/generate-manpage.nix @@ -110,6 +110,7 @@ let ${option.description} ''; categories = mapAttrs + # Convert each group from a list of key-value pairs back to an attrset (_: listToAttrs) (groupBy (cmd: cmd.value.category) From 70b5e6050cd135b75942f731ad892e4f473c6c21 Mon Sep 17 00:00:00 2001 From: waalge <47293755+waalge@users.noreply.github.com> Date: Mon, 25 Sep 2023 13:39:11 +0000 Subject: [PATCH 046/116] fix docstring --- src/libexpr/primops.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index cd9a05bb2..0d503ae26 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -2991,7 +2991,7 @@ static RegisterPrimOp primop_tail({ .name = "__tail", .args = {"list"}, .doc = R"( - Return the second to last elements of a list; abort evaluation if + Return the list without its first item; abort evaluation if the argument isn’t a list or is an empty list. > **Warning** From ad213103d85d22b52c608362cb2127fa10e49620 Mon Sep 17 00:00:00 2001 From: Sergei Trofimovich Date: Mon, 25 Sep 2023 17:45:13 +0100 Subject: [PATCH 047/116] src/libstore/profiles.cc: fix comparison of sign difference Detected by `gcc` as: CXX src/libstore/profiles.o src/libstore/profiles.cc: In function 'void nix::deleteGenerationsGreaterThan(const Path&, GenerationNumber, bool)': src/libstore/profiles.cc:186:50: warning: comparison of integer expressions of different signedness: 'int' and 'nix::GenerationNumber' {aka 'long unsigned int'} [-Wsign-compare] 186 | for (auto keep = 0; i != gens.rend() && keep < max; ++i, ++keep); | ~~~~~^~~~~ --- src/libstore/profiles.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstore/profiles.cc b/src/libstore/profiles.cc index 4e9955948..239047dd6 100644 --- a/src/libstore/profiles.cc +++ b/src/libstore/profiles.cc @@ -183,7 +183,7 @@ void deleteGenerationsGreaterThan(const Path & profile, GenerationNumber max, bo iterDropUntil(gens, i, [&](auto & g) { return g.number == curGen; }); // Skip over `max` generations, preserving them - for (auto keep = 0; i != gens.rend() && keep < max; ++i, ++keep); + for (GenerationNumber keep = 0; i != gens.rend() && keep < max; ++i, ++keep); // Delete the rest for (; i != gens.rend(); ++i) From bd24176ac57b9d418b4ec51448b188cb74b87945 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 25 Sep 2023 17:51:17 +0100 Subject: [PATCH 048/116] libexpr/nixexpr.hh: Remove redundant inline This is redundant since definitions in C++ record are implicitly inline-ed. Co-authored-by: Yingchi Long --- src/libexpr/nixexpr.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index 17b2d1136..5c9242759 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -156,7 +156,7 @@ std::string showAttrPath(const SymbolTable & symbols, const AttrPath & attrPath) struct Expr { static unsigned long nrExprs; - inline Expr() { + Expr() { nrExprs++; } virtual ~Expr() { }; From cba53b3a18c2758e3d969d9d5781294560afe1b7 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Tue, 26 Sep 2023 00:39:11 +0200 Subject: [PATCH 049/116] reformat for readability --- doc/manual/src/command-ref/nix-store/realise.md | 8 ++++++-- doc/manual/src/glossary.md | 10 +++++++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/doc/manual/src/command-ref/nix-store/realise.md b/doc/manual/src/command-ref/nix-store/realise.md index a421a4220..5428d57fa 100644 --- a/doc/manual/src/command-ref/nix-store/realise.md +++ b/doc/manual/src/command-ref/nix-store/realise.md @@ -15,8 +15,12 @@ Each of *paths* is processed as follows: 1. If it is not [valid], substitute the store derivation file itself. 2. Realise its [output paths]: - Try to fetch from [substituters] the [store objects] associated with the output paths in the store derivation's [closure]. - - With [content-addressed derivations] (experimental): Determine the output paths to realise by querying content-addressed realisation entries in the [Nix database]. - - For any store paths that cannot be substituted, produce the required store objects. This involves first realising all outputs of the derivation's dependencies and then running the derivation's [`builder`](@docroot@/language/derivations.md#attr-builder) executable. + - With [content-addressed derivations] (experimental): + Determine the output paths to realise by querying content-addressed realisation entries in the [Nix database]. + - For any store paths that cannot be substituted, produce the required store objects: + 1. Realise all outputs of the derivation's dependencies + 2. Run the derivation's [`builder`](@docroot@/language/derivations.md#attr-builder) executable + - Otherwise, and if the path is not already valid: Try to fetch the associated [store objects] in the path's [closure] from [substituters]. If no substitutes are available and no store derivation is given, realisation fails. diff --git a/doc/manual/src/glossary.md b/doc/manual/src/glossary.md index b7c340d36..5345f2deb 100644 --- a/doc/manual/src/glossary.md +++ b/doc/manual/src/glossary.md @@ -33,11 +33,15 @@ Ensure a [store path] is [valid][validity]. - This means either running the [`builder`](@docroot@/language/derivations.md#attr-builder) executable as specified in the corresponding [derivation], or fetching a pre-built [store object] from a [substituter], or delegating to a [remote builder](@docroot@/advanced-topics/distributed-builds.html) and retrieving the outputs. + This can be achieved by: + - Fetching a pre-built [store object] from a [substituter] + - Running the [`builder`](@docroot@/language/derivations.md#attr-builder) executable as specified in the corresponding [derivation] + - Delegating to a [remote builder](@docroot@/advanced-topics/distributed-builds.html) and retrieving the outputs + - See [`nix-build`](./command-ref/nix-build.md) and [`nix-store --realise`](@docroot@/command-ref/nix-store/realise.md). + See [`nix-store --realise`](@docroot@/command-ref/nix-store/realise.md) for a detailed description of the algorithm. - See [`nix build`](./command-ref/new-cli/nix3-build.md) (experimental). + See also [`nix-build`](./command-ref/nix-build.md) and [`nix build`](./command-ref/new-cli/nix3-build.md) (experimental). [realise]: #gloss-realise From 541890463d83d1a818dfabc79053d1302bd828a2 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Tue, 18 Jul 2023 13:49:59 +0200 Subject: [PATCH 050/116] make separate section for builder execution --- doc/manual/src/language/derivations.md | 116 +++++++++++++------------ 1 file changed, 59 insertions(+), 57 deletions(-) diff --git a/doc/manual/src/language/derivations.md b/doc/manual/src/language/derivations.md index 79a09122a..df4673cf5 100644 --- a/doc/manual/src/language/derivations.md +++ b/doc/manual/src/language/derivations.md @@ -93,69 +93,71 @@ wrapper around `derivation` that adds a default value for `system` and always uses Bash as the builder, to which the supplied builder is passed as a command-line argument. See the Nixpkgs manual for details. -The builder is executed as follows: +## Builder execution - - A temporary directory is created under the directory specified by - `TMPDIR` (default `/tmp`) where the build will take place. The - current directory is changed to this directory. +The [`builder`](#attr-builder) is executed as follows: - - The environment is cleared and set to the derivation attributes, as - specified above. +- A temporary directory is created under the directory specified by + `TMPDIR` (default `/tmp`) where the build will take place. The + current directory is changed to this directory. - - In addition, the following variables are set: - - - `NIX_BUILD_TOP` contains the path of the temporary directory for - this build. - - - Also, `TMPDIR`, `TEMPDIR`, `TMP`, `TEMP` are set to point to the - temporary directory. This is to prevent the builder from - accidentally writing temporary files anywhere else. Doing so - might cause interference by other processes. - - - `PATH` is set to `/path-not-set` to prevent shells from - initialising it to their built-in default value. - - - `HOME` is set to `/homeless-shelter` to prevent programs from - using `/etc/passwd` or the like to find the user's home - directory, which could cause impurity. Usually, when `HOME` is - set, it is used as the location of the home directory, even if - it points to a non-existent path. - - - `NIX_STORE` is set to the path of the top-level Nix store - directory (typically, `/nix/store`). - - - For each output declared in `outputs`, the corresponding - environment variable is set to point to the intended path in the - Nix store for that output. Each output path is a concatenation - of the cryptographic hash of all build inputs, the `name` - attribute and the output name. (The output name is omitted if - it’s `out`.) +- The environment is cleared and set to the derivation attributes, as + specified above. - - If an output path already exists, it is removed. Also, locks are - acquired to prevent multiple Nix instances from performing the same - build at the same time. +- In addition, the following variables are set: - - A log of the combined standard output and error is written to - `/nix/var/log/nix`. + - `NIX_BUILD_TOP` contains the path of the temporary directory for + this build. - - The builder is executed with the arguments specified by the - attribute `args`. If it exits with exit code 0, it is considered to - have succeeded. + - Also, `TMPDIR`, `TEMPDIR`, `TMP`, `TEMP` are set to point to the + temporary directory. This is to prevent the builder from + accidentally writing temporary files anywhere else. Doing so + might cause interference by other processes. - - The temporary directory is removed (unless the `-K` option was - specified). + - `PATH` is set to `/path-not-set` to prevent shells from + initialising it to their built-in default value. - - If the build was successful, Nix scans each output path for - references to input paths by looking for the hash parts of the input - paths. Since these are potential runtime dependencies, Nix registers - them as dependencies of the output paths. + - `HOME` is set to `/homeless-shelter` to prevent programs from + using `/etc/passwd` or the like to find the user's home + directory, which could cause impurity. Usually, when `HOME` is + set, it is used as the location of the home directory, even if + it points to a non-existent path. - - After the build, Nix sets the last-modified timestamp on all files - in the build result to 1 (00:00:01 1/1/1970 UTC), sets the group to - the default group, and sets the mode of the file to 0444 or 0555 - (i.e., read-only, with execute permission enabled if the file was - originally executable). Note that possible `setuid` and `setgid` - bits are cleared. Setuid and setgid programs are not currently - supported by Nix. This is because the Nix archives used in - deployment have no concept of ownership information, and because it - makes the build result dependent on the user performing the build. + - `NIX_STORE` is set to the path of the top-level Nix store + directory (typically, `/nix/store`). + + - For each output declared in `outputs`, the corresponding + environment variable is set to point to the intended path in the + Nix store for that output. Each output path is a concatenation + of the cryptographic hash of all build inputs, the `name` + attribute and the output name. (The output name is omitted if + it’s `out`.) + +- If an output path already exists, it is removed. Also, locks are + acquired to prevent multiple Nix instances from performing the same + build at the same time. + +- A log of the combined standard output and error is written to + `/nix/var/log/nix`. + +- The builder is executed with the arguments specified by the + attribute `args`. If it exits with exit code 0, it is considered to + have succeeded. + +- The temporary directory is removed (unless the `-K` option was + specified). + +- If the build was successful, Nix scans each output path for + references to input paths by looking for the hash parts of the input + paths. Since these are potential runtime dependencies, Nix registers + them as dependencies of the output paths. + +- After the build, Nix sets the last-modified timestamp on all files + in the build result to 1 (00:00:01 1/1/1970 UTC), sets the group to + the default group, and sets the mode of the file to 0444 or 0555 + (i.e., read-only, with execute permission enabled if the file was + originally executable). Note that possible `setuid` and `setgid` + bits are cleared. Setuid and setgid programs are not currently + supported by Nix. This is because the Nix archives used in + deployment have no concept of ownership information, and because it + makes the build result dependent on the user performing the build. From e2f118efeda5c0a9b1380db736eb2b86f691b1fa Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Tue, 18 Jul 2023 14:03:17 +0200 Subject: [PATCH 051/116] restructure and reword input attributes section on `derivation` --- doc/manual/src/language/derivations.md | 208 +++++++++++++++---------- 1 file changed, 123 insertions(+), 85 deletions(-) diff --git a/doc/manual/src/language/derivations.md b/doc/manual/src/language/derivations.md index df4673cf5..56c6818ca 100644 --- a/doc/manual/src/language/derivations.md +++ b/doc/manual/src/language/derivations.md @@ -1,97 +1,135 @@ # Derivations -The most important built-in function is `derivation`, which is used to -describe a single derivation (a build task). It takes as input a set, -the attributes of which specify the inputs of the build. +The most important built-in function is `derivation`, which is used to describe a single derivation: +a build task to run a process on precisely defined input files and repeatably produce output files at uniquely determined file system paths. - - There must be an attribute named [`system`]{#attr-system} whose value must be a - string specifying a Nix system type, such as `"i686-linux"` or - `"x86_64-darwin"`. (To figure out your system type, run `nix -vv - --version`.) The build can only be performed on a machine and - operating system matching the system type. (Nix can automatically - [forward builds for other - platforms](../advanced-topics/distributed-builds.md) by forwarding - them to other machines.) +It takes as input an attribute set, the attributes of which specify the inputs to the build. +It outputs an attribute set, and produces a [store derivation](@docroot@/glossary.md#gloss-store-derivation) as a side effect of evaluation. - - There must be an attribute named `name` whose value must be a - string. This is used as a symbolic name for the package by - `nix-env`, and it is appended to the output paths of the derivation. + - - There must be an attribute named [`builder`]{#attr-builder} that identifies the - program that is executed to perform the build. It can be either a - derivation or a source (a local file reference, e.g., - `./builder.sh`). +## Input attributes - - Every attribute is passed as an environment variable to the builder. - Attribute values are translated to environment variables as follows: - - - Strings and numbers are just passed verbatim. - - - A *path* (e.g., `../foo/sources.tar`) causes the referenced file - to be copied to the store; its location in the store is put in - the environment variable. The idea is that all sources should - reside in the Nix store, since all inputs to a derivation should - reside in the Nix store. - - - A *derivation* causes that derivation to be built prior to the - present derivation; its default output path is put in the - environment variable. - - - Lists of the previous types are also allowed. They are simply - concatenated, separated by spaces. - - - `true` is passed as the string `1`, `false` and `null` are - passed as an empty string. +### Required - - The optional attribute `args` specifies command-line arguments to be - passed to the builder. It should be a list. +- [`name`]{#attr-name} ([String](@docroot@/language/values.md#type-string)) - - The optional attribute `outputs` specifies a list of symbolic - outputs of the derivation. By default, a derivation produces a - single output path, denoted as `out`. However, derivations can - produce multiple output paths. This is useful because it allows - outputs to be downloaded or garbage-collected separately. For - instance, imagine a library package that provides a dynamic library, - header files, and documentation. A program that links against the - library doesn’t need the header files and documentation at runtime, - and it doesn’t need the documentation at build time. Thus, the - library package could specify: - - ```nix - outputs = [ "lib" "headers" "doc" ]; - ``` - - This will cause Nix to pass environment variables `lib`, `headers` - and `doc` to the builder containing the intended store paths of each - output. The builder would typically do something like - - ```bash - ./configure \ - --libdir=$lib/lib \ - --includedir=$headers/include \ - --docdir=$doc/share/doc - ``` - - for an Autoconf-style package. You can refer to each output of a - derivation by selecting it as an attribute, e.g. - - ```nix - buildInputs = [ pkg.lib pkg.headers ]; - ``` - - The first element of `outputs` determines the *default output*. - Thus, you could also write - - ```nix - buildInputs = [ pkg pkg.headers ]; - ``` - - since `pkg` is equivalent to `pkg.lib`. + Symbolic name for the derivation. + It is appended to the [store paths](@docroot@/glossary.md#gloss-store-path) of the resulting store derivation and its [output paths][output path]. -The function `mkDerivation` in the Nixpkgs standard environment is a -wrapper around `derivation` that adds a default value for `system` and -always uses Bash as the builder, to which the supplied builder is passed -as a command-line argument. See the Nixpkgs manual for details. + Example: `name = "hello";` + +- [`system`]{#attr-system} ([String](@docroot@/language/values.md#type-string)) + + The system type on which the [`builder`](#attr-builder) executable can be run. + + Nix will only build derivations where the `system` attribute matches the current [`system` configuration option]. + It can automatically [build on other platforms](../advanced-topics/distributed-builds.md) by forwarding build requests to other machines. + + Examples: + + `system = "x86_64-linux";` + + `system = builtins.currentSystem;` + + [`builtins.currentSystem`](@docroot@/language/builtin-constants.md#builtins-currentSystem) has the value of the [`system` configuration option], and defaults to the system type of the current Nix installation. + + [`system` configuration option]: @docroot@/command-ref/conf-file.md#conf-system + +- [`builder`]{#attr-builder} ([Path](@docroot@/language/values.md#type-path) | [String](@docroot@/language/values.md#type-string)) + + Path to an executable that will perform the build. + + Examples: + + `builder = "/bin/bash";` + + `builder = ./builder.sh;` + + `builder = "${pkgs.python}/bin/python";` + +### Optional + +- [`args`]{#attr-args} ([List](@docroot@/language/values.md#list) of [String](@docroot@/language/values.md#type-string)) Default: `[ ]` + + Command-line arguments to be passed to the [`builder`](#attr-builder) executable. + + Example: `args = [ "-c" "echo hello world > $out" ];` + +- [`outputs`]{#attr-outputs} ([List](@docroot@/language/values.md#list) of [String](@docroot@/language/values.md#type-string)) Default: `[ "out" ]` + + Symbolic outputs of the derivation. + Each output name is passed to the [`builder`](#attr-builder) executable as an environment variable with its value set to the corresponding [output path]. + + [output path]: @docroot@/glossary.md#gloss-output-path + + By default, a derivation produces a single output path called `out`. + However, derivations can produce multiple output paths. + This allows the associated [store objects](@docroot@/glossary.md#gloss-store-object) and their [closures](@docroot@/glossary.md#gloss-closure) to be copied or garbage-collected separately. + + Examples: + + Imagine a library package that provides a dynamic library, header files, and documentation. + A program that links against the library doesn’t need the header files and documentation at runtime, and it doesn’t need the documentation at build time. + Thus, the library package could specify: + + ```nix + outputs = [ "lib" "headers" "doc" ]; + ``` + + This will cause Nix to pass environment variables `lib`, `headers`, and `doc` to the builder containing the intended store paths of each output. + The builder would typically do something like + + ```bash + ./configure \ + --libdir=$lib/lib \ + --includedir=$headers/include \ + --docdir=$doc/share/doc + ``` + + for an Autoconf-style package. + + You can refer to each output of a + derivation by selecting it as an attribute, e.g. + + ```nix + buildInputs = [ pkg.lib pkg.headers ]; + ``` + + + The first element of `outputs` determines the *default output*. + Thus, you could also write + + ```nix + buildInputs = [ pkg pkg.headers ]; + ``` + + since `pkg` is equivalent to `pkg.lib`. + +- See [Advanced Attributes](./advanced-attributes.md) for more, infrequently used, optional attributes. + + + +- Every other attribute is passed as an environment variable to the builder. + Attribute values are translated to environment variables as follows: + + - Strings and numbers are just passed verbatim. + + - A *path* (e.g., `../foo/sources.tar`) causes the referenced file + to be copied to the store; its location in the store is put in + the environment variable. The idea is that all sources should + reside in the Nix store, since all inputs to a derivation should + reside in the Nix store. + + - A *derivation* causes that derivation to be built prior to the + present derivation; its default output path is put in the + environment variable. + + - Lists of the previous types are also allowed. They are simply + concatenated, separated by spaces. + + - `true` is passed as the string `1`, `false` and `null` are + passed as an empty string. ## Builder execution From d621dd17f23023d4d6a84daba90ef38e848a1375 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Fri, 15 Sep 2023 10:31:31 +0200 Subject: [PATCH 052/116] more precise wording Co-authored-by: Robert Hensing --- doc/manual/src/language/derivations.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/manual/src/language/derivations.md b/doc/manual/src/language/derivations.md index 56c6818ca..dfefe446f 100644 --- a/doc/manual/src/language/derivations.md +++ b/doc/manual/src/language/derivations.md @@ -14,14 +14,14 @@ It outputs an attribute set, and produces a [store derivation](@docroot@/glossar - [`name`]{#attr-name} ([String](@docroot@/language/values.md#type-string)) - Symbolic name for the derivation. - It is appended to the [store paths](@docroot@/glossary.md#gloss-store-path) of the resulting store derivation and its [output paths][output path]. + A symbolic name for the derivation. + It is added to the [store derivation]'s [path](@docroot@/glossary.md#gloss-store-path) and its [output paths][output path]. Example: `name = "hello";` - [`system`]{#attr-system} ([String](@docroot@/language/values.md#type-string)) - The system type on which the [`builder`](#attr-builder) executable can be run. + The system type on which the [`builder`](#attr-builder) executable is meant to be run. Nix will only build derivations where the `system` attribute matches the current [`system` configuration option]. It can automatically [build on other platforms](../advanced-topics/distributed-builds.md) by forwarding build requests to other machines. From 026c24e3787d7b1cf81c49fdcb66818fd5dad2e4 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Fri, 15 Sep 2023 15:59:07 +0200 Subject: [PATCH 053/116] add example for store path using the given name --- doc/manual/src/language/derivations.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/manual/src/language/derivations.md b/doc/manual/src/language/derivations.md index dfefe446f..3ced976f6 100644 --- a/doc/manual/src/language/derivations.md +++ b/doc/manual/src/language/derivations.md @@ -19,6 +19,7 @@ It outputs an attribute set, and produces a [store derivation](@docroot@/glossar Example: `name = "hello";` + The store derivation's path will be `/nix/store/-hello.drv`, and the output paths will be of the form `/nix/store/-hello[-]` - [`system`]{#attr-system} ([String](@docroot@/language/values.md#type-string)) The system type on which the [`builder`](#attr-builder) executable is meant to be run. From 17884f54d18b6d69dbfefe6c08272ea51b8879f8 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Fri, 22 Sep 2023 09:53:29 +0200 Subject: [PATCH 054/116] clarification on extra attributes Co-authored-by: Robert Hensing --- doc/manual/src/language/derivations.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/doc/manual/src/language/derivations.md b/doc/manual/src/language/derivations.md index 3ced976f6..77eac03b6 100644 --- a/doc/manual/src/language/derivations.md +++ b/doc/manual/src/language/derivations.md @@ -114,7 +114,11 @@ It outputs an attribute set, and produces a [store derivation](@docroot@/glossar - Every other attribute is passed as an environment variable to the builder. Attribute values are translated to environment variables as follows: - - Strings and numbers are just passed verbatim. + - Strings are passed unchanged. + + - Integral numbers are converted to decimal notation. + + - Floating point numbers are converted to simple decimal or scientific notation with a preset precision. - A *path* (e.g., `../foo/sources.tar`) causes the referenced file to be copied to the store; its location in the store is put in From 5e4734a08b8fb3f80b9617c190a2b5dfaba4a1da Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Tue, 26 Sep 2023 00:13:56 +0200 Subject: [PATCH 055/116] reword to avoid saying "build" derivations are about data transformation, so the term "build" does not add any information. there was also some feedback that "build task" is not more helpful than "derivation" if you have no prior experience with Nix or build systems, while existing associations may be misleading. --- doc/manual/src/language/derivations.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/manual/src/language/derivations.md b/doc/manual/src/language/derivations.md index 77eac03b6..da3cca4ad 100644 --- a/doc/manual/src/language/derivations.md +++ b/doc/manual/src/language/derivations.md @@ -1,9 +1,9 @@ # Derivations The most important built-in function is `derivation`, which is used to describe a single derivation: -a build task to run a process on precisely defined input files and repeatably produce output files at uniquely determined file system paths. +a specification for running an executable on precisely defined input files to repeatably produce output files at uniquely determined file system paths. -It takes as input an attribute set, the attributes of which specify the inputs to the build. +It takes as input an attribute set, the attributes of which specify the inputs to the process. It outputs an attribute set, and produces a [store derivation](@docroot@/glossary.md#gloss-store-derivation) as a side effect of evaluation. From 75a231147f2d6acd32321e088a5030a69f188854 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Tue, 26 Sep 2023 00:43:41 +0200 Subject: [PATCH 056/116] be more precise about `system` semantics --- doc/manual/src/language/derivations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/src/language/derivations.md b/doc/manual/src/language/derivations.md index da3cca4ad..c13c81e7d 100644 --- a/doc/manual/src/language/derivations.md +++ b/doc/manual/src/language/derivations.md @@ -24,7 +24,7 @@ It outputs an attribute set, and produces a [store derivation](@docroot@/glossar The system type on which the [`builder`](#attr-builder) executable is meant to be run. - Nix will only build derivations where the `system` attribute matches the current [`system` configuration option]. + A necessary condition for Nix to build derivations locally is that the `system` attribute matches the current [`system` configuration option]. It can automatically [build on other platforms](../advanced-topics/distributed-builds.md) by forwarding build requests to other machines. Examples: From 7de66f19f84edc0d617e00f6e0cc37f2dbc4f5f6 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Tue, 26 Sep 2023 00:46:26 +0200 Subject: [PATCH 057/116] example: headers -> dev make the example more realistic, since `headers` is not an output name used in Nixpkgs Co-authored-by: Robert Hensing --- doc/manual/src/language/derivations.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/manual/src/language/derivations.md b/doc/manual/src/language/derivations.md index c13c81e7d..6e6d3298b 100644 --- a/doc/manual/src/language/derivations.md +++ b/doc/manual/src/language/derivations.md @@ -75,16 +75,16 @@ It outputs an attribute set, and produces a [store derivation](@docroot@/glossar Thus, the library package could specify: ```nix - outputs = [ "lib" "headers" "doc" ]; + outputs = [ "lib" "dev" "doc" ]; ``` - This will cause Nix to pass environment variables `lib`, `headers`, and `doc` to the builder containing the intended store paths of each output. + This will cause Nix to pass environment variables `lib`, `dev`, and `doc` to the builder containing the intended store paths of each output. The builder would typically do something like ```bash ./configure \ --libdir=$lib/lib \ - --includedir=$headers/include \ + --includedir=$dev/include \ --docdir=$doc/share/doc ``` @@ -94,7 +94,7 @@ It outputs an attribute set, and produces a [store derivation](@docroot@/glossar derivation by selecting it as an attribute, e.g. ```nix - buildInputs = [ pkg.lib pkg.headers ]; + buildInputs = [ pkg.lib pkg.dev ]; ``` @@ -102,7 +102,7 @@ It outputs an attribute set, and produces a [store derivation](@docroot@/glossar Thus, you could also write ```nix - buildInputs = [ pkg pkg.headers ]; + buildInputs = [ pkg pkg.dev ]; ``` since `pkg` is equivalent to `pkg.lib`. From 5b0336b3b14551762f37126423ea8b4590ebb560 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Tue, 26 Sep 2023 00:58:50 +0200 Subject: [PATCH 058/116] reword example for clarity --- doc/manual/src/language/derivations.md | 28 ++++++++++---------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/doc/manual/src/language/derivations.md b/doc/manual/src/language/derivations.md index 6e6d3298b..e0b9d021a 100644 --- a/doc/manual/src/language/derivations.md +++ b/doc/manual/src/language/derivations.md @@ -71,11 +71,15 @@ It outputs an attribute set, and produces a [store derivation](@docroot@/glossar Examples: Imagine a library package that provides a dynamic library, header files, and documentation. - A program that links against the library doesn’t need the header files and documentation at runtime, and it doesn’t need the documentation at build time. + A program that links against such a library doesn’t need the header files and documentation at runtime, and it doesn’t need the documentation at build time. Thus, the library package could specify: ```nix - outputs = [ "lib" "dev" "doc" ]; + derivation { + # ... + outputs = [ "lib" "dev" "doc" ]; + # ... + } ``` This will cause Nix to pass environment variables `lib`, `dev`, and `doc` to the builder containing the intended store paths of each output. @@ -90,22 +94,12 @@ It outputs an attribute set, and produces a [store derivation](@docroot@/glossar for an Autoconf-style package. - You can refer to each output of a - derivation by selecting it as an attribute, e.g. - - ```nix - buildInputs = [ pkg.lib pkg.dev ]; - ``` - + You can refer to each output of a derivation by selecting it as an attribute, e.g. `myPackage.lib` or `myPackage.doc`. The first element of `outputs` determines the *default output*. - Thus, you could also write + Therefore, in the given example, `myPackage` is equivalent to `myPackage.lib`. - ```nix - buildInputs = [ pkg pkg.dev ]; - ``` - - since `pkg` is equivalent to `pkg.lib`. + - See [Advanced Attributes](./advanced-attributes.md) for more, infrequently used, optional attributes. @@ -115,9 +109,9 @@ It outputs an attribute set, and produces a [store derivation](@docroot@/glossar Attribute values are translated to environment variables as follows: - Strings are passed unchanged. - + - Integral numbers are converted to decimal notation. - + - Floating point numbers are converted to simple decimal or scientific notation with a preset precision. - A *path* (e.g., `../foo/sources.tar`) causes the referenced file From 887cbcd3958abed7cf726f7eb23ba530e021ddfb Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Thu, 21 Sep 2023 02:53:50 +0200 Subject: [PATCH 059/116] add contributing guide for documentation --- doc/manual/local.mk | 6 +- doc/manual/src/SUMMARY.md.in | 1 + doc/manual/src/contributing/contributing.md | 9 +- doc/manual/src/contributing/documentation.md | 181 +++++++++++++++++++ doc/manual/src/contributing/hacking.md | 51 ------ 5 files changed, 195 insertions(+), 53 deletions(-) create mode 100644 doc/manual/src/contributing/documentation.md diff --git a/doc/manual/local.mk b/doc/manual/local.mk index abdfd6a62..ab95af53c 100644 --- a/doc/manual/local.mk +++ b/doc/manual/local.mk @@ -173,6 +173,10 @@ doc/manual/generated/man1/nix3-manpages: $(d)/src/command-ref/new-cli done @touch $@ +# the `! -name 'contributing.md'` filter excludes the one place where +# `@docroot@` is to be preserved for documenting the mechanism +# FIXME: maybe contributing guides should live right next to the code +# instead of in the manual $(docdir)/manual/index.html: $(MANUAL_SRCS) $(d)/book.toml $(d)/anchors.jq $(d)/custom.css $(d)/src/SUMMARY.md $(d)/src/command-ref/new-cli $(d)/src/contributing/experimental-feature-descriptions.md $(d)/src/command-ref/conf-file.md $(d)/src/language/builtins.md $(d)/src/language/builtin-constants.md $(trace-gen) \ tmp="$$(mktemp -d)"; \ @@ -180,7 +184,7 @@ $(docdir)/manual/index.html: $(MANUAL_SRCS) $(d)/book.toml $(d)/anchors.jq $(d)/ find "$$tmp" -name '*.md' | while read -r file; do \ $(call process-includes,$$file,$$file); \ done; \ - find "$$tmp" -name '*.md' | while read -r file; do \ + find "$$tmp" -name '*.md' ! -name 'documentation.md' | while read -r file; do \ docroot="$$(realpath --relative-to="$$(dirname "$$file")" $$tmp/manual/src)"; \ sed -i "s,@docroot@,$$docroot,g" "$$file"; \ done; \ diff --git a/doc/manual/src/SUMMARY.md.in b/doc/manual/src/SUMMARY.md.in index c23186511..5c3667690 100644 --- a/doc/manual/src/SUMMARY.md.in +++ b/doc/manual/src/SUMMARY.md.in @@ -105,6 +105,7 @@ - [Contributing](contributing/contributing.md) - [Hacking](contributing/hacking.md) - [Testing](contributing/testing.md) + - [Documentation](contributing/documentation.md) - [Experimental Features](contributing/experimental-features.md) - [CLI guideline](contributing/cli-guideline.md) - [C++ style guide](contributing/cxx.md) diff --git a/doc/manual/src/contributing/contributing.md b/doc/manual/src/contributing/contributing.md index 854139a31..4d55c17a4 100644 --- a/doc/manual/src/contributing/contributing.md +++ b/doc/manual/src/contributing/contributing.md @@ -1 +1,8 @@ -# Contributing +# Development + +Nix is developed on GitHub. +Check the [contributing guide](https://github.com/NixOS/nix/blob/master/CONTRIBUTING.md) if you want to get involved. + +This chapter is a collection of guides for making changes to the code and documentation. + +If you're not sure where to start, try to [compile Nix from source](./hacking.md) and consider [making improvements to documentation](./documentation.md). diff --git a/doc/manual/src/contributing/documentation.md b/doc/manual/src/contributing/documentation.md new file mode 100644 index 000000000..f73ab2149 --- /dev/null +++ b/doc/manual/src/contributing/documentation.md @@ -0,0 +1,181 @@ +# Contributing documentation + +Improvements to documentation are very much appreciated, and a good way to start out with contributing to Nix. + +This is how you can help: +- Address [open issues with documentation](https://github.com/NixOS/nix/issues?q=is%3Aissue+is%3Aopen+label%3Adocumentation) +- Review [pull requests concerning documentation](https://github.com/NixOS/nix/pulls?q=is%3Apr+is%3Aopen+label%3Adocumentation) + +Incremental refactorings of the documentation build setup to make it faster or easier to understand and maintain are also welcome. + +## Building the manual + +Build the manual from scratch: + +```console +nix-build $(nix-instantiate)'!doc' +``` + +or + +```console +nix build .#^doc +``` + +and open `./result-doc/share/doc/nix/manual/index.html`. + +To build the manual incrementally, [enter the development shell](./hacking.md) and run: + +```console +make manual-html -j $NIX_BUILD_CORES +``` + +and open `./outputs/out/share/doc/nix/manual/language/index.html`. + +In order to reflect changes to the [Makefile for the manual], clear all generated files before re-building: + +[Makefile for the manual]: https://github.com/NixOS/nix/blob/master/doc/manual/local.mk + +```console +rm $(git ls-files doc/manual/ -o | grep -F '.md') && rmdir doc/manual/src/command-ref/new-cli && make manual-html -j $NIX_BUILD_CORES +``` + +## Style guide + +The goal of this style guide is to make it such that +- The manual is easy to search and skim for relevant information +- Documentation sources are easy to edit +- Changes to documentation are easy to review + +You will notice that this is not implemented consistently yet. +Please follow the guide when making additions or changes to existing documentation. +Do not make sweeping changes, unless they are programmatic and can be validated easily. + +### Language + +This manual is [reference documentation](https://diataxis.fr/reference/). +The typical usage pattern is to look up isolated pieces of information. +It should therefore aim to be correct, consistent, complete, and easy to navigate at a glance. + +- Aim for clarity and brevity. + + Please take the time to read the [plain language guidelines](https://www.plainlanguage.gov/guidelines/) for details. + +- Describe the subject factually. + + In particular, do not make value judgements or recommendations. + Check the code or add tests if in doubt. + +- Provide complete, minimal examples, and explain them. + + Readers should be able to try examples verbatim and get the same results as shown in the manual. + Always describe in words what a given example does. + + Non-trivial examples may need additional explanation, especially if they use concepts from outside the given context. + +- Use British English. + + This is a somewhat arbitrary choice to force consistency, and accounts for the fact that a majority of Nix users and developers are from Europe. + +### Links and anchors + +Reference documentation must be readable in arbitrary order. +Readers cannot be expected to have any particular prerequisite knowledge about Nix. +While the table of contents can provide guidance and full-text search can help, they are most likely to find what they need by following sensible cross-references. + +- Link to technical terms + + When mentioning Nix-specific concepts, commands, options, settings, etc., link to appropriate documentation. + Also link to external tools or concepts, especially if their meaning may be ambiguous. + You may also want to link to definitions of less common technical terms. + + Then readers won't have to actively search for definitions and are more likely to discover relevant information on their own. + + > **Note** + > + > `man` and `--help` pages don't display links. + > Use appropriate link texts such that readers of terminal output can infer search terms. + +- Do not break existing URLs between releases. + + There are countless links in the wild pointing to old versions of the manual. + We want people to find up-to-date documentation when following popular advice. + + - When moving files, update [redirects on nixos.org](https://github.com/NixOS/nixos-homepage/blob/master/netlify.toml). + + This is especially important when moving information out of the Nix manual to other resources. + + - When changing anchors, update [client-side redirects](https://github.com/NixOS/nix/blob/master/doc/manual/redirects.js) + + The current setup is cumbersome, and help making better automation is appreciated. + +The build checks for broken internal links with. +This happens late in the process, so [building the whole manual](#building-the-manual) is not suitable for iterating quickly. +[`mdbook-linkcheck`] does not implement checking [URI fragments] yet. + +[`mdbook-linkcheck`]: https://github.com/Michael-F-Bryan/mdbook-linkcheck +[URI fragments]: https://en.wikipedia.org/wiki/URI_fragment + +### Markdown conventions + +The manual is written in markdown, and rendered with [mdBook](https://github.com/rust-lang/mdBook) for the web and with [lowdown](https://github.com/kristapsdz/lowdown) for `man` pages and `--help` output. + +For supported markdown features, refer to: +- [mdBook documentation](https://rust-lang.github.io/mdBook/format/markdown.html) +- [lowdown documentation](https://kristaps.bsd.lv/lowdown/) + +Please observe these guidelines to ease reviews: + +- Write one sentence per line. + + This makes long sentences immediately visible, and makes it easier to review changes and make direct suggestions. + +- Use reference links – sparingly – to ease source readability. + Put definitions close to their first use. + + Example: + + ``` + A [store object] contains a [file system object] and [references] to other store objects. + + [store object]: @docroot@/glossary.md#gloss-store-object + [file system object]: @docroot@/architecture/file-system-object.md + [references]: @docroot@/glossary.md#gloss-reference + ``` + +- Use admonitions of the following form: + + ``` + > **Note** + > + > This is a note. + ``` + +### The `@docroot@` variable + +`@docroot@` provides a base path for links that occur in reusable snippets or other documentation that doesn't have a base path of its own. + +If a broken link occurs in a snippet that was inserted into multiple generated files in different directories, use `@docroot@` to reference the `doc/manual/src` directory. + +If the `@docroot@` literal appears in an error message from the [`mdbook-linkcheck`] tool, the `@docroot@` replacement needs to be applied to the generated source file that mentions it. +See existing `@docroot@` logic in the [Makefile for the manual]. +Regular markdown files used for the manual have a base path of their own and they can use relative paths instead of `@docroot@`. + +## API documentation + +[Doxygen API documentation] is available online. +You can also build and view it yourself: + +[Doxygen API documentation]: https://hydra.nixos.org/job/nix/master/internal-api-docs/latest/download-by-type/doc/internal-api-docs + +```console +# nix build .#hydraJobs.internal-api-docs +# xdg-open ./result/share/doc/nix/internal-api/html/index.html +``` + +or inside `nix-shell` or `nix develop`: + +``` +# make internal-api-html +# xdg-open ./outputs/doc/share/doc/nix/internal-api/html/index.html +``` diff --git a/doc/manual/src/contributing/hacking.md b/doc/manual/src/contributing/hacking.md index a2446b53a..db393942c 100644 --- a/doc/manual/src/contributing/hacking.md +++ b/doc/manual/src/contributing/hacking.md @@ -220,54 +220,3 @@ Configure your editor to use the `clangd` from the shell, either by running it i > For some editors (e.g. Visual Studio Code), you may need to install a [special extension](https://open-vsx.org/extension/llvm-vs-code-extensions/vscode-clangd) for the editor to interact with `clangd`. > Some other editors (e.g. Emacs, Vim) need a plugin to support LSP servers in general (e.g. [lsp-mode](https://github.com/emacs-lsp/lsp-mode) for Emacs and [vim-lsp](https://github.com/prabirshrestha/vim-lsp) for vim). > Editor-specific setup is typically opinionated, so we will not cover it here in more detail. - -### Checking links in the manual - -The build checks for broken internal links. -This happens late in the process, so `nix build` is not suitable for iterating. -To build the manual incrementally, run: - -```console -make manual-html -j $NIX_BUILD_CORES -``` - -In order to reflect changes to the [Makefile], clear all generated files before re-building: - -[Makefile]: https://github.com/NixOS/nix/blob/master/doc/manual/local.mk - -```console -rm $(git ls-files doc/manual/ -o | grep -F '.md') && rmdir doc/manual/src/command-ref/new-cli && make html -j $NIX_BUILD_CORES -``` - -[`mdbook-linkcheck`] does not implement checking [URI fragments] yet. - -[`mdbook-linkcheck`]: https://github.com/Michael-F-Bryan/mdbook-linkcheck -[URI fragments]: https://en.wikipedia.org/wiki/URI_fragment - -#### `@docroot@` variable - -`@docroot@` provides a base path for links that occur in reusable snippets or other documentation that doesn't have a base path of its own. - -If a broken link occurs in a snippet that was inserted into multiple generated files in different directories, use `@docroot@` to reference the `doc/manual/src` directory. - -If the `@docroot@` literal appears in an error message from the `mdbook-linkcheck` tool, the `@docroot@` replacement needs to be applied to the generated source file that mentions it. -See existing `@docroot@` logic in the [Makefile]. -Regular markdown files used for the manual have a base path of their own and they can use relative paths instead of `@docroot@`. - -## API documentation - -Doxygen API documentation is [available -online](https://hydra.nixos.org/job/nix/master/internal-api-docs/latest/download-by-type/doc/internal-api-docs). You -can also build and view it yourself: - -```console -# nix build .#hydraJobs.internal-api-docs -# xdg-open ./result/share/doc/nix/internal-api/html/index.html -``` - -or inside a `nix develop` shell by running: - -``` -# make internal-api-html -# xdg-open ./outputs/doc/share/doc/nix/internal-api/html/index.html -``` From 45de35bcf14d1bdad5dca2e24a113d26d1985aed Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Tue, 26 Sep 2023 01:24:07 +0200 Subject: [PATCH 060/116] fix broken redirects script --- doc/manual/redirects.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/redirects.js b/doc/manual/redirects.js index b43622ed6..b2fad19bb 100644 --- a/doc/manual/redirects.js +++ b/doc/manual/redirects.js @@ -343,7 +343,7 @@ const redirects = { "linux": "uninstall.html#linux", "macos": "uninstall.html#macos", "uninstalling": "uninstall.html", - } + }, "contributing/hacking.html": { "nix-with-flakes": "#building-nix-with-flakes", "classic-nix": "#building-nix", From c6f8247032612b4cc4a9f79213a887aa9ddddfda Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Tue, 26 Sep 2023 03:10:12 +0200 Subject: [PATCH 061/116] fix broken reference link --- doc/manual/src/language/derivations.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/manual/src/language/derivations.md b/doc/manual/src/language/derivations.md index e0b9d021a..4a1b70fd1 100644 --- a/doc/manual/src/language/derivations.md +++ b/doc/manual/src/language/derivations.md @@ -4,7 +4,9 @@ The most important built-in function is `derivation`, which is used to describe a specification for running an executable on precisely defined input files to repeatably produce output files at uniquely determined file system paths. It takes as input an attribute set, the attributes of which specify the inputs to the process. -It outputs an attribute set, and produces a [store derivation](@docroot@/glossary.md#gloss-store-derivation) as a side effect of evaluation. +It outputs an attribute set, and produces a [store derivation] as a side effect of evaluation. + +[store derivation]: @docroot@/glossary.md#gloss-store-derivation From 7e24dc606b1cf7fd4e4dfd7027ba1469a59784d5 Mon Sep 17 00:00:00 2001 From: Tom Bereknyei Date: Fri, 22 Sep 2023 13:05:55 -0400 Subject: [PATCH 062/116] fix(tests): fix assumption that string.s is a char* --- src/libexpr/tests/libexpr.hh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libexpr/tests/libexpr.hh b/src/libexpr/tests/libexpr.hh index b8e65aafe..97b05ac46 100644 --- a/src/libexpr/tests/libexpr.hh +++ b/src/libexpr/tests/libexpr.hh @@ -71,7 +71,7 @@ namespace nix { if (arg.type() != nString) { return false; } - return std::string_view(arg.string.s) == s; + return std::string_view(arg.c_str()) == s; } MATCHER_P(IsIntEq, v, fmt("The string is equal to \"%1%\"", v)) { @@ -106,8 +106,8 @@ namespace nix { if (arg.type() != nPath) { *result_listener << "Expected a path got " << arg.type(); return false; - } else if (std::string_view(arg.string.s) != p) { - *result_listener << "Expected a path that equals \"" << p << "\" but got: " << arg.string.s; + } else if (std::string_view(arg._path) != p) { + *result_listener << "Expected a path that equals \"" << p << "\" but got: " << arg.c_str(); return false; } return true; From b17f200b11b3901c4f975c9901f1e2c6fc54124f Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Tue, 26 Sep 2023 03:49:03 +0200 Subject: [PATCH 063/116] Document "Import From Derivation" (#7332) * document "Import From Derivation" Co-authored-by: Robert Hensing Co-authored-by: John Ericson --- doc/manual/src/SUMMARY.md.in | 1 + doc/manual/src/glossary.md | 5 +- .../src/language/import-from-derivation.md | 139 ++++++++++++++++++ src/libexpr/eval-settings.hh | 11 +- 4 files changed, 150 insertions(+), 6 deletions(-) create mode 100644 doc/manual/src/language/import-from-derivation.md diff --git a/doc/manual/src/SUMMARY.md.in b/doc/manual/src/SUMMARY.md.in index c23186511..9e2ad0618 100644 --- a/doc/manual/src/SUMMARY.md.in +++ b/doc/manual/src/SUMMARY.md.in @@ -33,6 +33,7 @@ - [Operators](language/operators.md) - [Derivations](language/derivations.md) - [Advanced Attributes](language/advanced-attributes.md) + - [Import From Derivation](language/import-from-derivation.md) - [Built-in Constants](language/builtin-constants.md) - [Built-in Functions](language/builtins.md) - [Advanced Topics](advanced-topics/advanced-topics.md) diff --git a/doc/manual/src/glossary.md b/doc/manual/src/glossary.md index b7c340d36..c23466351 100644 --- a/doc/manual/src/glossary.md +++ b/doc/manual/src/glossary.md @@ -105,12 +105,15 @@ - [store object]{#gloss-store-object} - A store object consists of a [file system object], [reference]s to other store objects, and other metadata. It can be referred to by a [store path]. [store object]: #gloss-store-object +- [IFD]{#gloss-ifd} + + [Import From Derivation](./language/import-from-derivation.md) + - [input-addressed store object]{#gloss-input-addressed-store-object} A store object produced by building a diff --git a/doc/manual/src/language/import-from-derivation.md b/doc/manual/src/language/import-from-derivation.md new file mode 100644 index 000000000..03b3f9d91 --- /dev/null +++ b/doc/manual/src/language/import-from-derivation.md @@ -0,0 +1,139 @@ +# Import From Derivation + +The value of a Nix expression can depend on the contents of a [store object](@docroot@/glossary.md#gloss-store-object). + +Passing an expression `expr` that evaluates to a [store path](@docroot@/glossary.md#gloss-store-path) to any built-in function which reads from the filesystem constitutes Import From Derivation (IFD): + +- [`import`](./builtins.md#builtins-import)` expr` +- [`builtins.readFile`](./builtins.md#builtins-readFile)` expr` +- [`builtins.readFileType`](./builtins.md#builtins-readFileType)` expr` +- [`builtins.readDir`](./builtins.md#builtins-readDir)` expr` +- [`builtins.pathExists`](./builtins.md#builtins-pathExists)` expr` +- [`builtins.filterSource`](./builtins.md#builtins-filterSource)` f expr` +- [`builtins.path`](./builtins.md#builtins-path)` { path = expr; }` +- [`builtins.hashFile`](./builtins.md#builtins-hashFile)` t expr` +- `builtins.scopedImport x drv` + +When the store path needs to be accessed, evaluation will be paused, the corresponding store object [realised], and then evaluation resumed. + +[realised]: @docroot@/glossary.md#gloss-realise + +This has performance implications: +Evaluation can only finish when all required store objects are realised. +Since the Nix language evaluator is sequential, it only finds store paths to read from one at a time. +While realisation is always parallel, in this case it cannot be done for all required store paths at once, and is therefore much slower than otherwise. + +Realising store objects during evaluation can be disabled by setting [`allow-import-from-derivation`](../command-ref/conf-file.md#conf-allow-import-from-derivation) to `false`. +Without IFD it is ensured that evaluation is complete and Nix can produce a build plan before starting any realisation. + +## Example + +In the following Nix expression, the inner derivation `drv` produces a file with contents `hello`. + +```nix +# IFD.nix +let + drv = derivation { + name = "hello"; + builder = "/bin/sh"; + args = [ "-c" "echo -n hello > $out" ]; + system = builtins.currentSystem; + }; +in "${builtins.readFile drv} world" +``` + +```shellSession +nix-instantiate IFD.nix --eval --read-write-mode +``` + +``` +building '/nix/store/348q1cal6sdgfxs8zqi9v8llrsn4kqkq-hello.drv'... +"hello world" +``` + +The contents of the derivation's output have to be [realised] before they can be read with [`readFile`](./builtins.md#builtins-readFile). +Only then evaluation can continue to produce the final result. + +## Illustration + +As a first approximation, the following data flow graph shows how evaluation and building are interleaved, if the value of a Nix expression depends on realising a [store object]. +Boxes are data structures, arrow labels are transformations. + +``` ++----------------------+ +------------------------+ +| Nix evaluator | | Nix store | +| .----------------. | | | +| | Nix expression | | | | +| '----------------' | | | +| | | | | +| evaluate | | | +| | | | | +| V | | | +| .------------. | | .------------------. | +| | derivation |----|-instantiate-|->| store derivation | | +| '------------' | | '------------------' | +| | | | | +| | | realise | +| | | | | +| | | V | +| .----------------. | | .--------------. | +| | Nix expression |<-|----read-----|----| store object | | +| '----------------' | | '--------------' | +| | | | | +| evaluate | | | +| | | | | +| V | | | +| .------------. | | | +| | value | | | | +| '------------' | | | ++----------------------+ +------------------------+ +``` + +In more detail, the following sequence diagram shows how the expression is evaluated step by step, and where evaluation is blocked to wait for the build output to appear. + +``` +.-------. .-------------. .---------. +|Nix CLI| |Nix evaluator| |Nix store| +'-------' '-------------' '---------' + | | | + |evaluate IFD.nix| | + |--------------->| | + | | | + | evaluate `"${readFile drv} world"` | + | | | + | evaluate `readFile drv` | + | | | + | evaluate `drv` as string | + | | | + | |instantiate /nix/store/...-hello.drv| + | |----------------------------------->| + | : | + | : realise /nix/store/...-hello.drv | + | :----------------------------------->| + | : | + | |--------. + | : | | + | (evaluation blocked) | echo hello > $out + | : | | + | |<-------' + | : /nix/store/...-hello | + | |<-----------------------------------| + | | | + | resume `readFile /nix/store/...-hello` | + | | | + | | readFile /nix/store/...-hello | + | |----------------------------------->| + | | | + | | hello | + | |<-----------------------------------| + | | | + | resume `"${"hello"} world"` | + | | | + | resume `"hello world"` | + | | | + | "hello world" | | + |<---------------| | +.-------. .-------------. .---------. +|Nix CLI| |Nix evaluator| |Nix store| +'-------' '-------------' '---------' +``` diff --git a/src/libexpr/eval-settings.hh b/src/libexpr/eval-settings.hh index 19122bc31..19ba679be 100644 --- a/src/libexpr/eval-settings.hh +++ b/src/libexpr/eval-settings.hh @@ -47,11 +47,12 @@ struct EvalSettings : Config Setting enableImportFromDerivation{ this, true, "allow-import-from-derivation", R"( - By default, Nix allows you to `import` from a derivation, allowing - building at evaluation time. With this option set to false, Nix will - throw an error when evaluating an expression that uses this feature, - allowing users to ensure their evaluation will not require any - builds to take place. + By default, Nix allows [Import from Derivation](@docroot@/language/import-from-derivation.md). + + With this option set to `false`, Nix will throw an error when evaluating an expression that uses this feature, + even when the required store object is readily available. + This ensures that evaluation will not require any builds to take place, + regardless of the state of the store. )"}; Setting allowedUris{this, {}, "allowed-uris", From a757749fcfa810f699fa096f98821efccba86522 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Tue, 26 Sep 2023 06:28:17 +0200 Subject: [PATCH 064/116] reword for readability --- doc/manual/src/release-notes/release-notes.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/manual/src/release-notes/release-notes.md b/doc/manual/src/release-notes/release-notes.md index 1fa2f5580..cc805e631 100644 --- a/doc/manual/src/release-notes/release-notes.md +++ b/doc/manual/src/release-notes/release-notes.md @@ -6,6 +6,7 @@ Notable changes and additions are announced in the release notes for each versio Bugfixes can be backported on request to previous Nix releases. We typically backport only as far back as the Nix version used in the latest NixOS release, which is announced in the [NixOS release notes](https://nixos.org/manual/nixos/stable/release-notes.html#ch-release-notes). -Backports never skip releases (if a feature is backported to `x.y`, it must also be available in `x.(y+1)`; +Backports never skip releases. +If a feature is backported to version `x.y`, it must also be available in version `x.(y+1)`. This ensures that upgrading from an older version with backports is still safe and no backported functionality will go missing. From 5b902ce9d60fca33c0f5bb76fcd31792c0bb03a6 Mon Sep 17 00:00:00 2001 From: Yingchi Long Date: Tue, 26 Sep 2023 23:30:32 +0800 Subject: [PATCH 065/116] libexpr: construct ExprPath by move ctor, not copy cotr --- src/libexpr/parser.y | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 792f51fde..509f4fcc4 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -520,7 +520,7 @@ path_start /* add back in the trailing '/' to the first segment */ if ($1.p[$1.l-1] == '/' && $1.l > 1) path += "/"; - $$ = new ExprPath(path); + $$ = new ExprPath(std::move(path)); } | HPATH { if (evalSettings.pureEval) { @@ -530,7 +530,7 @@ path_start ); } Path path(getHome() + std::string($1.p + 1, $1.l - 1)); - $$ = new ExprPath(path); + $$ = new ExprPath(std::move(path)); } ; From 1eeea01931d20d32c917e92d860513b7cb32ba10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christina=20S=C3=B8rensen?= Date: Wed, 27 Sep 2023 02:07:43 +0000 Subject: [PATCH 066/116] Fix repl.md duplicate typo Seems like `legacyPackages.x86_64-linux.emacs.name` is accidentally shown twice. --- src/nix/repl.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/nix/repl.md b/src/nix/repl.md index c5113be61..9dc61fd35 100644 --- a/src/nix/repl.md +++ b/src/nix/repl.md @@ -43,9 +43,6 @@ R""( nix-repl> legacyPackages.x86_64-linux.emacs.name "emacs-27.1" - nix-repl> legacyPackages.x86_64-linux.emacs.name - "emacs-27.1" - nix-repl> :q # nix repl --expr 'import {}' From 399ef8442012308f480ea5841fb6ac732207e03d Mon Sep 17 00:00:00 2001 From: Tom Bereknyei Date: Mon, 25 Sep 2023 21:30:41 -0400 Subject: [PATCH 067/116] refactor: use string accessors Create context, string_view, and c_str, accessors throughout in order to better support improvements to the underlying string representation. --- src/libcmd/repl.cc | 2 +- src/libexpr/eval-cache.cc | 8 ++++---- src/libexpr/eval.cc | 18 +++++++++--------- src/libexpr/flake/flake.cc | 10 +++++----- src/libexpr/get-drvs.cc | 12 ++++++------ src/libexpr/primops.cc | 10 +++++----- src/libexpr/primops/fetchClosure.cc | 2 +- src/libexpr/tests/primops.cc | 4 ++-- src/libexpr/value-to-json.cc | 2 +- src/libexpr/value-to-xml.cc | 6 +++--- src/libexpr/value.hh | 20 +++++++++++++++----- src/nix-env/nix-env.cc | 6 +++--- src/nix/eval.cc | 2 +- 13 files changed, 56 insertions(+), 46 deletions(-) diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index 944609f22..2e17a29a7 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -922,7 +922,7 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m case nString: str << ANSI_WARNING; - printLiteralString(str, v.string.s); + printLiteralString(str, v.string_view()); str << ANSI_NORMAL; break; diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc index 2c9aa5532..391b32a77 100644 --- a/src/libexpr/eval-cache.cc +++ b/src/libexpr/eval-cache.cc @@ -440,8 +440,8 @@ Value & AttrCursor::forceValue() if (root->db && (!cachedValue || std::get_if(&cachedValue->second))) { if (v.type() == nString) - cachedValue = {root->db->setString(getKey(), v.string.s, v.string.context), - string_t{v.string.s, {}}}; + cachedValue = {root->db->setString(getKey(), v.c_str(), v.context()), + string_t{v.c_str(), {}}}; else if (v.type() == nPath) { auto path = v.path().path; cachedValue = {root->db->setString(getKey(), path.abs()), string_t{path.abs(), {}}}; @@ -582,7 +582,7 @@ std::string AttrCursor::getString() if (v.type() != nString && v.type() != nPath) root->state.error("'%s' is not a string but %s", getAttrPathStr()).debugThrow(); - return v.type() == nString ? v.string.s : v.path().to_string(); + return v.type() == nString ? v.c_str() : v.path().to_string(); } string_t AttrCursor::getStringWithContext() @@ -624,7 +624,7 @@ string_t AttrCursor::getStringWithContext() if (v.type() == nString) { NixStringContext context; copyContext(v, context); - return {v.string.s, std::move(context)}; + return {v.c_str(), std::move(context)}; } else if (v.type() == nPath) return {v.path().to_string(), {}}; diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index afa864730..a78c0afb1 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -114,7 +114,7 @@ void Value::print(const SymbolTable &symbols, std::ostream &str, printLiteralBool(str, boolean); break; case tString: - printLiteralString(str, string.s); + printLiteralString(str, string_view()); break; case tPath: str << path().to_string(); // !!! escaping? @@ -339,7 +339,7 @@ static Symbol getName(const AttrName & name, EvalState & state, Env & env) Value nameValue; name.expr->eval(state, env, nameValue); state.forceStringNoCtx(nameValue, noPos, "while evaluating an attribute name"); - return state.symbols.create(nameValue.string.s); + return state.symbols.create(nameValue.string_view()); } } @@ -1343,7 +1343,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v) if (nameVal.type() == nNull) continue; state.forceStringNoCtx(nameVal, i.pos, "while evaluating the name of a dynamic attribute"); - auto nameSym = state.symbols.create(nameVal.string.s); + auto nameSym = state.symbols.create(nameVal.string_view()); Bindings::iterator j = v.attrs->find(nameSym); if (j != v.attrs->end()) state.error("dynamic attribute '%1%' already defined at %2%", state.symbols[nameSym], state.positions[j->pos]).atPos(i.pos).withFrame(env, *this).debugThrow(); @@ -2155,7 +2155,7 @@ std::string_view EvalState::forceString(Value & v, const PosIdx pos, std::string forceValue(v, pos); if (v.type() != nString) error("value is %1% while a string was expected", showType(v)).debugThrow(); - return v.string.s; + return v.string_view(); } catch (Error & e) { e.addTrace(positions[pos], errorCtx); throw; @@ -2182,8 +2182,8 @@ std::string_view EvalState::forceString(Value & v, NixStringContext & context, c std::string_view EvalState::forceStringNoCtx(Value & v, const PosIdx pos, std::string_view errorCtx) { auto s = forceString(v, pos, errorCtx); - if (v.string.context) { - error("the string '%1%' is not allowed to refer to a store path (such as '%2%')", v.string.s, v.string.context[0]).withTrace(pos, errorCtx).debugThrow(); + if (v.context()) { + error("the string '%1%' is not allowed to refer to a store path (such as '%2%')", v.string_view(), v.context()[0]).withTrace(pos, errorCtx).debugThrow(); } return s; } @@ -2196,7 +2196,7 @@ bool EvalState::isDerivation(Value & v) if (i == v.attrs->end()) return false; forceValue(*i->value, i->pos); if (i->value->type() != nString) return false; - return strcmp(i->value->string.s, "derivation") == 0; + return i->value->string_view().compare("derivation") == 0; } @@ -2228,7 +2228,7 @@ BackedStringView EvalState::coerceToString( if (v.type() == nString) { copyContext(v, context); - return std::string_view(v.string.s); + return v.string_view(); } if (v.type() == nPath) { @@ -2426,7 +2426,7 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v return v1.boolean == v2.boolean; case nString: - return strcmp(v1.string.s, v2.string.s) == 0; + return v1.string_view().compare(v2.string_view()) == 0; case nPath: return strcmp(v1._path, v2._path) == 0; diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc index 90427e064..a6212c12f 100644 --- a/src/libexpr/flake/flake.cc +++ b/src/libexpr/flake/flake.cc @@ -113,7 +113,7 @@ static FlakeInput parseFlakeInput(EvalState & state, try { if (attr.name == sUrl) { expectType(state, nString, *attr.value, attr.pos); - url = attr.value->string.s; + url = attr.value->string_view(); attrs.emplace("url", *url); } else if (attr.name == sFlake) { expectType(state, nBool, *attr.value, attr.pos); @@ -122,7 +122,7 @@ static FlakeInput parseFlakeInput(EvalState & state, input.overrides = parseFlakeInputs(state, attr.value, attr.pos, baseDir, lockRootPath); } else if (attr.name == sFollows) { expectType(state, nString, *attr.value, attr.pos); - auto follows(parseInputPath(attr.value->string.s)); + auto follows(parseInputPath(attr.value->c_str())); follows.insert(follows.begin(), lockRootPath.begin(), lockRootPath.end()); input.follows = follows; } else { @@ -131,7 +131,7 @@ static FlakeInput parseFlakeInput(EvalState & state, #pragma GCC diagnostic ignored "-Wswitch-enum" switch (attr.value->type()) { case nString: - attrs.emplace(state.symbols[attr.name], attr.value->string.s); + attrs.emplace(state.symbols[attr.name], attr.value->c_str()); break; case nBool: attrs.emplace(state.symbols[attr.name], Explicit { attr.value->boolean }); @@ -229,7 +229,7 @@ static Flake getFlake( if (auto description = vInfo.attrs->get(state.sDescription)) { expectType(state, nString, *description->value, description->pos); - flake.description = description->value->string.s; + flake.description = description->value->c_str(); } auto sInputs = state.symbols.create("inputs"); @@ -850,7 +850,7 @@ static void prim_flakeRefToString( Explicit { attr.value->boolean }); } else if (t == nString) { attrs.emplace(state.symbols[attr.name], - std::string(attr.value->str())); + std::string(attr.value->string_view())); } else { state.error( "flake reference attribute sets may only contain integers, Booleans, " diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc index 506a63677..fe3e6f7ee 100644 --- a/src/libexpr/get-drvs.cc +++ b/src/libexpr/get-drvs.cc @@ -156,7 +156,7 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool withPaths, bool onlyOutputsToInstall Outputs result; for (auto elem : outTI->listItems()) { if (elem->type() != nString) throw errMsg; - auto out = outputs.find(elem->string.s); + auto out = outputs.find(elem->c_str()); if (out == outputs.end()) throw errMsg; result.insert(*out); } @@ -230,7 +230,7 @@ std::string DrvInfo::queryMetaString(const std::string & name) { Value * v = queryMeta(name); if (!v || v->type() != nString) return ""; - return v->string.s; + return v->c_str(); } @@ -242,7 +242,7 @@ NixInt DrvInfo::queryMetaInt(const std::string & name, NixInt def) if (v->type() == nString) { /* Backwards compatibility with before we had support for integer meta fields. */ - if (auto n = string2Int(v->string.s)) + if (auto n = string2Int(v->c_str())) return *n; } return def; @@ -256,7 +256,7 @@ NixFloat DrvInfo::queryMetaFloat(const std::string & name, NixFloat def) if (v->type() == nString) { /* Backwards compatibility with before we had support for float meta fields. */ - if (auto n = string2Float(v->string.s)) + if (auto n = string2Float(v->c_str())) return *n; } return def; @@ -271,8 +271,8 @@ bool DrvInfo::queryMetaBool(const std::string & name, bool def) if (v->type() == nString) { /* Backwards compatibility with before we had support for Boolean meta fields. */ - if (strcmp(v->string.s, "true") == 0) return true; - if (strcmp(v->string.s, "false") == 0) return false; + if (v->string_view() == "true") return true; + if (v->string_view() == "false") return false; } return def; } diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index cd9a05bb2..1cb5cdf30 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -590,7 +590,7 @@ struct CompareValues case nFloat: return v1->fpoint < v2->fpoint; case nString: - return strcmp(v1->string.s, v2->string.s) < 0; + return v1->string_view().compare(v2->string_view()) < 0; case nPath: return strcmp(v1->_path, v2->_path) < 0; case nList: @@ -982,7 +982,7 @@ static void prim_trace(EvalState & state, const PosIdx pos, Value * * args, Valu { state.forceValue(*args[0], pos); if (args[0]->type() == nString) - printError("trace: %1%", args[0]->string.s); + printError("trace: %1%", args[0]->string_view()); else printError("trace: %1%", printValue(state, *args[0])); state.forceValue(*args[1], pos); @@ -1528,7 +1528,7 @@ static void prim_pathExists(EvalState & state, const PosIdx pos, Value * * args, auto path = realisePath(state, pos, arg, { .checkForPureEval = false }); /* SourcePath doesn't know about trailing slash. */ - auto mustBeDir = arg.type() == nString && arg.str().ends_with("/"); + auto mustBeDir = arg.type() == nString && arg.string_view().ends_with("/"); try { auto checked = state.checkSourcePath(path); @@ -2400,7 +2400,7 @@ static void prim_attrNames(EvalState & state, const PosIdx pos, Value * * args, (v.listElems()[n++] = state.allocValue())->mkString(state.symbols[i.name]); std::sort(v.listElems(), v.listElems() + n, - [](Value * v1, Value * v2) { return strcmp(v1->string.s, v2->string.s) < 0; }); + [](Value * v1, Value * v2) { return v1->string_view().compare(v2->string_view()) < 0; }); } static RegisterPrimOp primop_attrNames({ @@ -2541,7 +2541,7 @@ static void prim_removeAttrs(EvalState & state, const PosIdx pos, Value * * args names.reserve(args[1]->listSize()); for (auto elem : args[1]->listItems()) { state.forceStringNoCtx(*elem, pos, "while evaluating the values of the second argument passed to builtins.removeAttrs"); - names.emplace_back(state.symbols.create(elem->string.s), nullptr); + names.emplace_back(state.symbols.create(elem->string_view()), nullptr); } std::sort(names.begin(), names.end()); diff --git a/src/libexpr/primops/fetchClosure.cc b/src/libexpr/primops/fetchClosure.cc index 7fe8203f4..b86ef6b93 100644 --- a/src/libexpr/primops/fetchClosure.cc +++ b/src/libexpr/primops/fetchClosure.cc @@ -133,7 +133,7 @@ static void prim_fetchClosure(EvalState & state, const PosIdx pos, Value * * arg else if (attrName == "toPath") { state.forceValue(*attr.value, attr.pos); - bool isEmptyString = attr.value->type() == nString && attr.value->string.s == std::string(""); + bool isEmptyString = attr.value->type() == nString && attr.value->string_view() == ""; if (isEmptyString) { toPath = StorePathOrGap {}; } diff --git a/src/libexpr/tests/primops.cc b/src/libexpr/tests/primops.cc index ce3b5d11f..d820b860e 100644 --- a/src/libexpr/tests/primops.cc +++ b/src/libexpr/tests/primops.cc @@ -711,14 +711,14 @@ namespace nix { // FIXME: add a test that verifies the string context is as expected auto v = eval("builtins.replaceStrings [\"oo\" \"a\"] [\"a\" \"i\"] \"foobar\""); ASSERT_EQ(v.type(), nString); - ASSERT_EQ(v.string.s, std::string_view("fabir")); + ASSERT_EQ(v.string_view(), "fabir"); } TEST_F(PrimOpTest, concatStringsSep) { // FIXME: add a test that verifies the string context is as expected auto v = eval("builtins.concatStringsSep \"%\" [\"foo\" \"bar\" \"baz\"]"); ASSERT_EQ(v.type(), nString); - ASSERT_EQ(std::string_view(v.string.s), "foo%bar%baz"); + ASSERT_EQ(v.string_view(), "foo%bar%baz"); } TEST_F(PrimOpTest, split1) { diff --git a/src/libexpr/value-to-json.cc b/src/libexpr/value-to-json.cc index ac3986c87..cbc91f509 100644 --- a/src/libexpr/value-to-json.cc +++ b/src/libexpr/value-to-json.cc @@ -31,7 +31,7 @@ json printValueAsJSON(EvalState & state, bool strict, case nString: copyContext(v, context); - out = v.string.s; + out = v.c_str(); break; case nPath: diff --git a/src/libexpr/value-to-xml.cc b/src/libexpr/value-to-xml.cc index 2539ad1c1..bd7a4ae30 100644 --- a/src/libexpr/value-to-xml.cc +++ b/src/libexpr/value-to-xml.cc @@ -74,7 +74,7 @@ static void printValueAsXML(EvalState & state, bool strict, bool location, case nString: /* !!! show the context? */ copyContext(v, context); - doc.writeEmptyElement("string", singletonAttrs("value", v.string.s)); + doc.writeEmptyElement("string", singletonAttrs("value", v.c_str())); break; case nPath: @@ -96,14 +96,14 @@ static void printValueAsXML(EvalState & state, bool strict, bool location, if (a != v.attrs->end()) { if (strict) state.forceValue(*a->value, a->pos); if (a->value->type() == nString) - xmlAttrs["drvPath"] = drvPath = a->value->string.s; + xmlAttrs["drvPath"] = drvPath = a->value->c_str(); } a = v.attrs->find(state.sOutPath); if (a != v.attrs->end()) { if (strict) state.forceValue(*a->value, a->pos); if (a->value->type() == nString) - xmlAttrs["outPath"] = a->value->string.s; + xmlAttrs["outPath"] = a->value->c_str(); } XMLOpenElement _(doc, "derivation", xmlAttrs); diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh index c44683e50..7a1165cac 100644 --- a/src/libexpr/value.hh +++ b/src/libexpr/value.hh @@ -186,10 +186,9 @@ public: * For canonicity, the store paths should be in sorted order. */ struct { - const char * s; + const char * c_str; const char * * context; // must be in sorted order } string; - const char * _path; Bindings * attrs; struct { @@ -270,7 +269,7 @@ public: inline void mkString(const char * s, const char * * context = 0) { internalType = tString; - string.s = s; + string.c_str = s; string.context = context; } @@ -441,10 +440,21 @@ public: return SourcePath{CanonPath(_path)}; } - std::string_view str() const + std::string_view string_view() const { assert(internalType == tString); - return std::string_view(string.s); + return std::string_view(string.c_str); + } + + const char * const c_str() const + { + assert(internalType == tString); + return string.c_str; + } + + const char * * context() const + { + return string.context; } }; diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc index b112e8cb3..e455a50fd 100644 --- a/src/nix-env/nix-env.cc +++ b/src/nix-env/nix-env.cc @@ -1228,7 +1228,7 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs) else { if (v->type() == nString) { attrs2["type"] = "string"; - attrs2["value"] = v->string.s; + attrs2["value"] = v->c_str(); xml.writeEmptyElement("meta", attrs2); } else if (v->type() == nInt) { attrs2["type"] = "int"; @@ -1248,7 +1248,7 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs) for (auto elem : v->listItems()) { if (elem->type() != nString) continue; XMLAttrs attrs3; - attrs3["value"] = elem->string.s; + attrs3["value"] = elem->c_str(); xml.writeEmptyElement("string", attrs3); } } else if (v->type() == nAttrs) { @@ -1260,7 +1260,7 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs) if(a.value->type() != nString) continue; XMLAttrs attrs3; attrs3["type"] = globals.state->symbols[i.name]; - attrs3["value"] = a.value->string.s; + attrs3["value"] = a.value->c_str(); xml.writeEmptyElement("string", attrs3); } } diff --git a/src/nix/eval.cc b/src/nix/eval.cc index d880bef0a..b34af34e0 100644 --- a/src/nix/eval.cc +++ b/src/nix/eval.cc @@ -85,7 +85,7 @@ struct CmdEval : MixJSON, InstallableValueCommand, MixReadOnlyOption state->forceValue(v, pos); if (v.type() == nString) // FIXME: disallow strings with contexts? - writeFile(path, v.string.s); + writeFile(path, v.string_view()); else if (v.type() == nAttrs) { if (mkdir(path.c_str(), 0777) == -1) throw SysError("creating directory '%s'", path); From 5bc540a8ca7ada282839ab58225a1a24cc723c4e Mon Sep 17 00:00:00 2001 From: Alex Ameen Date: Wed, 27 Sep 2023 14:49:52 -0500 Subject: [PATCH 068/116] Respect `NOCOLOR` While `nix` has always been respectful towards requests for `NO_COLOR=1`, this change asks represents a new stage of maturity for `nix` - making it also respect quests for `NOCOLOR=1`. This ideally makes the tool more accessible to folks like me, who are exhausted by guessing whether `NO_COLOR` or `NOCOLOR` is the right environment variable to set. <3 --- src/libutil/util.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libutil/util.cc b/src/libutil/util.cc index 5a10c69e2..3b4c181e5 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -1515,7 +1515,7 @@ bool shouldANSI() { return isatty(STDERR_FILENO) && getEnv("TERM").value_or("dumb") != "dumb" - && !getEnv("NO_COLOR").has_value(); + && !(getEnv("NO_COLOR").has_value() || getEnv("NOCOLOR").has_value()); } std::string filterANSIEscapes(std::string_view s, bool filterAll, unsigned int width) From dafa38213bda956525fb692eea33f2ec4fed7ff3 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 27 Sep 2023 23:10:39 +0100 Subject: [PATCH 069/116] Update doc/manual/src/advanced-topics/post-build-hook.md Co-authored-by: Valentin Gagarin --- doc/manual/src/advanced-topics/post-build-hook.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/src/advanced-topics/post-build-hook.md b/doc/manual/src/advanced-topics/post-build-hook.md index 17b67111c..f7b3b1317 100644 --- a/doc/manual/src/advanced-topics/post-build-hook.md +++ b/doc/manual/src/advanced-topics/post-build-hook.md @@ -69,7 +69,7 @@ exec nix copy --to "s3://example-nix-cache" $OUT_PATHS > store sign`. Nix guarantees the paths will not contain any spaces, > however a store path might contain glob characters. The `set -f` > disables globbing in the shell. -> If you want to upload the .drv file too, the `$DRV_PATH` variable +> If you want to upload the `.drv` file too, the `$DRV_PATH` variable > is also defined for the script and works just like `$OUT_PATHS`. Then make sure the hook program is executable by the `root` user: From 13ed5d710674a9467c8959fed12641b40e38c791 Mon Sep 17 00:00:00 2001 From: Ilan Joselevich Date: Tue, 26 Sep 2023 00:31:00 +0200 Subject: [PATCH 070/116] flakes: adopt repl-flake behavior as default --- doc/manual/src/release-notes/rl-next.md | 2 ++ src/libutil/experimental-features.cc | 2 ++ src/nix/repl.cc | 2 +- src/nix/repl.md | 2 +- tests/repl.sh | 11 +++-------- 5 files changed, 9 insertions(+), 10 deletions(-) diff --git a/doc/manual/src/release-notes/rl-next.md b/doc/manual/src/release-notes/rl-next.md index 2467e7ccc..a69cad1db 100644 --- a/doc/manual/src/release-notes/rl-next.md +++ b/doc/manual/src/release-notes/rl-next.md @@ -3,3 +3,5 @@ - [URL flake references](@docroot@/command-ref/new-cli/nix3-flake.md#flake-references) now support [percent-encoded](https://datatracker.ietf.org/doc/html/rfc3986#section-2.1) characters. - [Path-like flake references](@docroot@/command-ref/new-cli/nix3-flake.md#path-like-syntax) now accept arbitrary unicode characters (except `#` and `?`). + +- The experimental feature `repl-flake` is no longer needed, as its functionality is now part of the `flakes` experimental feature. To get the previous behavior, use the `--file/--expr` flags accordingly. diff --git a/src/libutil/experimental-features.cc b/src/libutil/experimental-features.cc index 6d92222b9..203455b63 100644 --- a/src/libutil/experimental-features.cc +++ b/src/libutil/experimental-features.cc @@ -163,6 +163,8 @@ constexpr std::array xpFeatureDetails = {{ .tag = Xp::ReplFlake, .name = "repl-flake", .description = R"( + *Enabled with [`flakes`](#xp-feature-flakes) since 2.19* + Allow passing [installables](@docroot@/command-ref/new-cli/nix.md#installables) to `nix repl`, making its interface consistent with the other experimental commands. )", }, diff --git a/src/nix/repl.cc b/src/nix/repl.cc index 9677c1b48..63fe3044b 100644 --- a/src/nix/repl.cc +++ b/src/nix/repl.cc @@ -47,7 +47,7 @@ struct CmdRepl : RawInstallablesCommand void applyDefaultInstallables(std::vector & rawInstallables) override { - if (!experimentalFeatureSettings.isEnabled(Xp::ReplFlake) && !(file) && rawInstallables.size() >= 1) { + if (!experimentalFeatureSettings.isEnabled(Xp::Flakes) && !(file) && rawInstallables.size() >= 1) { warn("future versions of Nix will require using `--file` to load a file"); if (rawInstallables.size() > 1) warn("more than one input file is not currently supported"); diff --git a/src/nix/repl.md b/src/nix/repl.md index 9dc61fd35..32c08e24b 100644 --- a/src/nix/repl.md +++ b/src/nix/repl.md @@ -36,7 +36,7 @@ R""( Loading Installable ''... Added 1 variables. - # nix repl --extra-experimental-features 'flakes repl-flake' nixpkgs + # nix repl --extra-experimental-features 'flakes' nixpkgs Loading Installable 'flake:nixpkgs#'... Added 5 variables. diff --git a/tests/repl.sh b/tests/repl.sh index bb8b60e50..1b779c1f5 100644 --- a/tests/repl.sh +++ b/tests/repl.sh @@ -105,18 +105,13 @@ testReplResponseNoRegex ' testReplResponse ' drvPath ' '".*-simple.drv"' \ -$testDir/simple.nix +--file $testDir/simple.nix testReplResponse ' drvPath ' '".*-simple.drv"' \ --file $testDir/simple.nix --experimental-features 'ca-derivations' -testReplResponse ' -drvPath -' '".*-simple.drv"' \ ---file $testDir/simple.nix --extra-experimental-features 'repl-flake ca-derivations' - mkdir -p flake && cat < flake/flake.nix { outputs = { self }: { @@ -130,7 +125,7 @@ EOF testReplResponse ' foo + baz ' "3" \ - ./flake ./flake\#bar --experimental-features 'flakes repl-flake' + ./flake ./flake\#bar --experimental-features 'flakes' # Test the `:reload` mechansim with flakes: # - Eval `./flake#changingThing` @@ -143,7 +138,7 @@ sleep 1 # Leave the repl the time to eval 'foo' sed -i 's/beforeChange/afterChange/' flake/flake.nix echo ":reload" echo "changingThing" -) | nix repl ./flake --experimental-features 'flakes repl-flake') +) | nix repl ./flake --experimental-features 'flakes') echo "$replResult" | grepQuiet -s beforeChange echo "$replResult" | grepQuiet -s afterChange From add7c99c3b559e531b7426e7a0a987231e0028ed Mon Sep 17 00:00:00 2001 From: Andrea Bedini Date: Mon, 18 Sep 2023 16:57:18 +0800 Subject: [PATCH 071/116] Include "original" and "locked" in nix flake prefetch --json --- src/nix/flake.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 87dd4da1b..f8df032c8 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -1342,6 +1342,8 @@ struct CmdFlakePrefetch : FlakeCommand, MixJSON auto res = nlohmann::json::object(); res["storePath"] = store->printStorePath(tree.storePath); res["hash"] = hash.to_string(SRI, true); + res["original"] = fetchers::attrsToJSON(resolvedRef.toAttrs()); + res["locked"] = fetchers::attrsToJSON(lockedRef.toAttrs()); logger->cout(res.dump()); } else { notice("Downloaded '%s' to '%s' (hash '%s').", From cede94dbf7733c2c64bdc4a4f53645cffdd73379 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 28 Sep 2023 20:51:25 -0400 Subject: [PATCH 072/116] `builtins.fetchTree`: Mark experimental the new way This helps ensure uniform docs/error message. --- src/libexpr/primops/fetchTree.cc | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index 46d31a4e9..124165896 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -195,7 +195,6 @@ static void fetchTree( static void prim_fetchTree(EvalState & state, const PosIdx pos, Value * * args, Value & v) { - experimentalFeatureSettings.require(Xp::Flakes); fetchTree(state, pos, args, v, std::nullopt, FetchTreeParams { .allowNameArgument = false }); } @@ -237,12 +236,9 @@ static RegisterPrimOp primop_fetchTree({ ```nix builtins.fetchTree "https://example.com/" ``` - - > **Note** - > - > This function requires the [`flakes` experimental feature](@docroot@/contributing/experimental-features.md#xp-feature-flakes) to be enabled. )", - .fun = prim_fetchTree + .fun = prim_fetchTree, + .experimentalFeature = Xp::Flakes, }); static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v, From b912f3a9377ce0b7302b8833934bc1af448ea996 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 28 Sep 2023 20:55:41 -0400 Subject: [PATCH 073/116] Move `flakeIdRegex{,S}` from `libutil` to `flakeref.{cc,hh` It isn't used, and doesn't belong in `libutil`. --- src/libexpr/flake/flakeref.cc | 2 ++ src/libexpr/flake/flakeref.hh | 3 +++ src/libutil/url-parts.hh | 3 --- src/libutil/url.cc | 1 - 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/libexpr/flake/flakeref.cc b/src/libexpr/flake/flakeref.cc index f7ef19b3f..93ad33a33 100644 --- a/src/libexpr/flake/flakeref.cc +++ b/src/libexpr/flake/flakeref.cc @@ -289,4 +289,6 @@ std::tuple parseFlakeRefWithFragment return {std::move(flakeRef), fragment, std::move(extendedOutputsSpec)}; } +std::regex flakeIdRegex(flakeIdRegexS, std::regex::ECMAScript); + } diff --git a/src/libexpr/flake/flakeref.hh b/src/libexpr/flake/flakeref.hh index a7c9208c0..ffb2e50de 100644 --- a/src/libexpr/flake/flakeref.hh +++ b/src/libexpr/flake/flakeref.hh @@ -6,6 +6,7 @@ #include "fetchers.hh" #include "outputs-spec.hh" +#include #include namespace nix { @@ -91,5 +92,7 @@ std::tuple parseFlakeRefWithFragment bool allowMissing = false, bool isFlake = true); +const static std::string flakeIdRegexS = "[a-zA-Z][a-zA-Z0-9_-]*"; +extern std::regex flakeIdRegex; } diff --git a/src/libutil/url-parts.hh b/src/libutil/url-parts.hh index 98162b0f7..4712c0c91 100644 --- a/src/libutil/url-parts.hh +++ b/src/libutil/url-parts.hh @@ -41,7 +41,4 @@ extern std::regex revRegex; /// A ref or revision, or a ref followed by a revision. const static std::string refAndOrRevRegex = "(?:(" + revRegexS + ")|(?:(" + refRegexS + ")(?:/(" + revRegexS + "))?))"; -const static std::string flakeIdRegexS = "[a-zA-Z][a-zA-Z0-9_-]*"; -extern std::regex flakeIdRegex; - } diff --git a/src/libutil/url.cc b/src/libutil/url.cc index e700c8eaf..2970f8d2d 100644 --- a/src/libutil/url.cc +++ b/src/libutil/url.cc @@ -8,7 +8,6 @@ namespace nix { std::regex refRegex(refRegexS, std::regex::ECMAScript); std::regex badGitRefRegex(badGitRefRegexS, std::regex::ECMAScript); std::regex revRegex(revRegexS, std::regex::ECMAScript); -std::regex flakeIdRegex(flakeIdRegexS, std::regex::ECMAScript); ParsedURL parseURL(const std::string & url) { From c816c67eed6d8508a84f40ca00ac93f5926b44be Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 28 Sep 2023 21:10:17 -0400 Subject: [PATCH 074/116] Reword some comments/API docs to reflect `libfetcher`'s multiple users It's not just flakes, but also `builtins.fetchTree`. Also try to provide some more info in general. --- src/libfetchers/attrs.hh | 6 ++++++ src/libfetchers/fetchers.cc | 3 ++- src/libfetchers/fetchers.hh | 31 ++++++++++++++++++++----------- 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/src/libfetchers/attrs.hh b/src/libfetchers/attrs.hh index 9f885a793..b9a2c824e 100644 --- a/src/libfetchers/attrs.hh +++ b/src/libfetchers/attrs.hh @@ -13,6 +13,12 @@ namespace nix::fetchers { typedef std::variant> Attr; + +/** + * An `Attrs` can be thought of a JSON object restricted or simplified + * to be "flat", not containing any subcontainers (arrays or objects) + * and also not containing any `null`s. + */ typedef std::map Attrs; Attrs jsonToAttrs(const nlohmann::json & json); diff --git a/src/libfetchers/fetchers.cc b/src/libfetchers/fetchers.cc index e683b9f80..7bffb04ff 100644 --- a/src/libfetchers/fetchers.cc +++ b/src/libfetchers/fetchers.cc @@ -254,7 +254,8 @@ std::optional Input::getRev() const try { hash = Hash::parseAnyPrefixed(*s); } catch (BadHash &e) { - // Default to sha1 for backwards compatibility with existing flakes + // Default to sha1 for backwards compatibility with existing + // usages (e.g. `builtins.fetchTree` calls or flake inputs). hash = Hash::parseAny(*s, htSHA1); } } diff --git a/src/libfetchers/fetchers.hh b/src/libfetchers/fetchers.hh index 6e10e9513..b27ac4128 100644 --- a/src/libfetchers/fetchers.hh +++ b/src/libfetchers/fetchers.hh @@ -22,12 +22,11 @@ struct Tree struct InputScheme; /** - * The Input object is generated by a specific fetcher, based on the - * user-supplied input attribute in the flake.nix file, and contains + * The `Input` object is generated by a specific fetcher, based on + * user-supplied information, and contains * the information that the specific fetcher needs to perform the * actual fetch. The Input object is most commonly created via the - * "fromURL()" or "fromAttrs()" static functions which are provided - * the url or attrset specified in the flake file. + * `fromURL()` or `fromAttrs()` static functions. */ struct Input { @@ -44,10 +43,20 @@ struct Input std::optional parent; public: + /** + * Create an `Input` from a URL. + * + * The URL indicate which sort of fetcher, and provides information to that fetcher. + */ static Input fromURL(const std::string & url, bool requireTree = true); static Input fromURL(const ParsedURL & url, bool requireTree = true); + /** + * Create an `Input` from a an `Attrs`. + * + * The URL indicate which sort of fetcher, and provides information to that fetcher. + */ static Input fromAttrs(Attrs && attrs); ParsedURL toURL() const; @@ -116,13 +125,13 @@ public: /** - * The InputScheme represents a type of fetcher. Each fetcher - * registers with nix at startup time. When processing an input for a - * flake, each scheme is given an opportunity to "recognize" that - * input from the url or attributes in the flake file's specification - * and return an Input object to represent the input if it is - * recognized. The Input object contains the information the fetcher - * needs to actually perform the "fetch()" when called. + * The `InputScheme` represents a type of fetcher. Each fetcher + * registers with nix at startup time. When processing an `Input`, + * each scheme is given an opportunity to "recognize" that + * input from the user-provided url or attributes + * and return an `Input` object to represent the input if it is + * recognized. The `Input` object contains the information the fetcher + * needs to actually perform the `fetch()` when called. */ struct InputScheme { From bfe1308d3ff7f3dfd4facf4318adcb459e57ece9 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 28 Sep 2023 21:21:58 -0400 Subject: [PATCH 075/116] Add infra for `InputSchemes` to be experimental --- src/libfetchers/fetchers.cc | 7 +++++++ src/libfetchers/fetchers.hh | 5 +++++ src/libutil/args.hh | 2 +- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/libfetchers/fetchers.cc b/src/libfetchers/fetchers.cc index 7bffb04ff..e3d3ac562 100644 --- a/src/libfetchers/fetchers.cc +++ b/src/libfetchers/fetchers.cc @@ -36,6 +36,7 @@ Input Input::fromURL(const ParsedURL & url, bool requireTree) for (auto & inputScheme : *inputSchemes) { auto res = inputScheme->inputFromURL(url, requireTree); if (res) { + experimentalFeatureSettings.require(inputScheme->experimentalFeature()); res->scheme = inputScheme; fixupInput(*res); return std::move(*res); @@ -50,6 +51,7 @@ Input Input::fromAttrs(Attrs && attrs) for (auto & inputScheme : *inputSchemes) { auto res = inputScheme->inputFromAttrs(attrs); if (res) { + experimentalFeatureSettings.require(inputScheme->experimentalFeature()); res->scheme = inputScheme; fixupInput(*res); return std::move(*res); @@ -309,4 +311,9 @@ void InputScheme::clone(const Input & input, const Path & destDir) const throw Error("do not know how to clone input '%s'", input.to_string()); } +std::optional InputScheme::experimentalFeature() +{ + return {}; +} + } diff --git a/src/libfetchers/fetchers.hh b/src/libfetchers/fetchers.hh index b27ac4128..b55aabb6f 100644 --- a/src/libfetchers/fetchers.hh +++ b/src/libfetchers/fetchers.hh @@ -158,6 +158,11 @@ struct InputScheme virtual void markChangedFile(const Input & input, std::string_view file, std::optional commitMsg); virtual std::pair fetch(ref store, const Input & input) = 0; + + /** + * Is this `InputScheme` part of an experimental feature? + */ + virtual std::optional experimentalFeature(); }; void registerInputScheme(std::shared_ptr && fetcher); diff --git a/src/libutil/args.hh b/src/libutil/args.hh index d90129796..1e5bac650 100644 --- a/src/libutil/args.hh +++ b/src/libutil/args.hh @@ -236,7 +236,7 @@ struct Command : virtual public Args static constexpr Category catDefault = 0; - virtual std::optional experimentalFeature (); + virtual std::optional experimentalFeature(); virtual Category category() { return catDefault; } }; From 89b39520630726b7c48e8cbc065643134352e43d Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 28 Sep 2023 21:35:36 -0400 Subject: [PATCH 076/116] Make the indirect fetcher input scheme part of the Flakes XP feature I don't know much about it, but by the number of times "flake" appears in the code it seems like is part of flakes, at least for now. --- src/libfetchers/indirect.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libfetchers/indirect.cc b/src/libfetchers/indirect.cc index 4874a43ff..e09d8bfb8 100644 --- a/src/libfetchers/indirect.cc +++ b/src/libfetchers/indirect.cc @@ -98,6 +98,11 @@ struct IndirectInputScheme : InputScheme { throw Error("indirect input '%s' cannot be fetched directly", input.to_string()); } + + std::optional experimentalFeature() override + { + return Xp::Flakes; + } }; static auto rIndirectInputScheme = OnStartup([] { registerInputScheme(std::make_unique()); }); From 1dd03c62ad8f09b50dd77c18b973d4aee2112f7a Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Fri, 29 Sep 2023 10:46:42 +0200 Subject: [PATCH 077/116] add hint for troubleshooting tests --- doc/manual/src/contributing/testing.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/doc/manual/src/contributing/testing.md b/doc/manual/src/contributing/testing.md index 881118505..4a7f1c15c 100644 --- a/doc/manual/src/contributing/testing.md +++ b/doc/manual/src/contributing/testing.md @@ -130,6 +130,15 @@ GNU gdb (GDB) 12.1 One can debug the Nix invocation in all the usual ways. For example, enter `run` to start the Nix invocation. +### Troubleshooting + +Sometimes running tests in the development shell may leave artefacts in the local repository. +To remove any traces of that: + +```console +git clean -x --force tests +``` + ### Characterization testing Occasionally, Nix utilizes a technique called [Characterization Testing](https://en.wikipedia.org/wiki/Characterization_test) as part of the functional tests. From 08145a5be5cbc963a7006f65d9417f710b3fb8b5 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Fri, 29 Sep 2023 16:17:51 +0200 Subject: [PATCH 078/116] contributor guide: emphasize solving a well-specified problem with each pull request this moves the orientation step to the beginning, and adds notes how to make sure that a problem is well-spefified and the according change more likely to get accepted Co-authored-by: Robert Hensing --- CONTRIBUTING.md | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index facbf0eb0..4a9cff3b2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -24,25 +24,30 @@ Check out the [security policy](https://github.com/NixOS/nix/security/policy). ## Making changes to Nix -1. Check for [pull requests](https://github.com/NixOS/nix/pulls) that might already cover the contribution you are about to make. - There are many open pull requests that might already do what you intent to work on. - You can use [labels](https://github.com/NixOS/nix/labels) to filter for relevant topics. - -2. Search for related issues that cover what you're going to work on. It could help to mention there that you will work on the issue. +1. Search for related issues that cover what you're going to work on. + It could help to mention there that you will work on the issue. Issues labeled [good first issue](https://github.com/NixOS/nix/labels/good-first-issue) should be relatively easy to fix and are likely to get merged quickly. - Pull requests addressing issues labeled [idea approved](https://github.com/NixOS/nix/labels/idea%20approved) are especially welcomed by maintainers and will receive prioritised review. + Pull requests addressing issues labeled [idea approved](https://github.com/NixOS/nix/labels/idea%20approved) or [RFC](https://github.com/NixOS/nix/labels/RFC) are especially welcomed by maintainers and will receive prioritised review. + + If there is no relevant issue yet and you're not sure whether your change is likely to be accepted, [open an issue](https://github.com/NixOS/nix/issues/new/choose) yourself. + +2. Check for [pull requests](https://github.com/NixOS/nix/pulls) that might already cover the contribution you are about to make. + There are many open pull requests that might already do what you intend to work on. + You can use [labels](https://github.com/NixOS/nix/labels) to filter for relevant topics. 3. Check the [Nix reference manual](https://nixos.org/manual/nix/unstable/contributing/hacking.html) for information on building Nix and running its tests. For contributions to the command line interface, please check the [CLI guidelines](https://nixos.org/manual/nix/unstable/contributing/cli-guideline.html). -4. Make your changes! +4. Make your change! 5. [Create a pull request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request) for your changes. - * Link related issues in your pull request to inform interested parties and future contributors about your change. + * Clearly explain the problem that you're solving. + + Link related issues to inform interested parties and future contributors about your change. + If your pull request closes one or multiple issues, mention that in the description using `Closes: #`, as it will then happen automatically when your change is merged. * Make sure to have [a clean history of commits on your branch by using rebase](https://www.digitalocean.com/community/tutorials/how-to-rebase-and-update-a-pull-request). - If your pull request closes one or multiple issues, note that in the description using `Closes: #`, as it will then happen automatically when your change is merged. * [Mark the pull request as draft](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/changing-the-stage-of-a-pull-request) if you're not done with the changes. 6. Do not expect your pull request to be reviewed immediately. From f8a3893e8d77ce4a6e23719a0b2d88464cb84b9c Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 2 Sep 2023 22:47:38 +0200 Subject: [PATCH 079/116] pathExists: isDir when endswith /. --- src/libexpr/primops.cc | 4 +++- tests/lang/eval-okay-pathexists.nix | 21 +++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index cb1f99acb..5de1b2828 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1528,7 +1528,9 @@ static void prim_pathExists(EvalState & state, const PosIdx pos, Value * * args, auto path = realisePath(state, pos, arg, { .checkForPureEval = false }); /* SourcePath doesn't know about trailing slash. */ - auto mustBeDir = arg.type() == nString && arg.string_view().ends_with("/"); + auto mustBeDir = arg.type() == nString + && (arg.string_view().ends_with("/") + || arg.string_view().ends_with("/.")); try { auto checked = state.checkSourcePath(path); diff --git a/tests/lang/eval-okay-pathexists.nix b/tests/lang/eval-okay-pathexists.nix index e1246e370..c5e7a62de 100644 --- a/tests/lang/eval-okay-pathexists.nix +++ b/tests/lang/eval-okay-pathexists.nix @@ -2,6 +2,27 @@ builtins.pathExists (./lib.nix) && builtins.pathExists (builtins.toPath ./lib.nix) && builtins.pathExists (builtins.toString ./lib.nix) && !builtins.pathExists (builtins.toString ./lib.nix + "/") +&& !builtins.pathExists (builtins.toString ./lib.nix + "/.") +# FIXME +# && !builtins.pathExists (builtins.toString ./lib.nix + "/..") +# && !builtins.pathExists (builtins.toString ./lib.nix + "/a/..") +# && !builtins.pathExists (builtins.toString ./lib.nix + "/../lib.nix") +&& !builtins.pathExists (builtins.toString ./lib.nix + "/./") +&& !builtins.pathExists (builtins.toString ./lib.nix + "/./.") +&& builtins.pathExists (builtins.toString ./.. + "/lang/lib.nix") +&& !builtins.pathExists (builtins.toString ./.. + "lang/lib.nix") +&& builtins.pathExists (builtins.toString ./. + "/../lang/lib.nix") +&& builtins.pathExists (builtins.toString ./. + "/../lang/./lib.nix") +&& builtins.pathExists (builtins.toString ./.) +&& builtins.pathExists (builtins.toString ./. + "/") +&& builtins.pathExists (builtins.toString ./. + "/../lang") +&& builtins.pathExists (builtins.toString ./. + "/../lang/") +&& builtins.pathExists (builtins.toString ./. + "/../lang/.") +&& builtins.pathExists (builtins.toString ./. + "/../lang/./") +&& builtins.pathExists (builtins.toString ./. + "/../lang//./") +&& builtins.pathExists (builtins.toString ./. + "/../lang/..") +&& builtins.pathExists (builtins.toString ./. + "/../lang/../") +&& builtins.pathExists (builtins.toString ./. + "/../lang/..//") && builtins.pathExists (builtins.toPath (builtins.toString ./lib.nix)) && !builtins.pathExists (builtins.toPath (builtins.toString ./bla.nix)) && builtins.pathExists ./lib.nix From bfdd908f7dee5ae04c6a501167ea0fe1f4e08591 Mon Sep 17 00:00:00 2001 From: Maximilian Bosch Date: Sun, 24 Sep 2023 13:19:04 +0200 Subject: [PATCH 080/116] structured attrs: improve support / usage of NIX_ATTRS_{SH,JSON}_FILE In #4770 I implemented proper `nix-shell(1)` support for derivations using `__structuredAttrs = true;`. Back then we decided to introduce two new environment variables, `NIX_ATTRS_SH_FILE` for `.attrs.sh` and `NIX_ATTRS_JSON_FILE` for `.attrs.json`. This was to avoid having to copy these files to `$NIX_BUILD_TOP` in a `nix-shell(1)` session which effectively meant copying these files to the project dir without cleaning up afterwords[1]. On last NixCon I resumed hacking on `__structuredAttrs = true;` by default for `nixpkgs` with a few other folks and getting back to it, I identified a few problems with the how it's used in `nixpkgs`: * A lot of builders in `nixpkgs` don't care about the env vars and assume that `.attrs.sh` and `.attrs.json` are in `$NIX_BUILD_TOP`. The sole reason why this works is that `nix-shell(1)` sources the contents of `.attrs.sh` and then sources `$stdenv/setup` if it exists. This may not be pretty, but it mostly works. One notable difference when using nixpkgs' stdenv as of now is however that `$__structuredAttrs` is set to `1` on regular builds, but set to an empty string in a shell session. Also, `.attrs.json` cannot be used in shell sessions because it can only be accessed by `$NIX_ATTRS_JSON_FILE` and not by `$NIX_BUILD_TOP/.attrs.json`. I considered changing Nix to be compatible with what nixpkgs effectively does, but then we'd have to either move $NIX_BUILD_TOP for shell sessions to a temporary location (and thus breaking a lot of assumptions) or we'd reintroduce all the problems we solved back then by using these two env vars. This is partly because I didn't document these variables back then (mea culpa), so I decided to drop all mentions of `.attrs.{json,sh}` in the manual and only refer to `$NIX_ATTRS_SH_FILE` and `$NIX_ATTRS_JSON_FILE`. The same applies to all our integration tests. Theoretically we could deprecated using `"$NIX_BUILD_TOP"/.attrs.sh` in the future now. * `nix develop` and `nix print-dev-env` don't support this environment variable at all even though they're supposed to be part of the replacement for `nix-shell` - for the drv debugging part to be precise. This isn't a big deal for the vast majority of derivations, i.e. derivations relying on nixpkgs' `stdenv` wiring things together properly. This is because `nix develop` effectively "clones" the derivation and replaces the builder with a script that dumps all of the environment, shell variables, functions etc, so the state of structured attrs being "sourced" is transmitted into the dev shell and most of the time you don't need to worry about `.attrs.sh` not existing because the shell is correctly configured and the if [ -e .attrs.sh ]; then source .attrs.sh; fi is simply omitted. However, this will break when having a derivation that reads e.g. from `.attrs.json` like with import {}; runCommand "foo" { __structuredAttrs = true; foo.bar = 23; } '' cat $NIX_ATTRS_JSON_FILE # doesn't work because it points to /build/.attrs.json '' To work around this I employed a similar approach as it exists for `nix-shell`: the `NIX_ATTRS_{JSON,SH}_FILE` vars are replaced with temporary locations. The contents of `.attrs.sh` and `.attrs.json` are now written into the JSON by `get-env.sh`, the builder that `nix develop` injects into the derivation it's debugging. So finally the exact file contents are present and exported by `nix develop`. I also made `.attrs.json` a JSON string in the JSON printed by `get-env.sh` on purpose because then it's not necessary to serialize the object structure again. `nix develop` only needs the JSON as string because it's only written into the temporary file. I'm not entirely sure if it makes sense to also use a temporary location for `nix print-dev-env` (rather than just skipping the rewrite in there), but this would probably break certain cases where it's relied upon `$NIX_ATTRS_SH_FILE` to exist (prime example are the `nix print-dev-env` test-cases I wrote in this patch using `tests/shell.nix`, these would fail because the env var exists, but it cannot read from it). [1] https://github.com/NixOS/nix/pull/4770#issuecomment-836799719 --- .../src/language/advanced-attributes.md | 17 ++-- src/nix/develop.cc | 86 +++++++++++++++++-- src/nix/get-env.sh | 20 ++++- tests/build-hook-ca-fixed.nix | 5 +- tests/build-hook.nix | 5 +- tests/config.nix.in | 5 +- tests/failing.nix | 5 +- tests/hermetic.nix | 5 +- tests/structured-attrs.sh | 14 ++- 9 files changed, 141 insertions(+), 21 deletions(-) diff --git a/doc/manual/src/language/advanced-attributes.md b/doc/manual/src/language/advanced-attributes.md index 86f0f24b5..7d97b24b6 100644 --- a/doc/manual/src/language/advanced-attributes.md +++ b/doc/manual/src/language/advanced-attributes.md @@ -273,18 +273,21 @@ Derivations can declare some infrequently used optional attributes. - [`__structuredAttrs`]{#adv-attr-structuredAttrs}\ If the special attribute `__structuredAttrs` is set to `true`, the other derivation - attributes are serialised in JSON format and made available to the - builder via the file `.attrs.json` in the builder’s temporary - directory. This obviates the need for [`passAsFile`](#adv-attr-passAsFile) since JSON files - have no size restrictions, unlike process environments. + attributes are serialised into a file in JSON format. The environment variable + `NIX_ATTRS_JSON_FILE` points to the exact location of that file both in a build + and a [`nix-shell`](../command-ref/nix-shell.md). This obviates the need for + [`passAsFile`](#adv-attr-passAsFile) since JSON files have no size restrictions, + unlike process environments. It also makes it possible to tweak derivation settings in a structured way; see [`outputChecks`](#adv-attr-outputChecks) for example. As a convenience to Bash builders, - Nix writes a script named `.attrs.sh` to the builder’s directory - that initialises shell variables corresponding to all attributes - that are representable in Bash. This includes non-nested + Nix writes a script that initialises shell variables + corresponding to all attributes that are representable in Bash. The + environment variable `NIX_ATTRS_SH_FILE` points to the exact + location of the script, both in a build and a + [`nix-shell`](../command-ref/nix-shell.md). This includes non-nested (associative) arrays. For example, the attribute `hardening.format = true` ends up as the Bash associative array element `${hardening[format]}`. diff --git a/src/nix/develop.cc b/src/nix/develop.cc index c01ef1a2a..b080a3939 100644 --- a/src/nix/develop.cc +++ b/src/nix/develop.cc @@ -8,9 +8,12 @@ #include "derivations.hh" #include "progress-bar.hh" #include "run.hh" +#include "util.hh" +#include #include #include +#include using namespace nix; @@ -51,6 +54,7 @@ struct BuildEnvironment std::map vars; std::map bashFunctions; + std::optional> structuredAttrs; static BuildEnvironment fromJSON(std::string_view in) { @@ -74,6 +78,10 @@ struct BuildEnvironment res.bashFunctions.insert({name, def}); } + if (json.contains("structuredAttrs")) { + res.structuredAttrs = {json["structuredAttrs"][".attrs.json"], json["structuredAttrs"][".attrs.sh"]}; + } + return res; } @@ -102,6 +110,13 @@ struct BuildEnvironment res["bashFunctions"] = bashFunctions; + if (providesStructuredAttrs()) { + auto contents = nlohmann::json::object(); + contents[".attrs.sh"] = getAttrsSH(); + contents[".attrs.json"] = getAttrsJSON(); + res["structuredAttrs"] = std::move(contents); + } + auto json = res.dump(); assert(BuildEnvironment::fromJSON(json) == *this); @@ -109,6 +124,23 @@ struct BuildEnvironment return json; } + bool providesStructuredAttrs() const + { + return structuredAttrs.has_value(); + } + + std::string getAttrsJSON() const + { + assert(providesStructuredAttrs()); + return structuredAttrs->first; + } + + std::string getAttrsSH() const + { + assert(providesStructuredAttrs()); + return structuredAttrs->second; + } + void toBash(std::ostream & out, const std::set & ignoreVars) const { for (auto & [name, value] : vars) { @@ -291,6 +323,7 @@ struct Common : InstallableCommand, MixProfile std::string makeRcScript( ref store, const BuildEnvironment & buildEnvironment, + const Path & tmpDir, const Path & outputsDir = absPath(".") + "/outputs") { // A list of colon-separated environment variables that should be @@ -353,9 +386,48 @@ struct Common : InstallableCommand, MixProfile } } + if (buildEnvironment.providesStructuredAttrs()) { + fixupStructuredAttrs( + "sh", + "NIX_ATTRS_SH_FILE", + buildEnvironment.getAttrsSH(), + rewrites, + buildEnvironment, + tmpDir + ); + fixupStructuredAttrs( + "json", + "NIX_ATTRS_JSON_FILE", + buildEnvironment.getAttrsJSON(), + rewrites, + buildEnvironment, + tmpDir + ); + } + return rewriteStrings(script, rewrites); } + /** + * Replace the value of NIX_ATTRS_*_FILE (`/build/.attrs.*`) with a tmp file + * that's accessible from the interactive shell session. + */ + void fixupStructuredAttrs( + const std::string & ext, + const std::string & envVar, + const std::string & content, + StringMap & rewrites, + const BuildEnvironment & buildEnvironment, + const Path & tmpDir) + { + auto targetFilePath = tmpDir + "/.attrs." + ext; + writeFile(targetFilePath, content); + + auto fileInBuilderEnv = buildEnvironment.vars.find(envVar); + assert(fileInBuilderEnv != buildEnvironment.vars.end()); + rewrites.insert({BuildEnvironment::getString(fileInBuilderEnv->second), targetFilePath}); + } + Strings getDefaultFlakeAttrPaths() override { Strings paths{ @@ -487,7 +559,9 @@ struct CmdDevelop : Common, MixEnvironment auto [rcFileFd, rcFilePath] = createTempFile("nix-shell"); - auto script = makeRcScript(store, buildEnvironment); + AutoDelete tmpDir(createTempDir("", "nix-develop"), true); + + auto script = makeRcScript(store, buildEnvironment, (Path) tmpDir); if (verbosity >= lvlDebug) script += "set -x\n"; @@ -615,10 +689,12 @@ struct CmdPrintDevEnv : Common, MixJSON stopProgressBar(); - logger->writeToStdout( - json - ? buildEnvironment.toJSON() - : makeRcScript(store, buildEnvironment)); + if (json) { + logger->writeToStdout(buildEnvironment.toJSON()); + } else { + AutoDelete tmpDir(createTempDir("", "nix-dev-env"), true); + logger->writeToStdout(makeRcScript(store, buildEnvironment, tmpDir)); + } } }; diff --git a/src/nix/get-env.sh b/src/nix/get-env.sh index a7a8a01b9..832cc2f11 100644 --- a/src/nix/get-env.sh +++ b/src/nix/get-env.sh @@ -1,5 +1,5 @@ set -e -if [ -e .attrs.sh ]; then source .attrs.sh; fi +if [ -e "$NIX_ATTRS_SH_FILE" ]; then source "$NIX_ATTRS_SH_FILE"; fi export IN_NIX_SHELL=impure export dontAddDisableDepTrack=1 @@ -101,7 +101,21 @@ __dumpEnv() { printf "}" done < <(printf "%s\n" "$__vars") - printf '\n }\n}' + printf '\n }' + + if [ -e "$NIX_ATTRS_SH_FILE" ]; then + printf ',\n "structuredAttrs": {\n ' + __escapeString ".attrs.sh" + printf ': ' + __escapeString "$(<"$NIX_ATTRS_SH_FILE")" + printf ',\n ' + __escapeString ".attrs.json" + printf ': ' + __escapeString "$(<"$NIX_ATTRS_JSON_FILE")" + printf '\n }' + fi + + printf '\n}' } __escapeString() { @@ -117,7 +131,7 @@ __escapeString() { # In case of `__structuredAttrs = true;` the list of outputs is an associative # array with a format like `outname => /nix/store/hash-drvname-outname`, so `__olist` # must contain the array's keys (hence `${!...[@]}`) in this case. -if [ -e .attrs.sh ]; then +if [ -e "$NIX_ATTRS_SH_FILE" ]; then __olist="${!outputs[@]}" else __olist=$outputs diff --git a/tests/build-hook-ca-fixed.nix b/tests/build-hook-ca-fixed.nix index 4cb9e85d1..0ce6d9b12 100644 --- a/tests/build-hook-ca-fixed.nix +++ b/tests/build-hook-ca-fixed.nix @@ -8,7 +8,10 @@ let derivation ({ inherit system; builder = busybox; - args = ["sh" "-e" args.builder or (builtins.toFile "builder-${args.name}.sh" "if [ -e .attrs.sh ]; then source .attrs.sh; fi; eval \"$buildCommand\"")]; + args = ["sh" "-e" args.builder or (builtins.toFile "builder-${args.name}.sh" '' + if [ -e "$NIX_ATTRS_SH_FILE" ]; then source $NIX_ATTRS_SH_FILE; fi; + eval "$buildCommand" + '')]; outputHashMode = "recursive"; outputHashAlgo = "sha256"; } // removeAttrs args ["builder" "meta" "passthru"]) diff --git a/tests/build-hook.nix b/tests/build-hook.nix index 7effd7903..99a13aee4 100644 --- a/tests/build-hook.nix +++ b/tests/build-hook.nix @@ -14,7 +14,10 @@ let derivation ({ inherit system; builder = busybox; - args = ["sh" "-e" args.builder or (builtins.toFile "builder-${args.name}.sh" "if [ -e .attrs.sh ]; then source .attrs.sh; fi; eval \"$buildCommand\"")]; + args = ["sh" "-e" args.builder or (builtins.toFile "builder-${args.name}.sh" '' + if [ -e "$NIX_ATTRS_SH_FILE" ]; then source $NIX_ATTRS_SH_FILE; fi; + eval "$buildCommand" + '')]; } // removeAttrs args ["builder" "meta" "passthru"] // caArgs) // { meta = args.meta or {}; passthru = args.passthru or {}; }; diff --git a/tests/config.nix.in b/tests/config.nix.in index 7facbdcbc..00dc007e1 100644 --- a/tests/config.nix.in +++ b/tests/config.nix.in @@ -20,7 +20,10 @@ rec { derivation ({ inherit system; builder = shell; - args = ["-e" args.builder or (builtins.toFile "builder-${args.name}.sh" "if [ -e .attrs.sh ]; then source .attrs.sh; fi; eval \"$buildCommand\"")]; + args = ["-e" args.builder or (builtins.toFile "builder-${args.name}.sh" '' + if [ -e "$NIX_ATTRS_SH_FILE" ]; then source $NIX_ATTRS_SH_FILE; fi; + eval "$buildCommand" + '')]; PATH = path; } // caArgs // removeAttrs args ["builder" "meta"]) // { meta = args.meta or {}; }; diff --git a/tests/failing.nix b/tests/failing.nix index 2a0350d4d..d25e2d6b6 100644 --- a/tests/failing.nix +++ b/tests/failing.nix @@ -6,7 +6,10 @@ let derivation ({ inherit system; builder = busybox; - args = ["sh" "-e" args.builder or (builtins.toFile "builder-${args.name}.sh" "if [ -e .attrs.sh ]; then source .attrs.sh; fi; eval \"$buildCommand\"")]; + args = ["sh" "-e" args.builder or (builtins.toFile "builder-${args.name}.sh" '' + if [ -e "$NIX_ATTRS_SH_FILE" ]; then source $NIX_ATTRS_SH_FILE; fi; + eval "$buildCommand" + '')]; } // removeAttrs args ["builder" "meta"]) // { meta = args.meta or {}; }; in diff --git a/tests/hermetic.nix b/tests/hermetic.nix index 4c9d7a51f..0513540f0 100644 --- a/tests/hermetic.nix +++ b/tests/hermetic.nix @@ -14,7 +14,10 @@ let derivation ({ inherit system; builder = busybox; - args = ["sh" "-e" args.builder or (builtins.toFile "builder-${args.name}.sh" "if [ -e .attrs.sh ]; then source .attrs.sh; fi; eval \"$buildCommand\"")]; + args = ["sh" "-e" args.builder or (builtins.toFile "builder-${args.name}.sh" '' + if [ -e "$NIX_ATTRS_SH_FILE" ]; then source $NIX_ATTRS_SH_FILE; fi; + eval "$buildCommand" + '')]; } // removeAttrs args ["builder" "meta" "passthru"] // caArgs) // { meta = args.meta or {}; passthru = args.passthru or {}; }; diff --git a/tests/structured-attrs.sh b/tests/structured-attrs.sh index 378dbc735..f2835a761 100644 --- a/tests/structured-attrs.sh +++ b/tests/structured-attrs.sh @@ -15,9 +15,21 @@ nix-build structured-attrs.nix -A all -o $TEST_ROOT/result export NIX_BUILD_SHELL=$SHELL env NIX_PATH=nixpkgs=shell.nix nix-shell structured-attrs-shell.nix \ - --run 'test -e .attrs.json; test "3" = "$(jq ".my.list|length" < $NIX_ATTRS_JSON_FILE)"' + --run 'test "3" = "$(jq ".my.list|length" < $NIX_ATTRS_JSON_FILE)"' + +nix develop -f structured-attrs-shell.nix -c bash -c 'test "3" = "$(jq ".my.list|length" < $NIX_ATTRS_JSON_FILE)"' # `nix develop` is a slightly special way of dealing with environment vars, it parses # these from a shell-file exported from a derivation. This is to test especially `outputs` # (which is an associative array in thsi case) being fine. nix develop -f structured-attrs-shell.nix -c bash -c 'test -n "$out"' + +nix print-dev-env -f structured-attrs-shell.nix | grep -q 'NIX_ATTRS_JSON_FILE=' +nix print-dev-env -f structured-attrs-shell.nix | grep -q 'NIX_ATTRS_SH_FILE=' +nix print-dev-env -f shell.nix shellDrv | grepQuietInverse 'NIX_ATTRS_SH_FILE' + +jsonOut="$(nix print-dev-env -f structured-attrs-shell.nix --json)" + +test "$(<<<"$jsonOut" jq '.structuredAttrs|keys|.[]' -r)" = "$(printf ".attrs.json\n.attrs.sh")" + +test "$(<<<"$jsonOut" jq '.variables.out.value' -r)" = "$(<<<"$jsonOut" jq '.structuredAttrs.".attrs.json"' -r | jq -r '.outputs.out')" From 42e3c6d658ebdb60eeef179d91e71ca311f69da8 Mon Sep 17 00:00:00 2001 From: Maximilian Bosch Date: Fri, 29 Sep 2023 17:14:31 +0200 Subject: [PATCH 081/116] doc: reference NIX_ATTRS_*_FILE vars at the env var reference for drvs --- doc/manual/src/language/derivations.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/manual/src/language/derivations.md b/doc/manual/src/language/derivations.md index 4a1b70fd1..d80a7dd48 100644 --- a/doc/manual/src/language/derivations.md +++ b/doc/manual/src/language/derivations.md @@ -165,6 +165,11 @@ The [`builder`](#attr-builder) is executed as follows: - `NIX_STORE` is set to the path of the top-level Nix store directory (typically, `/nix/store`). + - `NIX_ATTRS_JSON_FILE` & `NIX_ATTRS_SH_FILE` if `__structuredAttrs` + is set to `true` for the dervation. A detailed explanation of this + behavior can be found in the + [section about structured attrs](./advanced-attributes.md#adv-attr-structuredAttrs). + - For each output declared in `outputs`, the corresponding environment variable is set to point to the intended path in the Nix store for that output. Each output path is a concatenation From 7a0886e3ccbab7da645dae6ecd015b3084aadc54 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 30 Sep 2023 22:04:38 +0100 Subject: [PATCH 082/116] tests/structured-attrs.sh: grep -q -> grepQuiet --- tests/structured-attrs.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/structured-attrs.sh b/tests/structured-attrs.sh index f2835a761..f11992dcd 100644 --- a/tests/structured-attrs.sh +++ b/tests/structured-attrs.sh @@ -24,8 +24,8 @@ nix develop -f structured-attrs-shell.nix -c bash -c 'test "3" = "$(jq ".my.list # (which is an associative array in thsi case) being fine. nix develop -f structured-attrs-shell.nix -c bash -c 'test -n "$out"' -nix print-dev-env -f structured-attrs-shell.nix | grep -q 'NIX_ATTRS_JSON_FILE=' -nix print-dev-env -f structured-attrs-shell.nix | grep -q 'NIX_ATTRS_SH_FILE=' +nix print-dev-env -f structured-attrs-shell.nix | grepQuiet 'NIX_ATTRS_JSON_FILE=' +nix print-dev-env -f structured-attrs-shell.nix | grepQuiet 'NIX_ATTRS_SH_FILE=' nix print-dev-env -f shell.nix shellDrv | grepQuietInverse 'NIX_ATTRS_SH_FILE' jsonOut="$(nix print-dev-env -f structured-attrs-shell.nix --json)" From 78e886bc5fd9e4d85f8503799540c0b71bb270be Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Fri, 29 Sep 2023 15:59:11 +0200 Subject: [PATCH 083/116] refine the maintainer's process to unblock discussions more quickly this addresses that we're too often running into open-ended discussions about attempts to solve problems where neither the problem nor the solution is well-understood enough to make decisions in a reasonable amount of time. this also prevents us from doing more work asynchronously. Co-authored-by: Robert Hensing --- maintainers/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/maintainers/README.md b/maintainers/README.md index 0d520cb0c..8eeb47e8b 100644 --- a/maintainers/README.md +++ b/maintainers/README.md @@ -96,8 +96,10 @@ What constitutes a trivial pull request is up to maintainers' judgement. Pull requests and issues that are deemed important and controversial are discussed by the team during discussion meetings. This may be where the merit of the change itself or the implementation strategy is contested by a team member. +Whenever the discussion opens up questions about the process or this team's goals, this may indicate that the change is too large in scope. +In that case it is taken off the board to be reconsidered by the author or broken down into smaller pieces that are less far-reaching and can be reviewed independently. -As a general guideline, the order of items is determined as follows: +As a general guideline, the order of items to discuss is determined as follows: - Prioritise pull requests over issues From 8440afbed756254784d9fea3eaab06649dffd390 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sun, 1 Oct 2023 23:29:45 -0400 Subject: [PATCH 084/116] Revert "Adapt scheduler to work with dynamic derivations" This reverts commit 5e3986f59cb58f48186a49dcec7aa317b4787522. This un-implements RFC 92 but fixes the critical bug #9052 which many people are hitting. This is a decent stop-gap until a minimal reproduction of that bug is found and a proper fix can be made. Mostly fixed #9052, but I would like to leave that issue open until we have a regression test, so I can then properly fix the bug (unbreaking RFC 92) later. --- .../create-derivation-and-realise-goal.cc | 157 ------------------ .../create-derivation-and-realise-goal.hh | 96 ----------- src/libstore/build/derivation-goal.cc | 33 +++- src/libstore/build/derivation-goal.hh | 5 +- src/libstore/build/entry-points.cc | 11 +- src/libstore/build/goal.hh | 10 -- src/libstore/build/worker.cc | 110 +++--------- src/libstore/build/worker.hh | 24 --- src/libstore/derived-path-map.cc | 3 - src/libstore/derived-path-map.hh | 7 +- tests/dyn-drv/build-built-drv.sh | 4 +- tests/dyn-drv/dep-built-drv.sh | 4 +- 12 files changed, 55 insertions(+), 409 deletions(-) delete mode 100644 src/libstore/build/create-derivation-and-realise-goal.cc delete mode 100644 src/libstore/build/create-derivation-and-realise-goal.hh diff --git a/src/libstore/build/create-derivation-and-realise-goal.cc b/src/libstore/build/create-derivation-and-realise-goal.cc deleted file mode 100644 index 60f67956d..000000000 --- a/src/libstore/build/create-derivation-and-realise-goal.cc +++ /dev/null @@ -1,157 +0,0 @@ -#include "create-derivation-and-realise-goal.hh" -#include "worker.hh" - -namespace nix { - -CreateDerivationAndRealiseGoal::CreateDerivationAndRealiseGoal(ref drvReq, - const OutputsSpec & wantedOutputs, Worker & worker, BuildMode buildMode) - : Goal(worker, DerivedPath::Built { .drvPath = drvReq, .outputs = wantedOutputs }) - , drvReq(drvReq) - , wantedOutputs(wantedOutputs) - , buildMode(buildMode) -{ - state = &CreateDerivationAndRealiseGoal::getDerivation; - name = fmt( - "outer obtaining drv from '%s' and then building outputs %s", - drvReq->to_string(worker.store), - std::visit(overloaded { - [&](const OutputsSpec::All) -> std::string { - return "* (all of them)"; - }, - [&](const OutputsSpec::Names os) { - return concatStringsSep(", ", quoteStrings(os)); - }, - }, wantedOutputs.raw)); - trace("created outer"); - - worker.updateProgress(); -} - - -CreateDerivationAndRealiseGoal::~CreateDerivationAndRealiseGoal() -{ -} - - -static StorePath pathPartOfReq(const SingleDerivedPath & req) -{ - return std::visit(overloaded { - [&](const SingleDerivedPath::Opaque & bo) { - return bo.path; - }, - [&](const SingleDerivedPath::Built & bfd) { - return pathPartOfReq(*bfd.drvPath); - }, - }, req.raw()); -} - - -std::string CreateDerivationAndRealiseGoal::key() -{ - /* Ensure that derivations get built in order of their name, - i.e. a derivation named "aardvark" always comes before "baboon". And - substitution goals and inner derivation goals always happen before - derivation goals (due to "b$"). */ - return "c$" + std::string(pathPartOfReq(*drvReq).name()) + "$" + drvReq->to_string(worker.store); -} - - -void CreateDerivationAndRealiseGoal::timedOut(Error && ex) -{ -} - - -void CreateDerivationAndRealiseGoal::work() -{ - (this->*state)(); -} - - -void CreateDerivationAndRealiseGoal::addWantedOutputs(const OutputsSpec & outputs) -{ - /* If we already want all outputs, there is nothing to do. */ - auto newWanted = wantedOutputs.union_(outputs); - bool needRestart = !newWanted.isSubsetOf(wantedOutputs); - wantedOutputs = newWanted; - - if (!needRestart) return; - - if (!optDrvPath) - // haven't started steps where the outputs matter yet - return; - worker.makeDerivationGoal(*optDrvPath, outputs, buildMode); -} - - -void CreateDerivationAndRealiseGoal::getDerivation() -{ - trace("outer init"); - - /* The first thing to do is to make sure that the derivation - exists. If it doesn't, it may be created through a - substitute. */ - if (auto optDrvPath = [this]() -> std::optional { - if (buildMode != bmNormal) return std::nullopt; - - auto drvPath = StorePath::dummy; - try { - drvPath = resolveDerivedPath(worker.store, *drvReq); - } catch (MissingRealisation &) { - return std::nullopt; - } - return worker.evalStore.isValidPath(drvPath) || worker.store.isValidPath(drvPath) - ? std::optional { drvPath } - : std::nullopt; - }()) { - trace(fmt("already have drv '%s' for '%s', can go straight to building", - worker.store.printStorePath(*optDrvPath), - drvReq->to_string(worker.store))); - - loadAndBuildDerivation(); - } else { - trace("need to obtain drv we want to build"); - - addWaitee(worker.makeGoal(DerivedPath::fromSingle(*drvReq))); - - state = &CreateDerivationAndRealiseGoal::loadAndBuildDerivation; - if (waitees.empty()) work(); - } -} - - -void CreateDerivationAndRealiseGoal::loadAndBuildDerivation() -{ - trace("outer load and build derivation"); - - if (nrFailed != 0) { - amDone(ecFailed, Error("cannot build missing derivation '%s'", drvReq->to_string(worker.store))); - return; - } - - StorePath drvPath = resolveDerivedPath(worker.store, *drvReq); - /* Build this step! */ - concreteDrvGoal = worker.makeDerivationGoal(drvPath, wantedOutputs, buildMode); - addWaitee(upcast_goal(concreteDrvGoal)); - state = &CreateDerivationAndRealiseGoal::buildDone; - optDrvPath = std::move(drvPath); - if (waitees.empty()) work(); -} - - -void CreateDerivationAndRealiseGoal::buildDone() -{ - trace("outer build done"); - - buildResult = upcast_goal(concreteDrvGoal)->getBuildResult(DerivedPath::Built { - .drvPath = drvReq, - .outputs = wantedOutputs, - }); - - if (buildResult.success()) - amDone(ecSuccess); - else - amDone(ecFailed, Error("building '%s' failed", drvReq->to_string(worker.store))); -} - - -} diff --git a/src/libstore/build/create-derivation-and-realise-goal.hh b/src/libstore/build/create-derivation-and-realise-goal.hh deleted file mode 100644 index ca936fc95..000000000 --- a/src/libstore/build/create-derivation-and-realise-goal.hh +++ /dev/null @@ -1,96 +0,0 @@ -#pragma once - -#include "parsed-derivations.hh" -#include "lock.hh" -#include "store-api.hh" -#include "pathlocks.hh" -#include "goal.hh" - -namespace nix { - -struct DerivationGoal; - -/** - * This goal type is essentially the serial composition (like function - * composition) of a goal for getting a derivation, and then a - * `DerivationGoal` using the newly-obtained derivation. - * - * In the (currently experimental) general inductive case of derivations - * that are themselves build outputs, that first goal will be *another* - * `CreateDerivationAndRealiseGoal`. In the (much more common) base-case - * where the derivation has no provence and is just referred to by - * (content-addressed) store path, that first goal is a - * `SubstitutionGoal`. - * - * If we already have the derivation (e.g. if the evalutator has created - * the derivation locally and then instructured the store to build it), - * we can skip the first goal entirely as a small optimization. - */ -struct CreateDerivationAndRealiseGoal : public Goal -{ - /** - * How to obtain a store path of the derivation to build. - */ - ref drvReq; - - /** - * The path of the derivation, once obtained. - **/ - std::optional optDrvPath; - - /** - * The goal for the corresponding concrete derivation. - **/ - std::shared_ptr concreteDrvGoal; - - /** - * The specific outputs that we need to build. - */ - OutputsSpec wantedOutputs; - - typedef void (CreateDerivationAndRealiseGoal::*GoalState)(); - GoalState state; - - /** - * The final output paths of the build. - * - * - For input-addressed derivations, always the precomputed paths - * - * - For content-addressed derivations, calcuated from whatever the - * hash ends up being. (Note that fixed outputs derivations that - * produce the "wrong" output still install that data under its - * true content-address.) - */ - OutputPathMap finalOutputs; - - BuildMode buildMode; - - CreateDerivationAndRealiseGoal(ref drvReq, - const OutputsSpec & wantedOutputs, Worker & worker, - BuildMode buildMode = bmNormal); - virtual ~CreateDerivationAndRealiseGoal(); - - void timedOut(Error && ex) override; - - std::string key() override; - - void work() override; - - /** - * Add wanted outputs to an already existing derivation goal. - */ - void addWantedOutputs(const OutputsSpec & outputs); - - /** - * The states. - */ - void getDerivation(); - void loadAndBuildDerivation(); - void buildDone(); - - JobCategory jobCategory() const override { - return JobCategory::Administration; - }; -}; - -} diff --git a/src/libstore/build/derivation-goal.cc b/src/libstore/build/derivation-goal.cc index 6472ecd99..83c0a3135 100644 --- a/src/libstore/build/derivation-goal.cc +++ b/src/libstore/build/derivation-goal.cc @@ -71,7 +71,7 @@ DerivationGoal::DerivationGoal(const StorePath & drvPath, , wantedOutputs(wantedOutputs) , buildMode(buildMode) { - state = &DerivationGoal::loadDerivation; + state = &DerivationGoal::getDerivation; name = fmt( "building of '%s' from .drv file", DerivedPath::Built { makeConstantStorePathRef(drvPath), wantedOutputs }.to_string(worker.store)); @@ -164,6 +164,24 @@ void DerivationGoal::addWantedOutputs(const OutputsSpec & outputs) } +void DerivationGoal::getDerivation() +{ + trace("init"); + + /* The first thing to do is to make sure that the derivation + exists. If it doesn't, it may be created through a + substitute. */ + if (buildMode == bmNormal && worker.evalStore.isValidPath(drvPath)) { + loadDerivation(); + return; + } + + addWaitee(upcast_goal(worker.makePathSubstitutionGoal(drvPath))); + + state = &DerivationGoal::loadDerivation; +} + + void DerivationGoal::loadDerivation() { trace("loading derivation"); @@ -1498,24 +1516,23 @@ void DerivationGoal::waiteeDone(GoalPtr waitee, ExitCode result) if (!useDerivation) return; auto & fullDrv = *dynamic_cast(drv.get()); - std::optional info = tryGetConcreteDrvGoal(waitee); - if (!info) return; - const auto & [dg, drvReq] = *info; + auto * dg = dynamic_cast(&*waitee); + if (!dg) return; - auto * nodeP = fullDrv.inputDrvs.findSlot(drvReq.get()); + auto * nodeP = fullDrv.inputDrvs.findSlot(DerivedPath::Opaque { .path = dg->drvPath }); if (!nodeP) return; auto & outputs = nodeP->value; for (auto & outputName : outputs) { - auto buildResult = dg.get().getBuildResult(DerivedPath::Built { - .drvPath = makeConstantStorePathRef(dg.get().drvPath), + auto buildResult = dg->getBuildResult(DerivedPath::Built { + .drvPath = makeConstantStorePathRef(dg->drvPath), .outputs = OutputsSpec::Names { outputName }, }); if (buildResult.success()) { auto i = buildResult.builtOutputs.find(outputName); if (i != buildResult.builtOutputs.end()) inputDrvOutputs.insert_or_assign( - { dg.get().drvPath, outputName }, + { dg->drvPath, outputName }, i->second.outPath); } } diff --git a/src/libstore/build/derivation-goal.hh b/src/libstore/build/derivation-goal.hh index 62b122c27..ddb5ee1e3 100644 --- a/src/libstore/build/derivation-goal.hh +++ b/src/libstore/build/derivation-goal.hh @@ -52,10 +52,6 @@ struct InitialOutput { /** * A goal for building some or all of the outputs of a derivation. - * - * The derivation must already be present, either in the store in a drv - * or in memory. If the derivation itself needs to be gotten first, a - * `CreateDerivationAndRealiseGoal` goal must be used instead. */ struct DerivationGoal : public Goal { @@ -235,6 +231,7 @@ struct DerivationGoal : public Goal /** * The states. */ + void getDerivation(); void loadDerivation(); void haveDerivation(); void outputsSubstitutionTried(); diff --git a/src/libstore/build/entry-points.cc b/src/libstore/build/entry-points.cc index f0f0e5519..13ff22f45 100644 --- a/src/libstore/build/entry-points.cc +++ b/src/libstore/build/entry-points.cc @@ -1,6 +1,5 @@ #include "worker.hh" #include "substitution-goal.hh" -#include "create-derivation-and-realise-goal.hh" #include "derivation-goal.hh" #include "local-store.hh" @@ -16,7 +15,7 @@ void Store::buildPaths(const std::vector & reqs, BuildMode buildMod worker.run(goals); - StringSet failed; + StorePathSet failed; std::optional ex; for (auto & i : goals) { if (i->ex) { @@ -26,10 +25,10 @@ void Store::buildPaths(const std::vector & reqs, BuildMode buildMod ex = std::move(i->ex); } if (i->exitCode != Goal::ecSuccess) { - if (auto i2 = dynamic_cast(i.get())) - failed.insert(i2->drvReq->to_string(*this)); + if (auto i2 = dynamic_cast(i.get())) + failed.insert(i2->drvPath); else if (auto i2 = dynamic_cast(i.get())) - failed.insert(printStorePath(i2->storePath)); + failed.insert(i2->storePath); } } @@ -38,7 +37,7 @@ void Store::buildPaths(const std::vector & reqs, BuildMode buildMod throw std::move(*ex); } else if (!failed.empty()) { if (ex) logError(ex->info()); - throw Error(worker.failingExitStatus(), "build of %s failed", concatStringsSep(", ", quoteStrings(failed))); + throw Error(worker.failingExitStatus(), "build of %s failed", showPaths(failed)); } } diff --git a/src/libstore/build/goal.hh b/src/libstore/build/goal.hh index 01d3c3491..9af083230 100644 --- a/src/libstore/build/goal.hh +++ b/src/libstore/build/goal.hh @@ -49,16 +49,6 @@ enum struct JobCategory { * A substitution an arbitrary store object; it will use network resources. */ Substitution, - /** - * A goal that does no "real" work by itself, and just exists to depend on - * other goals which *do* do real work. These goals therefore are not - * limited. - * - * These goals cannot infinitely create themselves, so there is no risk of - * a "fork bomb" type situation (which would be a problem even though the - * goal do no real work) either. - */ - Administration, }; struct Goal : public std::enable_shared_from_this diff --git a/src/libstore/build/worker.cc b/src/libstore/build/worker.cc index b4a634e7b..37cb86b91 100644 --- a/src/libstore/build/worker.cc +++ b/src/libstore/build/worker.cc @@ -2,7 +2,6 @@ #include "worker.hh" #include "substitution-goal.hh" #include "drv-output-substitution-goal.hh" -#include "create-derivation-and-realise-goal.hh" #include "local-derivation-goal.hh" #include "hook-instance.hh" @@ -42,24 +41,6 @@ Worker::~Worker() } -std::shared_ptr Worker::makeCreateDerivationAndRealiseGoal( - ref drvReq, - const OutputsSpec & wantedOutputs, - BuildMode buildMode) -{ - std::weak_ptr & goal_weak = outerDerivationGoals.ensureSlot(*drvReq).value; - std::shared_ptr goal = goal_weak.lock(); - if (!goal) { - goal = std::make_shared(drvReq, wantedOutputs, *this, buildMode); - goal_weak = goal; - wakeUp(goal); - } else { - goal->addWantedOutputs(wantedOutputs); - } - return goal; -} - - std::shared_ptr Worker::makeDerivationGoalCommon( const StorePath & drvPath, const OutputsSpec & wantedOutputs, @@ -130,7 +111,10 @@ GoalPtr Worker::makeGoal(const DerivedPath & req, BuildMode buildMode) { return std::visit(overloaded { [&](const DerivedPath::Built & bfd) -> GoalPtr { - return makeCreateDerivationAndRealiseGoal(bfd.drvPath, bfd.outputs, buildMode); + if (auto bop = std::get_if(&*bfd.drvPath)) + return makeDerivationGoal(bop->path, bfd.outputs, buildMode); + else + throw UnimplementedError("Building dynamic derivations in one shot is not yet implemented."); }, [&](const DerivedPath::Opaque & bo) -> GoalPtr { return makePathSubstitutionGoal(bo.path, buildMode == bmRepair ? Repair : NoRepair); @@ -139,46 +123,24 @@ GoalPtr Worker::makeGoal(const DerivedPath & req, BuildMode buildMode) } -template -static void cullMap(std::map & goalMap, F f) -{ - for (auto i = goalMap.begin(); i != goalMap.end();) - if (!f(i->second)) - i = goalMap.erase(i); - else ++i; -} - - template static void removeGoal(std::shared_ptr goal, std::map> & goalMap) { /* !!! inefficient */ - cullMap(goalMap, [&](const std::weak_ptr & gp) -> bool { - return gp.lock() != goal; - }); -} - -template -static void removeGoal(std::shared_ptr goal, std::map>::ChildNode> & goalMap); - -template -static void removeGoal(std::shared_ptr goal, std::map>::ChildNode> & goalMap) -{ - /* !!! inefficient */ - cullMap(goalMap, [&](DerivedPathMap>::ChildNode & node) -> bool { - if (node.value.lock() == goal) - node.value.reset(); - removeGoal(goal, node.childMap); - return !node.value.expired() || !node.childMap.empty(); - }); + for (auto i = goalMap.begin(); + i != goalMap.end(); ) + if (i->second.lock() == goal) { + auto j = i; ++j; + goalMap.erase(i); + i = j; + } + else ++i; } void Worker::removeGoal(GoalPtr goal) { - if (auto drvGoal = std::dynamic_pointer_cast(goal)) - nix::removeGoal(drvGoal, outerDerivationGoals.map); - else if (auto drvGoal = std::dynamic_pointer_cast(goal)) + if (auto drvGoal = std::dynamic_pointer_cast(goal)) nix::removeGoal(drvGoal, derivationGoals); else if (auto subGoal = std::dynamic_pointer_cast(goal)) nix::removeGoal(subGoal, substitutionGoals); @@ -236,19 +198,8 @@ void Worker::childStarted(GoalPtr goal, const std::set & fds, child.respectTimeouts = respectTimeouts; children.emplace_back(child); if (inBuildSlot) { - switch (goal->jobCategory()) { - case JobCategory::Substitution: - nrSubstitutions++; - break; - case JobCategory::Build: - nrLocalBuilds++; - break; - case JobCategory::Administration: - /* Intentionally not limited, see docs */ - break; - default: - abort(); - } + if (goal->jobCategory() == JobCategory::Substitution) nrSubstitutions++; + else nrLocalBuilds++; } } @@ -260,20 +211,12 @@ void Worker::childTerminated(Goal * goal, bool wakeSleepers) if (i == children.end()) return; if (i->inBuildSlot) { - switch (goal->jobCategory()) { - case JobCategory::Substitution: + if (goal->jobCategory() == JobCategory::Substitution) { assert(nrSubstitutions > 0); nrSubstitutions--; - break; - case JobCategory::Build: + } else { assert(nrLocalBuilds > 0); nrLocalBuilds--; - break; - case JobCategory::Administration: - /* Intentionally not limited, see docs */ - break; - default: - abort(); } } @@ -324,9 +267,9 @@ void Worker::run(const Goals & _topGoals) for (auto & i : _topGoals) { topGoals.insert(i); - if (auto goal = dynamic_cast(i.get())) { + if (auto goal = dynamic_cast(i.get())) { topPaths.push_back(DerivedPath::Built { - .drvPath = goal->drvReq, + .drvPath = makeConstantStorePathRef(goal->drvPath), .outputs = goal->wantedOutputs, }); } else if (auto goal = dynamic_cast(i.get())) { @@ -589,19 +532,4 @@ GoalPtr upcast_goal(std::shared_ptr subGoal) return subGoal; } -GoalPtr upcast_goal(std::shared_ptr subGoal) -{ - return subGoal; -} - -std::optional, std::reference_wrapper>> tryGetConcreteDrvGoal(GoalPtr waitee) -{ - auto * odg = dynamic_cast(&*waitee); - if (!odg) return std::nullopt; - return {{ - std::cref(*odg->concreteDrvGoal), - std::cref(*odg->drvReq), - }}; -} - } diff --git a/src/libstore/build/worker.hh b/src/libstore/build/worker.hh index 6f6d25d7d..23ad87914 100644 --- a/src/libstore/build/worker.hh +++ b/src/libstore/build/worker.hh @@ -4,7 +4,6 @@ #include "types.hh" #include "lock.hh" #include "store-api.hh" -#include "derived-path-map.hh" #include "goal.hh" #include "realisation.hh" @@ -14,7 +13,6 @@ namespace nix { /* Forward definition. */ -struct CreateDerivationAndRealiseGoal; struct DerivationGoal; struct PathSubstitutionGoal; class DrvOutputSubstitutionGoal; @@ -33,25 +31,9 @@ class DrvOutputSubstitutionGoal; */ GoalPtr upcast_goal(std::shared_ptr subGoal); GoalPtr upcast_goal(std::shared_ptr subGoal); -GoalPtr upcast_goal(std::shared_ptr subGoal); typedef std::chrono::time_point steady_time_point; -/** - * The current implementation of impure derivations has - * `DerivationGoal`s accumulate realisations from their waitees. - * Unfortunately, `DerivationGoal`s don't directly depend on other - * goals, but instead depend on `CreateDerivationAndRealiseGoal`s. - * - * We try not to share any of the details of any goal type with any - * other, for sake of modularity and quicker rebuilds. This means we - * cannot "just" downcast and fish out the field. So as an escape hatch, - * we have made the function, written in `worker.cc` where all the goal - * types are visible, and use it instead. - */ - -std::optional, std::reference_wrapper>> tryGetConcreteDrvGoal(GoalPtr waitee); - /** * A mapping used to remember for each child process to what goal it * belongs, and file descriptors for receiving log data and output @@ -119,9 +101,6 @@ private: * Maps used to prevent multiple instantiations of a goal for the * same derivation / path. */ - - DerivedPathMap> outerDerivationGoals; - std::map> derivationGoals; std::map> substitutionGoals; std::map> drvOutputSubstitutionGoals; @@ -209,9 +188,6 @@ public: * @ref DerivationGoal "derivation goal" */ private: - std::shared_ptr makeCreateDerivationAndRealiseGoal( - ref drvPath, - const OutputsSpec & wantedOutputs, BuildMode buildMode = bmNormal); std::shared_ptr makeDerivationGoalCommon( const StorePath & drvPath, const OutputsSpec & wantedOutputs, std::function()> mkDrvGoal); diff --git a/src/libstore/derived-path-map.cc b/src/libstore/derived-path-map.cc index 437b6a71a..5982c04b3 100644 --- a/src/libstore/derived-path-map.cc +++ b/src/libstore/derived-path-map.cc @@ -51,11 +51,8 @@ typename DerivedPathMap::ChildNode * DerivedPathMap::findSlot(const Single // instantiations -#include "create-derivation-and-realise-goal.hh" namespace nix { -template struct DerivedPathMap>; - GENERATE_CMP_EXT( template<>, DerivedPathMap>::ChildNode, diff --git a/src/libstore/derived-path-map.hh b/src/libstore/derived-path-map.hh index 4a2c90733..4d72b301e 100644 --- a/src/libstore/derived-path-map.hh +++ b/src/libstore/derived-path-map.hh @@ -20,11 +20,8 @@ namespace nix { * * @param V A type to instantiate for each output. It should probably * should be an "optional" type so not every interior node has to have a - * value. For example, the scheduler uses - * `DerivedPathMap>` to - * remember which goals correspond to which outputs. `* const Something` - * or `std::optional` would also be good choices for - * "optional" types. + * value. `* const Something` or `std::optional` would be + * good choices for "optional" types. */ template struct DerivedPathMap { diff --git a/tests/dyn-drv/build-built-drv.sh b/tests/dyn-drv/build-built-drv.sh index 94f3550bd..647be9457 100644 --- a/tests/dyn-drv/build-built-drv.sh +++ b/tests/dyn-drv/build-built-drv.sh @@ -18,6 +18,4 @@ clearStore drvDep=$(nix-instantiate ./text-hashed-output.nix -A producingDrv) -out2=$(nix build "${drvDep}^out^out" --no-link) - -test $out1 == $out2 +expectStderr 1 nix build "${drvDep}^out^out" --no-link | grepQuiet "Building dynamic derivations in one shot is not yet implemented" diff --git a/tests/dyn-drv/dep-built-drv.sh b/tests/dyn-drv/dep-built-drv.sh index c3daf2c59..4f6e9b080 100644 --- a/tests/dyn-drv/dep-built-drv.sh +++ b/tests/dyn-drv/dep-built-drv.sh @@ -6,6 +6,6 @@ out1=$(nix-build ./text-hashed-output.nix -A hello --no-out-link) clearStore -out2=$(nix-build ./text-hashed-output.nix -A wrapper --no-out-link) +expectStderr 1 nix-build ./text-hashed-output.nix -A wrapper --no-out-link | grepQuiet "Building dynamic derivations in one shot is not yet implemented" -diff -r $out1 $out2 +# diff -r $out1 $out2 From 632f24166d9d22e329d9d8626fa7dcd6b9ac83ce Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 26 Sep 2023 14:44:15 -0400 Subject: [PATCH 085/116] Test the rest of the worker protocol serializers Part of the `BuildResult` test is commented out because we have caught a roundtrip bug! A future PR will fix the bug and uncomment that test. --- src/libstore/build-result.cc | 18 ++ src/libstore/build-result.hh | 3 + src/libstore/tests/worker-protocol.cc | 189 ++++++++++++++++++ src/libstore/worker-protocol.hh | 4 +- .../libstore/worker-protocol/build-result.bin | Bin 0 -> 144 bytes .../libstore/worker-protocol/drv-output.bin | Bin 0 -> 176 bytes .../worker-protocol/keyed-build-result.bin | Bin 0 -> 264 bytes .../optional-content-address.bin | Bin 0 -> 64 bytes .../worker-protocol/optional-store-path.bin | Bin 0 -> 72 bytes .../worker-protocol/optional-trusted-flag.bin | Bin 0 -> 24 bytes .../libstore/worker-protocol/realisation.bin | Bin 0 -> 520 bytes .../libstore/worker-protocol/set.bin | Bin 0 -> 152 bytes .../libstore/worker-protocol/vector.bin | Bin 0 -> 152 bytes 13 files changed, 212 insertions(+), 2 deletions(-) create mode 100644 src/libstore/build-result.cc create mode 100644 unit-test-data/libstore/worker-protocol/build-result.bin create mode 100644 unit-test-data/libstore/worker-protocol/drv-output.bin create mode 100644 unit-test-data/libstore/worker-protocol/keyed-build-result.bin create mode 100644 unit-test-data/libstore/worker-protocol/optional-content-address.bin create mode 100644 unit-test-data/libstore/worker-protocol/optional-store-path.bin create mode 100644 unit-test-data/libstore/worker-protocol/optional-trusted-flag.bin create mode 100644 unit-test-data/libstore/worker-protocol/realisation.bin create mode 100644 unit-test-data/libstore/worker-protocol/set.bin create mode 100644 unit-test-data/libstore/worker-protocol/vector.bin diff --git a/src/libstore/build-result.cc b/src/libstore/build-result.cc new file mode 100644 index 000000000..18f519c5c --- /dev/null +++ b/src/libstore/build-result.cc @@ -0,0 +1,18 @@ +#include "build-result.hh" + +namespace nix { + +GENERATE_CMP_EXT( + , + BuildResult, + me->status, + me->errorMsg, + me->timesBuilt, + me->isNonDeterministic, + me->builtOutputs, + me->startTime, + me->stopTime, + me->cpuUser, + me->cpuSystem); + +} diff --git a/src/libstore/build-result.hh b/src/libstore/build-result.hh index b7a56e791..8840fa7e3 100644 --- a/src/libstore/build-result.hh +++ b/src/libstore/build-result.hh @@ -3,6 +3,7 @@ #include "realisation.hh" #include "derived-path.hh" +#include "comparator.hh" #include #include @@ -100,6 +101,8 @@ struct BuildResult */ std::optional cpuUser, cpuSystem; + DECLARE_CMP(BuildResult); + bool success() { return status == Built || status == Substituted || status == AlreadyValid || status == ResolvesToAlreadyValid; diff --git a/src/libstore/tests/worker-protocol.cc b/src/libstore/tests/worker-protocol.cc index 4a6ccf7c0..24a06503c 100644 --- a/src/libstore/tests/worker-protocol.cc +++ b/src/libstore/tests/worker-protocol.cc @@ -6,6 +6,7 @@ #include "worker-protocol.hh" #include "worker-protocol-impl.hh" #include "derived-path.hh" +#include "build-result.hh" #include "tests/libstore.hh" namespace nix { @@ -136,4 +137,192 @@ CHARACTERIZATION_TEST( }, })) +CHARACTERIZATION_TEST( + drvOutput, + "drv-output", + (std::tuple { + { + .drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="), + .outputName = "baz", + }, + DrvOutput { + .drvHash = Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="), + .outputName = "quux", + }, + })) + +CHARACTERIZATION_TEST( + realisation, + "realisation", + (std::tuple { + Realisation { + .id = DrvOutput { + .drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="), + .outputName = "baz", + }, + .outPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo" }, + .signatures = { "asdf", "qwer" }, + }, + Realisation { + .id = { + .drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="), + .outputName = "baz", + }, + .outPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo" }, + .signatures = { "asdf", "qwer" }, + .dependentRealisations = { + { + DrvOutput { + .drvHash = Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="), + .outputName = "quux", + }, + StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo" }, + }, + }, + }, + })) + +CHARACTERIZATION_TEST( + buildResult, + "build-result", + ({ + using namespace std::literals::chrono_literals; + std::tuple t { + BuildResult { + .status = BuildResult::OutputRejected, + .errorMsg = "no idea why", + }, + BuildResult { + .status = BuildResult::NotDeterministic, + .errorMsg = "no idea why", + .timesBuilt = 3, + .isNonDeterministic = true, + .startTime = 30, + .stopTime = 50, + }, +#if 0 + // This is commented because this test would fail! + // FIXME uncomment this and fix the underlying bug. + BuildResult { + .status = BuildResult::Built, + .timesBuilt = 1, + .builtOutputs = { + { + "foo", + { + .id = DrvOutput { + .drvHash = Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="), + .outputName = "foo", + }, + .outPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo" }, + }, + }, + { + "bar", + { + .id = DrvOutput { + .drvHash = Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="), + .outputName = "bar", + }, + .outPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar" }, + }, + }, + }, + .startTime = 30, + .stopTime = 50, + .cpuUser = std::chrono::milliseconds(500s), + .cpuSystem = std::chrono::milliseconds(604s), + }, +#endif + }; + t; + })) + +CHARACTERIZATION_TEST( + keyedBuildResult, + "keyed-build-result", + ({ + using namespace std::literals::chrono_literals; + std::tuple t { + KeyedBuildResult { + { + .status = KeyedBuildResult::OutputRejected, + .errorMsg = "no idea why", + }, + /* .path = */ DerivedPath::Opaque { + StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-xxx" }, + }, + }, + KeyedBuildResult { + { + .status = KeyedBuildResult::NotDeterministic, + .errorMsg = "no idea why", + .timesBuilt = 3, + .isNonDeterministic = true, + .startTime = 30, + .stopTime = 50, + }, + /* .path = */ DerivedPath::Built { + .drvPath = makeConstantStorePathRef(StorePath { + "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar.drv", + }), + .outputs = OutputsSpec::Names { "out" }, + }, + }, + }; + t; + })) + +CHARACTERIZATION_TEST( + optionalTrustedFlag, + "optional-trusted-flag", + (std::tuple, std::optional, std::optional> { + std::nullopt, + std::optional { Trusted }, + std::optional { NotTrusted }, + })) + +CHARACTERIZATION_TEST( + vector, + "vector", + (std::tuple, std::vector, std::vector, std::vector>> { + { }, + { "" }, + { "", "foo", "bar" }, + { {}, { "" }, { "", "1", "2" } }, + })) + +CHARACTERIZATION_TEST( + set, + "set", + (std::tuple, std::set, std::set, std::set>> { + { }, + { "" }, + { "", "foo", "bar" }, + { {}, { "" }, { "", "1", "2" } }, + })) + +CHARACTERIZATION_TEST( + optionalStorePath, + "optional-store-path", + (std::tuple, std::optional> { + std::nullopt, + std::optional { + StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo-bar" }, + }, + })) + +CHARACTERIZATION_TEST( + optionalContentAddress, + "optional-content-address", + (std::tuple, std::optional> { + std::nullopt, + std::optional { + ContentAddress { + .method = FileIngestionMethod::Flat, + .hash = hashString(HashType::htSHA1, "blob blob..."), + }, + }, + })) + } diff --git a/src/libstore/worker-protocol.hh b/src/libstore/worker-protocol.hh index 70a5bddb9..b7f42f24d 100644 --- a/src/libstore/worker-protocol.hh +++ b/src/libstore/worker-protocol.hh @@ -208,10 +208,10 @@ MAKE_WORKER_PROTO(ContentAddress); template<> MAKE_WORKER_PROTO(DerivedPath); template<> -MAKE_WORKER_PROTO(Realisation); -template<> MAKE_WORKER_PROTO(DrvOutput); template<> +MAKE_WORKER_PROTO(Realisation); +template<> MAKE_WORKER_PROTO(BuildResult); template<> MAKE_WORKER_PROTO(KeyedBuildResult); diff --git a/unit-test-data/libstore/worker-protocol/build-result.bin b/unit-test-data/libstore/worker-protocol/build-result.bin new file mode 100644 index 0000000000000000000000000000000000000000..f981fd91f7f32724dc2b9ce1938398aad3d73cdf GIT binary patch literal 144 tcmZQ&fB%U7Sq@}q)U|6h9n2j1!MT?w+C~^gO WO9=?G&ojrYjy`x4ZufnEe#tjMpDPdm literal 0 HcmV?d00001 diff --git a/unit-test-data/libstore/worker-protocol/keyed-build-result.bin b/unit-test-data/libstore/worker-protocol/keyed-build-result.bin new file mode 100644 index 0000000000000000000000000000000000000000..c5b3c7f3669a906fef3ef450e8fc8cf8d87d1d04 GIT binary patch literal 264 zcmdOAfB^lx%nJSDlKi4n{dB`}^NdR4LR_?NMMVVzD^wphl+Mdn$V^F1R4C7=go+SH onM0M4Vt-O%kzPtsnPPrv2?GyQKQ4DLL**Hvv>cQ+g3>Sw03m8EMgRZ+ literal 0 HcmV?d00001 diff --git a/unit-test-data/libstore/worker-protocol/optional-content-address.bin b/unit-test-data/libstore/worker-protocol/optional-content-address.bin new file mode 100644 index 0000000000000000000000000000000000000000..f8cfe65ba27fd78ec7787eed7ebec8dfdead2fba GIT binary patch literal 64 zcmZQzfB#ECTBU06lLKJOBUy literal 0 HcmV?d00001 diff --git a/unit-test-data/libstore/worker-protocol/optional-trusted-flag.bin b/unit-test-data/libstore/worker-protocol/optional-trusted-flag.bin new file mode 100644 index 0000000000000000000000000000000000000000..51b239409bc815c19b00cb97f62996fb782f24c3 GIT binary patch literal 24 PcmZQzfB;4)%><3#3x z?rW}!>Uc!$VBY>Sr2mUC`<2<)qY;)1KN Po44)luetw6d9AunL$;dR literal 0 HcmV?d00001 diff --git a/unit-test-data/libstore/worker-protocol/set.bin b/unit-test-data/libstore/worker-protocol/set.bin new file mode 100644 index 0000000000000000000000000000000000000000..ce11ede7fe7aa061a30b211a18cd7a336f879de8 GIT binary patch literal 152 ucmZQzfB;4)4WpQ03@8obCnXkvMPU52{CpHXOdBEdVDg4g4KThDln(&+h63;a literal 0 HcmV?d00001 diff --git a/unit-test-data/libstore/worker-protocol/vector.bin b/unit-test-data/libstore/worker-protocol/vector.bin new file mode 100644 index 0000000000000000000000000000000000000000..7a37c8cd1093201c56f384747ae9c2aa8aa700f9 GIT binary patch literal 152 ucmZQzfB;4)4WpQ03@8obr{(8^MPU4-#3B?vOdBEdVDg4g4KThDln(&-b^`DK literal 0 HcmV?d00001 From e1af175707007591113b0d6fc56036782b0e347c Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 4 Oct 2023 17:36:49 -0400 Subject: [PATCH 086/116] Enable most of the third `BuildResult` worker protocol test This was somewhat of a false alarm. The problem was not that the protocol implementation actually failed to round trip, but that two of the fields were ignored entirely --- not serialized and deserialized at all. For reference, those fields were added in fa68eb367e79297bb1c0451cd92ad18a06edce96. --- src/libstore/tests/worker-protocol.cc | 11 ++++++----- .../libstore/worker-protocol/build-result.bin | Bin 144 -> 744 bytes 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/libstore/tests/worker-protocol.cc b/src/libstore/tests/worker-protocol.cc index 24a06503c..fa7cbe121 100644 --- a/src/libstore/tests/worker-protocol.cc +++ b/src/libstore/tests/worker-protocol.cc @@ -187,7 +187,7 @@ CHARACTERIZATION_TEST( "build-result", ({ using namespace std::literals::chrono_literals; - std::tuple t { + std::tuple t { BuildResult { .status = BuildResult::OutputRejected, .errorMsg = "no idea why", @@ -200,9 +200,6 @@ CHARACTERIZATION_TEST( .startTime = 30, .stopTime = 50, }, -#if 0 - // This is commented because this test would fail! - // FIXME uncomment this and fix the underlying bug. BuildResult { .status = BuildResult::Built, .timesBuilt = 1, @@ -230,10 +227,14 @@ CHARACTERIZATION_TEST( }, .startTime = 30, .stopTime = 50, +#if 0 + // These fields are not yet serialized. + // FIXME Include in next version of protocol or document + // why they are skipped. .cpuUser = std::chrono::milliseconds(500s), .cpuSystem = std::chrono::milliseconds(604s), - }, #endif + }, }; t; })) diff --git a/unit-test-data/libstore/worker-protocol/build-result.bin b/unit-test-data/libstore/worker-protocol/build-result.bin index f981fd91f7f32724dc2b9ce1938398aad3d73cdf..b02c706eab8de4d60eadd6afdcc2c888ab605ad3 100644 GIT binary patch literal 744 zcmc(b&uRiO5XSolPxTp!=B%)rxXHS&(2IC2JXe~O6CwQ^%woR?}>#sX4il$bfs;n zmv%`YOQ~vvTo;t-%xH@l(n4vSK8bRZQHc`kI^B)Ih0TkNGRhXy8rqZN5BnYj(ieFo zAJ+t*u7l`;??iPt&V)lzi91dfGZFf@g4iVAZN4+jUVRU7o>ol_o!fedeM@Pl_mAVf T^ROZOQyyvZZF!s Date: Thu, 7 Sep 2023 00:31:33 +0200 Subject: [PATCH 087/116] always show anchors on setting listings refactor the templates for readability --- doc/manual/generate-manpage.nix | 55 ++++++-------------------- doc/manual/generate-settings.nix | 63 ++++++++++++++++++++++++++++++ doc/manual/generate-store-info.nix | 45 +++++++++++++++++++++ doc/manual/local.mk | 6 +-- doc/manual/utils.nix | 57 --------------------------- 5 files changed, 124 insertions(+), 102 deletions(-) create mode 100644 doc/manual/generate-settings.nix create mode 100644 doc/manual/generate-store-info.nix diff --git a/doc/manual/generate-manpage.nix b/doc/manual/generate-manpage.nix index 41725aed6..425eea002 100644 --- a/doc/manual/generate-manpage.nix +++ b/doc/manual/generate-manpage.nix @@ -2,7 +2,8 @@ let inherit (builtins) attrNames attrValues fromJSON listToAttrs mapAttrs groupBy concatStringsSep concatMap length lessThan replaceStrings sort; - inherit (import ./utils.nix) attrsToList concatStrings optionalString filterAttrs trim squash unique showSettings; + inherit (import ./utils.nix) attrsToList concatStrings optionalString filterAttrs trim squash unique; + showStoreDocs = import ./generate-store-info.nix; in commandDump: @@ -30,7 +31,7 @@ let ${maybeSubcommands} - ${maybeDocumentation} + ${maybeStoreDocs} ${maybeOptions} ''; @@ -40,15 +41,15 @@ let showArgument = arg: "*${arg.label}*" + optionalString (! arg ? arity) "..."; arguments = concatStringsSep " " (map showArgument args); in '' - `${command}` [*option*...] ${arguments} + `${command}` [*option*...] ${arguments} ''; maybeSubcommands = optionalString (details ? commands && details.commands != {}) - '' - where *subcommand* is one of the following: + '' + where *subcommand* is one of the following: - ${subcommands} - ''; + ${subcommands} + ''; subcommands = if length categories > 1 then listCategories @@ -70,10 +71,11 @@ let * [`${command} ${name}`](./${appendName filename name}.md) - ${subcmd.description} ''; - # TODO: move this confusing special case out of here when implementing #8496 - maybeDocumentation = optionalString - (details ? doc) - (replaceStrings ["@stores@"] [storeDocs] details.doc); + # FIXME: this is a hack. + # store parameters should not be part of command documentation to begin + # with, but instead be rendered on separate pages. + maybeStoreDocs = optionalString (details ? doc) + (replaceStrings [ "@stores@" ] [ (showStoreDocs commandInfo.stores) ] details.doc); maybeOptions = let allVisibleOptions = filterAttrs @@ -147,35 +149,4 @@ let " - [${page.command}](command-ref/new-cli/${page.name})"; in concatStringsSep "\n" (map showEntry manpages) + "\n"; - storeDocs = - let - showStore = name: { settings, doc, experimentalFeature }: - let - experimentalFeatureNote = optionalString (experimentalFeature != null) '' - > **Warning** - > This store is part of an - > [experimental feature](@docroot@/contributing/experimental-features.md). - - To use this store, you need to make sure the corresponding experimental feature, - [`${experimentalFeature}`](@docroot@/contributing/experimental-features.md#xp-feature-${experimentalFeature}), - is enabled. - For example, include the following in [`nix.conf`](@docroot@/command-ref/conf-file.md): - - ``` - extra-experimental-features = ${experimentalFeature} - ``` - ''; - in '' - ## ${name} - - ${doc} - - ${experimentalFeatureNote} - - **Settings**: - - ${showSettings { useAnchors = false; } settings} - ''; - in concatStrings (attrValues (mapAttrs showStore commandInfo.stores)); - in (listToAttrs manpages) // { "SUMMARY.md" = tableOfContents; } diff --git a/doc/manual/generate-settings.nix b/doc/manual/generate-settings.nix new file mode 100644 index 000000000..450771f73 --- /dev/null +++ b/doc/manual/generate-settings.nix @@ -0,0 +1,63 @@ +let + inherit (builtins) attrValues concatStringsSep isAttrs isBool mapAttrs; + inherit (import ./utils.nix) concatStrings indent optionalString squash; +in + +prefix: settingsInfo: + +let + + showSetting = prefix: setting: { description, documentDefault, defaultValue, aliases, value, experimentalFeature }: + let + result = squash '' + - [`${setting}`](#${prefix}-${setting}) + + ${indent " " body} + ''; + + # separate body to cleanly handle indentation + body = '' + ${description} + + ${experimentalFeatureNote} + + **Default:** ${showDefault documentDefault defaultValue} + + ${showAliases aliases} + ''; + + experimentalFeatureNote = optionalString (experimentalFeature != null) '' + > **Warning** + > This setting is part of an + > [experimental feature](@docroot@/contributing/experimental-features.md). + + To change this setting, you need to make sure the corresponding experimental feature, + [`${experimentalFeature}`](@docroot@/contributing/experimental-features.md#xp-feature-${experimentalFeature}), + is enabled. + For example, include the following in [`nix.conf`](#): + + ``` + extra-experimental-features = ${experimentalFeature} + ${setting} = ... + ``` + ''; + + showDefault = documentDefault: defaultValue: + if documentDefault then + # a StringMap value type is specified as a string, but + # this shows the value type. The empty stringmap is `null` in + # JSON, but that converts to `{ }` here. + if defaultValue == "" || defaultValue == [] || isAttrs defaultValue + then "*empty*" + else if isBool defaultValue then + if defaultValue then "`true`" else "`false`" + else "`${toString defaultValue}`" + else "*machine-specific*"; + + showAliases = aliases: + optionalString (aliases != []) + "**Deprecated alias:** ${(concatStringsSep ", " (map (s: "`${s}`") aliases))}"; + + in result; + +in concatStrings (attrValues (mapAttrs (showSetting prefix) settingsInfo)) diff --git a/doc/manual/generate-store-info.nix b/doc/manual/generate-store-info.nix new file mode 100644 index 000000000..7bb2ebad3 --- /dev/null +++ b/doc/manual/generate-store-info.nix @@ -0,0 +1,45 @@ +let + inherit (builtins) attrValues mapAttrs; + inherit (import ./utils.nix) concatStrings optionalString; + showSettings = import ./generate-settings.nix; +in + +storesInfo: + +let + + showStore = name: { settings, doc, experimentalFeature }: + let + + result = '' + ## ${name} + + ${doc} + + ${experimentalFeatureNote} + + ### Settings + + ${showSettings "store-${slug}" settings} + ''; + + # markdown doesn't like spaces in URLs + slug = builtins.replaceStrings [ " " ] [ "-" ] name; + + experimentalFeatureNote = optionalString (experimentalFeature != null) '' + > **Warning** + > This store is part of an + > [experimental feature](@docroot@/contributing/experimental-features.md). + + To use this store, you need to make sure the corresponding experimental feature, + [`${experimentalFeature}`](@docroot@/contributing/experimental-features.md#xp-feature-${experimentalFeature}), + is enabled. + For example, include the following in [`nix.conf`](#): + + ``` + extra-experimental-features = ${experimentalFeature} + ``` + ''; + in result; + +in concatStrings (attrValues (mapAttrs showStore storesInfo)) diff --git a/doc/manual/local.mk b/doc/manual/local.mk index abdfd6a62..927854066 100644 --- a/doc/manual/local.mk +++ b/doc/manual/local.mk @@ -96,14 +96,14 @@ $(d)/src/SUMMARY.md: $(d)/src/SUMMARY.md.in $(d)/src/command-ref/new-cli $(d)/sr @cp $< $@ @$(call process-includes,$@,$@) -$(d)/src/command-ref/new-cli: $(d)/nix.json $(d)/utils.nix $(d)/generate-manpage.nix $(bindir)/nix +$(d)/src/command-ref/new-cli: $(d)/nix.json $(d)/utils.nix $(d)/generate-manpage.nix $(d)/generate-settings.nix $(d)/generate-store-info.nix $(bindir)/nix @rm -rf $@ $@.tmp $(trace-gen) $(nix-eval) --write-to $@.tmp --expr 'import doc/manual/generate-manpage.nix (builtins.readFile $<)' @mv $@.tmp $@ -$(d)/src/command-ref/conf-file.md: $(d)/conf-file.json $(d)/utils.nix $(d)/src/command-ref/conf-file-prefix.md $(d)/src/command-ref/experimental-features-shortlist.md $(bindir)/nix +$(d)/src/command-ref/conf-file.md: $(d)/conf-file.json $(d)/utils.nix $(d)/generate-settings.nix $(d)/src/command-ref/conf-file-prefix.md $(d)/src/command-ref/experimental-features-shortlist.md $(bindir)/nix @cat doc/manual/src/command-ref/conf-file-prefix.md > $@.tmp - $(trace-gen) $(nix-eval) --expr '(import doc/manual/utils.nix).showSettings { useAnchors = true; } (builtins.fromJSON (builtins.readFile $<))' >> $@.tmp; + $(trace-gen) $(nix-eval) --expr 'import doc/manual/generate-settings.nix "opt-" (builtins.fromJSON (builtins.readFile $<))' >> $@.tmp; @mv $@.tmp $@ $(d)/nix.json: $(bindir)/nix diff --git a/doc/manual/utils.nix b/doc/manual/utils.nix index 9043dd8cd..849832b2c 100644 --- a/doc/manual/utils.nix +++ b/doc/manual/utils.nix @@ -44,63 +44,6 @@ rec { optionalString = cond: string: if cond then string else ""; - showSetting = { useAnchors }: name: { description, documentDefault, defaultValue, aliases, value, experimentalFeature }: - let - result = squash '' - - ${if useAnchors - then ''[`${name}`](#conf-${name})'' - else ''`${name}`''} - - ${indent " " body} - ''; - - experimentalFeatureNote = optionalString (experimentalFeature != null) '' - > **Warning** - > This setting is part of an - > [experimental feature](@docroot@/contributing/experimental-features.md). - - To change this setting, you need to make sure the corresponding experimental feature, - [`${experimentalFeature}`](@docroot@/contributing/experimental-features.md#xp-feature-${experimentalFeature}), - is enabled. - For example, include the following in [`nix.conf`](#): - - ``` - extra-experimental-features = ${experimentalFeature} - ${name} = ... - ``` - ''; - - # separate body to cleanly handle indentation - body = '' - ${description} - - ${experimentalFeatureNote} - - **Default:** ${showDefault documentDefault defaultValue} - - ${showAliases aliases} - ''; - - showDefault = documentDefault: defaultValue: - if documentDefault then - # a StringMap value type is specified as a string, but - # this shows the value type. The empty stringmap is `null` in - # JSON, but that converts to `{ }` here. - if defaultValue == "" || defaultValue == [] || isAttrs defaultValue - then "*empty*" - else if isBool defaultValue then - if defaultValue then "`true`" else "`false`" - else "`${toString defaultValue}`" - else "*machine-specific*"; - - showAliases = aliases: - optionalString (aliases != []) - "**Deprecated alias:** ${(concatStringsSep ", " (map (s: "`${s}`") aliases))}"; - - in result; - indent = prefix: s: concatStringsSep "\n" (map (x: if x == "" then x else "${prefix}${x}") (splitLines s)); - - showSettings = args: settingsInfo: concatStrings (attrValues (mapAttrs (showSetting args) settingsInfo)); } From 8232711c9fa0ba831c3554e1d19e6707dd093c39 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Wed, 4 Oct 2023 23:07:13 +0200 Subject: [PATCH 088/116] fix wiring of baked-in Nix expressions --- src/nix/local.mk | 2 +- src/nix/main.cc | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/nix/local.mk b/src/nix/local.mk index 20ea29d10..57f8259c4 100644 --- a/src/nix/local.mk +++ b/src/nix/local.mk @@ -31,7 +31,7 @@ src/nix/develop.cc: src/nix/get-env.sh.gen.hh src/nix-channel/nix-channel.cc: src/nix-channel/unpack-channel.nix.gen.hh -src/nix/main.cc: doc/manual/generate-manpage.nix.gen.hh doc/manual/utils.nix.gen.hh +src/nix/main.cc: doc/manual/generate-manpage.nix.gen.hh doc/manual/utils.nix.gen.hh doc/manual/generate-settings.nix.gen.hh doc/manual/generate-store-info.nix.gen.hh src/nix/doc/files/%.md: doc/manual/src/command-ref/files/%.md @mkdir -p $$(dirname $@) diff --git a/src/nix/main.cc b/src/nix/main.cc index d05bac68e..031dc2348 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -214,6 +214,22 @@ static void showHelp(std::vector subcommand, NixArgs & toplevel) , CanonPath::root), *vUtils); + auto vSettingsInfo = state.allocValue(); + state.cacheFile( + CanonPath("/generate-settings.nix"), CanonPath("/generate-settings.nix"), + state.parseExprFromString( + #include "generate-settings.nix.gen.hh" + , CanonPath::root), + *vSettingsInfo); + + auto vStoreInfo = state.allocValue(); + state.cacheFile( + CanonPath("/generate-store-info.nix"), CanonPath("/generate-store-info.nix"), + state.parseExprFromString( + #include "generate-store-info.nix.gen.hh" + , CanonPath::root), + *vStoreInfo); + auto vDump = state.allocValue(); vDump->mkString(toplevel.dumpCli()); From 24bda0c7b381e1a017023c6f7cb9661fae8560bd Mon Sep 17 00:00:00 2001 From: edef Date: Wed, 4 Oct 2023 16:50:24 +0000 Subject: [PATCH 089/116] StorePath: reject names starting with '.' This has been the behaviour before Nix 2.4. It was dropped in a rewrite in 759947bf72c134592f0ce23d385e48095bd0a301, allowing the creation of store paths that aren't considered valid by older Nix versions or other Nix tooling. Nix 2.4 didn't ship in NixOS until 22.05, and stdenv.mkDerivation in nixpkgs drops leading periods since April 2022, so it's unlikely anyone is relying on the current lax behaviour. Closes #9091. Change-Id: I4a57bd9899e1b0dba56870ae5a1b680918a18ce9 --- src/libstore/path-regex.hh | 2 +- src/libstore/path.cc | 2 ++ src/libstore/tests/path.cc | 9 +++++++-- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/libstore/path-regex.hh b/src/libstore/path-regex.hh index 4f8dc4c1f..a44e6a2eb 100644 --- a/src/libstore/path-regex.hh +++ b/src/libstore/path-regex.hh @@ -3,6 +3,6 @@ namespace nix { -static constexpr std::string_view nameRegexStr = R"([0-9a-zA-Z\+\-\._\?=]+)"; +static constexpr std::string_view nameRegexStr = R"([0-9a-zA-Z\+\-_\?=][0-9a-zA-Z\+\-\._\?=]*)"; } diff --git a/src/libstore/path.cc b/src/libstore/path.cc index 552e83114..3c6b9fc10 100644 --- a/src/libstore/path.cc +++ b/src/libstore/path.cc @@ -11,6 +11,8 @@ static void checkName(std::string_view path, std::string_view name) if (name.size() > StorePath::MaxPathLen) throw BadStorePath("store path '%s' has a name longer than %d characters", path, StorePath::MaxPathLen); + if (name[0] == '.') + throw BadStorePath("store path '%s' starts with illegal character '.'", path); // See nameRegexStr for the definition for (auto c : name) if (!((c >= '0' && c <= '9') diff --git a/src/libstore/tests/path.cc b/src/libstore/tests/path.cc index efa35ef2b..5a84d646c 100644 --- a/src/libstore/tests/path.cc +++ b/src/libstore/tests/path.cc @@ -39,6 +39,7 @@ TEST_DONT_PARSE(double_star, "**") TEST_DONT_PARSE(star_first, "*,foo") TEST_DONT_PARSE(star_second, "foo,*") TEST_DONT_PARSE(bang, "foo!o") +TEST_DONT_PARSE(dotfile, ".gitignore") #undef TEST_DONT_PARSE @@ -101,8 +102,12 @@ Gen Arbitrary::arbitrary() pre += '-'; break; case 64: - pre += '.'; - break; + // names aren't permitted to start with a period, + // so just fall through to the next case here + if (c != 0) { + pre += '.'; + break; + } case 65: pre += '_'; break; From e0e47c0a68f7e1b6b0f9e5065e8113b32b12c212 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Thu, 5 Oct 2023 01:20:26 +0200 Subject: [PATCH 090/116] accommodate inconsistent output from `lowdown` the `term` output mode leaves inline HTML around verbatim, while `nroff` mode (used for `man` pages) does not. the correct solution would be to pre-render all output with a more benign tool so we have less liabilities in our own code, but this has to do for now. --- doc/manual/generate-manpage.nix | 22 +++++++++++++--------- doc/manual/generate-settings.nix | 9 ++++++--- doc/manual/generate-store-info.nix | 4 ++-- doc/manual/local.mk | 4 ++-- src/nix/main.cc | 3 ++- 5 files changed, 25 insertions(+), 17 deletions(-) diff --git a/doc/manual/generate-manpage.nix b/doc/manual/generate-manpage.nix index 425eea002..d81eac182 100644 --- a/doc/manual/generate-manpage.nix +++ b/doc/manual/generate-manpage.nix @@ -6,7 +6,7 @@ let showStoreDocs = import ./generate-store-info.nix; in -commandDump: +inlineHTML: commandDump: let @@ -75,7 +75,7 @@ let # store parameters should not be part of command documentation to begin # with, but instead be rendered on separate pages. maybeStoreDocs = optionalString (details ? doc) - (replaceStrings [ "@stores@" ] [ (showStoreDocs commandInfo.stores) ] details.doc); + (replaceStrings [ "@stores@" ] [ (showStoreDocs inlineHTML commandInfo.stores) ] details.doc); maybeOptions = let allVisibleOptions = filterAttrs @@ -84,14 +84,14 @@ let in optionalString (allVisibleOptions != {}) '' # Options - ${showOptions allVisibleOptions} + ${showOptions inlineHTML allVisibleOptions} > **Note** > > See [`man nix.conf`](@docroot@/command-ref/conf-file.md#command-line-flags) for overriding configuration settings with command line flags. ''; - showOptions = allOptions: + showOptions = inlineHTML: allOptions: let showCategory = cat: opts: '' ${optionalString (cat != "") "## ${cat}"} @@ -100,17 +100,21 @@ let ''; showOption = name: option: let + result = trim '' + - ${item} + + ${option.description} + ''; + item = if inlineHTML + then ''[`--${name}`](#opt-${name}) ${shortName} ${labels}'' + else "`--${name}` ${shortName} ${labels}"; shortName = optionalString (option ? shortName) ("/ `-${option.shortName}`"); labels = optionalString (option ? labels) (concatStringsSep " " (map (s: "*${s}*") option.labels)); - in trim '' - - [`--${name}`](#opt-${name}) ${shortName} ${labels} - - ${option.description} - ''; + in result; categories = mapAttrs # Convert each group from a list of key-value pairs back to an attrset (_: listToAttrs) diff --git a/doc/manual/generate-settings.nix b/doc/manual/generate-settings.nix index 450771f73..8736bb793 100644 --- a/doc/manual/generate-settings.nix +++ b/doc/manual/generate-settings.nix @@ -3,18 +3,21 @@ let inherit (import ./utils.nix) concatStrings indent optionalString squash; in -prefix: settingsInfo: +# `inlineHTML` is a hack to accommodate inconsistent output from `lowdown` +{ prefix, inlineHTML ? true }: settingsInfo: let showSetting = prefix: setting: { description, documentDefault, defaultValue, aliases, value, experimentalFeature }: let result = squash '' - - [`${setting}`](#${prefix}-${setting}) + - ${item} ${indent " " body} ''; - + item = if inlineHTML + then ''[`${setting}`](#${prefix}-${setting})'' + else "`${setting}`"; # separate body to cleanly handle indentation body = '' ${description} diff --git a/doc/manual/generate-store-info.nix b/doc/manual/generate-store-info.nix index 7bb2ebad3..36215aadf 100644 --- a/doc/manual/generate-store-info.nix +++ b/doc/manual/generate-store-info.nix @@ -4,7 +4,7 @@ let showSettings = import ./generate-settings.nix; in -storesInfo: +inlineHTML: storesInfo: let @@ -20,7 +20,7 @@ let ### Settings - ${showSettings "store-${slug}" settings} + ${showSettings { prefix = "store-${slug}"; inherit inlineHTML; } settings} ''; # markdown doesn't like spaces in URLs diff --git a/doc/manual/local.mk b/doc/manual/local.mk index 927854066..2d1db66f3 100644 --- a/doc/manual/local.mk +++ b/doc/manual/local.mk @@ -98,12 +98,12 @@ $(d)/src/SUMMARY.md: $(d)/src/SUMMARY.md.in $(d)/src/command-ref/new-cli $(d)/sr $(d)/src/command-ref/new-cli: $(d)/nix.json $(d)/utils.nix $(d)/generate-manpage.nix $(d)/generate-settings.nix $(d)/generate-store-info.nix $(bindir)/nix @rm -rf $@ $@.tmp - $(trace-gen) $(nix-eval) --write-to $@.tmp --expr 'import doc/manual/generate-manpage.nix (builtins.readFile $<)' + $(trace-gen) $(nix-eval) --write-to $@.tmp --expr 'import doc/manual/generate-manpage.nix true (builtins.readFile $<)' @mv $@.tmp $@ $(d)/src/command-ref/conf-file.md: $(d)/conf-file.json $(d)/utils.nix $(d)/generate-settings.nix $(d)/src/command-ref/conf-file-prefix.md $(d)/src/command-ref/experimental-features-shortlist.md $(bindir)/nix @cat doc/manual/src/command-ref/conf-file-prefix.md > $@.tmp - $(trace-gen) $(nix-eval) --expr 'import doc/manual/generate-settings.nix "opt-" (builtins.fromJSON (builtins.readFile $<))' >> $@.tmp; + $(trace-gen) $(nix-eval) --expr 'import doc/manual/generate-settings.nix { prefix = "opt-"; } (builtins.fromJSON (builtins.readFile $<))' >> $@.tmp; @mv $@.tmp $@ $(d)/nix.json: $(bindir)/nix diff --git a/src/nix/main.cc b/src/nix/main.cc index 031dc2348..879baa5f5 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -234,7 +234,8 @@ static void showHelp(std::vector subcommand, NixArgs & toplevel) vDump->mkString(toplevel.dumpCli()); auto vRes = state.allocValue(); - state.callFunction(*vGenerateManpage, *vDump, *vRes, noPos); + state.callFunction(*vGenerateManpage, state.getBuiltin("false"), *vRes, noPos); + state.callFunction(*vRes, *vDump, *vRes, noPos); auto attr = vRes->attrs->get(state.symbols.create(mdName + ".md")); if (!attr) From 80f734a803d3170eadd51758c71390ff87be22f9 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Thu, 5 Oct 2023 09:20:42 +0200 Subject: [PATCH 091/116] more specific links to nix.dev --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1440239bc..65daa22e4 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ for more details. ## Installation and first steps -Visit [nix.dev](https://nix.dev) for installation instructions and beginner tutorials. +Visit [nix.dev](https://nix.dev) for [installation instructions](https://nix.dev/tutorials/install-nix) and [beginner tutorials](https://nix.dev/tutorials/first-steps). Full reference documentation can be found in the [Nix manual](https://nixos.org/nix/manual). From eb68454be61bd6e8baf40136ae69c2b5fec3006a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Wed, 4 Oct 2023 17:10:54 +0200 Subject: [PATCH 092/116] Don't run the tests that require building if we're not building A couple of tests require building some libraries that depend on Nix, and assume it to be built locally. Don't run these if we only want to run the install tests. This prevents the CI from rebuilding several times Nix (like in https://github.com/NixOS/nix/actions/runs/6404422275/job/17384964033#step:6:6412), thus removing a fair amount of build time. --- flake.nix | 1 + tests/local.mk | 24 ++++++++++++++++-------- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/flake.nix b/flake.nix index e3b9477a5..d74085db7 100644 --- a/flake.nix +++ b/flake.nix @@ -260,6 +260,7 @@ testNixVersions = pkgs: client: daemon: with commonDeps { inherit pkgs; }; with pkgs.lib; pkgs.stdenv.mkDerivation { NIX_DAEMON_PACKAGE = daemon; NIX_CLIENT_PACKAGE = client; + HAVE_LOCAL_NIX_BUILD = false; name = "nix-tests" + optionalString diff --git a/tests/local.mk b/tests/local.mk index 7479b1576..ac418dc0d 100644 --- a/tests/local.mk +++ b/tests/local.mk @@ -1,3 +1,7 @@ +# whether to run the tests that assume that we have a local build of +# Nix +HAVE_LOCAL_NIX_BUILD ?= 1 + nix_tests = \ test-infra.sh \ init.sh \ @@ -104,7 +108,6 @@ nix_tests = \ case-hack.sh \ placeholders.sh \ ssh-relay.sh \ - plugins.sh \ build.sh \ build-delete.sh \ output-normalization.sh \ @@ -120,7 +123,6 @@ nix_tests = \ flakes/show.sh \ impure-derivations.sh \ path-from-hash-part.sh \ - test-libstoreconsumer.sh \ toString-path.sh \ read-only-store.sh \ nested-sandboxing.sh @@ -129,6 +131,17 @@ ifeq ($(HAVE_LIBCPUID), 1) nix_tests += compute-levels.sh endif +ifeq ($(HAVE_LOCAL_NIX_BUILD), 1) + nix_tests += test-libstoreconsumer.sh + + ifeq ($(BUILD_SHARED_LIBS), 1) + nix_tests += plugins.sh + endif +endif + +tests/test-libstoreconsumer.sh.test: tests/test-libstoreconsumer/test-libstoreconsumer +tests/plugins.sh: tests/plugins/libplugintest.$(SO_EXT) + install-tests += $(foreach x, $(nix_tests), $(d)/$(x)) clean-files += \ @@ -137,9 +150,4 @@ clean-files += \ test-deps += \ tests/common/vars-and-functions.sh \ - tests/config.nix \ - tests/test-libstoreconsumer/test-libstoreconsumer - -ifeq ($(BUILD_SHARED_LIBS), 1) - test-deps += tests/plugins/libplugintest.$(SO_EXT) -endif + tests/config.nix From 92e8e1b1bbc59d3a7e9efd5bff146b0a2726c7b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Wed, 4 Oct 2023 17:22:45 +0200 Subject: [PATCH 093/116] Poison the build on the test derivation Make sure that we're not accidentally rebuilding Nix here as it's just wasteful and awful for CI times. --- flake.nix | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index d74085db7..4de155362 100644 --- a/flake.nix +++ b/flake.nix @@ -280,9 +280,13 @@ enableParallelBuilding = true; configureFlags = testConfigureFlags; # otherwise configure fails - dontBuild = true; doInstallCheck = true; + buildPhase = '' + # Remove the source files to make sure that we're not accidentally rebuilding Nix + rm src/**/*.cc + ''; + installPhase = '' mkdir -p $out ''; From f95364a803ddf694e4d7a7f6841101b93d9741fe Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 1 Sep 2023 12:20:54 +0200 Subject: [PATCH 094/116] eval: Run a full GC before printing stats This makes the numbers more deterministic, especially when it comes to the final heap size. --- src/libexpr/eval.cc | 212 +++++++++++++++++++++++++------------------- src/libexpr/eval.hh | 18 +++- 2 files changed, 136 insertions(+), 94 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index a78c0afb1..600444955 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -2477,10 +2477,37 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v } } +bool EvalState::fullGC() { +#if HAVE_BOEHMGC + GC_gcollect(); + // Check that it ran. We might replace this with a version that uses more + // of the boehm API to get this reliably, at a maintenance cost. + // We use a 1K margin because technically this has a race condtion, but we + // probably won't encounter it in practice, because the CLI isn't concurrent + // like that. + return GC_get_bytes_since_gc() < 1024; +#else + return false; +#endif +} + void EvalState::printStats() { bool showStats = getEnv("NIX_SHOW_STATS").value_or("0") != "0"; + if (showStats) { + // Make the final heap size more deterministic. +#if HAVE_BOEHMGC + if (!fullGC()) { + warn("failed to perform a full GC before reporting stats"); + } +#endif + printStatistics(); + } +} + +void EvalState::printStatistics() +{ struct rusage buf; getrusage(RUSAGE_SELF, &buf); float cpuTime = buf.ru_utime.tv_sec + ((float) buf.ru_utime.tv_usec / 1000000); @@ -2494,106 +2521,105 @@ void EvalState::printStats() GC_word heapSize, totalBytes; GC_get_heap_usage_safe(&heapSize, 0, 0, 0, &totalBytes); #endif - if (showStats) { - auto outPath = getEnv("NIX_SHOW_STATS_PATH").value_or("-"); - std::fstream fs; - if (outPath != "-") - fs.open(outPath, std::fstream::out); - json topObj = json::object(); - topObj["cpuTime"] = cpuTime; - topObj["envs"] = { - {"number", nrEnvs}, - {"elements", nrValuesInEnvs}, - {"bytes", bEnvs}, - }; - topObj["nrExprs"] = Expr::nrExprs; - topObj["list"] = { - {"elements", nrListElems}, - {"bytes", bLists}, - {"concats", nrListConcats}, - }; - topObj["values"] = { - {"number", nrValues}, - {"bytes", bValues}, - }; - topObj["symbols"] = { - {"number", symbols.size()}, - {"bytes", symbols.totalSize()}, - }; - topObj["sets"] = { - {"number", nrAttrsets}, - {"bytes", bAttrsets}, - {"elements", nrAttrsInAttrsets}, - }; - topObj["sizes"] = { - {"Env", sizeof(Env)}, - {"Value", sizeof(Value)}, - {"Bindings", sizeof(Bindings)}, - {"Attr", sizeof(Attr)}, - }; - topObj["nrOpUpdates"] = nrOpUpdates; - topObj["nrOpUpdateValuesCopied"] = nrOpUpdateValuesCopied; - topObj["nrThunks"] = nrThunks; - topObj["nrAvoided"] = nrAvoided; - topObj["nrLookups"] = nrLookups; - topObj["nrPrimOpCalls"] = nrPrimOpCalls; - topObj["nrFunctionCalls"] = nrFunctionCalls; + + auto outPath = getEnv("NIX_SHOW_STATS_PATH").value_or("-"); + std::fstream fs; + if (outPath != "-") + fs.open(outPath, std::fstream::out); + json topObj = json::object(); + topObj["cpuTime"] = cpuTime; + topObj["envs"] = { + {"number", nrEnvs}, + {"elements", nrValuesInEnvs}, + {"bytes", bEnvs}, + }; + topObj["nrExprs"] = Expr::nrExprs; + topObj["list"] = { + {"elements", nrListElems}, + {"bytes", bLists}, + {"concats", nrListConcats}, + }; + topObj["values"] = { + {"number", nrValues}, + {"bytes", bValues}, + }; + topObj["symbols"] = { + {"number", symbols.size()}, + {"bytes", symbols.totalSize()}, + }; + topObj["sets"] = { + {"number", nrAttrsets}, + {"bytes", bAttrsets}, + {"elements", nrAttrsInAttrsets}, + }; + topObj["sizes"] = { + {"Env", sizeof(Env)}, + {"Value", sizeof(Value)}, + {"Bindings", sizeof(Bindings)}, + {"Attr", sizeof(Attr)}, + }; + topObj["nrOpUpdates"] = nrOpUpdates; + topObj["nrOpUpdateValuesCopied"] = nrOpUpdateValuesCopied; + topObj["nrThunks"] = nrThunks; + topObj["nrAvoided"] = nrAvoided; + topObj["nrLookups"] = nrLookups; + topObj["nrPrimOpCalls"] = nrPrimOpCalls; + topObj["nrFunctionCalls"] = nrFunctionCalls; #if HAVE_BOEHMGC - topObj["gc"] = { - {"heapSize", heapSize}, - {"totalBytes", totalBytes}, - }; + topObj["gc"] = { + {"heapSize", heapSize}, + {"totalBytes", totalBytes}, + }; #endif - if (countCalls) { - topObj["primops"] = primOpCalls; - { - auto& list = topObj["functions"]; - list = json::array(); - for (auto & [fun, count] : functionCalls) { - json obj = json::object(); - if (fun->name) - obj["name"] = (std::string_view) symbols[fun->name]; - else - obj["name"] = nullptr; - if (auto pos = positions[fun->pos]) { - if (auto path = std::get_if(&pos.origin)) - obj["file"] = path->to_string(); - obj["line"] = pos.line; - obj["column"] = pos.column; - } - obj["count"] = count; - list.push_back(obj); - } - } - { - auto list = topObj["attributes"]; - list = json::array(); - for (auto & i : attrSelects) { - json obj = json::object(); - if (auto pos = positions[i.first]) { - if (auto path = std::get_if(&pos.origin)) - obj["file"] = path->to_string(); - obj["line"] = pos.line; - obj["column"] = pos.column; - } - obj["count"] = i.second; - list.push_back(obj); + if (countCalls) { + topObj["primops"] = primOpCalls; + { + auto& list = topObj["functions"]; + list = json::array(); + for (auto & [fun, count] : functionCalls) { + json obj = json::object(); + if (fun->name) + obj["name"] = (std::string_view) symbols[fun->name]; + else + obj["name"] = nullptr; + if (auto pos = positions[fun->pos]) { + if (auto path = std::get_if(&pos.origin)) + obj["file"] = path->to_string(); + obj["line"] = pos.line; + obj["column"] = pos.column; } + obj["count"] = count; + list.push_back(obj); } } + { + auto list = topObj["attributes"]; + list = json::array(); + for (auto & i : attrSelects) { + json obj = json::object(); + if (auto pos = positions[i.first]) { + if (auto path = std::get_if(&pos.origin)) + obj["file"] = path->to_string(); + obj["line"] = pos.line; + obj["column"] = pos.column; + } + obj["count"] = i.second; + list.push_back(obj); + } + } + } - if (getEnv("NIX_SHOW_SYMBOLS").value_or("0") != "0") { - // XXX: overrides earlier assignment - topObj["symbols"] = json::array(); - auto &list = topObj["symbols"]; - symbols.dump([&](const std::string & s) { list.emplace_back(s); }); - } - if (outPath == "-") { - std::cerr << topObj.dump(2) << std::endl; - } else { - fs << topObj.dump(2) << std::endl; - } + if (getEnv("NIX_SHOW_SYMBOLS").value_or("0") != "0") { + // XXX: overrides earlier assignment + topObj["symbols"] = json::array(); + auto &list = topObj["symbols"]; + symbols.dump([&](const std::string & s) { list.emplace_back(s); }); + } + if (outPath == "-") { + std::cerr << topObj.dump(2) << std::endl; + } else { + fs << topObj.dump(2) << std::endl; } } diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index fa8fa462b..3bfa34ef6 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -709,10 +709,26 @@ public: void concatLists(Value & v, size_t nrLists, Value * * lists, const PosIdx pos, std::string_view errorCtx); /** - * Print statistics. + * Print statistics, if enabled. + * + * Performs a full memory GC before printing the statistics, so that the + * GC statistics are more accurate. */ void printStats(); + /** + * Print statistics, unconditionally, cheaply, without performing a GC first. + */ + void printStatistics(); + + /** + * Perform a full memory garbage collection - not incremental. + * + * @return true if Nix was built with GC and a GC was performed, false if not. + * The return value is currently not thread safe - just the return value. + */ + bool fullGC(); + /** * Realise the given context, and return a mapping from the placeholders * used to construct the associated value to their final store path From 369b076986531f3fbf6933539a0379b5d3fb1970 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Fri, 6 Oct 2023 11:46:41 +0200 Subject: [PATCH 095/116] add links and anchors --- doc/manual/src/command-ref/nix-env/install.md | 70 +++++++++---------- 1 file changed, 34 insertions(+), 36 deletions(-) diff --git a/doc/manual/src/command-ref/nix-env/install.md b/doc/manual/src/command-ref/nix-env/install.md index 95648e9e0..c1fff50e8 100644 --- a/doc/manual/src/command-ref/nix-env/install.md +++ b/doc/manual/src/command-ref/nix-env/install.md @@ -14,19 +14,21 @@ # Description -The install operation creates a new user environment, based on the -current generation of the active profile, to which a set of store paths -described by *args* is added. The arguments *args* map to store paths in -a number of possible ways: +The install operation creates a new user environment. +It is based on the current generation of the active [profile](@docroot@/command-ref/files/profiles.md), to which a set of [store paths] described by *args* is added. + +[store paths]: @docroot@/glossary.md#gloss-store-path + +The arguments *args* map to store paths in a number of possible ways: - - By default, *args* is a set of [derivation] names denoting derivations - in the active Nix expression. These are realised, and the resulting - output paths are installed. Currently installed derivations with a - name equal to the name of a derivation being added are removed - unless the option `--preserve-installed` is specified. + - By default, *args* is a set of [derivation] names denoting derivations in the [default Nix expression]. + These are [realised], and the resulting output paths are installed. + Currently installed derivations with a name equal to the name of a derivation being added are removed unless the option `--preserve-installed` is specified. - [derivation]: @docroot@/language/derivations.md + [derivation]: @docroot@/glossary.md#gloss-derivation + [default Nix expression]: @docroot@/command-ref/files/default-nix-expression.md + [realised]: @docroot@/glossary.md#gloss-realise If there are multiple derivations matching a name in *args* that have the same name (e.g., `gcc-3.3.6` and `gcc-4.1.1`), then the @@ -43,40 +45,33 @@ a number of possible ways: gcc-3.3.6 gcc-4.1.1` will install both version of GCC (and will probably cause a user environment conflict\!). - - If `--attr` (`-A`) is specified, the arguments are *attribute - paths* that select attributes from the top-level Nix - expression. This is faster than using derivation names and - unambiguous. To find out the attribute paths of available - packages, use `nix-env --query --available --attr-path `. + - If [`--attr`](#opt-attr) / `-A` is specified, the arguments are *attribute paths* that select attributes from the [default Nix expression]. + This is faster than using derivation names and unambiguous. + Show the attribute paths of available packages with [`nix-env --query`](./query.md): + + ```console + nix-env --query --available --attr-path` + ``` - If `--from-profile` *path* is given, *args* is a set of names - denoting installed store paths in the profile *path*. This is an + denoting installed [store paths] in the profile *path*. This is an easy way to copy user environment elements from one profile to another. - - If `--from-expression` is given, *args* are Nix - [functions](@docroot@/language/constructs.md#functions) - that are called with the active Nix expression as their single - argument. The derivations returned by those function calls are - installed. This allows derivations to be specified in an - unambiguous way, which is necessary if there are multiple - derivations with the same name. + - If `--from-expression` is given, *args* are [Nix language functions](@docroot@/language/constructs.md#functions) that are called with the [default Nix expression] as their single argument. + The derivations returned by those function calls are installed. + This allows derivations to be specified in an unambiguous way, which is necessary if there are multiple derivations with the same name. - - If *args* are [store derivations](@docroot@/glossary.md#gloss-store-derivation), then these are - [realised](@docroot@/command-ref/nix-store/realise.md), and the resulting output paths - are installed. + - If *args* are [store derivations](@docroot@/glossary.md#gloss-store-derivation), then these are [realised], and the resulting output paths are installed. - - If *args* are store paths that are not store derivations, then these - are [realised](@docroot@/command-ref/nix-store/realise.md) and installed. + - If *args* are [store paths] that are not store derivations, then these are [realised] and installed. - - By default all outputs are installed for each derivation. + - By default all [outputs](@docroot@/language/derivations.md#attr-outputs) are installed for each [derivation]. This can be overridden by adding a `meta.outputsToInstall` attribute on the derivation listing a subset of the output names. - - Example: - The file `example.nix` defines a [derivation] with two outputs `foo` and `bar`, each containing a file. + The file `example.nix` defines a derivation with two outputs `foo` and `bar`, each containing a file. ```nix # example.nix @@ -123,15 +118,17 @@ a number of possible ways: manifest.nix ``` -# Flags +# Options + + - `--prebuilt-only` / `-b` - - `--prebuilt-only` / `-b`\ Use only derivations for which a substitute is registered, i.e., there is a pre-built binary available that can be downloaded in lieu of building the derivation. Thus, no packages will be built from source. - - `--preserve-installed` / `-P`\ + - `--preserve-installed` / `-P` + Do not remove derivations with a name matching one of the derivations being installed. Usually, trying to have two versions of the same package installed in the same generation of a profile will @@ -139,7 +136,8 @@ a number of possible ways: clashes between the two versions. However, this is not the case for all packages. - - `--remove-all` / `-r`\ + - `--remove-all` / `-r` + Remove all previously installed packages first. This is equivalent to running `nix-env --uninstall '.*'` first, except that everything happens in a single transaction. From 68c81c737571794f7246db53fb4774e94fcf4b7e Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 5 Oct 2023 12:12:18 -0400 Subject: [PATCH 096/116] Put functional tests in `tests/functional` I think it is bad for these reasons when `tests/` contains a mix of functional and integration tests - Concepts is harder to understand, the documentation makes a good unit vs functional vs integration distinction, but when the integration tests are just two subdirs within `tests/` this is not clear. - Source filtering in the `flake.nix` is more complex. We need to filter out some of the dirs from `tests/`, rather than simply pick the dirs we want and take all of them. This is a good sign the structure of what we are trying to do is not matching the structure of the files. With this change we have a clean: ```shell-session $ git show 'HEAD:tests' tree HEAD:tests functional/ installer/ nixos/ ``` --- .github/labeler.yml | 2 +- .gitignore | 34 ++++++------ CONTRIBUTING.md | 2 +- Makefile | 10 ++-- doc/manual/src/contributing/testing.md | 26 ++++----- flake.nix | 52 ++++++++---------- mk/common-test.sh | 8 ++- src/libstore/gc.cc | 2 +- src/libutil/url-parts.hh | 2 +- tests/{ => functional}/add.sh | 0 tests/{ => functional}/bad.tar.xz | Bin tests/{ => functional}/bash-profile.sh | 2 +- .../{ => functional}/big-derivation-attr.nix | 0 .../binary-cache-build-remote.sh | 0 tests/{ => functional}/binary-cache.sh | 0 tests/{ => functional}/brotli.sh | 0 tests/{ => functional}/build-delete.sh | 0 tests/{ => functional}/build-dry.sh | 0 .../{ => functional}/build-hook-ca-fixed.nix | 0 .../build-hook-ca-floating.nix | 0 tests/{ => functional}/build-hook.nix | 0 .../build-remote-content-addressed-fixed.sh | 0 ...build-remote-content-addressed-floating.sh | 0 .../build-remote-input-addressed.sh | 0 .../build-remote-trustless-after.sh | 0 .../build-remote-trustless-should-fail-0.sh | 0 .../build-remote-trustless-should-pass-0.sh | 0 .../build-remote-trustless-should-pass-1.sh | 0 .../build-remote-trustless-should-pass-2.sh | 0 .../build-remote-trustless-should-pass-3.sh | 0 .../build-remote-trustless.sh | 2 +- tests/{ => functional}/build-remote.sh | 0 tests/{ => functional}/build.sh | 0 tests/{ => functional}/ca-shell.nix | 0 tests/{ => functional}/ca/build-cache.sh | 0 tests/{ => functional}/ca/build-dry.sh | 0 .../ca/build-with-garbage-path.sh | 0 tests/{ => functional}/ca/build.sh | 0 tests/{ => functional}/ca/common.sh | 0 .../{ => functional}/ca/concurrent-builds.sh | 0 tests/{ => functional}/ca/config.nix.in | 0 .../{ => functional}/ca/content-addressed.nix | 0 tests/{ => functional}/ca/derivation-json.sh | 0 .../ca/duplicate-realisation-in-closure.sh | 0 tests/{ => functional}/ca/flake.nix | 0 tests/{ => functional}/ca/gc.sh | 0 .../{ => functional}/ca/import-derivation.sh | 0 tests/{ => functional}/ca/local.mk | 2 +- tests/{ => functional}/ca/new-build-cmd.sh | 0 tests/{ => functional}/ca/nix-copy.sh | 0 tests/{ => functional}/ca/nix-run.sh | 0 tests/{ => functional}/ca/nix-shell.sh | 0 .../{ => functional}/ca/nondeterministic.nix | 0 tests/{ => functional}/ca/post-hook.sh | 0 tests/{ => functional}/ca/racy.nix | 0 tests/{ => functional}/ca/recursive.sh | 0 tests/{ => functional}/ca/repl.sh | 0 tests/{ => functional}/ca/selfref-gc.sh | 0 tests/{ => functional}/ca/signatures.sh | 0 tests/{ => functional}/ca/substitute.sh | 0 tests/{ => functional}/ca/why-depends.sh | 0 tests/{ => functional}/case-hack.sh | 0 tests/{ => functional}/case.nar | Bin tests/{ => functional}/check-refs.nix | 0 tests/{ => functional}/check-refs.sh | 0 tests/{ => functional}/check-reqs.nix | 0 tests/{ => functional}/check-reqs.sh | 0 tests/{ => functional}/check.nix | 0 tests/{ => functional}/check.sh | 0 tests/{ => functional}/common.sh | 0 .../common/vars-and-functions.sh.in | 2 +- tests/{ => functional}/completions.sh | 0 tests/{ => functional}/compression-levels.sh | 0 tests/{ => functional}/compute-levels.sh | 0 tests/{ => functional}/config.nix.in | 0 tests/{ => functional}/config.sh | 0 .../config/nix-with-substituters.conf | 0 tests/{ => functional}/db-migration.sh | 0 .../{ => functional}/dependencies.builder0.sh | 0 tests/{ => functional}/dependencies.nix | 0 tests/{ => functional}/dependencies.sh | 0 tests/{ => functional}/derivation-json.sh | 0 tests/{ => functional}/dummy | 0 tests/{ => functional}/dump-db.sh | 0 .../dyn-drv/build-built-drv.sh | 0 tests/{ => functional}/dyn-drv/common.sh | 0 tests/{ => functional}/dyn-drv/config.nix.in | 0 .../{ => functional}/dyn-drv/dep-built-drv.sh | 0 .../{ => functional}/dyn-drv/eval-outputOf.sh | 0 tests/{ => functional}/dyn-drv/local.mk | 2 +- .../dyn-drv/old-daemon-error-hack.nix | 0 .../dyn-drv/old-daemon-error-hack.sh | 0 .../dyn-drv/recursive-mod-json.nix | 0 .../dyn-drv/recursive-mod-json.sh | 0 .../dyn-drv/text-hashed-output.nix | 0 .../dyn-drv/text-hashed-output.sh | 0 tests/{ => functional}/eval-store.sh | 0 tests/{ => functional}/eval.nix | 0 tests/{ => functional}/eval.sh | 0 .../{ => functional}/experimental-features.sh | 0 tests/{ => functional}/export-graph.nix | 0 tests/{ => functional}/export-graph.sh | 0 tests/{ => functional}/export.sh | 0 tests/{ => functional}/failing.nix | 0 tests/{ => functional}/fetchClosure.sh | 0 tests/{ => functional}/fetchGit.sh | 0 tests/{ => functional}/fetchGitRefs.sh | 0 tests/{ => functional}/fetchGitSubmodules.sh | 0 tests/{ => functional}/fetchMercurial.sh | 0 tests/{ => functional}/fetchPath.sh | 0 tests/{ => functional}/fetchTree-file.sh | 0 tests/{ => functional}/fetchurl.sh | 0 tests/{ => functional}/filter-source.nix | 0 tests/{ => functional}/filter-source.sh | 0 tests/{ => functional}/fixed.builder1.sh | 0 tests/{ => functional}/fixed.builder2.sh | 0 tests/{ => functional}/fixed.nix | 0 tests/{ => functional}/fixed.sh | 0 .../flakes/absolute-attr-paths.sh | 0 .../{ => functional}/flakes/absolute-paths.sh | 0 tests/{ => functional}/flakes/build-paths.sh | 0 tests/{ => functional}/flakes/bundle.sh | 0 tests/{ => functional}/flakes/check.sh | 0 tests/{ => functional}/flakes/circular.sh | 0 tests/{ => functional}/flakes/common.sh | 0 tests/{ => functional}/flakes/config.sh | 0 .../flakes/flake-in-submodule.sh | 0 tests/{ => functional}/flakes/flakes.sh | 0 tests/{ => functional}/flakes/follow-paths.sh | 0 tests/{ => functional}/flakes/init.sh | 0 tests/{ => functional}/flakes/inputs.sh | 0 tests/{ => functional}/flakes/mercurial.sh | 0 tests/{ => functional}/flakes/run.sh | 0 tests/{ => functional}/flakes/search-root.sh | 0 tests/{ => functional}/flakes/show.sh | 0 .../flakes/unlocked-override.sh | 0 tests/{ => functional}/fmt.sh | 0 tests/{ => functional}/fmt.simple.sh | 0 tests/{ => functional}/function-trace.sh | 0 tests/{ => functional}/gc-auto.sh | 0 .../{ => functional}/gc-concurrent.builder.sh | 0 tests/{ => functional}/gc-concurrent.nix | 0 tests/{ => functional}/gc-concurrent.sh | 0 .../gc-concurrent2.builder.sh | 0 tests/{ => functional}/gc-non-blocking.sh | 0 tests/{ => functional}/gc-runtime.nix | 0 tests/{ => functional}/gc-runtime.sh | 0 tests/{ => functional}/gc.sh | 0 tests/{ => functional}/hash-check.nix | 0 tests/{ => functional}/hash.sh | 0 tests/{ => functional}/hermetic.nix | 0 tests/{ => functional}/import-derivation.nix | 0 tests/{ => functional}/import-derivation.sh | 0 tests/{ => functional}/impure-derivations.nix | 0 tests/{ => functional}/impure-derivations.sh | 0 tests/{ => functional}/init.sh | 0 tests/{ => functional}/install-darwin.sh | 0 tests/{ => functional}/lang-test-infra.sh | 0 tests/{ => functional}/lang.sh | 2 +- tests/{ => functional}/lang/binary-data | Bin tests/{ => functional}/lang/data | 0 tests/{ => functional}/lang/dir1/a.nix | 0 tests/{ => functional}/lang/dir2/a.nix | 0 tests/{ => functional}/lang/dir2/b.nix | 0 tests/{ => functional}/lang/dir3/a.nix | 0 tests/{ => functional}/lang/dir3/b.nix | 0 tests/{ => functional}/lang/dir3/c.nix | 0 tests/{ => functional}/lang/dir4/a.nix | 0 tests/{ => functional}/lang/dir4/c.nix | 0 tests/{ => functional}/lang/empty.exp | 0 .../lang/eval-fail-abort.err.exp | 0 .../{ => functional}/lang/eval-fail-abort.nix | 0 .../lang/eval-fail-antiquoted-path.err.exp | 0 .../lang/eval-fail-assert.err.exp | 0 .../lang/eval-fail-assert.nix | 0 .../lang/eval-fail-bad-antiquote-1.err.exp | 0 .../lang/eval-fail-bad-antiquote-2.err.exp | 0 .../lang/eval-fail-bad-antiquote-3.err.exp | 0 ...al-fail-bad-string-interpolation-1.err.exp | 0 .../eval-fail-bad-string-interpolation-1.nix | 0 ...al-fail-bad-string-interpolation-2.err.exp | 0 .../eval-fail-bad-string-interpolation-2.nix | 0 ...al-fail-bad-string-interpolation-3.err.exp | 0 .../eval-fail-bad-string-interpolation-3.nix | 0 .../lang/eval-fail-blackhole.err.exp | 0 .../lang/eval-fail-blackhole.nix | 0 .../lang/eval-fail-deepseq.err.exp | 0 .../lang/eval-fail-deepseq.nix | 0 .../lang/eval-fail-dup-dynamic-attrs.err.exp | 0 .../lang/eval-fail-dup-dynamic-attrs.nix | 0 ...-foldlStrict-strict-op-application.err.exp | 0 ...fail-foldlStrict-strict-op-application.nix | 0 .../eval-fail-fromTOML-timestamps.err.exp | 0 .../lang/eval-fail-fromTOML-timestamps.nix | 0 .../lang/eval-fail-hashfile-missing.err.exp | 0 .../lang/eval-fail-hashfile-missing.nix | 0 .../lang/eval-fail-list.err.exp | 0 .../{ => functional}/lang/eval-fail-list.nix | 0 .../lang/eval-fail-missing-arg.err.exp | 0 .../lang/eval-fail-missing-arg.nix | 0 .../lang/eval-fail-nonexist-path.err.exp | 0 .../lang/eval-fail-nonexist-path.nix | 0 .../lang/eval-fail-path-slash.err.exp | 0 .../lang/eval-fail-path-slash.nix | 0 .../lang/eval-fail-recursion.err.exp | 0 .../lang/eval-fail-recursion.nix | 0 .../lang/eval-fail-remove.err.exp | 0 .../lang/eval-fail-remove.nix | 0 .../lang/eval-fail-scope-5.err.exp | 0 .../lang/eval-fail-scope-5.nix | 0 .../lang/eval-fail-seq.err.exp | 0 tests/{ => functional}/lang/eval-fail-seq.nix | 0 .../lang/eval-fail-set-override.err.exp | 0 .../lang/eval-fail-set-override.nix | 0 .../lang/eval-fail-set.err.exp | 0 tests/{ => functional}/lang/eval-fail-set.nix | 0 .../lang/eval-fail-substring.err.exp | 0 .../lang/eval-fail-substring.nix | 0 .../lang/eval-fail-to-path.err.exp | 0 .../lang/eval-fail-to-path.nix | 0 .../lang/eval-fail-toJSON.err.exp | 0 .../lang/eval-fail-toJSON.nix | 0 .../lang/eval-fail-undeclared-arg.err.exp | 0 .../lang/eval-fail-undeclared-arg.nix | 0 .../lang/eval-okay-any-all.exp | 0 .../lang/eval-okay-any-all.nix | 0 .../lang/eval-okay-arithmetic.exp | 0 .../lang/eval-okay-arithmetic.nix | 0 .../lang/eval-okay-attrnames.exp | 0 .../lang/eval-okay-attrnames.nix | 0 .../{ => functional}/lang/eval-okay-attrs.exp | 0 .../{ => functional}/lang/eval-okay-attrs.nix | 0 .../lang/eval-okay-attrs2.exp | 0 .../lang/eval-okay-attrs2.nix | 0 .../lang/eval-okay-attrs3.exp | 0 .../lang/eval-okay-attrs3.nix | 0 .../lang/eval-okay-attrs4.exp | 0 .../lang/eval-okay-attrs4.nix | 0 .../lang/eval-okay-attrs5.exp | 0 .../lang/eval-okay-attrs5.nix | 0 .../lang/eval-okay-attrs6.exp | 0 .../lang/eval-okay-attrs6.nix | 0 .../lang/eval-okay-autoargs.exp | 0 .../lang/eval-okay-autoargs.flags | 0 .../lang/eval-okay-autoargs.nix | 0 .../lang/eval-okay-backslash-newline-1.exp | 0 .../lang/eval-okay-backslash-newline-1.nix | 0 .../lang/eval-okay-backslash-newline-2.exp | 0 .../lang/eval-okay-backslash-newline-2.nix | 0 .../lang/eval-okay-builtins-add.exp | 0 .../lang/eval-okay-builtins-add.nix | 0 .../lang/eval-okay-builtins.exp | 0 .../lang/eval-okay-builtins.nix | 0 .../lang/eval-okay-callable-attrs.exp | 0 .../lang/eval-okay-callable-attrs.nix | 0 .../lang/eval-okay-catattrs.exp | 0 .../lang/eval-okay-catattrs.nix | 0 .../lang/eval-okay-closure.exp | 0 .../lang/eval-okay-closure.exp.xml | 0 .../lang/eval-okay-closure.nix | 0 .../lang/eval-okay-comments.exp | 0 .../lang/eval-okay-comments.nix | 0 .../lang/eval-okay-concat.exp | 0 .../lang/eval-okay-concat.nix | 0 .../lang/eval-okay-concatmap.exp | 0 .../lang/eval-okay-concatmap.nix | 0 .../lang/eval-okay-concatstringssep.exp | 0 .../lang/eval-okay-concatstringssep.nix | 0 .../lang/eval-okay-context-introspection.exp | 0 .../lang/eval-okay-context-introspection.nix | 0 .../lang/eval-okay-context.exp | 0 .../lang/eval-okay-context.nix | 0 .../lang/eval-okay-curpos.exp | 0 .../lang/eval-okay-curpos.nix | 0 .../lang/eval-okay-deepseq.exp | 0 .../lang/eval-okay-deepseq.nix | 0 .../lang/eval-okay-delayed-with-inherit.exp | 0 .../lang/eval-okay-delayed-with-inherit.nix | 0 .../lang/eval-okay-delayed-with.exp | 0 .../lang/eval-okay-delayed-with.nix | 0 .../lang/eval-okay-dynamic-attrs-2.exp | 0 .../lang/eval-okay-dynamic-attrs-2.nix | 0 .../lang/eval-okay-dynamic-attrs-bare.exp | 0 .../lang/eval-okay-dynamic-attrs-bare.nix | 0 .../lang/eval-okay-dynamic-attrs.exp | 0 .../lang/eval-okay-dynamic-attrs.nix | 0 .../{ => functional}/lang/eval-okay-elem.exp | 0 .../{ => functional}/lang/eval-okay-elem.nix | 0 .../lang/eval-okay-empty-args.exp | 0 .../lang/eval-okay-empty-args.nix | 0 .../lang/eval-okay-eq-derivations.exp | 0 .../lang/eval-okay-eq-derivations.nix | 0 tests/{ => functional}/lang/eval-okay-eq.exp | 0 tests/{ => functional}/lang/eval-okay-eq.nix | 0 .../lang/eval-okay-filter.exp | 0 .../lang/eval-okay-filter.nix | 0 .../lang/eval-okay-flake-ref-to-string.exp | 0 .../lang/eval-okay-flake-ref-to-string.nix | 0 .../lang/eval-okay-flatten.exp | 0 .../lang/eval-okay-flatten.nix | 0 .../{ => functional}/lang/eval-okay-float.exp | 0 .../{ => functional}/lang/eval-okay-float.nix | 0 .../lang/eval-okay-floor-ceil.exp | 0 .../lang/eval-okay-floor-ceil.nix | 0 .../eval-okay-foldlStrict-lazy-elements.exp | 0 .../eval-okay-foldlStrict-lazy-elements.nix | 0 ...y-foldlStrict-lazy-initial-accumulator.exp | 0 ...y-foldlStrict-lazy-initial-accumulator.nix | 0 .../lang/eval-okay-foldlStrict.exp | 0 .../lang/eval-okay-foldlStrict.nix | 0 .../lang/eval-okay-fromTOML-timestamps.exp | 0 .../lang/eval-okay-fromTOML-timestamps.flags | 0 .../lang/eval-okay-fromTOML-timestamps.nix | 0 .../lang/eval-okay-fromTOML.exp | 0 .../lang/eval-okay-fromTOML.nix | 0 .../lang/eval-okay-fromjson-escapes.exp | 0 .../lang/eval-okay-fromjson-escapes.nix | 0 .../lang/eval-okay-fromjson.exp | 0 .../lang/eval-okay-fromjson.nix | 0 .../lang/eval-okay-functionargs.exp | 0 .../lang/eval-okay-functionargs.exp.xml | 0 .../lang/eval-okay-functionargs.nix | 0 .../eval-okay-getattrpos-functionargs.exp | 0 .../eval-okay-getattrpos-functionargs.nix | 0 .../lang/eval-okay-getattrpos-undefined.exp | 0 .../lang/eval-okay-getattrpos-undefined.nix | 0 .../lang/eval-okay-getattrpos.exp | 0 .../lang/eval-okay-getattrpos.nix | 0 .../lang/eval-okay-getenv.exp | 0 .../lang/eval-okay-getenv.nix | 0 .../lang/eval-okay-groupBy.exp | 0 .../lang/eval-okay-groupBy.nix | 0 .../{ => functional}/lang/eval-okay-hash.exp | 0 .../lang/eval-okay-hashfile.exp | 0 .../lang/eval-okay-hashfile.nix | 0 .../lang/eval-okay-hashstring.exp | 0 .../lang/eval-okay-hashstring.nix | 0 tests/{ => functional}/lang/eval-okay-if.exp | 0 tests/{ => functional}/lang/eval-okay-if.nix | 0 .../lang/eval-okay-import.exp | 0 .../lang/eval-okay-import.nix | 0 .../lang/eval-okay-ind-string.exp | 0 .../lang/eval-okay-ind-string.nix | 0 .../lang/eval-okay-intersectAttrs.exp | 0 .../lang/eval-okay-intersectAttrs.nix | 0 tests/{ => functional}/lang/eval-okay-let.exp | 0 tests/{ => functional}/lang/eval-okay-let.nix | 0 .../{ => functional}/lang/eval-okay-list.exp | 0 .../{ => functional}/lang/eval-okay-list.nix | 0 .../lang/eval-okay-listtoattrs.exp | 0 .../lang/eval-okay-listtoattrs.nix | 0 .../{ => functional}/lang/eval-okay-logic.exp | 0 .../{ => functional}/lang/eval-okay-logic.nix | 0 tests/{ => functional}/lang/eval-okay-map.exp | 0 tests/{ => functional}/lang/eval-okay-map.nix | 0 .../lang/eval-okay-mapattrs.exp | 0 .../lang/eval-okay-mapattrs.nix | 0 .../lang/eval-okay-merge-dynamic-attrs.exp | 0 .../lang/eval-okay-merge-dynamic-attrs.nix | 0 .../lang/eval-okay-nested-with.exp | 0 .../lang/eval-okay-nested-with.nix | 0 .../lang/eval-okay-new-let.exp | 0 .../lang/eval-okay-new-let.nix | 0 .../lang/eval-okay-null-dynamic-attrs.exp | 0 .../lang/eval-okay-null-dynamic-attrs.nix | 0 .../lang/eval-okay-overrides.exp | 0 .../lang/eval-okay-overrides.nix | 0 .../lang/eval-okay-parse-flake-ref.exp | 0 .../lang/eval-okay-parse-flake-ref.nix | 0 .../lang/eval-okay-partition.exp | 0 .../lang/eval-okay-partition.nix | 0 .../eval-okay-path-string-interpolation.exp | 0 .../eval-okay-path-string-interpolation.nix | 0 .../{ => functional}/lang/eval-okay-path.exp | 0 .../{ => functional}/lang/eval-okay-path.nix | 0 .../lang/eval-okay-pathexists.exp | 0 .../lang/eval-okay-pathexists.nix | 0 .../lang/eval-okay-patterns.exp | 0 .../lang/eval-okay-patterns.nix | 0 .../lang/eval-okay-print.err.exp | 0 .../{ => functional}/lang/eval-okay-print.exp | 0 .../{ => functional}/lang/eval-okay-print.nix | 0 .../lang/eval-okay-readDir.exp | 0 .../lang/eval-okay-readDir.nix | 0 .../lang/eval-okay-readFileType.exp | 0 .../lang/eval-okay-readFileType.nix | 0 .../lang/eval-okay-readfile.exp | 0 .../lang/eval-okay-readfile.nix | 0 .../lang/eval-okay-redefine-builtin.exp | 0 .../lang/eval-okay-redefine-builtin.nix | 0 .../lang/eval-okay-regex-match.exp | 0 .../lang/eval-okay-regex-match.nix | 0 .../lang/eval-okay-regex-split.exp | 0 .../lang/eval-okay-regex-split.nix | 0 .../lang/eval-okay-regression-20220122.exp | 0 .../lang/eval-okay-regression-20220122.nix | 0 .../lang/eval-okay-regression-20220125.exp | 0 .../lang/eval-okay-regression-20220125.nix | 0 .../lang/eval-okay-remove.exp | 0 .../lang/eval-okay-remove.nix | 0 .../lang/eval-okay-replacestrings.exp | 0 .../lang/eval-okay-replacestrings.nix | 0 .../lang/eval-okay-scope-1.exp | 0 .../lang/eval-okay-scope-1.nix | 0 .../lang/eval-okay-scope-2.exp | 0 .../lang/eval-okay-scope-2.nix | 0 .../lang/eval-okay-scope-3.exp | 0 .../lang/eval-okay-scope-3.nix | 0 .../lang/eval-okay-scope-4.exp | 0 .../lang/eval-okay-scope-4.nix | 0 .../lang/eval-okay-scope-6.exp | 0 .../lang/eval-okay-scope-6.nix | 0 .../lang/eval-okay-scope-7.exp | 0 .../lang/eval-okay-scope-7.nix | 0 .../lang/eval-okay-search-path.exp | 0 .../lang/eval-okay-search-path.flags | 0 .../lang/eval-okay-search-path.nix | 0 tests/{ => functional}/lang/eval-okay-seq.exp | 0 tests/{ => functional}/lang/eval-okay-seq.nix | 0 .../{ => functional}/lang/eval-okay-sort.exp | 0 .../{ => functional}/lang/eval-okay-sort.nix | 0 .../lang/eval-okay-splitversion.exp | 0 .../lang/eval-okay-splitversion.nix | 0 .../lang/eval-okay-string.exp | 0 .../lang/eval-okay-string.nix | 0 .../lang/eval-okay-strings-as-attrs-names.exp | 0 .../lang/eval-okay-strings-as-attrs-names.nix | 0 .../lang/eval-okay-substring.exp | 0 .../lang/eval-okay-substring.nix | 0 .../lang/eval-okay-tail-call-1.exp-disabled | 0 .../lang/eval-okay-tail-call-1.nix | 0 .../lang/eval-okay-tojson.exp | 0 .../lang/eval-okay-tojson.nix | 0 .../{ => functional}/lang/eval-okay-toxml.exp | 0 .../{ => functional}/lang/eval-okay-toxml.nix | 0 .../lang/eval-okay-toxml2.exp | 0 .../lang/eval-okay-toxml2.nix | 0 .../lang/eval-okay-tryeval.exp | 0 .../lang/eval-okay-tryeval.nix | 0 .../{ => functional}/lang/eval-okay-types.exp | 0 .../{ => functional}/lang/eval-okay-types.nix | 0 .../lang/eval-okay-versions.exp | 0 .../lang/eval-okay-versions.nix | 0 .../{ => functional}/lang/eval-okay-with.exp | 0 .../{ => functional}/lang/eval-okay-with.nix | 0 .../lang/eval-okay-xml.exp.xml | 0 tests/{ => functional}/lang/eval-okay-xml.nix | 0 .../lang/eval-okay-zipAttrsWith.exp | 0 .../lang/eval-okay-zipAttrsWith.nix | 0 tests/{ => functional}/lang/framework.sh | 0 tests/{ => functional}/lang/imported.nix | 0 tests/{ => functional}/lang/imported2.nix | 0 tests/{ => functional}/lang/lib.nix | 0 .../lang/parse-fail-dup-attrs-1.err.exp | 0 .../lang/parse-fail-dup-attrs-1.nix | 0 .../lang/parse-fail-dup-attrs-2.err.exp | 0 .../lang/parse-fail-dup-attrs-2.nix | 0 .../lang/parse-fail-dup-attrs-3.err.exp | 0 .../lang/parse-fail-dup-attrs-3.nix | 0 .../lang/parse-fail-dup-attrs-4.err.exp | 0 .../lang/parse-fail-dup-attrs-4.nix | 0 .../lang/parse-fail-dup-attrs-6.err.exp | 0 .../lang/parse-fail-dup-attrs-7.err.exp | 0 .../lang/parse-fail-dup-attrs-7.nix | 0 .../lang/parse-fail-dup-formals.err.exp | 0 .../lang/parse-fail-dup-formals.nix | 0 .../lang/parse-fail-eof-in-string.err.exp | 0 .../lang/parse-fail-eof-in-string.nix | 0 .../parse-fail-mixed-nested-attrs1.err.exp | 0 .../lang/parse-fail-mixed-nested-attrs1.nix | 0 .../parse-fail-mixed-nested-attrs2.err.exp | 0 .../lang/parse-fail-mixed-nested-attrs2.nix | 0 .../lang/parse-fail-patterns-1.err.exp | 0 .../lang/parse-fail-patterns-1.nix | 0 .../parse-fail-regression-20060610.err.exp | 0 .../lang/parse-fail-regression-20060610.nix | 0 .../lang/parse-fail-undef-var-2.err.exp | 0 .../lang/parse-fail-undef-var-2.nix | 0 .../lang/parse-fail-undef-var.err.exp | 0 .../lang/parse-fail-undef-var.nix | 0 .../lang/parse-fail-utf8.err.exp | 0 .../{ => functional}/lang/parse-fail-utf8.nix | 0 tests/{ => functional}/lang/parse-okay-1.exp | 0 tests/{ => functional}/lang/parse-okay-1.nix | 0 .../{ => functional}/lang/parse-okay-crlf.exp | 0 .../{ => functional}/lang/parse-okay-crlf.nix | 0 .../lang/parse-okay-dup-attrs-5.exp | 0 .../lang/parse-okay-dup-attrs-5.nix | 0 .../lang/parse-okay-dup-attrs-6.exp | 0 .../lang/parse-okay-dup-attrs-6.nix | 0 .../lang/parse-okay-mixed-nested-attrs-1.exp | 0 .../lang/parse-okay-mixed-nested-attrs-1.nix | 0 .../lang/parse-okay-mixed-nested-attrs-2.exp | 0 .../lang/parse-okay-mixed-nested-attrs-2.nix | 0 .../lang/parse-okay-mixed-nested-attrs-3.exp | 0 .../lang/parse-okay-mixed-nested-attrs-3.nix | 0 .../lang/parse-okay-regression-20041027.exp | 0 .../lang/parse-okay-regression-20041027.nix | 0 .../lang/parse-okay-regression-751.exp | 0 .../lang/parse-okay-regression-751.nix | 0 .../lang/parse-okay-subversion.exp | 0 .../lang/parse-okay-subversion.nix | 0 .../{ => functional}/lang/parse-okay-url.exp | 0 .../{ => functional}/lang/parse-okay-url.nix | 0 tests/{ => functional}/lang/readDir/bar | 0 .../lang/readDir/foo/git-hates-directories | 0 tests/{ => functional}/lang/readDir/ldir | 0 tests/{ => functional}/lang/readDir/linked | 0 tests/{ => functional}/legacy-ssh-store.sh | 0 .../linux-sandbox-cert-test.nix | 0 tests/{ => functional}/linux-sandbox.sh | 0 tests/{ => functional}/local-store.sh | 0 tests/{ => functional}/local.mk | 13 +++-- tests/{ => functional}/logging.sh | 0 tests/{ => functional}/misc.sh | 0 tests/{ => functional}/multiple-outputs.nix | 0 tests/{ => functional}/multiple-outputs.sh | 0 tests/{ => functional}/nar-access.nix | 0 tests/{ => functional}/nar-access.sh | 0 tests/{ => functional}/nested-sandboxing.sh | 2 +- .../nested-sandboxing/command.sh | 0 .../nested-sandboxing/runner.nix | 0 tests/{ => functional}/nix-build-examples.nix | 0 tests/{ => functional}/nix-build.sh | 0 tests/{ => functional}/nix-channel.sh | 0 .../{ => functional}/nix-collect-garbage-d.sh | 0 tests/{ => functional}/nix-copy-ssh-ng.sh | 0 tests/{ => functional}/nix-copy-ssh.sh | 0 .../{ => functional}/nix-daemon-untrusting.sh | 0 tests/{ => functional}/nix-profile.sh | 0 tests/{ => functional}/nix-shell.sh | 0 tests/{ => functional}/nix_path.sh | 0 tests/{ => functional}/optimise-store.sh | 0 .../{ => functional}/output-normalization.sh | 0 tests/{ => functional}/parallel.builder.sh | 0 tests/{ => functional}/parallel.nix | 0 tests/{ => functional}/parallel.sh | 0 tests/{ => functional}/pass-as-file.sh | 0 tests/{ => functional}/path-from-hash-part.sh | 0 tests/{ => functional}/path.nix | 0 tests/{ => functional}/placeholders.sh | 0 tests/{ => functional}/plugins.sh | 0 tests/{ => functional}/plugins/local.mk | 0 tests/{ => functional}/plugins/plugintest.cc | 0 tests/{ => functional}/post-hook.sh | 0 tests/{ => functional}/pure-eval.nix | 0 tests/{ => functional}/pure-eval.sh | 0 tests/{ => functional}/push-to-store-old.sh | 0 tests/{ => functional}/push-to-store.sh | 0 tests/{ => functional}/read-only-store.sh | 0 tests/{ => functional}/readfile-context.nix | 0 tests/{ => functional}/readfile-context.sh | 0 tests/{ => functional}/recursive.nix | 0 tests/{ => functional}/recursive.sh | 0 tests/{ => functional}/referrers.sh | 0 tests/{ => functional}/remote-store.sh | 0 tests/{ => functional}/repair.sh | 0 tests/{ => functional}/repl.sh | 0 tests/{ => functional}/restricted.nix | 0 tests/{ => functional}/restricted.sh | 6 +- tests/{ => functional}/search.nix | 0 tests/{ => functional}/search.sh | 0 tests/{ => functional}/secure-drv-outputs.nix | 0 tests/{ => functional}/secure-drv-outputs.sh | 0 tests/{ => functional}/selfref-gc.sh | 0 tests/{ => functional}/shell-hello.nix | 0 tests/{ => functional}/shell.nix | 0 tests/{ => functional}/shell.sh | 0 tests/{ => functional}/shell.shebang.rb | 0 tests/{ => functional}/shell.shebang.sh | 0 tests/{ => functional}/signing.sh | 0 tests/{ => functional}/simple-failing.nix | 0 tests/{ => functional}/simple.builder.sh | 0 tests/{ => functional}/simple.nix | 0 tests/{ => functional}/simple.sh | 0 tests/{ => functional}/ssh-relay.sh | 0 tests/{ => functional}/store-ping.sh | 0 .../structured-attrs-shell.nix | 0 tests/{ => functional}/structured-attrs.nix | 0 tests/{ => functional}/structured-attrs.sh | 0 .../substitute-with-invalid-ca.sh | 0 tests/{ => functional}/suggestions.sh | 0 .../{ => functional}/supplementary-groups.sh | 0 tests/{ => functional}/tarball.sh | 0 tests/{ => functional}/test-infra.sh | 0 .../{ => functional}/test-libstoreconsumer.sh | 0 .../test-libstoreconsumer/README.md | 0 .../test-libstoreconsumer/local.mk | 0 .../test-libstoreconsumer/main.cc | 0 tests/{ => functional}/timeout.nix | 0 tests/{ => functional}/timeout.sh | 0 tests/{ => functional}/toString-path.sh | 0 tests/{ => functional}/undefined-variable.nix | 0 tests/{ => functional}/user-envs-migration.sh | 0 tests/{ => functional}/user-envs.builder.sh | 0 tests/{ => functional}/user-envs.nix | 0 tests/{ => functional}/user-envs.sh | 0 tests/{ => functional}/why-depends.sh | 0 tests/{ => functional}/zstd.sh | 0 599 files changed, 84 insertions(+), 87 deletions(-) rename tests/{ => functional}/add.sh (100%) rename tests/{ => functional}/bad.tar.xz (100%) rename tests/{ => functional}/bash-profile.sh (78%) rename tests/{ => functional}/big-derivation-attr.nix (100%) rename tests/{ => functional}/binary-cache-build-remote.sh (100%) rename tests/{ => functional}/binary-cache.sh (100%) rename tests/{ => functional}/brotli.sh (100%) rename tests/{ => functional}/build-delete.sh (100%) rename tests/{ => functional}/build-dry.sh (100%) rename tests/{ => functional}/build-hook-ca-fixed.nix (100%) rename tests/{ => functional}/build-hook-ca-floating.nix (100%) rename tests/{ => functional}/build-hook.nix (100%) rename tests/{ => functional}/build-remote-content-addressed-fixed.sh (100%) rename tests/{ => functional}/build-remote-content-addressed-floating.sh (100%) rename tests/{ => functional}/build-remote-input-addressed.sh (100%) rename tests/{ => functional}/build-remote-trustless-after.sh (100%) rename tests/{ => functional}/build-remote-trustless-should-fail-0.sh (100%) rename tests/{ => functional}/build-remote-trustless-should-pass-0.sh (100%) rename tests/{ => functional}/build-remote-trustless-should-pass-1.sh (100%) rename tests/{ => functional}/build-remote-trustless-should-pass-2.sh (100%) rename tests/{ => functional}/build-remote-trustless-should-pass-3.sh (100%) rename tests/{ => functional}/build-remote-trustless.sh (82%) rename tests/{ => functional}/build-remote.sh (100%) rename tests/{ => functional}/build.sh (100%) rename tests/{ => functional}/ca-shell.nix (100%) rename tests/{ => functional}/ca/build-cache.sh (100%) rename tests/{ => functional}/ca/build-dry.sh (100%) rename tests/{ => functional}/ca/build-with-garbage-path.sh (100%) rename tests/{ => functional}/ca/build.sh (100%) rename tests/{ => functional}/ca/common.sh (100%) rename tests/{ => functional}/ca/concurrent-builds.sh (100%) rename tests/{ => functional}/ca/config.nix.in (100%) rename tests/{ => functional}/ca/content-addressed.nix (100%) rename tests/{ => functional}/ca/derivation-json.sh (100%) rename tests/{ => functional}/ca/duplicate-realisation-in-closure.sh (100%) rename tests/{ => functional}/ca/flake.nix (100%) rename tests/{ => functional}/ca/gc.sh (100%) rename tests/{ => functional}/ca/import-derivation.sh (100%) rename tests/{ => functional}/ca/local.mk (94%) rename tests/{ => functional}/ca/new-build-cmd.sh (100%) rename tests/{ => functional}/ca/nix-copy.sh (100%) rename tests/{ => functional}/ca/nix-run.sh (100%) rename tests/{ => functional}/ca/nix-shell.sh (100%) rename tests/{ => functional}/ca/nondeterministic.nix (100%) rename tests/{ => functional}/ca/post-hook.sh (100%) rename tests/{ => functional}/ca/racy.nix (100%) rename tests/{ => functional}/ca/recursive.sh (100%) rename tests/{ => functional}/ca/repl.sh (100%) rename tests/{ => functional}/ca/selfref-gc.sh (100%) rename tests/{ => functional}/ca/signatures.sh (100%) rename tests/{ => functional}/ca/substitute.sh (100%) rename tests/{ => functional}/ca/why-depends.sh (100%) rename tests/{ => functional}/case-hack.sh (100%) rename tests/{ => functional}/case.nar (100%) rename tests/{ => functional}/check-refs.nix (100%) rename tests/{ => functional}/check-refs.sh (100%) rename tests/{ => functional}/check-reqs.nix (100%) rename tests/{ => functional}/check-reqs.sh (100%) rename tests/{ => functional}/check.nix (100%) rename tests/{ => functional}/check.sh (100%) rename tests/{ => functional}/common.sh (100%) rename tests/{ => functional}/common/vars-and-functions.sh.in (99%) rename tests/{ => functional}/completions.sh (100%) rename tests/{ => functional}/compression-levels.sh (100%) rename tests/{ => functional}/compute-levels.sh (100%) rename tests/{ => functional}/config.nix.in (100%) rename tests/{ => functional}/config.sh (100%) rename tests/{ => functional}/config/nix-with-substituters.conf (100%) rename tests/{ => functional}/db-migration.sh (100%) rename tests/{ => functional}/dependencies.builder0.sh (100%) rename tests/{ => functional}/dependencies.nix (100%) rename tests/{ => functional}/dependencies.sh (100%) rename tests/{ => functional}/derivation-json.sh (100%) rename tests/{ => functional}/dummy (100%) rename tests/{ => functional}/dump-db.sh (100%) rename tests/{ => functional}/dyn-drv/build-built-drv.sh (100%) rename tests/{ => functional}/dyn-drv/common.sh (100%) rename tests/{ => functional}/dyn-drv/config.nix.in (100%) rename tests/{ => functional}/dyn-drv/dep-built-drv.sh (100%) rename tests/{ => functional}/dyn-drv/eval-outputOf.sh (100%) rename tests/{ => functional}/dyn-drv/local.mk (87%) rename tests/{ => functional}/dyn-drv/old-daemon-error-hack.nix (100%) rename tests/{ => functional}/dyn-drv/old-daemon-error-hack.sh (100%) rename tests/{ => functional}/dyn-drv/recursive-mod-json.nix (100%) rename tests/{ => functional}/dyn-drv/recursive-mod-json.sh (100%) rename tests/{ => functional}/dyn-drv/text-hashed-output.nix (100%) rename tests/{ => functional}/dyn-drv/text-hashed-output.sh (100%) rename tests/{ => functional}/eval-store.sh (100%) rename tests/{ => functional}/eval.nix (100%) rename tests/{ => functional}/eval.sh (100%) rename tests/{ => functional}/experimental-features.sh (100%) rename tests/{ => functional}/export-graph.nix (100%) rename tests/{ => functional}/export-graph.sh (100%) rename tests/{ => functional}/export.sh (100%) rename tests/{ => functional}/failing.nix (100%) rename tests/{ => functional}/fetchClosure.sh (100%) rename tests/{ => functional}/fetchGit.sh (100%) rename tests/{ => functional}/fetchGitRefs.sh (100%) rename tests/{ => functional}/fetchGitSubmodules.sh (100%) rename tests/{ => functional}/fetchMercurial.sh (100%) rename tests/{ => functional}/fetchPath.sh (100%) rename tests/{ => functional}/fetchTree-file.sh (100%) rename tests/{ => functional}/fetchurl.sh (100%) rename tests/{ => functional}/filter-source.nix (100%) rename tests/{ => functional}/filter-source.sh (100%) rename tests/{ => functional}/fixed.builder1.sh (100%) rename tests/{ => functional}/fixed.builder2.sh (100%) rename tests/{ => functional}/fixed.nix (100%) rename tests/{ => functional}/fixed.sh (100%) rename tests/{ => functional}/flakes/absolute-attr-paths.sh (100%) rename tests/{ => functional}/flakes/absolute-paths.sh (100%) rename tests/{ => functional}/flakes/build-paths.sh (100%) rename tests/{ => functional}/flakes/bundle.sh (100%) rename tests/{ => functional}/flakes/check.sh (100%) rename tests/{ => functional}/flakes/circular.sh (100%) rename tests/{ => functional}/flakes/common.sh (100%) rename tests/{ => functional}/flakes/config.sh (100%) rename tests/{ => functional}/flakes/flake-in-submodule.sh (100%) rename tests/{ => functional}/flakes/flakes.sh (100%) rename tests/{ => functional}/flakes/follow-paths.sh (100%) rename tests/{ => functional}/flakes/init.sh (100%) rename tests/{ => functional}/flakes/inputs.sh (100%) rename tests/{ => functional}/flakes/mercurial.sh (100%) rename tests/{ => functional}/flakes/run.sh (100%) rename tests/{ => functional}/flakes/search-root.sh (100%) rename tests/{ => functional}/flakes/show.sh (100%) rename tests/{ => functional}/flakes/unlocked-override.sh (100%) rename tests/{ => functional}/fmt.sh (100%) rename tests/{ => functional}/fmt.simple.sh (100%) rename tests/{ => functional}/function-trace.sh (100%) rename tests/{ => functional}/gc-auto.sh (100%) rename tests/{ => functional}/gc-concurrent.builder.sh (100%) rename tests/{ => functional}/gc-concurrent.nix (100%) rename tests/{ => functional}/gc-concurrent.sh (100%) rename tests/{ => functional}/gc-concurrent2.builder.sh (100%) rename tests/{ => functional}/gc-non-blocking.sh (100%) rename tests/{ => functional}/gc-runtime.nix (100%) rename tests/{ => functional}/gc-runtime.sh (100%) rename tests/{ => functional}/gc.sh (100%) rename tests/{ => functional}/hash-check.nix (100%) rename tests/{ => functional}/hash.sh (100%) rename tests/{ => functional}/hermetic.nix (100%) rename tests/{ => functional}/import-derivation.nix (100%) rename tests/{ => functional}/import-derivation.sh (100%) rename tests/{ => functional}/impure-derivations.nix (100%) rename tests/{ => functional}/impure-derivations.sh (100%) rename tests/{ => functional}/init.sh (100%) rename tests/{ => functional}/install-darwin.sh (100%) rename tests/{ => functional}/lang-test-infra.sh (100%) rename tests/{ => functional}/lang.sh (98%) rename tests/{ => functional}/lang/binary-data (100%) rename tests/{ => functional}/lang/data (100%) rename tests/{ => functional}/lang/dir1/a.nix (100%) rename tests/{ => functional}/lang/dir2/a.nix (100%) rename tests/{ => functional}/lang/dir2/b.nix (100%) rename tests/{ => functional}/lang/dir3/a.nix (100%) rename tests/{ => functional}/lang/dir3/b.nix (100%) rename tests/{ => functional}/lang/dir3/c.nix (100%) rename tests/{ => functional}/lang/dir4/a.nix (100%) rename tests/{ => functional}/lang/dir4/c.nix (100%) rename tests/{ => functional}/lang/empty.exp (100%) rename tests/{ => functional}/lang/eval-fail-abort.err.exp (100%) rename tests/{ => functional}/lang/eval-fail-abort.nix (100%) rename tests/{ => functional}/lang/eval-fail-antiquoted-path.err.exp (100%) rename tests/{ => functional}/lang/eval-fail-assert.err.exp (100%) rename tests/{ => functional}/lang/eval-fail-assert.nix (100%) rename tests/{ => functional}/lang/eval-fail-bad-antiquote-1.err.exp (100%) rename tests/{ => functional}/lang/eval-fail-bad-antiquote-2.err.exp (100%) rename tests/{ => functional}/lang/eval-fail-bad-antiquote-3.err.exp (100%) rename tests/{ => functional}/lang/eval-fail-bad-string-interpolation-1.err.exp (100%) rename tests/{ => functional}/lang/eval-fail-bad-string-interpolation-1.nix (100%) rename tests/{ => functional}/lang/eval-fail-bad-string-interpolation-2.err.exp (100%) rename tests/{ => functional}/lang/eval-fail-bad-string-interpolation-2.nix (100%) rename tests/{ => functional}/lang/eval-fail-bad-string-interpolation-3.err.exp (100%) rename tests/{ => functional}/lang/eval-fail-bad-string-interpolation-3.nix (100%) rename tests/{ => functional}/lang/eval-fail-blackhole.err.exp (100%) rename tests/{ => functional}/lang/eval-fail-blackhole.nix (100%) rename tests/{ => functional}/lang/eval-fail-deepseq.err.exp (100%) rename tests/{ => functional}/lang/eval-fail-deepseq.nix (100%) rename tests/{ => functional}/lang/eval-fail-dup-dynamic-attrs.err.exp (100%) rename tests/{ => functional}/lang/eval-fail-dup-dynamic-attrs.nix (100%) rename tests/{ => functional}/lang/eval-fail-foldlStrict-strict-op-application.err.exp (100%) rename tests/{ => functional}/lang/eval-fail-foldlStrict-strict-op-application.nix (100%) rename tests/{ => functional}/lang/eval-fail-fromTOML-timestamps.err.exp (100%) rename tests/{ => functional}/lang/eval-fail-fromTOML-timestamps.nix (100%) rename tests/{ => functional}/lang/eval-fail-hashfile-missing.err.exp (100%) rename tests/{ => functional}/lang/eval-fail-hashfile-missing.nix (100%) rename tests/{ => functional}/lang/eval-fail-list.err.exp (100%) rename tests/{ => functional}/lang/eval-fail-list.nix (100%) rename tests/{ => functional}/lang/eval-fail-missing-arg.err.exp (100%) rename tests/{ => functional}/lang/eval-fail-missing-arg.nix (100%) rename tests/{ => functional}/lang/eval-fail-nonexist-path.err.exp (100%) rename tests/{ => functional}/lang/eval-fail-nonexist-path.nix (100%) rename tests/{ => functional}/lang/eval-fail-path-slash.err.exp (100%) rename tests/{ => functional}/lang/eval-fail-path-slash.nix (100%) rename tests/{ => functional}/lang/eval-fail-recursion.err.exp (100%) rename tests/{ => functional}/lang/eval-fail-recursion.nix (100%) rename tests/{ => functional}/lang/eval-fail-remove.err.exp (100%) rename tests/{ => functional}/lang/eval-fail-remove.nix (100%) rename tests/{ => functional}/lang/eval-fail-scope-5.err.exp (100%) rename tests/{ => functional}/lang/eval-fail-scope-5.nix (100%) rename tests/{ => functional}/lang/eval-fail-seq.err.exp (100%) rename tests/{ => functional}/lang/eval-fail-seq.nix (100%) rename tests/{ => functional}/lang/eval-fail-set-override.err.exp (100%) rename tests/{ => functional}/lang/eval-fail-set-override.nix (100%) rename tests/{ => functional}/lang/eval-fail-set.err.exp (100%) rename tests/{ => functional}/lang/eval-fail-set.nix (100%) rename tests/{ => functional}/lang/eval-fail-substring.err.exp (100%) rename tests/{ => functional}/lang/eval-fail-substring.nix (100%) rename tests/{ => functional}/lang/eval-fail-to-path.err.exp (100%) rename tests/{ => functional}/lang/eval-fail-to-path.nix (100%) rename tests/{ => functional}/lang/eval-fail-toJSON.err.exp (100%) rename tests/{ => functional}/lang/eval-fail-toJSON.nix (100%) rename tests/{ => functional}/lang/eval-fail-undeclared-arg.err.exp (100%) rename tests/{ => functional}/lang/eval-fail-undeclared-arg.nix (100%) rename tests/{ => functional}/lang/eval-okay-any-all.exp (100%) rename tests/{ => functional}/lang/eval-okay-any-all.nix (100%) rename tests/{ => functional}/lang/eval-okay-arithmetic.exp (100%) rename tests/{ => functional}/lang/eval-okay-arithmetic.nix (100%) rename tests/{ => functional}/lang/eval-okay-attrnames.exp (100%) rename tests/{ => functional}/lang/eval-okay-attrnames.nix (100%) rename tests/{ => functional}/lang/eval-okay-attrs.exp (100%) rename tests/{ => functional}/lang/eval-okay-attrs.nix (100%) rename tests/{ => functional}/lang/eval-okay-attrs2.exp (100%) rename tests/{ => functional}/lang/eval-okay-attrs2.nix (100%) rename tests/{ => functional}/lang/eval-okay-attrs3.exp (100%) rename tests/{ => functional}/lang/eval-okay-attrs3.nix (100%) rename tests/{ => functional}/lang/eval-okay-attrs4.exp (100%) rename tests/{ => functional}/lang/eval-okay-attrs4.nix (100%) rename tests/{ => functional}/lang/eval-okay-attrs5.exp (100%) rename tests/{ => functional}/lang/eval-okay-attrs5.nix (100%) rename tests/{ => functional}/lang/eval-okay-attrs6.exp (100%) rename tests/{ => functional}/lang/eval-okay-attrs6.nix (100%) rename tests/{ => functional}/lang/eval-okay-autoargs.exp (100%) rename tests/{ => functional}/lang/eval-okay-autoargs.flags (100%) rename tests/{ => functional}/lang/eval-okay-autoargs.nix (100%) rename tests/{ => functional}/lang/eval-okay-backslash-newline-1.exp (100%) rename tests/{ => functional}/lang/eval-okay-backslash-newline-1.nix (100%) rename tests/{ => functional}/lang/eval-okay-backslash-newline-2.exp (100%) rename tests/{ => functional}/lang/eval-okay-backslash-newline-2.nix (100%) rename tests/{ => functional}/lang/eval-okay-builtins-add.exp (100%) rename tests/{ => functional}/lang/eval-okay-builtins-add.nix (100%) rename tests/{ => functional}/lang/eval-okay-builtins.exp (100%) rename tests/{ => functional}/lang/eval-okay-builtins.nix (100%) rename tests/{ => functional}/lang/eval-okay-callable-attrs.exp (100%) rename tests/{ => functional}/lang/eval-okay-callable-attrs.nix (100%) rename tests/{ => functional}/lang/eval-okay-catattrs.exp (100%) rename tests/{ => functional}/lang/eval-okay-catattrs.nix (100%) rename tests/{ => functional}/lang/eval-okay-closure.exp (100%) rename tests/{ => functional}/lang/eval-okay-closure.exp.xml (100%) rename tests/{ => functional}/lang/eval-okay-closure.nix (100%) rename tests/{ => functional}/lang/eval-okay-comments.exp (100%) rename tests/{ => functional}/lang/eval-okay-comments.nix (100%) rename tests/{ => functional}/lang/eval-okay-concat.exp (100%) rename tests/{ => functional}/lang/eval-okay-concat.nix (100%) rename tests/{ => functional}/lang/eval-okay-concatmap.exp (100%) rename tests/{ => functional}/lang/eval-okay-concatmap.nix (100%) rename tests/{ => functional}/lang/eval-okay-concatstringssep.exp (100%) rename tests/{ => functional}/lang/eval-okay-concatstringssep.nix (100%) rename tests/{ => functional}/lang/eval-okay-context-introspection.exp (100%) rename tests/{ => functional}/lang/eval-okay-context-introspection.nix (100%) rename tests/{ => functional}/lang/eval-okay-context.exp (100%) rename tests/{ => functional}/lang/eval-okay-context.nix (100%) rename tests/{ => functional}/lang/eval-okay-curpos.exp (100%) rename tests/{ => functional}/lang/eval-okay-curpos.nix (100%) rename tests/{ => functional}/lang/eval-okay-deepseq.exp (100%) rename tests/{ => functional}/lang/eval-okay-deepseq.nix (100%) rename tests/{ => functional}/lang/eval-okay-delayed-with-inherit.exp (100%) rename tests/{ => functional}/lang/eval-okay-delayed-with-inherit.nix (100%) rename tests/{ => functional}/lang/eval-okay-delayed-with.exp (100%) rename tests/{ => functional}/lang/eval-okay-delayed-with.nix (100%) rename tests/{ => functional}/lang/eval-okay-dynamic-attrs-2.exp (100%) rename tests/{ => functional}/lang/eval-okay-dynamic-attrs-2.nix (100%) rename tests/{ => functional}/lang/eval-okay-dynamic-attrs-bare.exp (100%) rename tests/{ => functional}/lang/eval-okay-dynamic-attrs-bare.nix (100%) rename tests/{ => functional}/lang/eval-okay-dynamic-attrs.exp (100%) rename tests/{ => functional}/lang/eval-okay-dynamic-attrs.nix (100%) rename tests/{ => functional}/lang/eval-okay-elem.exp (100%) rename tests/{ => functional}/lang/eval-okay-elem.nix (100%) rename tests/{ => functional}/lang/eval-okay-empty-args.exp (100%) rename tests/{ => functional}/lang/eval-okay-empty-args.nix (100%) rename tests/{ => functional}/lang/eval-okay-eq-derivations.exp (100%) rename tests/{ => functional}/lang/eval-okay-eq-derivations.nix (100%) rename tests/{ => functional}/lang/eval-okay-eq.exp (100%) rename tests/{ => functional}/lang/eval-okay-eq.nix (100%) rename tests/{ => functional}/lang/eval-okay-filter.exp (100%) rename tests/{ => functional}/lang/eval-okay-filter.nix (100%) rename tests/{ => functional}/lang/eval-okay-flake-ref-to-string.exp (100%) rename tests/{ => functional}/lang/eval-okay-flake-ref-to-string.nix (100%) rename tests/{ => functional}/lang/eval-okay-flatten.exp (100%) rename tests/{ => functional}/lang/eval-okay-flatten.nix (100%) rename tests/{ => functional}/lang/eval-okay-float.exp (100%) rename tests/{ => functional}/lang/eval-okay-float.nix (100%) rename tests/{ => functional}/lang/eval-okay-floor-ceil.exp (100%) rename tests/{ => functional}/lang/eval-okay-floor-ceil.nix (100%) rename tests/{ => functional}/lang/eval-okay-foldlStrict-lazy-elements.exp (100%) rename tests/{ => functional}/lang/eval-okay-foldlStrict-lazy-elements.nix (100%) rename tests/{ => functional}/lang/eval-okay-foldlStrict-lazy-initial-accumulator.exp (100%) rename tests/{ => functional}/lang/eval-okay-foldlStrict-lazy-initial-accumulator.nix (100%) rename tests/{ => functional}/lang/eval-okay-foldlStrict.exp (100%) rename tests/{ => functional}/lang/eval-okay-foldlStrict.nix (100%) rename tests/{ => functional}/lang/eval-okay-fromTOML-timestamps.exp (100%) rename tests/{ => functional}/lang/eval-okay-fromTOML-timestamps.flags (100%) rename tests/{ => functional}/lang/eval-okay-fromTOML-timestamps.nix (100%) rename tests/{ => functional}/lang/eval-okay-fromTOML.exp (100%) rename tests/{ => functional}/lang/eval-okay-fromTOML.nix (100%) rename tests/{ => functional}/lang/eval-okay-fromjson-escapes.exp (100%) rename tests/{ => functional}/lang/eval-okay-fromjson-escapes.nix (100%) rename tests/{ => functional}/lang/eval-okay-fromjson.exp (100%) rename tests/{ => functional}/lang/eval-okay-fromjson.nix (100%) rename tests/{ => functional}/lang/eval-okay-functionargs.exp (100%) rename tests/{ => functional}/lang/eval-okay-functionargs.exp.xml (100%) rename tests/{ => functional}/lang/eval-okay-functionargs.nix (100%) rename tests/{ => functional}/lang/eval-okay-getattrpos-functionargs.exp (100%) rename tests/{ => functional}/lang/eval-okay-getattrpos-functionargs.nix (100%) rename tests/{ => functional}/lang/eval-okay-getattrpos-undefined.exp (100%) rename tests/{ => functional}/lang/eval-okay-getattrpos-undefined.nix (100%) rename tests/{ => functional}/lang/eval-okay-getattrpos.exp (100%) rename tests/{ => functional}/lang/eval-okay-getattrpos.nix (100%) rename tests/{ => functional}/lang/eval-okay-getenv.exp (100%) rename tests/{ => functional}/lang/eval-okay-getenv.nix (100%) rename tests/{ => functional}/lang/eval-okay-groupBy.exp (100%) rename tests/{ => functional}/lang/eval-okay-groupBy.nix (100%) rename tests/{ => functional}/lang/eval-okay-hash.exp (100%) rename tests/{ => functional}/lang/eval-okay-hashfile.exp (100%) rename tests/{ => functional}/lang/eval-okay-hashfile.nix (100%) rename tests/{ => functional}/lang/eval-okay-hashstring.exp (100%) rename tests/{ => functional}/lang/eval-okay-hashstring.nix (100%) rename tests/{ => functional}/lang/eval-okay-if.exp (100%) rename tests/{ => functional}/lang/eval-okay-if.nix (100%) rename tests/{ => functional}/lang/eval-okay-import.exp (100%) rename tests/{ => functional}/lang/eval-okay-import.nix (100%) rename tests/{ => functional}/lang/eval-okay-ind-string.exp (100%) rename tests/{ => functional}/lang/eval-okay-ind-string.nix (100%) rename tests/{ => functional}/lang/eval-okay-intersectAttrs.exp (100%) rename tests/{ => functional}/lang/eval-okay-intersectAttrs.nix (100%) rename tests/{ => functional}/lang/eval-okay-let.exp (100%) rename tests/{ => functional}/lang/eval-okay-let.nix (100%) rename tests/{ => functional}/lang/eval-okay-list.exp (100%) rename tests/{ => functional}/lang/eval-okay-list.nix (100%) rename tests/{ => functional}/lang/eval-okay-listtoattrs.exp (100%) rename tests/{ => functional}/lang/eval-okay-listtoattrs.nix (100%) rename tests/{ => functional}/lang/eval-okay-logic.exp (100%) rename tests/{ => functional}/lang/eval-okay-logic.nix (100%) rename tests/{ => functional}/lang/eval-okay-map.exp (100%) rename tests/{ => functional}/lang/eval-okay-map.nix (100%) rename tests/{ => functional}/lang/eval-okay-mapattrs.exp (100%) rename tests/{ => functional}/lang/eval-okay-mapattrs.nix (100%) rename tests/{ => functional}/lang/eval-okay-merge-dynamic-attrs.exp (100%) rename tests/{ => functional}/lang/eval-okay-merge-dynamic-attrs.nix (100%) rename tests/{ => functional}/lang/eval-okay-nested-with.exp (100%) rename tests/{ => functional}/lang/eval-okay-nested-with.nix (100%) rename tests/{ => functional}/lang/eval-okay-new-let.exp (100%) rename tests/{ => functional}/lang/eval-okay-new-let.nix (100%) rename tests/{ => functional}/lang/eval-okay-null-dynamic-attrs.exp (100%) rename tests/{ => functional}/lang/eval-okay-null-dynamic-attrs.nix (100%) rename tests/{ => functional}/lang/eval-okay-overrides.exp (100%) rename tests/{ => functional}/lang/eval-okay-overrides.nix (100%) rename tests/{ => functional}/lang/eval-okay-parse-flake-ref.exp (100%) rename tests/{ => functional}/lang/eval-okay-parse-flake-ref.nix (100%) rename tests/{ => functional}/lang/eval-okay-partition.exp (100%) rename tests/{ => functional}/lang/eval-okay-partition.nix (100%) rename tests/{ => functional}/lang/eval-okay-path-string-interpolation.exp (100%) rename tests/{ => functional}/lang/eval-okay-path-string-interpolation.nix (100%) rename tests/{ => functional}/lang/eval-okay-path.exp (100%) rename tests/{ => functional}/lang/eval-okay-path.nix (100%) rename tests/{ => functional}/lang/eval-okay-pathexists.exp (100%) rename tests/{ => functional}/lang/eval-okay-pathexists.nix (100%) rename tests/{ => functional}/lang/eval-okay-patterns.exp (100%) rename tests/{ => functional}/lang/eval-okay-patterns.nix (100%) rename tests/{ => functional}/lang/eval-okay-print.err.exp (100%) rename tests/{ => functional}/lang/eval-okay-print.exp (100%) rename tests/{ => functional}/lang/eval-okay-print.nix (100%) rename tests/{ => functional}/lang/eval-okay-readDir.exp (100%) rename tests/{ => functional}/lang/eval-okay-readDir.nix (100%) rename tests/{ => functional}/lang/eval-okay-readFileType.exp (100%) rename tests/{ => functional}/lang/eval-okay-readFileType.nix (100%) rename tests/{ => functional}/lang/eval-okay-readfile.exp (100%) rename tests/{ => functional}/lang/eval-okay-readfile.nix (100%) rename tests/{ => functional}/lang/eval-okay-redefine-builtin.exp (100%) rename tests/{ => functional}/lang/eval-okay-redefine-builtin.nix (100%) rename tests/{ => functional}/lang/eval-okay-regex-match.exp (100%) rename tests/{ => functional}/lang/eval-okay-regex-match.nix (100%) rename tests/{ => functional}/lang/eval-okay-regex-split.exp (100%) rename tests/{ => functional}/lang/eval-okay-regex-split.nix (100%) rename tests/{ => functional}/lang/eval-okay-regression-20220122.exp (100%) rename tests/{ => functional}/lang/eval-okay-regression-20220122.nix (100%) rename tests/{ => functional}/lang/eval-okay-regression-20220125.exp (100%) rename tests/{ => functional}/lang/eval-okay-regression-20220125.nix (100%) rename tests/{ => functional}/lang/eval-okay-remove.exp (100%) rename tests/{ => functional}/lang/eval-okay-remove.nix (100%) rename tests/{ => functional}/lang/eval-okay-replacestrings.exp (100%) rename tests/{ => functional}/lang/eval-okay-replacestrings.nix (100%) rename tests/{ => functional}/lang/eval-okay-scope-1.exp (100%) rename tests/{ => functional}/lang/eval-okay-scope-1.nix (100%) rename tests/{ => functional}/lang/eval-okay-scope-2.exp (100%) rename tests/{ => functional}/lang/eval-okay-scope-2.nix (100%) rename tests/{ => functional}/lang/eval-okay-scope-3.exp (100%) rename tests/{ => functional}/lang/eval-okay-scope-3.nix (100%) rename tests/{ => functional}/lang/eval-okay-scope-4.exp (100%) rename tests/{ => functional}/lang/eval-okay-scope-4.nix (100%) rename tests/{ => functional}/lang/eval-okay-scope-6.exp (100%) rename tests/{ => functional}/lang/eval-okay-scope-6.nix (100%) rename tests/{ => functional}/lang/eval-okay-scope-7.exp (100%) rename tests/{ => functional}/lang/eval-okay-scope-7.nix (100%) rename tests/{ => functional}/lang/eval-okay-search-path.exp (100%) rename tests/{ => functional}/lang/eval-okay-search-path.flags (100%) rename tests/{ => functional}/lang/eval-okay-search-path.nix (100%) rename tests/{ => functional}/lang/eval-okay-seq.exp (100%) rename tests/{ => functional}/lang/eval-okay-seq.nix (100%) rename tests/{ => functional}/lang/eval-okay-sort.exp (100%) rename tests/{ => functional}/lang/eval-okay-sort.nix (100%) rename tests/{ => functional}/lang/eval-okay-splitversion.exp (100%) rename tests/{ => functional}/lang/eval-okay-splitversion.nix (100%) rename tests/{ => functional}/lang/eval-okay-string.exp (100%) rename tests/{ => functional}/lang/eval-okay-string.nix (100%) rename tests/{ => functional}/lang/eval-okay-strings-as-attrs-names.exp (100%) rename tests/{ => functional}/lang/eval-okay-strings-as-attrs-names.nix (100%) rename tests/{ => functional}/lang/eval-okay-substring.exp (100%) rename tests/{ => functional}/lang/eval-okay-substring.nix (100%) rename tests/{ => functional}/lang/eval-okay-tail-call-1.exp-disabled (100%) rename tests/{ => functional}/lang/eval-okay-tail-call-1.nix (100%) rename tests/{ => functional}/lang/eval-okay-tojson.exp (100%) rename tests/{ => functional}/lang/eval-okay-tojson.nix (100%) rename tests/{ => functional}/lang/eval-okay-toxml.exp (100%) rename tests/{ => functional}/lang/eval-okay-toxml.nix (100%) rename tests/{ => functional}/lang/eval-okay-toxml2.exp (100%) rename tests/{ => functional}/lang/eval-okay-toxml2.nix (100%) rename tests/{ => functional}/lang/eval-okay-tryeval.exp (100%) rename tests/{ => functional}/lang/eval-okay-tryeval.nix (100%) rename tests/{ => functional}/lang/eval-okay-types.exp (100%) rename tests/{ => functional}/lang/eval-okay-types.nix (100%) rename tests/{ => functional}/lang/eval-okay-versions.exp (100%) rename tests/{ => functional}/lang/eval-okay-versions.nix (100%) rename tests/{ => functional}/lang/eval-okay-with.exp (100%) rename tests/{ => functional}/lang/eval-okay-with.nix (100%) rename tests/{ => functional}/lang/eval-okay-xml.exp.xml (100%) rename tests/{ => functional}/lang/eval-okay-xml.nix (100%) rename tests/{ => functional}/lang/eval-okay-zipAttrsWith.exp (100%) rename tests/{ => functional}/lang/eval-okay-zipAttrsWith.nix (100%) rename tests/{ => functional}/lang/framework.sh (100%) rename tests/{ => functional}/lang/imported.nix (100%) rename tests/{ => functional}/lang/imported2.nix (100%) rename tests/{ => functional}/lang/lib.nix (100%) rename tests/{ => functional}/lang/parse-fail-dup-attrs-1.err.exp (100%) rename tests/{ => functional}/lang/parse-fail-dup-attrs-1.nix (100%) rename tests/{ => functional}/lang/parse-fail-dup-attrs-2.err.exp (100%) rename tests/{ => functional}/lang/parse-fail-dup-attrs-2.nix (100%) rename tests/{ => functional}/lang/parse-fail-dup-attrs-3.err.exp (100%) rename tests/{ => functional}/lang/parse-fail-dup-attrs-3.nix (100%) rename tests/{ => functional}/lang/parse-fail-dup-attrs-4.err.exp (100%) rename tests/{ => functional}/lang/parse-fail-dup-attrs-4.nix (100%) rename tests/{ => functional}/lang/parse-fail-dup-attrs-6.err.exp (100%) rename tests/{ => functional}/lang/parse-fail-dup-attrs-7.err.exp (100%) rename tests/{ => functional}/lang/parse-fail-dup-attrs-7.nix (100%) rename tests/{ => functional}/lang/parse-fail-dup-formals.err.exp (100%) rename tests/{ => functional}/lang/parse-fail-dup-formals.nix (100%) rename tests/{ => functional}/lang/parse-fail-eof-in-string.err.exp (100%) rename tests/{ => functional}/lang/parse-fail-eof-in-string.nix (100%) rename tests/{ => functional}/lang/parse-fail-mixed-nested-attrs1.err.exp (100%) rename tests/{ => functional}/lang/parse-fail-mixed-nested-attrs1.nix (100%) rename tests/{ => functional}/lang/parse-fail-mixed-nested-attrs2.err.exp (100%) rename tests/{ => functional}/lang/parse-fail-mixed-nested-attrs2.nix (100%) rename tests/{ => functional}/lang/parse-fail-patterns-1.err.exp (100%) rename tests/{ => functional}/lang/parse-fail-patterns-1.nix (100%) rename tests/{ => functional}/lang/parse-fail-regression-20060610.err.exp (100%) rename tests/{ => functional}/lang/parse-fail-regression-20060610.nix (100%) rename tests/{ => functional}/lang/parse-fail-undef-var-2.err.exp (100%) rename tests/{ => functional}/lang/parse-fail-undef-var-2.nix (100%) rename tests/{ => functional}/lang/parse-fail-undef-var.err.exp (100%) rename tests/{ => functional}/lang/parse-fail-undef-var.nix (100%) rename tests/{ => functional}/lang/parse-fail-utf8.err.exp (100%) rename tests/{ => functional}/lang/parse-fail-utf8.nix (100%) rename tests/{ => functional}/lang/parse-okay-1.exp (100%) rename tests/{ => functional}/lang/parse-okay-1.nix (100%) rename tests/{ => functional}/lang/parse-okay-crlf.exp (100%) rename tests/{ => functional}/lang/parse-okay-crlf.nix (100%) rename tests/{ => functional}/lang/parse-okay-dup-attrs-5.exp (100%) rename tests/{ => functional}/lang/parse-okay-dup-attrs-5.nix (100%) rename tests/{ => functional}/lang/parse-okay-dup-attrs-6.exp (100%) rename tests/{ => functional}/lang/parse-okay-dup-attrs-6.nix (100%) rename tests/{ => functional}/lang/parse-okay-mixed-nested-attrs-1.exp (100%) rename tests/{ => functional}/lang/parse-okay-mixed-nested-attrs-1.nix (100%) rename tests/{ => functional}/lang/parse-okay-mixed-nested-attrs-2.exp (100%) rename tests/{ => functional}/lang/parse-okay-mixed-nested-attrs-2.nix (100%) rename tests/{ => functional}/lang/parse-okay-mixed-nested-attrs-3.exp (100%) rename tests/{ => functional}/lang/parse-okay-mixed-nested-attrs-3.nix (100%) rename tests/{ => functional}/lang/parse-okay-regression-20041027.exp (100%) rename tests/{ => functional}/lang/parse-okay-regression-20041027.nix (100%) rename tests/{ => functional}/lang/parse-okay-regression-751.exp (100%) rename tests/{ => functional}/lang/parse-okay-regression-751.nix (100%) rename tests/{ => functional}/lang/parse-okay-subversion.exp (100%) rename tests/{ => functional}/lang/parse-okay-subversion.nix (100%) rename tests/{ => functional}/lang/parse-okay-url.exp (100%) rename tests/{ => functional}/lang/parse-okay-url.nix (100%) rename tests/{ => functional}/lang/readDir/bar (100%) rename tests/{ => functional}/lang/readDir/foo/git-hates-directories (100%) rename tests/{ => functional}/lang/readDir/ldir (100%) rename tests/{ => functional}/lang/readDir/linked (100%) rename tests/{ => functional}/legacy-ssh-store.sh (100%) rename tests/{ => functional}/linux-sandbox-cert-test.nix (100%) rename tests/{ => functional}/linux-sandbox.sh (100%) rename tests/{ => functional}/local-store.sh (100%) rename tests/{ => functional}/local.mk (90%) rename tests/{ => functional}/logging.sh (100%) rename tests/{ => functional}/misc.sh (100%) rename tests/{ => functional}/multiple-outputs.nix (100%) rename tests/{ => functional}/multiple-outputs.sh (100%) rename tests/{ => functional}/nar-access.nix (100%) rename tests/{ => functional}/nar-access.sh (100%) rename tests/{ => functional}/nested-sandboxing.sh (75%) rename tests/{ => functional}/nested-sandboxing/command.sh (100%) rename tests/{ => functional}/nested-sandboxing/runner.nix (100%) rename tests/{ => functional}/nix-build-examples.nix (100%) rename tests/{ => functional}/nix-build.sh (100%) rename tests/{ => functional}/nix-channel.sh (100%) rename tests/{ => functional}/nix-collect-garbage-d.sh (100%) rename tests/{ => functional}/nix-copy-ssh-ng.sh (100%) rename tests/{ => functional}/nix-copy-ssh.sh (100%) rename tests/{ => functional}/nix-daemon-untrusting.sh (100%) rename tests/{ => functional}/nix-profile.sh (100%) rename tests/{ => functional}/nix-shell.sh (100%) rename tests/{ => functional}/nix_path.sh (100%) rename tests/{ => functional}/optimise-store.sh (100%) rename tests/{ => functional}/output-normalization.sh (100%) rename tests/{ => functional}/parallel.builder.sh (100%) rename tests/{ => functional}/parallel.nix (100%) rename tests/{ => functional}/parallel.sh (100%) rename tests/{ => functional}/pass-as-file.sh (100%) rename tests/{ => functional}/path-from-hash-part.sh (100%) rename tests/{ => functional}/path.nix (100%) rename tests/{ => functional}/placeholders.sh (100%) rename tests/{ => functional}/plugins.sh (100%) rename tests/{ => functional}/plugins/local.mk (100%) rename tests/{ => functional}/plugins/plugintest.cc (100%) rename tests/{ => functional}/post-hook.sh (100%) rename tests/{ => functional}/pure-eval.nix (100%) rename tests/{ => functional}/pure-eval.sh (100%) rename tests/{ => functional}/push-to-store-old.sh (100%) rename tests/{ => functional}/push-to-store.sh (100%) rename tests/{ => functional}/read-only-store.sh (100%) rename tests/{ => functional}/readfile-context.nix (100%) rename tests/{ => functional}/readfile-context.sh (100%) rename tests/{ => functional}/recursive.nix (100%) rename tests/{ => functional}/recursive.sh (100%) rename tests/{ => functional}/referrers.sh (100%) rename tests/{ => functional}/remote-store.sh (100%) rename tests/{ => functional}/repair.sh (100%) rename tests/{ => functional}/repl.sh (100%) rename tests/{ => functional}/restricted.nix (100%) rename tests/{ => functional}/restricted.sh (95%) rename tests/{ => functional}/search.nix (100%) rename tests/{ => functional}/search.sh (100%) rename tests/{ => functional}/secure-drv-outputs.nix (100%) rename tests/{ => functional}/secure-drv-outputs.sh (100%) rename tests/{ => functional}/selfref-gc.sh (100%) rename tests/{ => functional}/shell-hello.nix (100%) rename tests/{ => functional}/shell.nix (100%) rename tests/{ => functional}/shell.sh (100%) rename tests/{ => functional}/shell.shebang.rb (100%) rename tests/{ => functional}/shell.shebang.sh (100%) rename tests/{ => functional}/signing.sh (100%) rename tests/{ => functional}/simple-failing.nix (100%) rename tests/{ => functional}/simple.builder.sh (100%) rename tests/{ => functional}/simple.nix (100%) rename tests/{ => functional}/simple.sh (100%) rename tests/{ => functional}/ssh-relay.sh (100%) rename tests/{ => functional}/store-ping.sh (100%) rename tests/{ => functional}/structured-attrs-shell.nix (100%) rename tests/{ => functional}/structured-attrs.nix (100%) rename tests/{ => functional}/structured-attrs.sh (100%) rename tests/{ => functional}/substitute-with-invalid-ca.sh (100%) rename tests/{ => functional}/suggestions.sh (100%) rename tests/{ => functional}/supplementary-groups.sh (100%) rename tests/{ => functional}/tarball.sh (100%) rename tests/{ => functional}/test-infra.sh (100%) rename tests/{ => functional}/test-libstoreconsumer.sh (100%) rename tests/{ => functional}/test-libstoreconsumer/README.md (100%) rename tests/{ => functional}/test-libstoreconsumer/local.mk (100%) rename tests/{ => functional}/test-libstoreconsumer/main.cc (100%) rename tests/{ => functional}/timeout.nix (100%) rename tests/{ => functional}/timeout.sh (100%) rename tests/{ => functional}/toString-path.sh (100%) rename tests/{ => functional}/undefined-variable.nix (100%) rename tests/{ => functional}/user-envs-migration.sh (100%) rename tests/{ => functional}/user-envs.builder.sh (100%) rename tests/{ => functional}/user-envs.nix (100%) rename tests/{ => functional}/user-envs.sh (100%) rename tests/{ => functional}/why-depends.sh (100%) rename tests/{ => functional}/zstd.sh (100%) diff --git a/.github/labeler.yml b/.github/labeler.yml index 12120bdb3..7544f07a6 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -20,4 +20,4 @@ # Unit tests - src/*/tests/**/* # Functional and integration tests - - tests/**/* + - tests/functional/**/* diff --git a/.gitignore b/.gitignore index bf4f33564..2d3314015 100644 --- a/.gitignore +++ b/.gitignore @@ -79,24 +79,24 @@ perl/Makefile.config /src/build-remote/build-remote -# /tests/ -/tests/test-tmp -/tests/common/vars-and-functions.sh -/tests/result* -/tests/restricted-innocent -/tests/shell -/tests/shell.drv -/tests/config.nix -/tests/ca/config.nix -/tests/dyn-drv/config.nix -/tests/repl-result-out -/tests/test-libstoreconsumer/test-libstoreconsumer +# /tests/functional/ +/tests/functional/test-tmp +/tests/functional/common/vars-and-functions.sh +/tests/functional/result* +/tests/functional/restricted-innocent +/tests/functional/shell +/tests/functional/shell.drv +/tests/functional/config.nix +/tests/functional/ca/config.nix +/tests/functional/dyn-drv/config.nix +/tests/functional/repl-result-out +/tests/functional/test-libstoreconsumer/test-libstoreconsumer -# /tests/lang/ -/tests/lang/*.out -/tests/lang/*.out.xml -/tests/lang/*.err -/tests/lang/*.ast +# /tests/functional/lang/ +/tests/functional/lang/*.out +/tests/functional/lang/*.out.xml +/tests/functional/lang/*.err +/tests/functional/lang/*.ast /perl/lib/Nix/Config.pm /perl/lib/Nix/Store.cc diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4a9cff3b2..73210b303 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -57,7 +57,7 @@ Check out the [security policy](https://github.com/NixOS/nix/security/policy). - [ ] Fixes an [idea approved](https://github.com/NixOS/nix/labels/idea%20approved) issue - [ ] Tests, as appropriate: - - Functional tests – [`tests/**.sh`](./tests) + - Functional tests – [`tests/functional/**.sh`](./tests/functional) - Unit tests – [`src/*/tests`](./src/) - Integration tests – [`tests/nixos/*`](./tests/nixos) - [ ] User documentation in the [manual](..doc/manual/src) diff --git a/Makefile b/Makefile index 2eabeb077..6658e3490 100644 --- a/Makefile +++ b/Makefile @@ -27,11 +27,11 @@ makefiles += \ src/libutil/tests/local.mk \ src/libstore/tests/local.mk \ src/libexpr/tests/local.mk \ - tests/local.mk \ - tests/ca/local.mk \ - tests/dyn-drv/local.mk \ - tests/test-libstoreconsumer/local.mk \ - tests/plugins/local.mk + tests/functional/local.mk \ + tests/functional/ca/local.mk \ + tests/functional/dyn-drv/local.mk \ + tests/functional/test-libstoreconsumer/local.mk \ + tests/functional/plugins/local.mk else makefiles += \ mk/disable-tests.mk diff --git a/doc/manual/src/contributing/testing.md b/doc/manual/src/contributing/testing.md index 04bb4343d..1a6388e40 100644 --- a/doc/manual/src/contributing/testing.md +++ b/doc/manual/src/contributing/testing.md @@ -82,7 +82,7 @@ The characterization tests will mark themselves "skipped" since they regenerated ## Functional tests -The functional tests reside under the `tests` directory and are listed in `tests/local.mk`. +The functional tests reside under the `tests/functional` directory and are listed in `tests/functional/local.mk`. Each test is a bash script. ### Running the whole test suite @@ -91,8 +91,8 @@ The whole test suite can be run with: ```shell-session $ make install && make installcheck -ran test tests/foo.sh... [PASS] -ran test tests/bar.sh... [PASS] +ran test tests/functional/foo.sh... [PASS] +ran test tests/functional/bar.sh... [PASS] ... ``` @@ -100,14 +100,14 @@ ran test tests/bar.sh... [PASS] Sometimes it is useful to group related tests so they can be easily run together without running the entire test suite. Each test group is in a subdirectory of `tests`. -For example, `tests/ca/local.mk` defines a `ca` test group for content-addressed derivation outputs. +For example, `tests/functional/ca/local.mk` defines a `ca` test group for content-addressed derivation outputs. That test group can be run like this: ```shell-session $ make ca.test-group -j50 -ran test tests/ca/nix-run.sh... [PASS] -ran test tests/ca/import-derivation.sh... [PASS] +ran test tests/functional/ca/nix-run.sh... [PASS] +ran test tests/functional/ca/import-derivation.sh... [PASS] ... ``` @@ -126,21 +126,21 @@ install-tests-groups += $(test-group-name) Individual tests can be run with `make`: ```shell-session -$ make tests/${testName}.sh.test -ran test tests/${testName}.sh... [PASS] +$ make tests/functional/${testName}.sh.test +ran test tests/functional/${testName}.sh... [PASS] ``` or without `make`: ```shell-session -$ ./mk/run-test.sh tests/${testName}.sh -ran test tests/${testName}.sh... [PASS] +$ ./mk/run-test.sh tests/functional/${testName}.sh +ran test tests/functional/${testName}.sh... [PASS] ``` To see the complete output, one can also run: ```shell-session -$ ./mk/debug-test.sh tests/${testName}.sh +$ ./mk/debug-test.sh tests/functional/${testName}.sh + foo output from foo + bar @@ -175,7 +175,7 @@ edit it like so: Then, running the test with `./mk/debug-test.sh` will drop you into GDB once the script reaches that point: ```shell-session -$ ./mk/debug-test.sh tests/${testName}.sh +$ ./mk/debug-test.sh tests/functional/${testName}.sh ... + gdb blash blub GNU gdb (GDB) 12.1 @@ -206,7 +206,7 @@ It is frequently useful to regenerate the expected output. To do that, rerun the failed test(s) with `_NIX_TEST_ACCEPT=1`. For example: ```bash -_NIX_TEST_ACCEPT=1 make tests/lang.sh.test +_NIX_TEST_ACCEPT=1 make tests/functional/lang.sh.test ``` This convention is shared with the [characterization unit tests](#characterization-testing-1) too. diff --git a/flake.nix b/flake.nix index 4de155362..c331b2651 100644 --- a/flake.nix +++ b/flake.nix @@ -61,36 +61,28 @@ nixSrc = fileset.toSource { root = ./.; - fileset = fileset.intersect baseFiles ( - fileset.difference - (fileset.unions [ - ./.version - ./boehmgc-coroutine-sp-fallback.diff - ./bootstrap.sh - ./configure.ac - ./doc - ./local.mk - ./m4 - ./Makefile - ./Makefile.config.in - ./misc - ./mk - ./precompiled-headers.h - ./src - ./tests - ./unit-test-data - ./COPYING - ./scripts/local.mk - (fileset.fileFilter (f: lib.strings.hasPrefix "nix-profile" f.name) ./scripts) - # TODO: do we really need README.md? It doesn't seem used in the build. - ./README.md - ]) - (fileset.unions [ - # Removed file sets - ./tests/nixos - ./tests/installer - ]) - ); + fileset = fileset.intersect baseFiles (fileset.unions [ + ./.version + ./boehmgc-coroutine-sp-fallback.diff + ./bootstrap.sh + ./configure.ac + ./doc + ./local.mk + ./m4 + ./Makefile + ./Makefile.config.in + ./misc + ./mk + ./precompiled-headers.h + ./src + ./tests/functional + ./unit-test-data + ./COPYING + ./scripts/local.mk + (fileset.fileFilter (f: lib.strings.hasPrefix "nix-profile" f.name) ./scripts) + # TODO: do we really need README.md? It doesn't seem used in the build. + ./README.md + ]); }; # Memoize nixpkgs for different platforms for efficiency. diff --git a/mk/common-test.sh b/mk/common-test.sh index 0a2e4c1c2..7ab25febf 100644 --- a/mk/common-test.sh +++ b/mk/common-test.sh @@ -1,11 +1,15 @@ +test_dir=tests/functional + +test=$(echo -n "$test" | sed -e "s|^$test_dir/||") + TESTS_ENVIRONMENT=("TEST_NAME=${test%.*}" 'NIX_REMOTE=') : ${BASH:=/usr/bin/env bash} init_test () { - cd tests && env "${TESTS_ENVIRONMENT[@]}" $BASH -e init.sh 2>/dev/null > /dev/null + cd "$test_dir" && env "${TESTS_ENVIRONMENT[@]}" $BASH -e init.sh 2>/dev/null > /dev/null } run_test_proper () { - cd $(dirname $test) && env "${TESTS_ENVIRONMENT[@]}" $BASH -e $(basename $test) + cd "$test_dir/$(dirname $test)" && env "${TESTS_ENVIRONMENT[@]}" $BASH -e $(basename $test) } diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index 26c87391c..516cbef83 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -776,7 +776,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) } }; - /* Synchronisation point for testing, see tests/gc-concurrent.sh. */ + /* Synchronisation point for testing, see tests/functional/gc-concurrent.sh. */ if (auto p = getEnv("_NIX_TEST_GC_SYNC")) readFile(*p); diff --git a/src/libutil/url-parts.hh b/src/libutil/url-parts.hh index 4712c0c91..5c5a30dc2 100644 --- a/src/libutil/url-parts.hh +++ b/src/libutil/url-parts.hh @@ -30,7 +30,7 @@ extern std::regex refRegex; /// Instead of defining what a good Git Ref is, we define what a bad Git Ref is /// This is because of the definition of a ref in refs.c in https://github.com/git/git -/// See tests/fetchGitRefs.sh for the full definition +/// See tests/functional/fetchGitRefs.sh for the full definition const static std::string badGitRefRegexS = "//|^[./]|/\\.|\\.\\.|[[:cntrl:][:space:]:?^~\[]|\\\\|\\*|\\.lock$|\\.lock/|@\\{|[/.]$|^@$|^$"; extern std::regex badGitRefRegex; diff --git a/tests/add.sh b/tests/functional/add.sh similarity index 100% rename from tests/add.sh rename to tests/functional/add.sh diff --git a/tests/bad.tar.xz b/tests/functional/bad.tar.xz similarity index 100% rename from tests/bad.tar.xz rename to tests/functional/bad.tar.xz diff --git a/tests/bash-profile.sh b/tests/functional/bash-profile.sh similarity index 78% rename from tests/bash-profile.sh rename to tests/functional/bash-profile.sh index e2e0d1090..3faeaaba1 100644 --- a/tests/bash-profile.sh +++ b/tests/functional/bash-profile.sh @@ -1,6 +1,6 @@ source common.sh -sed -e "s|@localstatedir@|$TEST_ROOT/profile-var|g" -e "s|@coreutils@|$coreutils|g" < ../scripts/nix-profile.sh.in > $TEST_ROOT/nix-profile.sh +sed -e "s|@localstatedir@|$TEST_ROOT/profile-var|g" -e "s|@coreutils@|$coreutils|g" < ../../scripts/nix-profile.sh.in > $TEST_ROOT/nix-profile.sh user=$(whoami) rm -rf $TEST_HOME $TEST_ROOT/profile-var diff --git a/tests/big-derivation-attr.nix b/tests/functional/big-derivation-attr.nix similarity index 100% rename from tests/big-derivation-attr.nix rename to tests/functional/big-derivation-attr.nix diff --git a/tests/binary-cache-build-remote.sh b/tests/functional/binary-cache-build-remote.sh similarity index 100% rename from tests/binary-cache-build-remote.sh rename to tests/functional/binary-cache-build-remote.sh diff --git a/tests/binary-cache.sh b/tests/functional/binary-cache.sh similarity index 100% rename from tests/binary-cache.sh rename to tests/functional/binary-cache.sh diff --git a/tests/brotli.sh b/tests/functional/brotli.sh similarity index 100% rename from tests/brotli.sh rename to tests/functional/brotli.sh diff --git a/tests/build-delete.sh b/tests/functional/build-delete.sh similarity index 100% rename from tests/build-delete.sh rename to tests/functional/build-delete.sh diff --git a/tests/build-dry.sh b/tests/functional/build-dry.sh similarity index 100% rename from tests/build-dry.sh rename to tests/functional/build-dry.sh diff --git a/tests/build-hook-ca-fixed.nix b/tests/functional/build-hook-ca-fixed.nix similarity index 100% rename from tests/build-hook-ca-fixed.nix rename to tests/functional/build-hook-ca-fixed.nix diff --git a/tests/build-hook-ca-floating.nix b/tests/functional/build-hook-ca-floating.nix similarity index 100% rename from tests/build-hook-ca-floating.nix rename to tests/functional/build-hook-ca-floating.nix diff --git a/tests/build-hook.nix b/tests/functional/build-hook.nix similarity index 100% rename from tests/build-hook.nix rename to tests/functional/build-hook.nix diff --git a/tests/build-remote-content-addressed-fixed.sh b/tests/functional/build-remote-content-addressed-fixed.sh similarity index 100% rename from tests/build-remote-content-addressed-fixed.sh rename to tests/functional/build-remote-content-addressed-fixed.sh diff --git a/tests/build-remote-content-addressed-floating.sh b/tests/functional/build-remote-content-addressed-floating.sh similarity index 100% rename from tests/build-remote-content-addressed-floating.sh rename to tests/functional/build-remote-content-addressed-floating.sh diff --git a/tests/build-remote-input-addressed.sh b/tests/functional/build-remote-input-addressed.sh similarity index 100% rename from tests/build-remote-input-addressed.sh rename to tests/functional/build-remote-input-addressed.sh diff --git a/tests/build-remote-trustless-after.sh b/tests/functional/build-remote-trustless-after.sh similarity index 100% rename from tests/build-remote-trustless-after.sh rename to tests/functional/build-remote-trustless-after.sh diff --git a/tests/build-remote-trustless-should-fail-0.sh b/tests/functional/build-remote-trustless-should-fail-0.sh similarity index 100% rename from tests/build-remote-trustless-should-fail-0.sh rename to tests/functional/build-remote-trustless-should-fail-0.sh diff --git a/tests/build-remote-trustless-should-pass-0.sh b/tests/functional/build-remote-trustless-should-pass-0.sh similarity index 100% rename from tests/build-remote-trustless-should-pass-0.sh rename to tests/functional/build-remote-trustless-should-pass-0.sh diff --git a/tests/build-remote-trustless-should-pass-1.sh b/tests/functional/build-remote-trustless-should-pass-1.sh similarity index 100% rename from tests/build-remote-trustless-should-pass-1.sh rename to tests/functional/build-remote-trustless-should-pass-1.sh diff --git a/tests/build-remote-trustless-should-pass-2.sh b/tests/functional/build-remote-trustless-should-pass-2.sh similarity index 100% rename from tests/build-remote-trustless-should-pass-2.sh rename to tests/functional/build-remote-trustless-should-pass-2.sh diff --git a/tests/build-remote-trustless-should-pass-3.sh b/tests/functional/build-remote-trustless-should-pass-3.sh similarity index 100% rename from tests/build-remote-trustless-should-pass-3.sh rename to tests/functional/build-remote-trustless-should-pass-3.sh diff --git a/tests/build-remote-trustless.sh b/tests/functional/build-remote-trustless.sh similarity index 82% rename from tests/build-remote-trustless.sh rename to tests/functional/build-remote-trustless.sh index 9df44e0c5..81e5253bf 100644 --- a/tests/build-remote-trustless.sh +++ b/tests/functional/build-remote-trustless.sh @@ -6,7 +6,7 @@ unset NIX_STATE_DIR remoteDir=$TEST_ROOT/remote -# Note: ssh{-ng}://localhost bypasses ssh. See tests/build-remote.sh for +# Note: ssh{-ng}://localhost bypasses ssh. See tests/functional/build-remote.sh for # more details. nix-build $file -o $TEST_ROOT/result --max-jobs 0 \ --arg busybox $busybox \ diff --git a/tests/build-remote.sh b/tests/functional/build-remote.sh similarity index 100% rename from tests/build-remote.sh rename to tests/functional/build-remote.sh diff --git a/tests/build.sh b/tests/functional/build.sh similarity index 100% rename from tests/build.sh rename to tests/functional/build.sh diff --git a/tests/ca-shell.nix b/tests/functional/ca-shell.nix similarity index 100% rename from tests/ca-shell.nix rename to tests/functional/ca-shell.nix diff --git a/tests/ca/build-cache.sh b/tests/functional/ca/build-cache.sh similarity index 100% rename from tests/ca/build-cache.sh rename to tests/functional/ca/build-cache.sh diff --git a/tests/ca/build-dry.sh b/tests/functional/ca/build-dry.sh similarity index 100% rename from tests/ca/build-dry.sh rename to tests/functional/ca/build-dry.sh diff --git a/tests/ca/build-with-garbage-path.sh b/tests/functional/ca/build-with-garbage-path.sh similarity index 100% rename from tests/ca/build-with-garbage-path.sh rename to tests/functional/ca/build-with-garbage-path.sh diff --git a/tests/ca/build.sh b/tests/functional/ca/build.sh similarity index 100% rename from tests/ca/build.sh rename to tests/functional/ca/build.sh diff --git a/tests/ca/common.sh b/tests/functional/ca/common.sh similarity index 100% rename from tests/ca/common.sh rename to tests/functional/ca/common.sh diff --git a/tests/ca/concurrent-builds.sh b/tests/functional/ca/concurrent-builds.sh similarity index 100% rename from tests/ca/concurrent-builds.sh rename to tests/functional/ca/concurrent-builds.sh diff --git a/tests/ca/config.nix.in b/tests/functional/ca/config.nix.in similarity index 100% rename from tests/ca/config.nix.in rename to tests/functional/ca/config.nix.in diff --git a/tests/ca/content-addressed.nix b/tests/functional/ca/content-addressed.nix similarity index 100% rename from tests/ca/content-addressed.nix rename to tests/functional/ca/content-addressed.nix diff --git a/tests/ca/derivation-json.sh b/tests/functional/ca/derivation-json.sh similarity index 100% rename from tests/ca/derivation-json.sh rename to tests/functional/ca/derivation-json.sh diff --git a/tests/ca/duplicate-realisation-in-closure.sh b/tests/functional/ca/duplicate-realisation-in-closure.sh similarity index 100% rename from tests/ca/duplicate-realisation-in-closure.sh rename to tests/functional/ca/duplicate-realisation-in-closure.sh diff --git a/tests/ca/flake.nix b/tests/functional/ca/flake.nix similarity index 100% rename from tests/ca/flake.nix rename to tests/functional/ca/flake.nix diff --git a/tests/ca/gc.sh b/tests/functional/ca/gc.sh similarity index 100% rename from tests/ca/gc.sh rename to tests/functional/ca/gc.sh diff --git a/tests/ca/import-derivation.sh b/tests/functional/ca/import-derivation.sh similarity index 100% rename from tests/ca/import-derivation.sh rename to tests/functional/ca/import-derivation.sh diff --git a/tests/ca/local.mk b/tests/functional/ca/local.mk similarity index 94% rename from tests/ca/local.mk rename to tests/functional/ca/local.mk index 0852e592e..fd87b8d1f 100644 --- a/tests/ca/local.mk +++ b/tests/functional/ca/local.mk @@ -25,4 +25,4 @@ clean-files += \ $(d)/config.nix test-deps += \ - tests/ca/config.nix + tests/functional/ca/config.nix diff --git a/tests/ca/new-build-cmd.sh b/tests/functional/ca/new-build-cmd.sh similarity index 100% rename from tests/ca/new-build-cmd.sh rename to tests/functional/ca/new-build-cmd.sh diff --git a/tests/ca/nix-copy.sh b/tests/functional/ca/nix-copy.sh similarity index 100% rename from tests/ca/nix-copy.sh rename to tests/functional/ca/nix-copy.sh diff --git a/tests/ca/nix-run.sh b/tests/functional/ca/nix-run.sh similarity index 100% rename from tests/ca/nix-run.sh rename to tests/functional/ca/nix-run.sh diff --git a/tests/ca/nix-shell.sh b/tests/functional/ca/nix-shell.sh similarity index 100% rename from tests/ca/nix-shell.sh rename to tests/functional/ca/nix-shell.sh diff --git a/tests/ca/nondeterministic.nix b/tests/functional/ca/nondeterministic.nix similarity index 100% rename from tests/ca/nondeterministic.nix rename to tests/functional/ca/nondeterministic.nix diff --git a/tests/ca/post-hook.sh b/tests/functional/ca/post-hook.sh similarity index 100% rename from tests/ca/post-hook.sh rename to tests/functional/ca/post-hook.sh diff --git a/tests/ca/racy.nix b/tests/functional/ca/racy.nix similarity index 100% rename from tests/ca/racy.nix rename to tests/functional/ca/racy.nix diff --git a/tests/ca/recursive.sh b/tests/functional/ca/recursive.sh similarity index 100% rename from tests/ca/recursive.sh rename to tests/functional/ca/recursive.sh diff --git a/tests/ca/repl.sh b/tests/functional/ca/repl.sh similarity index 100% rename from tests/ca/repl.sh rename to tests/functional/ca/repl.sh diff --git a/tests/ca/selfref-gc.sh b/tests/functional/ca/selfref-gc.sh similarity index 100% rename from tests/ca/selfref-gc.sh rename to tests/functional/ca/selfref-gc.sh diff --git a/tests/ca/signatures.sh b/tests/functional/ca/signatures.sh similarity index 100% rename from tests/ca/signatures.sh rename to tests/functional/ca/signatures.sh diff --git a/tests/ca/substitute.sh b/tests/functional/ca/substitute.sh similarity index 100% rename from tests/ca/substitute.sh rename to tests/functional/ca/substitute.sh diff --git a/tests/ca/why-depends.sh b/tests/functional/ca/why-depends.sh similarity index 100% rename from tests/ca/why-depends.sh rename to tests/functional/ca/why-depends.sh diff --git a/tests/case-hack.sh b/tests/functional/case-hack.sh similarity index 100% rename from tests/case-hack.sh rename to tests/functional/case-hack.sh diff --git a/tests/case.nar b/tests/functional/case.nar similarity index 100% rename from tests/case.nar rename to tests/functional/case.nar diff --git a/tests/check-refs.nix b/tests/functional/check-refs.nix similarity index 100% rename from tests/check-refs.nix rename to tests/functional/check-refs.nix diff --git a/tests/check-refs.sh b/tests/functional/check-refs.sh similarity index 100% rename from tests/check-refs.sh rename to tests/functional/check-refs.sh diff --git a/tests/check-reqs.nix b/tests/functional/check-reqs.nix similarity index 100% rename from tests/check-reqs.nix rename to tests/functional/check-reqs.nix diff --git a/tests/check-reqs.sh b/tests/functional/check-reqs.sh similarity index 100% rename from tests/check-reqs.sh rename to tests/functional/check-reqs.sh diff --git a/tests/check.nix b/tests/functional/check.nix similarity index 100% rename from tests/check.nix rename to tests/functional/check.nix diff --git a/tests/check.sh b/tests/functional/check.sh similarity index 100% rename from tests/check.sh rename to tests/functional/check.sh diff --git a/tests/common.sh b/tests/functional/common.sh similarity index 100% rename from tests/common.sh rename to tests/functional/common.sh diff --git a/tests/common/vars-and-functions.sh.in b/tests/functional/common/vars-and-functions.sh.in similarity index 99% rename from tests/common/vars-and-functions.sh.in rename to tests/functional/common/vars-and-functions.sh.in index 8f9ec4b1a..967d6be54 100644 --- a/tests/common/vars-and-functions.sh.in +++ b/tests/functional/common/vars-and-functions.sh.in @@ -6,7 +6,7 @@ COMMON_VARS_AND_FUNCTIONS_SH_SOURCED=1 export PS4='+(${BASH_SOURCE[0]-$0}:$LINENO) ' -export TEST_ROOT=$(realpath ${TMPDIR:-/tmp}/nix-test)/${TEST_NAME:-default} +export TEST_ROOT=$(realpath ${TMPDIR:-/tmp}/nix-test)/${TEST_NAME:-default/tests\/functional//} export NIX_STORE_DIR if ! NIX_STORE_DIR=$(readlink -f $TEST_ROOT/store 2> /dev/null); then # Maybe the build directory is symlinked. diff --git a/tests/completions.sh b/tests/functional/completions.sh similarity index 100% rename from tests/completions.sh rename to tests/functional/completions.sh diff --git a/tests/compression-levels.sh b/tests/functional/compression-levels.sh similarity index 100% rename from tests/compression-levels.sh rename to tests/functional/compression-levels.sh diff --git a/tests/compute-levels.sh b/tests/functional/compute-levels.sh similarity index 100% rename from tests/compute-levels.sh rename to tests/functional/compute-levels.sh diff --git a/tests/config.nix.in b/tests/functional/config.nix.in similarity index 100% rename from tests/config.nix.in rename to tests/functional/config.nix.in diff --git a/tests/config.sh b/tests/functional/config.sh similarity index 100% rename from tests/config.sh rename to tests/functional/config.sh diff --git a/tests/config/nix-with-substituters.conf b/tests/functional/config/nix-with-substituters.conf similarity index 100% rename from tests/config/nix-with-substituters.conf rename to tests/functional/config/nix-with-substituters.conf diff --git a/tests/db-migration.sh b/tests/functional/db-migration.sh similarity index 100% rename from tests/db-migration.sh rename to tests/functional/db-migration.sh diff --git a/tests/dependencies.builder0.sh b/tests/functional/dependencies.builder0.sh similarity index 100% rename from tests/dependencies.builder0.sh rename to tests/functional/dependencies.builder0.sh diff --git a/tests/dependencies.nix b/tests/functional/dependencies.nix similarity index 100% rename from tests/dependencies.nix rename to tests/functional/dependencies.nix diff --git a/tests/dependencies.sh b/tests/functional/dependencies.sh similarity index 100% rename from tests/dependencies.sh rename to tests/functional/dependencies.sh diff --git a/tests/derivation-json.sh b/tests/functional/derivation-json.sh similarity index 100% rename from tests/derivation-json.sh rename to tests/functional/derivation-json.sh diff --git a/tests/dummy b/tests/functional/dummy similarity index 100% rename from tests/dummy rename to tests/functional/dummy diff --git a/tests/dump-db.sh b/tests/functional/dump-db.sh similarity index 100% rename from tests/dump-db.sh rename to tests/functional/dump-db.sh diff --git a/tests/dyn-drv/build-built-drv.sh b/tests/functional/dyn-drv/build-built-drv.sh similarity index 100% rename from tests/dyn-drv/build-built-drv.sh rename to tests/functional/dyn-drv/build-built-drv.sh diff --git a/tests/dyn-drv/common.sh b/tests/functional/dyn-drv/common.sh similarity index 100% rename from tests/dyn-drv/common.sh rename to tests/functional/dyn-drv/common.sh diff --git a/tests/dyn-drv/config.nix.in b/tests/functional/dyn-drv/config.nix.in similarity index 100% rename from tests/dyn-drv/config.nix.in rename to tests/functional/dyn-drv/config.nix.in diff --git a/tests/dyn-drv/dep-built-drv.sh b/tests/functional/dyn-drv/dep-built-drv.sh similarity index 100% rename from tests/dyn-drv/dep-built-drv.sh rename to tests/functional/dyn-drv/dep-built-drv.sh diff --git a/tests/dyn-drv/eval-outputOf.sh b/tests/functional/dyn-drv/eval-outputOf.sh similarity index 100% rename from tests/dyn-drv/eval-outputOf.sh rename to tests/functional/dyn-drv/eval-outputOf.sh diff --git a/tests/dyn-drv/local.mk b/tests/functional/dyn-drv/local.mk similarity index 87% rename from tests/dyn-drv/local.mk rename to tests/functional/dyn-drv/local.mk index 6b435499b..c87534944 100644 --- a/tests/dyn-drv/local.mk +++ b/tests/functional/dyn-drv/local.mk @@ -12,4 +12,4 @@ clean-files += \ $(d)/config.nix test-deps += \ - tests/dyn-drv/config.nix + tests/functional/dyn-drv/config.nix diff --git a/tests/dyn-drv/old-daemon-error-hack.nix b/tests/functional/dyn-drv/old-daemon-error-hack.nix similarity index 100% rename from tests/dyn-drv/old-daemon-error-hack.nix rename to tests/functional/dyn-drv/old-daemon-error-hack.nix diff --git a/tests/dyn-drv/old-daemon-error-hack.sh b/tests/functional/dyn-drv/old-daemon-error-hack.sh similarity index 100% rename from tests/dyn-drv/old-daemon-error-hack.sh rename to tests/functional/dyn-drv/old-daemon-error-hack.sh diff --git a/tests/dyn-drv/recursive-mod-json.nix b/tests/functional/dyn-drv/recursive-mod-json.nix similarity index 100% rename from tests/dyn-drv/recursive-mod-json.nix rename to tests/functional/dyn-drv/recursive-mod-json.nix diff --git a/tests/dyn-drv/recursive-mod-json.sh b/tests/functional/dyn-drv/recursive-mod-json.sh similarity index 100% rename from tests/dyn-drv/recursive-mod-json.sh rename to tests/functional/dyn-drv/recursive-mod-json.sh diff --git a/tests/dyn-drv/text-hashed-output.nix b/tests/functional/dyn-drv/text-hashed-output.nix similarity index 100% rename from tests/dyn-drv/text-hashed-output.nix rename to tests/functional/dyn-drv/text-hashed-output.nix diff --git a/tests/dyn-drv/text-hashed-output.sh b/tests/functional/dyn-drv/text-hashed-output.sh similarity index 100% rename from tests/dyn-drv/text-hashed-output.sh rename to tests/functional/dyn-drv/text-hashed-output.sh diff --git a/tests/eval-store.sh b/tests/functional/eval-store.sh similarity index 100% rename from tests/eval-store.sh rename to tests/functional/eval-store.sh diff --git a/tests/eval.nix b/tests/functional/eval.nix similarity index 100% rename from tests/eval.nix rename to tests/functional/eval.nix diff --git a/tests/eval.sh b/tests/functional/eval.sh similarity index 100% rename from tests/eval.sh rename to tests/functional/eval.sh diff --git a/tests/experimental-features.sh b/tests/functional/experimental-features.sh similarity index 100% rename from tests/experimental-features.sh rename to tests/functional/experimental-features.sh diff --git a/tests/export-graph.nix b/tests/functional/export-graph.nix similarity index 100% rename from tests/export-graph.nix rename to tests/functional/export-graph.nix diff --git a/tests/export-graph.sh b/tests/functional/export-graph.sh similarity index 100% rename from tests/export-graph.sh rename to tests/functional/export-graph.sh diff --git a/tests/export.sh b/tests/functional/export.sh similarity index 100% rename from tests/export.sh rename to tests/functional/export.sh diff --git a/tests/failing.nix b/tests/functional/failing.nix similarity index 100% rename from tests/failing.nix rename to tests/functional/failing.nix diff --git a/tests/fetchClosure.sh b/tests/functional/fetchClosure.sh similarity index 100% rename from tests/fetchClosure.sh rename to tests/functional/fetchClosure.sh diff --git a/tests/fetchGit.sh b/tests/functional/fetchGit.sh similarity index 100% rename from tests/fetchGit.sh rename to tests/functional/fetchGit.sh diff --git a/tests/fetchGitRefs.sh b/tests/functional/fetchGitRefs.sh similarity index 100% rename from tests/fetchGitRefs.sh rename to tests/functional/fetchGitRefs.sh diff --git a/tests/fetchGitSubmodules.sh b/tests/functional/fetchGitSubmodules.sh similarity index 100% rename from tests/fetchGitSubmodules.sh rename to tests/functional/fetchGitSubmodules.sh diff --git a/tests/fetchMercurial.sh b/tests/functional/fetchMercurial.sh similarity index 100% rename from tests/fetchMercurial.sh rename to tests/functional/fetchMercurial.sh diff --git a/tests/fetchPath.sh b/tests/functional/fetchPath.sh similarity index 100% rename from tests/fetchPath.sh rename to tests/functional/fetchPath.sh diff --git a/tests/fetchTree-file.sh b/tests/functional/fetchTree-file.sh similarity index 100% rename from tests/fetchTree-file.sh rename to tests/functional/fetchTree-file.sh diff --git a/tests/fetchurl.sh b/tests/functional/fetchurl.sh similarity index 100% rename from tests/fetchurl.sh rename to tests/functional/fetchurl.sh diff --git a/tests/filter-source.nix b/tests/functional/filter-source.nix similarity index 100% rename from tests/filter-source.nix rename to tests/functional/filter-source.nix diff --git a/tests/filter-source.sh b/tests/functional/filter-source.sh similarity index 100% rename from tests/filter-source.sh rename to tests/functional/filter-source.sh diff --git a/tests/fixed.builder1.sh b/tests/functional/fixed.builder1.sh similarity index 100% rename from tests/fixed.builder1.sh rename to tests/functional/fixed.builder1.sh diff --git a/tests/fixed.builder2.sh b/tests/functional/fixed.builder2.sh similarity index 100% rename from tests/fixed.builder2.sh rename to tests/functional/fixed.builder2.sh diff --git a/tests/fixed.nix b/tests/functional/fixed.nix similarity index 100% rename from tests/fixed.nix rename to tests/functional/fixed.nix diff --git a/tests/fixed.sh b/tests/functional/fixed.sh similarity index 100% rename from tests/fixed.sh rename to tests/functional/fixed.sh diff --git a/tests/flakes/absolute-attr-paths.sh b/tests/functional/flakes/absolute-attr-paths.sh similarity index 100% rename from tests/flakes/absolute-attr-paths.sh rename to tests/functional/flakes/absolute-attr-paths.sh diff --git a/tests/flakes/absolute-paths.sh b/tests/functional/flakes/absolute-paths.sh similarity index 100% rename from tests/flakes/absolute-paths.sh rename to tests/functional/flakes/absolute-paths.sh diff --git a/tests/flakes/build-paths.sh b/tests/functional/flakes/build-paths.sh similarity index 100% rename from tests/flakes/build-paths.sh rename to tests/functional/flakes/build-paths.sh diff --git a/tests/flakes/bundle.sh b/tests/functional/flakes/bundle.sh similarity index 100% rename from tests/flakes/bundle.sh rename to tests/functional/flakes/bundle.sh diff --git a/tests/flakes/check.sh b/tests/functional/flakes/check.sh similarity index 100% rename from tests/flakes/check.sh rename to tests/functional/flakes/check.sh diff --git a/tests/flakes/circular.sh b/tests/functional/flakes/circular.sh similarity index 100% rename from tests/flakes/circular.sh rename to tests/functional/flakes/circular.sh diff --git a/tests/flakes/common.sh b/tests/functional/flakes/common.sh similarity index 100% rename from tests/flakes/common.sh rename to tests/functional/flakes/common.sh diff --git a/tests/flakes/config.sh b/tests/functional/flakes/config.sh similarity index 100% rename from tests/flakes/config.sh rename to tests/functional/flakes/config.sh diff --git a/tests/flakes/flake-in-submodule.sh b/tests/functional/flakes/flake-in-submodule.sh similarity index 100% rename from tests/flakes/flake-in-submodule.sh rename to tests/functional/flakes/flake-in-submodule.sh diff --git a/tests/flakes/flakes.sh b/tests/functional/flakes/flakes.sh similarity index 100% rename from tests/flakes/flakes.sh rename to tests/functional/flakes/flakes.sh diff --git a/tests/flakes/follow-paths.sh b/tests/functional/flakes/follow-paths.sh similarity index 100% rename from tests/flakes/follow-paths.sh rename to tests/functional/flakes/follow-paths.sh diff --git a/tests/flakes/init.sh b/tests/functional/flakes/init.sh similarity index 100% rename from tests/flakes/init.sh rename to tests/functional/flakes/init.sh diff --git a/tests/flakes/inputs.sh b/tests/functional/flakes/inputs.sh similarity index 100% rename from tests/flakes/inputs.sh rename to tests/functional/flakes/inputs.sh diff --git a/tests/flakes/mercurial.sh b/tests/functional/flakes/mercurial.sh similarity index 100% rename from tests/flakes/mercurial.sh rename to tests/functional/flakes/mercurial.sh diff --git a/tests/flakes/run.sh b/tests/functional/flakes/run.sh similarity index 100% rename from tests/flakes/run.sh rename to tests/functional/flakes/run.sh diff --git a/tests/flakes/search-root.sh b/tests/functional/flakes/search-root.sh similarity index 100% rename from tests/flakes/search-root.sh rename to tests/functional/flakes/search-root.sh diff --git a/tests/flakes/show.sh b/tests/functional/flakes/show.sh similarity index 100% rename from tests/flakes/show.sh rename to tests/functional/flakes/show.sh diff --git a/tests/flakes/unlocked-override.sh b/tests/functional/flakes/unlocked-override.sh similarity index 100% rename from tests/flakes/unlocked-override.sh rename to tests/functional/flakes/unlocked-override.sh diff --git a/tests/fmt.sh b/tests/functional/fmt.sh similarity index 100% rename from tests/fmt.sh rename to tests/functional/fmt.sh diff --git a/tests/fmt.simple.sh b/tests/functional/fmt.simple.sh similarity index 100% rename from tests/fmt.simple.sh rename to tests/functional/fmt.simple.sh diff --git a/tests/function-trace.sh b/tests/functional/function-trace.sh similarity index 100% rename from tests/function-trace.sh rename to tests/functional/function-trace.sh diff --git a/tests/gc-auto.sh b/tests/functional/gc-auto.sh similarity index 100% rename from tests/gc-auto.sh rename to tests/functional/gc-auto.sh diff --git a/tests/gc-concurrent.builder.sh b/tests/functional/gc-concurrent.builder.sh similarity index 100% rename from tests/gc-concurrent.builder.sh rename to tests/functional/gc-concurrent.builder.sh diff --git a/tests/gc-concurrent.nix b/tests/functional/gc-concurrent.nix similarity index 100% rename from tests/gc-concurrent.nix rename to tests/functional/gc-concurrent.nix diff --git a/tests/gc-concurrent.sh b/tests/functional/gc-concurrent.sh similarity index 100% rename from tests/gc-concurrent.sh rename to tests/functional/gc-concurrent.sh diff --git a/tests/gc-concurrent2.builder.sh b/tests/functional/gc-concurrent2.builder.sh similarity index 100% rename from tests/gc-concurrent2.builder.sh rename to tests/functional/gc-concurrent2.builder.sh diff --git a/tests/gc-non-blocking.sh b/tests/functional/gc-non-blocking.sh similarity index 100% rename from tests/gc-non-blocking.sh rename to tests/functional/gc-non-blocking.sh diff --git a/tests/gc-runtime.nix b/tests/functional/gc-runtime.nix similarity index 100% rename from tests/gc-runtime.nix rename to tests/functional/gc-runtime.nix diff --git a/tests/gc-runtime.sh b/tests/functional/gc-runtime.sh similarity index 100% rename from tests/gc-runtime.sh rename to tests/functional/gc-runtime.sh diff --git a/tests/gc.sh b/tests/functional/gc.sh similarity index 100% rename from tests/gc.sh rename to tests/functional/gc.sh diff --git a/tests/hash-check.nix b/tests/functional/hash-check.nix similarity index 100% rename from tests/hash-check.nix rename to tests/functional/hash-check.nix diff --git a/tests/hash.sh b/tests/functional/hash.sh similarity index 100% rename from tests/hash.sh rename to tests/functional/hash.sh diff --git a/tests/hermetic.nix b/tests/functional/hermetic.nix similarity index 100% rename from tests/hermetic.nix rename to tests/functional/hermetic.nix diff --git a/tests/import-derivation.nix b/tests/functional/import-derivation.nix similarity index 100% rename from tests/import-derivation.nix rename to tests/functional/import-derivation.nix diff --git a/tests/import-derivation.sh b/tests/functional/import-derivation.sh similarity index 100% rename from tests/import-derivation.sh rename to tests/functional/import-derivation.sh diff --git a/tests/impure-derivations.nix b/tests/functional/impure-derivations.nix similarity index 100% rename from tests/impure-derivations.nix rename to tests/functional/impure-derivations.nix diff --git a/tests/impure-derivations.sh b/tests/functional/impure-derivations.sh similarity index 100% rename from tests/impure-derivations.sh rename to tests/functional/impure-derivations.sh diff --git a/tests/init.sh b/tests/functional/init.sh similarity index 100% rename from tests/init.sh rename to tests/functional/init.sh diff --git a/tests/install-darwin.sh b/tests/functional/install-darwin.sh similarity index 100% rename from tests/install-darwin.sh rename to tests/functional/install-darwin.sh diff --git a/tests/lang-test-infra.sh b/tests/functional/lang-test-infra.sh similarity index 100% rename from tests/lang-test-infra.sh rename to tests/functional/lang-test-infra.sh diff --git a/tests/lang.sh b/tests/functional/lang.sh similarity index 98% rename from tests/lang.sh rename to tests/functional/lang.sh index 75dbbc38e..f4760eced 100755 --- a/tests/lang.sh +++ b/tests/functional/lang.sh @@ -134,7 +134,7 @@ else echo '' echo 'You can rerun this test with:' echo '' - echo ' _NIX_TEST_ACCEPT=1 make tests/lang.sh.test' + echo ' _NIX_TEST_ACCEPT=1 make tests/functional/lang.sh.test' echo '' echo 'to regenerate the files containing the expected output,' echo 'and then view the git diff to decide whether a change is' diff --git a/tests/lang/binary-data b/tests/functional/lang/binary-data similarity index 100% rename from tests/lang/binary-data rename to tests/functional/lang/binary-data diff --git a/tests/lang/data b/tests/functional/lang/data similarity index 100% rename from tests/lang/data rename to tests/functional/lang/data diff --git a/tests/lang/dir1/a.nix b/tests/functional/lang/dir1/a.nix similarity index 100% rename from tests/lang/dir1/a.nix rename to tests/functional/lang/dir1/a.nix diff --git a/tests/lang/dir2/a.nix b/tests/functional/lang/dir2/a.nix similarity index 100% rename from tests/lang/dir2/a.nix rename to tests/functional/lang/dir2/a.nix diff --git a/tests/lang/dir2/b.nix b/tests/functional/lang/dir2/b.nix similarity index 100% rename from tests/lang/dir2/b.nix rename to tests/functional/lang/dir2/b.nix diff --git a/tests/lang/dir3/a.nix b/tests/functional/lang/dir3/a.nix similarity index 100% rename from tests/lang/dir3/a.nix rename to tests/functional/lang/dir3/a.nix diff --git a/tests/lang/dir3/b.nix b/tests/functional/lang/dir3/b.nix similarity index 100% rename from tests/lang/dir3/b.nix rename to tests/functional/lang/dir3/b.nix diff --git a/tests/lang/dir3/c.nix b/tests/functional/lang/dir3/c.nix similarity index 100% rename from tests/lang/dir3/c.nix rename to tests/functional/lang/dir3/c.nix diff --git a/tests/lang/dir4/a.nix b/tests/functional/lang/dir4/a.nix similarity index 100% rename from tests/lang/dir4/a.nix rename to tests/functional/lang/dir4/a.nix diff --git a/tests/lang/dir4/c.nix b/tests/functional/lang/dir4/c.nix similarity index 100% rename from tests/lang/dir4/c.nix rename to tests/functional/lang/dir4/c.nix diff --git a/tests/lang/empty.exp b/tests/functional/lang/empty.exp similarity index 100% rename from tests/lang/empty.exp rename to tests/functional/lang/empty.exp diff --git a/tests/lang/eval-fail-abort.err.exp b/tests/functional/lang/eval-fail-abort.err.exp similarity index 100% rename from tests/lang/eval-fail-abort.err.exp rename to tests/functional/lang/eval-fail-abort.err.exp diff --git a/tests/lang/eval-fail-abort.nix b/tests/functional/lang/eval-fail-abort.nix similarity index 100% rename from tests/lang/eval-fail-abort.nix rename to tests/functional/lang/eval-fail-abort.nix diff --git a/tests/lang/eval-fail-antiquoted-path.err.exp b/tests/functional/lang/eval-fail-antiquoted-path.err.exp similarity index 100% rename from tests/lang/eval-fail-antiquoted-path.err.exp rename to tests/functional/lang/eval-fail-antiquoted-path.err.exp diff --git a/tests/lang/eval-fail-assert.err.exp b/tests/functional/lang/eval-fail-assert.err.exp similarity index 100% rename from tests/lang/eval-fail-assert.err.exp rename to tests/functional/lang/eval-fail-assert.err.exp diff --git a/tests/lang/eval-fail-assert.nix b/tests/functional/lang/eval-fail-assert.nix similarity index 100% rename from tests/lang/eval-fail-assert.nix rename to tests/functional/lang/eval-fail-assert.nix diff --git a/tests/lang/eval-fail-bad-antiquote-1.err.exp b/tests/functional/lang/eval-fail-bad-antiquote-1.err.exp similarity index 100% rename from tests/lang/eval-fail-bad-antiquote-1.err.exp rename to tests/functional/lang/eval-fail-bad-antiquote-1.err.exp diff --git a/tests/lang/eval-fail-bad-antiquote-2.err.exp b/tests/functional/lang/eval-fail-bad-antiquote-2.err.exp similarity index 100% rename from tests/lang/eval-fail-bad-antiquote-2.err.exp rename to tests/functional/lang/eval-fail-bad-antiquote-2.err.exp diff --git a/tests/lang/eval-fail-bad-antiquote-3.err.exp b/tests/functional/lang/eval-fail-bad-antiquote-3.err.exp similarity index 100% rename from tests/lang/eval-fail-bad-antiquote-3.err.exp rename to tests/functional/lang/eval-fail-bad-antiquote-3.err.exp diff --git a/tests/lang/eval-fail-bad-string-interpolation-1.err.exp b/tests/functional/lang/eval-fail-bad-string-interpolation-1.err.exp similarity index 100% rename from tests/lang/eval-fail-bad-string-interpolation-1.err.exp rename to tests/functional/lang/eval-fail-bad-string-interpolation-1.err.exp diff --git a/tests/lang/eval-fail-bad-string-interpolation-1.nix b/tests/functional/lang/eval-fail-bad-string-interpolation-1.nix similarity index 100% rename from tests/lang/eval-fail-bad-string-interpolation-1.nix rename to tests/functional/lang/eval-fail-bad-string-interpolation-1.nix diff --git a/tests/lang/eval-fail-bad-string-interpolation-2.err.exp b/tests/functional/lang/eval-fail-bad-string-interpolation-2.err.exp similarity index 100% rename from tests/lang/eval-fail-bad-string-interpolation-2.err.exp rename to tests/functional/lang/eval-fail-bad-string-interpolation-2.err.exp diff --git a/tests/lang/eval-fail-bad-string-interpolation-2.nix b/tests/functional/lang/eval-fail-bad-string-interpolation-2.nix similarity index 100% rename from tests/lang/eval-fail-bad-string-interpolation-2.nix rename to tests/functional/lang/eval-fail-bad-string-interpolation-2.nix diff --git a/tests/lang/eval-fail-bad-string-interpolation-3.err.exp b/tests/functional/lang/eval-fail-bad-string-interpolation-3.err.exp similarity index 100% rename from tests/lang/eval-fail-bad-string-interpolation-3.err.exp rename to tests/functional/lang/eval-fail-bad-string-interpolation-3.err.exp diff --git a/tests/lang/eval-fail-bad-string-interpolation-3.nix b/tests/functional/lang/eval-fail-bad-string-interpolation-3.nix similarity index 100% rename from tests/lang/eval-fail-bad-string-interpolation-3.nix rename to tests/functional/lang/eval-fail-bad-string-interpolation-3.nix diff --git a/tests/lang/eval-fail-blackhole.err.exp b/tests/functional/lang/eval-fail-blackhole.err.exp similarity index 100% rename from tests/lang/eval-fail-blackhole.err.exp rename to tests/functional/lang/eval-fail-blackhole.err.exp diff --git a/tests/lang/eval-fail-blackhole.nix b/tests/functional/lang/eval-fail-blackhole.nix similarity index 100% rename from tests/lang/eval-fail-blackhole.nix rename to tests/functional/lang/eval-fail-blackhole.nix diff --git a/tests/lang/eval-fail-deepseq.err.exp b/tests/functional/lang/eval-fail-deepseq.err.exp similarity index 100% rename from tests/lang/eval-fail-deepseq.err.exp rename to tests/functional/lang/eval-fail-deepseq.err.exp diff --git a/tests/lang/eval-fail-deepseq.nix b/tests/functional/lang/eval-fail-deepseq.nix similarity index 100% rename from tests/lang/eval-fail-deepseq.nix rename to tests/functional/lang/eval-fail-deepseq.nix diff --git a/tests/lang/eval-fail-dup-dynamic-attrs.err.exp b/tests/functional/lang/eval-fail-dup-dynamic-attrs.err.exp similarity index 100% rename from tests/lang/eval-fail-dup-dynamic-attrs.err.exp rename to tests/functional/lang/eval-fail-dup-dynamic-attrs.err.exp diff --git a/tests/lang/eval-fail-dup-dynamic-attrs.nix b/tests/functional/lang/eval-fail-dup-dynamic-attrs.nix similarity index 100% rename from tests/lang/eval-fail-dup-dynamic-attrs.nix rename to tests/functional/lang/eval-fail-dup-dynamic-attrs.nix diff --git a/tests/lang/eval-fail-foldlStrict-strict-op-application.err.exp b/tests/functional/lang/eval-fail-foldlStrict-strict-op-application.err.exp similarity index 100% rename from tests/lang/eval-fail-foldlStrict-strict-op-application.err.exp rename to tests/functional/lang/eval-fail-foldlStrict-strict-op-application.err.exp diff --git a/tests/lang/eval-fail-foldlStrict-strict-op-application.nix b/tests/functional/lang/eval-fail-foldlStrict-strict-op-application.nix similarity index 100% rename from tests/lang/eval-fail-foldlStrict-strict-op-application.nix rename to tests/functional/lang/eval-fail-foldlStrict-strict-op-application.nix diff --git a/tests/lang/eval-fail-fromTOML-timestamps.err.exp b/tests/functional/lang/eval-fail-fromTOML-timestamps.err.exp similarity index 100% rename from tests/lang/eval-fail-fromTOML-timestamps.err.exp rename to tests/functional/lang/eval-fail-fromTOML-timestamps.err.exp diff --git a/tests/lang/eval-fail-fromTOML-timestamps.nix b/tests/functional/lang/eval-fail-fromTOML-timestamps.nix similarity index 100% rename from tests/lang/eval-fail-fromTOML-timestamps.nix rename to tests/functional/lang/eval-fail-fromTOML-timestamps.nix diff --git a/tests/lang/eval-fail-hashfile-missing.err.exp b/tests/functional/lang/eval-fail-hashfile-missing.err.exp similarity index 100% rename from tests/lang/eval-fail-hashfile-missing.err.exp rename to tests/functional/lang/eval-fail-hashfile-missing.err.exp diff --git a/tests/lang/eval-fail-hashfile-missing.nix b/tests/functional/lang/eval-fail-hashfile-missing.nix similarity index 100% rename from tests/lang/eval-fail-hashfile-missing.nix rename to tests/functional/lang/eval-fail-hashfile-missing.nix diff --git a/tests/lang/eval-fail-list.err.exp b/tests/functional/lang/eval-fail-list.err.exp similarity index 100% rename from tests/lang/eval-fail-list.err.exp rename to tests/functional/lang/eval-fail-list.err.exp diff --git a/tests/lang/eval-fail-list.nix b/tests/functional/lang/eval-fail-list.nix similarity index 100% rename from tests/lang/eval-fail-list.nix rename to tests/functional/lang/eval-fail-list.nix diff --git a/tests/lang/eval-fail-missing-arg.err.exp b/tests/functional/lang/eval-fail-missing-arg.err.exp similarity index 100% rename from tests/lang/eval-fail-missing-arg.err.exp rename to tests/functional/lang/eval-fail-missing-arg.err.exp diff --git a/tests/lang/eval-fail-missing-arg.nix b/tests/functional/lang/eval-fail-missing-arg.nix similarity index 100% rename from tests/lang/eval-fail-missing-arg.nix rename to tests/functional/lang/eval-fail-missing-arg.nix diff --git a/tests/lang/eval-fail-nonexist-path.err.exp b/tests/functional/lang/eval-fail-nonexist-path.err.exp similarity index 100% rename from tests/lang/eval-fail-nonexist-path.err.exp rename to tests/functional/lang/eval-fail-nonexist-path.err.exp diff --git a/tests/lang/eval-fail-nonexist-path.nix b/tests/functional/lang/eval-fail-nonexist-path.nix similarity index 100% rename from tests/lang/eval-fail-nonexist-path.nix rename to tests/functional/lang/eval-fail-nonexist-path.nix diff --git a/tests/lang/eval-fail-path-slash.err.exp b/tests/functional/lang/eval-fail-path-slash.err.exp similarity index 100% rename from tests/lang/eval-fail-path-slash.err.exp rename to tests/functional/lang/eval-fail-path-slash.err.exp diff --git a/tests/lang/eval-fail-path-slash.nix b/tests/functional/lang/eval-fail-path-slash.nix similarity index 100% rename from tests/lang/eval-fail-path-slash.nix rename to tests/functional/lang/eval-fail-path-slash.nix diff --git a/tests/lang/eval-fail-recursion.err.exp b/tests/functional/lang/eval-fail-recursion.err.exp similarity index 100% rename from tests/lang/eval-fail-recursion.err.exp rename to tests/functional/lang/eval-fail-recursion.err.exp diff --git a/tests/lang/eval-fail-recursion.nix b/tests/functional/lang/eval-fail-recursion.nix similarity index 100% rename from tests/lang/eval-fail-recursion.nix rename to tests/functional/lang/eval-fail-recursion.nix diff --git a/tests/lang/eval-fail-remove.err.exp b/tests/functional/lang/eval-fail-remove.err.exp similarity index 100% rename from tests/lang/eval-fail-remove.err.exp rename to tests/functional/lang/eval-fail-remove.err.exp diff --git a/tests/lang/eval-fail-remove.nix b/tests/functional/lang/eval-fail-remove.nix similarity index 100% rename from tests/lang/eval-fail-remove.nix rename to tests/functional/lang/eval-fail-remove.nix diff --git a/tests/lang/eval-fail-scope-5.err.exp b/tests/functional/lang/eval-fail-scope-5.err.exp similarity index 100% rename from tests/lang/eval-fail-scope-5.err.exp rename to tests/functional/lang/eval-fail-scope-5.err.exp diff --git a/tests/lang/eval-fail-scope-5.nix b/tests/functional/lang/eval-fail-scope-5.nix similarity index 100% rename from tests/lang/eval-fail-scope-5.nix rename to tests/functional/lang/eval-fail-scope-5.nix diff --git a/tests/lang/eval-fail-seq.err.exp b/tests/functional/lang/eval-fail-seq.err.exp similarity index 100% rename from tests/lang/eval-fail-seq.err.exp rename to tests/functional/lang/eval-fail-seq.err.exp diff --git a/tests/lang/eval-fail-seq.nix b/tests/functional/lang/eval-fail-seq.nix similarity index 100% rename from tests/lang/eval-fail-seq.nix rename to tests/functional/lang/eval-fail-seq.nix diff --git a/tests/lang/eval-fail-set-override.err.exp b/tests/functional/lang/eval-fail-set-override.err.exp similarity index 100% rename from tests/lang/eval-fail-set-override.err.exp rename to tests/functional/lang/eval-fail-set-override.err.exp diff --git a/tests/lang/eval-fail-set-override.nix b/tests/functional/lang/eval-fail-set-override.nix similarity index 100% rename from tests/lang/eval-fail-set-override.nix rename to tests/functional/lang/eval-fail-set-override.nix diff --git a/tests/lang/eval-fail-set.err.exp b/tests/functional/lang/eval-fail-set.err.exp similarity index 100% rename from tests/lang/eval-fail-set.err.exp rename to tests/functional/lang/eval-fail-set.err.exp diff --git a/tests/lang/eval-fail-set.nix b/tests/functional/lang/eval-fail-set.nix similarity index 100% rename from tests/lang/eval-fail-set.nix rename to tests/functional/lang/eval-fail-set.nix diff --git a/tests/lang/eval-fail-substring.err.exp b/tests/functional/lang/eval-fail-substring.err.exp similarity index 100% rename from tests/lang/eval-fail-substring.err.exp rename to tests/functional/lang/eval-fail-substring.err.exp diff --git a/tests/lang/eval-fail-substring.nix b/tests/functional/lang/eval-fail-substring.nix similarity index 100% rename from tests/lang/eval-fail-substring.nix rename to tests/functional/lang/eval-fail-substring.nix diff --git a/tests/lang/eval-fail-to-path.err.exp b/tests/functional/lang/eval-fail-to-path.err.exp similarity index 100% rename from tests/lang/eval-fail-to-path.err.exp rename to tests/functional/lang/eval-fail-to-path.err.exp diff --git a/tests/lang/eval-fail-to-path.nix b/tests/functional/lang/eval-fail-to-path.nix similarity index 100% rename from tests/lang/eval-fail-to-path.nix rename to tests/functional/lang/eval-fail-to-path.nix diff --git a/tests/lang/eval-fail-toJSON.err.exp b/tests/functional/lang/eval-fail-toJSON.err.exp similarity index 100% rename from tests/lang/eval-fail-toJSON.err.exp rename to tests/functional/lang/eval-fail-toJSON.err.exp diff --git a/tests/lang/eval-fail-toJSON.nix b/tests/functional/lang/eval-fail-toJSON.nix similarity index 100% rename from tests/lang/eval-fail-toJSON.nix rename to tests/functional/lang/eval-fail-toJSON.nix diff --git a/tests/lang/eval-fail-undeclared-arg.err.exp b/tests/functional/lang/eval-fail-undeclared-arg.err.exp similarity index 100% rename from tests/lang/eval-fail-undeclared-arg.err.exp rename to tests/functional/lang/eval-fail-undeclared-arg.err.exp diff --git a/tests/lang/eval-fail-undeclared-arg.nix b/tests/functional/lang/eval-fail-undeclared-arg.nix similarity index 100% rename from tests/lang/eval-fail-undeclared-arg.nix rename to tests/functional/lang/eval-fail-undeclared-arg.nix diff --git a/tests/lang/eval-okay-any-all.exp b/tests/functional/lang/eval-okay-any-all.exp similarity index 100% rename from tests/lang/eval-okay-any-all.exp rename to tests/functional/lang/eval-okay-any-all.exp diff --git a/tests/lang/eval-okay-any-all.nix b/tests/functional/lang/eval-okay-any-all.nix similarity index 100% rename from tests/lang/eval-okay-any-all.nix rename to tests/functional/lang/eval-okay-any-all.nix diff --git a/tests/lang/eval-okay-arithmetic.exp b/tests/functional/lang/eval-okay-arithmetic.exp similarity index 100% rename from tests/lang/eval-okay-arithmetic.exp rename to tests/functional/lang/eval-okay-arithmetic.exp diff --git a/tests/lang/eval-okay-arithmetic.nix b/tests/functional/lang/eval-okay-arithmetic.nix similarity index 100% rename from tests/lang/eval-okay-arithmetic.nix rename to tests/functional/lang/eval-okay-arithmetic.nix diff --git a/tests/lang/eval-okay-attrnames.exp b/tests/functional/lang/eval-okay-attrnames.exp similarity index 100% rename from tests/lang/eval-okay-attrnames.exp rename to tests/functional/lang/eval-okay-attrnames.exp diff --git a/tests/lang/eval-okay-attrnames.nix b/tests/functional/lang/eval-okay-attrnames.nix similarity index 100% rename from tests/lang/eval-okay-attrnames.nix rename to tests/functional/lang/eval-okay-attrnames.nix diff --git a/tests/lang/eval-okay-attrs.exp b/tests/functional/lang/eval-okay-attrs.exp similarity index 100% rename from tests/lang/eval-okay-attrs.exp rename to tests/functional/lang/eval-okay-attrs.exp diff --git a/tests/lang/eval-okay-attrs.nix b/tests/functional/lang/eval-okay-attrs.nix similarity index 100% rename from tests/lang/eval-okay-attrs.nix rename to tests/functional/lang/eval-okay-attrs.nix diff --git a/tests/lang/eval-okay-attrs2.exp b/tests/functional/lang/eval-okay-attrs2.exp similarity index 100% rename from tests/lang/eval-okay-attrs2.exp rename to tests/functional/lang/eval-okay-attrs2.exp diff --git a/tests/lang/eval-okay-attrs2.nix b/tests/functional/lang/eval-okay-attrs2.nix similarity index 100% rename from tests/lang/eval-okay-attrs2.nix rename to tests/functional/lang/eval-okay-attrs2.nix diff --git a/tests/lang/eval-okay-attrs3.exp b/tests/functional/lang/eval-okay-attrs3.exp similarity index 100% rename from tests/lang/eval-okay-attrs3.exp rename to tests/functional/lang/eval-okay-attrs3.exp diff --git a/tests/lang/eval-okay-attrs3.nix b/tests/functional/lang/eval-okay-attrs3.nix similarity index 100% rename from tests/lang/eval-okay-attrs3.nix rename to tests/functional/lang/eval-okay-attrs3.nix diff --git a/tests/lang/eval-okay-attrs4.exp b/tests/functional/lang/eval-okay-attrs4.exp similarity index 100% rename from tests/lang/eval-okay-attrs4.exp rename to tests/functional/lang/eval-okay-attrs4.exp diff --git a/tests/lang/eval-okay-attrs4.nix b/tests/functional/lang/eval-okay-attrs4.nix similarity index 100% rename from tests/lang/eval-okay-attrs4.nix rename to tests/functional/lang/eval-okay-attrs4.nix diff --git a/tests/lang/eval-okay-attrs5.exp b/tests/functional/lang/eval-okay-attrs5.exp similarity index 100% rename from tests/lang/eval-okay-attrs5.exp rename to tests/functional/lang/eval-okay-attrs5.exp diff --git a/tests/lang/eval-okay-attrs5.nix b/tests/functional/lang/eval-okay-attrs5.nix similarity index 100% rename from tests/lang/eval-okay-attrs5.nix rename to tests/functional/lang/eval-okay-attrs5.nix diff --git a/tests/lang/eval-okay-attrs6.exp b/tests/functional/lang/eval-okay-attrs6.exp similarity index 100% rename from tests/lang/eval-okay-attrs6.exp rename to tests/functional/lang/eval-okay-attrs6.exp diff --git a/tests/lang/eval-okay-attrs6.nix b/tests/functional/lang/eval-okay-attrs6.nix similarity index 100% rename from tests/lang/eval-okay-attrs6.nix rename to tests/functional/lang/eval-okay-attrs6.nix diff --git a/tests/lang/eval-okay-autoargs.exp b/tests/functional/lang/eval-okay-autoargs.exp similarity index 100% rename from tests/lang/eval-okay-autoargs.exp rename to tests/functional/lang/eval-okay-autoargs.exp diff --git a/tests/lang/eval-okay-autoargs.flags b/tests/functional/lang/eval-okay-autoargs.flags similarity index 100% rename from tests/lang/eval-okay-autoargs.flags rename to tests/functional/lang/eval-okay-autoargs.flags diff --git a/tests/lang/eval-okay-autoargs.nix b/tests/functional/lang/eval-okay-autoargs.nix similarity index 100% rename from tests/lang/eval-okay-autoargs.nix rename to tests/functional/lang/eval-okay-autoargs.nix diff --git a/tests/lang/eval-okay-backslash-newline-1.exp b/tests/functional/lang/eval-okay-backslash-newline-1.exp similarity index 100% rename from tests/lang/eval-okay-backslash-newline-1.exp rename to tests/functional/lang/eval-okay-backslash-newline-1.exp diff --git a/tests/lang/eval-okay-backslash-newline-1.nix b/tests/functional/lang/eval-okay-backslash-newline-1.nix similarity index 100% rename from tests/lang/eval-okay-backslash-newline-1.nix rename to tests/functional/lang/eval-okay-backslash-newline-1.nix diff --git a/tests/lang/eval-okay-backslash-newline-2.exp b/tests/functional/lang/eval-okay-backslash-newline-2.exp similarity index 100% rename from tests/lang/eval-okay-backslash-newline-2.exp rename to tests/functional/lang/eval-okay-backslash-newline-2.exp diff --git a/tests/lang/eval-okay-backslash-newline-2.nix b/tests/functional/lang/eval-okay-backslash-newline-2.nix similarity index 100% rename from tests/lang/eval-okay-backslash-newline-2.nix rename to tests/functional/lang/eval-okay-backslash-newline-2.nix diff --git a/tests/lang/eval-okay-builtins-add.exp b/tests/functional/lang/eval-okay-builtins-add.exp similarity index 100% rename from tests/lang/eval-okay-builtins-add.exp rename to tests/functional/lang/eval-okay-builtins-add.exp diff --git a/tests/lang/eval-okay-builtins-add.nix b/tests/functional/lang/eval-okay-builtins-add.nix similarity index 100% rename from tests/lang/eval-okay-builtins-add.nix rename to tests/functional/lang/eval-okay-builtins-add.nix diff --git a/tests/lang/eval-okay-builtins.exp b/tests/functional/lang/eval-okay-builtins.exp similarity index 100% rename from tests/lang/eval-okay-builtins.exp rename to tests/functional/lang/eval-okay-builtins.exp diff --git a/tests/lang/eval-okay-builtins.nix b/tests/functional/lang/eval-okay-builtins.nix similarity index 100% rename from tests/lang/eval-okay-builtins.nix rename to tests/functional/lang/eval-okay-builtins.nix diff --git a/tests/lang/eval-okay-callable-attrs.exp b/tests/functional/lang/eval-okay-callable-attrs.exp similarity index 100% rename from tests/lang/eval-okay-callable-attrs.exp rename to tests/functional/lang/eval-okay-callable-attrs.exp diff --git a/tests/lang/eval-okay-callable-attrs.nix b/tests/functional/lang/eval-okay-callable-attrs.nix similarity index 100% rename from tests/lang/eval-okay-callable-attrs.nix rename to tests/functional/lang/eval-okay-callable-attrs.nix diff --git a/tests/lang/eval-okay-catattrs.exp b/tests/functional/lang/eval-okay-catattrs.exp similarity index 100% rename from tests/lang/eval-okay-catattrs.exp rename to tests/functional/lang/eval-okay-catattrs.exp diff --git a/tests/lang/eval-okay-catattrs.nix b/tests/functional/lang/eval-okay-catattrs.nix similarity index 100% rename from tests/lang/eval-okay-catattrs.nix rename to tests/functional/lang/eval-okay-catattrs.nix diff --git a/tests/lang/eval-okay-closure.exp b/tests/functional/lang/eval-okay-closure.exp similarity index 100% rename from tests/lang/eval-okay-closure.exp rename to tests/functional/lang/eval-okay-closure.exp diff --git a/tests/lang/eval-okay-closure.exp.xml b/tests/functional/lang/eval-okay-closure.exp.xml similarity index 100% rename from tests/lang/eval-okay-closure.exp.xml rename to tests/functional/lang/eval-okay-closure.exp.xml diff --git a/tests/lang/eval-okay-closure.nix b/tests/functional/lang/eval-okay-closure.nix similarity index 100% rename from tests/lang/eval-okay-closure.nix rename to tests/functional/lang/eval-okay-closure.nix diff --git a/tests/lang/eval-okay-comments.exp b/tests/functional/lang/eval-okay-comments.exp similarity index 100% rename from tests/lang/eval-okay-comments.exp rename to tests/functional/lang/eval-okay-comments.exp diff --git a/tests/lang/eval-okay-comments.nix b/tests/functional/lang/eval-okay-comments.nix similarity index 100% rename from tests/lang/eval-okay-comments.nix rename to tests/functional/lang/eval-okay-comments.nix diff --git a/tests/lang/eval-okay-concat.exp b/tests/functional/lang/eval-okay-concat.exp similarity index 100% rename from tests/lang/eval-okay-concat.exp rename to tests/functional/lang/eval-okay-concat.exp diff --git a/tests/lang/eval-okay-concat.nix b/tests/functional/lang/eval-okay-concat.nix similarity index 100% rename from tests/lang/eval-okay-concat.nix rename to tests/functional/lang/eval-okay-concat.nix diff --git a/tests/lang/eval-okay-concatmap.exp b/tests/functional/lang/eval-okay-concatmap.exp similarity index 100% rename from tests/lang/eval-okay-concatmap.exp rename to tests/functional/lang/eval-okay-concatmap.exp diff --git a/tests/lang/eval-okay-concatmap.nix b/tests/functional/lang/eval-okay-concatmap.nix similarity index 100% rename from tests/lang/eval-okay-concatmap.nix rename to tests/functional/lang/eval-okay-concatmap.nix diff --git a/tests/lang/eval-okay-concatstringssep.exp b/tests/functional/lang/eval-okay-concatstringssep.exp similarity index 100% rename from tests/lang/eval-okay-concatstringssep.exp rename to tests/functional/lang/eval-okay-concatstringssep.exp diff --git a/tests/lang/eval-okay-concatstringssep.nix b/tests/functional/lang/eval-okay-concatstringssep.nix similarity index 100% rename from tests/lang/eval-okay-concatstringssep.nix rename to tests/functional/lang/eval-okay-concatstringssep.nix diff --git a/tests/lang/eval-okay-context-introspection.exp b/tests/functional/lang/eval-okay-context-introspection.exp similarity index 100% rename from tests/lang/eval-okay-context-introspection.exp rename to tests/functional/lang/eval-okay-context-introspection.exp diff --git a/tests/lang/eval-okay-context-introspection.nix b/tests/functional/lang/eval-okay-context-introspection.nix similarity index 100% rename from tests/lang/eval-okay-context-introspection.nix rename to tests/functional/lang/eval-okay-context-introspection.nix diff --git a/tests/lang/eval-okay-context.exp b/tests/functional/lang/eval-okay-context.exp similarity index 100% rename from tests/lang/eval-okay-context.exp rename to tests/functional/lang/eval-okay-context.exp diff --git a/tests/lang/eval-okay-context.nix b/tests/functional/lang/eval-okay-context.nix similarity index 100% rename from tests/lang/eval-okay-context.nix rename to tests/functional/lang/eval-okay-context.nix diff --git a/tests/lang/eval-okay-curpos.exp b/tests/functional/lang/eval-okay-curpos.exp similarity index 100% rename from tests/lang/eval-okay-curpos.exp rename to tests/functional/lang/eval-okay-curpos.exp diff --git a/tests/lang/eval-okay-curpos.nix b/tests/functional/lang/eval-okay-curpos.nix similarity index 100% rename from tests/lang/eval-okay-curpos.nix rename to tests/functional/lang/eval-okay-curpos.nix diff --git a/tests/lang/eval-okay-deepseq.exp b/tests/functional/lang/eval-okay-deepseq.exp similarity index 100% rename from tests/lang/eval-okay-deepseq.exp rename to tests/functional/lang/eval-okay-deepseq.exp diff --git a/tests/lang/eval-okay-deepseq.nix b/tests/functional/lang/eval-okay-deepseq.nix similarity index 100% rename from tests/lang/eval-okay-deepseq.nix rename to tests/functional/lang/eval-okay-deepseq.nix diff --git a/tests/lang/eval-okay-delayed-with-inherit.exp b/tests/functional/lang/eval-okay-delayed-with-inherit.exp similarity index 100% rename from tests/lang/eval-okay-delayed-with-inherit.exp rename to tests/functional/lang/eval-okay-delayed-with-inherit.exp diff --git a/tests/lang/eval-okay-delayed-with-inherit.nix b/tests/functional/lang/eval-okay-delayed-with-inherit.nix similarity index 100% rename from tests/lang/eval-okay-delayed-with-inherit.nix rename to tests/functional/lang/eval-okay-delayed-with-inherit.nix diff --git a/tests/lang/eval-okay-delayed-with.exp b/tests/functional/lang/eval-okay-delayed-with.exp similarity index 100% rename from tests/lang/eval-okay-delayed-with.exp rename to tests/functional/lang/eval-okay-delayed-with.exp diff --git a/tests/lang/eval-okay-delayed-with.nix b/tests/functional/lang/eval-okay-delayed-with.nix similarity index 100% rename from tests/lang/eval-okay-delayed-with.nix rename to tests/functional/lang/eval-okay-delayed-with.nix diff --git a/tests/lang/eval-okay-dynamic-attrs-2.exp b/tests/functional/lang/eval-okay-dynamic-attrs-2.exp similarity index 100% rename from tests/lang/eval-okay-dynamic-attrs-2.exp rename to tests/functional/lang/eval-okay-dynamic-attrs-2.exp diff --git a/tests/lang/eval-okay-dynamic-attrs-2.nix b/tests/functional/lang/eval-okay-dynamic-attrs-2.nix similarity index 100% rename from tests/lang/eval-okay-dynamic-attrs-2.nix rename to tests/functional/lang/eval-okay-dynamic-attrs-2.nix diff --git a/tests/lang/eval-okay-dynamic-attrs-bare.exp b/tests/functional/lang/eval-okay-dynamic-attrs-bare.exp similarity index 100% rename from tests/lang/eval-okay-dynamic-attrs-bare.exp rename to tests/functional/lang/eval-okay-dynamic-attrs-bare.exp diff --git a/tests/lang/eval-okay-dynamic-attrs-bare.nix b/tests/functional/lang/eval-okay-dynamic-attrs-bare.nix similarity index 100% rename from tests/lang/eval-okay-dynamic-attrs-bare.nix rename to tests/functional/lang/eval-okay-dynamic-attrs-bare.nix diff --git a/tests/lang/eval-okay-dynamic-attrs.exp b/tests/functional/lang/eval-okay-dynamic-attrs.exp similarity index 100% rename from tests/lang/eval-okay-dynamic-attrs.exp rename to tests/functional/lang/eval-okay-dynamic-attrs.exp diff --git a/tests/lang/eval-okay-dynamic-attrs.nix b/tests/functional/lang/eval-okay-dynamic-attrs.nix similarity index 100% rename from tests/lang/eval-okay-dynamic-attrs.nix rename to tests/functional/lang/eval-okay-dynamic-attrs.nix diff --git a/tests/lang/eval-okay-elem.exp b/tests/functional/lang/eval-okay-elem.exp similarity index 100% rename from tests/lang/eval-okay-elem.exp rename to tests/functional/lang/eval-okay-elem.exp diff --git a/tests/lang/eval-okay-elem.nix b/tests/functional/lang/eval-okay-elem.nix similarity index 100% rename from tests/lang/eval-okay-elem.nix rename to tests/functional/lang/eval-okay-elem.nix diff --git a/tests/lang/eval-okay-empty-args.exp b/tests/functional/lang/eval-okay-empty-args.exp similarity index 100% rename from tests/lang/eval-okay-empty-args.exp rename to tests/functional/lang/eval-okay-empty-args.exp diff --git a/tests/lang/eval-okay-empty-args.nix b/tests/functional/lang/eval-okay-empty-args.nix similarity index 100% rename from tests/lang/eval-okay-empty-args.nix rename to tests/functional/lang/eval-okay-empty-args.nix diff --git a/tests/lang/eval-okay-eq-derivations.exp b/tests/functional/lang/eval-okay-eq-derivations.exp similarity index 100% rename from tests/lang/eval-okay-eq-derivations.exp rename to tests/functional/lang/eval-okay-eq-derivations.exp diff --git a/tests/lang/eval-okay-eq-derivations.nix b/tests/functional/lang/eval-okay-eq-derivations.nix similarity index 100% rename from tests/lang/eval-okay-eq-derivations.nix rename to tests/functional/lang/eval-okay-eq-derivations.nix diff --git a/tests/lang/eval-okay-eq.exp b/tests/functional/lang/eval-okay-eq.exp similarity index 100% rename from tests/lang/eval-okay-eq.exp rename to tests/functional/lang/eval-okay-eq.exp diff --git a/tests/lang/eval-okay-eq.nix b/tests/functional/lang/eval-okay-eq.nix similarity index 100% rename from tests/lang/eval-okay-eq.nix rename to tests/functional/lang/eval-okay-eq.nix diff --git a/tests/lang/eval-okay-filter.exp b/tests/functional/lang/eval-okay-filter.exp similarity index 100% rename from tests/lang/eval-okay-filter.exp rename to tests/functional/lang/eval-okay-filter.exp diff --git a/tests/lang/eval-okay-filter.nix b/tests/functional/lang/eval-okay-filter.nix similarity index 100% rename from tests/lang/eval-okay-filter.nix rename to tests/functional/lang/eval-okay-filter.nix diff --git a/tests/lang/eval-okay-flake-ref-to-string.exp b/tests/functional/lang/eval-okay-flake-ref-to-string.exp similarity index 100% rename from tests/lang/eval-okay-flake-ref-to-string.exp rename to tests/functional/lang/eval-okay-flake-ref-to-string.exp diff --git a/tests/lang/eval-okay-flake-ref-to-string.nix b/tests/functional/lang/eval-okay-flake-ref-to-string.nix similarity index 100% rename from tests/lang/eval-okay-flake-ref-to-string.nix rename to tests/functional/lang/eval-okay-flake-ref-to-string.nix diff --git a/tests/lang/eval-okay-flatten.exp b/tests/functional/lang/eval-okay-flatten.exp similarity index 100% rename from tests/lang/eval-okay-flatten.exp rename to tests/functional/lang/eval-okay-flatten.exp diff --git a/tests/lang/eval-okay-flatten.nix b/tests/functional/lang/eval-okay-flatten.nix similarity index 100% rename from tests/lang/eval-okay-flatten.nix rename to tests/functional/lang/eval-okay-flatten.nix diff --git a/tests/lang/eval-okay-float.exp b/tests/functional/lang/eval-okay-float.exp similarity index 100% rename from tests/lang/eval-okay-float.exp rename to tests/functional/lang/eval-okay-float.exp diff --git a/tests/lang/eval-okay-float.nix b/tests/functional/lang/eval-okay-float.nix similarity index 100% rename from tests/lang/eval-okay-float.nix rename to tests/functional/lang/eval-okay-float.nix diff --git a/tests/lang/eval-okay-floor-ceil.exp b/tests/functional/lang/eval-okay-floor-ceil.exp similarity index 100% rename from tests/lang/eval-okay-floor-ceil.exp rename to tests/functional/lang/eval-okay-floor-ceil.exp diff --git a/tests/lang/eval-okay-floor-ceil.nix b/tests/functional/lang/eval-okay-floor-ceil.nix similarity index 100% rename from tests/lang/eval-okay-floor-ceil.nix rename to tests/functional/lang/eval-okay-floor-ceil.nix diff --git a/tests/lang/eval-okay-foldlStrict-lazy-elements.exp b/tests/functional/lang/eval-okay-foldlStrict-lazy-elements.exp similarity index 100% rename from tests/lang/eval-okay-foldlStrict-lazy-elements.exp rename to tests/functional/lang/eval-okay-foldlStrict-lazy-elements.exp diff --git a/tests/lang/eval-okay-foldlStrict-lazy-elements.nix b/tests/functional/lang/eval-okay-foldlStrict-lazy-elements.nix similarity index 100% rename from tests/lang/eval-okay-foldlStrict-lazy-elements.nix rename to tests/functional/lang/eval-okay-foldlStrict-lazy-elements.nix diff --git a/tests/lang/eval-okay-foldlStrict-lazy-initial-accumulator.exp b/tests/functional/lang/eval-okay-foldlStrict-lazy-initial-accumulator.exp similarity index 100% rename from tests/lang/eval-okay-foldlStrict-lazy-initial-accumulator.exp rename to tests/functional/lang/eval-okay-foldlStrict-lazy-initial-accumulator.exp diff --git a/tests/lang/eval-okay-foldlStrict-lazy-initial-accumulator.nix b/tests/functional/lang/eval-okay-foldlStrict-lazy-initial-accumulator.nix similarity index 100% rename from tests/lang/eval-okay-foldlStrict-lazy-initial-accumulator.nix rename to tests/functional/lang/eval-okay-foldlStrict-lazy-initial-accumulator.nix diff --git a/tests/lang/eval-okay-foldlStrict.exp b/tests/functional/lang/eval-okay-foldlStrict.exp similarity index 100% rename from tests/lang/eval-okay-foldlStrict.exp rename to tests/functional/lang/eval-okay-foldlStrict.exp diff --git a/tests/lang/eval-okay-foldlStrict.nix b/tests/functional/lang/eval-okay-foldlStrict.nix similarity index 100% rename from tests/lang/eval-okay-foldlStrict.nix rename to tests/functional/lang/eval-okay-foldlStrict.nix diff --git a/tests/lang/eval-okay-fromTOML-timestamps.exp b/tests/functional/lang/eval-okay-fromTOML-timestamps.exp similarity index 100% rename from tests/lang/eval-okay-fromTOML-timestamps.exp rename to tests/functional/lang/eval-okay-fromTOML-timestamps.exp diff --git a/tests/lang/eval-okay-fromTOML-timestamps.flags b/tests/functional/lang/eval-okay-fromTOML-timestamps.flags similarity index 100% rename from tests/lang/eval-okay-fromTOML-timestamps.flags rename to tests/functional/lang/eval-okay-fromTOML-timestamps.flags diff --git a/tests/lang/eval-okay-fromTOML-timestamps.nix b/tests/functional/lang/eval-okay-fromTOML-timestamps.nix similarity index 100% rename from tests/lang/eval-okay-fromTOML-timestamps.nix rename to tests/functional/lang/eval-okay-fromTOML-timestamps.nix diff --git a/tests/lang/eval-okay-fromTOML.exp b/tests/functional/lang/eval-okay-fromTOML.exp similarity index 100% rename from tests/lang/eval-okay-fromTOML.exp rename to tests/functional/lang/eval-okay-fromTOML.exp diff --git a/tests/lang/eval-okay-fromTOML.nix b/tests/functional/lang/eval-okay-fromTOML.nix similarity index 100% rename from tests/lang/eval-okay-fromTOML.nix rename to tests/functional/lang/eval-okay-fromTOML.nix diff --git a/tests/lang/eval-okay-fromjson-escapes.exp b/tests/functional/lang/eval-okay-fromjson-escapes.exp similarity index 100% rename from tests/lang/eval-okay-fromjson-escapes.exp rename to tests/functional/lang/eval-okay-fromjson-escapes.exp diff --git a/tests/lang/eval-okay-fromjson-escapes.nix b/tests/functional/lang/eval-okay-fromjson-escapes.nix similarity index 100% rename from tests/lang/eval-okay-fromjson-escapes.nix rename to tests/functional/lang/eval-okay-fromjson-escapes.nix diff --git a/tests/lang/eval-okay-fromjson.exp b/tests/functional/lang/eval-okay-fromjson.exp similarity index 100% rename from tests/lang/eval-okay-fromjson.exp rename to tests/functional/lang/eval-okay-fromjson.exp diff --git a/tests/lang/eval-okay-fromjson.nix b/tests/functional/lang/eval-okay-fromjson.nix similarity index 100% rename from tests/lang/eval-okay-fromjson.nix rename to tests/functional/lang/eval-okay-fromjson.nix diff --git a/tests/lang/eval-okay-functionargs.exp b/tests/functional/lang/eval-okay-functionargs.exp similarity index 100% rename from tests/lang/eval-okay-functionargs.exp rename to tests/functional/lang/eval-okay-functionargs.exp diff --git a/tests/lang/eval-okay-functionargs.exp.xml b/tests/functional/lang/eval-okay-functionargs.exp.xml similarity index 100% rename from tests/lang/eval-okay-functionargs.exp.xml rename to tests/functional/lang/eval-okay-functionargs.exp.xml diff --git a/tests/lang/eval-okay-functionargs.nix b/tests/functional/lang/eval-okay-functionargs.nix similarity index 100% rename from tests/lang/eval-okay-functionargs.nix rename to tests/functional/lang/eval-okay-functionargs.nix diff --git a/tests/lang/eval-okay-getattrpos-functionargs.exp b/tests/functional/lang/eval-okay-getattrpos-functionargs.exp similarity index 100% rename from tests/lang/eval-okay-getattrpos-functionargs.exp rename to tests/functional/lang/eval-okay-getattrpos-functionargs.exp diff --git a/tests/lang/eval-okay-getattrpos-functionargs.nix b/tests/functional/lang/eval-okay-getattrpos-functionargs.nix similarity index 100% rename from tests/lang/eval-okay-getattrpos-functionargs.nix rename to tests/functional/lang/eval-okay-getattrpos-functionargs.nix diff --git a/tests/lang/eval-okay-getattrpos-undefined.exp b/tests/functional/lang/eval-okay-getattrpos-undefined.exp similarity index 100% rename from tests/lang/eval-okay-getattrpos-undefined.exp rename to tests/functional/lang/eval-okay-getattrpos-undefined.exp diff --git a/tests/lang/eval-okay-getattrpos-undefined.nix b/tests/functional/lang/eval-okay-getattrpos-undefined.nix similarity index 100% rename from tests/lang/eval-okay-getattrpos-undefined.nix rename to tests/functional/lang/eval-okay-getattrpos-undefined.nix diff --git a/tests/lang/eval-okay-getattrpos.exp b/tests/functional/lang/eval-okay-getattrpos.exp similarity index 100% rename from tests/lang/eval-okay-getattrpos.exp rename to tests/functional/lang/eval-okay-getattrpos.exp diff --git a/tests/lang/eval-okay-getattrpos.nix b/tests/functional/lang/eval-okay-getattrpos.nix similarity index 100% rename from tests/lang/eval-okay-getattrpos.nix rename to tests/functional/lang/eval-okay-getattrpos.nix diff --git a/tests/lang/eval-okay-getenv.exp b/tests/functional/lang/eval-okay-getenv.exp similarity index 100% rename from tests/lang/eval-okay-getenv.exp rename to tests/functional/lang/eval-okay-getenv.exp diff --git a/tests/lang/eval-okay-getenv.nix b/tests/functional/lang/eval-okay-getenv.nix similarity index 100% rename from tests/lang/eval-okay-getenv.nix rename to tests/functional/lang/eval-okay-getenv.nix diff --git a/tests/lang/eval-okay-groupBy.exp b/tests/functional/lang/eval-okay-groupBy.exp similarity index 100% rename from tests/lang/eval-okay-groupBy.exp rename to tests/functional/lang/eval-okay-groupBy.exp diff --git a/tests/lang/eval-okay-groupBy.nix b/tests/functional/lang/eval-okay-groupBy.nix similarity index 100% rename from tests/lang/eval-okay-groupBy.nix rename to tests/functional/lang/eval-okay-groupBy.nix diff --git a/tests/lang/eval-okay-hash.exp b/tests/functional/lang/eval-okay-hash.exp similarity index 100% rename from tests/lang/eval-okay-hash.exp rename to tests/functional/lang/eval-okay-hash.exp diff --git a/tests/lang/eval-okay-hashfile.exp b/tests/functional/lang/eval-okay-hashfile.exp similarity index 100% rename from tests/lang/eval-okay-hashfile.exp rename to tests/functional/lang/eval-okay-hashfile.exp diff --git a/tests/lang/eval-okay-hashfile.nix b/tests/functional/lang/eval-okay-hashfile.nix similarity index 100% rename from tests/lang/eval-okay-hashfile.nix rename to tests/functional/lang/eval-okay-hashfile.nix diff --git a/tests/lang/eval-okay-hashstring.exp b/tests/functional/lang/eval-okay-hashstring.exp similarity index 100% rename from tests/lang/eval-okay-hashstring.exp rename to tests/functional/lang/eval-okay-hashstring.exp diff --git a/tests/lang/eval-okay-hashstring.nix b/tests/functional/lang/eval-okay-hashstring.nix similarity index 100% rename from tests/lang/eval-okay-hashstring.nix rename to tests/functional/lang/eval-okay-hashstring.nix diff --git a/tests/lang/eval-okay-if.exp b/tests/functional/lang/eval-okay-if.exp similarity index 100% rename from tests/lang/eval-okay-if.exp rename to tests/functional/lang/eval-okay-if.exp diff --git a/tests/lang/eval-okay-if.nix b/tests/functional/lang/eval-okay-if.nix similarity index 100% rename from tests/lang/eval-okay-if.nix rename to tests/functional/lang/eval-okay-if.nix diff --git a/tests/lang/eval-okay-import.exp b/tests/functional/lang/eval-okay-import.exp similarity index 100% rename from tests/lang/eval-okay-import.exp rename to tests/functional/lang/eval-okay-import.exp diff --git a/tests/lang/eval-okay-import.nix b/tests/functional/lang/eval-okay-import.nix similarity index 100% rename from tests/lang/eval-okay-import.nix rename to tests/functional/lang/eval-okay-import.nix diff --git a/tests/lang/eval-okay-ind-string.exp b/tests/functional/lang/eval-okay-ind-string.exp similarity index 100% rename from tests/lang/eval-okay-ind-string.exp rename to tests/functional/lang/eval-okay-ind-string.exp diff --git a/tests/lang/eval-okay-ind-string.nix b/tests/functional/lang/eval-okay-ind-string.nix similarity index 100% rename from tests/lang/eval-okay-ind-string.nix rename to tests/functional/lang/eval-okay-ind-string.nix diff --git a/tests/lang/eval-okay-intersectAttrs.exp b/tests/functional/lang/eval-okay-intersectAttrs.exp similarity index 100% rename from tests/lang/eval-okay-intersectAttrs.exp rename to tests/functional/lang/eval-okay-intersectAttrs.exp diff --git a/tests/lang/eval-okay-intersectAttrs.nix b/tests/functional/lang/eval-okay-intersectAttrs.nix similarity index 100% rename from tests/lang/eval-okay-intersectAttrs.nix rename to tests/functional/lang/eval-okay-intersectAttrs.nix diff --git a/tests/lang/eval-okay-let.exp b/tests/functional/lang/eval-okay-let.exp similarity index 100% rename from tests/lang/eval-okay-let.exp rename to tests/functional/lang/eval-okay-let.exp diff --git a/tests/lang/eval-okay-let.nix b/tests/functional/lang/eval-okay-let.nix similarity index 100% rename from tests/lang/eval-okay-let.nix rename to tests/functional/lang/eval-okay-let.nix diff --git a/tests/lang/eval-okay-list.exp b/tests/functional/lang/eval-okay-list.exp similarity index 100% rename from tests/lang/eval-okay-list.exp rename to tests/functional/lang/eval-okay-list.exp diff --git a/tests/lang/eval-okay-list.nix b/tests/functional/lang/eval-okay-list.nix similarity index 100% rename from tests/lang/eval-okay-list.nix rename to tests/functional/lang/eval-okay-list.nix diff --git a/tests/lang/eval-okay-listtoattrs.exp b/tests/functional/lang/eval-okay-listtoattrs.exp similarity index 100% rename from tests/lang/eval-okay-listtoattrs.exp rename to tests/functional/lang/eval-okay-listtoattrs.exp diff --git a/tests/lang/eval-okay-listtoattrs.nix b/tests/functional/lang/eval-okay-listtoattrs.nix similarity index 100% rename from tests/lang/eval-okay-listtoattrs.nix rename to tests/functional/lang/eval-okay-listtoattrs.nix diff --git a/tests/lang/eval-okay-logic.exp b/tests/functional/lang/eval-okay-logic.exp similarity index 100% rename from tests/lang/eval-okay-logic.exp rename to tests/functional/lang/eval-okay-logic.exp diff --git a/tests/lang/eval-okay-logic.nix b/tests/functional/lang/eval-okay-logic.nix similarity index 100% rename from tests/lang/eval-okay-logic.nix rename to tests/functional/lang/eval-okay-logic.nix diff --git a/tests/lang/eval-okay-map.exp b/tests/functional/lang/eval-okay-map.exp similarity index 100% rename from tests/lang/eval-okay-map.exp rename to tests/functional/lang/eval-okay-map.exp diff --git a/tests/lang/eval-okay-map.nix b/tests/functional/lang/eval-okay-map.nix similarity index 100% rename from tests/lang/eval-okay-map.nix rename to tests/functional/lang/eval-okay-map.nix diff --git a/tests/lang/eval-okay-mapattrs.exp b/tests/functional/lang/eval-okay-mapattrs.exp similarity index 100% rename from tests/lang/eval-okay-mapattrs.exp rename to tests/functional/lang/eval-okay-mapattrs.exp diff --git a/tests/lang/eval-okay-mapattrs.nix b/tests/functional/lang/eval-okay-mapattrs.nix similarity index 100% rename from tests/lang/eval-okay-mapattrs.nix rename to tests/functional/lang/eval-okay-mapattrs.nix diff --git a/tests/lang/eval-okay-merge-dynamic-attrs.exp b/tests/functional/lang/eval-okay-merge-dynamic-attrs.exp similarity index 100% rename from tests/lang/eval-okay-merge-dynamic-attrs.exp rename to tests/functional/lang/eval-okay-merge-dynamic-attrs.exp diff --git a/tests/lang/eval-okay-merge-dynamic-attrs.nix b/tests/functional/lang/eval-okay-merge-dynamic-attrs.nix similarity index 100% rename from tests/lang/eval-okay-merge-dynamic-attrs.nix rename to tests/functional/lang/eval-okay-merge-dynamic-attrs.nix diff --git a/tests/lang/eval-okay-nested-with.exp b/tests/functional/lang/eval-okay-nested-with.exp similarity index 100% rename from tests/lang/eval-okay-nested-with.exp rename to tests/functional/lang/eval-okay-nested-with.exp diff --git a/tests/lang/eval-okay-nested-with.nix b/tests/functional/lang/eval-okay-nested-with.nix similarity index 100% rename from tests/lang/eval-okay-nested-with.nix rename to tests/functional/lang/eval-okay-nested-with.nix diff --git a/tests/lang/eval-okay-new-let.exp b/tests/functional/lang/eval-okay-new-let.exp similarity index 100% rename from tests/lang/eval-okay-new-let.exp rename to tests/functional/lang/eval-okay-new-let.exp diff --git a/tests/lang/eval-okay-new-let.nix b/tests/functional/lang/eval-okay-new-let.nix similarity index 100% rename from tests/lang/eval-okay-new-let.nix rename to tests/functional/lang/eval-okay-new-let.nix diff --git a/tests/lang/eval-okay-null-dynamic-attrs.exp b/tests/functional/lang/eval-okay-null-dynamic-attrs.exp similarity index 100% rename from tests/lang/eval-okay-null-dynamic-attrs.exp rename to tests/functional/lang/eval-okay-null-dynamic-attrs.exp diff --git a/tests/lang/eval-okay-null-dynamic-attrs.nix b/tests/functional/lang/eval-okay-null-dynamic-attrs.nix similarity index 100% rename from tests/lang/eval-okay-null-dynamic-attrs.nix rename to tests/functional/lang/eval-okay-null-dynamic-attrs.nix diff --git a/tests/lang/eval-okay-overrides.exp b/tests/functional/lang/eval-okay-overrides.exp similarity index 100% rename from tests/lang/eval-okay-overrides.exp rename to tests/functional/lang/eval-okay-overrides.exp diff --git a/tests/lang/eval-okay-overrides.nix b/tests/functional/lang/eval-okay-overrides.nix similarity index 100% rename from tests/lang/eval-okay-overrides.nix rename to tests/functional/lang/eval-okay-overrides.nix diff --git a/tests/lang/eval-okay-parse-flake-ref.exp b/tests/functional/lang/eval-okay-parse-flake-ref.exp similarity index 100% rename from tests/lang/eval-okay-parse-flake-ref.exp rename to tests/functional/lang/eval-okay-parse-flake-ref.exp diff --git a/tests/lang/eval-okay-parse-flake-ref.nix b/tests/functional/lang/eval-okay-parse-flake-ref.nix similarity index 100% rename from tests/lang/eval-okay-parse-flake-ref.nix rename to tests/functional/lang/eval-okay-parse-flake-ref.nix diff --git a/tests/lang/eval-okay-partition.exp b/tests/functional/lang/eval-okay-partition.exp similarity index 100% rename from tests/lang/eval-okay-partition.exp rename to tests/functional/lang/eval-okay-partition.exp diff --git a/tests/lang/eval-okay-partition.nix b/tests/functional/lang/eval-okay-partition.nix similarity index 100% rename from tests/lang/eval-okay-partition.nix rename to tests/functional/lang/eval-okay-partition.nix diff --git a/tests/lang/eval-okay-path-string-interpolation.exp b/tests/functional/lang/eval-okay-path-string-interpolation.exp similarity index 100% rename from tests/lang/eval-okay-path-string-interpolation.exp rename to tests/functional/lang/eval-okay-path-string-interpolation.exp diff --git a/tests/lang/eval-okay-path-string-interpolation.nix b/tests/functional/lang/eval-okay-path-string-interpolation.nix similarity index 100% rename from tests/lang/eval-okay-path-string-interpolation.nix rename to tests/functional/lang/eval-okay-path-string-interpolation.nix diff --git a/tests/lang/eval-okay-path.exp b/tests/functional/lang/eval-okay-path.exp similarity index 100% rename from tests/lang/eval-okay-path.exp rename to tests/functional/lang/eval-okay-path.exp diff --git a/tests/lang/eval-okay-path.nix b/tests/functional/lang/eval-okay-path.nix similarity index 100% rename from tests/lang/eval-okay-path.nix rename to tests/functional/lang/eval-okay-path.nix diff --git a/tests/lang/eval-okay-pathexists.exp b/tests/functional/lang/eval-okay-pathexists.exp similarity index 100% rename from tests/lang/eval-okay-pathexists.exp rename to tests/functional/lang/eval-okay-pathexists.exp diff --git a/tests/lang/eval-okay-pathexists.nix b/tests/functional/lang/eval-okay-pathexists.nix similarity index 100% rename from tests/lang/eval-okay-pathexists.nix rename to tests/functional/lang/eval-okay-pathexists.nix diff --git a/tests/lang/eval-okay-patterns.exp b/tests/functional/lang/eval-okay-patterns.exp similarity index 100% rename from tests/lang/eval-okay-patterns.exp rename to tests/functional/lang/eval-okay-patterns.exp diff --git a/tests/lang/eval-okay-patterns.nix b/tests/functional/lang/eval-okay-patterns.nix similarity index 100% rename from tests/lang/eval-okay-patterns.nix rename to tests/functional/lang/eval-okay-patterns.nix diff --git a/tests/lang/eval-okay-print.err.exp b/tests/functional/lang/eval-okay-print.err.exp similarity index 100% rename from tests/lang/eval-okay-print.err.exp rename to tests/functional/lang/eval-okay-print.err.exp diff --git a/tests/lang/eval-okay-print.exp b/tests/functional/lang/eval-okay-print.exp similarity index 100% rename from tests/lang/eval-okay-print.exp rename to tests/functional/lang/eval-okay-print.exp diff --git a/tests/lang/eval-okay-print.nix b/tests/functional/lang/eval-okay-print.nix similarity index 100% rename from tests/lang/eval-okay-print.nix rename to tests/functional/lang/eval-okay-print.nix diff --git a/tests/lang/eval-okay-readDir.exp b/tests/functional/lang/eval-okay-readDir.exp similarity index 100% rename from tests/lang/eval-okay-readDir.exp rename to tests/functional/lang/eval-okay-readDir.exp diff --git a/tests/lang/eval-okay-readDir.nix b/tests/functional/lang/eval-okay-readDir.nix similarity index 100% rename from tests/lang/eval-okay-readDir.nix rename to tests/functional/lang/eval-okay-readDir.nix diff --git a/tests/lang/eval-okay-readFileType.exp b/tests/functional/lang/eval-okay-readFileType.exp similarity index 100% rename from tests/lang/eval-okay-readFileType.exp rename to tests/functional/lang/eval-okay-readFileType.exp diff --git a/tests/lang/eval-okay-readFileType.nix b/tests/functional/lang/eval-okay-readFileType.nix similarity index 100% rename from tests/lang/eval-okay-readFileType.nix rename to tests/functional/lang/eval-okay-readFileType.nix diff --git a/tests/lang/eval-okay-readfile.exp b/tests/functional/lang/eval-okay-readfile.exp similarity index 100% rename from tests/lang/eval-okay-readfile.exp rename to tests/functional/lang/eval-okay-readfile.exp diff --git a/tests/lang/eval-okay-readfile.nix b/tests/functional/lang/eval-okay-readfile.nix similarity index 100% rename from tests/lang/eval-okay-readfile.nix rename to tests/functional/lang/eval-okay-readfile.nix diff --git a/tests/lang/eval-okay-redefine-builtin.exp b/tests/functional/lang/eval-okay-redefine-builtin.exp similarity index 100% rename from tests/lang/eval-okay-redefine-builtin.exp rename to tests/functional/lang/eval-okay-redefine-builtin.exp diff --git a/tests/lang/eval-okay-redefine-builtin.nix b/tests/functional/lang/eval-okay-redefine-builtin.nix similarity index 100% rename from tests/lang/eval-okay-redefine-builtin.nix rename to tests/functional/lang/eval-okay-redefine-builtin.nix diff --git a/tests/lang/eval-okay-regex-match.exp b/tests/functional/lang/eval-okay-regex-match.exp similarity index 100% rename from tests/lang/eval-okay-regex-match.exp rename to tests/functional/lang/eval-okay-regex-match.exp diff --git a/tests/lang/eval-okay-regex-match.nix b/tests/functional/lang/eval-okay-regex-match.nix similarity index 100% rename from tests/lang/eval-okay-regex-match.nix rename to tests/functional/lang/eval-okay-regex-match.nix diff --git a/tests/lang/eval-okay-regex-split.exp b/tests/functional/lang/eval-okay-regex-split.exp similarity index 100% rename from tests/lang/eval-okay-regex-split.exp rename to tests/functional/lang/eval-okay-regex-split.exp diff --git a/tests/lang/eval-okay-regex-split.nix b/tests/functional/lang/eval-okay-regex-split.nix similarity index 100% rename from tests/lang/eval-okay-regex-split.nix rename to tests/functional/lang/eval-okay-regex-split.nix diff --git a/tests/lang/eval-okay-regression-20220122.exp b/tests/functional/lang/eval-okay-regression-20220122.exp similarity index 100% rename from tests/lang/eval-okay-regression-20220122.exp rename to tests/functional/lang/eval-okay-regression-20220122.exp diff --git a/tests/lang/eval-okay-regression-20220122.nix b/tests/functional/lang/eval-okay-regression-20220122.nix similarity index 100% rename from tests/lang/eval-okay-regression-20220122.nix rename to tests/functional/lang/eval-okay-regression-20220122.nix diff --git a/tests/lang/eval-okay-regression-20220125.exp b/tests/functional/lang/eval-okay-regression-20220125.exp similarity index 100% rename from tests/lang/eval-okay-regression-20220125.exp rename to tests/functional/lang/eval-okay-regression-20220125.exp diff --git a/tests/lang/eval-okay-regression-20220125.nix b/tests/functional/lang/eval-okay-regression-20220125.nix similarity index 100% rename from tests/lang/eval-okay-regression-20220125.nix rename to tests/functional/lang/eval-okay-regression-20220125.nix diff --git a/tests/lang/eval-okay-remove.exp b/tests/functional/lang/eval-okay-remove.exp similarity index 100% rename from tests/lang/eval-okay-remove.exp rename to tests/functional/lang/eval-okay-remove.exp diff --git a/tests/lang/eval-okay-remove.nix b/tests/functional/lang/eval-okay-remove.nix similarity index 100% rename from tests/lang/eval-okay-remove.nix rename to tests/functional/lang/eval-okay-remove.nix diff --git a/tests/lang/eval-okay-replacestrings.exp b/tests/functional/lang/eval-okay-replacestrings.exp similarity index 100% rename from tests/lang/eval-okay-replacestrings.exp rename to tests/functional/lang/eval-okay-replacestrings.exp diff --git a/tests/lang/eval-okay-replacestrings.nix b/tests/functional/lang/eval-okay-replacestrings.nix similarity index 100% rename from tests/lang/eval-okay-replacestrings.nix rename to tests/functional/lang/eval-okay-replacestrings.nix diff --git a/tests/lang/eval-okay-scope-1.exp b/tests/functional/lang/eval-okay-scope-1.exp similarity index 100% rename from tests/lang/eval-okay-scope-1.exp rename to tests/functional/lang/eval-okay-scope-1.exp diff --git a/tests/lang/eval-okay-scope-1.nix b/tests/functional/lang/eval-okay-scope-1.nix similarity index 100% rename from tests/lang/eval-okay-scope-1.nix rename to tests/functional/lang/eval-okay-scope-1.nix diff --git a/tests/lang/eval-okay-scope-2.exp b/tests/functional/lang/eval-okay-scope-2.exp similarity index 100% rename from tests/lang/eval-okay-scope-2.exp rename to tests/functional/lang/eval-okay-scope-2.exp diff --git a/tests/lang/eval-okay-scope-2.nix b/tests/functional/lang/eval-okay-scope-2.nix similarity index 100% rename from tests/lang/eval-okay-scope-2.nix rename to tests/functional/lang/eval-okay-scope-2.nix diff --git a/tests/lang/eval-okay-scope-3.exp b/tests/functional/lang/eval-okay-scope-3.exp similarity index 100% rename from tests/lang/eval-okay-scope-3.exp rename to tests/functional/lang/eval-okay-scope-3.exp diff --git a/tests/lang/eval-okay-scope-3.nix b/tests/functional/lang/eval-okay-scope-3.nix similarity index 100% rename from tests/lang/eval-okay-scope-3.nix rename to tests/functional/lang/eval-okay-scope-3.nix diff --git a/tests/lang/eval-okay-scope-4.exp b/tests/functional/lang/eval-okay-scope-4.exp similarity index 100% rename from tests/lang/eval-okay-scope-4.exp rename to tests/functional/lang/eval-okay-scope-4.exp diff --git a/tests/lang/eval-okay-scope-4.nix b/tests/functional/lang/eval-okay-scope-4.nix similarity index 100% rename from tests/lang/eval-okay-scope-4.nix rename to tests/functional/lang/eval-okay-scope-4.nix diff --git a/tests/lang/eval-okay-scope-6.exp b/tests/functional/lang/eval-okay-scope-6.exp similarity index 100% rename from tests/lang/eval-okay-scope-6.exp rename to tests/functional/lang/eval-okay-scope-6.exp diff --git a/tests/lang/eval-okay-scope-6.nix b/tests/functional/lang/eval-okay-scope-6.nix similarity index 100% rename from tests/lang/eval-okay-scope-6.nix rename to tests/functional/lang/eval-okay-scope-6.nix diff --git a/tests/lang/eval-okay-scope-7.exp b/tests/functional/lang/eval-okay-scope-7.exp similarity index 100% rename from tests/lang/eval-okay-scope-7.exp rename to tests/functional/lang/eval-okay-scope-7.exp diff --git a/tests/lang/eval-okay-scope-7.nix b/tests/functional/lang/eval-okay-scope-7.nix similarity index 100% rename from tests/lang/eval-okay-scope-7.nix rename to tests/functional/lang/eval-okay-scope-7.nix diff --git a/tests/lang/eval-okay-search-path.exp b/tests/functional/lang/eval-okay-search-path.exp similarity index 100% rename from tests/lang/eval-okay-search-path.exp rename to tests/functional/lang/eval-okay-search-path.exp diff --git a/tests/lang/eval-okay-search-path.flags b/tests/functional/lang/eval-okay-search-path.flags similarity index 100% rename from tests/lang/eval-okay-search-path.flags rename to tests/functional/lang/eval-okay-search-path.flags diff --git a/tests/lang/eval-okay-search-path.nix b/tests/functional/lang/eval-okay-search-path.nix similarity index 100% rename from tests/lang/eval-okay-search-path.nix rename to tests/functional/lang/eval-okay-search-path.nix diff --git a/tests/lang/eval-okay-seq.exp b/tests/functional/lang/eval-okay-seq.exp similarity index 100% rename from tests/lang/eval-okay-seq.exp rename to tests/functional/lang/eval-okay-seq.exp diff --git a/tests/lang/eval-okay-seq.nix b/tests/functional/lang/eval-okay-seq.nix similarity index 100% rename from tests/lang/eval-okay-seq.nix rename to tests/functional/lang/eval-okay-seq.nix diff --git a/tests/lang/eval-okay-sort.exp b/tests/functional/lang/eval-okay-sort.exp similarity index 100% rename from tests/lang/eval-okay-sort.exp rename to tests/functional/lang/eval-okay-sort.exp diff --git a/tests/lang/eval-okay-sort.nix b/tests/functional/lang/eval-okay-sort.nix similarity index 100% rename from tests/lang/eval-okay-sort.nix rename to tests/functional/lang/eval-okay-sort.nix diff --git a/tests/lang/eval-okay-splitversion.exp b/tests/functional/lang/eval-okay-splitversion.exp similarity index 100% rename from tests/lang/eval-okay-splitversion.exp rename to tests/functional/lang/eval-okay-splitversion.exp diff --git a/tests/lang/eval-okay-splitversion.nix b/tests/functional/lang/eval-okay-splitversion.nix similarity index 100% rename from tests/lang/eval-okay-splitversion.nix rename to tests/functional/lang/eval-okay-splitversion.nix diff --git a/tests/lang/eval-okay-string.exp b/tests/functional/lang/eval-okay-string.exp similarity index 100% rename from tests/lang/eval-okay-string.exp rename to tests/functional/lang/eval-okay-string.exp diff --git a/tests/lang/eval-okay-string.nix b/tests/functional/lang/eval-okay-string.nix similarity index 100% rename from tests/lang/eval-okay-string.nix rename to tests/functional/lang/eval-okay-string.nix diff --git a/tests/lang/eval-okay-strings-as-attrs-names.exp b/tests/functional/lang/eval-okay-strings-as-attrs-names.exp similarity index 100% rename from tests/lang/eval-okay-strings-as-attrs-names.exp rename to tests/functional/lang/eval-okay-strings-as-attrs-names.exp diff --git a/tests/lang/eval-okay-strings-as-attrs-names.nix b/tests/functional/lang/eval-okay-strings-as-attrs-names.nix similarity index 100% rename from tests/lang/eval-okay-strings-as-attrs-names.nix rename to tests/functional/lang/eval-okay-strings-as-attrs-names.nix diff --git a/tests/lang/eval-okay-substring.exp b/tests/functional/lang/eval-okay-substring.exp similarity index 100% rename from tests/lang/eval-okay-substring.exp rename to tests/functional/lang/eval-okay-substring.exp diff --git a/tests/lang/eval-okay-substring.nix b/tests/functional/lang/eval-okay-substring.nix similarity index 100% rename from tests/lang/eval-okay-substring.nix rename to tests/functional/lang/eval-okay-substring.nix diff --git a/tests/lang/eval-okay-tail-call-1.exp-disabled b/tests/functional/lang/eval-okay-tail-call-1.exp-disabled similarity index 100% rename from tests/lang/eval-okay-tail-call-1.exp-disabled rename to tests/functional/lang/eval-okay-tail-call-1.exp-disabled diff --git a/tests/lang/eval-okay-tail-call-1.nix b/tests/functional/lang/eval-okay-tail-call-1.nix similarity index 100% rename from tests/lang/eval-okay-tail-call-1.nix rename to tests/functional/lang/eval-okay-tail-call-1.nix diff --git a/tests/lang/eval-okay-tojson.exp b/tests/functional/lang/eval-okay-tojson.exp similarity index 100% rename from tests/lang/eval-okay-tojson.exp rename to tests/functional/lang/eval-okay-tojson.exp diff --git a/tests/lang/eval-okay-tojson.nix b/tests/functional/lang/eval-okay-tojson.nix similarity index 100% rename from tests/lang/eval-okay-tojson.nix rename to tests/functional/lang/eval-okay-tojson.nix diff --git a/tests/lang/eval-okay-toxml.exp b/tests/functional/lang/eval-okay-toxml.exp similarity index 100% rename from tests/lang/eval-okay-toxml.exp rename to tests/functional/lang/eval-okay-toxml.exp diff --git a/tests/lang/eval-okay-toxml.nix b/tests/functional/lang/eval-okay-toxml.nix similarity index 100% rename from tests/lang/eval-okay-toxml.nix rename to tests/functional/lang/eval-okay-toxml.nix diff --git a/tests/lang/eval-okay-toxml2.exp b/tests/functional/lang/eval-okay-toxml2.exp similarity index 100% rename from tests/lang/eval-okay-toxml2.exp rename to tests/functional/lang/eval-okay-toxml2.exp diff --git a/tests/lang/eval-okay-toxml2.nix b/tests/functional/lang/eval-okay-toxml2.nix similarity index 100% rename from tests/lang/eval-okay-toxml2.nix rename to tests/functional/lang/eval-okay-toxml2.nix diff --git a/tests/lang/eval-okay-tryeval.exp b/tests/functional/lang/eval-okay-tryeval.exp similarity index 100% rename from tests/lang/eval-okay-tryeval.exp rename to tests/functional/lang/eval-okay-tryeval.exp diff --git a/tests/lang/eval-okay-tryeval.nix b/tests/functional/lang/eval-okay-tryeval.nix similarity index 100% rename from tests/lang/eval-okay-tryeval.nix rename to tests/functional/lang/eval-okay-tryeval.nix diff --git a/tests/lang/eval-okay-types.exp b/tests/functional/lang/eval-okay-types.exp similarity index 100% rename from tests/lang/eval-okay-types.exp rename to tests/functional/lang/eval-okay-types.exp diff --git a/tests/lang/eval-okay-types.nix b/tests/functional/lang/eval-okay-types.nix similarity index 100% rename from tests/lang/eval-okay-types.nix rename to tests/functional/lang/eval-okay-types.nix diff --git a/tests/lang/eval-okay-versions.exp b/tests/functional/lang/eval-okay-versions.exp similarity index 100% rename from tests/lang/eval-okay-versions.exp rename to tests/functional/lang/eval-okay-versions.exp diff --git a/tests/lang/eval-okay-versions.nix b/tests/functional/lang/eval-okay-versions.nix similarity index 100% rename from tests/lang/eval-okay-versions.nix rename to tests/functional/lang/eval-okay-versions.nix diff --git a/tests/lang/eval-okay-with.exp b/tests/functional/lang/eval-okay-with.exp similarity index 100% rename from tests/lang/eval-okay-with.exp rename to tests/functional/lang/eval-okay-with.exp diff --git a/tests/lang/eval-okay-with.nix b/tests/functional/lang/eval-okay-with.nix similarity index 100% rename from tests/lang/eval-okay-with.nix rename to tests/functional/lang/eval-okay-with.nix diff --git a/tests/lang/eval-okay-xml.exp.xml b/tests/functional/lang/eval-okay-xml.exp.xml similarity index 100% rename from tests/lang/eval-okay-xml.exp.xml rename to tests/functional/lang/eval-okay-xml.exp.xml diff --git a/tests/lang/eval-okay-xml.nix b/tests/functional/lang/eval-okay-xml.nix similarity index 100% rename from tests/lang/eval-okay-xml.nix rename to tests/functional/lang/eval-okay-xml.nix diff --git a/tests/lang/eval-okay-zipAttrsWith.exp b/tests/functional/lang/eval-okay-zipAttrsWith.exp similarity index 100% rename from tests/lang/eval-okay-zipAttrsWith.exp rename to tests/functional/lang/eval-okay-zipAttrsWith.exp diff --git a/tests/lang/eval-okay-zipAttrsWith.nix b/tests/functional/lang/eval-okay-zipAttrsWith.nix similarity index 100% rename from tests/lang/eval-okay-zipAttrsWith.nix rename to tests/functional/lang/eval-okay-zipAttrsWith.nix diff --git a/tests/lang/framework.sh b/tests/functional/lang/framework.sh similarity index 100% rename from tests/lang/framework.sh rename to tests/functional/lang/framework.sh diff --git a/tests/lang/imported.nix b/tests/functional/lang/imported.nix similarity index 100% rename from tests/lang/imported.nix rename to tests/functional/lang/imported.nix diff --git a/tests/lang/imported2.nix b/tests/functional/lang/imported2.nix similarity index 100% rename from tests/lang/imported2.nix rename to tests/functional/lang/imported2.nix diff --git a/tests/lang/lib.nix b/tests/functional/lang/lib.nix similarity index 100% rename from tests/lang/lib.nix rename to tests/functional/lang/lib.nix diff --git a/tests/lang/parse-fail-dup-attrs-1.err.exp b/tests/functional/lang/parse-fail-dup-attrs-1.err.exp similarity index 100% rename from tests/lang/parse-fail-dup-attrs-1.err.exp rename to tests/functional/lang/parse-fail-dup-attrs-1.err.exp diff --git a/tests/lang/parse-fail-dup-attrs-1.nix b/tests/functional/lang/parse-fail-dup-attrs-1.nix similarity index 100% rename from tests/lang/parse-fail-dup-attrs-1.nix rename to tests/functional/lang/parse-fail-dup-attrs-1.nix diff --git a/tests/lang/parse-fail-dup-attrs-2.err.exp b/tests/functional/lang/parse-fail-dup-attrs-2.err.exp similarity index 100% rename from tests/lang/parse-fail-dup-attrs-2.err.exp rename to tests/functional/lang/parse-fail-dup-attrs-2.err.exp diff --git a/tests/lang/parse-fail-dup-attrs-2.nix b/tests/functional/lang/parse-fail-dup-attrs-2.nix similarity index 100% rename from tests/lang/parse-fail-dup-attrs-2.nix rename to tests/functional/lang/parse-fail-dup-attrs-2.nix diff --git a/tests/lang/parse-fail-dup-attrs-3.err.exp b/tests/functional/lang/parse-fail-dup-attrs-3.err.exp similarity index 100% rename from tests/lang/parse-fail-dup-attrs-3.err.exp rename to tests/functional/lang/parse-fail-dup-attrs-3.err.exp diff --git a/tests/lang/parse-fail-dup-attrs-3.nix b/tests/functional/lang/parse-fail-dup-attrs-3.nix similarity index 100% rename from tests/lang/parse-fail-dup-attrs-3.nix rename to tests/functional/lang/parse-fail-dup-attrs-3.nix diff --git a/tests/lang/parse-fail-dup-attrs-4.err.exp b/tests/functional/lang/parse-fail-dup-attrs-4.err.exp similarity index 100% rename from tests/lang/parse-fail-dup-attrs-4.err.exp rename to tests/functional/lang/parse-fail-dup-attrs-4.err.exp diff --git a/tests/lang/parse-fail-dup-attrs-4.nix b/tests/functional/lang/parse-fail-dup-attrs-4.nix similarity index 100% rename from tests/lang/parse-fail-dup-attrs-4.nix rename to tests/functional/lang/parse-fail-dup-attrs-4.nix diff --git a/tests/lang/parse-fail-dup-attrs-6.err.exp b/tests/functional/lang/parse-fail-dup-attrs-6.err.exp similarity index 100% rename from tests/lang/parse-fail-dup-attrs-6.err.exp rename to tests/functional/lang/parse-fail-dup-attrs-6.err.exp diff --git a/tests/lang/parse-fail-dup-attrs-7.err.exp b/tests/functional/lang/parse-fail-dup-attrs-7.err.exp similarity index 100% rename from tests/lang/parse-fail-dup-attrs-7.err.exp rename to tests/functional/lang/parse-fail-dup-attrs-7.err.exp diff --git a/tests/lang/parse-fail-dup-attrs-7.nix b/tests/functional/lang/parse-fail-dup-attrs-7.nix similarity index 100% rename from tests/lang/parse-fail-dup-attrs-7.nix rename to tests/functional/lang/parse-fail-dup-attrs-7.nix diff --git a/tests/lang/parse-fail-dup-formals.err.exp b/tests/functional/lang/parse-fail-dup-formals.err.exp similarity index 100% rename from tests/lang/parse-fail-dup-formals.err.exp rename to tests/functional/lang/parse-fail-dup-formals.err.exp diff --git a/tests/lang/parse-fail-dup-formals.nix b/tests/functional/lang/parse-fail-dup-formals.nix similarity index 100% rename from tests/lang/parse-fail-dup-formals.nix rename to tests/functional/lang/parse-fail-dup-formals.nix diff --git a/tests/lang/parse-fail-eof-in-string.err.exp b/tests/functional/lang/parse-fail-eof-in-string.err.exp similarity index 100% rename from tests/lang/parse-fail-eof-in-string.err.exp rename to tests/functional/lang/parse-fail-eof-in-string.err.exp diff --git a/tests/lang/parse-fail-eof-in-string.nix b/tests/functional/lang/parse-fail-eof-in-string.nix similarity index 100% rename from tests/lang/parse-fail-eof-in-string.nix rename to tests/functional/lang/parse-fail-eof-in-string.nix diff --git a/tests/lang/parse-fail-mixed-nested-attrs1.err.exp b/tests/functional/lang/parse-fail-mixed-nested-attrs1.err.exp similarity index 100% rename from tests/lang/parse-fail-mixed-nested-attrs1.err.exp rename to tests/functional/lang/parse-fail-mixed-nested-attrs1.err.exp diff --git a/tests/lang/parse-fail-mixed-nested-attrs1.nix b/tests/functional/lang/parse-fail-mixed-nested-attrs1.nix similarity index 100% rename from tests/lang/parse-fail-mixed-nested-attrs1.nix rename to tests/functional/lang/parse-fail-mixed-nested-attrs1.nix diff --git a/tests/lang/parse-fail-mixed-nested-attrs2.err.exp b/tests/functional/lang/parse-fail-mixed-nested-attrs2.err.exp similarity index 100% rename from tests/lang/parse-fail-mixed-nested-attrs2.err.exp rename to tests/functional/lang/parse-fail-mixed-nested-attrs2.err.exp diff --git a/tests/lang/parse-fail-mixed-nested-attrs2.nix b/tests/functional/lang/parse-fail-mixed-nested-attrs2.nix similarity index 100% rename from tests/lang/parse-fail-mixed-nested-attrs2.nix rename to tests/functional/lang/parse-fail-mixed-nested-attrs2.nix diff --git a/tests/lang/parse-fail-patterns-1.err.exp b/tests/functional/lang/parse-fail-patterns-1.err.exp similarity index 100% rename from tests/lang/parse-fail-patterns-1.err.exp rename to tests/functional/lang/parse-fail-patterns-1.err.exp diff --git a/tests/lang/parse-fail-patterns-1.nix b/tests/functional/lang/parse-fail-patterns-1.nix similarity index 100% rename from tests/lang/parse-fail-patterns-1.nix rename to tests/functional/lang/parse-fail-patterns-1.nix diff --git a/tests/lang/parse-fail-regression-20060610.err.exp b/tests/functional/lang/parse-fail-regression-20060610.err.exp similarity index 100% rename from tests/lang/parse-fail-regression-20060610.err.exp rename to tests/functional/lang/parse-fail-regression-20060610.err.exp diff --git a/tests/lang/parse-fail-regression-20060610.nix b/tests/functional/lang/parse-fail-regression-20060610.nix similarity index 100% rename from tests/lang/parse-fail-regression-20060610.nix rename to tests/functional/lang/parse-fail-regression-20060610.nix diff --git a/tests/lang/parse-fail-undef-var-2.err.exp b/tests/functional/lang/parse-fail-undef-var-2.err.exp similarity index 100% rename from tests/lang/parse-fail-undef-var-2.err.exp rename to tests/functional/lang/parse-fail-undef-var-2.err.exp diff --git a/tests/lang/parse-fail-undef-var-2.nix b/tests/functional/lang/parse-fail-undef-var-2.nix similarity index 100% rename from tests/lang/parse-fail-undef-var-2.nix rename to tests/functional/lang/parse-fail-undef-var-2.nix diff --git a/tests/lang/parse-fail-undef-var.err.exp b/tests/functional/lang/parse-fail-undef-var.err.exp similarity index 100% rename from tests/lang/parse-fail-undef-var.err.exp rename to tests/functional/lang/parse-fail-undef-var.err.exp diff --git a/tests/lang/parse-fail-undef-var.nix b/tests/functional/lang/parse-fail-undef-var.nix similarity index 100% rename from tests/lang/parse-fail-undef-var.nix rename to tests/functional/lang/parse-fail-undef-var.nix diff --git a/tests/lang/parse-fail-utf8.err.exp b/tests/functional/lang/parse-fail-utf8.err.exp similarity index 100% rename from tests/lang/parse-fail-utf8.err.exp rename to tests/functional/lang/parse-fail-utf8.err.exp diff --git a/tests/lang/parse-fail-utf8.nix b/tests/functional/lang/parse-fail-utf8.nix similarity index 100% rename from tests/lang/parse-fail-utf8.nix rename to tests/functional/lang/parse-fail-utf8.nix diff --git a/tests/lang/parse-okay-1.exp b/tests/functional/lang/parse-okay-1.exp similarity index 100% rename from tests/lang/parse-okay-1.exp rename to tests/functional/lang/parse-okay-1.exp diff --git a/tests/lang/parse-okay-1.nix b/tests/functional/lang/parse-okay-1.nix similarity index 100% rename from tests/lang/parse-okay-1.nix rename to tests/functional/lang/parse-okay-1.nix diff --git a/tests/lang/parse-okay-crlf.exp b/tests/functional/lang/parse-okay-crlf.exp similarity index 100% rename from tests/lang/parse-okay-crlf.exp rename to tests/functional/lang/parse-okay-crlf.exp diff --git a/tests/lang/parse-okay-crlf.nix b/tests/functional/lang/parse-okay-crlf.nix similarity index 100% rename from tests/lang/parse-okay-crlf.nix rename to tests/functional/lang/parse-okay-crlf.nix diff --git a/tests/lang/parse-okay-dup-attrs-5.exp b/tests/functional/lang/parse-okay-dup-attrs-5.exp similarity index 100% rename from tests/lang/parse-okay-dup-attrs-5.exp rename to tests/functional/lang/parse-okay-dup-attrs-5.exp diff --git a/tests/lang/parse-okay-dup-attrs-5.nix b/tests/functional/lang/parse-okay-dup-attrs-5.nix similarity index 100% rename from tests/lang/parse-okay-dup-attrs-5.nix rename to tests/functional/lang/parse-okay-dup-attrs-5.nix diff --git a/tests/lang/parse-okay-dup-attrs-6.exp b/tests/functional/lang/parse-okay-dup-attrs-6.exp similarity index 100% rename from tests/lang/parse-okay-dup-attrs-6.exp rename to tests/functional/lang/parse-okay-dup-attrs-6.exp diff --git a/tests/lang/parse-okay-dup-attrs-6.nix b/tests/functional/lang/parse-okay-dup-attrs-6.nix similarity index 100% rename from tests/lang/parse-okay-dup-attrs-6.nix rename to tests/functional/lang/parse-okay-dup-attrs-6.nix diff --git a/tests/lang/parse-okay-mixed-nested-attrs-1.exp b/tests/functional/lang/parse-okay-mixed-nested-attrs-1.exp similarity index 100% rename from tests/lang/parse-okay-mixed-nested-attrs-1.exp rename to tests/functional/lang/parse-okay-mixed-nested-attrs-1.exp diff --git a/tests/lang/parse-okay-mixed-nested-attrs-1.nix b/tests/functional/lang/parse-okay-mixed-nested-attrs-1.nix similarity index 100% rename from tests/lang/parse-okay-mixed-nested-attrs-1.nix rename to tests/functional/lang/parse-okay-mixed-nested-attrs-1.nix diff --git a/tests/lang/parse-okay-mixed-nested-attrs-2.exp b/tests/functional/lang/parse-okay-mixed-nested-attrs-2.exp similarity index 100% rename from tests/lang/parse-okay-mixed-nested-attrs-2.exp rename to tests/functional/lang/parse-okay-mixed-nested-attrs-2.exp diff --git a/tests/lang/parse-okay-mixed-nested-attrs-2.nix b/tests/functional/lang/parse-okay-mixed-nested-attrs-2.nix similarity index 100% rename from tests/lang/parse-okay-mixed-nested-attrs-2.nix rename to tests/functional/lang/parse-okay-mixed-nested-attrs-2.nix diff --git a/tests/lang/parse-okay-mixed-nested-attrs-3.exp b/tests/functional/lang/parse-okay-mixed-nested-attrs-3.exp similarity index 100% rename from tests/lang/parse-okay-mixed-nested-attrs-3.exp rename to tests/functional/lang/parse-okay-mixed-nested-attrs-3.exp diff --git a/tests/lang/parse-okay-mixed-nested-attrs-3.nix b/tests/functional/lang/parse-okay-mixed-nested-attrs-3.nix similarity index 100% rename from tests/lang/parse-okay-mixed-nested-attrs-3.nix rename to tests/functional/lang/parse-okay-mixed-nested-attrs-3.nix diff --git a/tests/lang/parse-okay-regression-20041027.exp b/tests/functional/lang/parse-okay-regression-20041027.exp similarity index 100% rename from tests/lang/parse-okay-regression-20041027.exp rename to tests/functional/lang/parse-okay-regression-20041027.exp diff --git a/tests/lang/parse-okay-regression-20041027.nix b/tests/functional/lang/parse-okay-regression-20041027.nix similarity index 100% rename from tests/lang/parse-okay-regression-20041027.nix rename to tests/functional/lang/parse-okay-regression-20041027.nix diff --git a/tests/lang/parse-okay-regression-751.exp b/tests/functional/lang/parse-okay-regression-751.exp similarity index 100% rename from tests/lang/parse-okay-regression-751.exp rename to tests/functional/lang/parse-okay-regression-751.exp diff --git a/tests/lang/parse-okay-regression-751.nix b/tests/functional/lang/parse-okay-regression-751.nix similarity index 100% rename from tests/lang/parse-okay-regression-751.nix rename to tests/functional/lang/parse-okay-regression-751.nix diff --git a/tests/lang/parse-okay-subversion.exp b/tests/functional/lang/parse-okay-subversion.exp similarity index 100% rename from tests/lang/parse-okay-subversion.exp rename to tests/functional/lang/parse-okay-subversion.exp diff --git a/tests/lang/parse-okay-subversion.nix b/tests/functional/lang/parse-okay-subversion.nix similarity index 100% rename from tests/lang/parse-okay-subversion.nix rename to tests/functional/lang/parse-okay-subversion.nix diff --git a/tests/lang/parse-okay-url.exp b/tests/functional/lang/parse-okay-url.exp similarity index 100% rename from tests/lang/parse-okay-url.exp rename to tests/functional/lang/parse-okay-url.exp diff --git a/tests/lang/parse-okay-url.nix b/tests/functional/lang/parse-okay-url.nix similarity index 100% rename from tests/lang/parse-okay-url.nix rename to tests/functional/lang/parse-okay-url.nix diff --git a/tests/lang/readDir/bar b/tests/functional/lang/readDir/bar similarity index 100% rename from tests/lang/readDir/bar rename to tests/functional/lang/readDir/bar diff --git a/tests/lang/readDir/foo/git-hates-directories b/tests/functional/lang/readDir/foo/git-hates-directories similarity index 100% rename from tests/lang/readDir/foo/git-hates-directories rename to tests/functional/lang/readDir/foo/git-hates-directories diff --git a/tests/lang/readDir/ldir b/tests/functional/lang/readDir/ldir similarity index 100% rename from tests/lang/readDir/ldir rename to tests/functional/lang/readDir/ldir diff --git a/tests/lang/readDir/linked b/tests/functional/lang/readDir/linked similarity index 100% rename from tests/lang/readDir/linked rename to tests/functional/lang/readDir/linked diff --git a/tests/legacy-ssh-store.sh b/tests/functional/legacy-ssh-store.sh similarity index 100% rename from tests/legacy-ssh-store.sh rename to tests/functional/legacy-ssh-store.sh diff --git a/tests/linux-sandbox-cert-test.nix b/tests/functional/linux-sandbox-cert-test.nix similarity index 100% rename from tests/linux-sandbox-cert-test.nix rename to tests/functional/linux-sandbox-cert-test.nix diff --git a/tests/linux-sandbox.sh b/tests/functional/linux-sandbox.sh similarity index 100% rename from tests/linux-sandbox.sh rename to tests/functional/linux-sandbox.sh diff --git a/tests/local-store.sh b/tests/functional/local-store.sh similarity index 100% rename from tests/local-store.sh rename to tests/functional/local-store.sh diff --git a/tests/local.mk b/tests/functional/local.mk similarity index 90% rename from tests/local.mk rename to tests/functional/local.mk index ac418dc0d..4d29f60a1 100644 --- a/tests/local.mk +++ b/tests/functional/local.mk @@ -139,15 +139,16 @@ ifeq ($(HAVE_LOCAL_NIX_BUILD), 1) endif endif -tests/test-libstoreconsumer.sh.test: tests/test-libstoreconsumer/test-libstoreconsumer -tests/plugins.sh: tests/plugins/libplugintest.$(SO_EXT) +$(d)/test-libstoreconsumer.sh.test $(d)/test-libstoreconsumer.sh.test-debug: \ + $(d)/test-libstoreconsumer/test-libstoreconsumer +$(d)/plugins.sh.test $(d)/plugins.sh.test-debug: \ + $(d)/plugins/libplugintest.$(SO_EXT) install-tests += $(foreach x, $(nix_tests), $(d)/$(x)) -clean-files += \ +test-clean-files := \ $(d)/common/vars-and-functions.sh \ $(d)/config.nix -test-deps += \ - tests/common/vars-and-functions.sh \ - tests/config.nix +clean-files += $(test-clean-files) +test-deps += $(test-clean-files) diff --git a/tests/logging.sh b/tests/functional/logging.sh similarity index 100% rename from tests/logging.sh rename to tests/functional/logging.sh diff --git a/tests/misc.sh b/tests/functional/misc.sh similarity index 100% rename from tests/misc.sh rename to tests/functional/misc.sh diff --git a/tests/multiple-outputs.nix b/tests/functional/multiple-outputs.nix similarity index 100% rename from tests/multiple-outputs.nix rename to tests/functional/multiple-outputs.nix diff --git a/tests/multiple-outputs.sh b/tests/functional/multiple-outputs.sh similarity index 100% rename from tests/multiple-outputs.sh rename to tests/functional/multiple-outputs.sh diff --git a/tests/nar-access.nix b/tests/functional/nar-access.nix similarity index 100% rename from tests/nar-access.nix rename to tests/functional/nar-access.nix diff --git a/tests/nar-access.sh b/tests/functional/nar-access.sh similarity index 100% rename from tests/nar-access.sh rename to tests/functional/nar-access.sh diff --git a/tests/nested-sandboxing.sh b/tests/functional/nested-sandboxing.sh similarity index 75% rename from tests/nested-sandboxing.sh rename to tests/functional/nested-sandboxing.sh index d9fa788aa..61fe043c6 100644 --- a/tests/nested-sandboxing.sh +++ b/tests/functional/nested-sandboxing.sh @@ -1,5 +1,5 @@ source common.sh -# This test is run by `tests/nested-sandboxing/runner.nix` in an extra layer of sandboxing. +# This test is run by `tests/functional/nested-sandboxing/runner.nix` in an extra layer of sandboxing. [[ -d /nix/store ]] || skipTest "running this test without Nix's deps being drawn from /nix/store is not yet supported" requireSandboxSupport diff --git a/tests/nested-sandboxing/command.sh b/tests/functional/nested-sandboxing/command.sh similarity index 100% rename from tests/nested-sandboxing/command.sh rename to tests/functional/nested-sandboxing/command.sh diff --git a/tests/nested-sandboxing/runner.nix b/tests/functional/nested-sandboxing/runner.nix similarity index 100% rename from tests/nested-sandboxing/runner.nix rename to tests/functional/nested-sandboxing/runner.nix diff --git a/tests/nix-build-examples.nix b/tests/functional/nix-build-examples.nix similarity index 100% rename from tests/nix-build-examples.nix rename to tests/functional/nix-build-examples.nix diff --git a/tests/nix-build.sh b/tests/functional/nix-build.sh similarity index 100% rename from tests/nix-build.sh rename to tests/functional/nix-build.sh diff --git a/tests/nix-channel.sh b/tests/functional/nix-channel.sh similarity index 100% rename from tests/nix-channel.sh rename to tests/functional/nix-channel.sh diff --git a/tests/nix-collect-garbage-d.sh b/tests/functional/nix-collect-garbage-d.sh similarity index 100% rename from tests/nix-collect-garbage-d.sh rename to tests/functional/nix-collect-garbage-d.sh diff --git a/tests/nix-copy-ssh-ng.sh b/tests/functional/nix-copy-ssh-ng.sh similarity index 100% rename from tests/nix-copy-ssh-ng.sh rename to tests/functional/nix-copy-ssh-ng.sh diff --git a/tests/nix-copy-ssh.sh b/tests/functional/nix-copy-ssh.sh similarity index 100% rename from tests/nix-copy-ssh.sh rename to tests/functional/nix-copy-ssh.sh diff --git a/tests/nix-daemon-untrusting.sh b/tests/functional/nix-daemon-untrusting.sh similarity index 100% rename from tests/nix-daemon-untrusting.sh rename to tests/functional/nix-daemon-untrusting.sh diff --git a/tests/nix-profile.sh b/tests/functional/nix-profile.sh similarity index 100% rename from tests/nix-profile.sh rename to tests/functional/nix-profile.sh diff --git a/tests/nix-shell.sh b/tests/functional/nix-shell.sh similarity index 100% rename from tests/nix-shell.sh rename to tests/functional/nix-shell.sh diff --git a/tests/nix_path.sh b/tests/functional/nix_path.sh similarity index 100% rename from tests/nix_path.sh rename to tests/functional/nix_path.sh diff --git a/tests/optimise-store.sh b/tests/functional/optimise-store.sh similarity index 100% rename from tests/optimise-store.sh rename to tests/functional/optimise-store.sh diff --git a/tests/output-normalization.sh b/tests/functional/output-normalization.sh similarity index 100% rename from tests/output-normalization.sh rename to tests/functional/output-normalization.sh diff --git a/tests/parallel.builder.sh b/tests/functional/parallel.builder.sh similarity index 100% rename from tests/parallel.builder.sh rename to tests/functional/parallel.builder.sh diff --git a/tests/parallel.nix b/tests/functional/parallel.nix similarity index 100% rename from tests/parallel.nix rename to tests/functional/parallel.nix diff --git a/tests/parallel.sh b/tests/functional/parallel.sh similarity index 100% rename from tests/parallel.sh rename to tests/functional/parallel.sh diff --git a/tests/pass-as-file.sh b/tests/functional/pass-as-file.sh similarity index 100% rename from tests/pass-as-file.sh rename to tests/functional/pass-as-file.sh diff --git a/tests/path-from-hash-part.sh b/tests/functional/path-from-hash-part.sh similarity index 100% rename from tests/path-from-hash-part.sh rename to tests/functional/path-from-hash-part.sh diff --git a/tests/path.nix b/tests/functional/path.nix similarity index 100% rename from tests/path.nix rename to tests/functional/path.nix diff --git a/tests/placeholders.sh b/tests/functional/placeholders.sh similarity index 100% rename from tests/placeholders.sh rename to tests/functional/placeholders.sh diff --git a/tests/plugins.sh b/tests/functional/plugins.sh similarity index 100% rename from tests/plugins.sh rename to tests/functional/plugins.sh diff --git a/tests/plugins/local.mk b/tests/functional/plugins/local.mk similarity index 100% rename from tests/plugins/local.mk rename to tests/functional/plugins/local.mk diff --git a/tests/plugins/plugintest.cc b/tests/functional/plugins/plugintest.cc similarity index 100% rename from tests/plugins/plugintest.cc rename to tests/functional/plugins/plugintest.cc diff --git a/tests/post-hook.sh b/tests/functional/post-hook.sh similarity index 100% rename from tests/post-hook.sh rename to tests/functional/post-hook.sh diff --git a/tests/pure-eval.nix b/tests/functional/pure-eval.nix similarity index 100% rename from tests/pure-eval.nix rename to tests/functional/pure-eval.nix diff --git a/tests/pure-eval.sh b/tests/functional/pure-eval.sh similarity index 100% rename from tests/pure-eval.sh rename to tests/functional/pure-eval.sh diff --git a/tests/push-to-store-old.sh b/tests/functional/push-to-store-old.sh similarity index 100% rename from tests/push-to-store-old.sh rename to tests/functional/push-to-store-old.sh diff --git a/tests/push-to-store.sh b/tests/functional/push-to-store.sh similarity index 100% rename from tests/push-to-store.sh rename to tests/functional/push-to-store.sh diff --git a/tests/read-only-store.sh b/tests/functional/read-only-store.sh similarity index 100% rename from tests/read-only-store.sh rename to tests/functional/read-only-store.sh diff --git a/tests/readfile-context.nix b/tests/functional/readfile-context.nix similarity index 100% rename from tests/readfile-context.nix rename to tests/functional/readfile-context.nix diff --git a/tests/readfile-context.sh b/tests/functional/readfile-context.sh similarity index 100% rename from tests/readfile-context.sh rename to tests/functional/readfile-context.sh diff --git a/tests/recursive.nix b/tests/functional/recursive.nix similarity index 100% rename from tests/recursive.nix rename to tests/functional/recursive.nix diff --git a/tests/recursive.sh b/tests/functional/recursive.sh similarity index 100% rename from tests/recursive.sh rename to tests/functional/recursive.sh diff --git a/tests/referrers.sh b/tests/functional/referrers.sh similarity index 100% rename from tests/referrers.sh rename to tests/functional/referrers.sh diff --git a/tests/remote-store.sh b/tests/functional/remote-store.sh similarity index 100% rename from tests/remote-store.sh rename to tests/functional/remote-store.sh diff --git a/tests/repair.sh b/tests/functional/repair.sh similarity index 100% rename from tests/repair.sh rename to tests/functional/repair.sh diff --git a/tests/repl.sh b/tests/functional/repl.sh similarity index 100% rename from tests/repl.sh rename to tests/functional/repl.sh diff --git a/tests/restricted.nix b/tests/functional/restricted.nix similarity index 100% rename from tests/restricted.nix rename to tests/functional/restricted.nix diff --git a/tests/restricted.sh b/tests/functional/restricted.sh similarity index 95% rename from tests/restricted.sh rename to tests/functional/restricted.sh index 17f310a4b..197ae7a10 100644 --- a/tests/restricted.sh +++ b/tests/functional/restricted.sh @@ -9,10 +9,10 @@ nix-instantiate --restrict-eval ./simple.nix -I src=. nix-instantiate --restrict-eval ./simple.nix -I src1=simple.nix -I src2=config.nix -I src3=./simple.builder.sh (! nix-instantiate --restrict-eval --eval -E 'builtins.readFile ./simple.nix') -nix-instantiate --restrict-eval --eval -E 'builtins.readFile ./simple.nix' -I src=.. +nix-instantiate --restrict-eval --eval -E 'builtins.readFile ./simple.nix' -I src=../.. -(! nix-instantiate --restrict-eval --eval -E 'builtins.readDir ../src/nix-channel') -nix-instantiate --restrict-eval --eval -E 'builtins.readDir ../src/nix-channel' -I src=../src +(! nix-instantiate --restrict-eval --eval -E 'builtins.readDir ../../src/nix-channel') +nix-instantiate --restrict-eval --eval -E 'builtins.readDir ../../src/nix-channel' -I src=../../src (! nix-instantiate --restrict-eval --eval -E 'let __nixPath = [ { prefix = "foo"; path = ./.; } ]; in ') nix-instantiate --restrict-eval --eval -E 'let __nixPath = [ { prefix = "foo"; path = ./.; } ]; in ' -I src=. diff --git a/tests/search.nix b/tests/functional/search.nix similarity index 100% rename from tests/search.nix rename to tests/functional/search.nix diff --git a/tests/search.sh b/tests/functional/search.sh similarity index 100% rename from tests/search.sh rename to tests/functional/search.sh diff --git a/tests/secure-drv-outputs.nix b/tests/functional/secure-drv-outputs.nix similarity index 100% rename from tests/secure-drv-outputs.nix rename to tests/functional/secure-drv-outputs.nix diff --git a/tests/secure-drv-outputs.sh b/tests/functional/secure-drv-outputs.sh similarity index 100% rename from tests/secure-drv-outputs.sh rename to tests/functional/secure-drv-outputs.sh diff --git a/tests/selfref-gc.sh b/tests/functional/selfref-gc.sh similarity index 100% rename from tests/selfref-gc.sh rename to tests/functional/selfref-gc.sh diff --git a/tests/shell-hello.nix b/tests/functional/shell-hello.nix similarity index 100% rename from tests/shell-hello.nix rename to tests/functional/shell-hello.nix diff --git a/tests/shell.nix b/tests/functional/shell.nix similarity index 100% rename from tests/shell.nix rename to tests/functional/shell.nix diff --git a/tests/shell.sh b/tests/functional/shell.sh similarity index 100% rename from tests/shell.sh rename to tests/functional/shell.sh diff --git a/tests/shell.shebang.rb b/tests/functional/shell.shebang.rb similarity index 100% rename from tests/shell.shebang.rb rename to tests/functional/shell.shebang.rb diff --git a/tests/shell.shebang.sh b/tests/functional/shell.shebang.sh similarity index 100% rename from tests/shell.shebang.sh rename to tests/functional/shell.shebang.sh diff --git a/tests/signing.sh b/tests/functional/signing.sh similarity index 100% rename from tests/signing.sh rename to tests/functional/signing.sh diff --git a/tests/simple-failing.nix b/tests/functional/simple-failing.nix similarity index 100% rename from tests/simple-failing.nix rename to tests/functional/simple-failing.nix diff --git a/tests/simple.builder.sh b/tests/functional/simple.builder.sh similarity index 100% rename from tests/simple.builder.sh rename to tests/functional/simple.builder.sh diff --git a/tests/simple.nix b/tests/functional/simple.nix similarity index 100% rename from tests/simple.nix rename to tests/functional/simple.nix diff --git a/tests/simple.sh b/tests/functional/simple.sh similarity index 100% rename from tests/simple.sh rename to tests/functional/simple.sh diff --git a/tests/ssh-relay.sh b/tests/functional/ssh-relay.sh similarity index 100% rename from tests/ssh-relay.sh rename to tests/functional/ssh-relay.sh diff --git a/tests/store-ping.sh b/tests/functional/store-ping.sh similarity index 100% rename from tests/store-ping.sh rename to tests/functional/store-ping.sh diff --git a/tests/structured-attrs-shell.nix b/tests/functional/structured-attrs-shell.nix similarity index 100% rename from tests/structured-attrs-shell.nix rename to tests/functional/structured-attrs-shell.nix diff --git a/tests/structured-attrs.nix b/tests/functional/structured-attrs.nix similarity index 100% rename from tests/structured-attrs.nix rename to tests/functional/structured-attrs.nix diff --git a/tests/structured-attrs.sh b/tests/functional/structured-attrs.sh similarity index 100% rename from tests/structured-attrs.sh rename to tests/functional/structured-attrs.sh diff --git a/tests/substitute-with-invalid-ca.sh b/tests/functional/substitute-with-invalid-ca.sh similarity index 100% rename from tests/substitute-with-invalid-ca.sh rename to tests/functional/substitute-with-invalid-ca.sh diff --git a/tests/suggestions.sh b/tests/functional/suggestions.sh similarity index 100% rename from tests/suggestions.sh rename to tests/functional/suggestions.sh diff --git a/tests/supplementary-groups.sh b/tests/functional/supplementary-groups.sh similarity index 100% rename from tests/supplementary-groups.sh rename to tests/functional/supplementary-groups.sh diff --git a/tests/tarball.sh b/tests/functional/tarball.sh similarity index 100% rename from tests/tarball.sh rename to tests/functional/tarball.sh diff --git a/tests/test-infra.sh b/tests/functional/test-infra.sh similarity index 100% rename from tests/test-infra.sh rename to tests/functional/test-infra.sh diff --git a/tests/test-libstoreconsumer.sh b/tests/functional/test-libstoreconsumer.sh similarity index 100% rename from tests/test-libstoreconsumer.sh rename to tests/functional/test-libstoreconsumer.sh diff --git a/tests/test-libstoreconsumer/README.md b/tests/functional/test-libstoreconsumer/README.md similarity index 100% rename from tests/test-libstoreconsumer/README.md rename to tests/functional/test-libstoreconsumer/README.md diff --git a/tests/test-libstoreconsumer/local.mk b/tests/functional/test-libstoreconsumer/local.mk similarity index 100% rename from tests/test-libstoreconsumer/local.mk rename to tests/functional/test-libstoreconsumer/local.mk diff --git a/tests/test-libstoreconsumer/main.cc b/tests/functional/test-libstoreconsumer/main.cc similarity index 100% rename from tests/test-libstoreconsumer/main.cc rename to tests/functional/test-libstoreconsumer/main.cc diff --git a/tests/timeout.nix b/tests/functional/timeout.nix similarity index 100% rename from tests/timeout.nix rename to tests/functional/timeout.nix diff --git a/tests/timeout.sh b/tests/functional/timeout.sh similarity index 100% rename from tests/timeout.sh rename to tests/functional/timeout.sh diff --git a/tests/toString-path.sh b/tests/functional/toString-path.sh similarity index 100% rename from tests/toString-path.sh rename to tests/functional/toString-path.sh diff --git a/tests/undefined-variable.nix b/tests/functional/undefined-variable.nix similarity index 100% rename from tests/undefined-variable.nix rename to tests/functional/undefined-variable.nix diff --git a/tests/user-envs-migration.sh b/tests/functional/user-envs-migration.sh similarity index 100% rename from tests/user-envs-migration.sh rename to tests/functional/user-envs-migration.sh diff --git a/tests/user-envs.builder.sh b/tests/functional/user-envs.builder.sh similarity index 100% rename from tests/user-envs.builder.sh rename to tests/functional/user-envs.builder.sh diff --git a/tests/user-envs.nix b/tests/functional/user-envs.nix similarity index 100% rename from tests/user-envs.nix rename to tests/functional/user-envs.nix diff --git a/tests/user-envs.sh b/tests/functional/user-envs.sh similarity index 100% rename from tests/user-envs.sh rename to tests/functional/user-envs.sh diff --git a/tests/why-depends.sh b/tests/functional/why-depends.sh similarity index 100% rename from tests/why-depends.sh rename to tests/functional/why-depends.sh diff --git a/tests/zstd.sh b/tests/functional/zstd.sh similarity index 100% rename from tests/zstd.sh rename to tests/functional/zstd.sh From 644ebaab5f96089bc3a8fd3873e8e7f2131a9074 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 6 Oct 2023 10:53:01 -0400 Subject: [PATCH 097/116] Define NixOS tests in `tests/nixos/default.nix` rather than `flake.nix` I think the our `flake.nix` is currently too large and too scary looking. I think this matters --- if Nix cannot dog-food itself in a way that is elegant, why should other people have confidence that their own code can be elegant and easy to maintain? We could do this at many points in time, but I think around now, when we are thinking about stabilizing parts of Flakes, is an especially good time. This is a first step to make the `flake.nix` smaller, and make individual components responsible for their own packaging. I hope we can do this many more follow-ups like it, until the top-level `flake.nix` is very small and just coordinates between other things. --- flake.nix | 74 ++++++++++++----------------------------- tests/nixos/default.nix | 41 +++++++++++++++++++++++ 2 files changed, 62 insertions(+), 53 deletions(-) create mode 100644 tests/nixos/default.nix diff --git a/flake.nix b/flake.nix index c331b2651..bec62fb95 100644 --- a/flake.nix +++ b/flake.nix @@ -509,18 +509,6 @@ }; }; - nixos-lib = import (nixpkgs + "/nixos/lib") { }; - - # https://nixos.org/manual/nixos/unstable/index.html#sec-calling-nixos-tests - runNixOSTestFor = system: test: nixos-lib.runTest { - imports = [ test ]; - hostPkgs = nixpkgsFor.${system}.native; - defaults = { - nixpkgs.pkgs = nixpkgsFor.${system}.native; - }; - _module.args.nixpkgs = nixpkgs; - }; - in { # A Nixpkgs overlay that overrides the 'nix' and # 'nix.perl-bindings' packages. @@ -627,49 +615,29 @@ }; # System tests. - tests.authorization = runNixOSTestFor "x86_64-linux" ./tests/nixos/authorization.nix; + tests = import ./tests/nixos { inherit lib nixpkgs nixpkgsFor; } // { - tests.remoteBuilds = runNixOSTestFor "x86_64-linux" ./tests/nixos/remote-builds.nix; + # Make sure that nix-env still produces the exact same result + # on a particular version of Nixpkgs. + evalNixpkgs = + with nixpkgsFor.x86_64-linux.native; + runCommand "eval-nixos" { buildInputs = [ nix ]; } + '' + type -p nix-env + # Note: we're filtering out nixos-install-tools because https://github.com/NixOS/nixpkgs/pull/153594#issuecomment-1020530593. + time nix-env --store dummy:// -f ${nixpkgs-regression} -qaP --drv-path | sort | grep -v nixos-install-tools > packages + [[ $(sha1sum < packages | cut -c1-40) = ff451c521e61e4fe72bdbe2d0ca5d1809affa733 ]] + mkdir $out + ''; - tests.nix-copy-closure = runNixOSTestFor "x86_64-linux" ./tests/nixos/nix-copy-closure.nix; - - tests.nix-copy = runNixOSTestFor "x86_64-linux" ./tests/nixos/nix-copy.nix; - - tests.nssPreload = runNixOSTestFor "x86_64-linux" ./tests/nixos/nss-preload.nix; - - tests.githubFlakes = runNixOSTestFor "x86_64-linux" ./tests/nixos/github-flakes.nix; - - tests.sourcehutFlakes = runNixOSTestFor "x86_64-linux" ./tests/nixos/sourcehut-flakes.nix; - - tests.tarballFlakes = runNixOSTestFor "x86_64-linux" ./tests/nixos/tarball-flakes.nix; - - tests.containers = runNixOSTestFor "x86_64-linux" ./tests/nixos/containers/containers.nix; - - tests.setuid = lib.genAttrs - ["i686-linux" "x86_64-linux"] - (system: runNixOSTestFor system ./tests/nixos/setuid.nix); - - - # Make sure that nix-env still produces the exact same result - # on a particular version of Nixpkgs. - tests.evalNixpkgs = - with nixpkgsFor.x86_64-linux.native; - runCommand "eval-nixos" { buildInputs = [ nix ]; } - '' - type -p nix-env - # Note: we're filtering out nixos-install-tools because https://github.com/NixOS/nixpkgs/pull/153594#issuecomment-1020530593. - time nix-env --store dummy:// -f ${nixpkgs-regression} -qaP --drv-path | sort | grep -v nixos-install-tools > packages - [[ $(sha1sum < packages | cut -c1-40) = ff451c521e61e4fe72bdbe2d0ca5d1809affa733 ]] - mkdir $out - ''; - - tests.nixpkgsLibTests = - forAllSystems (system: - import (nixpkgs + "/lib/tests/release.nix") - { pkgs = nixpkgsFor.${system}.native; - nixVersions = [ self.packages.${system}.nix ]; - } - ); + nixpkgsLibTests = + forAllSystems (system: + import (nixpkgs + "/lib/tests/release.nix") + { pkgs = nixpkgsFor.${system}.native; + nixVersions = [ self.packages.${system}.nix ]; + } + ); + }; metrics.nixpkgs = import "${nixpkgs-regression}/pkgs/top-level/metrics.nix" { pkgs = nixpkgsFor.x86_64-linux.native; diff --git a/tests/nixos/default.nix b/tests/nixos/default.nix new file mode 100644 index 000000000..b391d7ef2 --- /dev/null +++ b/tests/nixos/default.nix @@ -0,0 +1,41 @@ +{ lib, nixpkgs, nixpkgsFor }: + +let + + nixos-lib = import (nixpkgs + "/nixos/lib") { }; + + # https://nixos.org/manual/nixos/unstable/index.html#sec-calling-nixos-tests + runNixOSTestFor = system: test: nixos-lib.runTest { + imports = [ test ]; + hostPkgs = nixpkgsFor.${system}.native; + defaults = { + nixpkgs.pkgs = nixpkgsFor.${system}.native; + }; + _module.args.nixpkgs = nixpkgs; + }; + +in + +{ + authorization = runNixOSTestFor "x86_64-linux" ./authorization.nix; + + remoteBuilds = runNixOSTestFor "x86_64-linux" ./remote-builds.nix; + + nix-copy-closure = runNixOSTestFor "x86_64-linux" ./nix-copy-closure.nix; + + nix-copy = runNixOSTestFor "x86_64-linux" ./nix-copy.nix; + + nssPreload = runNixOSTestFor "x86_64-linux" ./nss-preload.nix; + + githubFlakes = runNixOSTestFor "x86_64-linux" ./github-flakes.nix; + + sourcehutFlakes = runNixOSTestFor "x86_64-linux" ./sourcehut-flakes.nix; + + tarballFlakes = runNixOSTestFor "x86_64-linux" ./tarball-flakes.nix; + + containers = runNixOSTestFor "x86_64-linux" ./containers/containers.nix; + + setuid = lib.genAttrs + ["i686-linux" "x86_64-linux"] + (system: runNixOSTestFor system ./setuid.nix); +} From 517c547dec086f6332292332571d05dd1cbb6f4b Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Fri, 6 Oct 2023 23:34:08 +0200 Subject: [PATCH 098/116] remove duplicate redirects entry --- doc/manual/redirects.js | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/manual/redirects.js b/doc/manual/redirects.js index b2fad19bb..9d083a43d 100644 --- a/doc/manual/redirects.js +++ b/doc/manual/redirects.js @@ -336,7 +336,6 @@ const redirects = { "simple-values": "#primitives", "lists": "#list", "strings": "#string", - "lists": "#list", "attribute-sets": "#attribute-set", }, "installation/installing-binary.html": { From a67cee965a72f60da73713f47ee665a9ffe1873d Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Sat, 7 Oct 2023 02:27:06 +0200 Subject: [PATCH 099/116] expand on interpolated expressions --- .../src/language/string-interpolation.md | 120 ++++++++++++++++-- doc/manual/src/language/values.md | 12 +- src/libexpr/primops.cc | 4 +- 3 files changed, 116 insertions(+), 20 deletions(-) diff --git a/doc/manual/src/language/string-interpolation.md b/doc/manual/src/language/string-interpolation.md index ddc6b8230..85e02df37 100644 --- a/doc/manual/src/language/string-interpolation.md +++ b/doc/manual/src/language/string-interpolation.md @@ -1,19 +1,12 @@ # String interpolation -String interpolation is a language feature where a [string], [path], or [attribute name] can contain expressions enclosed in `${ }` (dollar-sign with curly brackets). +String interpolation is a language feature where a [string], [path], or [attribute name][attribute set] can contain expressions enclosed in `${ }` (dollar-sign with curly brackets). -Such a string is an *interpolated string*, and an expression inside is an *interpolated expression*. - -Interpolated expressions must evaluate to one of the following: - -- a [string] -- a [path] -- a [derivation] +Such a construct is called *interpolated string*, and the expression inside is an [interpolated expression](#interpolated-expression). [string]: ./values.md#type-string [path]: ./values.md#type-path -[attribute name]: ./values.md#attribute-set -[derivation]: ../glossary.md#gloss-derivation +[attribute set]: ./values.md#attribute-set ## Examples @@ -80,3 +73,110 @@ let name = "foo"; in ``` { foo = "bar"; } + +# Interpolated expression + +An interpolated expression must evaluate to one of the following: + +- a [string] +- a [path] +- an [attribute set] that has a `__toString` attribute or an `outPath` attribute + + - `__toString` must be a function that takes the attribute set itself and returns a string + - `outPath` must be a string + + This includes [derivations](./derivations.md) or [flake inputs](@docroot@/command-ref/new-cli/nix3-flake.md#flake-inputs) (experimental). + +A string interpolates to itself. + +A path in an interpolated expression is first copied into the Nix store, and the resulting string is the [store path] of the newly created [store object](../glossary.md#gloss-store-object). + +[store path]: ../glossary.md#gloss-store-path + +> **Example** +> +> ```console +> $ mkdir foo +> ``` +> +> Reference the empty directory in an interpolated expression: +> +> ```nix +> "${./foo}" +> ``` +> +> "/nix/store/2hhl2nz5v0khbn06ys82nrk99aa1xxdw-foo" + +A derivation interpolates to the [store path] of its first [output](./derivations.md#attr-outputs). + +> **Example** +> +> ```nix +> let +> pkgs = import {}; +> in +> "${pkgs.hello}" +> ``` +> +> "/nix/store/4xpfqf29z4m8vbhrqcz064wfmb46w5r7-hello-2.12.1" + +An attribute set interpolates to the return value of the function in the `__toString` applied to the attribute set itself. + +> **Example** +> +> ```nix +> let +> a = { +> value = 1; +> __toString = self: toString (self.value + 1); +> }; +> in +> "${a}" +> ``` +> +> "2" + +An attribute set also interpolates to the value of its `outPath` attribute. + +> **Example** +> +> ```nix +> let +> a = { outPath = "foo"; }; +> in +> "${a}" +> ``` +> +> "foo" + +If both `__toString` and `outPath` are present in an attribute set, `__toString` takes precedence. + +> **Example** +> +> ```nix +> let +> a = { __toString = _: "yes"; outPath = throw "no"; }; +> in +> "${a}" +> ``` +> +> "yes" + +If neither is present, an error is thrown. + +> **Example** +> +> ```nix +> let +> a = {}; +> in +> "${a}" +> ``` +> +> error: cannot coerce a set to a string +> +> at «string»:4:2: +> +> 3| in +> 4| "${a}" +> | ^ diff --git a/doc/manual/src/language/values.md b/doc/manual/src/language/values.md index 2ae3e143a..fe0e2e4d5 100644 --- a/doc/manual/src/language/values.md +++ b/doc/manual/src/language/values.md @@ -112,18 +112,16 @@ environment variable `NIX_PATH` will be searched for the given file or directory name. - When an [interpolated string][string interpolation] evaluates to a path, the path is first copied into the Nix store and the resulting string is the [store path] of the newly created [store object]. - - [store path]: ../glossary.md#gloss-store-path - [store object]: ../glossary.md#gloss-store-object - For instance, evaluating `"${./foo.txt}"` will cause `foo.txt` in the current directory to be copied into the Nix store and result in the string `"/nix/store/-foo.txt"`. Note that the Nix language assumes that all input files will remain _unchanged_ while evaluating a Nix expression. For example, assume you used a file path in an interpolated string during a `nix repl` session. - Later in the same session, after having changed the file contents, evaluating the interpolated string with the file path again might not return a new store path, since Nix might not re-read the file contents. + Later in the same session, after having changed the file contents, evaluating the interpolated string with the file path again might not return a new [store path], since Nix might not re-read the file contents. - Paths themselves, except those in angle brackets (`< >`), support [string interpolation]. + [store path]: ../glossary.md#gloss-store-path + + Paths, except those in angle brackets (`< >`), support [string interpolation] and can be used in [interpolated expressions]. + [interpolated expressions]: ./string-interpolation.md#interpolated-expressions At least one slash (`/`) must appear *before* any interpolated expression for the result to be recognized as a path. diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 5de1b2828..1d04aeb2e 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -260,9 +260,7 @@ static RegisterPrimOp primop_import({ .doc = R"( Load, parse and return the Nix expression in the file *path*. - The value *path* can be a path, a string, or an attribute set with an - `__toString` attribute or a `outPath` attribute (as derivations or flake - inputs typically have). + The *path* argument must meet the same criteria as an [interpolated expression](@docroot@/language/string-interpolation.md#interpolated-expression). If *path* is a directory, the file `default.nix` in that directory is loaded. From a7ba8c3f4a69118c4c936fd8fd1415c0446803ca Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Sat, 7 Oct 2023 02:46:59 +0200 Subject: [PATCH 100/116] complete example on attribute name interpolation --- .../src/language/string-interpolation.md | 32 ++++++++++++++----- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/doc/manual/src/language/string-interpolation.md b/doc/manual/src/language/string-interpolation.md index 85e02df37..2e650e348 100644 --- a/doc/manual/src/language/string-interpolation.md +++ b/doc/manual/src/language/string-interpolation.md @@ -63,16 +63,32 @@ you can instead write ### Attribute name -Attribute names can be created dynamically with string interpolation: + -```nix -let name = "foo"; in -{ - ${name} = "bar"; -} -``` +Attribute names can be interpolated strings. - { foo = "bar"; } +> **Example** +> +> ```nix +> let name = "foo"; in +> { ${name} = 123; } +> ``` +> +> { foo = 123; } + +Attributes can be selected with interpolated strings. + +> **Example** +> +> ```nix +> let name = "foo"; in +> { foo = 123; }.${name} +> ``` +> +> 123 # Interpolated expression From a86a3e5e59dd932d80431e9bbbb9ec3f883b2899 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Sat, 7 Oct 2023 03:12:21 +0200 Subject: [PATCH 101/116] document that pure-eval also disables `builtins.nixPath` --- src/libexpr/eval-settings.hh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libexpr/eval-settings.hh b/src/libexpr/eval-settings.hh index 19ba679be..6a5c448b4 100644 --- a/src/libexpr/eval-settings.hh +++ b/src/libexpr/eval-settings.hh @@ -40,7 +40,10 @@ struct EvalSettings : Config Pure evaluation mode ensures that the result of Nix expressions is fully determined by explicitly declared inputs, and not influenced by external state: - Restrict file system and network access to files specified by cryptographic hash - - Disable [`bultins.currentSystem`](@docroot@/language/builtin-constants.md#builtins-currentSystem) and [`builtins.currentTime`](@docroot@/language/builtin-constants.md#builtins-currentTime) + - Disable impure constants: + - [`bultins.currentSystem`](@docroot@/language/builtin-constants.md#builtins-currentSystem) + - [`builtins.currentTime`](@docroot@/language/builtin-constants.md#builtins-currentTime) + - [`builtins.nixPath`](@docroot@/language/builtin-constants.md#builtins-nixPath) )" }; From 2fe1ccf7973891cfbd921279a5f94879b2c7dce3 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Sat, 7 Oct 2023 03:20:20 +0200 Subject: [PATCH 102/116] describe the effect of `restrict-eval` in a more focused manner --- src/libexpr/eval-settings.hh | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/libexpr/eval-settings.hh b/src/libexpr/eval-settings.hh index 19ba679be..2f534f496 100644 --- a/src/libexpr/eval-settings.hh +++ b/src/libexpr/eval-settings.hh @@ -29,10 +29,12 @@ struct EvalSettings : Config this, false, "restrict-eval", R"( If set to `true`, the Nix evaluator will not allow access to any - files outside of the Nix search path (as set via the `NIX_PATH` - environment variable or the `-I` option), or to URIs outside of - [`allowed-uris`](../command-ref/conf-file.md#conf-allowed-uris). - The default is `false`. + files outside of + [`builtins.nixPath`](@docroot@/language/builtin-constants.md#builtins-nixPath), + or to URIs outside of + [`allowed-uris`](@docroot@/command-ref/conf-file.md#conf-allowed-uris). + + Also the default value for [`nix-path`](#conf-nix-path) is ignored, such that only explicitly set search path entries are taken into account. )"}; Setting pureEval{this, false, "pure-eval", From 630580162686dfd9a74d23acdee7b24ebd6b7c4c Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Sat, 7 Oct 2023 02:53:41 +0200 Subject: [PATCH 103/116] reword and reformat description of `builtins.import` --- src/libexpr/primops.cc | 101 ++++++++++++++++++++++------------------- 1 file changed, 55 insertions(+), 46 deletions(-) diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 1d04aeb2e..1ff2d5f0b 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -258,62 +258,71 @@ static RegisterPrimOp primop_import({ .args = {"path"}, // TODO turn "normal path values" into link below .doc = R"( - Load, parse and return the Nix expression in the file *path*. - - The *path* argument must meet the same criteria as an [interpolated expression](@docroot@/language/string-interpolation.md#interpolated-expression). - - If *path* is a directory, the file `default.nix` in that directory - is loaded. - - Evaluation aborts if the file doesn’t exist or contains - an incorrect Nix expression. `import` implements Nix’s module - system: you can put any Nix expression (such as a set or a - function) in a separate file, and use it from Nix expressions in - other files. + Load, parse, and return the Nix expression in the file *path*. > **Note** > > Unlike some languages, `import` is a regular function in Nix. - > Paths using the angle bracket syntax (e.g., `import` *\*) - > are normal [path values](@docroot@/language/values.md#type-path). - A Nix expression loaded by `import` must not contain any *free - variables* (identifiers that are not defined in the Nix expression - itself and are not built-in). Therefore, it cannot refer to - variables that are in scope at the call site. For instance, if you - have a calling expression + The *path* argument must meet the same criteria as an [interpolated expression](@docroot@/language/string-interpolation.md#interpolated-expression). - ```nix - rec { - x = 123; - y = import ./foo.nix; - } - ``` + If *path* is a directory, the file `default.nix` in that directory is used if it exists. - then the following `foo.nix` will give an error: + > **Example** + > + > ```console + > $ echo 123 > default.nix + > ``` + > + > Import `default.nix` from the current directory. + > + > ```nix + > import ./. + > ``` + > + > 123 - ```nix - x + 456 - ``` + Evaluation aborts if the file doesn’t exist or contains an invalid Nix expression. - since `x` is not in scope in `foo.nix`. If you want `x` to be - available in `foo.nix`, you should pass it as a function argument: + A Nix expression loaded by `import` must not contain any *free variables*, that is, identifiers that are not defined in the Nix expression itself and are not built-in. + Therefore, it cannot refer to variables that are in scope at the call site. - ```nix - rec { - x = 123; - y = import ./foo.nix x; - } - ``` - - and - - ```nix - x: x + 456 - ``` - - (The function argument doesn’t have to be called `x` in `foo.nix`; - any name would work.) + > **Example** + > + > If you have a calling expression + > + > ```nix + > rec { + > x = 123; + > y = import ./foo.nix; + > } + > ``` + > + > then the following `foo.nix` will give an error: + > + > ```nix + > # foo.nix + > x + 456 + > ``` + > + > since `x` is not in scope in `foo.nix`. + > If you want `x` to be available in `foo.nix`, pass it as a function argument: + > + > ```nix + > rec { + > x = 123; + > y = import ./foo.nix x; + > } + > ``` + > + > and + > + > ```nix + > # foo.nix + > x: x + 456 + > ``` + > + > The function argument doesn’t have to be called `x` in `foo.nix`; any name would work. )", .fun = [](EvalState & state, const PosIdx pos, Value * * args, Value & v) { From f00a5eb11b53a800850468f5e9e4658d05b419f9 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Sat, 7 Oct 2023 03:10:20 +0200 Subject: [PATCH 104/116] introduce lookup paths as a distinct language construct so far they did not really have a name, and were at best referred to as "angle bracket syntax". --- doc/manual/src/SUMMARY.md.in | 1 + .../src/language/constructs/lookup-path.md | 27 +++++++++++++++++++ doc/manual/src/language/values.md | 9 +++---- src/libexpr/primops.cc | 6 ++--- 4 files changed, 34 insertions(+), 9 deletions(-) create mode 100644 doc/manual/src/language/constructs/lookup-path.md diff --git a/doc/manual/src/SUMMARY.md.in b/doc/manual/src/SUMMARY.md.in index 2e631058f..e49e77cf5 100644 --- a/doc/manual/src/SUMMARY.md.in +++ b/doc/manual/src/SUMMARY.md.in @@ -30,6 +30,7 @@ - [Data Types](language/values.md) - [Language Constructs](language/constructs.md) - [String interpolation](language/string-interpolation.md) + - [Lookup path](language/constructs/lookup-path.md) - [Operators](language/operators.md) - [Derivations](language/derivations.md) - [Advanced Attributes](language/advanced-attributes.md) diff --git a/doc/manual/src/language/constructs/lookup-path.md b/doc/manual/src/language/constructs/lookup-path.md new file mode 100644 index 000000000..8ec835309 --- /dev/null +++ b/doc/manual/src/language/constructs/lookup-path.md @@ -0,0 +1,27 @@ +# Lookup path + +> **Syntax** +> +> *lookup-path* = `<` *identifier* [ `/` *identifier* `]`... `>` + +A lookup path is an identifier with an optional path suffix that resolves to a [path value](@docroot@/language/values.md#type-path) if the identifier matches a search path entry. + +The value of a lookup path is determined by [`builtins.nixPath`](@docroot@/language/builtin-constants.md#builtins-nixPath). + +See [`builtins.findFile`](@docroot@/language/builtins.md#builtins-findFile) for details on lookup path resolution. + +> **Example** +> +> ```nix +> +>``` +> +> /nix/var/nix/profiles/per-user/root/channels/nixpkgs + +> **Example** +> +> ```nix +> +>``` +> +> /nix/var/nix/profiles/per-user/root/channels/nixpkgs/nixos diff --git a/doc/manual/src/language/values.md b/doc/manual/src/language/values.md index 2ae3e143a..4d8950e85 100644 --- a/doc/manual/src/language/values.md +++ b/doc/manual/src/language/values.md @@ -107,11 +107,6 @@ e.g. `~/foo` would be equivalent to `/home/edolstra/foo` for a user whose home directory is `/home/edolstra`. - Paths can also be specified between angle brackets, e.g. - ``. This means that the directories listed in the - environment variable `NIX_PATH` will be searched for the given file - or directory name. - When an [interpolated string][string interpolation] evaluates to a path, the path is first copied into the Nix store and the resulting string is the [store path] of the newly created [store object]. [store path]: ../glossary.md#gloss-store-path @@ -123,13 +118,15 @@ For example, assume you used a file path in an interpolated string during a `nix repl` session. Later in the same session, after having changed the file contents, evaluating the interpolated string with the file path again might not return a new store path, since Nix might not re-read the file contents. - Paths themselves, except those in angle brackets (`< >`), support [string interpolation]. + Paths support [string interpolation]. At least one slash (`/`) must appear *before* any interpolated expression for the result to be recognized as a path. `a.${foo}/b.${bar}` is a syntactically valid division operation. `./a.${foo}/b.${bar}` is a path. + [Lookup paths](./constructs/lookup-path.md) such as `` resolve to path values. + - Boolean *Booleans* with values `true` and `false`. diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 5de1b2828..a8cb4ccfe 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1723,7 +1723,7 @@ static RegisterPrimOp primop_findFile(PrimOp { If the suffix is found inside that directory, then the entry is a match; the combined absolute path of the directory (now downloaded if need be) and the suffix is returned. - The syntax + [Lookup path](@docroot@/language/constructs/lookup-path.md) expressions can be [desugared](https://en.wikipedia.org/wiki/Syntactic_sugar) using this and [`builtins.nixPath`](@docroot@/language/builtin-constants.md#builtins-nixPath): ```nix @@ -4389,9 +4389,9 @@ void EvalState::createBaseEnv() addConstant("__nixPath", v, { .type = nList, .doc = R"( - The search path used to resolve angle bracket path lookups. + List of search path entries used to resolve [lookup paths](@docroot@/language/constructs/lookup-path.md). - Angle bracket expressions can be + Lookup path expressions can be [desugared](https://en.wikipedia.org/wiki/Syntactic_sugar) using this and [`builtins.findFile`](./builtins.html#builtins-findFile): From 896a90520280f2645c32b9a0136ab281843942c3 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Mon, 9 Oct 2023 10:07:46 +0200 Subject: [PATCH 105/116] AE -> BE; fix redirects --- doc/manual/redirects.js | 1 + doc/manual/src/contributing/testing.md | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/doc/manual/redirects.js b/doc/manual/redirects.js index 9d083a43d..d1b10109d 100644 --- a/doc/manual/redirects.js +++ b/doc/manual/redirects.js @@ -354,6 +354,7 @@ const redirects = { "installer-tests": "testing.html#installer-tests", "one-time-setup": "testing.html#one-time-setup", "using-the-ci-generated-installer-for-manual-testing": "testing.html#using-the-ci-generated-installer-for-manual-testing", + "characterization-testing": "#characterisation-testing-unit", } }; diff --git a/doc/manual/src/contributing/testing.md b/doc/manual/src/contributing/testing.md index 1a6388e40..3d75ebe7b 100644 --- a/doc/manual/src/contributing/testing.md +++ b/doc/manual/src/contributing/testing.md @@ -62,11 +62,11 @@ The path to the `unit-test-data` directory is passed to the unit test executable You can run the whole testsuite with `make check`, or the tests for a specific component with `make libfoo-tests_RUN`. Finer-grained filtering is also possible using the [--gtest_filter](https://google.github.io/googletest/advanced.html#running-a-subset-of-the-tests) command-line option, or the `GTEST_FILTER` environment variable. -### Characterization testing +### Characterisation testing { #characaterisation-testing-unit } -See [below](#characterization-testing-1) for a broader discussion of characterization testing. +See [functional characterisation testing](#characterisation-testing-functional) for a broader discussion of characterisation testing. -Like with the functional characterization, `_NIX_TEST_ACCEPT=1` is also used. +Like with the functional characterisation, `_NIX_TEST_ACCEPT=1` is also used. For example: ```shell-session $ _NIX_TEST_ACCEPT=1 make libstore-tests-exe_RUN @@ -77,8 +77,8 @@ $ _NIX_TEST_ACCEPT=1 make libstore-tests-exe_RUN [ SKIPPED ] WorkerProtoTest.storePath_write ... ``` -will regenerate the "golden master" expected result for the `libnixstore` characterization tests. -The characterization tests will mark themselves "skipped" since they regenerated the expected result instead of actually testing anything. +will regenerate the "golden master" expected result for the `libnixstore` characterisation tests. +The characterisation tests will mark themselves "skipped" since they regenerated the expected result instead of actually testing anything. ## Functional tests @@ -195,9 +195,9 @@ To remove any traces of that: git clean -x --force tests ``` -### Characterization testing +### Characterisation testing { #characterisation-testing-functional } -Occasionally, Nix utilizes a technique called [Characterization Testing](https://en.wikipedia.org/wiki/Characterization_test) as part of the functional tests. +Occasionally, Nix utilizes a technique called [Characterisation Testing](https://en.wikipedia.org/wiki/Characterization_test) as part of the functional tests. This technique is to include the exact output/behavior of a former version of Nix in a test in order to check that Nix continues to produce the same behavior going forward. For example, this technique is used for the language tests, to check both the printed final value if evaluation was successful, and any errors and warnings encountered. @@ -208,7 +208,7 @@ For example: ```bash _NIX_TEST_ACCEPT=1 make tests/functional/lang.sh.test ``` -This convention is shared with the [characterization unit tests](#characterization-testing-1) too. +This convention is shared with the [characterisation unit tests](#characterisation-testing-unit) too. An interesting situation to document is the case when these tests are "overfitted". The language tests are, again, an example of this. @@ -221,7 +221,7 @@ Diagnostic outputs are indeed not a stable interface, but they still are importa By recording the expected output, the test suite guards against accidental changes, and ensure the *result* (not just the code that implements it) of the diagnostic code paths are under code review. Regressions are caught, and improvements always show up in code review. -To ensure that characterization testing doesn't make it harder to intentionally change these interfaces, there always must be an easy way to regenerate the expected output, as we do with `_NIX_TEST_ACCEPT=1`. +To ensure that characterisation testing doesn't make it harder to intentionally change these interfaces, there always must be an easy way to regenerate the expected output, as we do with `_NIX_TEST_ACCEPT=1`. ## Integration tests @@ -235,7 +235,7 @@ You can run them manually with `nix build .#hydraJobs.tests.{testName}` or `nix- After a one-time setup, the Nix repository's GitHub Actions continuous integration (CI) workflow can test the installer each time you push to a branch. -Creating a Cachix cache for your installer tests and adding its authorization token to GitHub enables [two installer-specific jobs in the CI workflow](https://github.com/NixOS/nix/blob/88a45d6149c0e304f6eb2efcc2d7a4d0d569f8af/.github/workflows/ci.yml#L50-L91): +Creating a Cachix cache for your installer tests and adding its authorisation token to GitHub enables [two installer-specific jobs in the CI workflow](https://github.com/NixOS/nix/blob/88a45d6149c0e304f6eb2efcc2d7a4d0d569f8af/.github/workflows/ci.yml#L50-L91): - The `installer` job generates installers for the platforms below and uploads them to your Cachix cache: - `x86_64-linux` From 0246de1896df732739b0874c83b7d05becc83cf3 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Mon, 9 Oct 2023 10:14:25 +0200 Subject: [PATCH 106/116] remove unnecessary indentation from markdown list this makes it a bit easier to work with, as some tooling doesn't work well with too much indentation. --- doc/manual/src/command-ref/env-common.md | 175 ++++++++++++----------- 1 file changed, 95 insertions(+), 80 deletions(-) diff --git a/doc/manual/src/command-ref/env-common.md b/doc/manual/src/command-ref/env-common.md index b4a9bb2a9..34e0dbfbd 100644 --- a/doc/manual/src/command-ref/env-common.md +++ b/doc/manual/src/command-ref/env-common.md @@ -2,109 +2,124 @@ Most Nix commands interpret the following environment variables: - - [`IN_NIX_SHELL`](#env-IN_NIX_SHELL)\ - Indicator that tells if the current environment was set up by - `nix-shell`. It can have the values `pure` or `impure`. +- [`IN_NIX_SHELL`](#env-IN_NIX_SHELL) - - [`NIX_PATH`](#env-NIX_PATH)\ - A colon-separated list of directories used to look up the location of Nix - expressions using [paths](@docroot@/language/values.md#type-path) - enclosed in angle brackets (i.e., ``), - e.g. `/home/eelco/Dev:/etc/nixos`. It can be extended using the - [`-I` option](@docroot@/command-ref/opt-common.md#opt-I). + Indicator that tells if the current environment was set up by + `nix-shell`. It can have the values `pure` or `impure`. - If `NIX_PATH` is not set at all, Nix will fall back to the following list in [impure](@docroot@/command-ref/conf-file.md#conf-pure-eval) and [unrestricted](@docroot@/command-ref/conf-file.md#conf-restrict-eval) evaluation mode: +- [`NIX_PATH`](#env-NIX_PATH) - 1. `$HOME/.nix-defexpr/channels` - 2. `nixpkgs=/nix/var/nix/profiles/per-user/root/channels/nixpkgs` - 3. `/nix/var/nix/profiles/per-user/root/channels` + A colon-separated list of directories used to look up the location of Nix + expressions using [paths](@docroot@/language/values.md#type-path) + enclosed in angle brackets (i.e., ``), + e.g. `/home/eelco/Dev:/etc/nixos`. It can be extended using the + [`-I` option](@docroot@/command-ref/opt-common.md#opt-I). - If `NIX_PATH` is set to an empty string, resolving search paths will always fail. - For example, attempting to use `` will produce: + If `NIX_PATH` is not set at all, Nix will fall back to the following list in [impure](@docroot@/command-ref/conf-file.md#conf-pure-eval) and [unrestricted](@docroot@/command-ref/conf-file.md#conf-restrict-eval) evaluation mode: - error: file 'nixpkgs' was not found in the Nix search path + 1. `$HOME/.nix-defexpr/channels` + 2. `nixpkgs=/nix/var/nix/profiles/per-user/root/channels/nixpkgs` + 3. `/nix/var/nix/profiles/per-user/root/channels` - - [`NIX_IGNORE_SYMLINK_STORE`](#env-NIX_IGNORE_SYMLINK_STORE)\ - Normally, the Nix store directory (typically `/nix/store`) is not - allowed to contain any symlink components. This is to prevent - “impure” builds. Builders sometimes “canonicalise” paths by - resolving all symlink components. Thus, builds on different machines - (with `/nix/store` resolving to different locations) could yield - different results. This is generally not a problem, except when - builds are deployed to machines where `/nix/store` resolves - differently. If you are sure that you’re not going to do that, you - can set `NIX_IGNORE_SYMLINK_STORE` to `1`. + If `NIX_PATH` is set to an empty string, resolving search paths will always fail. + For example, attempting to use `` will produce: - Note that if you’re symlinking the Nix store so that you can put it - on another file system than the root file system, on Linux you’re - better off using `bind` mount points, e.g., + error: file 'nixpkgs' was not found in the Nix search path - ```console - $ mkdir /nix - $ mount -o bind /mnt/otherdisk/nix /nix - ``` +- [`NIX_IGNORE_SYMLINK_STORE`](#env-NIX_IGNORE_SYMLINK_STORE) - Consult the mount 8 manual page for details. + Normally, the Nix store directory (typically `/nix/store`) is not + allowed to contain any symlink components. This is to prevent + “impure” builds. Builders sometimes “canonicalise” paths by + resolving all symlink components. Thus, builds on different machines + (with `/nix/store` resolving to different locations) could yield + different results. This is generally not a problem, except when + builds are deployed to machines where `/nix/store` resolves + differently. If you are sure that you’re not going to do that, you + can set `NIX_IGNORE_SYMLINK_STORE` to `1`. - - [`NIX_STORE_DIR`](#env-NIX_STORE_DIR)\ - Overrides the location of the Nix store (default `prefix/store`). + Note that if you’re symlinking the Nix store so that you can put it + on another file system than the root file system, on Linux you’re + better off using `bind` mount points, e.g., - - [`NIX_DATA_DIR`](#env-NIX_DATA_DIR)\ - Overrides the location of the Nix static data directory (default - `prefix/share`). + ```console + $ mkdir /nix + $ mount -o bind /mnt/otherdisk/nix /nix + ``` - - [`NIX_LOG_DIR`](#env-NIX_LOG_DIR)\ - Overrides the location of the Nix log directory (default - `prefix/var/log/nix`). + Consult the mount 8 manual page for details. - - [`NIX_STATE_DIR`](#env-NIX_STATE_DIR)\ - Overrides the location of the Nix state directory (default - `prefix/var/nix`). +- [`NIX_STORE_DIR`](#env-NIX_STORE_DIR) - - [`NIX_CONF_DIR`](#env-NIX_CONF_DIR)\ - Overrides the location of the system Nix configuration directory - (default `prefix/etc/nix`). + Overrides the location of the Nix store (default `prefix/store`). - - [`NIX_CONFIG`](#env-NIX_CONFIG)\ - Applies settings from Nix configuration from the environment. - The content is treated as if it was read from a Nix configuration file. - Settings are separated by the newline character. +- [`NIX_DATA_DIR`](#env-NIX_DATA_DIR) - - [`NIX_USER_CONF_FILES`](#env-NIX_USER_CONF_FILES)\ - Overrides the location of the Nix user configuration files to load from. + Overrides the location of the Nix static data directory (default + `prefix/share`). - The default are the locations according to the [XDG Base Directory Specification]. - See the [XDG Base Directories](#xdg-base-directories) sub-section for details. +- [`NIX_LOG_DIR`](#env-NIX_LOG_DIR) - The variable is treated as a list separated by the `:` token. + Overrides the location of the Nix log directory (default + `prefix/var/log/nix`). - - [`TMPDIR`](#env-TMPDIR)\ - Use the specified directory to store temporary files. In particular, - this includes temporary build directories; these can take up - substantial amounts of disk space. The default is `/tmp`. +- [`NIX_STATE_DIR`](#env-NIX_STATE_DIR) - - [`NIX_REMOTE`](#env-NIX_REMOTE)\ - This variable should be set to `daemon` if you want to use the Nix - daemon to execute Nix operations. This is necessary in [multi-user - Nix installations](@docroot@/installation/multi-user.md). If the Nix - daemon's Unix socket is at some non-standard path, this variable - should be set to `unix://path/to/socket`. Otherwise, it should be - left unset. + Overrides the location of the Nix state directory (default + `prefix/var/nix`). - - [`NIX_SHOW_STATS`](#env-NIX_SHOW_STATS)\ - If set to `1`, Nix will print some evaluation statistics, such as - the number of values allocated. +- [`NIX_CONF_DIR`](#env-NIX_CONF_DIR) - - [`NIX_COUNT_CALLS`](#env-NIX_COUNT_CALLS)\ - If set to `1`, Nix will print how often functions were called during - Nix expression evaluation. This is useful for profiling your Nix - expressions. + Overrides the location of the system Nix configuration directory + (default `prefix/etc/nix`). - - [`GC_INITIAL_HEAP_SIZE`](#env-GC_INITIAL_HEAP_SIZE)\ - If Nix has been configured to use the Boehm garbage collector, this - variable sets the initial size of the heap in bytes. It defaults to - 384 MiB. Setting it to a low value reduces memory consumption, but - will increase runtime due to the overhead of garbage collection. +- [`NIX_CONFIG`](#env-NIX_CONFIG) + + Applies settings from Nix configuration from the environment. + The content is treated as if it was read from a Nix configuration file. + Settings are separated by the newline character. + +- [`NIX_USER_CONF_FILES`](#env-NIX_USER_CONF_FILES) + + Overrides the location of the Nix user configuration files to load from. + + The default are the locations according to the [XDG Base Directory Specification]. + See the [XDG Base Directories](#xdg-base-directories) sub-section for details. + + The variable is treated as a list separated by the `:` token. + +- [`TMPDIR`](#env-TMPDIR) + + Use the specified directory to store temporary files. In particular, + this includes temporary build directories; these can take up + substantial amounts of disk space. The default is `/tmp`. + +- [`NIX_REMOTE`](#env-NIX_REMOTE) + + This variable should be set to `daemon` if you want to use the Nix + daemon to execute Nix operations. This is necessary in [multi-user + Nix installations](@docroot@/installation/multi-user.md). If the Nix + daemon's Unix socket is at some non-standard path, this variable + should be set to `unix://path/to/socket`. Otherwise, it should be + left unset. + +- [`NIX_SHOW_STATS`](#env-NIX_SHOW_STATS) + + If set to `1`, Nix will print some evaluation statistics, such as + the number of values allocated. + +- [`NIX_COUNT_CALLS`](#env-NIX_COUNT_CALLS) + + If set to `1`, Nix will print how often functions were called during + Nix expression evaluation. This is useful for profiling your Nix + expressions. + +- [`GC_INITIAL_HEAP_SIZE`](#env-GC_INITIAL_HEAP_SIZE) + + If Nix has been configured to use the Boehm garbage collector, this + variable sets the initial size of the heap in bytes. It defaults to + 384 MiB. Setting it to a low value reduces memory consumption, but + will increase runtime due to the overhead of garbage collection. ## XDG Base Directories From 47b3508665bb9dc6a8467841ce487c7426080f17 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 6 Oct 2023 11:57:31 -0400 Subject: [PATCH 107/116] Use positive source filtering for the standalone functional tests job Additionally this skipping of the building is reimplemented to be a bit more robust and use the same idioms as the functionality for skipping the tests. In particular, it will now work even if the source files exist, so we can do this during development too. --- Makefile | 15 +++++++--- Makefile.config.in | 3 +- configure.ac | 11 +++++-- flake.nix | 60 +++++++++++++++++++++++++-------------- local.mk | 2 -- tests/functional/local.mk | 6 +--- 6 files changed, 61 insertions(+), 36 deletions(-) diff --git a/Makefile b/Makefile index 6658e3490..4f4ac0c6e 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,7 @@ +-include Makefile.config +clean-files += Makefile.config + +ifeq ($(ENABLE_BUILD), yes) makefiles = \ mk/precompiled-headers.mk \ local.mk \ @@ -18,15 +22,18 @@ makefiles = \ misc/upstart/local.mk \ doc/manual/local.mk \ doc/internal-api/local.mk +endif --include Makefile.config - -ifeq ($(tests), yes) +ifeq ($(ENABLE_BUILD)_$(ENABLE_TESTS), yes_yes) UNIT_TEST_ENV = _NIX_TEST_UNIT_DATA=unit-test-data makefiles += \ src/libutil/tests/local.mk \ src/libstore/tests/local.mk \ - src/libexpr/tests/local.mk \ + src/libexpr/tests/local.mk +endif + +ifeq ($(ENABLE_TESTS), yes) +makefiles += \ tests/functional/local.mk \ tests/functional/ca/local.mk \ tests/functional/dyn-drv/local.mk \ diff --git a/Makefile.config.in b/Makefile.config.in index 707cfe0e3..19992fa20 100644 --- a/Makefile.config.in +++ b/Makefile.config.in @@ -46,5 +46,6 @@ sandbox_shell = @sandbox_shell@ storedir = @storedir@ sysconfdir = @sysconfdir@ system = @system@ -tests = @tests@ +ENABLE_BUILD = @ENABLE_BUILD@ +ENABLE_TESTS = @ENABLE_TESTS@ internal_api_docs = @internal_api_docs@ diff --git a/configure.ac b/configure.ac index 6d78237f0..225baf6b5 100644 --- a/configure.ac +++ b/configure.ac @@ -152,12 +152,17 @@ if test "x$GCC_ATOMIC_BUILTINS_NEED_LIBATOMIC" = xyes; then LDFLAGS="-latomic $LDFLAGS" fi +# Running the functional tests without building Nix is useful for testing +# different pre-built versions of Nix against each other. +AC_ARG_ENABLE(build, AS_HELP_STRING([--disable-build],[Do not build nix]), + ENABLE_BUILD=$enableval, ENABLE_BUILD=yes) +AC_SUBST(ENABLE_BUILD) # Building without tests is useful for bootstrapping with a smaller footprint # or running the tests in a separate derivation. Otherwise, we do compile and # run them. AC_ARG_ENABLE(tests, AS_HELP_STRING([--disable-tests],[Do not build the tests]), - tests=$enableval, tests=yes) -AC_SUBST(tests) + ENABLE_TESTS=$enableval, ENABLE_TESTS=yes) +AC_SUBST(ENABLE_TESTS) # Building without API docs is the default as Nix' C++ interfaces are internal and unstable. AC_ARG_ENABLE(internal_api_docs, AS_HELP_STRING([--enable-internal-api-docs],[Build API docs for Nix's internal unstable C++ interfaces]), @@ -289,7 +294,7 @@ if test "$gc" = yes; then fi -if test "$tests" = yes; then +if test "$ENABLE_TESTS" = yes; then # Look for gtest. PKG_CHECK_MODULES([GTEST], [gtest_main]) diff --git a/flake.nix b/flake.nix index bec62fb95..1fe26853f 100644 --- a/flake.nix +++ b/flake.nix @@ -59,29 +59,40 @@ # that would interfere with repo semantics. fileset.fileFilter (f: f.name != ".gitignore") ./.; + configureFiles = fileset.unions [ + ./.version + ./configure.ac + ./m4 + # TODO: do we really need README.md? It doesn't seem used in the build. + ./README.md + ]; + + topLevelBuildFiles = fileset.unions [ + ./local.mk + ./Makefile + ./Makefile.config.in + ./mk + ]; + + functionalTestFiles = fileset.unions [ + ./tests/functional + (fileset.fileFilter (f: lib.strings.hasPrefix "nix-profile" f.name) ./scripts) + ]; + nixSrc = fileset.toSource { root = ./.; fileset = fileset.intersect baseFiles (fileset.unions [ - ./.version + configureFiles + topLevelBuildFiles ./boehmgc-coroutine-sp-fallback.diff - ./bootstrap.sh - ./configure.ac ./doc - ./local.mk - ./m4 - ./Makefile - ./Makefile.config.in ./misc - ./mk ./precompiled-headers.h ./src - ./tests/functional ./unit-test-data ./COPYING ./scripts/local.mk - (fileset.fileFilter (f: lib.strings.hasPrefix "nix-profile" f.name) ./scripts) - # TODO: do we really need README.md? It doesn't seem used in the build. - ./README.md + functionalTestFiles ]); }; @@ -252,7 +263,6 @@ testNixVersions = pkgs: client: daemon: with commonDeps { inherit pkgs; }; with pkgs.lib; pkgs.stdenv.mkDerivation { NIX_DAEMON_PACKAGE = daemon; NIX_CLIENT_PACKAGE = client; - HAVE_LOCAL_NIX_BUILD = false; name = "nix-tests" + optionalString @@ -261,7 +271,14 @@ "-${client.version}-against-${daemon.version}"; inherit version; - src = nixSrc; + src = fileset.toSource { + root = ./.; + fileset = fileset.intersect baseFiles (fileset.unions [ + configureFiles + topLevelBuildFiles + functionalTestFiles + ]); + }; VERSION_SUFFIX = versionSuffix; @@ -271,19 +288,20 @@ enableParallelBuilding = true; - configureFlags = testConfigureFlags; # otherwise configure fails + configureFlags = + testConfigureFlags # otherwise configure fails + ++ [ "--disable-build" ]; + dontBuild = true; doInstallCheck = true; - buildPhase = '' - # Remove the source files to make sure that we're not accidentally rebuilding Nix - rm src/**/*.cc - ''; - installPhase = '' mkdir -p $out ''; - installCheckPhase = "make installcheck -j$NIX_BUILD_CORES -l$NIX_BUILD_CORES"; + installCheckPhase = '' + mkdir -p src/nix-channel + make installcheck -j$NIX_BUILD_CORES -l$NIX_BUILD_CORES + ''; }; binaryTarball = nix: pkgs: diff --git a/local.mk b/local.mk index 6951c179e..3f3abb9f0 100644 --- a/local.mk +++ b/local.mk @@ -1,5 +1,3 @@ -clean-files += Makefile.config - GLOBAL_CXXFLAGS += -Wno-deprecated-declarations -Werror=switch # Allow switch-enum to be overridden for files that do not support it, usually because of dependency headers. ERROR_SWITCH_ENUM = -Werror=switch-enum diff --git a/tests/functional/local.mk b/tests/functional/local.mk index 4d29f60a1..6f6c94fe6 100644 --- a/tests/functional/local.mk +++ b/tests/functional/local.mk @@ -1,7 +1,3 @@ -# whether to run the tests that assume that we have a local build of -# Nix -HAVE_LOCAL_NIX_BUILD ?= 1 - nix_tests = \ test-infra.sh \ init.sh \ @@ -131,7 +127,7 @@ ifeq ($(HAVE_LIBCPUID), 1) nix_tests += compute-levels.sh endif -ifeq ($(HAVE_LOCAL_NIX_BUILD), 1) +ifeq ($(ENABLE_BUILD), yes) nix_tests += test-libstoreconsumer.sh ifeq ($(BUILD_SHARED_LIBS), 1) From 6654b4e3b4cf4b2181a0d14aa792d56eeab6219a Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 9 Oct 2023 07:56:59 -0400 Subject: [PATCH 108/116] Use positive source filtering for the Perl bindings --- flake.nix | 10 +++++++++- perl/Makefile | 8 +++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/flake.nix b/flake.nix index 1fe26853f..301e65545 100644 --- a/flake.nix +++ b/flake.nix @@ -477,7 +477,15 @@ passthru.perl-bindings = with final; perl.pkgs.toPerlModule (currentStdenv.mkDerivation { name = "nix-perl-${version}"; - src = self; + src = fileset.toSource { + root = ./.; + fileset = fileset.intersect baseFiles (fileset.unions [ + ./perl + ./.version + ./m4 + ./mk + ]); + }; nativeBuildInputs = [ buildPackages.autoconf-archive diff --git a/perl/Makefile b/perl/Makefile index c2c95f255..832668dd1 100644 --- a/perl/Makefile +++ b/perl/Makefile @@ -1,6 +1,12 @@ makefiles = local.mk -GLOBAL_CXXFLAGS += -g -Wall -std=c++2a -I ../src +GLOBAL_CXXFLAGS += -g -Wall -std=c++2a + +# A convenience for concurrent development of Nix and its Perl bindings. +# Not needed in a standalone build of the Perl bindings. +ifneq ("$(wildcard ../src)", "") + GLOBAL_CXXFLAGS += -I ../src +endif -include Makefile.config From 81d3a8542a6c919bb2302a877fd4443a6c07a2dc Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 9 Oct 2023 10:21:18 -0400 Subject: [PATCH 109/116] doc: Slight reword of "interpolated expression" in paragraph I was sleepy and confused that "interpolated expression" was a new type of thing at first. This nudges the reader to understand that its just a regular expression, and these conditions are imposed by the interpolation operation. --- doc/manual/src/language/string-interpolation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/src/language/string-interpolation.md b/doc/manual/src/language/string-interpolation.md index 2e650e348..e999b287b 100644 --- a/doc/manual/src/language/string-interpolation.md +++ b/doc/manual/src/language/string-interpolation.md @@ -92,7 +92,7 @@ Attributes can be selected with interpolated strings. # Interpolated expression -An interpolated expression must evaluate to one of the following: +An expression that is interpolated must evaluate to one of the following: - a [string] - a [path] From c32084a12cb922f54829b329fa1527f865e5b8ca Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 9 Oct 2023 16:25:53 +0200 Subject: [PATCH 110/116] printStats -> maybePrintStats --- src/libcmd/command.cc | 2 +- src/libexpr/eval.cc | 2 +- src/libexpr/eval.hh | 2 +- src/nix-build/nix-build.cc | 2 +- src/nix-env/nix-env.cc | 2 +- src/nix-instantiate/nix-instantiate.cc | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libcmd/command.cc b/src/libcmd/command.cc index 4fc197956..a88ba8134 100644 --- a/src/libcmd/command.cc +++ b/src/libcmd/command.cc @@ -98,7 +98,7 @@ EvalCommand::EvalCommand() EvalCommand::~EvalCommand() { if (evalState) - evalState->printStats(); + evalState->maybePrintStats(); } ref EvalCommand::getEvalStore() diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 600444955..b18eb5266 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -2491,7 +2491,7 @@ bool EvalState::fullGC() { #endif } -void EvalState::printStats() +void EvalState::maybePrintStats() { bool showStats = getEnv("NIX_SHOW_STATS").value_or("0") != "0"; diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 3bfa34ef6..c95558952 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -714,7 +714,7 @@ public: * Performs a full memory GC before printing the statistics, so that the * GC statistics are more accurate. */ - void printStats(); + void maybePrintStats(); /** * Print statistics, unconditionally, cheaply, without performing a GC first. diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index e2189fc66..2895c5e3c 100644 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -344,7 +344,7 @@ static void main_nix_build(int argc, char * * argv) } } - state->printStats(); + state->maybePrintStats(); auto buildPaths = [&](const std::vector & paths) { /* Note: we do this even when !printMissing to efficiently diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc index e455a50fd..01742daa8 100644 --- a/src/nix-env/nix-env.cc +++ b/src/nix-env/nix-env.cc @@ -1531,7 +1531,7 @@ static int main_nix_env(int argc, char * * argv) op(globals, std::move(opFlags), std::move(opArgs)); - globals.state->printStats(); + globals.state->maybePrintStats(); return 0; } diff --git a/src/nix-instantiate/nix-instantiate.cc b/src/nix-instantiate/nix-instantiate.cc index 446b27e66..d40196497 100644 --- a/src/nix-instantiate/nix-instantiate.cc +++ b/src/nix-instantiate/nix-instantiate.cc @@ -189,7 +189,7 @@ static int main_nix_instantiate(int argc, char * * argv) evalOnly, outputKind, xmlOutputSourceLocation, e); } - state->printStats(); + state->maybePrintStats(); return 0; } From 2f0b508c29f1c8eedc1f93c1a2dc31e8eb60de9a Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 9 Oct 2023 12:52:58 -0400 Subject: [PATCH 111/116] Get rid of `bootstrap.sh` For people working on Nix with `nix develop`, it's better to just use `autoreconfPhase` and `configurePhase`, which is standard Nixpkgs / nix shell make from Nixpkgs practice --- it is good to emphasize the degree to which Nix is *just* a regular C++ project which can be worked on in the regular way. (For people running `nix-shell`, the story is similar, except `configurePhase` would use non-writable store paths, which matters for hte times we use output paths before `make install`, so I kept the existing `./configure ...` instruction.) For people building Nix without Nix (e.g. packaging it for another distro) they also don't need `bootstrap.sh`, and can just run `autoreconf -vfi` directly. (More likely, they have their own idioms to do this just as we have `autoreconfPhase`.) --- bootstrap.sh | 4 ---- doc/manual/src/contributing/hacking.md | 6 +++--- doc/manual/src/installation/building-source.md | 2 +- 3 files changed, 4 insertions(+), 8 deletions(-) delete mode 100755 bootstrap.sh diff --git a/bootstrap.sh b/bootstrap.sh deleted file mode 100755 index e3e259351..000000000 --- a/bootstrap.sh +++ /dev/null @@ -1,4 +0,0 @@ -#! /bin/sh -e -rm -f aclocal.m4 -mkdir -p config -exec autoreconf -vfi diff --git a/doc/manual/src/contributing/hacking.md b/doc/manual/src/contributing/hacking.md index db393942c..38c144fcc 100644 --- a/doc/manual/src/contributing/hacking.md +++ b/doc/manual/src/contributing/hacking.md @@ -42,8 +42,8 @@ $ nix develop .#native-clang11StdenvPackages To build Nix itself in this shell: ```console -[nix-shell]$ ./bootstrap.sh -[nix-shell]$ ./configure $configureFlags --prefix=$(pwd)/outputs/out +[nix-shell]$ autoreconfPhase +[nix-shell]$ configurePhase [nix-shell]$ make -j $NIX_BUILD_CORES ``` @@ -86,7 +86,7 @@ $ nix-shell --attr devShells.x86_64-linux.native-clang11StdenvPackages To build Nix itself in this shell: ```console -[nix-shell]$ ./bootstrap.sh +[nix-shell]$ autoreconfPhase [nix-shell]$ ./configure $configureFlags --prefix=$(pwd)/outputs/out [nix-shell]$ make -j $NIX_BUILD_CORES ``` diff --git a/doc/manual/src/installation/building-source.md b/doc/manual/src/installation/building-source.md index ed1efffd8..7dad9805a 100644 --- a/doc/manual/src/installation/building-source.md +++ b/doc/manual/src/installation/building-source.md @@ -3,7 +3,7 @@ After cloning Nix's Git repository, issue the following commands: ```console -$ ./bootstrap.sh +$ autoreconf -vfi $ ./configure options... $ make $ make install From be81764320fc28131d23b85575076218eb7424c0 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 25 Mar 2022 04:39:57 +0000 Subject: [PATCH 112/116] Factor out bits of the worker protocol to use elsewhere This introduces some shared infrastructure for our notion of protocols. We can then define multiple protocols in terms of that notion. We an also express how particular protocols depend on each other. For example, we can define a common protocol and a worker protocol, where the second depends on the first in terms of the data types it can read and write. The "serve" protocol can just use the common one for now, but will eventually need its own machinary just like the worker protocol for version-aware serialisers --- doc/internal-api/doxygen.cfg.in | 30 ++++ src/libstore/build/derivation-goal.cc | 10 +- src/libstore/common-protocol-impl.hh | 41 +++++ src/libstore/common-protocol.cc | 98 +++++++++++ src/libstore/common-protocol.hh | 106 ++++++++++++ src/libstore/derivations.cc | 12 +- src/libstore/export-import.cc | 12 +- src/libstore/legacy-ssh-store.cc | 44 ++--- .../length-prefixed-protocol-helper.hh | 162 ++++++++++++++++++ src/libstore/worker-protocol-impl.hh | 111 ++++-------- src/libstore/worker-protocol.cc | 81 +-------- src/libstore/worker-protocol.hh | 60 ++----- src/nix-store/nix-store.cc | 26 +-- 13 files changed, 542 insertions(+), 251 deletions(-) create mode 100644 src/libstore/common-protocol-impl.hh create mode 100644 src/libstore/common-protocol.cc create mode 100644 src/libstore/common-protocol.hh create mode 100644 src/libstore/length-prefixed-protocol-helper.hh diff --git a/doc/internal-api/doxygen.cfg.in b/doc/internal-api/doxygen.cfg.in index 8f526536d..599be2470 100644 --- a/doc/internal-api/doxygen.cfg.in +++ b/doc/internal-api/doxygen.cfg.in @@ -54,6 +54,23 @@ INPUT = \ src/nix-env \ src/nix-store +# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names +# in the source code. If set to NO, only conditional compilation will be +# performed. Macro expansion can be done in a controlled way by setting +# EXPAND_ONLY_PREDEF to YES. +# The default value is: NO. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +MACRO_EXPANSION = YES + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then +# the macro expansion is limited to the macros specified with the PREDEFINED and +# EXPAND_AS_DEFINED tags. +# The default value is: NO. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +EXPAND_ONLY_PREDEF = YES + # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by the # preprocessor. Note that the INCLUDE_PATH is not recursive, so the setting of @@ -61,3 +78,16 @@ INPUT = \ # This tag requires that the tag SEARCH_INCLUDES is set to YES. INCLUDE_PATH = @RAPIDCHECK_HEADERS@ + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this +# tag can be used to specify a list of macro names that should be expanded. The +# macro definition that is found in the sources will be used. Use the PREDEFINED +# tag if you want to use a different macro definition that overrules the +# definition found in the source code. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +EXPAND_AS_DEFINED = \ + DECLARE_COMMON_SERIALISER \ + DECLARE_WORKER_SERIALISER \ + DECLARE_SERVE_SERIALISER \ + LENGTH_PREFIXED_PROTO_HELPER diff --git a/src/libstore/build/derivation-goal.cc b/src/libstore/build/derivation-goal.cc index 83c0a3135..dc4d91079 100644 --- a/src/libstore/build/derivation-goal.cc +++ b/src/libstore/build/derivation-goal.cc @@ -8,8 +8,8 @@ #include "util.hh" #include "archive.hh" #include "compression.hh" -#include "worker-protocol.hh" -#include "worker-protocol-impl.hh" +#include "common-protocol.hh" +#include "common-protocol-impl.hh" #include "topo-sort.hh" #include "callback.hh" #include "local-store.hh" // TODO remove, along with remaining downcasts @@ -1185,11 +1185,11 @@ HookReply DerivationGoal::tryBuildHook() throw; } - WorkerProto::WriteConn conn { hook->sink }; + CommonProto::WriteConn conn { hook->sink }; /* Tell the hook all the inputs that have to be copied to the remote system. */ - WorkerProto::write(worker.store, conn, inputPaths); + CommonProto::write(worker.store, conn, inputPaths); /* Tell the hooks the missing outputs that have to be copied back from the remote system. */ @@ -1200,7 +1200,7 @@ HookReply DerivationGoal::tryBuildHook() if (buildMode != bmCheck && status.known && status.known->isValid()) continue; missingOutputs.insert(outputName); } - WorkerProto::write(worker.store, conn, missingOutputs); + CommonProto::write(worker.store, conn, missingOutputs); } hook->sink = FdSink(); diff --git a/src/libstore/common-protocol-impl.hh b/src/libstore/common-protocol-impl.hh new file mode 100644 index 000000000..079c182b8 --- /dev/null +++ b/src/libstore/common-protocol-impl.hh @@ -0,0 +1,41 @@ +#pragma once +/** + * @file + * + * Template implementations (as opposed to mere declarations). + * + * This file is an exmample of the "impl.hh" pattern. See the + * contributing guide. + */ + +#include "common-protocol.hh" +#include "length-prefixed-protocol-helper.hh" + +namespace nix { + +/* protocol-agnostic templates */ + +#define COMMON_USE_LENGTH_PREFIX_SERIALISER(TEMPLATE, T) \ + TEMPLATE T CommonProto::Serialise< T >::read(const Store & store, CommonProto::ReadConn conn) \ + { \ + return LengthPrefixedProtoHelper::read(store, conn); \ + } \ + TEMPLATE void CommonProto::Serialise< T >::write(const Store & store, CommonProto::WriteConn conn, const T & t) \ + { \ + LengthPrefixedProtoHelper::write(store, conn, t); \ + } + +COMMON_USE_LENGTH_PREFIX_SERIALISER(template, std::vector) +COMMON_USE_LENGTH_PREFIX_SERIALISER(template, std::set) +COMMON_USE_LENGTH_PREFIX_SERIALISER(template, std::tuple) + +#define COMMA_ , +COMMON_USE_LENGTH_PREFIX_SERIALISER( + template, + std::map) +#undef COMMA_ + + +/* protocol-specific templates */ + +} diff --git a/src/libstore/common-protocol.cc b/src/libstore/common-protocol.cc new file mode 100644 index 000000000..f906814bc --- /dev/null +++ b/src/libstore/common-protocol.cc @@ -0,0 +1,98 @@ +#include "serialise.hh" +#include "util.hh" +#include "path-with-outputs.hh" +#include "store-api.hh" +#include "build-result.hh" +#include "common-protocol.hh" +#include "common-protocol-impl.hh" +#include "archive.hh" +#include "derivations.hh" + +#include + +namespace nix { + +/* protocol-agnostic definitions */ + +std::string CommonProto::Serialise::read(const Store & store, CommonProto::ReadConn conn) +{ + return readString(conn.from); +} + +void CommonProto::Serialise::write(const Store & store, CommonProto::WriteConn conn, const std::string & str) +{ + conn.to << str; +} + + +StorePath CommonProto::Serialise::read(const Store & store, CommonProto::ReadConn conn) +{ + return store.parseStorePath(readString(conn.from)); +} + +void CommonProto::Serialise::write(const Store & store, CommonProto::WriteConn conn, const StorePath & storePath) +{ + conn.to << store.printStorePath(storePath); +} + + +ContentAddress CommonProto::Serialise::read(const Store & store, CommonProto::ReadConn conn) +{ + return ContentAddress::parse(readString(conn.from)); +} + +void CommonProto::Serialise::write(const Store & store, CommonProto::WriteConn conn, const ContentAddress & ca) +{ + conn.to << renderContentAddress(ca); +} + + +Realisation CommonProto::Serialise::read(const Store & store, CommonProto::ReadConn conn) +{ + std::string rawInput = readString(conn.from); + return Realisation::fromJSON( + nlohmann::json::parse(rawInput), + "remote-protocol" + ); +} + +void CommonProto::Serialise::write(const Store & store, CommonProto::WriteConn conn, const Realisation & realisation) +{ + conn.to << realisation.toJSON().dump(); +} + + +DrvOutput CommonProto::Serialise::read(const Store & store, CommonProto::ReadConn conn) +{ + return DrvOutput::parse(readString(conn.from)); +} + +void CommonProto::Serialise::write(const Store & store, CommonProto::WriteConn conn, const DrvOutput & drvOutput) +{ + conn.to << drvOutput.to_string(); +} + + +std::optional CommonProto::Serialise>::read(const Store & store, CommonProto::ReadConn conn) +{ + auto s = readString(conn.from); + return s == "" ? std::optional {} : store.parseStorePath(s); +} + +void CommonProto::Serialise>::write(const Store & store, CommonProto::WriteConn conn, const std::optional & storePathOpt) +{ + conn.to << (storePathOpt ? store.printStorePath(*storePathOpt) : ""); +} + + +std::optional CommonProto::Serialise>::read(const Store & store, CommonProto::ReadConn conn) +{ + return ContentAddress::parseOpt(readString(conn.from)); +} + +void CommonProto::Serialise>::write(const Store & store, CommonProto::WriteConn conn, const std::optional & caOpt) +{ + conn.to << (caOpt ? renderContentAddress(*caOpt) : ""); +} + +} diff --git a/src/libstore/common-protocol.hh b/src/libstore/common-protocol.hh new file mode 100644 index 000000000..f3f28972a --- /dev/null +++ b/src/libstore/common-protocol.hh @@ -0,0 +1,106 @@ +#pragma once +///@file + +#include "serialise.hh" + +namespace nix { + +class Store; +struct Source; + +// items being serialized +class StorePath; +struct ContentAddress; +struct DrvOutput; +struct Realisation; + + +/** + * Shared serializers between the worker protocol, serve protocol, and a + * few others. + * + * This `struct` is basically just a `namespace`; We use a type rather + * than a namespace just so we can use it as a template argument. + */ +struct CommonProto +{ + /** + * A unidirectional read connection, to be used by the read half of the + * canonical serializers below. + */ + struct ReadConn { + Source & from; + }; + + /** + * A unidirectional write connection, to be used by the write half of the + * canonical serializers below. + */ + struct WriteConn { + Sink & to; + }; + + template + struct Serialise; + + /** + * Wrapper function around `CommonProto::Serialise::write` that allows us to + * infer the type instead of having to write it down explicitly. + */ + template + static void write(const Store & store, WriteConn conn, const T & t) + { + CommonProto::Serialise::write(store, conn, t); + } +}; + +#define DECLARE_COMMON_SERIALISER(T) \ + struct CommonProto::Serialise< T > \ + { \ + static T read(const Store & store, CommonProto::ReadConn conn); \ + static void write(const Store & store, CommonProto::WriteConn conn, const T & str); \ + } + +template<> +DECLARE_COMMON_SERIALISER(std::string); +template<> +DECLARE_COMMON_SERIALISER(StorePath); +template<> +DECLARE_COMMON_SERIALISER(ContentAddress); +template<> +DECLARE_COMMON_SERIALISER(DrvOutput); +template<> +DECLARE_COMMON_SERIALISER(Realisation); + +template +DECLARE_COMMON_SERIALISER(std::vector); +template +DECLARE_COMMON_SERIALISER(std::set); +template +DECLARE_COMMON_SERIALISER(std::tuple); + +#define COMMA_ , +template +DECLARE_COMMON_SERIALISER(std::map); +#undef COMMA_ + +/** + * These use the empty string for the null case, relying on the fact + * that the underlying types never serialize to the empty string. + * + * We do this instead of a generic std::optional instance because + * ordinal tags (0 or 1, here) are a bit of a compatability hazard. For + * the same reason, we don't have a std::variant instances (ordinal + * tags 0...n). + * + * We could the generic instances and then these as specializations for + * compatability, but that's proven a bit finnicky, and also makes the + * worker protocol harder to implement in other languages where such + * specializations may not be allowed. + */ +template<> +DECLARE_COMMON_SERIALISER(std::optional); +template<> +DECLARE_COMMON_SERIALISER(std::optional); + +} diff --git a/src/libstore/derivations.cc b/src/libstore/derivations.cc index 67069c3c9..fc17e520c 100644 --- a/src/libstore/derivations.cc +++ b/src/libstore/derivations.cc @@ -4,8 +4,8 @@ #include "globals.hh" #include "util.hh" #include "split.hh" -#include "worker-protocol.hh" -#include "worker-protocol-impl.hh" +#include "common-protocol.hh" +#include "common-protocol-impl.hh" #include "fs-accessor.hh" #include #include @@ -895,8 +895,8 @@ Source & readDerivation(Source & in, const Store & store, BasicDerivation & drv, drv.outputs.emplace(std::move(name), std::move(output)); } - drv.inputSrcs = WorkerProto::Serialise::read(store, - WorkerProto::ReadConn { .from = in }); + drv.inputSrcs = CommonProto::Serialise::read(store, + CommonProto::ReadConn { .from = in }); in >> drv.platform >> drv.builder; drv.args = readStrings(in); @@ -944,8 +944,8 @@ void writeDerivation(Sink & out, const Store & store, const BasicDerivation & dr }, }, i.second.raw); } - WorkerProto::write(store, - WorkerProto::WriteConn { .to = out }, + CommonProto::write(store, + CommonProto::WriteConn { .to = out }, drv.inputSrcs); out << drv.platform << drv.builder << drv.args; out << drv.env.size(); diff --git a/src/libstore/export-import.cc b/src/libstore/export-import.cc index e866aeb42..87b2f8741 100644 --- a/src/libstore/export-import.cc +++ b/src/libstore/export-import.cc @@ -1,8 +1,8 @@ #include "serialise.hh" #include "store-api.hh" #include "archive.hh" -#include "worker-protocol.hh" -#include "worker-protocol-impl.hh" +#include "common-protocol.hh" +#include "common-protocol-impl.hh" #include @@ -46,8 +46,8 @@ void Store::exportPath(const StorePath & path, Sink & sink) teeSink << exportMagic << printStorePath(path); - WorkerProto::write(*this, - WorkerProto::WriteConn { .to = teeSink }, + CommonProto::write(*this, + CommonProto::WriteConn { .to = teeSink }, info->references); teeSink << (info->deriver ? printStorePath(*info->deriver) : "") @@ -76,8 +76,8 @@ StorePaths Store::importPaths(Source & source, CheckSigsFlag checkSigs) //Activity act(*logger, lvlInfo, "importing path '%s'", info.path); - auto references = WorkerProto::Serialise::read(*this, - WorkerProto::ReadConn { .from = source }); + auto references = CommonProto::Serialise::read(*this, + CommonProto::ReadConn { .from = source }); auto deriver = readString(source); auto narHash = hashString(htSHA256, saved.s); diff --git a/src/libstore/legacy-ssh-store.cc b/src/libstore/legacy-ssh-store.cc index 78b05031a..7bf4476d0 100644 --- a/src/libstore/legacy-ssh-store.cc +++ b/src/libstore/legacy-ssh-store.cc @@ -6,8 +6,8 @@ #include "build-result.hh" #include "store-api.hh" #include "path-with-outputs.hh" -#include "worker-protocol.hh" -#include "worker-protocol-impl.hh" +#include "common-protocol.hh" +#include "common-protocol-impl.hh" #include "ssh.hh" #include "derivations.hh" #include "callback.hh" @@ -50,37 +50,37 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor bool good = true; /** - * Coercion to `WorkerProto::ReadConn`. This makes it easy to use the - * factored out worker protocol searlizers with a + * Coercion to `CommonProto::ReadConn`. This makes it easy to use the + * factored out common protocol serialisers with a * `LegacySSHStore::Connection`. * - * The worker protocol connection types are unidirectional, unlike + * The common protocol connection types are unidirectional, unlike * this type. * - * @todo Use server protocol serializers, not worker protocol + * @todo Use server protocol serializers, not common protocol * serializers, once we have made that distiction. */ - operator WorkerProto::ReadConn () + operator CommonProto::ReadConn () { - return WorkerProto::ReadConn { + return CommonProto::ReadConn { .from = from, }; } /* - * Coercion to `WorkerProto::WriteConn`. This makes it easy to use the - * factored out worker protocol searlizers with a + * Coercion to `CommonProto::WriteConn`. This makes it easy to use the + * factored out common protocol searlizers with a * `LegacySSHStore::Connection`. * - * The worker protocol connection types are unidirectional, unlike + * The common protocol connection types are unidirectional, unlike * this type. * - * @todo Use server protocol serializers, not worker protocol + * @todo Use server protocol serializers, not common protocol * serializers, once we have made that distiction. */ - operator WorkerProto::WriteConn () + operator CommonProto::WriteConn () { - return WorkerProto::WriteConn { + return CommonProto::WriteConn { .to = to, }; } @@ -183,7 +183,7 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor auto deriver = readString(conn->from); if (deriver != "") info->deriver = parseStorePath(deriver); - info->references = WorkerProto::Serialise::read(*this, *conn); + info->references = CommonProto::Serialise::read(*this, *conn); readLongLong(conn->from); // download size info->narSize = readLongLong(conn->from); @@ -217,7 +217,7 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor << printStorePath(info.path) << (info.deriver ? printStorePath(*info.deriver) : "") << info.narHash.to_string(Base16, false); - WorkerProto::write(*this, *conn, info.references); + CommonProto::write(*this, *conn, info.references); conn->to << info.registrationTime << info.narSize @@ -246,7 +246,7 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor conn->to << exportMagic << printStorePath(info.path); - WorkerProto::write(*this, *conn, info.references); + CommonProto::write(*this, *conn, info.references); conn->to << (info.deriver ? printStorePath(*info.deriver) : "") << 0 @@ -331,7 +331,7 @@ public: if (GET_PROTOCOL_MINOR(conn->remoteVersion) >= 3) conn->from >> status.timesBuilt >> status.isNonDeterministic >> status.startTime >> status.stopTime; if (GET_PROTOCOL_MINOR(conn->remoteVersion) >= 6) { - auto builtOutputs = WorkerProto::Serialise::read(*this, *conn); + auto builtOutputs = CommonProto::Serialise::read(*this, *conn); for (auto && [output, realisation] : builtOutputs) status.builtOutputs.insert_or_assign( std::move(output.outputName), @@ -409,10 +409,10 @@ public: conn->to << ServeProto::Command::QueryClosure << includeOutputs; - WorkerProto::write(*this, *conn, paths); + CommonProto::write(*this, *conn, paths); conn->to.flush(); - for (auto & i : WorkerProto::Serialise::read(*this, *conn)) + for (auto & i : CommonProto::Serialise::read(*this, *conn)) out.insert(i); } @@ -425,10 +425,10 @@ public: << ServeProto::Command::QueryValidPaths << false // lock << maybeSubstitute; - WorkerProto::write(*this, *conn, paths); + CommonProto::write(*this, *conn, paths); conn->to.flush(); - return WorkerProto::Serialise::read(*this, *conn); + return CommonProto::Serialise::read(*this, *conn); } void connect() override diff --git a/src/libstore/length-prefixed-protocol-helper.hh b/src/libstore/length-prefixed-protocol-helper.hh new file mode 100644 index 000000000..4061b0cd6 --- /dev/null +++ b/src/libstore/length-prefixed-protocol-helper.hh @@ -0,0 +1,162 @@ +#pragma once +/** + * @file Reusable serialisers for serialization container types in a + * length-prefixed manner. + * + * Used by both the Worker and Serve protocols. + */ + +#include "types.hh" + +namespace nix { + +class Store; + +/** + * Reusable serialisers for serialization container types in a + * length-prefixed manner. + * + * @param T The type of the collection being serialised + * + * @param Inner This the most important parameter; this is the "inner" + * protocol. The user of this will substitute `MyProtocol` or similar + * when making a `MyProtocol::Serialiser>`. Note that the + * inside is allowed to call to call `Inner::Serialiser` on different + * types. This is especially important for `std::map` which doesn't have + * a single `T` but one `K` and one `V`. + */ +template +struct LengthPrefixedProtoHelper; + +/*! + * \typedef LengthPrefixedProtoHelper::S + * + * Read this as simply `using S = Inner::Serialise;`. + * + * It would be nice to use that directly, but C++ doesn't seem to allow + * it. The `typename` keyword needed to refer to `Inner` seems to greedy + * (low precedence), and then C++ complains that `Serialise` is not a + * type parameter but a real type. + * + * Making this `S` alias seems to be the only way to avoid these issues. + */ + +#define LENGTH_PREFIXED_PROTO_HELPER(Inner, T) \ + struct LengthPrefixedProtoHelper< Inner, T > \ + { \ + static T read(const Store & store, typename Inner::ReadConn conn); \ + static void write(const Store & store, typename Inner::WriteConn conn, const T & str); \ + private: \ + template using S = typename Inner::template Serialise; \ + } + +template +LENGTH_PREFIXED_PROTO_HELPER(Inner, std::vector); + +template +LENGTH_PREFIXED_PROTO_HELPER(Inner, std::set); + +template +LENGTH_PREFIXED_PROTO_HELPER(Inner, std::tuple); + +template +#define _X std::map +LENGTH_PREFIXED_PROTO_HELPER(Inner, _X); +#undef _X + +template +std::vector +LengthPrefixedProtoHelper>::read( + const Store & store, typename Inner::ReadConn conn) +{ + std::vector resSet; + auto size = readNum(conn.from); + while (size--) { + resSet.push_back(S::read(store, conn)); + } + return resSet; +} + +template +void +LengthPrefixedProtoHelper>::write( + const Store & store, typename Inner::WriteConn conn, const std::vector & resSet) +{ + conn.to << resSet.size(); + for (auto & key : resSet) { + S::write(store, conn, key); + } +} + +template +std::set +LengthPrefixedProtoHelper>::read( + const Store & store, typename Inner::ReadConn conn) +{ + std::set resSet; + auto size = readNum(conn.from); + while (size--) { + resSet.insert(S::read(store, conn)); + } + return resSet; +} + +template +void +LengthPrefixedProtoHelper>::write( + const Store & store, typename Inner::WriteConn conn, const std::set & resSet) +{ + conn.to << resSet.size(); + for (auto & key : resSet) { + S::write(store, conn, key); + } +} + +template +std::map +LengthPrefixedProtoHelper>::read( + const Store & store, typename Inner::ReadConn conn) +{ + std::map resMap; + auto size = readNum(conn.from); + while (size--) { + auto k = S::read(store, conn); + auto v = S::read(store, conn); + resMap.insert_or_assign(std::move(k), std::move(v)); + } + return resMap; +} + +template +void +LengthPrefixedProtoHelper>::write( + const Store & store, typename Inner::WriteConn conn, const std::map & resMap) +{ + conn.to << resMap.size(); + for (auto & i : resMap) { + S::write(store, conn, i.first); + S::write(store, conn, i.second); + } +} + +template +std::tuple +LengthPrefixedProtoHelper>::read( + const Store & store, typename Inner::ReadConn conn) +{ + return std::tuple { + S::read(store, conn)..., + }; +} + +template +void +LengthPrefixedProtoHelper>::write( + const Store & store, typename Inner::WriteConn conn, const std::tuple & res) +{ + std::apply([&](const Us &... args) { + (S::write(store, conn, args), ...); + }, res); +} + +} diff --git a/src/libstore/worker-protocol-impl.hh b/src/libstore/worker-protocol-impl.hh index 4f797f95a..c043588d6 100644 --- a/src/libstore/worker-protocol-impl.hh +++ b/src/libstore/worker-protocol-impl.hh @@ -9,86 +9,51 @@ */ #include "worker-protocol.hh" +#include "length-prefixed-protocol-helper.hh" namespace nix { +/* protocol-agnostic templates */ + +#define WORKER_USE_LENGTH_PREFIX_SERIALISER(TEMPLATE, T) \ + TEMPLATE T WorkerProto::Serialise< T >::read(const Store & store, WorkerProto::ReadConn conn) \ + { \ + return LengthPrefixedProtoHelper::read(store, conn); \ + } \ + TEMPLATE void WorkerProto::Serialise< T >::write(const Store & store, WorkerProto::WriteConn conn, const T & t) \ + { \ + LengthPrefixedProtoHelper::write(store, conn, t); \ + } + +WORKER_USE_LENGTH_PREFIX_SERIALISER(template, std::vector) +WORKER_USE_LENGTH_PREFIX_SERIALISER(template, std::set) +WORKER_USE_LENGTH_PREFIX_SERIALISER(template, std::tuple) + +#define COMMA_ , +WORKER_USE_LENGTH_PREFIX_SERIALISER( + template, + std::map) +#undef COMMA_ + +/** + * Use `CommonProto` where possible. + */ template -std::vector WorkerProto::Serialise>::read(const Store & store, WorkerProto::ReadConn conn) +struct WorkerProto::Serialise { - std::vector resSet; - auto size = readNum(conn.from); - while (size--) { - resSet.push_back(WorkerProto::Serialise::read(store, conn)); + static T read(const Store & store, WorkerProto::ReadConn conn) + { + return CommonProto::Serialise::read(store, + CommonProto::ReadConn { .from = conn.from }); } - return resSet; -} - -template -void WorkerProto::Serialise>::write(const Store & store, WorkerProto::WriteConn conn, const std::vector & resSet) -{ - conn.to << resSet.size(); - for (auto & key : resSet) { - WorkerProto::Serialise::write(store, conn, key); + static void write(const Store & store, WorkerProto::WriteConn conn, const T & t) + { + CommonProto::Serialise::write(store, + CommonProto::WriteConn { .to = conn.to }, + t); } -} +}; -template -std::set WorkerProto::Serialise>::read(const Store & store, WorkerProto::ReadConn conn) -{ - std::set resSet; - auto size = readNum(conn.from); - while (size--) { - resSet.insert(WorkerProto::Serialise::read(store, conn)); - } - return resSet; -} - -template -void WorkerProto::Serialise>::write(const Store & store, WorkerProto::WriteConn conn, const std::set & resSet) -{ - conn.to << resSet.size(); - for (auto & key : resSet) { - WorkerProto::Serialise::write(store, conn, key); - } -} - -template -std::map WorkerProto::Serialise>::read(const Store & store, WorkerProto::ReadConn conn) -{ - std::map resMap; - auto size = readNum(conn.from); - while (size--) { - auto k = WorkerProto::Serialise::read(store, conn); - auto v = WorkerProto::Serialise::read(store, conn); - resMap.insert_or_assign(std::move(k), std::move(v)); - } - return resMap; -} - -template -void WorkerProto::Serialise>::write(const Store & store, WorkerProto::WriteConn conn, const std::map & resMap) -{ - conn.to << resMap.size(); - for (auto & i : resMap) { - WorkerProto::Serialise::write(store, conn, i.first); - WorkerProto::Serialise::write(store, conn, i.second); - } -} - -template -std::tuple WorkerProto::Serialise>::read(const Store & store, WorkerProto::ReadConn conn) -{ - return std::tuple { - WorkerProto::Serialise::read(store, conn)..., - }; -} - -template -void WorkerProto::Serialise>::write(const Store & store, WorkerProto::WriteConn conn, const std::tuple & res) -{ - std::apply([&](const Us &... args) { - (WorkerProto::Serialise::write(store, conn, args), ...); - }, res); -} +/* protocol-specific templates */ } diff --git a/src/libstore/worker-protocol.cc b/src/libstore/worker-protocol.cc index a23130743..415e66f16 100644 --- a/src/libstore/worker-protocol.cc +++ b/src/libstore/worker-protocol.cc @@ -12,27 +12,7 @@ namespace nix { -std::string WorkerProto::Serialise::read(const Store & store, WorkerProto::ReadConn conn) -{ - return readString(conn.from); -} - -void WorkerProto::Serialise::write(const Store & store, WorkerProto::WriteConn conn, const std::string & str) -{ - conn.to << str; -} - - -StorePath WorkerProto::Serialise::read(const Store & store, WorkerProto::ReadConn conn) -{ - return store.parseStorePath(readString(conn.from)); -} - -void WorkerProto::Serialise::write(const Store & store, WorkerProto::WriteConn conn, const StorePath & storePath) -{ - conn.to << store.printStorePath(storePath); -} - +/* protocol-specific definitions */ std::optional WorkerProto::Serialise>::read(const Store & store, WorkerProto::ReadConn conn) { @@ -68,17 +48,6 @@ void WorkerProto::Serialise>::write(const Store & sto } -ContentAddress WorkerProto::Serialise::read(const Store & store, WorkerProto::ReadConn conn) -{ - return ContentAddress::parse(readString(conn.from)); -} - -void WorkerProto::Serialise::write(const Store & store, WorkerProto::WriteConn conn, const ContentAddress & ca) -{ - conn.to << renderContentAddress(ca); -} - - DerivedPath WorkerProto::Serialise::read(const Store & store, WorkerProto::ReadConn conn) { auto s = readString(conn.from); @@ -91,32 +60,6 @@ void WorkerProto::Serialise::write(const Store & store, WorkerProto } -Realisation WorkerProto::Serialise::read(const Store & store, WorkerProto::ReadConn conn) -{ - std::string rawInput = readString(conn.from); - return Realisation::fromJSON( - nlohmann::json::parse(rawInput), - "remote-protocol" - ); -} - -void WorkerProto::Serialise::write(const Store & store, WorkerProto::WriteConn conn, const Realisation & realisation) -{ - conn.to << realisation.toJSON().dump(); -} - - -DrvOutput WorkerProto::Serialise::read(const Store & store, WorkerProto::ReadConn conn) -{ - return DrvOutput::parse(readString(conn.from)); -} - -void WorkerProto::Serialise::write(const Store & store, WorkerProto::WriteConn conn, const DrvOutput & drvOutput) -{ - conn.to << drvOutput.to_string(); -} - - KeyedBuildResult WorkerProto::Serialise::read(const Store & store, WorkerProto::ReadConn conn) { auto path = WorkerProto::Serialise::read(store, conn); @@ -168,26 +111,4 @@ void WorkerProto::Serialise::write(const Store & store, WorkerProto } -std::optional WorkerProto::Serialise>::read(const Store & store, WorkerProto::ReadConn conn) -{ - auto s = readString(conn.from); - return s == "" ? std::optional {} : store.parseStorePath(s); -} - -void WorkerProto::Serialise>::write(const Store & store, WorkerProto::WriteConn conn, const std::optional & storePathOpt) -{ - conn.to << (storePathOpt ? store.printStorePath(*storePathOpt) : ""); -} - - -std::optional WorkerProto::Serialise>::read(const Store & store, WorkerProto::ReadConn conn) -{ - return ContentAddress::parseOpt(readString(conn.from)); -} - -void WorkerProto::Serialise>::write(const Store & store, WorkerProto::WriteConn conn, const std::optional & caOpt) -{ - conn.to << (caOpt ? renderContentAddress(*caOpt) : ""); -} - } diff --git a/src/libstore/worker-protocol.hh b/src/libstore/worker-protocol.hh index b7f42f24d..c84060103 100644 --- a/src/libstore/worker-protocol.hh +++ b/src/libstore/worker-protocol.hh @@ -1,7 +1,7 @@ #pragma once ///@file -#include "serialise.hh" +#include "common-protocol.hh" namespace nix { @@ -28,11 +28,7 @@ class Store; struct Source; // items being serialised -class StorePath; -struct ContentAddress; struct DerivedPath; -struct DrvOutput; -struct Realisation; struct BuildResult; struct KeyedBuildResult; enum TrustedFlag : bool; @@ -193,60 +189,32 @@ inline std::ostream & operator << (std::ostream & s, WorkerProto::Op op) * be legal specialization syntax. See below for what that looks like in * practice. */ -#define MAKE_WORKER_PROTO(T) \ - struct WorkerProto::Serialise< T > { \ +#define DECLARE_WORKER_SERIALISER(T) \ + struct WorkerProto::Serialise< T > \ + { \ static T read(const Store & store, WorkerProto::ReadConn conn); \ static void write(const Store & store, WorkerProto::WriteConn conn, const T & t); \ }; template<> -MAKE_WORKER_PROTO(std::string); +DECLARE_WORKER_SERIALISER(DerivedPath); template<> -MAKE_WORKER_PROTO(StorePath); +DECLARE_WORKER_SERIALISER(BuildResult); template<> -MAKE_WORKER_PROTO(ContentAddress); +DECLARE_WORKER_SERIALISER(KeyedBuildResult); template<> -MAKE_WORKER_PROTO(DerivedPath); -template<> -MAKE_WORKER_PROTO(DrvOutput); -template<> -MAKE_WORKER_PROTO(Realisation); -template<> -MAKE_WORKER_PROTO(BuildResult); -template<> -MAKE_WORKER_PROTO(KeyedBuildResult); -template<> -MAKE_WORKER_PROTO(std::optional); +DECLARE_WORKER_SERIALISER(std::optional); template -MAKE_WORKER_PROTO(std::vector); +DECLARE_WORKER_SERIALISER(std::vector); template -MAKE_WORKER_PROTO(std::set); +DECLARE_WORKER_SERIALISER(std::set); template -MAKE_WORKER_PROTO(std::tuple); +DECLARE_WORKER_SERIALISER(std::tuple); +#define COMMA_ , template -#define X_ std::map -MAKE_WORKER_PROTO(X_); -#undef X_ - -/** - * These use the empty string for the null case, relying on the fact - * that the underlying types never serialise to the empty string. - * - * We do this instead of a generic std::optional instance because - * ordinal tags (0 or 1, here) are a bit of a compatability hazard. For - * the same reason, we don't have a std::variant instances (ordinal - * tags 0...n). - * - * We could the generic instances and then these as specializations for - * compatability, but that's proven a bit finnicky, and also makes the - * worker protocol harder to implement in other languages where such - * specializations may not be allowed. - */ -template<> -MAKE_WORKER_PROTO(std::optional); -template<> -MAKE_WORKER_PROTO(std::optional); +DECLARE_WORKER_SERIALISER(std::map); +#undef COMMA_ } diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc index 96c3f7d7e..6fc22214a 100644 --- a/src/nix-store/nix-store.cc +++ b/src/nix-store/nix-store.cc @@ -11,8 +11,8 @@ #include "serve-protocol.hh" #include "shared.hh" #include "util.hh" -#include "worker-protocol.hh" -#include "worker-protocol-impl.hh" +#include "common-protocol.hh" +#include "common-protocol-impl.hh" #include "graphml.hh" #include "legacy.hh" #include "path-with-outputs.hh" @@ -821,8 +821,8 @@ static void opServe(Strings opFlags, Strings opArgs) out.flush(); unsigned int clientVersion = readInt(in); - WorkerProto::ReadConn rconn { .from = in }; - WorkerProto::WriteConn wconn { .to = out }; + CommonProto::ReadConn rconn { .from = in }; + CommonProto::WriteConn wconn { .to = out }; auto getBuildSettings = [&]() { // FIXME: changing options here doesn't work if we're @@ -867,7 +867,7 @@ static void opServe(Strings opFlags, Strings opArgs) case ServeProto::Command::QueryValidPaths: { bool lock = readInt(in); bool substitute = readInt(in); - auto paths = WorkerProto::Serialise::read(*store, rconn); + auto paths = CommonProto::Serialise::read(*store, rconn); if (lock && writeAllowed) for (auto & path : paths) store->addTempRoot(path); @@ -876,19 +876,19 @@ static void opServe(Strings opFlags, Strings opArgs) store->substitutePaths(paths); } - WorkerProto::write(*store, wconn, store->queryValidPaths(paths)); + CommonProto::write(*store, wconn, store->queryValidPaths(paths)); break; } case ServeProto::Command::QueryPathInfos: { - auto paths = WorkerProto::Serialise::read(*store, rconn); + auto paths = CommonProto::Serialise::read(*store, rconn); // !!! Maybe we want a queryPathInfos? for (auto & i : paths) { try { auto info = store->queryPathInfo(i); out << store->printStorePath(info->path) << (info->deriver ? store->printStorePath(*info->deriver) : ""); - WorkerProto::write(*store, wconn, info->references); + CommonProto::write(*store, wconn, info->references); // !!! Maybe we want compression? out << info->narSize // downloadSize << info->narSize; @@ -916,7 +916,7 @@ static void opServe(Strings opFlags, Strings opArgs) case ServeProto::Command::ExportPaths: { readInt(in); // obsolete - store->exportPaths(WorkerProto::Serialise::read(*store, rconn), out); + store->exportPaths(CommonProto::Serialise::read(*store, rconn), out); break; } @@ -962,7 +962,7 @@ static void opServe(Strings opFlags, Strings opArgs) DrvOutputs builtOutputs; for (auto & [output, realisation] : status.builtOutputs) builtOutputs.insert_or_assign(realisation.id, realisation); - WorkerProto::write(*store, wconn, builtOutputs); + CommonProto::write(*store, wconn, builtOutputs); } break; @@ -971,9 +971,9 @@ static void opServe(Strings opFlags, Strings opArgs) case ServeProto::Command::QueryClosure: { bool includeOutputs = readInt(in); StorePathSet closure; - store->computeFSClosure(WorkerProto::Serialise::read(*store, rconn), + store->computeFSClosure(CommonProto::Serialise::read(*store, rconn), closure, false, includeOutputs); - WorkerProto::write(*store, wconn, closure); + CommonProto::write(*store, wconn, closure); break; } @@ -988,7 +988,7 @@ static void opServe(Strings opFlags, Strings opArgs) }; if (deriver != "") info.deriver = store->parseStorePath(deriver); - info.references = WorkerProto::Serialise::read(*store, rconn); + info.references = CommonProto::Serialise::read(*store, rconn); in >> info.registrationTime >> info.narSize >> info.ultimate; info.sigs = readStrings(in); info.ca = ContentAddress::parseOpt(readString(in)); From 4de54b21907e3fc41b9e6250396eb01d2b10c4db Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 4 Oct 2023 23:27:50 -0400 Subject: [PATCH 113/116] Unit test the "common protocol" too Copy the relevant tests to ensure the new interfaces added in the last commit are tested. Perhaps I should try to deduplicat these tests some more. However its not clear how to do that outside of a big ugly C++ macro. https://github.com/google/googletest/blob/main/docs/advanced.md has some stuff but it is cumbersome and I didn't figure it out yet. This is done in a separate commit in order to be sure that the first commit really didn't change any behavior; if we changed the implementation and the tests at once, it would be harder to tell whether or not some behavioral changes slipped in what is supposed to be a "pure refactor". Co-Authored-By: Valentin Gagarin --- src/libstore/tests/characterization.hh | 23 +++ src/libstore/tests/common-protocol.cc | 152 ++++++++++++++++++ src/libstore/tests/protocol.hh | 88 ++++++++++ src/libstore/tests/worker-protocol.cc | 90 ++--------- .../common-protocol/content-address.bin | Bin 0 -> 208 bytes .../libstore/common-protocol/drv-output.bin | Bin 0 -> 176 bytes .../optional-content-address.bin | Bin 0 -> 64 bytes .../common-protocol/optional-store-path.bin | Bin 0 -> 72 bytes .../libstore/common-protocol/realisation.bin | Bin 0 -> 520 bytes .../libstore/common-protocol/set.bin | Bin 0 -> 152 bytes .../libstore/common-protocol/store-path.bin | Bin 0 -> 120 bytes .../libstore/common-protocol/string.bin | Bin 0 -> 88 bytes .../libstore/common-protocol/vector.bin | Bin 0 -> 152 bytes 13 files changed, 280 insertions(+), 73 deletions(-) create mode 100644 src/libstore/tests/characterization.hh create mode 100644 src/libstore/tests/common-protocol.cc create mode 100644 src/libstore/tests/protocol.hh create mode 100644 unit-test-data/libstore/common-protocol/content-address.bin create mode 100644 unit-test-data/libstore/common-protocol/drv-output.bin create mode 100644 unit-test-data/libstore/common-protocol/optional-content-address.bin create mode 100644 unit-test-data/libstore/common-protocol/optional-store-path.bin create mode 100644 unit-test-data/libstore/common-protocol/realisation.bin create mode 100644 unit-test-data/libstore/common-protocol/set.bin create mode 100644 unit-test-data/libstore/common-protocol/store-path.bin create mode 100644 unit-test-data/libstore/common-protocol/string.bin create mode 100644 unit-test-data/libstore/common-protocol/vector.bin diff --git a/src/libstore/tests/characterization.hh b/src/libstore/tests/characterization.hh new file mode 100644 index 000000000..5f366cb42 --- /dev/null +++ b/src/libstore/tests/characterization.hh @@ -0,0 +1,23 @@ +#pragma once +///@file + +namespace nix { + +/** + * The path to the `unit-test-data` directory. See the contributing + * guide in the manual for further details. + */ +static Path getUnitTestData() { + return getEnv("_NIX_TEST_UNIT_DATA").value(); +} + +/** + * Whether we should update "golden masters" instead of running tests + * against them. See the contributing guide in the manual for further + * details. + */ +static bool testAccept() { + return getEnv("_NIX_TEST_ACCEPT") == "1"; +} + +} diff --git a/src/libstore/tests/common-protocol.cc b/src/libstore/tests/common-protocol.cc new file mode 100644 index 000000000..de57211f0 --- /dev/null +++ b/src/libstore/tests/common-protocol.cc @@ -0,0 +1,152 @@ +#include + +#include +#include + +#include "common-protocol.hh" +#include "common-protocol-impl.hh" +#include "build-result.hh" +#include "tests/protocol.hh" +#include "tests/characterization.hh" + +namespace nix { + +const char commonProtoDir[] = "common-protocol"; + +using CommonProtoTest = ProtoTest; + +CHARACTERIZATION_TEST( + CommonProtoTest, + string, + "string", + (std::tuple { + "", + "hi", + "white rabbit", + "大白兔", + "oh no \0\0\0 what was that!", + })) + +CHARACTERIZATION_TEST( + CommonProtoTest, + storePath, + "store-path", + (std::tuple { + StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo" }, + StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo-bar" }, + })) + +CHARACTERIZATION_TEST( + CommonProtoTest, + contentAddress, + "content-address", + (std::tuple { + ContentAddress { + .method = TextIngestionMethod {}, + .hash = hashString(HashType::htSHA256, "Derive(...)"), + }, + ContentAddress { + .method = FileIngestionMethod::Flat, + .hash = hashString(HashType::htSHA1, "blob blob..."), + }, + ContentAddress { + .method = FileIngestionMethod::Recursive, + .hash = hashString(HashType::htSHA256, "(...)"), + }, + })) + +CHARACTERIZATION_TEST( + CommonProtoTest, + drvOutput, + "drv-output", + (std::tuple { + { + .drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="), + .outputName = "baz", + }, + DrvOutput { + .drvHash = Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="), + .outputName = "quux", + }, + })) + +CHARACTERIZATION_TEST( + CommonProtoTest, + realisation, + "realisation", + (std::tuple { + Realisation { + .id = DrvOutput { + .drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="), + .outputName = "baz", + }, + .outPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo" }, + .signatures = { "asdf", "qwer" }, + }, + Realisation { + .id = { + .drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="), + .outputName = "baz", + }, + .outPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo" }, + .signatures = { "asdf", "qwer" }, + .dependentRealisations = { + { + DrvOutput { + .drvHash = Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="), + .outputName = "quux", + }, + StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo" }, + }, + }, + }, + })) + +CHARACTERIZATION_TEST( + CommonProtoTest, + vector, + "vector", + (std::tuple, std::vector, std::vector, std::vector>> { + { }, + { "" }, + { "", "foo", "bar" }, + { {}, { "" }, { "", "1", "2" } }, + })) + +CHARACTERIZATION_TEST( + CommonProtoTest, + set, + "set", + (std::tuple, std::set, std::set, std::set>> { + { }, + { "" }, + { "", "foo", "bar" }, + { {}, { "" }, { "", "1", "2" } }, + })) + +CHARACTERIZATION_TEST( + CommonProtoTest, + optionalStorePath, + "optional-store-path", + (std::tuple, std::optional> { + std::nullopt, + std::optional { + StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo-bar" }, + }, + })) + +CHARACTERIZATION_TEST( + CommonProtoTest, + optionalContentAddress, + "optional-content-address", + (std::tuple, std::optional> { + std::nullopt, + std::optional { + ContentAddress { + .method = FileIngestionMethod::Flat, + .hash = hashString(HashType::htSHA1, "blob blob..."), + }, + }, + })) + +} diff --git a/src/libstore/tests/protocol.hh b/src/libstore/tests/protocol.hh new file mode 100644 index 000000000..86a900757 --- /dev/null +++ b/src/libstore/tests/protocol.hh @@ -0,0 +1,88 @@ +#include +#include + +#include "tests/libstore.hh" +#include "tests/characterization.hh" + +namespace nix { + +template +class ProtoTest : public LibStoreTest +{ + /** + * Read this as simply `using S = Inner::Serialise;`. + * + * See `LengthPrefixedProtoHelper::S` for the same trick, and its + * rationale. + */ + template using S = typename Proto::template Serialise; + +public: + Path unitTestData = getUnitTestData() + "/libstore/" + protocolDir; + + Path goldenMaster(std::string_view testStem) { + return unitTestData + "/" + testStem + ".bin"; + } + + /** + * Golden test for `T` reading + */ + template + void readTest(PathView testStem, T value) + { + if (testAccept()) + { + GTEST_SKIP() << "Cannot read golden master because another test is also updating it"; + } + else + { + auto expected = readFile(goldenMaster(testStem)); + + T got = ({ + StringSource from { expected }; + S::read( + *store, + typename Proto::ReadConn { .from = from }); + }); + + ASSERT_EQ(got, value); + } + } + + /** + * Golden test for `T` write + */ + template + void writeTest(PathView testStem, const T & value) + { + auto file = goldenMaster(testStem); + + StringSink to; + Proto::write( + *store, + typename Proto::WriteConn { .to = to }, + value); + + if (testAccept()) + { + createDirs(dirOf(file)); + writeFile(file, to.s); + GTEST_SKIP() << "Updating golden master"; + } + else + { + auto expected = readFile(file); + ASSERT_EQ(to.s, expected); + } + } +}; + +#define CHARACTERIZATION_TEST(FIXTURE, NAME, STEM, VALUE) \ + TEST_F(FIXTURE, NAME ## _read) { \ + readTest(STEM, VALUE); \ + } \ + TEST_F(FIXTURE, NAME ## _write) { \ + writeTest(STEM, VALUE); \ + } + +} diff --git a/src/libstore/tests/worker-protocol.cc b/src/libstore/tests/worker-protocol.cc index fa7cbe121..59d7ff96d 100644 --- a/src/libstore/tests/worker-protocol.cc +++ b/src/libstore/tests/worker-protocol.cc @@ -7,85 +7,17 @@ #include "worker-protocol-impl.hh" #include "derived-path.hh" #include "build-result.hh" -#include "tests/libstore.hh" +#include "tests/protocol.hh" +#include "tests/characterization.hh" namespace nix { -class WorkerProtoTest : public LibStoreTest -{ -public: - Path unitTestData = getEnv("_NIX_TEST_UNIT_DATA").value() + "/libstore/worker-protocol"; +const char workerProtoDir[] = "worker-protocol"; - bool testAccept() { - return getEnv("_NIX_TEST_ACCEPT") == "1"; - } - - Path goldenMaster(std::string_view testStem) { - return unitTestData + "/" + testStem + ".bin"; - } - - /** - * Golden test for `T` reading - */ - template - void readTest(PathView testStem, T value) - { - if (testAccept()) - { - GTEST_SKIP() << "Cannot read golden master because another test is also updating it"; - } - else - { - auto expected = readFile(goldenMaster(testStem)); - - T got = ({ - StringSource from { expected }; - WorkerProto::Serialise::read( - *store, - WorkerProto::ReadConn { .from = from }); - }); - - ASSERT_EQ(got, value); - } - } - - /** - * Golden test for `T` write - */ - template - void writeTest(PathView testStem, const T & value) - { - auto file = goldenMaster(testStem); - - StringSink to; - WorkerProto::write( - *store, - WorkerProto::WriteConn { .to = to }, - value); - - if (testAccept()) - { - createDirs(dirOf(file)); - writeFile(file, to.s); - GTEST_SKIP() << "Updating golden master"; - } - else - { - auto expected = readFile(file); - ASSERT_EQ(to.s, expected); - } - } -}; - -#define CHARACTERIZATION_TEST(NAME, STEM, VALUE) \ - TEST_F(WorkerProtoTest, NAME ## _read) { \ - readTest(STEM, VALUE); \ - } \ - TEST_F(WorkerProtoTest, NAME ## _write) { \ - writeTest(STEM, VALUE); \ - } +using WorkerProtoTest = ProtoTest; CHARACTERIZATION_TEST( + WorkerProtoTest, string, "string", (std::tuple { @@ -97,6 +29,7 @@ CHARACTERIZATION_TEST( })) CHARACTERIZATION_TEST( + WorkerProtoTest, storePath, "store-path", (std::tuple { @@ -105,6 +38,7 @@ CHARACTERIZATION_TEST( })) CHARACTERIZATION_TEST( + WorkerProtoTest, contentAddress, "content-address", (std::tuple { @@ -123,6 +57,7 @@ CHARACTERIZATION_TEST( })) CHARACTERIZATION_TEST( + WorkerProtoTest, derivedPath, "derived-path", (std::tuple { @@ -138,6 +73,7 @@ CHARACTERIZATION_TEST( })) CHARACTERIZATION_TEST( + WorkerProtoTest, drvOutput, "drv-output", (std::tuple { @@ -152,6 +88,7 @@ CHARACTERIZATION_TEST( })) CHARACTERIZATION_TEST( + WorkerProtoTest, realisation, "realisation", (std::tuple { @@ -183,6 +120,7 @@ CHARACTERIZATION_TEST( })) CHARACTERIZATION_TEST( + WorkerProtoTest, buildResult, "build-result", ({ @@ -240,6 +178,7 @@ CHARACTERIZATION_TEST( })) CHARACTERIZATION_TEST( + WorkerProtoTest, keyedBuildResult, "keyed-build-result", ({ @@ -275,6 +214,7 @@ CHARACTERIZATION_TEST( })) CHARACTERIZATION_TEST( + WorkerProtoTest, optionalTrustedFlag, "optional-trusted-flag", (std::tuple, std::optional, std::optional> { @@ -284,6 +224,7 @@ CHARACTERIZATION_TEST( })) CHARACTERIZATION_TEST( + WorkerProtoTest, vector, "vector", (std::tuple, std::vector, std::vector, std::vector>> { @@ -294,6 +235,7 @@ CHARACTERIZATION_TEST( })) CHARACTERIZATION_TEST( + WorkerProtoTest, set, "set", (std::tuple, std::set, std::set, std::set>> { @@ -304,6 +246,7 @@ CHARACTERIZATION_TEST( })) CHARACTERIZATION_TEST( + WorkerProtoTest, optionalStorePath, "optional-store-path", (std::tuple, std::optional> { @@ -314,6 +257,7 @@ CHARACTERIZATION_TEST( })) CHARACTERIZATION_TEST( + WorkerProtoTest, optionalContentAddress, "optional-content-address", (std::tuple, std::optional> { diff --git a/unit-test-data/libstore/common-protocol/content-address.bin b/unit-test-data/libstore/common-protocol/content-address.bin new file mode 100644 index 0000000000000000000000000000000000000000..8f14bcdb3e50cc72d87106ce159a6fd7b9f75713 GIT binary patch literal 208 zcmY+;K@P$o5QSmy;|7WYrYjRqQfMm$+LWPzDW_MfG4bucKks(>Y#V56lkFOiEzi24 zUMdC5VUCa86OJ&gL0BF^B{HBQEY%f|I<6v7#q+l_PBirI5N~-md%37WE|g33QTE2k zc~-WF&R}7Oxc@o)T?ojpD!+*5=xpO;%IoCKSV5FMh={>xePK|8<${>E)*huNHZ(pX literal 0 HcmV?d00001 diff --git a/unit-test-data/libstore/common-protocol/drv-output.bin b/unit-test-data/libstore/common-protocol/drv-output.bin new file mode 100644 index 0000000000000000000000000000000000000000..800a45fd8757a15e8810066aed48be56a600b7d3 GIT binary patch literal 176 zcmY++xeWp_5I|8{*$(Wn=b{B@!gGlfp_LHTb8N(qe)KM%U7Sq@}q)U|6h9n2j1!MT?w+C~^gO WO9=?G&ojrYjy`x4ZufnEe#tjMpDPdm literal 0 HcmV?d00001 diff --git a/unit-test-data/libstore/common-protocol/optional-content-address.bin b/unit-test-data/libstore/common-protocol/optional-content-address.bin new file mode 100644 index 0000000000000000000000000000000000000000..f8cfe65ba27fd78ec7787eed7ebec8dfdead2fba GIT binary patch literal 64 zcmZQzfB#ECTBU06lLKJOBUy literal 0 HcmV?d00001 diff --git a/unit-test-data/libstore/common-protocol/realisation.bin b/unit-test-data/libstore/common-protocol/realisation.bin new file mode 100644 index 0000000000000000000000000000000000000000..2176c6c4afd96b32fe372de6084ac7f4c7a11d49 GIT binary patch literal 520 zcmdUrO-{o=429t+opq7s&z_l_03#3x z?rW}!>Uc!$VBY>Sr2mUC`<2<)qY;)1KN Po44)luetw6d9AunL$;dR literal 0 HcmV?d00001 diff --git a/unit-test-data/libstore/common-protocol/set.bin b/unit-test-data/libstore/common-protocol/set.bin new file mode 100644 index 0000000000000000000000000000000000000000..ce11ede7fe7aa061a30b211a18cd7a336f879de8 GIT binary patch literal 152 ucmZQzfB;4)4WpQ03@8obCnXkvMPU52{CpHXOdBEdVDg4g4KThDln(&+h63;a literal 0 HcmV?d00001 diff --git a/unit-test-data/libstore/common-protocol/store-path.bin b/unit-test-data/libstore/common-protocol/store-path.bin new file mode 100644 index 0000000000000000000000000000000000000000..3fc05f2981d3398ab30ec34125c903cdcefa8729 GIT binary patch literal 120 tcmdOAfB^lx%nJSDlKi4n{dB`}^NdR4LR_?NT7Eu*F?srQlM;)-IsqP{BM|@q literal 0 HcmV?d00001 diff --git a/unit-test-data/libstore/common-protocol/string.bin b/unit-test-data/libstore/common-protocol/string.bin new file mode 100644 index 0000000000000000000000000000000000000000..aa7b5a604745b735473ec34283177e69320776e4 GIT binary patch literal 88 zcmZQzfB+^aoskJ)@Id+H8JQ)i3Pp)YNtq=eAx^0H( Date: Tue, 10 Oct 2023 00:32:36 +0200 Subject: [PATCH 114/116] annotate admonitions showing syntax also fix typos --- .../src/language/constructs/lookup-path.md | 2 +- doc/manual/src/language/operators.md | 16 ++++++++++++++++ doc/manual/src/language/values.md | 6 +++++- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/doc/manual/src/language/constructs/lookup-path.md b/doc/manual/src/language/constructs/lookup-path.md index 8ec835309..e87d2922b 100644 --- a/doc/manual/src/language/constructs/lookup-path.md +++ b/doc/manual/src/language/constructs/lookup-path.md @@ -2,7 +2,7 @@ > **Syntax** > -> *lookup-path* = `<` *identifier* [ `/` *identifier* `]`... `>` +> *lookup-path* = `<` *identifier* [ `/` *identifier* ]... `>` A lookup path is an identifier with an optional path suffix that resolves to a [path value](@docroot@/language/values.md#type-path) if the identifier matches a search path entry. diff --git a/doc/manual/src/language/operators.md b/doc/manual/src/language/operators.md index f8382ae19..a22c71109 100644 --- a/doc/manual/src/language/operators.md +++ b/doc/manual/src/language/operators.md @@ -35,6 +35,8 @@ ## Attribute selection +> **Syntax** +> > *attrset* `.` *attrpath* \[ `or` *expr* \] Select the attribute denoted by attribute path *attrpath* from [attribute set] *attrset*. @@ -42,12 +44,16 @@ If the attribute doesn’t exist, return the *expr* after `or` if provided, othe An attribute path is a dot-separated list of [attribute names](./values.md#attribute-set). +> **Syntax** +> > *attrpath* = *name* [ `.` *name* ]... [Attribute selection]: #attribute-selection ## Has attribute +> **Syntax** +> > *attrset* `?` *attrpath* Test whether [attribute set] *attrset* contains the attribute denoted by *attrpath*. @@ -70,6 +76,8 @@ The `+` operator is overloaded to also work on strings and paths. ## String concatenation +> **Syntax** +> > *string* `+` *string* Concatenate two [string]s and merge their string contexts. @@ -78,6 +86,8 @@ Concatenate two [string]s and merge their string contexts. ## Path concatenation +> **Syntax** +> > *path* `+` *path* Concatenate two [path]s. @@ -87,6 +97,8 @@ The result is a path. ## Path and string concatenation +> **Syntax** +> > *path* + *string* Concatenate *[path]* with *[string]*. @@ -100,6 +112,8 @@ The result is a path. ## String and path concatenation +> **Syntax** +> > *string* + *path* Concatenate *[string]* with *[path]*. @@ -117,6 +131,8 @@ The result is a string. ## Update +> **Syntax** +> > *attrset1* // *attrset2* Update [attribute set] *attrset1* with names and values from *attrset2*. diff --git a/doc/manual/src/language/values.md b/doc/manual/src/language/values.md index 8856d33ae..0bb656746 100644 --- a/doc/manual/src/language/values.md +++ b/doc/manual/src/language/values.md @@ -162,13 +162,17 @@ An attribute set is a collection of name-value-pairs (called *attributes*) enclo An attribute name can be an identifier or a [string](#string). An identifier must start with a letter (`a-z`, `A-Z`) or underscore (`_`), and can otherwise contain letters (`a-z`, `A-Z`), numbers (`0-9`), underscores (`_`), apostrophes (`'`), or dashes (`-`). +> **Syntax** +> > *name* = *identifier* | *string* \ > *identifier* ~ `[a-zA-Z_][a-zA-Z0-9_'-]*` Names and values are separated by an equal sign (`=`). Each value is an arbitrary expression terminated by a semicolon (`;`). -> *attrset* = `{` [ *name* `=` *expr* `;` `]`... `}` +> **Syntax** +> +> *attrset* = `{` [ *name* `=` *expr* `;` ]... `}` Attributes can appear in any order. An attribute name may only occur once. From 7642894a4e1c9916f92a234c8cbaef604a7b7d00 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Sat, 7 Oct 2023 03:49:49 +0200 Subject: [PATCH 115/116] reword documentation on lookup path resolution --- src/libexpr/primops.cc | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index bfcac7510..3bed7c5aa 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1698,13 +1698,14 @@ static void prim_findFile(EvalState & state, const PosIdx pos, Value * * args, V static RegisterPrimOp primop_findFile(PrimOp { .name = "__findFile", - .args = {"search path", "lookup path"}, + .args = {"search-path", "lookup-path"}, .doc = R"( - Look up the given path with the given search path. + Find *lookup-path* in *search-path*. - A search path is represented list of [attribute sets](./values.md#attribute-set) with two attributes, `prefix`, and `path`. - `prefix` is a relative path. - `path` denotes a file system location; the exact syntax depends on the command line interface. + A search path is represented list of [attribute sets](./values.md#attribute-set) with two attributes: + - `prefix` is a relative path. + - `path` denotes a file system location + The exact syntax depends on the command line interface. Examples of search path attribute sets: @@ -1722,13 +1723,12 @@ static RegisterPrimOp primop_findFile(PrimOp { } ``` - The lookup algorithm checks each entry until a match is found, returning a [path value](@docroot@/language/values.html#type-path) of the match. + The lookup algorithm checks each entry until a match is found, returning a [path value](@docroot@/language/values.html#type-path) of the match: - This is the process for each entry: - If the lookup path matches `prefix`, then the remainder of the lookup path (the "suffix") is searched for within the directory denoted by `patch`. - Note that the `path` may need to be downloaded at this point to look inside. - If the suffix is found inside that directory, then the entry is a match; - the combined absolute path of the directory (now downloaded if need be) and the suffix is returned. + - If *lookup-path* matches `prefix`, then the remainder of *lookup-path* (the "suffix") is searched for within the directory denoted by `path`. + Note that the `path` may need to be downloaded at this point to look inside. + - If the suffix is found inside that directory, then the entry is a match. + The combined absolute path of the directory (now downloaded if need be) and the suffix is returned. [Lookup path](@docroot@/language/constructs/lookup-path.md) expressions can be [desugared](https://en.wikipedia.org/wiki/Syntactic_sugar) using this and [`builtins.nixPath`](@docroot@/language/builtin-constants.md#builtins-nixPath): From 94e91566ed7f1df778468862204e7495a3f0f001 Mon Sep 17 00:00:00 2001 From: Ninlives Date: Wed, 11 Oct 2023 19:58:42 +0800 Subject: [PATCH 116/116] Allow CLI to pass environment variables to FOD builder (#8830) Add a new experimental `impure-env` setting that is a key-value list of environment variables to inject into FOD derivations that specify the corresponding `impureEnvVars`. This allows clients to make use of this feature (without having to change the environment of the daemon itself) and might eventually deprecate the current behaviour (pick whatever is in the environment of the daemon) as it's more principled and might prevent information leakage. --- .../src/language/advanced-attributes.md | 7 ++++ src/libstore/build/local-derivation-goal.cc | 14 ++++++-- src/libstore/globals.hh | 20 +++++++++++ src/libutil/experimental-features.cc | 9 ++++- src/libutil/experimental-features.hh | 1 + tests/functional/impure-env.nix | 16 +++++++++ tests/functional/impure-env.sh | 33 +++++++++++++++++++ tests/functional/local.mk | 3 +- 8 files changed, 99 insertions(+), 4 deletions(-) create mode 100644 tests/functional/impure-env.nix create mode 100644 tests/functional/impure-env.sh diff --git a/doc/manual/src/language/advanced-attributes.md b/doc/manual/src/language/advanced-attributes.md index 7d97b24b6..4e4372331 100644 --- a/doc/manual/src/language/advanced-attributes.md +++ b/doc/manual/src/language/advanced-attributes.md @@ -112,6 +112,13 @@ Derivations can declare some infrequently used optional attributes. > environmental variables come from the environment of the > `nix-build`. + If the [`configurable-impure-env` experimental + feature](@docroot@/contributing/experimental-features.md#xp-feature-configurable-impure-env) + is enabled, these environment variables can also be controlled + through the + [`impure-env`](@docroot@/command-ref/conf-file.md#conf-impure-env) + configuration setting. + - [`outputHash`]{#adv-attr-outputHash}; [`outputHashAlgo`]{#adv-attr-outputHashAlgo}; [`outputHashMode`]{#adv-attr-outputHashMode}\ These attributes declare that the derivation is a so-called *fixed-output derivation*, which means that a cryptographic hash of diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc index 64b55ca6a..817b20b2f 100644 --- a/src/libstore/build/local-derivation-goal.cc +++ b/src/libstore/build/local-derivation-goal.cc @@ -1135,8 +1135,18 @@ void LocalDerivationGoal::initEnv() fixed-output derivations is by definition pure (since we already know the cryptographic hash of the output). */ if (!derivationType->isSandboxed()) { - for (auto & i : parsedDrv->getStringsAttr("impureEnvVars").value_or(Strings())) - env[i] = getEnv(i).value_or(""); + auto & impureEnv = settings.impureEnv.get(); + if (!impureEnv.empty()) + experimentalFeatureSettings.require(Xp::ConfigurableImpureEnv); + + for (auto & i : parsedDrv->getStringsAttr("impureEnvVars").value_or(Strings())) { + auto envVar = impureEnv.find(i); + if (envVar != impureEnv.end()) { + env[i] = envVar->second; + } else { + env[i] = getEnv(i).value_or(""); + } + } } /* Currently structured log messages piggyback on stderr, but we diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index 0569f43b9..bd793c3d6 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -4,6 +4,7 @@ #include "types.hh" #include "config.hh" #include "util.hh" +#include "experimental-features.hh" #include #include @@ -1052,6 +1053,25 @@ public: ``` )" }; + + Setting impureEnv {this, {}, "impure-env", + R"( + A list of items, each in the format of: + + - `name=value`: Set environment variable `name` to `value`. + + If the user is trusted (see `trusted-users` option), when building + a fixed-output derivation, environment variables set in this option + will be passed to the builder if they are listed in [`impureEnvVars`](@docroot@/language/advanced-attributes.md##adv-attr-impureEnvVars). + + This option is useful for, e.g., setting `https_proxy` for + fixed-output derivations and in a multi-user Nix installation, or + setting private access tokens when fetching a private repository. + )", + {}, // aliases + true, // document default + Xp::ConfigurableImpureEnv + }; }; diff --git a/src/libutil/experimental-features.cc b/src/libutil/experimental-features.cc index 203455b63..74af9aae0 100644 --- a/src/libutil/experimental-features.cc +++ b/src/libutil/experimental-features.cc @@ -12,7 +12,7 @@ struct ExperimentalFeatureDetails std::string_view description; }; -constexpr std::array xpFeatureDetails = {{ +constexpr std::array xpFeatureDetails = {{ { .tag = Xp::CaDerivations, .name = "ca-derivations", @@ -221,6 +221,13 @@ constexpr std::array xpFeatureDetails = {{ Allow the use of the `read-only` parameter in [local store](@docroot@/command-ref/new-cli/nix3-help-stores.md#local-store) URIs. )", }, + { + .tag = Xp::ConfigurableImpureEnv, + .name = "configurable-impure-env", + .description = R"( + Allow the use of the [impure-env](@docroot@/command-ref/conf-file.md#conf-impure-env) setting. + )", + } }}; static_assert( diff --git a/src/libutil/experimental-features.hh b/src/libutil/experimental-features.hh index add592ae6..e02f8353e 100644 --- a/src/libutil/experimental-features.hh +++ b/src/libutil/experimental-features.hh @@ -31,6 +31,7 @@ enum struct ExperimentalFeature DynamicDerivations, ParseTomlTimestamps, ReadOnlyLocalStore, + ConfigurableImpureEnv, }; /** diff --git a/tests/functional/impure-env.nix b/tests/functional/impure-env.nix new file mode 100644 index 000000000..2b0380ed7 --- /dev/null +++ b/tests/functional/impure-env.nix @@ -0,0 +1,16 @@ +{ var, value }: + +with import ./config.nix; + +mkDerivation { + name = "test"; + buildCommand = '' + echo ${var} = "''$${var}" + echo -n "''$${var}" > "$out" + ''; + + impureEnvVars = [ var ]; + + outputHashAlgo = "sha256"; + outputHash = builtins.hashString "sha256" value; +} diff --git a/tests/functional/impure-env.sh b/tests/functional/impure-env.sh new file mode 100644 index 000000000..d9e4a34a2 --- /dev/null +++ b/tests/functional/impure-env.sh @@ -0,0 +1,33 @@ +source common.sh + +# Needs the config option 'impure-env' to work +requireDaemonNewerThan "2.18.0pre20230816" + +enableFeatures "configurable-impure-env" +restartDaemon + +varTest() { + local var="$1"; shift + local value="$1"; shift + nix build --no-link -vL --argstr var "$var" --argstr value "$value" --impure "$@" --file impure-env.nix + clearStore +} + +clearStore +startDaemon + +varTest env_name value --impure-env env_name=value + +echo 'impure-env = set_in_config=config_value' >> "$NIX_CONF_DIR/nix.conf" +set_in_config=daemon_value restartDaemon + +varTest set_in_config config_value +varTest set_in_config client_value --impure-env set_in_config=client_value + +sed -i -e '/^trusted-users =/d' "$NIX_CONF_DIR/nix.conf" + +env_name=daemon_value restartDaemon + +varTest env_name daemon_value --impure-env env_name=client_value + +killDaemon diff --git a/tests/functional/local.mk b/tests/functional/local.mk index 6f6c94fe6..643351163 100644 --- a/tests/functional/local.mk +++ b/tests/functional/local.mk @@ -121,7 +121,8 @@ nix_tests = \ path-from-hash-part.sh \ toString-path.sh \ read-only-store.sh \ - nested-sandboxing.sh + nested-sandboxing.sh \ + impure-env.sh ifeq ($(HAVE_LIBCPUID), 1) nix_tests += compute-levels.sh