about summary refs log tree commit diff
path: root/nixpkgs/pkgs/test
diff options
context:
space:
mode:
Diffstat (limited to 'nixpkgs/pkgs/test')
-rw-r--r--nixpkgs/pkgs/test/auto-patchelf-hook/default.nix6
-rw-r--r--nixpkgs/pkgs/test/auto-patchelf-hook/package.nix96
-rw-r--r--nixpkgs/pkgs/test/buildFHSEnv/default.nix84
-rw-r--r--nixpkgs/pkgs/test/cc-wrapper/atomics.cc8
-rw-r--r--nixpkgs/pkgs/test/cc-wrapper/cc-main.c7
-rw-r--r--nixpkgs/pkgs/test/cc-wrapper/cflags-main.c10
-rw-r--r--nixpkgs/pkgs/test/cc-wrapper/core-foundation-main.c7
-rw-r--r--nixpkgs/pkgs/test/cc-wrapper/cxx-main.cc7
-rw-r--r--nixpkgs/pkgs/test/cc-wrapper/default.nix124
-rw-r--r--nixpkgs/pkgs/test/cc-wrapper/foo.c4
-rw-r--r--nixpkgs/pkgs/test/cc-wrapper/fortify1-example.c16
-rw-r--r--nixpkgs/pkgs/test/cc-wrapper/fortify2-example.c16
-rw-r--r--nixpkgs/pkgs/test/cc-wrapper/fortify3-example.c13
-rw-r--r--nixpkgs/pkgs/test/cc-wrapper/hardening.nix396
-rw-r--r--nixpkgs/pkgs/test/cc-wrapper/include-cxxabi.cc8
-rw-r--r--nixpkgs/pkgs/test/cc-wrapper/ldflags-main.c12
-rw-r--r--nixpkgs/pkgs/test/cc-wrapper/multilib.nix37
-rw-r--r--nixpkgs/pkgs/test/cc-wrapper/nostdinc-main.c8
-rw-r--r--nixpkgs/pkgs/test/cc-wrapper/sanitizers.c8
-rw-r--r--nixpkgs/pkgs/test/cc-wrapper/stdio.h1
-rw-r--r--nixpkgs/pkgs/test/checkpointBuild/default.nix57
-rw-r--r--nixpkgs/pkgs/test/checkpointBuild/hello-additionalFile.patch67
-rw-r--r--nixpkgs/pkgs/test/checkpointBuild/hello-removeFile.patch67
-rw-r--r--nixpkgs/pkgs/test/checkpointBuild/hello.patch26
-rw-r--r--nixpkgs/pkgs/test/config.nix24
-rw-r--r--nixpkgs/pkgs/test/coq/default.nix6
-rw-r--r--nixpkgs/pkgs/test/coq/overrideCoqDerivation/default.nix40
-rw-r--r--nixpkgs/pkgs/test/cross/default.nix169
-rw-r--r--nixpkgs/pkgs/test/cuda/default.nix51
-rw-r--r--nixpkgs/pkgs/test/cue/default.nix24
-rw-r--r--nixpkgs/pkgs/test/default.nix183
-rw-r--r--nixpkgs/pkgs/test/dhall/buildDhallUrl/default.nix14
-rw-r--r--nixpkgs/pkgs/test/dhall/default.nix6
-rw-r--r--nixpkgs/pkgs/test/dhall/generateDhallDirectoryPackage/default.nix17
-rw-r--r--nixpkgs/pkgs/test/dotnet/default.nix5
-rw-r--r--nixpkgs/pkgs/test/dotnet/project-references/application/Application.cs1
-rw-r--r--nixpkgs/pkgs/test/dotnet/project-references/application/Application.csproj10
-rw-r--r--nixpkgs/pkgs/test/dotnet/project-references/default.nix38
-rw-r--r--nixpkgs/pkgs/test/dotnet/project-references/library/Library.cs8
-rw-r--r--nixpkgs/pkgs/test/dotnet/project-references/library/Library.csproj5
-rw-r--r--nixpkgs/pkgs/test/dotnet/project-references/nuget-deps.nix5
-rw-r--r--nixpkgs/pkgs/test/haskell/cabalSdist/default.nix28
-rw-r--r--nixpkgs/pkgs/test/haskell/cabalSdist/local/CHANGELOG.md5
-rw-r--r--nixpkgs/pkgs/test/haskell/cabalSdist/local/app/Main.hs4
-rw-r--r--nixpkgs/pkgs/test/haskell/cabalSdist/local/generated.nix12
-rw-r--r--nixpkgs/pkgs/test/haskell/cabalSdist/local/local.cabal13
-rw-r--r--nixpkgs/pkgs/test/haskell/default.nix10
-rw-r--r--nixpkgs/pkgs/test/haskell/documentationTarball/default.nix21
-rw-r--r--nixpkgs/pkgs/test/haskell/incremental/default.nix35
-rw-r--r--nixpkgs/pkgs/test/haskell/setBuildTarget/Bar.hs4
-rw-r--r--nixpkgs/pkgs/test/haskell/setBuildTarget/Foo.hs4
-rw-r--r--nixpkgs/pkgs/test/haskell/setBuildTarget/Setup.hs2
-rw-r--r--nixpkgs/pkgs/test/haskell/setBuildTarget/default.nix43
-rw-r--r--nixpkgs/pkgs/test/haskell/setBuildTarget/haskell-setBuildTarget.cabal16
-rw-r--r--nixpkgs/pkgs/test/haskell/shellFor/default.nix57
-rw-r--r--nixpkgs/pkgs/test/haskell/upstreamStackHpackVersion/default.nix152
-rw-r--r--nixpkgs/pkgs/test/hooks/default.nix8
-rw-r--r--nixpkgs/pkgs/test/install-shell-files/default.nix125
-rw-r--r--nixpkgs/pkgs/test/kernel.nix73
-rw-r--r--nixpkgs/pkgs/test/ld-library-path/default.nix88
-rw-r--r--nixpkgs/pkgs/test/macos-sierra-shared/default.nix90
-rw-r--r--nixpkgs/pkgs/test/make-binary-wrapper/add-flags.c25
-rw-r--r--nixpkgs/pkgs/test/make-binary-wrapper/add-flags.cmdline4
-rw-r--r--nixpkgs/pkgs/test/make-binary-wrapper/add-flags.env10
-rw-r--r--nixpkgs/pkgs/test/make-binary-wrapper/argv0.c7
-rw-r--r--nixpkgs/pkgs/test/make-binary-wrapper/argv0.cmdline1
-rw-r--r--nixpkgs/pkgs/test/make-binary-wrapper/argv0.env2
-rw-r--r--nixpkgs/pkgs/test/make-binary-wrapper/basic.c7
-rw-r--r--nixpkgs/pkgs/test/make-binary-wrapper/basic.cmdline0
-rw-r--r--nixpkgs/pkgs/test/make-binary-wrapper/basic.env2
-rw-r--r--nixpkgs/pkgs/test/make-binary-wrapper/chdir.c11
-rw-r--r--nixpkgs/pkgs/test/make-binary-wrapper/chdir.cmdline1
-rw-r--r--nixpkgs/pkgs/test/make-binary-wrapper/chdir.env2
-rw-r--r--nixpkgs/pkgs/test/make-binary-wrapper/combination.c53
-rw-r--r--nixpkgs/pkgs/test/make-binary-wrapper/combination.cmdline6
-rw-r--r--nixpkgs/pkgs/test/make-binary-wrapper/combination.env8
-rw-r--r--nixpkgs/pkgs/test/make-binary-wrapper/cross.nix28
-rw-r--r--nixpkgs/pkgs/test/make-binary-wrapper/default.nix61
-rw-r--r--nixpkgs/pkgs/test/make-binary-wrapper/env.c14
-rw-r--r--nixpkgs/pkgs/test/make-binary-wrapper/env.cmdline4
-rw-r--r--nixpkgs/pkgs/test/make-binary-wrapper/env.env6
-rw-r--r--nixpkgs/pkgs/test/make-binary-wrapper/envcheck.c22
-rw-r--r--nixpkgs/pkgs/test/make-binary-wrapper/inherit-argv0.c6
-rw-r--r--nixpkgs/pkgs/test/make-binary-wrapper/inherit-argv0.cmdline1
-rw-r--r--nixpkgs/pkgs/test/make-binary-wrapper/inherit-argv0.env2
-rw-r--r--nixpkgs/pkgs/test/make-binary-wrapper/invalid-env.c14
-rw-r--r--nixpkgs/pkgs/test/make-binary-wrapper/invalid-env.cmdline2
-rw-r--r--nixpkgs/pkgs/test/make-binary-wrapper/overlength-strings.c25
-rw-r--r--nixpkgs/pkgs/test/make-binary-wrapper/overlength-strings.cmdline1
-rw-r--r--nixpkgs/pkgs/test/make-binary-wrapper/overlength-strings.env3
-rw-r--r--nixpkgs/pkgs/test/make-binary-wrapper/prefix.c26
-rw-r--r--nixpkgs/pkgs/test/make-binary-wrapper/prefix.cmdline2
-rw-r--r--nixpkgs/pkgs/test/make-binary-wrapper/prefix.env3
-rw-r--r--nixpkgs/pkgs/test/make-binary-wrapper/suffix.c26
-rw-r--r--nixpkgs/pkgs/test/make-binary-wrapper/suffix.cmdline2
-rw-r--r--nixpkgs/pkgs/test/make-binary-wrapper/suffix.env3
-rw-r--r--nixpkgs/pkgs/test/make-hardcode-gsettings-patch/default.nix77
-rw-r--r--nixpkgs/pkgs/test/make-hardcode-gsettings-patch/fixtures/example-project-patched/main.c85
-rw-r--r--nixpkgs/pkgs/test/make-hardcode-gsettings-patch/fixtures/example-project-wrapped-settings-constructor-patched/main.c15
-rw-r--r--nixpkgs/pkgs/test/make-hardcode-gsettings-patch/fixtures/example-project-wrapped-settings-constructor-resolve.patch17
-rw-r--r--nixpkgs/pkgs/test/make-hardcode-gsettings-patch/fixtures/example-project-wrapped-settings-constructor/main.c13
-rw-r--r--nixpkgs/pkgs/test/make-hardcode-gsettings-patch/fixtures/example-project/main.c49
-rw-r--r--nixpkgs/pkgs/test/make-wrapper/default.nix139
-rw-r--r--nixpkgs/pkgs/test/nixos-functions/default.nix31
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/.envrc1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/.gitignore2
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/Cargo.lock643
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/Cargo.toml23
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/README.md102
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/default.nix82
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/scripts/README.md38
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/scripts/pinned-tool.json4
-rwxr-xr-xnixpkgs/pkgs/test/nixpkgs-check-by-name/scripts/run-local.sh82
-rwxr-xr-xnixpkgs/pkgs/test/nixpkgs-check-by-name/scripts/update-pinned-tool.sh40
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/shell.nix6
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/src/eval.nix116
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/src/eval.rs559
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/src/main.rs293
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/src/nix_file.rs555
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/src/nixpkgs_problem.rs425
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/src/ratchet.rs184
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/src/references.rs176
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/src/structure.rs184
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/src/utils.rs95
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/src/validation.rs111
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/aliases/aliases.nix3
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/aliases/all-packages.nix3
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/aliases/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/aliases/expected1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/aliases/pkgs/by-name/fo/foo/package.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/alt-callPackage/all-packages.nix7
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/alt-callPackage/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/alt-callPackage/expected9
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/alt-callPackage/pkgs/by-name/fo/foo/package.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/base-fixed/base/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/base-fixed/base/pkgs/by-name/foo0
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/base-fixed/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/base-fixed/expected1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/base-fixed/pkgs/by-name/README.md0
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/base-still-broken/base/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/base-still-broken/base/pkgs/by-name/foo0
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/base-still-broken/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/base-still-broken/expected3
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/base-still-broken/pkgs/by-name/bar0
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/broken-autocall/default.nix4
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/broken-autocall/expected2
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/broken-autocall/pkgs/by-name/fo/foo/package.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/callPackage-syntax/all-packages.nix7
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/callPackage-syntax/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/callPackage-syntax/expected1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/callPackage-syntax/pkgs/by-name/README.md0
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/empty-base/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/empty-base/expected1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/empty-base/pkgs/by-name/README.md0
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/incorrect-shard/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/incorrect-shard/expected2
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/incorrect-shard/pkgs/by-name/aa/FOO/package.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/internalCallPackage/all-packages.nix4
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/internalCallPackage/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/internalCallPackage/expected2
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/internalCallPackage/foo.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/internalCallPackage/pkgs/by-name/README.md0
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/internalCallPackage/pkgs/by-name/fo/foo/package.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/invalid-package-name/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/invalid-package-name/expected2
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/invalid-package-name/pkgs/by-name/fo/fo@/package.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/invalid-shard-name/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/invalid-shard-name/expected2
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/invalid-shard-name/pkgs/by-name/A/A/.git-keep0
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/invalid-shard-name/pkgs/by-name/A/A/package.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/manual-definition/all-packages.nix10
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/manual-definition/base/all-packages.nix9
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/manual-definition/base/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/manual-definition/base/pkgs/by-name/README.md0
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/manual-definition/base/some-pkg.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/manual-definition/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/manual-definition/expected21
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/manual-definition/pkgs/by-name/no/noEval/package.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/manual-definition/pkgs/by-name/on/onlyMove/package.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/missing-package-nix/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/missing-package-nix/expected2
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/missing-package-nix/pkgs/by-name/fo/foo/.git-keep0
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/mock-nixpkgs.nix110
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/move-to-non-by-name/all-packages.nix10
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/move-to-non-by-name/base/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/move-to-non-by-name/base/pkgs/by-name/fo/foo1/package.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/move-to-non-by-name/base/pkgs/by-name/fo/foo2/package.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/move-to-non-by-name/base/pkgs/by-name/fo/foo3/package.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/move-to-non-by-name/base/pkgs/by-name/fo/foo4/package.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/move-to-non-by-name/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/move-to-non-by-name/expected13
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/move-to-non-by-name/pkgs/by-name/README.md0
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/move-to-non-by-name/with-config.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/move-to-non-by-name/without-config.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/multiple-failures/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/multiple-failures/expected14
l---------nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/multiple-failures/pkgs/by-name/A/fo@/foo1
l---------nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/multiple-failures/pkgs/by-name/A/fo@/package.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/multiple-failures/pkgs/by-name/aa0
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/multiple-failures/pkgs/by-name/ba/bar0
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/multiple-failures/pkgs/by-name/ba/baz/package.nix/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/multiple-failures/pkgs/by-name/ba/foo/package.nix6
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/multiple-failures/pkgs/by-name/fo/foo/.git-keep0
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/multiple-failures/someDrv.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/new-package-non-by-name/all-packages.nix11
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/new-package-non-by-name/base/all-packages.nix5
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/new-package-non-by-name/base/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/new-package-non-by-name/base/pkgs/by-name/README.md0
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/new-package-non-by-name/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/new-package-non-by-name/expected21
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/new-package-non-by-name/pkgs/by-name/README.md0
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/new-package-non-by-name/with-config.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/new-package-non-by-name/without-config.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/no-by-name/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/no-by-name/expected1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/no-eval/all-packages.nix5
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/no-eval/base/all-packages.nix3
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/no-eval/base/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/no-eval/base/pkgs/by-name/README.md0
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/no-eval/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/no-eval/expected1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/no-eval/pkgs/by-name/README.md0
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/non-attrs/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/non-attrs/expected2
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/non-attrs/pkgs/by-name/no/nonDerivation/package.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/non-derivation/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/non-derivation/expected2
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/non-derivation/pkgs/by-name/no/nonDerivation/package.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/non-syntactical-callPackage-by-name/all-packages.nix6
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/non-syntactical-callPackage-by-name/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/non-syntactical-callPackage-by-name/expected9
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/non-syntactical-callPackage-by-name/pkgs/by-name/fo/foo/package.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/one-letter/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/one-letter/expected1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/one-letter/pkgs/by-name/a/a/package.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/only-callPackage-derivations/all-packages.nix16
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/only-callPackage-derivations/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/only-callPackage-derivations/expected1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/only-callPackage-derivations/pkgs/by-name/README.md0
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-different-file/all-packages.nix3
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-different-file/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-different-file/expected9
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-different-file/pkgs/by-name/no/nonDerivation/package.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-different-file/someDrv.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg-gradual/all-packages.nix3
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg-gradual/base/all-packages.nix3
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg-gradual/base/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg-gradual/base/pkgs/by-name/no/nonDerivation/package.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg-gradual/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg-gradual/expected1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg-gradual/pkgs/by-name/no/nonDerivation/package.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg/all-packages.nix3
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg/base/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg/base/pkgs/by-name/README.md0
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg/expected11
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg/pkgs/by-name/no/nonDerivation/package.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-no-call-package/all-packages.nix3
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-no-call-package/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-no-call-package/expected9
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-no-call-package/pkgs/by-name/no/nonDerivation/package.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-no-file/all-packages.nix3
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-no-file/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-no-file/expected9
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-no-file/pkgs/by-name/no/nonDerivation/package.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-non-path/all-packages.nix5
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-non-path/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-non-path/expected9
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-non-path/pkgs/by-name/fo/foo/package.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-success/all-packages.nix5
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-success/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-success/expected1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-success/pkgs/by-name/fo/foo/package.nix8
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-dir-is-file/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-dir-is-file/expected2
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-dir-is-file/pkgs/by-name/fo/foo0
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-nix-dir/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-nix-dir/expected2
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-nix-dir/pkgs/by-name/fo/foo/package.nix/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-variants/all-packages.nix5
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-variants/base/all-packages.nix3
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-variants/base/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-variants/base/pkgs/by-name/fo/foo/package.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-variants/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-variants/expected1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-variants/package.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-variants/pkgs/by-name/fo/foo/package.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-absolute/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-absolute/expected2
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-absolute/pkgs/by-name/aa/aa/package.nix3
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-escape/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-escape/expected2
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-escape/pkgs/by-name/aa/aa/package.nix3
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-nix-path/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-nix-path/expected2
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-nix-path/pkgs/by-name/aa/aa/package.nix3
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-path-subexpr/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-path-subexpr/expected2
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-path-subexpr/pkgs/by-name/aa/aa/package.nix3
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-success/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-success/expected1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-success/pkgs/by-name/aa/aa/dir/default.nix2
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-success/pkgs/by-name/aa/aa/file0
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-success/pkgs/by-name/aa/aa/file.nix2
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-success/pkgs/by-name/aa/aa/package.nix5
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/shard-file/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/shard-file/expected2
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/shard-file/pkgs/by-name/fo0
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/sorted-order/all-packages.nix6
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/sorted-order/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/sorted-order/expected31
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/sorted-order/pkgs/by-name/a/a/package.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/sorted-order/pkgs/by-name/c/c/package.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/success/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/success/expected1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/success/pkgs/by-name/fo/foo/package.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/symlink-escape/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/symlink-escape/expected2
l---------nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/symlink-escape/pkgs/by-name/fo/foo/package.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/symlink-escape/someDrv.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/symlink-invalid/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/symlink-invalid/expected2
l---------nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/symlink-invalid/pkgs/by-name/fo/foo/foo1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/symlink-invalid/pkgs/by-name/fo/foo/package.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/symlink-invalid/someDrv.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/unknown-location/all-packages.nix3
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/unknown-location/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/unknown-location/expected2
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/unknown-location/pkgs/by-name/fo/foo/package.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/uppercase/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/uppercase/expected1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/uppercase/pkgs/by-name/fo/FOO/package.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/with-readme/default.nix1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/with-readme/expected1
-rw-r--r--nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/with-readme/pkgs/by-name/README.md0
-rw-r--r--nixpkgs/pkgs/test/overriding.nix66
-rw-r--r--nixpkgs/pkgs/test/php/default.nix116
-rw-r--r--nixpkgs/pkgs/test/release/default.nix46
-rw-r--r--nixpkgs/pkgs/test/simple/builder.sh43
-rw-r--r--nixpkgs/pkgs/test/stdenv-inputs/bar.c3
-rw-r--r--nixpkgs/pkgs/test/stdenv-inputs/cc-main.c7
-rw-r--r--nixpkgs/pkgs/test/stdenv-inputs/default.nix68
-rw-r--r--nixpkgs/pkgs/test/stdenv-inputs/foo.c3
-rw-r--r--nixpkgs/pkgs/test/stdenv-inputs/include-main.c13
-rw-r--r--nixpkgs/pkgs/test/stdenv-inputs/lib-main.c14
-rw-r--r--nixpkgs/pkgs/test/stdenv/default.nix302
-rw-r--r--nixpkgs/pkgs/test/stdenv/gcc-stageCompare.nix32
-rw-r--r--nixpkgs/pkgs/test/stdenv/hooks.nix136
-rw-r--r--nixpkgs/pkgs/test/stdenv/patch-shebangs.nix130
-rw-r--r--nixpkgs/pkgs/test/substitute/default.nix96
-rw-r--r--nixpkgs/pkgs/test/systemd/default.nix5
-rw-r--r--nixpkgs/pkgs/test/systemd/nixos/default.nix37
-rw-r--r--nixpkgs/pkgs/test/texlive/default.nix695
-rw-r--r--nixpkgs/pkgs/test/top-level/default.nix47
-rw-r--r--nixpkgs/pkgs/test/vim/default.nix26
355 files changed, 9518 insertions, 0 deletions
diff --git a/nixpkgs/pkgs/test/auto-patchelf-hook/default.nix b/nixpkgs/pkgs/test/auto-patchelf-hook/default.nix
new file mode 100644
index 000000000000..6e05e729fba8
--- /dev/null
+++ b/nixpkgs/pkgs/test/auto-patchelf-hook/default.nix
@@ -0,0 +1,6 @@
+{ lib, callPackage }:
+
+lib.recurseIntoAttrs {
+  withStructuredAttrs = callPackage ./package.nix { __structuredAttrs = true; };
+  withoutStructuredAttrs = callPackage ./package.nix { __structuredAttrs = false; };
+}
diff --git a/nixpkgs/pkgs/test/auto-patchelf-hook/package.nix b/nixpkgs/pkgs/test/auto-patchelf-hook/package.nix
new file mode 100644
index 000000000000..be03ee68c039
--- /dev/null
+++ b/nixpkgs/pkgs/test/auto-patchelf-hook/package.nix
@@ -0,0 +1,96 @@
+# This is a test for autoPatchelfHook. To test it, we just need a simple binary
+# which uses the hook. We took the derivation from tonelib-jam, which sounds
+# like a good candidate with a small closure, and trimmed it down.
+
+{ stdenv
+, lib
+, fetchurl
+, autoPatchelfHook
+, dpkg
+, freetype
+, curl
+# This test checks that the behavior of autoPatchelfHook is correct whether
+# __structuredAttrs
+# (https://nixos.org/manual/nix/stable/language/advanced-attributes#adv-attr-structuredAttrs)
+# is set or not. Hence __structuredAttrs is provided as a parameter.
+, __structuredAttrs
+}:
+
+let runtimeDependencies = [
+  (lib.getLib curl)
+  "/some/dep"
+  "/some/other/dep"
+]
+# A dependency with space only works with __structuredAttrs set to true.
+++ lib.lists.optional __structuredAttrs "/some/dep with space";
+in
+
+stdenv.mkDerivation {
+  name = "auto-patchelf-test";
+
+  src = fetchurl {
+    url = "https://tonelib.net/download/221222/ToneLib-Jam-amd64.deb";
+    sha256 = "sha256-c6At2lRPngQPpE7O+VY/Hsfw+QfIb3COIuHfbqqIEuM=";
+  };
+
+  unpackCmd = ''
+    dpkg -x $curSrc source
+  '';
+
+  nativeBuildInputs = [
+    dpkg
+    autoPatchelfHook
+  ];
+
+  installPhase = ''
+    mv usr $out
+  '';
+
+  buildInputs = [
+    freetype
+  ];
+
+  autoPatchelfIgnoreMissingDeps = [
+    "libGL.so.1"
+    "libasound.so.2"
+  ];
+
+  inherit runtimeDependencies;
+
+  # Additional phase performing the actual test.
+  installCheckPhase =
+    let allDeps = runtimeDependencies ++ [ (lib.getLib freetype) ];
+    in
+    ''
+      local binary="$out/bin/ToneLib-Jam"
+      local interpreter=$(patchelf --print-interpreter $binary)
+      local runpath=$(patchelf --print-rpath $binary)
+      local glibcStorePath="${stdenv.cc.libc}"
+
+      # Check that the glibc path is a prefix of the interpreter. If
+      # autoPatchelfHook ran correctly, the binary should have set the interpreter
+      # to point to the store.
+      echo "[auto-patchelf-hook-test]: Check that the interpreter is in the store"
+      test "''${interpreter#$glibcStorePath}" != "$interpreter"
+
+      readarray -td':' runpathArray < <(echo -n "$runpath")
+
+      echo "[auto-patchelf-hook-test]: Check that the runpath has the right number of entries"
+      test "''${#runpathArray[@]}" -eq ${builtins.toString (builtins.length allDeps)}
+
+      echo "[auto-patchelf-hook-test]: Check that the runpath contains the expected runtime deps"
+    ''
+    + lib.strings.concatStringsSep "\n"
+      (lib.lists.imap0
+        (i: path:
+          let iAsStr = builtins.toString i; in
+          ''
+            echo "[auto-patchelf-hook-test]: Check that entry ${iAsStr} is ${path}"
+            test "''${paths[${iAsStr}]}" = "$path"
+          '')
+        allDeps
+      );
+
+  doInstallCheck = true;
+  inherit __structuredAttrs;
+}
diff --git a/nixpkgs/pkgs/test/buildFHSEnv/default.nix b/nixpkgs/pkgs/test/buildFHSEnv/default.nix
new file mode 100644
index 000000000000..0a355e1aeeac
--- /dev/null
+++ b/nixpkgs/pkgs/test/buildFHSEnv/default.nix
@@ -0,0 +1,84 @@
+{ lib
+, buildFHSEnv
+, runCommand
+, stdenv
+, fetchurl
+, dpkg
+, glibc
+, callPackage
+}:
+
+let
+  getSharedObjectFromDebian = sharedObjectName: src: stdenv.mkDerivation  {
+    name = "${sharedObjectName}-fetcher";
+    inherit src;
+    nativeBuildInputs = [
+      dpkg
+    ];
+    dontBuild = true;
+    dontConfigure = true;
+    dontFixup = true;
+    installPhase = ''
+      echo shared objects found are:
+      ls -l usr/lib/*/
+      cp usr/lib/*/${sharedObjectName} $out
+    '';
+  };
+
+  makeSharedObjectTest = sharedObject: targetPkgs: let
+    lddFHSEnv = buildFHSEnv {
+      name = "ldd-with-ncurses-FHS-env";
+      inherit targetPkgs;
+      runScript = "ldd";
+    };
+    ldd-in-FHS = "${lddFHSEnv}/bin/${lddFHSEnv.name}";
+    ldd = "${lib.getBin glibc}/bin/ldd";
+    find_libFHSEnv = buildFHSEnv {
+      name = "ls-with-ncurses-FHS-env";
+      targetPkgs = p: [
+        p.ncurses5
+      ];
+      runScript = "find /lib/ -executable";
+    };
+    find_lib-in-FHS = "${find_libFHSEnv}/bin/${find_libFHSEnv.name}";
+  in runCommand "FHS-lib-test" {} ''
+    echo original ldd output is:
+    ${ldd} ${sharedObject}
+    lddOutput="$(${ldd-in-FHS} ${sharedObject})"
+    echo ldd output inside FHS is:
+    echo "$lddOutput"
+    if echo $lddOutput | grep -q "not found"; then
+      echo "shared object could not find all dependencies in the FHS!"
+      echo The libraries below where found in the FHS:
+      ${find_lib-in-FHS}
+      exit 1
+    else
+      echo $lddOutput > $out
+    fi
+  '';
+
+in {
+  # This test proves an issue with buildFHSEnv - don't expect it to succeed,
+  # this is discussed in https://github.com/NixOS/nixpkgs/pull/279844 .
+  libtinfo = makeSharedObjectTest (getSharedObjectFromDebian "libedit.so.2.0.70" (fetchurl {
+    url = "mirror://debian/pool/main/libe/libedit/libedit2_3.1-20221030-2_amd64.deb";
+    hash = "sha256-HPFKvycW0yedsS0GV6VzfPcAdKHnHTvfcyBmJePInOY=";
+  })) (p: let
+    ncurses' = p.ncurses.overrideAttrs (old: {
+      configureFlags = old.configureFlags ++ [ "--with-termlib" ];
+      postFixup = "";
+    });
+  in [
+    (ncurses'.override { unicodeSupport = false; })
+    p.libbsd
+  ]);
+
+  liblzma = makeSharedObjectTest (getSharedObjectFromDebian "libxml2.so.2.9.14" (fetchurl {
+    url = "mirror://debian/pool/main/libx/libxml2/libxml2_2.9.14+dfsg-1.3~deb12u1_amd64.deb";
+    hash = "sha256-NbdstwOPwclAIEpPBfM/+3nQJzU85Gk5fZrc+Pmz4ac=";
+  })) (p: [
+    p.xz
+    p.zlib
+    p.icu72
+  ]);
+}
diff --git a/nixpkgs/pkgs/test/cc-wrapper/atomics.cc b/nixpkgs/pkgs/test/cc-wrapper/atomics.cc
new file mode 100644
index 000000000000..23601ae92f0b
--- /dev/null
+++ b/nixpkgs/pkgs/test/cc-wrapper/atomics.cc
@@ -0,0 +1,8 @@
+#include <atomic>
+#include <cstdint>
+
+int main()
+{
+  std::atomic_int x = {0};
+  return !std::atomic_is_lock_free(&x);
+}
diff --git a/nixpkgs/pkgs/test/cc-wrapper/cc-main.c b/nixpkgs/pkgs/test/cc-wrapper/cc-main.c
new file mode 100644
index 000000000000..06f28bc33c69
--- /dev/null
+++ b/nixpkgs/pkgs/test/cc-wrapper/cc-main.c
@@ -0,0 +1,7 @@
+#include <stdio.h>
+
+int main(int argc, char **argv)
+{
+  fprintf(stderr, "ok\n");
+  return 0;
+}
diff --git a/nixpkgs/pkgs/test/cc-wrapper/cflags-main.c b/nixpkgs/pkgs/test/cc-wrapper/cflags-main.c
new file mode 100644
index 000000000000..9491232b5387
--- /dev/null
+++ b/nixpkgs/pkgs/test/cc-wrapper/cflags-main.c
@@ -0,0 +1,10 @@
+#include <stdio.h>
+#include <foo.h>
+
+int main(int argc, char **argv)
+{
+  if (foo() != 42)
+    return 1;
+  fprintf(stderr, "ok\n");
+  return 0;
+}
diff --git a/nixpkgs/pkgs/test/cc-wrapper/core-foundation-main.c b/nixpkgs/pkgs/test/cc-wrapper/core-foundation-main.c
new file mode 100644
index 000000000000..fb3bd3126191
--- /dev/null
+++ b/nixpkgs/pkgs/test/cc-wrapper/core-foundation-main.c
@@ -0,0 +1,7 @@
+#include <CoreFoundation/CoreFoundation.h>
+
+int main(int argc, char** argv)
+{
+  CFShow(CFSTR("ok"));
+  return 0;
+}
diff --git a/nixpkgs/pkgs/test/cc-wrapper/cxx-main.cc b/nixpkgs/pkgs/test/cc-wrapper/cxx-main.cc
new file mode 100644
index 000000000000..83f704617a46
--- /dev/null
+++ b/nixpkgs/pkgs/test/cc-wrapper/cxx-main.cc
@@ -0,0 +1,7 @@
+#include <iostream>
+
+int main(int argc, char **argv)
+{
+  std::cerr << "ok" << std::endl;
+  return 0;
+}
diff --git a/nixpkgs/pkgs/test/cc-wrapper/default.nix b/nixpkgs/pkgs/test/cc-wrapper/default.nix
new file mode 100644
index 000000000000..a0088751d4a2
--- /dev/null
+++ b/nixpkgs/pkgs/test/cc-wrapper/default.nix
@@ -0,0 +1,124 @@
+{ lib, stdenv, glibc, buildPackages }:
+
+let
+  # Sanitizers are not supported on Darwin.
+  # Sanitizer headers aren't available in older libc++ stdenvs due to a bug
+  sanitizersWorking = (stdenv.buildPlatform == stdenv.hostPlatform) && !stdenv.isDarwin && !stdenv.hostPlatform.isMusl && (
+    (stdenv.cc.isClang && lib.versionAtLeast (lib.getVersion stdenv.cc.name) "5.0.0")
+    || (stdenv.cc.isGNU && stdenv.isLinux)
+  );
+  staticLibc = lib.optionalString (stdenv.hostPlatform.libc == "glibc") "-L ${glibc.static}/lib";
+  emulator = stdenv.hostPlatform.emulator buildPackages;
+  isCxx = stdenv.cc.libcxx != null;
+  libcxxStdenvSuffix = lib.optionalString isCxx "-libcxx";
+in stdenv.mkDerivation {
+  pname = "cc-wrapper-test-${stdenv.cc.cc.pname}${libcxxStdenvSuffix}";
+  version = stdenv.cc.version;
+
+  buildCommand = ''
+    echo "Testing: ${stdenv.cc.name}" >&2
+    echo "With libc: ${stdenv.cc.libc.name}" >&2
+    set -o pipefail
+
+    NIX_DEBUG=1 $CC -v
+    NIX_DEBUG=1 $CXX -v
+
+    echo "checking whether compiler builds valid C binaries... " >&2
+    $CC -o cc-check ${./cc-main.c}
+    ${emulator} ./cc-check
+
+    echo "checking whether compiler builds valid C++ binaries... " >&2
+    $CXX -o cxx-check ${./cxx-main.cc}
+    ${emulator} ./cxx-check
+
+    # test for https://github.com/NixOS/nixpkgs/issues/214524#issuecomment-1431745905
+    # .../include/cxxabi.h:20:10: fatal error: '__cxxabi_config.h' file not found
+    # in libcxxStdenv
+    echo "checking whether cxxabi.h can be included... " >&2
+    $CXX -o include-cxxabi ${./include-cxxabi.cc}
+    ${emulator} ./include-cxxabi
+
+    # cxx doesn't have libatomic.so
+    ${lib.optionalString (!isCxx) ''
+      # https://github.com/NixOS/nixpkgs/issues/91285
+      echo "checking whether libatomic.so can be linked... " >&2
+      $CXX -shared -o atomics.so ${./atomics.cc} -latomic ${lib.optionalString (stdenv.cc.isClang && lib.versionOlder stdenv.cc.version "6.0.0" ) "-std=c++17"}
+      $READELF -d ./atomics.so | grep libatomic.so && echo "ok" >&2 || echo "failed" >&2
+    ''}
+
+    ${lib.optionalString (stdenv.isDarwin && stdenv.cc.isClang) ''
+      echo "checking whether compiler can build with CoreFoundation.framework... " >&2
+      mkdir -p foo/lib
+      $CC -framework CoreFoundation -o core-foundation-check ${./core-foundation-main.c}
+      ${emulator} ./core-foundation-check
+    ''}
+
+
+    ${lib.optionalString (!stdenv.isDarwin) ''
+      echo "checking whether compiler builds valid static C binaries... " >&2
+      $CC ${staticLibc} -static -o cc-static ${./cc-main.c}
+      ${emulator} ./cc-static
+      ${lib.optionalString (stdenv.cc.isGNU && lib.versionAtLeast (lib.getVersion stdenv.cc.name) "8.0.0") ''
+        echo "checking whether compiler builds valid static pie C binaries... " >&2
+        $CC ${staticLibc} -static-pie -o cc-static-pie ${./cc-main.c}
+        ${emulator} ./cc-static-pie
+      ''}
+    ''}
+
+    ${# See: https://github.com/llvm/llvm-project/commit/ed1d07282cc9d8e4c25d585e03e5c8a1b6f63a74
+      # `gcc` does not support this so we gate the test on `clang`
+      lib.optionalString stdenv.cc.isClang ''
+        echo "checking whether cc-wrapper accepts -- followed by positional (file) args..." >&2
+        mkdir -p positional
+
+        # Make sure `--` is not parsed as a "non flag arg"; we should get an
+        # input file error here and *not* a linker error.
+        { ! $CC --; } |& grep -q "no input files"
+
+        # And that positional file args _must_ be files (this is just testing
+        # that we remembered to put the `--` back in the args to the compiler):
+        { ! $CC -c -- -o foo ${./foo.c}; } \
+          |& grep -q "no such file or directory: '-o'"
+
+        # Now check that we accept single and multiple positional file args:
+        $CC -c -DVALUE=42 -o positional/foo.o -- ${./foo.c}
+        $CC -o positional/main -- positional/foo.o ${./ldflags-main.c}
+        ${emulator} ./positional/main
+    ''}
+
+    echo "checking whether compiler uses NIX_CFLAGS_COMPILE... " >&2
+    mkdir -p foo/include
+    cp ${./foo.c} foo/include/foo.h
+    NIX_CFLAGS_COMPILE="-Ifoo/include -DVALUE=42" $CC -o cflags-check ${./cflags-main.c}
+    ${emulator} ./cflags-check
+
+    echo "checking whether compiler uses NIX_LDFLAGS... " >&2
+    mkdir -p foo/lib
+    $CC -shared \
+      ${lib.optionalString stdenv.isDarwin "-Wl,-install_name,@rpath/libfoo.dylib"} \
+      -DVALUE=42 \
+      -o foo/lib/libfoo${stdenv.hostPlatform.extensions.sharedLibrary} \
+      ${./foo.c}
+
+    NIX_LDFLAGS="-L$NIX_BUILD_TOP/foo/lib -rpath $NIX_BUILD_TOP/foo/lib" $CC -lfoo -o ldflags-check ${./ldflags-main.c}
+    ${emulator} ./ldflags-check
+
+    echo "Check whether -nostdinc and -nostdinc++ is handled correctly" >&2
+    mkdir -p std-include
+    cp ${./stdio.h} std-include/stdio.h
+    NIX_DEBUG=1 $CC -I std-include -nostdinc -o nostdinc-main ${./nostdinc-main.c}
+    ${emulator} ./nostdinc-main
+    $CXX -I std-include -nostdinc++ -o nostdinc-main++ ${./nostdinc-main.c}
+    ${emulator} ./nostdinc-main++
+
+    ${lib.optionalString sanitizersWorking ''
+      echo "checking whether sanitizers are fully functional... ">&2
+      $CC -o sanitizers -fsanitize=address,undefined ${./sanitizers.c}
+      ASAN_OPTIONS=use_sigaltstack=0 ${emulator} ./sanitizers
+    ''}
+
+    touch $out
+  '';
+
+  meta.platforms = lib.platforms.all;
+}
diff --git a/nixpkgs/pkgs/test/cc-wrapper/foo.c b/nixpkgs/pkgs/test/cc-wrapper/foo.c
new file mode 100644
index 000000000000..8be674be3103
--- /dev/null
+++ b/nixpkgs/pkgs/test/cc-wrapper/foo.c
@@ -0,0 +1,4 @@
+unsigned int foo(void)
+{
+  return VALUE;
+}
diff --git a/nixpkgs/pkgs/test/cc-wrapper/fortify1-example.c b/nixpkgs/pkgs/test/cc-wrapper/fortify1-example.c
new file mode 100644
index 000000000000..48b9c268e728
--- /dev/null
+++ b/nixpkgs/pkgs/test/cc-wrapper/fortify1-example.c
@@ -0,0 +1,16 @@
+/* an example that should be protected by FORTIFY_SOURCE=1 */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+
+int main(int argc, char *argv[]) {
+    /* allocate on the heap so we're likely to get an
+     * over-allocation and can be more sure that a
+     * failure is because of fortify protection rather
+     * than a genuine segfault */
+    char* buffer = malloc(sizeof(char) * 7);
+    strcpy(buffer, argv[1]);
+    puts(buffer);
+    return 0;
+}
diff --git a/nixpkgs/pkgs/test/cc-wrapper/fortify2-example.c b/nixpkgs/pkgs/test/cc-wrapper/fortify2-example.c
new file mode 100644
index 000000000000..dfb5a8e87294
--- /dev/null
+++ b/nixpkgs/pkgs/test/cc-wrapper/fortify2-example.c
@@ -0,0 +1,16 @@
+/* an example that should be protected by FORTIFY_SOURCE=2 but
+ * not FORTIFY_SOURCE=1 */
+#include <stdio.h>
+#include <string.h>
+
+struct buffer_with_pad {
+    char buffer[7];
+    char pad[25];
+};
+
+int main(int argc, char *argv[]) {
+    struct buffer_with_pad b;
+    strcpy(b.buffer, argv[1]);
+    puts(b.buffer);
+    return 0;
+}
diff --git a/nixpkgs/pkgs/test/cc-wrapper/fortify3-example.c b/nixpkgs/pkgs/test/cc-wrapper/fortify3-example.c
new file mode 100644
index 000000000000..9a0a5f4792c3
--- /dev/null
+++ b/nixpkgs/pkgs/test/cc-wrapper/fortify3-example.c
@@ -0,0 +1,13 @@
+/* an example that should be protected by FORTIFY_SOURCE=3 but
+ * not FORTIFY_SOURCE=2 */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+
+int main(int argc, char *argv[]) {
+    char* buffer = malloc(atoi(argv[2]));
+    strcpy(buffer, argv[1]);
+    puts(buffer);
+    return 0;
+}
diff --git a/nixpkgs/pkgs/test/cc-wrapper/hardening.nix b/nixpkgs/pkgs/test/cc-wrapper/hardening.nix
new file mode 100644
index 000000000000..41ddaefdfea8
--- /dev/null
+++ b/nixpkgs/pkgs/test/cc-wrapper/hardening.nix
@@ -0,0 +1,396 @@
+{ lib
+, stdenv
+, runCommand
+, runCommandWith
+, runCommandCC
+, debian-devscripts
+}:
+
+let
+  # writeCBin from trivial-builders won't let us choose
+  # our own stdenv
+  writeCBinWithStdenv = codePath: stdenv': env: runCommandWith {
+    name = "test-bin";
+    stdenv = stdenv';
+    derivationArgs = {
+      inherit codePath;
+      preferLocalBuild = true;
+      allowSubstitutes = false;
+    } // env;
+  } ''
+    [ -n "$preBuild" ] && eval "$preBuild"
+    n=$out/bin/test-bin
+    mkdir -p "$(dirname "$n")"
+    cp "$codePath" code.c
+    NIX_DEBUG=1 $CC -x c code.c -O1 $TEST_EXTRA_FLAGS -o "$n"
+  '';
+
+  f1exampleWithStdEnv = writeCBinWithStdenv ./fortify1-example.c;
+  f2exampleWithStdEnv = writeCBinWithStdenv ./fortify2-example.c;
+  f3exampleWithStdEnv = writeCBinWithStdenv ./fortify3-example.c;
+
+  stdenvUnsupport = additionalUnsupported: stdenv.override {
+    cc = stdenv.cc.override {
+      cc = (lib.extendDerivation true {
+        hardeningUnsupportedFlags = (stdenv.cc.cc.hardeningUnsupportedFlags or []) ++ additionalUnsupported;
+      } stdenv.cc.cc);
+    };
+    allowedRequisites = null;
+  };
+
+  checkTestBin = testBin: {
+    # can only test flags that are detectable by hardening-check
+    ignoreBindNow ? true,
+    ignoreFortify ? true,
+    ignorePie ? true,
+    ignoreRelRO ? true,
+    ignoreStackProtector ? true,
+    expectFailure ? false,
+  }: let
+    expectFailureClause = lib.optionalString expectFailure
+      " && echo 'ERROR: Expected hardening-check to fail, but it passed!' >&2 && exit 1";
+  in runCommandCC "check-test-bin" {
+    nativeBuildInputs = [ debian-devscripts ];
+    buildInputs = [ testBin ];
+    meta.platforms = lib.platforms.linux;  # ELF-reliant
+  } ''
+    hardening-check --nocfprotection \
+      ${lib.optionalString ignoreBindNow "--nobindnow"} \
+      ${lib.optionalString ignoreFortify "--nofortify"} \
+      ${lib.optionalString ignorePie "--nopie"} \
+      ${lib.optionalString ignoreRelRO "--norelro"} \
+      ${lib.optionalString ignoreStackProtector "--nostackprotector"} \
+      $(PATH=$HOST_PATH type -P test-bin) ${expectFailureClause}
+    touch $out
+  '';
+
+  nameDrvAfterAttrName = builtins.mapAttrs (name: drv:
+    drv.overrideAttrs (_: { name = "test-${name}"; })
+  );
+
+  # returning a specific exit code when aborting due to a fortify
+  # check isn't mandated. so it's better to just ensure that a
+  # nonzero exit code is returned when we go a single byte beyond
+  # the buffer, with the example programs being designed to be
+  # unlikely to genuinely segfault for such a small overflow.
+  fortifyExecTest = testBin: runCommand "exec-test" {
+    buildInputs = [
+      testBin
+    ];
+    meta.broken = !(stdenv.buildPlatform.canExecute stdenv.hostPlatform);
+  } ''
+    (
+      export PATH=$HOST_PATH
+      echo "Saturated buffer:" # check program isn't completly broken
+      test-bin 012345 7
+      echo "One byte too far:" # eighth byte being the null terminator
+      (! test-bin 0123456 7) || (echo 'Expected failure, but succeeded!' && exit 1)
+    )
+    echo "Expected behaviour observed"
+    touch $out
+  '';
+
+  brokenIf = cond: drv: if cond then drv.overrideAttrs (old: { meta = old.meta or {} // { broken = true; }; }) else drv;
+
+in nameDrvAfterAttrName ({
+  bindNowExplicitEnabled = brokenIf stdenv.hostPlatform.isStatic (checkTestBin (f2exampleWithStdEnv stdenv {
+    hardeningEnable = [ "bindnow" ];
+  }) {
+    ignoreBindNow = false;
+  });
+
+  # musl implementation undetectable by this means even if present
+  fortifyExplicitEnabled = brokenIf stdenv.hostPlatform.isMusl (checkTestBin (f2exampleWithStdEnv stdenv {
+    hardeningEnable = [ "fortify" ];
+  }) {
+    ignoreFortify = false;
+  });
+
+  fortify1ExplicitEnabledExecTest = fortifyExecTest (f1exampleWithStdEnv stdenv {
+    hardeningEnable = [ "fortify" ];
+  });
+
+  # musl implementation is effectively FORTIFY_SOURCE=1-only,
+  # clang-on-glibc also only appears to support FORTIFY_SOURCE=1 (!)
+  fortifyExplicitEnabledExecTest = brokenIf (
+    stdenv.hostPlatform.isMusl || (stdenv.cc.isClang && stdenv.hostPlatform.libc == "glibc")
+  ) (fortifyExecTest (f2exampleWithStdEnv stdenv {
+    hardeningEnable = [ "fortify" ];
+  }));
+
+  fortify3ExplicitEnabled = brokenIf (
+    stdenv.hostPlatform.isMusl || !stdenv.cc.isGNU || lib.versionOlder stdenv.cc.version "12"
+  ) (checkTestBin (f3exampleWithStdEnv stdenv {
+    hardeningEnable = [ "fortify3" ];
+  }) {
+    ignoreFortify = false;
+  });
+
+  # musl implementation is effectively FORTIFY_SOURCE=1-only
+  fortify3ExplicitEnabledExecTest = brokenIf (
+    stdenv.hostPlatform.isMusl || !stdenv.cc.isGNU || lib.versionOlder stdenv.cc.version "12"
+  ) (fortifyExecTest (f3exampleWithStdEnv stdenv {
+    hardeningEnable = [ "fortify3" ];
+  }));
+
+  pieExplicitEnabled = brokenIf stdenv.hostPlatform.isStatic (checkTestBin (f2exampleWithStdEnv stdenv {
+    hardeningEnable = [ "pie" ];
+  }) {
+    ignorePie = false;
+  });
+
+  relROExplicitEnabled = checkTestBin (f2exampleWithStdEnv stdenv {
+    hardeningEnable = [ "relro" ];
+  }) {
+    ignoreRelRO = false;
+  };
+
+  stackProtectorExplicitEnabled = brokenIf stdenv.hostPlatform.isStatic (checkTestBin (f2exampleWithStdEnv stdenv {
+    hardeningEnable = [ "stackprotector" ];
+  }) {
+    ignoreStackProtector = false;
+  });
+
+  bindNowExplicitDisabled = checkTestBin (f2exampleWithStdEnv stdenv {
+    hardeningDisable = [ "bindnow" ];
+  }) {
+    ignoreBindNow = false;
+    expectFailure = true;
+  };
+
+  fortifyExplicitDisabled = checkTestBin (f2exampleWithStdEnv stdenv {
+    hardeningDisable = [ "fortify" ];
+  }) {
+    ignoreFortify = false;
+    expectFailure = true;
+  };
+
+  fortify3ExplicitDisabled = checkTestBin (f3exampleWithStdEnv stdenv {
+    hardeningDisable = [ "fortify3" ];
+  }) {
+    ignoreFortify = false;
+    expectFailure = true;
+  };
+
+  fortifyExplicitDisabledDisablesFortify3 = checkTestBin (f3exampleWithStdEnv stdenv {
+    hardeningEnable = [ "fortify3" ];
+    hardeningDisable = [ "fortify" ];
+  }) {
+    ignoreFortify = false;
+    expectFailure = true;
+  };
+
+  fortify3ExplicitDisabledDoesntDisableFortify = checkTestBin (f2exampleWithStdEnv stdenv {
+    hardeningEnable = [ "fortify" ];
+    hardeningDisable = [ "fortify3" ];
+  }) {
+    ignoreFortify = false;
+  };
+
+  pieExplicitDisabled = brokenIf (
+    stdenv.hostPlatform.isMusl && stdenv.cc.isClang
+  ) (checkTestBin (f2exampleWithStdEnv stdenv {
+    hardeningDisable = [ "pie" ];
+  }) {
+    ignorePie = false;
+    expectFailure = true;
+  });
+
+  # can't force-disable ("partial"?) relro
+  relROExplicitDisabled = brokenIf true (checkTestBin (f2exampleWithStdEnv stdenv {
+    hardeningDisable = [ "pie" ];
+  }) {
+    ignoreRelRO = false;
+    expectFailure = true;
+  });
+
+  stackProtectorExplicitDisabled = checkTestBin (f2exampleWithStdEnv stdenv {
+    hardeningDisable = [ "stackprotector" ];
+  }) {
+    ignoreStackProtector = false;
+    expectFailure = true;
+  };
+
+  # most flags can't be "unsupported" by compiler alone and
+  # binutils doesn't have an accessible hardeningUnsupportedFlags
+  # mechanism, so can only test a couple of flags through altered
+  # stdenv trickery
+
+  fortifyStdenvUnsupp = checkTestBin (f2exampleWithStdEnv (stdenvUnsupport ["fortify"]) {
+    hardeningEnable = [ "fortify" ];
+  }) {
+    ignoreFortify = false;
+    expectFailure = true;
+  };
+
+  fortify3StdenvUnsupp = checkTestBin (f3exampleWithStdEnv (stdenvUnsupport ["fortify3"]) {
+    hardeningEnable = [ "fortify3" ];
+  }) {
+    ignoreFortify = false;
+    expectFailure = true;
+  };
+
+  fortifyStdenvUnsuppUnsupportsFortify3 = checkTestBin (f3exampleWithStdEnv (stdenvUnsupport ["fortify"]) {
+    hardeningEnable = [ "fortify3" ];
+  }) {
+    ignoreFortify = false;
+    expectFailure = true;
+  };
+
+  fortify3StdenvUnsuppDoesntUnsuppFortify = brokenIf stdenv.hostPlatform.isMusl (checkTestBin (f2exampleWithStdEnv (stdenvUnsupport ["fortify3"]) {
+    hardeningEnable = [ "fortify" ];
+  }) {
+    ignoreFortify = false;
+  });
+
+  fortify3StdenvUnsuppDoesntUnsuppFortifyExecTest = fortifyExecTest (f2exampleWithStdEnv (stdenvUnsupport ["fortify3"]) {
+    hardeningEnable = [ "fortify" ];
+  });
+
+  stackProtectorStdenvUnsupp = checkTestBin (f2exampleWithStdEnv (stdenvUnsupport ["stackprotector"]) {
+    hardeningEnable = [ "stackprotector" ];
+  }) {
+    ignoreStackProtector = false;
+    expectFailure = true;
+  };
+
+  # NIX_HARDENING_ENABLE set in the shell overrides hardeningDisable
+  # and hardeningEnable
+
+  stackProtectorReenabledEnv = checkTestBin (f2exampleWithStdEnv stdenv {
+    hardeningDisable = [ "stackprotector" ];
+    preBuild = ''
+      export NIX_HARDENING_ENABLE="stackprotector"
+    '';
+  }) {
+    ignoreStackProtector = false;
+  };
+
+  stackProtectorReenabledFromAllEnv = checkTestBin (f2exampleWithStdEnv stdenv {
+    hardeningDisable = [ "all" ];
+    preBuild = ''
+      export NIX_HARDENING_ENABLE="stackprotector"
+    '';
+  }) {
+    ignoreStackProtector = false;
+  };
+
+  stackProtectorRedisabledEnv = checkTestBin (f2exampleWithStdEnv stdenv {
+    hardeningEnable = [ "stackprotector" ];
+    preBuild = ''
+      export NIX_HARDENING_ENABLE=""
+    '';
+  }) {
+    ignoreStackProtector = false;
+    expectFailure = true;
+  };
+
+  fortify3EnabledEnvEnablesFortify = brokenIf stdenv.hostPlatform.isMusl (checkTestBin (f2exampleWithStdEnv stdenv {
+    hardeningDisable = [ "fortify" "fortify3" ];
+    preBuild = ''
+      export NIX_HARDENING_ENABLE="fortify3"
+    '';
+  }) {
+    ignoreFortify = false;
+  });
+
+  fortify3EnabledEnvEnablesFortifyExecTest = fortifyExecTest (f2exampleWithStdEnv stdenv {
+    hardeningDisable = [ "fortify" "fortify3" ];
+    preBuild = ''
+      export NIX_HARDENING_ENABLE="fortify3"
+    '';
+  });
+
+  fortifyEnabledEnvDoesntEnableFortify3 = checkTestBin (f3exampleWithStdEnv stdenv {
+    hardeningDisable = [ "fortify" "fortify3" ];
+    preBuild = ''
+      export NIX_HARDENING_ENABLE="fortify"
+    '';
+  }) {
+    ignoreFortify = false;
+    expectFailure = true;
+  };
+
+  # NIX_HARDENING_ENABLE can't enable an unsupported feature
+
+  stackProtectorUnsupportedEnabledEnv = checkTestBin (f2exampleWithStdEnv (stdenvUnsupport ["stackprotector"]) {
+    preBuild = ''
+      export NIX_HARDENING_ENABLE="stackprotector"
+    '';
+  }) {
+    ignoreStackProtector = false;
+    expectFailure = true;
+  };
+
+  # undetectable by this means on static even if present
+  fortify1ExplicitEnabledCmdlineDisabled = brokenIf stdenv.hostPlatform.isStatic (checkTestBin (f1exampleWithStdEnv stdenv {
+    hardeningEnable = [ "fortify" ];
+    preBuild = ''
+      export TEST_EXTRA_FLAGS='-D_FORTIFY_SOURCE=0'
+    '';
+  }) {
+    ignoreFortify = false;
+    expectFailure = true;
+  });
+
+  # musl implementation undetectable by this means even if present
+  fortify1ExplicitDisabledCmdlineEnabled = brokenIf (
+    stdenv.hostPlatform.isMusl || stdenv.hostPlatform.isStatic
+  ) (checkTestBin (f1exampleWithStdEnv stdenv {
+    hardeningDisable = [ "fortify" ];
+    preBuild = ''
+      export TEST_EXTRA_FLAGS='-D_FORTIFY_SOURCE=1'
+    '';
+  }) {
+    ignoreFortify = false;
+  });
+
+  fortify1ExplicitDisabledCmdlineEnabledExecTest = fortifyExecTest (f1exampleWithStdEnv stdenv {
+    hardeningDisable = [ "fortify" ];
+    preBuild = ''
+      export TEST_EXTRA_FLAGS='-D_FORTIFY_SOURCE=1'
+    '';
+  });
+
+  fortify1ExplicitEnabledCmdlineDisabledNoWarn = f1exampleWithStdEnv stdenv {
+    hardeningEnable = [ "fortify" ];
+    preBuild = ''
+      export TEST_EXTRA_FLAGS='-D_FORTIFY_SOURCE=0 -Werror'
+    '';
+  };
+
+} // (let
+  tb = f2exampleWithStdEnv stdenv {
+    hardeningDisable = [ "all" ];
+    hardeningEnable = [ "fortify" "pie" ];
+  };
+in {
+
+  allExplicitDisabledBindNow = checkTestBin tb {
+    ignoreBindNow = false;
+    expectFailure = true;
+  };
+
+  allExplicitDisabledFortify = checkTestBin tb {
+    ignoreFortify = false;
+    expectFailure = true;
+  };
+
+  allExplicitDisabledPie = brokenIf (
+    stdenv.hostPlatform.isMusl && stdenv.cc.isClang
+  ) (checkTestBin tb {
+    ignorePie = false;
+    expectFailure = true;
+  });
+
+  # can't force-disable ("partial"?) relro
+  allExplicitDisabledRelRO = brokenIf true (checkTestBin tb {
+    ignoreRelRO = false;
+    expectFailure = true;
+  });
+
+  allExplicitDisabledStackProtector = checkTestBin tb {
+    ignoreStackProtector = false;
+    expectFailure = true;
+  };
+}))
diff --git a/nixpkgs/pkgs/test/cc-wrapper/include-cxxabi.cc b/nixpkgs/pkgs/test/cc-wrapper/include-cxxabi.cc
new file mode 100644
index 000000000000..6ffc97e414a5
--- /dev/null
+++ b/nixpkgs/pkgs/test/cc-wrapper/include-cxxabi.cc
@@ -0,0 +1,8 @@
+#include <cxxabi.h>
+#include <iostream>
+
+int main(int argc, char **argv)
+{
+  std::cerr << "ok" << std::endl;
+  return 0;
+}
diff --git a/nixpkgs/pkgs/test/cc-wrapper/ldflags-main.c b/nixpkgs/pkgs/test/cc-wrapper/ldflags-main.c
new file mode 100644
index 000000000000..89832b3bbad2
--- /dev/null
+++ b/nixpkgs/pkgs/test/cc-wrapper/ldflags-main.c
@@ -0,0 +1,12 @@
+#include <stdio.h>
+
+extern unsigned int foo(void);
+
+int main(int argc, char **argv)
+{
+  if (foo() != 42) {
+    return 1;
+  }
+  fprintf(stderr, "ok\n");
+  return 0;
+}
diff --git a/nixpkgs/pkgs/test/cc-wrapper/multilib.nix b/nixpkgs/pkgs/test/cc-wrapper/multilib.nix
new file mode 100644
index 000000000000..a26880681f22
--- /dev/null
+++ b/nixpkgs/pkgs/test/cc-wrapper/multilib.nix
@@ -0,0 +1,37 @@
+{ lib, stdenv }:
+
+stdenv.mkDerivation {
+  name = "cc-multilib-test";
+
+  # XXX: "depend" on cc-wrapper test?
+
+  # TODO: Have tests report pointer size or something; ensure they are what we asked for
+  buildCommand = ''
+    NIX_DEBUG=1 $CC -v
+    NIX_DEBUG=1 $CXX -v
+
+    printf "checking whether compiler builds valid C binaries...\n " >&2
+    $CC -o cc-check ${./cc-main.c}
+    ./cc-check
+
+    printf "checking whether compiler builds valid 32bit C binaries...\n " >&2
+    $CC -m32 -o c32-check ${./cc-main.c}
+    ./c32-check
+
+    printf "checking whether compiler builds valid 64bit C binaries...\n " >&2
+    $CC -m64 -o c64-check ${./cc-main.c}
+    ./c64-check
+
+    printf "checking whether compiler builds valid 32bit C++ binaries...\n " >&2
+    $CXX -m32 -o cxx32-check ${./cxx-main.cc}
+    ./cxx32-check
+
+    printf "checking whether compiler builds valid 64bit C++ binaries...\n " >&2
+    $CXX -m64 -o cxx64-check ${./cxx-main.cc}
+    ./cxx64-check
+
+    touch $out
+  '';
+
+  meta.platforms = lib.platforms.x86_64;
+}
diff --git a/nixpkgs/pkgs/test/cc-wrapper/nostdinc-main.c b/nixpkgs/pkgs/test/cc-wrapper/nostdinc-main.c
new file mode 100644
index 000000000000..f71d155b1b27
--- /dev/null
+++ b/nixpkgs/pkgs/test/cc-wrapper/nostdinc-main.c
@@ -0,0 +1,8 @@
+// This one should not come from libc because of -nostdinc
+#include <stdio.h>
+
+int main(int argc, char *argv[]) {
+  // provided by our own stdio.h
+  foo();
+  return 0;
+}
diff --git a/nixpkgs/pkgs/test/cc-wrapper/sanitizers.c b/nixpkgs/pkgs/test/cc-wrapper/sanitizers.c
new file mode 100644
index 000000000000..93dd78a903ce
--- /dev/null
+++ b/nixpkgs/pkgs/test/cc-wrapper/sanitizers.c
@@ -0,0 +1,8 @@
+#include <sanitizer/asan_interface.h>
+#include <stdio.h>
+
+int main(int argc, char **argv)
+{
+  fprintf(stderr, "ok\n");
+  return 0;
+}
diff --git a/nixpkgs/pkgs/test/cc-wrapper/stdio.h b/nixpkgs/pkgs/test/cc-wrapper/stdio.h
new file mode 100644
index 000000000000..4bddf1d9d486
--- /dev/null
+++ b/nixpkgs/pkgs/test/cc-wrapper/stdio.h
@@ -0,0 +1 @@
+static void foo(void) {}
diff --git a/nixpkgs/pkgs/test/checkpointBuild/default.nix b/nixpkgs/pkgs/test/checkpointBuild/default.nix
new file mode 100644
index 000000000000..0843dcd3d6eb
--- /dev/null
+++ b/nixpkgs/pkgs/test/checkpointBuild/default.nix
@@ -0,0 +1,57 @@
+{ hello, checkpointBuildTools, runCommand, texinfo, stdenv, rsync }:
+let
+  baseHelloArtifacts = checkpointBuildTools.prepareCheckpointBuild hello;
+  patchedHello = hello.overrideAttrs (old: {
+    buildInputs = [ texinfo ];
+    src = runCommand "patch-hello-src" { } ''
+      mkdir -p $out
+      cd $out
+      tar xf ${hello.src} --strip-components=1
+      patch -p1 < ${./hello.patch}
+    '';
+  });
+  checkpointBuiltHello = checkpointBuildTools.mkCheckpointBuild patchedHello baseHelloArtifacts;
+
+  checkpointBuiltHelloWithCheck = checkpointBuiltHello.overrideAttrs (old: {
+    doCheck = true;
+    checkPhase = ''
+      echo "checking if unchanged source file is not recompiled"
+        [ "$(stat --format="%Y" lib/exitfail.o)" = "$(stat --format="%Y" ${baseHelloArtifacts}/outputs/lib/exitfail.o)" ]
+    '';
+  });
+
+  baseHelloRemoveFileArtifacts = checkpointBuildTools.prepareCheckpointBuild (hello.overrideAttrs (old: {
+    patches = [ ./hello-additionalFile.patch ];
+  }));
+
+  preparedHelloRemoveFileSrc = runCommand "patch-hello-src" { } ''
+    mkdir -p $out
+    cd $out
+    tar xf ${hello.src} --strip-components=1
+    patch -p1 < ${./hello-additionalFile.patch}
+  '';
+
+  patchedHelloRemoveFile = hello.overrideAttrs (old: {
+    buildInputs = [ texinfo ];
+    src = runCommand "patch-hello-src" { } ''
+      mkdir -p $out
+      cd $out
+      ${rsync}/bin/rsync -cutU --chown=$USER:$USER --chmod=+w -r ${preparedHelloRemoveFileSrc}/* .
+      patch -p1 < ${./hello-removeFile.patch}
+    '';
+  });
+
+  checkpointBuiltHelloWithRemovedFile = checkpointBuildTools.mkCheckpointBuild patchedHelloRemoveFile baseHelloRemoveFileArtifacts;
+in
+stdenv.mkDerivation {
+  name = "patched-hello-returns-correct-output";
+  buildCommand = ''
+    touch $out
+
+    echo "testing output of hello binary"
+    [ "$(${checkpointBuiltHelloWithCheck}/bin/hello)" = "Hello, incremental world!" ]
+    echo "testing output of hello with removed file"
+    [ "$(${checkpointBuiltHelloWithRemovedFile}/bin/hello)" = "Hello, incremental world!" ]
+  '';
+}
+
diff --git a/nixpkgs/pkgs/test/checkpointBuild/hello-additionalFile.patch b/nixpkgs/pkgs/test/checkpointBuild/hello-additionalFile.patch
new file mode 100644
index 000000000000..345bc10ee49e
--- /dev/null
+++ b/nixpkgs/pkgs/test/checkpointBuild/hello-additionalFile.patch
@@ -0,0 +1,67 @@
+:100644 100644 0000000 0000000 M	Makefile.in
+:000000 100644 0000000 0000000 A	src/additionalFile.c
+:100644 100644 0000000 0000000 M	src/hello.c
+:100644 100644 0000000 0000000 M	src/system.h
+
+diff --git a/Makefile.in b/Makefile.in
+index 1597d39..f63f830 100644
+--- a/Makefile.in
++++ b/Makefile.in
+@@ -312,7 +312,7 @@ am_lib_libhello_a_OBJECTS = lib/basename-lgpl.$(OBJEXT) \
+ 	lib/version-etc.$(OBJEXT) lib/version-etc-fsf.$(OBJEXT) \
+ 	lib/wctype-h.$(OBJEXT) lib/xmalloc.$(OBJEXT) \
+ 	lib/xalloc-die.$(OBJEXT) lib/xstriconv.$(OBJEXT) \
+-	lib/xstrndup.$(OBJEXT)
++	lib/xstrndup.$(OBJEXT) src/additionalFile.$(OBJEXT)
+ lib_libhello_a_OBJECTS = $(am_lib_libhello_a_OBJECTS)
+ am_hello_OBJECTS = src/hello.$(OBJEXT)
+ hello_OBJECTS = $(am_hello_OBJECTS)
+@@ -1842,7 +1842,7 @@ lib_libhello_a_SOURCES = lib/basename-lgpl.c lib/c-ctype.h \
+ 	$(am__append_4) $(am__append_5) lib/version-etc.h \
+ 	lib/version-etc.c lib/version-etc-fsf.c lib/wctype-h.c \
+ 	lib/xmalloc.c lib/xalloc-die.c lib/xstriconv.h lib/xstriconv.c \
+-	lib/xstrndup.h lib/xstrndup.c
++	lib/xstrndup.h lib/xstrndup.c src/additionalFile.c
+ lib_libhello_a_LIBADD = $(gl_LIBOBJS)
+ lib_libhello_a_DEPENDENCIES = $(gl_LIBOBJS)
+ EXTRA_lib_libhello_a_SOURCES = lib/close.c lib/stripslash.c lib/dup2.c \
+diff --git a/src/additionalFile.c b/src/additionalFile.c
+new file mode 100644
+index 0000000..34d683d
+--- /dev/null
++++ b/src/additionalFile.c
+@@ -0,0 +1,6 @@
++#include "config.h"
++#include "system.h"
++
++int somefunc() {
++    return 0;
++}
+diff --git a/src/hello.c b/src/hello.c
+index 2e7d38e..a8e36dc 100644
+--- a/src/hello.c
++++ b/src/hello.c
+@@ -146,7 +146,11 @@ main (int argc, char *argv[])
+ #endif
+ 
+   /* Having initialized gettext, get the default message. */
+-  greeting_msg = _("Hello, world!");
++  if (somefunc() == 0) {
++    greeting_msg = _("Hello, world!");
++  } else {
++    greeting_msg = _("Hello, incremental world!");
++  }
+ 
+   /* Even exiting has subtleties.  On exit, if any writes failed, change
+      the exit status.  The /dev/full device on GNU/Linux can be used for
+diff --git a/src/system.h b/src/system.h
+index d39cdb9..dc425d2 100644
+--- a/src/system.h
++++ b/src/system.h
+@@ -59,4 +59,6 @@
+     } \
+   while (0)
+ 
++int somefunc();
++
+ #endif /* HELLO_SYSTEM_H */
diff --git a/nixpkgs/pkgs/test/checkpointBuild/hello-removeFile.patch b/nixpkgs/pkgs/test/checkpointBuild/hello-removeFile.patch
new file mode 100644
index 000000000000..2939790dabce
--- /dev/null
+++ b/nixpkgs/pkgs/test/checkpointBuild/hello-removeFile.patch
@@ -0,0 +1,67 @@
+:100644 100644 0000000 0000000 M	Makefile.in
+:100644 000000 0000000 0000000 D	src/additionalFile.c
+:100644 100644 0000000 0000000 M	src/hello.c
+:100755 100755 0000000 0000000 M	tests/hello-1
+
+diff --git a/Makefile.in b/Makefile.in
+index f63f830..1597d39 100644
+--- a/Makefile.in
++++ b/Makefile.in
+@@ -312,7 +312,7 @@ am_lib_libhello_a_OBJECTS = lib/basename-lgpl.$(OBJEXT) \
+ 	lib/version-etc.$(OBJEXT) lib/version-etc-fsf.$(OBJEXT) \
+ 	lib/wctype-h.$(OBJEXT) lib/xmalloc.$(OBJEXT) \
+ 	lib/xalloc-die.$(OBJEXT) lib/xstriconv.$(OBJEXT) \
+-	lib/xstrndup.$(OBJEXT) src/additionalFile.$(OBJEXT)
++	lib/xstrndup.$(OBJEXT)
+ lib_libhello_a_OBJECTS = $(am_lib_libhello_a_OBJECTS)
+ am_hello_OBJECTS = src/hello.$(OBJEXT)
+ hello_OBJECTS = $(am_hello_OBJECTS)
+@@ -1842,7 +1842,7 @@ lib_libhello_a_SOURCES = lib/basename-lgpl.c lib/c-ctype.h \
+ 	$(am__append_4) $(am__append_5) lib/version-etc.h \
+ 	lib/version-etc.c lib/version-etc-fsf.c lib/wctype-h.c \
+ 	lib/xmalloc.c lib/xalloc-die.c lib/xstriconv.h lib/xstriconv.c \
+-	lib/xstrndup.h lib/xstrndup.c src/additionalFile.c
++	lib/xstrndup.h lib/xstrndup.c
+ lib_libhello_a_LIBADD = $(gl_LIBOBJS)
+ lib_libhello_a_DEPENDENCIES = $(gl_LIBOBJS)
+ EXTRA_lib_libhello_a_SOURCES = lib/close.c lib/stripslash.c lib/dup2.c \
+diff --git a/src/additionalFile.c b/src/additionalFile.c
+deleted file mode 100644
+index 34d683d..0000000
+--- a/src/additionalFile.c
++++ /dev/null
+@@ -1,6 +0,0 @@
+-#include "config.h"
+-#include "system.h"
+-
+-int somefunc() {
+-    return 0;
+-}
+diff --git a/src/hello.c b/src/hello.c
+index a8e36dc..53722d9 100644
+--- a/src/hello.c
++++ b/src/hello.c
+@@ -126,6 +126,10 @@ parse_options (int argc, char *argv[], const char **greeting_msg)
+     }
+ }
+ 
++int somefunc() {
++    return 1;
++}
++
+ int
+ main (int argc, char *argv[])
+ {
+diff --git a/tests/hello-1 b/tests/hello-1
+index 96ffef8..f0b9f8d 100755
+--- a/tests/hello-1
++++ b/tests/hello-1
+@@ -21,7 +21,7 @@ export LANGUAGE LC_ALL LC_MESSAGES LANG
+ 
+ tmpfiles="hello-test1.ok"
+ cat <<EOF > hello-test1.ok
+-Hello, world!
++Hello, incremental world!
+ EOF
+ 
+ tmpfiles="$tmpfiles hello-test1.out"
diff --git a/nixpkgs/pkgs/test/checkpointBuild/hello.patch b/nixpkgs/pkgs/test/checkpointBuild/hello.patch
new file mode 100644
index 000000000000..3d0d50c2f20e
--- /dev/null
+++ b/nixpkgs/pkgs/test/checkpointBuild/hello.patch
@@ -0,0 +1,26 @@
+diff --git a/src/hello.c b/src/hello.c
+index 182303c..453962f 100644
+--- a/src/hello.c
++++ b/src/hello.c
+@@ -57,7 +57,7 @@ main (int argc, char *argv[])
+ #endif
+ 
+   /* Having initialized gettext, get the default message. */
+-  greeting_msg = _("Hello, world!");
++  greeting_msg = _("Hello, incremental world!");
+ 
+   /* Even exiting has subtleties.  On exit, if any writes failed, change
+      the exit status.  The /dev/full device on GNU/Linux can be used for
+diff --git a/tests/hello-1 b/tests/hello-1
+index 3b7a815..e15fa95 100755
+--- a/tests/hello-1
++++ b/tests/hello-1
+@@ -21,7 +21,7 @@ export LANGUAGE LC_ALL LC_MESSAGES LANG
+ 
+ tmpfiles="hello-test1.ok"
+ cat <<EOF > hello-test1.ok
+-Hello, world!
++Hello, incremental world!
+ EOF
+ 
+ tmpfiles="$tmpfiles hello-test1.out"
diff --git a/nixpkgs/pkgs/test/config.nix b/nixpkgs/pkgs/test/config.nix
new file mode 100644
index 000000000000..734e1aace148
--- /dev/null
+++ b/nixpkgs/pkgs/test/config.nix
@@ -0,0 +1,24 @@
+{ lib, ... }:
+lib.recurseIntoAttrs {
+
+  # https://github.com/NixOS/nixpkgs/issues/175196
+  allowPkgsInPermittedInsecurePackages =
+    let pkgs = import ../.. {
+          config = {
+            permittedInsecurePackages =
+              tempAllow pkgs.authy "2.1.0" [ "electron-9.4.4" ];
+          };
+        };
+        # A simplification of `tempAllow` that doesn't check the version, but
+        # has the same strictness characteristics. Actually checking a version
+        # here would add undue maintenance.
+        #
+        # Original:
+        #     tempAllow = p: v: pa:
+        #       lib.optionals (lib.assertMsg (p.version == v) "${p.name} is no longer at version ${v}, consider removing the tempAllow") pa;
+        #
+        tempAllow = p: v: pa: builtins.seq v builtins.seq p.version pa;
+
+    in pkgs.hello;
+
+}
diff --git a/nixpkgs/pkgs/test/coq/default.nix b/nixpkgs/pkgs/test/coq/default.nix
new file mode 100644
index 000000000000..cf59dd473b4c
--- /dev/null
+++ b/nixpkgs/pkgs/test/coq/default.nix
@@ -0,0 +1,6 @@
+{ lib, callPackage }:
+
+lib.recurseIntoAttrs {
+  overrideCoqDerivation = callPackage ./overrideCoqDerivation { };
+}
+
diff --git a/nixpkgs/pkgs/test/coq/overrideCoqDerivation/default.nix b/nixpkgs/pkgs/test/coq/overrideCoqDerivation/default.nix
new file mode 100644
index 000000000000..c251c498e609
--- /dev/null
+++ b/nixpkgs/pkgs/test/coq/overrideCoqDerivation/default.nix
@@ -0,0 +1,40 @@
+{ lib, coq, mkCoqPackages, runCommand }:
+
+let
+
+  # This is just coq, but with dontFilter set to true. We need to set
+  # dontFilter to true here so that _all_ packages are visibile in coqPackages.
+  # There may be some versions of the top-level coq and coqPackages that don't
+  # build QuickChick, which is what we are using for this test below.
+  coqWithAllPackages = coq // { dontFilter = true; };
+
+  coqPackages = mkCoqPackages coqWithAllPackages;
+
+  # This is the main test.  This uses overrideCoqDerivation to
+  # override arguments to mkCoqDerivation.
+  #
+  # Here, we override the defaultVersion and release arguments to
+  # mkCoqDerivation.
+  overriddenQuickChick =
+    coqPackages.lib.overrideCoqDerivation
+      {
+        defaultVersion = "9999";
+        release."9999".sha256 = lib.fakeSha256;
+      }
+      coqPackages.QuickChick;
+in
+
+runCommand
+  "coq-overrideCoqDerivation-test-0.1"
+  { meta.maintainers = with lib.maintainers; [cdepillabout]; }
+  ''
+    # Confirm that the computed version number for the overridden QuickChick does
+    # actually become 9999, as set above.
+    if [ "${overriddenQuickChick.version}" -eq "9999" ]; then
+      echo "overriddenQuickChick version was successfully set to 9999"
+      touch $out
+    else
+      echo "ERROR: overriddenQuickChick version was supposed to be 9999, but was actually: ${overriddenQuickChick.version}"
+      exit 1
+    fi
+  ''
diff --git a/nixpkgs/pkgs/test/cross/default.nix b/nixpkgs/pkgs/test/cross/default.nix
new file mode 100644
index 000000000000..bd233db4cd50
--- /dev/null
+++ b/nixpkgs/pkgs/test/cross/default.nix
@@ -0,0 +1,169 @@
+{ pkgs, lib }:
+
+let
+
+  testedSystems = lib.filterAttrs (name: value: let
+    platform = lib.systems.elaborate value;
+  in platform.isLinux || platform.isWindows
+  ) lib.systems.examples;
+
+  getExecutable = pkgs: pkgFun: exec:
+    "${pkgFun pkgs}${exec}${pkgs.stdenv.hostPlatform.extensions.executable}";
+
+  compareTest = { emulator, pkgFun, hostPkgs, crossPkgs, exec, args ? [] }: let
+    pkgName = (pkgFun hostPkgs).name;
+    args' = lib.concatStringsSep " " args;
+  in crossPkgs.runCommand "test-${pkgName}-${crossPkgs.stdenv.hostPlatform.config}" {
+    nativeBuildInputs = [ pkgs.dos2unix ];
+  } ''
+    # Just in case we are using wine, get rid of that annoying extra
+    # stuff.
+    export WINEDEBUG=-all
+
+    HOME=$(pwd)
+    mkdir -p $out
+
+    # We need to remove whitespace, unfortunately
+    # Windows programs use \r but Unix programs use \n
+
+    echo Running native-built program natively
+
+    # find expected value natively
+    ${getExecutable hostPkgs pkgFun exec} ${args'} \
+      | dos2unix > $out/expected
+
+    echo Running cross-built program in emulator
+
+    # run emulator to get actual value
+    ${emulator} ${getExecutable crossPkgs pkgFun exec} ${args'} \
+      | dos2unix > $out/actual
+
+    echo Comparing results...
+
+    if [ "$(cat $out/actual)" != "$(cat $out/expected)" ]; then
+      echo "${pkgName} did not output expected value:"
+      cat $out/expected
+      echo "instead it output:"
+      cat $out/actual
+      exit 1
+    else
+      echo "${pkgName} test passed"
+      echo "both produced output:"
+      cat $out/actual
+    fi
+  '';
+
+  mapMultiPlatformTest = crossSystemFun: test: lib.mapAttrs (name: system: test rec {
+    crossPkgs = import pkgs.path {
+      localSystem = { inherit (pkgs.stdenv.hostPlatform) config; };
+      crossSystem = crossSystemFun system;
+    };
+
+    emulator = crossPkgs.stdenv.hostPlatform.emulator pkgs;
+
+    # Apply some transformation on windows to get dlls in the right
+    # place. Unfortunately mingw doesn’t seem to be able to do linking
+    # properly.
+    platformFun = pkg: if crossPkgs.stdenv.hostPlatform.isWindows then
+      pkgs.buildEnv {
+        name = "${pkg.name}-winlinks";
+        paths = [pkg] ++ pkg.buildInputs;
+      } else pkg;
+  }) testedSystems;
+
+  tests = {
+
+    file = {platformFun, crossPkgs, emulator}: compareTest {
+      inherit emulator crossPkgs;
+      hostPkgs = pkgs;
+      exec = "/bin/file";
+      args = [
+        "${pkgs.file}/share/man/man1/file.1.gz"
+        "${pkgs.dejavu_fonts}/share/fonts/truetype/DejaVuMathTeXGyre.ttf"
+      ];
+      pkgFun = pkgs: platformFun pkgs.file;
+    };
+
+    hello = {platformFun, crossPkgs, emulator}: compareTest {
+      inherit emulator crossPkgs;
+      hostPkgs = pkgs;
+      exec = "/bin/hello";
+      pkgFun = pkgs: pkgs.hello;
+    };
+
+    pkg-config = {platformFun, crossPkgs, emulator}: crossPkgs.runCommand
+      "test-pkg-config-${crossPkgs.stdenv.hostPlatform.config}"
+    {
+      depsBuildBuild = [ crossPkgs.pkgsBuildBuild.pkg-config ];
+      nativeBuildInputs = [ crossPkgs.pkgsBuildHost.pkg-config crossPkgs.buildPackages.zlib ];
+      depsBuildTarget = [ crossPkgs.pkgsBuildTarget.pkg-config ];
+      buildInputs = [ crossPkgs.zlib ];
+      NIX_DEBUG = 7;
+    } ''
+       mkdir $out
+       ${crossPkgs.pkgsBuildBuild.pkg-config.targetPrefix}pkg-config --cflags zlib > "$out/for-build"
+       ${crossPkgs.pkgsBuildHost.pkg-config.targetPrefix}pkg-config --cflags zlib > "$out/for-host"
+       ! diff "$out/for-build" "$out/for-host"
+    '';
+  };
+
+  # see https://github.com/NixOS/nixpkgs/issues/213453
+  # this is a good test of a lot of tricky glibc/libgcc corner cases
+  mbuffer = let
+    mbuffer = pkgs.pkgsCross.aarch64-multiplatform.mbuffer;
+    emulator = with lib.systems; (elaborate examples.aarch64-multiplatform).emulator pkgs;
+  in
+    pkgs.runCommand "test-mbuffer" {} ''
+      echo hello | ${emulator} ${mbuffer}/bin/mbuffer
+      touch $out
+    '';
+
+  # This is meant to be a carefully curated list of builds/packages
+  # that tend to break when refactoring our cross-compilation
+  # infrastructure.
+  #
+  # It should strike a balance between being small enough to fit in
+  # a single eval (i.e. not so large that hydra-eval-jobs is needed)
+  # so we can ask @ofborg to check it, yet should have good examples
+  # of things that often break.  So, no buckshot `mapTestOnCross`
+  # calls here.
+  sanity = [
+    mbuffer
+    #pkgs.pkgsCross.gnu64.bash # https://github.com/NixOS/nixpkgs/issues/243164
+    pkgs.gcc_multi.cc
+    pkgs.pkgsMusl.stdenv
+    pkgs.pkgsLLVM.stdenv
+    pkgs.pkgsStatic.bash
+    #pkgs.pkgsCross.gnu64_simplekernel.bash   # https://github.com/NixOS/nixpkgs/issues/264989
+    pkgs.pkgsCross.arm-embedded.stdenv
+    pkgs.pkgsCross.sheevaplug.stdenv  # for armv5tel
+    pkgs.pkgsCross.raspberryPi.stdenv  # for armv6l
+    pkgs.pkgsCross.armv7l-hf-multiplatform.stdenv
+    pkgs.pkgsCross.m68k.stdenv
+    pkgs.pkgsCross.aarch64-multiplatform.pkgsBuildTarget.gcc
+    pkgs.pkgsCross.powernv.pkgsBuildTarget.gcc
+    pkgs.pkgsCross.s390.stdenv
+    pkgs.pkgsCross.mips64el-linux-gnuabi64.stdenv
+    pkgs.pkgsCross.mips64el-linux-gnuabin32.stdenv
+    pkgs.pkgsCross.mingwW64.stdenv
+
+  ] ++ lib.optionals (with pkgs.stdenv.buildPlatform; isx86_64 && isLinux) [
+    # Musl-to-glibc cross on the same architecture tends to turn up
+    # lots of interesting corner cases.  Only expected to work for
+    # x86_64-linux buildPlatform.
+    pkgs.pkgsMusl.pkgsCross.gnu64.hello
+
+    # Two web browsers -- exercises almost the entire packageset
+    pkgs.pkgsCross.aarch64-multiplatform.qutebrowser-qt5
+    pkgs.pkgsCross.aarch64-multiplatform.firefox
+
+    # Uses pkgsCross.riscv64-embedded; see https://github.com/NixOS/nixpkgs/issues/267859
+    pkgs.spike
+  ];
+
+in {
+  gcc = (lib.mapAttrs (_: mapMultiPlatformTest (system: system // {useLLVM = false;})) tests);
+  llvm = (lib.mapAttrs (_: mapMultiPlatformTest (system: system // {useLLVM = true;})) tests);
+
+  inherit mbuffer sanity;
+}
diff --git a/nixpkgs/pkgs/test/cuda/default.nix b/nixpkgs/pkgs/test/cuda/default.nix
new file mode 100644
index 000000000000..dd9ad8b814dc
--- /dev/null
+++ b/nixpkgs/pkgs/test/cuda/default.nix
@@ -0,0 +1,51 @@
+{
+  lib,
+  recurseIntoAttrs,
+
+  cudaPackages,
+  cudaPackagesGoogle,
+
+  cudaPackages_10_0,
+  cudaPackages_10_1,
+  cudaPackages_10_2,
+  cudaPackages_10,
+
+  cudaPackages_11_0,
+  cudaPackages_11_1,
+  cudaPackages_11_2,
+  cudaPackages_11_3,
+  cudaPackages_11_4,
+  cudaPackages_11_5,
+  cudaPackages_11_6,
+  cudaPackages_11_7,
+  cudaPackages_11_8,
+  cudaPackages_11,
+
+  cudaPackages_12_0,
+  cudaPackages_12_1,
+  cudaPackages_12_2,
+  cudaPackages_12_3,
+  cudaPackages_12,
+}@args:
+
+let
+  isTest =
+    name: package:
+    builtins.elem (package.pname or null) [
+      "cuda-samples"
+      "cuda-library-samples"
+      "saxpy"
+    ];
+in
+(lib.trivial.pipe args [
+  (lib.filterAttrs (name: _: lib.hasPrefix "cudaPackages" name))
+  (lib.mapAttrs (
+    _: ps:
+    lib.pipe ps [
+      (lib.filterAttrs isTest)
+      (as: as // { __attrsFailEvaluation = true; })
+      recurseIntoAttrs
+    ]
+  ))
+  recurseIntoAttrs
+])
diff --git a/nixpkgs/pkgs/test/cue/default.nix b/nixpkgs/pkgs/test/cue/default.nix
new file mode 100644
index 000000000000..2cc8bf34bb04
--- /dev/null
+++ b/nixpkgs/pkgs/test/cue/default.nix
@@ -0,0 +1,24 @@
+{ writeCueValidator, runCommand, writeText, ... }:
+
+let
+  validator = writeCueValidator
+    (writeText "schema.cue" ''
+      #Def1: {
+        field1: string
+      }
+    '')
+    { document = "#Def1"; };
+in runCommand "cue-validation" {} ''
+  cat > valid.json <<EOF
+  { "field1": "abc" }
+  EOF
+  cat > invalid.json <<EOF
+  { "field2": "abc" }
+  EOF
+  ${validator} valid.json
+  if ${validator} invalid.json; then
+    echo "this example should fail"
+    exit 1
+  fi
+  touch $out
+''
diff --git a/nixpkgs/pkgs/test/default.nix b/nixpkgs/pkgs/test/default.nix
new file mode 100644
index 000000000000..3fcc9d047db9
--- /dev/null
+++ b/nixpkgs/pkgs/test/default.nix
@@ -0,0 +1,183 @@
+{ pkgs, callPackage }:
+
+with pkgs;
+
+{
+  cc-wrapper = with builtins; let
+    pkgNames = (attrNames pkgs);
+    llvmTests = let
+      pkgSets = lib.pipe pkgNames [
+        (filter (lib.hasPrefix "llvmPackages"))
+        (filter (n: n != "rocmPackages.llvm"))
+        # Are throw aliases.
+        (filter (n: n != "llvmPackages_rocm"))
+        (filter (n: n != "llvmPackages_latest"))
+        (filter (n: n != "llvmPackages_git"))
+        (filter (n: n != "llvmPackages_6"))
+        (filter (n: n != "llvmPackages_7"))
+        (filter (n: n != "llvmPackages_8"))
+        (filter (n: n != "llvmPackages_10"))
+        (filter (n: n != "llvmPackages_11"))
+      ];
+      tests = lib.genAttrs pkgSets (name: recurseIntoAttrs {
+        clang = callPackage ./cc-wrapper { stdenv = pkgs.${name}.stdenv; };
+        libcxx = callPackage ./cc-wrapper { stdenv = pkgs.${name}.libcxxStdenv; };
+      });
+    in
+      tests;
+    gccTests = let
+      pkgSets = lib.pipe (attrNames pkgs) ([
+        (filter (lib.hasPrefix "gcc"))
+        (filter (lib.hasSuffix "Stdenv"))
+        (filter (n: n != "gccCrossLibcStdenv"))
+      ] ++ lib.optionals (!(
+        (stdenv.buildPlatform.isLinux && stdenv.buildPlatform.isx86_64) &&
+        (stdenv.hostPlatform.isLinux && stdenv.hostPlatform.isx86_64)
+      )) [
+        (filter (n: !lib.hasSuffix "MultiStdenv" n))
+      ]);
+    in lib.genAttrs pkgSets (name: callPackage ./cc-wrapper { stdenv = pkgs.${name}; });
+  in recurseIntoAttrs {
+    default = callPackage ./cc-wrapper { };
+
+    supported = stdenv.mkDerivation {
+      name = "cc-wrapper-supported";
+      builtGCC =
+        let
+          inherit (lib) filterAttrs;
+          sets = lib.pipe gccTests ([
+            (filterAttrs (_: v: lib.meta.availableOn stdenv.hostPlatform v.stdenv.cc))
+            # Broken
+            (filterAttrs (n: _: n != "gcc49Stdenv"))
+            (filterAttrs (n: _: n != "gccMultiStdenv"))
+          ] ++ lib.optionals (stdenv.hostPlatform.isDarwin && stdenv.hostPlatform.isAarch64) [
+            # fails with things like
+            # ld: warning: ld: warning: object file (trunctfsf2_s.o) was built for newer macOS version (11.0) than being linked (10.5)
+            # ld: warning: ld: warning: could not create compact unwind for ___fixunstfdi: register 20 saved somewhere other than in frame
+            (filterAttrs (n: _: n != "gcc11Stdenv"))
+          ]);
+        in
+        toJSON sets;
+
+      builtLLVM =
+        let
+          inherit (lib) filterAttrs;
+          sets = lib.pipe llvmTests ([
+            (filterAttrs (_: v: lib.meta.availableOn stdenv.hostPlatform v.clang.stdenv.cc))
+            (filterAttrs (_: v: lib.meta.availableOn stdenv.hostPlatform v.libcxx.stdenv.cc))
+
+            # libcxxStdenv broken
+            # fix in https://github.com/NixOS/nixpkgs/pull/216273
+          ] ++ lib.optionals (stdenv.hostPlatform.isDarwin && stdenv.hostPlatform.isAarch64) [
+            (filterAttrs (n: _: n != "llvmPackages_9"))
+          ]);
+        in
+        toJSON sets;
+        buildCommand = ''
+          touch $out
+        '';
+    };
+
+    llvmTests = recurseIntoAttrs llvmTests;
+    inherit gccTests;
+  };
+
+  stdenv-inputs = callPackage ./stdenv-inputs { };
+  stdenv = callPackage ./stdenv { };
+
+  hardeningFlags = recurseIntoAttrs (callPackage ./cc-wrapper/hardening.nix {});
+  hardeningFlags-gcc = recurseIntoAttrs (callPackage ./cc-wrapper/hardening.nix {
+    stdenv = gccStdenv;
+  });
+  hardeningFlags-clang = recurseIntoAttrs (callPackage ./cc-wrapper/hardening.nix {
+    stdenv = llvmPackages.stdenv;
+  });
+
+  config = callPackage ./config.nix { };
+
+  top-level = callPackage ./top-level { };
+
+  haskell = callPackage ./haskell { };
+
+  hooks = callPackage ./hooks { };
+
+  cc-multilib-gcc = callPackage ./cc-wrapper/multilib.nix { stdenv = gccMultiStdenv; };
+  cc-multilib-clang = callPackage ./cc-wrapper/multilib.nix { stdenv = clangMultiStdenv; };
+
+  fetchurl = callPackages ../build-support/fetchurl/tests.nix { };
+  fetchtorrent = callPackages ../build-support/fetchtorrent/tests.nix { };
+  fetchpatch = callPackages ../build-support/fetchpatch/tests.nix { };
+  fetchpatch2 = callPackages ../build-support/fetchpatch/tests.nix { fetchpatch = fetchpatch2; };
+  fetchDebianPatch = callPackages ../build-support/fetchdebianpatch/tests.nix { };
+  fetchzip = callPackages ../build-support/fetchzip/tests.nix { };
+  fetchgit = callPackages ../build-support/fetchgit/tests.nix { };
+  fetchFirefoxAddon = callPackages ../build-support/fetchfirefoxaddon/tests.nix { };
+  fetchPypiLegacy = callPackages ../build-support/fetchpypilegacy/tests.nix { };
+
+  install-shell-files = callPackage ./install-shell-files {};
+
+  checkpointBuildTools = callPackage ./checkpointBuild {};
+
+  kernel-config = callPackage ./kernel.nix {};
+
+  ld-library-path = callPackage ./ld-library-path {};
+
+  macOSSierraShared = callPackage ./macos-sierra-shared {};
+
+  cross = callPackage ./cross {} // { __attrsFailEvaluation = true; };
+
+  php = recurseIntoAttrs (callPackages ./php {});
+
+  pkg-config = recurseIntoAttrs (callPackage ../top-level/pkg-config/tests.nix { }) // { __recurseIntoDerivationForReleaseJobs = true; };
+
+  buildRustCrate = callPackage ../build-support/rust/build-rust-crate/test { };
+  importCargoLock = callPackage ../build-support/rust/test/import-cargo-lock { };
+
+  vim = callPackage ./vim {};
+
+  nixos-functions = callPackage ./nixos-functions {};
+
+  overriding = callPackage ./overriding.nix { };
+
+  texlive = callPackage ./texlive {};
+
+  cuda = callPackage ./cuda { };
+
+  trivial-builders = callPackage ../build-support/trivial-builders/test/default.nix {};
+
+  writers = callPackage ../build-support/writers/test.nix {};
+
+  testers = callPackage ../build-support/testers/test/default.nix {};
+
+  dhall = callPackage ./dhall { };
+
+  cue-validation = callPackage ./cue {};
+
+  coq = callPackage ./coq {};
+
+  dotnet = recurseIntoAttrs (callPackages ./dotnet { });
+
+  makeHardcodeGsettingsPatch = callPackage ./make-hardcode-gsettings-patch { };
+
+  makeWrapper = callPackage ./make-wrapper { };
+  makeBinaryWrapper = callPackage ./make-binary-wrapper {
+    makeBinaryWrapper = pkgs.makeBinaryWrapper.override {
+      # Enable sanitizers in the tests only, to avoid the performance cost in regular usage.
+      # The sanitizers cause errors on aarch64-darwin, see https://github.com/NixOS/nixpkgs/pull/150079#issuecomment-994132734
+      sanitizers = pkgs.lib.optionals (! (pkgs.stdenv.isDarwin && pkgs.stdenv.isAarch64))
+        [ "undefined" "address" ];
+    };
+  };
+
+  pkgs-lib = recurseIntoAttrs (import ../pkgs-lib/tests { inherit pkgs; });
+
+  buildFHSEnv = recurseIntoAttrs (callPackages ./buildFHSEnv { });
+
+  nixpkgs-check-by-name = callPackage ./nixpkgs-check-by-name { };
+
+  auto-patchelf-hook = callPackage ./auto-patchelf-hook { };
+
+  systemd = callPackage ./systemd { };
+
+  substitute = recurseIntoAttrs (callPackage ./substitute { });
+}
diff --git a/nixpkgs/pkgs/test/dhall/buildDhallUrl/default.nix b/nixpkgs/pkgs/test/dhall/buildDhallUrl/default.nix
new file mode 100644
index 000000000000..a75101a303d6
--- /dev/null
+++ b/nixpkgs/pkgs/test/dhall/buildDhallUrl/default.nix
@@ -0,0 +1,14 @@
+{ dhallPackages, lib }:
+
+# This file tests that dhallPackages.buildDhallUrl is able to successfully
+# build a Nix Dhall package for a given remote Dhall import.
+#
+# TODO: It would be nice to extend this test to make sure that the resulting
+# Nix Dhall package is has the expected contents.
+
+dhallPackages.buildDhallUrl {
+  url = "https://raw.githubusercontent.com/cdepillabout/example-dhall-nix/e6a675c72ecd4dd23d254a02aea8181fe875747f/mydhallfile.dhall";
+  hash = "sha256-434x+QjHRzuprBdw0h6wmwB1Zj6yZqQb533me8XdO4c=";
+  dhallHash = "sha256-434x+QjHRzuprBdw0h6wmwB1Zj6yZqQb533me8XdO4c=";
+  source = true;
+}
diff --git a/nixpkgs/pkgs/test/dhall/default.nix b/nixpkgs/pkgs/test/dhall/default.nix
new file mode 100644
index 000000000000..4c7eba6c9579
--- /dev/null
+++ b/nixpkgs/pkgs/test/dhall/default.nix
@@ -0,0 +1,6 @@
+{ lib, callPackage }:
+
+lib.recurseIntoAttrs {
+  buildDhallUrl = callPackage ./buildDhallUrl { };
+  generateDhallDirectoryPackage = callPackage ./generateDhallDirectoryPackage { };
+}
diff --git a/nixpkgs/pkgs/test/dhall/generateDhallDirectoryPackage/default.nix b/nixpkgs/pkgs/test/dhall/generateDhallDirectoryPackage/default.nix
new file mode 100644
index 000000000000..4771e1629806
--- /dev/null
+++ b/nixpkgs/pkgs/test/dhall/generateDhallDirectoryPackage/default.nix
@@ -0,0 +1,17 @@
+{ dhallPackages, fetchFromGitHub }:
+
+# This file tests that dhallPackages.generateDhallDirectoryPackage works.
+#
+# TODO: It would be nice to extend this test to make sure that the resulting
+# Nix file has the expected contents, but it might be tough to do that easily
+# without IFD.
+
+dhallPackages.generateDhallDirectoryPackage {
+  src = fetchFromGitHub {
+    owner = "cdepillabout";
+    repo = "example-dhall-nix";
+    rev = "e6a675c72ecd4dd23d254a02aea8181fe875747f";
+    sha256 = "sha256-c/EZq76s/+hmLkaeJWKqgh2KrHuJRYI6kWry0E0YQ6s=";
+  };
+  file = "mydhallfile.dhall";
+}
diff --git a/nixpkgs/pkgs/test/dotnet/default.nix b/nixpkgs/pkgs/test/dotnet/default.nix
new file mode 100644
index 000000000000..7592b09d76e3
--- /dev/null
+++ b/nixpkgs/pkgs/test/dotnet/default.nix
@@ -0,0 +1,5 @@
+{ callPackage }:
+
+{
+  project-references = callPackage ./project-references { };
+}
diff --git a/nixpkgs/pkgs/test/dotnet/project-references/application/Application.cs b/nixpkgs/pkgs/test/dotnet/project-references/application/Application.cs
new file mode 100644
index 000000000000..ae2c956a4df7
--- /dev/null
+++ b/nixpkgs/pkgs/test/dotnet/project-references/application/Application.cs
@@ -0,0 +1 @@
+ProjectReferencesTest.Library.Hello();
diff --git a/nixpkgs/pkgs/test/dotnet/project-references/application/Application.csproj b/nixpkgs/pkgs/test/dotnet/project-references/application/Application.csproj
new file mode 100644
index 000000000000..6507637a5535
--- /dev/null
+++ b/nixpkgs/pkgs/test/dotnet/project-references/application/Application.csproj
@@ -0,0 +1,10 @@
+<Project Sdk="Microsoft.NET.Sdk">
+    <PropertyGroup>
+        <OutputType>exe</OutputType>
+    </PropertyGroup>
+
+    <ItemGroup>
+        <ProjectReference Include="../library/Library.csproj" />
+        <PackageReference Include="ProjectReferencesTest.Library" Version="*" Condition=" '$(ContinuousIntegrationBuild)'=='true' " />
+    </ItemGroup>
+</Project>
diff --git a/nixpkgs/pkgs/test/dotnet/project-references/default.nix b/nixpkgs/pkgs/test/dotnet/project-references/default.nix
new file mode 100644
index 000000000000..f40b9196c209
--- /dev/null
+++ b/nixpkgs/pkgs/test/dotnet/project-references/default.nix
@@ -0,0 +1,38 @@
+# Tests the `projectReferences = [ ... ];` feature of buildDotnetModule.
+# The `library` derivation exposes a .nupkg, which is then consumed by the `application` derivation.
+# https://nixos.org/manual/nixpkgs/unstable/index.html#packaging-a-dotnet-application
+
+{ lib
+, dotnet-sdk
+, buildDotnetModule
+, runCommand
+}:
+
+let
+  nugetDeps = ./nuget-deps.nix;
+
+  # Specify the TargetFramework via an environment variable so that we don't
+  # have to update the .csproj files when updating dotnet-sdk
+  TargetFramework = "net${lib.versions.majorMinor (lib.getVersion dotnet-sdk)}";
+
+  library = buildDotnetModule {
+    name = "project-references-test-library";
+    src = ./library;
+    inherit nugetDeps TargetFramework;
+
+    packNupkg = true;
+  };
+
+  application = buildDotnetModule {
+    name = "project-references-test-application";
+    src = ./application;
+    inherit nugetDeps TargetFramework;
+
+    projectReferences = [ library ];
+  };
+in
+
+runCommand "project-references-test" { } ''
+  ${application}/bin/Application
+  touch $out
+''
diff --git a/nixpkgs/pkgs/test/dotnet/project-references/library/Library.cs b/nixpkgs/pkgs/test/dotnet/project-references/library/Library.cs
new file mode 100644
index 000000000000..a4af4a0fea2d
--- /dev/null
+++ b/nixpkgs/pkgs/test/dotnet/project-references/library/Library.cs
@@ -0,0 +1,8 @@
+namespace ProjectReferencesTest;
+public static class Library
+{
+    public static void Hello()
+    {
+        System.Console.WriteLine("Hello, World!");
+    }
+}
diff --git a/nixpkgs/pkgs/test/dotnet/project-references/library/Library.csproj b/nixpkgs/pkgs/test/dotnet/project-references/library/Library.csproj
new file mode 100644
index 000000000000..b9a71276d24a
--- /dev/null
+++ b/nixpkgs/pkgs/test/dotnet/project-references/library/Library.csproj
@@ -0,0 +1,5 @@
+<Project Sdk="Microsoft.NET.Sdk">
+    <PropertyGroup>
+        <PackageId>ProjectReferencesTest.Library</PackageId>
+    </PropertyGroup>
+</Project>
diff --git a/nixpkgs/pkgs/test/dotnet/project-references/nuget-deps.nix b/nixpkgs/pkgs/test/dotnet/project-references/nuget-deps.nix
new file mode 100644
index 000000000000..f3a17967e25c
--- /dev/null
+++ b/nixpkgs/pkgs/test/dotnet/project-references/nuget-deps.nix
@@ -0,0 +1,5 @@
+# This file was automatically generated by passthru.fetch-deps.
+# Please dont edit it manually, your changes might get overwritten!
+
+{ fetchNuGet }: [
+]
diff --git a/nixpkgs/pkgs/test/haskell/cabalSdist/default.nix b/nixpkgs/pkgs/test/haskell/cabalSdist/default.nix
new file mode 100644
index 000000000000..1031e51e4f14
--- /dev/null
+++ b/nixpkgs/pkgs/test/haskell/cabalSdist/default.nix
@@ -0,0 +1,28 @@
+{ lib, haskellPackages, runCommand }:
+
+let
+  localRaw = haskellPackages.callPackage ./local/generated.nix {};
+in
+lib.recurseIntoAttrs rec {
+
+  helloFromCabalSdist = haskellPackages.buildFromCabalSdist haskellPackages.hello;
+
+  # A more complicated example with a cabal hook.
+  hercules-ci-cnix-store = haskellPackages.buildFromCabalSdist haskellPackages.hercules-ci-cnix-store;
+
+  localFromCabalSdist = haskellPackages.buildFromCabalSdist localRaw;
+
+  assumptionLocalHasDirectReference = runCommand "localHasDirectReference" {
+    drvPath = builtins.unsafeDiscardOutputDependency localRaw.drvPath;
+  } ''
+    grep ${./local} $drvPath >/dev/null
+    touch $out
+  '';
+
+  localHasNoDirectReference = runCommand "localHasNoDirectReference" {
+    drvPath = builtins.unsafeDiscardOutputDependency localFromCabalSdist.drvPath;
+  } ''
+    grep -v ${./local} $drvPath >/dev/null
+    touch $out
+  '';
+}
diff --git a/nixpkgs/pkgs/test/haskell/cabalSdist/local/CHANGELOG.md b/nixpkgs/pkgs/test/haskell/cabalSdist/local/CHANGELOG.md
new file mode 100644
index 000000000000..53cc3ae43d8a
--- /dev/null
+++ b/nixpkgs/pkgs/test/haskell/cabalSdist/local/CHANGELOG.md
@@ -0,0 +1,5 @@
+# Revision history for local
+
+## 0.1.0.0 -- YYYY-mm-dd
+
+* First version. Released on an unsuspecting world.
diff --git a/nixpkgs/pkgs/test/haskell/cabalSdist/local/app/Main.hs b/nixpkgs/pkgs/test/haskell/cabalSdist/local/app/Main.hs
new file mode 100644
index 000000000000..65ae4a05d5db
--- /dev/null
+++ b/nixpkgs/pkgs/test/haskell/cabalSdist/local/app/Main.hs
@@ -0,0 +1,4 @@
+module Main where
+
+main :: IO ()
+main = putStrLn "Hello, Haskell!"
diff --git a/nixpkgs/pkgs/test/haskell/cabalSdist/local/generated.nix b/nixpkgs/pkgs/test/haskell/cabalSdist/local/generated.nix
new file mode 100644
index 000000000000..bfa299962bcb
--- /dev/null
+++ b/nixpkgs/pkgs/test/haskell/cabalSdist/local/generated.nix
@@ -0,0 +1,12 @@
+# nix run ../../../../..#cabal2nix -- ./.
+{ mkDerivation, base, lib }:
+mkDerivation {
+  pname = "local";
+  version = "0.1.0.0";
+  src = ./.;
+  isLibrary = false;
+  isExecutable = true;
+  executableHaskellDepends = [ base ];
+  description = "Nixpkgs test case";
+  license = lib.licenses.mit;
+}
diff --git a/nixpkgs/pkgs/test/haskell/cabalSdist/local/local.cabal b/nixpkgs/pkgs/test/haskell/cabalSdist/local/local.cabal
new file mode 100644
index 000000000000..1670aa3af631
--- /dev/null
+++ b/nixpkgs/pkgs/test/haskell/cabalSdist/local/local.cabal
@@ -0,0 +1,13 @@
+cabal-version:      2.4
+name:               local
+version:            0.1.0.0
+
+synopsis: Nixpkgs test case
+license:  MIT
+extra-source-files: CHANGELOG.md
+
+executable local
+    main-is:          Main.hs
+    build-depends:    base
+    hs-source-dirs:   app
+    default-language: Haskell2010
diff --git a/nixpkgs/pkgs/test/haskell/default.nix b/nixpkgs/pkgs/test/haskell/default.nix
new file mode 100644
index 000000000000..8f1f21d65b51
--- /dev/null
+++ b/nixpkgs/pkgs/test/haskell/default.nix
@@ -0,0 +1,10 @@
+{ lib, callPackage }:
+
+lib.recurseIntoAttrs {
+  shellFor = callPackage ./shellFor { };
+  cabalSdist = callPackage ./cabalSdist { };
+  documentationTarball = callPackage ./documentationTarball { };
+  setBuildTarget = callPackage ./setBuildTarget { };
+  incremental = callPackage ./incremental { };
+  upstreamStackHpackVersion = callPackage ./upstreamStackHpackVersion { };
+}
diff --git a/nixpkgs/pkgs/test/haskell/documentationTarball/default.nix b/nixpkgs/pkgs/test/haskell/documentationTarball/default.nix
new file mode 100644
index 000000000000..337510281012
--- /dev/null
+++ b/nixpkgs/pkgs/test/haskell/documentationTarball/default.nix
@@ -0,0 +1,21 @@
+{ pkgs, haskellPackages }:
+
+let
+  drv = haskellPackages.vector;
+  docs = pkgs.haskell.lib.compose.documentationTarball drv;
+
+in pkgs.runCommand "test haskell.lib.compose.documentationTarball" {
+  meta = {
+    inherit (docs.meta) platforms;
+  };
+} ''
+  tar xvzf "${docs}/${drv.name}-docs.tar.gz"
+
+  # Check for Haddock html
+  find "${drv.name}-docs" | grep -q "Data-Vector.html"
+
+  # Check for source html
+  find "${drv.name}-docs" | grep -q  "src/Data.Vector.html"
+
+  touch "$out"
+''
diff --git a/nixpkgs/pkgs/test/haskell/incremental/default.nix b/nixpkgs/pkgs/test/haskell/incremental/default.nix
new file mode 100644
index 000000000000..c7bd43b11af6
--- /dev/null
+++ b/nixpkgs/pkgs/test/haskell/incremental/default.nix
@@ -0,0 +1,35 @@
+# Demonstration of incremental builds for Haskell. Useful for speeding up CI.
+#
+# See: https://www.haskellforall.com/2022/12/nixpkgs-support-for-incremental-haskell.html
+# See: https://felixspringer.xyz/homepage/blog/incrementalHaskellBuildsWithNix
+
+{ haskell, haskellPackages, lib }:
+
+let
+  inherit (haskell.lib.compose) overrideCabal;
+
+  # Incremental builds work with GHC >=9.4.
+  temporary = haskellPackages.temporary;
+
+  # This will do a full build of `temporary`, while writing the intermediate build products
+  # (compiled modules, etc.) to the `intermediates` output.
+  temporary-full-build-with-incremental-output = overrideCabal (drv: {
+    doInstallIntermediates = true;
+    enableSeparateIntermediatesOutput = true;
+  }) temporary;
+
+  # This will do an incremental build of `temporary` by copying the previously
+  # compiled modules and intermediate build products into the source tree
+  # before running the build.
+  #
+  # GHC will then naturally pick up and reuse these products, making this build
+  # complete much more quickly than the previous one.
+  temporary-incremental-build = overrideCabal (drv: {
+    previousIntermediates = temporary-full-build-with-incremental-output.intermediates;
+  }) temporary;
+in
+  temporary-incremental-build.overrideAttrs (old: {
+    meta = {
+      maintainers = lib.teams.mercury.members;
+    };
+  })
diff --git a/nixpkgs/pkgs/test/haskell/setBuildTarget/Bar.hs b/nixpkgs/pkgs/test/haskell/setBuildTarget/Bar.hs
new file mode 100644
index 000000000000..010014082c7d
--- /dev/null
+++ b/nixpkgs/pkgs/test/haskell/setBuildTarget/Bar.hs
@@ -0,0 +1,4 @@
+module Main where
+
+main :: IO ()
+main = putStrLn "Hello, Bar!"
diff --git a/nixpkgs/pkgs/test/haskell/setBuildTarget/Foo.hs b/nixpkgs/pkgs/test/haskell/setBuildTarget/Foo.hs
new file mode 100644
index 000000000000..fec7bb11fe6c
--- /dev/null
+++ b/nixpkgs/pkgs/test/haskell/setBuildTarget/Foo.hs
@@ -0,0 +1,4 @@
+module Main where
+
+main :: IO ()
+main = putStrLn "Hello, Foo!"
diff --git a/nixpkgs/pkgs/test/haskell/setBuildTarget/Setup.hs b/nixpkgs/pkgs/test/haskell/setBuildTarget/Setup.hs
new file mode 100644
index 000000000000..9a994af677b0
--- /dev/null
+++ b/nixpkgs/pkgs/test/haskell/setBuildTarget/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/nixpkgs/pkgs/test/haskell/setBuildTarget/default.nix b/nixpkgs/pkgs/test/haskell/setBuildTarget/default.nix
new file mode 100644
index 000000000000..f0c78c510449
--- /dev/null
+++ b/nixpkgs/pkgs/test/haskell/setBuildTarget/default.nix
@@ -0,0 +1,43 @@
+{ pkgs, haskellPackages }:
+
+let
+  # This can be regenerated by running `cabal2nix .` in the current directory.
+  pkgDef =
+    { mkDerivation, base, lib }:
+      mkDerivation {
+        pname = "haskell-setBuildTarget";
+        version = "0.1.0.0";
+        src = ./.;
+        isLibrary = false;
+        isExecutable = true;
+        executableHaskellDepends = [ base ];
+        license = lib.licenses.bsd3;
+      };
+
+  drv = haskellPackages.callPackage pkgDef {};
+
+  test  = target: excluded:
+    let only = pkgs.haskell.lib.compose.setBuildTarget target drv;
+    in ''
+         if [[ ! -f "${only}/bin/${target}" ]]; then
+           echo "${target} was not built"
+           exit 1
+         fi
+
+         if [[ -f "${only}/bin/${excluded}" ]]; then
+           echo "${excluded} was built, when it should not have been"
+           exit 1
+         fi
+     '';
+
+in
+pkgs.runCommand "test haskell.lib.compose.setBuildTarget" {
+  meta = {
+    inherit (drv.meta) platforms;
+  };
+} ''
+  ${test "foo" "bar"}
+  ${test "bar" "foo"}
+  touch "$out"
+''
+
diff --git a/nixpkgs/pkgs/test/haskell/setBuildTarget/haskell-setBuildTarget.cabal b/nixpkgs/pkgs/test/haskell/setBuildTarget/haskell-setBuildTarget.cabal
new file mode 100644
index 000000000000..7395e139451c
--- /dev/null
+++ b/nixpkgs/pkgs/test/haskell/setBuildTarget/haskell-setBuildTarget.cabal
@@ -0,0 +1,16 @@
+cabal-version:       >=1.10
+name:                haskell-setBuildTarget
+version:             0.1.0.0
+author:              Isaac Shapira
+maintainer:          fresheyeball@protonmail.com
+build-type:          Simple
+
+executable foo
+  main-is:             Foo.hs
+  build-depends:       base
+  default-language:    Haskell2010
+
+executable bar
+  main-is:             Bar.hs
+  build-depends:       base
+  default-language:    Haskell2010
diff --git a/nixpkgs/pkgs/test/haskell/shellFor/default.nix b/nixpkgs/pkgs/test/haskell/shellFor/default.nix
new file mode 100644
index 000000000000..82cd9e38e731
--- /dev/null
+++ b/nixpkgs/pkgs/test/haskell/shellFor/default.nix
@@ -0,0 +1,57 @@
+{ lib, writeText, haskellPackages, cabal-install }:
+
+(haskellPackages.shellFor {
+  packages = p: [ p.constraints p.cereal ];
+  # WARNING: When updating this, make sure that the libraries passed to
+  # `extraDependencies` are not actually transitive dependencies of libraries in
+  # `packages` above.  We explicitly want to test that it is possible to specify
+  # `extraDependencies` that are not in the closure of `packages`.
+  extraDependencies = p: { libraryHaskellDepends = [ p.conduit ]; };
+  nativeBuildInputs = [ cabal-install ];
+  phases = [ "unpackPhase" "buildPhase" "installPhase" ];
+  unpackPhase = ''
+    sourceRoot=$(pwd)/scratch
+    mkdir -p "$sourceRoot"
+    cd "$sourceRoot"
+    tar -xf ${haskellPackages.constraints.src}
+    tar -xf ${haskellPackages.cereal.src}
+    cp ${writeText "cabal.project" "packages: constraints* cereal*"} cabal.project
+  '';
+  buildPhase = ''
+    export HOME=$(mktemp -d)
+    mkdir -p $HOME/.cabal
+    touch $HOME/.cabal/config
+
+    # Check that the extraDependencies.libraryHaskellDepends arg is correctly
+    # picked up. This uses ghci to interpret a small Haskell program that uses
+    # a package from extraDependencies.
+    ghci <<EOF
+    :set -XOverloadedStrings
+    :m + Conduit
+    runResourceT $ connect (yield "done") (sinkFile "outfile")
+    EOF
+
+    if [[ "done" != "$(cat outfile)" ]]; then
+      echo "ERROR: extraDependencies appear not to be available in the environment"
+      exit 1
+    fi
+
+    # Check packages arg
+    cabal v2-build --offline --verbose constraints cereal --ghc-options="-O0 -j$NIX_BUILD_CORES"
+  '';
+  installPhase = ''
+    touch $out
+  '';
+}).overrideAttrs (oldAttrs: {
+  meta =
+    let
+      oldMeta = oldAttrs.meta or {};
+      oldMaintainers = oldMeta.maintainers or [];
+      additionalMaintainers = with lib.maintainers; [ cdepillabout ];
+      allMaintainers = oldMaintainers ++ additionalMaintainers;
+    in
+    oldMeta // {
+      maintainers = allMaintainers;
+      inherit (cabal-install.meta) platforms;
+    };
+})
diff --git a/nixpkgs/pkgs/test/haskell/upstreamStackHpackVersion/default.nix b/nixpkgs/pkgs/test/haskell/upstreamStackHpackVersion/default.nix
new file mode 100644
index 000000000000..f3ddbcd3e016
--- /dev/null
+++ b/nixpkgs/pkgs/test/haskell/upstreamStackHpackVersion/default.nix
@@ -0,0 +1,152 @@
+
+# This derivation confirms that the version of hpack used by stack in Nixpkgs
+# is the exact same version as the upstream stack release.
+#
+# It is important to make sure the version of hpack used by stack in Nixpkgs
+# matches with the version of hpack used by the upstream stack release.  This
+# is because hpack works slightly differently based on the version, and it can
+# be frustrating to use hpack in a team setting when members are using different
+# versions. See for more info: https://github.com/NixOS/nixpkgs/issues/223390
+#
+# This test is written as a fixed-output derivation, because we need to access
+# accesses the internet to download the upstream stack release.
+
+{ cacert, curl, lib, stack, stdenv }:
+
+let
+  # Find the hpack derivation that is a dependency of stack.  Throw exception
+  # if hpack cannot be found.
+  hpack =
+    lib.findFirst
+      (v: v.pname or "" == "hpack")
+      (throw "could not find stack's hpack dependency")
+      stack.passthru.getCabalDeps.executableHaskellDepends;
+
+  # This is a statically linked version of stack, so it should be usable within
+  # the Nixpkgs builder (at least on x86_64-linux).
+  stackDownloadUrl =
+    "https://github.com/commercialhaskell/stack/releases/download/v${stack.version}/stack-${stack.version}-linux-x86_64.tar.gz";
+
+  # This test code has been explicitly pulled out of the derivation below so
+  # that it can be hashed and added to the `name` of the derivation.  This is
+  # so that this test derivation won't be cached if the body of the test is
+  # modified.
+  #
+  # WARNING: When modifying this script, make sure you don't introduce any
+  # paths to the Nix store within it.  We only want this derivation to be re-run
+  # when the stack version (or the version of its hpack dependency) changes in
+  # Nixpkgs.
+  testScript = ''
+    curl=(
+      curl
+      --location
+      --max-redirs 20
+      --retry 3
+      --disable-epsv
+      --cookie-jar cookies
+      --user-agent "curl "
+      --insecure
+    )
+
+    # Fetch the statically-linked upstream Stack binary.
+    echo "Trying to download a statically linked stack binary from ${stackDownloadUrl} to ./stack.tar.gz ..."
+    "''${curl[@]}" "${stackDownloadUrl}" > ./stack.tar.gz
+    tar xf ./stack.tar.gz
+
+    upstream_stack_version_output="$(./stack-${stack.version}-linux-x86_64/stack --version)"
+    echo "upstream \`stack --version\` output: $upstream_stack_version_output"
+
+    nixpkgs_stack_version_output="$(stack --version)"
+    echo "nixpkgs \`stack --version\` output: $nixpkgs_stack_version_output"
+
+    # Confirm that the upstream stack version is the same as the stack version
+    # in Nixpkgs. This check isn't strictly necessary, but it is a good sanity
+    # check.
+
+    if [[ "$upstream_stack_version_output" =~ "Version "([0-9]+((\.[0-9]+)+)) ]]; then
+      upstream_stack_version="''${BASH_REMATCH[1]}"
+
+      echo "parsed upstream stack version: $upstream_stack_version"
+      echo "stack version from nixpkgs: ${stack.version}"
+
+      if [[ "${stack.version}" != "$upstream_stack_version" ]]; then
+        echo "ERROR: stack version in Nixpkgs (${stack.version}) does not match the upstream version for some reason: $upstream_stack_version"
+        exit 1
+      fi
+    else
+      echo "ERROR: Upstream stack version cannot be found in --version output: $upstream_stack_version"
+      exit 1
+    fi
+
+    # Confirm that the hpack version used in the upstream stack release is the
+    # same as the hpack version used by the Nixpkgs stack binary.
+
+    if [[ "$upstream_stack_version_output" =~ hpack-([0-9]+((\.[0-9]+)+)) ]]; then
+      upstream_hpack_version="''${BASH_REMATCH[1]}"
+
+      echo "parsed upstream stack's hpack version: $upstream_hpack_version"
+      echo "Nixpkgs stack's hpack version: ${hpack.version}"
+
+      if [[ "${hpack.version}" != "$upstream_hpack_version" ]]; then
+        echo "ERROR: stack's hpack version in Nixpkgs (${hpack.version}) does not match the upstream stack's hpack version: $upstream_hpack_version"
+        echo "The stack derivation in Nixpkgs needs to be fixed up so that it depends on hpack-$upstream_hpack_version, instead of ${hpack.name}"
+        exit 1
+      fi
+    else
+      echo "ERROR: Upstream stack's hpack version cannot be found in --version output: $upstream_hpack_version"
+      exit 1
+    fi
+
+    # Output a string with a known hash.
+    echo "success" > $out
+  '';
+
+  testScriptHash = builtins.hashString "sha256" testScript;
+in
+
+stdenv.mkDerivation {
+
+  # This name is very important.
+  #
+  # The idea here is that want this derivation to be re-run everytime the
+  # version of stack (or the version of its hpack dependency) changes in
+  # Nixpkgs.  We also want to re-run this derivation whenever the test script
+  # is changed.
+  #
+  # Nix/Hydra will re-run derivations if their name changes (even if they are a
+  # FOD and they have the same hash).
+  #
+  # The name of this derivation contains the stack version string, the hpack
+  # version string, and a hash of the test script.  So Nix will know to
+  # re-run this version when (and only when) one of those values change.
+  name = "upstream-stack-hpack-version-test-${stack.name}-${hpack.name}-${testScriptHash}";
+
+  # This is the sha256 hash for the string "success", which is output upon this
+  # test succeeding.
+  outputHash = "sha256-gbK9TqmMjbZlVPvI12N6GmmhMPMx/rcyt1yqtMSGj9U=";
+  outputHashMode = "flat";
+  outputHashAlgo = "sha256";
+
+  nativeBuildInputs = [ curl stack ];
+
+  impureEnvVars = lib.fetchers.proxyImpureEnvVars;
+
+  buildCommand = ''
+    # Make sure curl can access HTTPS sites, like GitHub.
+    #
+    # Note that we absolutely don't want the Nix store path of the cacert
+    # derivation in the testScript, because we don't want to rebuild this
+    # derivation when only the cacert derivation changes.
+    export SSL_CERT_FILE="${cacert}/etc/ssl/certs/ca-bundle.crt"
+  '' + testScript;
+
+  meta = with lib; {
+    description = "Test that the stack in Nixpkgs uses the same version of Hpack as the upstream stack release";
+    maintainers = with maintainers; [ cdepillabout ];
+
+    # This derivation internally runs a statically-linked version of stack from
+    # upstream.  This statically-linked version of stack is only available for
+    # x86_64-linux, so this test can only be run on x86_64-linux.
+    platforms = [ "x86_64-linux" ];
+  };
+}
diff --git a/nixpkgs/pkgs/test/hooks/default.nix b/nixpkgs/pkgs/test/hooks/default.nix
new file mode 100644
index 000000000000..aabf939b6865
--- /dev/null
+++ b/nixpkgs/pkgs/test/hooks/default.nix
@@ -0,0 +1,8 @@
+# To run these tests:
+# nix-build -A tests.hooks
+
+{ stdenv, tests, lib }:
+
+{
+  default-stdenv-hooks = lib.recurseIntoAttrs tests.stdenv.hooks;
+}
diff --git a/nixpkgs/pkgs/test/install-shell-files/default.nix b/nixpkgs/pkgs/test/install-shell-files/default.nix
new file mode 100644
index 000000000000..aef5acc1de6b
--- /dev/null
+++ b/nixpkgs/pkgs/test/install-shell-files/default.nix
@@ -0,0 +1,125 @@
+{ lib, runCommandLocal, recurseIntoAttrs, installShellFiles }:
+
+let
+  runTest = name: env: buildCommand:
+    runCommandLocal "install-shell-files--${name}" ({
+      nativeBuildInputs = [ installShellFiles ];
+      meta.platforms = lib.platforms.all;
+    } // env) buildCommand;
+in
+
+recurseIntoAttrs {
+  # installManPage
+
+  install-manpage = runTest "install-manpage" {} ''
+    mkdir -p doc
+    echo foo > doc/foo.1
+    echo bar > doc/bar.2.gz
+    echo baz > doc/baz.3
+
+    installManPage doc/*
+
+    cmp doc/foo.1 $out/share/man/man1/foo.1
+    cmp doc/bar.2.gz $out/share/man/man2/bar.2.gz
+    cmp doc/baz.3 $out/share/man/man3/baz.3
+  '';
+  install-manpage-outputs = runTest "install-manpage-outputs" {
+    outputs = [ "out" "man" "devman" ];
+  } ''
+    mkdir -p doc
+    echo foo > doc/foo.1
+    echo bar > doc/bar.3
+
+    installManPage doc/*
+
+    # assert they didn't go into $out
+    [[ ! -f $out/share/man/man1/foo.1 && ! -f $out/share/man/man3/bar.3 ]]
+
+    # foo.1 alone went into man
+    cmp doc/foo.1 ''${!outputMan:?}/share/man/man1/foo.1
+    [[ ! -f ''${!outputMan:?}/share/man/man3/bar.3 ]]
+
+    # bar.3 alone went into devman
+    cmp doc/bar.3 ''${!outputDevman:?}/share/man/man3/bar.3
+    [[ ! -f ''${!outputDevman:?}/share/man/man1/foo.1 ]]
+
+    touch $out
+  '';
+
+  # installShellCompletion
+
+  install-completion = runTest "install-completion" {} ''
+    echo foo > foo
+    echo bar > bar
+    echo baz > baz
+    echo qux > qux.zsh
+    echo quux > quux
+
+    installShellCompletion --bash foo bar --zsh baz qux.zsh --fish quux
+
+    cmp foo $out/share/bash-completion/completions/foo
+    cmp bar $out/share/bash-completion/completions/bar
+    cmp baz $out/share/zsh/site-functions/_baz
+    cmp qux.zsh $out/share/zsh/site-functions/_qux
+    cmp quux $out/share/fish/vendor_completions.d/quux
+  '';
+  install-completion-output = runTest "install-completion-output" {
+    outputs = [ "out" "bin" ];
+  } ''
+    echo foo > foo
+
+    installShellCompletion --bash foo
+
+    # assert it didn't go into $out
+    [[ ! -f $out/share/bash-completion/completions/foo ]]
+
+    cmp foo ''${!outputBin:?}/share/bash-completion/completions/foo
+
+    touch $out
+  '';
+  install-completion-name = runTest "install-completion-name" {} ''
+    echo foo > foo
+    echo bar > bar
+    echo baz > baz
+
+    installShellCompletion --bash --name foobar.bash foo --zsh --name _foobar bar --fish baz
+
+    cmp foo $out/share/bash-completion/completions/foobar.bash
+    cmp bar $out/share/zsh/site-functions/_foobar
+    cmp baz $out/share/fish/vendor_completions.d/baz
+  '';
+  install-completion-inference = runTest "install-completion-inference" {} ''
+    echo foo > foo.bash
+    echo bar > bar.zsh
+    echo baz > baz.fish
+
+    installShellCompletion foo.bash bar.zsh baz.fish
+
+    cmp foo.bash $out/share/bash-completion/completions/foo.bash
+    cmp bar.zsh $out/share/zsh/site-functions/_bar
+    cmp baz.fish $out/share/fish/vendor_completions.d/baz.fish
+  '';
+  install-completion-cmd = runTest "install-completion-cmd" {} ''
+    echo foo > foo.bash
+    echo bar > bar.zsh
+    echo baz > baz.fish
+    echo qux > qux.fish
+
+    installShellCompletion --cmd foobar --bash foo.bash --zsh bar.zsh --fish baz.fish --name qux qux.fish
+
+    cmp foo.bash $out/share/bash-completion/completions/foobar.bash
+    cmp bar.zsh $out/share/zsh/site-functions/_foobar
+    cmp baz.fish $out/share/fish/vendor_completions.d/foobar.fish
+    cmp qux.fish $out/share/fish/vendor_completions.d/qux
+  '';
+  install-completion-fifo = runTest "install-completion-fifo" {} ''
+    installShellCompletion \
+      --bash --name foo.bash <(echo foo) \
+      --zsh --name _foo <(echo bar) \
+      --fish --name foo.fish <(echo baz)
+
+    [[ $(<$out/share/bash-completion/completions/foo.bash) == foo ]] || { echo "foo.bash comparison failed"; exit 1; }
+    [[ $(<$out/share/zsh/site-functions/_foo) == bar ]] || { echo "_foo comparison failed"; exit 1; }
+    [[ $(<$out/share/fish/vendor_completions.d/foo.fish) == baz ]] || { echo "foo.fish comparison failed"; exit 1; }
+  '';
+}
diff --git a/nixpkgs/pkgs/test/kernel.nix b/nixpkgs/pkgs/test/kernel.nix
new file mode 100644
index 000000000000..2ccd188b1edb
--- /dev/null
+++ b/nixpkgs/pkgs/test/kernel.nix
@@ -0,0 +1,73 @@
+# to run these tests:
+# nix-instantiate --eval --strict . -A tests.kernel-config
+#
+# make sure to use NON EXISTING kernel settings else they may conflict with
+# common-config.nix
+{ lib, pkgs }:
+
+with lib;
+with kernel;
+
+let
+  lts_kernel = pkgs.linuxPackages.kernel;
+
+  # to see the result once the module transformed the lose structured config
+  getConfig = structuredConfig:
+    (lts_kernel.override {
+      structuredExtraConfig = structuredConfig;
+    }).configfile.structuredConfig;
+
+  mandatoryVsOptionalConfig = mkMerge [
+    { NIXOS_FAKE_USB_DEBUG = yes;}
+    { NIXOS_FAKE_USB_DEBUG = option yes; }
+  ];
+
+  freeformConfig = mkMerge [
+    { NIXOS_FAKE_MMC_BLOCK_MINORS = freeform "32"; } # same as default, won't trigger any error
+    { NIXOS_FAKE_MMC_BLOCK_MINORS = freeform "64"; } # will trigger an error but the message is not great:
+  ];
+
+  mkDefaultWorksConfig = mkMerge [
+    { "NIXOS_TEST_BOOLEAN"  = yes; }
+    { "NIXOS_TEST_BOOLEAN"  = lib.mkDefault no; }
+  ];
+
+  allOptionalRemainOptional = mkMerge [
+    { NIXOS_FAKE_USB_DEBUG = option yes;}
+    { NIXOS_FAKE_USB_DEBUG = option yes;}
+  ];
+
+in
+runTests {
+  testEasy = {
+    expr = (getConfig { NIXOS_FAKE_USB_DEBUG = yes;}).NIXOS_FAKE_USB_DEBUG;
+    expected = { tristate = "y"; optional = false; freeform = null; };
+  };
+
+  # mandatory flag should win over optional
+  testMandatoryCheck = {
+    expr = (getConfig mandatoryVsOptionalConfig).NIXOS_FAKE_USB_DEBUG.optional;
+    expected = false;
+  };
+
+  testYesWinsOverNo = {
+    expr = (getConfig mkDefaultWorksConfig)."NIXOS_TEST_BOOLEAN".tristate;
+    expected = "y";
+  };
+
+  testAllOptionalRemainOptional = {
+    expr = (getConfig allOptionalRemainOptional)."NIXOS_FAKE_USB_DEBUG".optional;
+    expected = true;
+  };
+
+  # check that freeform options are unique
+  # Should trigger
+  # > The option `settings.NIXOS_FAKE_MMC_BLOCK_MINORS.freeform' has conflicting definitions, in `<unknown-file>' and `<unknown-file>'
+  testTreeform = let
+    res = builtins.tryEval ( (getConfig freeformConfig).NIXOS_FAKE_MMC_BLOCK_MINORS.freeform);
+  in {
+    expr = res.success;
+    expected = false;
+  };
+
+}
diff --git a/nixpkgs/pkgs/test/ld-library-path/default.nix b/nixpkgs/pkgs/test/ld-library-path/default.nix
new file mode 100644
index 000000000000..74c52cef2532
--- /dev/null
+++ b/nixpkgs/pkgs/test/ld-library-path/default.nix
@@ -0,0 +1,88 @@
+{ lib, stdenv }:
+
+# This tests that libraries listed in LD_LIBRARY_PATH take precedence over those listed in RPATH.
+
+let
+  # A simple test library: libgreeting.so which exports a single function getGreeting() returning the good old hello greeting.
+  libgreeting = stdenv.mkDerivation {
+    name = "libgreeting";
+
+    code = ''
+      const char* getGreeting() { return "Hello, world!"; }
+    '';
+
+    unpackPhase = ''
+      echo "$code" > libgreeting.c
+    '';
+
+    installPhase = ''
+      mkdir -p $out/lib
+      $CC -c -fpic libgreeting.c
+      $CC -shared libgreeting.o -o $out/lib/libgreeting.so
+    '';
+  };
+
+  # A variant of libgreeting.so that returns a different message.
+  libgoodbye = libgreeting.overrideAttrs (_: {
+    name = "libgoodbye";
+    code = ''
+      const char* getGreeting() { return "Goodbye, world!"; }
+    '';
+  });
+
+  # A simple consumer of libgreeting.so that just prints the greeting to stdout.
+  testProgram = stdenv.mkDerivation {
+    name = "greeting-test";
+
+    buildInputs = [ libgreeting ];
+
+    code = ''
+      #include <stdio.h>
+
+      extern const char* getGreeting(void);
+
+      int main() {
+        puts(getGreeting());
+      }
+    '';
+
+    unpackPhase = ''
+      echo "$code" > greeting-test.c
+    '';
+
+    installPhase = ''
+      mkdir -p $out/bin
+      $CC -c greeting-test.c
+      $CC greeting-test.o -lgreeting -o $out/bin/greeting-test
+
+      # Now test the installed binaries right after compiling them. In particular,
+      # don't do this in installCheckPhase because fixupPhase has been run by then!
+      (
+        export PATH=$out/bin
+        set -x
+
+        # Verify that our unmodified binary works as expected.
+        [ "$(greeting-test)" = "Hello, world!" ]
+
+        # And finally, test that a library in LD_LIBRARY_PATH takes precedence over the linked-in library.
+        [ "$(LD_LIBRARY_PATH=${libgoodbye}/lib greeting-test)" = "Goodbye, world!" ]
+      )
+    '';
+
+  };
+in stdenv.mkDerivation {
+  name = "test-LD_LIBRARY_PATH";
+  nativeBuildInputs = [ testProgram ];
+
+  buildCommand = ''
+    # And for good measure, repeat the tests again from a separate derivation,
+    # as fixupPhase done by the stdenv can (and has!) affect the result.
+
+    [ "$(greeting-test)" = "Hello, world!" ]
+    [ "$(LD_LIBRARY_PATH=${libgoodbye}/lib greeting-test)" = "Goodbye, world!" ]
+
+    touch $out
+  '';
+
+  meta.platforms = lib.platforms.linux;
+}
diff --git a/nixpkgs/pkgs/test/macos-sierra-shared/default.nix b/nixpkgs/pkgs/test/macos-sierra-shared/default.nix
new file mode 100644
index 000000000000..810d5d97829b
--- /dev/null
+++ b/nixpkgs/pkgs/test/macos-sierra-shared/default.nix
@@ -0,0 +1,90 @@
+{ lib, clangStdenv, clang-sierraHack-stdenv, stdenvNoCC }:
+
+let
+  makeBigExe = stdenv: prefix: rec {
+
+    count = 320;
+
+    sillyLibs = lib.genList (i: stdenv.mkDerivation rec {
+      name = "${prefix}-fluff-${toString i}";
+      unpackPhase = ''
+        src=$PWD
+        cat << 'EOF' > ${name}.c
+        unsigned int asdf_${toString i}(void) {
+          return ${toString i};
+        }
+        EOF
+      '';
+      buildPhase = ''
+        $CC -std=c99 -shared ${name}.c -o lib${name}.dylib -Wl,-install_name,$out/lib/lib${name}.dylib
+      '';
+      installPhase = ''
+        mkdir -p "$out/lib"
+        mv lib${name}.dylib "$out/lib"
+      '';
+      meta.platforms = lib.platforms.darwin;
+    }) count;
+
+    finalExe = stdenv.mkDerivation {
+      name = "${prefix}-final-asdf";
+      unpackPhase = ''
+        src=$PWD
+        cat << 'EOF' > main.cxx
+
+        #include <cstdlib>
+        #include <iostream>
+
+        ${toString (lib.genList (i: "extern \"C\" unsigned int asdf_${toString i}(void); ") count)}
+
+        unsigned int (*funs[])(void) = {
+          ${toString (lib.genList (i: "asdf_${toString i},") count)}
+        };
+
+        int main(int argc, char **argv) {
+          bool ret;
+          unsigned int i = 0;
+          for (auto f : funs) {
+            if (f() != i++) {
+              std::cerr << "Failed to get expected response from function #" << i << std::endl;
+              return EXIT_FAILURE;
+            }
+          }
+          return EXIT_SUCCESS;
+        }
+        EOF
+      '';
+      buildPhase = ''
+        $CXX -std=c++11 main.cxx ${toString (map (x: "-l${x.name}") sillyLibs)} -o ${prefix}-asdf
+      '';
+      buildInputs = sillyLibs;
+      installPhase = ''
+        mkdir -p "$out/bin"
+        mv ${prefix}-asdf "$out/bin"
+      '';
+      meta.platforms = lib.platforms.darwin;
+    };
+
+  };
+
+  good = makeBigExe clang-sierraHack-stdenv "good";
+
+  bad  = makeBigExe clangStdenv             "bad";
+
+in stdenvNoCC.mkDerivation {
+  name = "macos-sierra-shared-test";
+  buildInputs = [ good.finalExe bad.finalExe ];
+  # TODO(@Ericson2314): Be impure or require exact MacOS version of builder?
+  buildCommand = ''
+    if bad-asdf &> /dev/null
+    then echo "WARNING: bad-asdf did not fail, not running on sierra?" >&2
+    else echo "bad-asdf should fail on sierra, OK" >&2
+    fi
+
+    # Must succeed on all supported MacOS versions
+    good-asdf
+    echo "good-asdf should succeed on sierra, OK"
+
+    touch $out
+  '';
+  meta.platforms = lib.platforms.darwin;
+}
diff --git a/nixpkgs/pkgs/test/make-binary-wrapper/add-flags.c b/nixpkgs/pkgs/test/make-binary-wrapper/add-flags.c
new file mode 100644
index 000000000000..d998a5f6f983
--- /dev/null
+++ b/nixpkgs/pkgs/test/make-binary-wrapper/add-flags.c
@@ -0,0 +1,25 @@
+#include <unistd.h>
+#include <stdlib.h>
+#include <assert.h>
+
+int main(int argc, char **argv) {
+    char **argv_tmp = calloc(6 + argc + 2 + 1, sizeof(*argv_tmp));
+    assert(argv_tmp != NULL);
+    argv_tmp[0] = argv[0];
+    argv_tmp[1] = "-x";
+    argv_tmp[2] = "-y";
+    argv_tmp[3] = "-z";
+    argv_tmp[4] = "-abc";
+    argv_tmp[5] = "-g";
+    argv_tmp[6] = "*.txt";
+    for (int i = 1; i < argc; ++i) {
+        argv_tmp[6 + i] = argv[i];
+    }
+    argv_tmp[6 + argc + 0] = "-foo";
+    argv_tmp[6 + argc + 1] = "-bar";
+    argv_tmp[6 + argc + 2] = NULL;
+    argv = argv_tmp;
+
+    argv[0] = "/send/me/flags";
+    return execv("/send/me/flags", argv);
+}
diff --git a/nixpkgs/pkgs/test/make-binary-wrapper/add-flags.cmdline b/nixpkgs/pkgs/test/make-binary-wrapper/add-flags.cmdline
new file mode 100644
index 000000000000..1ca964ab4e7a
--- /dev/null
+++ b/nixpkgs/pkgs/test/make-binary-wrapper/add-flags.cmdline
@@ -0,0 +1,4 @@
+    --append-flags "-foo -bar" \
+    --add-flags "-x -y -z" \
+    --add-flags -abc \
+    --add-flags "-g *.txt"
diff --git a/nixpkgs/pkgs/test/make-binary-wrapper/add-flags.env b/nixpkgs/pkgs/test/make-binary-wrapper/add-flags.env
new file mode 100644
index 000000000000..f0641ef36f7c
--- /dev/null
+++ b/nixpkgs/pkgs/test/make-binary-wrapper/add-flags.env
@@ -0,0 +1,10 @@
+CWD=SUBST_CWD
+SUBST_ARGV0
+-x
+-y
+-z
+-abc
+-g
+*.txt
+-foo
+-bar
diff --git a/nixpkgs/pkgs/test/make-binary-wrapper/argv0.c b/nixpkgs/pkgs/test/make-binary-wrapper/argv0.c
new file mode 100644
index 000000000000..70c36889dc89
--- /dev/null
+++ b/nixpkgs/pkgs/test/make-binary-wrapper/argv0.c
@@ -0,0 +1,7 @@
+#include <unistd.h>
+#include <stdlib.h>
+
+int main(int argc, char **argv) {
+    argv[0] = "alternative-name";
+    return execv("/send/me/flags", argv);
+}
diff --git a/nixpkgs/pkgs/test/make-binary-wrapper/argv0.cmdline b/nixpkgs/pkgs/test/make-binary-wrapper/argv0.cmdline
new file mode 100644
index 000000000000..1cadce8312a4
--- /dev/null
+++ b/nixpkgs/pkgs/test/make-binary-wrapper/argv0.cmdline
@@ -0,0 +1 @@
+    --argv0 alternative-name
diff --git a/nixpkgs/pkgs/test/make-binary-wrapper/argv0.env b/nixpkgs/pkgs/test/make-binary-wrapper/argv0.env
new file mode 100644
index 000000000000..04c13d32ee6d
--- /dev/null
+++ b/nixpkgs/pkgs/test/make-binary-wrapper/argv0.env
@@ -0,0 +1,2 @@
+CWD=SUBST_CWD
+alternative-name
diff --git a/nixpkgs/pkgs/test/make-binary-wrapper/basic.c b/nixpkgs/pkgs/test/make-binary-wrapper/basic.c
new file mode 100644
index 000000000000..1c1266181377
--- /dev/null
+++ b/nixpkgs/pkgs/test/make-binary-wrapper/basic.c
@@ -0,0 +1,7 @@
+#include <unistd.h>
+#include <stdlib.h>
+
+int main(int argc, char **argv) {
+    argv[0] = "/send/me/flags";
+    return execv("/send/me/flags", argv);
+}
diff --git a/nixpkgs/pkgs/test/make-binary-wrapper/basic.cmdline b/nixpkgs/pkgs/test/make-binary-wrapper/basic.cmdline
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/nixpkgs/pkgs/test/make-binary-wrapper/basic.cmdline
diff --git a/nixpkgs/pkgs/test/make-binary-wrapper/basic.env b/nixpkgs/pkgs/test/make-binary-wrapper/basic.env
new file mode 100644
index 000000000000..b0da31959447
--- /dev/null
+++ b/nixpkgs/pkgs/test/make-binary-wrapper/basic.env
@@ -0,0 +1,2 @@
+CWD=SUBST_CWD
+SUBST_ARGV0
diff --git a/nixpkgs/pkgs/test/make-binary-wrapper/chdir.c b/nixpkgs/pkgs/test/make-binary-wrapper/chdir.c
new file mode 100644
index 000000000000..9e0b7e2c7f52
--- /dev/null
+++ b/nixpkgs/pkgs/test/make-binary-wrapper/chdir.c
@@ -0,0 +1,11 @@
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#define assert_success(e) do { if ((e) < 0) { perror(#e); abort(); } } while (0)
+
+int main(int argc, char **argv) {
+    assert_success(chdir("./tmp/foo"));
+    argv[0] = "/send/me/flags";
+    return execv("/send/me/flags", argv);
+}
diff --git a/nixpkgs/pkgs/test/make-binary-wrapper/chdir.cmdline b/nixpkgs/pkgs/test/make-binary-wrapper/chdir.cmdline
new file mode 100644
index 000000000000..d6ab081e8d35
--- /dev/null
+++ b/nixpkgs/pkgs/test/make-binary-wrapper/chdir.cmdline
@@ -0,0 +1 @@
+    --chdir ./tmp/foo
diff --git a/nixpkgs/pkgs/test/make-binary-wrapper/chdir.env b/nixpkgs/pkgs/test/make-binary-wrapper/chdir.env
new file mode 100644
index 000000000000..ea1c61054e50
--- /dev/null
+++ b/nixpkgs/pkgs/test/make-binary-wrapper/chdir.env
@@ -0,0 +1,2 @@
+CWD=SUBST_CWD/tmp/foo
+SUBST_ARGV0
diff --git a/nixpkgs/pkgs/test/make-binary-wrapper/combination.c b/nixpkgs/pkgs/test/make-binary-wrapper/combination.c
new file mode 100644
index 000000000000..8ce8a4722a0b
--- /dev/null
+++ b/nixpkgs/pkgs/test/make-binary-wrapper/combination.c
@@ -0,0 +1,53 @@
+#define _GNU_SOURCE         /* See feature_test_macros(7) */
+#include <unistd.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <stdio.h>
+
+#define assert_success(e) do { if ((e) < 0) { perror(#e); abort(); } } while (0)
+
+void set_env_prefix(char *env, char *sep, char *prefix) {
+    char *existing = getenv(env);
+    if (existing) {
+        char *val;
+        assert_success(asprintf(&val, "%s%s%s", prefix, sep, existing));
+        assert_success(setenv(env, val, 1));
+        free(val);
+    } else {
+        assert_success(setenv(env, prefix, 1));
+    }
+}
+
+void set_env_suffix(char *env, char *sep, char *suffix) {
+    char *existing = getenv(env);
+    if (existing) {
+        char *val;
+        assert_success(asprintf(&val, "%s%s%s", existing, sep, suffix));
+        assert_success(setenv(env, val, 1));
+        free(val);
+    } else {
+        assert_success(setenv(env, suffix, 1));
+    }
+}
+
+int main(int argc, char **argv) {
+    assert_success(setenv("MESSAGE", "HELLO", 0));
+    set_env_prefix("PATH", ":", "/usr/bin/");
+    set_env_suffix("PATH", ":", "/usr/local/bin/");
+    putenv("MESSAGE2=WORLD");
+
+    char **argv_tmp = calloc(3 + argc + 0 + 1, sizeof(*argv_tmp));
+    assert(argv_tmp != NULL);
+    argv_tmp[0] = argv[0];
+    argv_tmp[1] = "-x";
+    argv_tmp[2] = "-y";
+    argv_tmp[3] = "-z";
+    for (int i = 1; i < argc; ++i) {
+        argv_tmp[3 + i] = argv[i];
+    }
+    argv_tmp[3 + argc + 0] = NULL;
+    argv = argv_tmp;
+
+    argv[0] = "my-wrapper";
+    return execv("/send/me/flags", argv);
+}
diff --git a/nixpkgs/pkgs/test/make-binary-wrapper/combination.cmdline b/nixpkgs/pkgs/test/make-binary-wrapper/combination.cmdline
new file mode 100644
index 000000000000..fb3861235c8b
--- /dev/null
+++ b/nixpkgs/pkgs/test/make-binary-wrapper/combination.cmdline
@@ -0,0 +1,6 @@
+    --argv0 my-wrapper \
+    --set-default MESSAGE HELLO \
+    --prefix PATH : /usr/bin/ \
+    --suffix PATH : /usr/local/bin/ \
+    --add-flags "-x -y -z" \
+    --set MESSAGE2 WORLD
diff --git a/nixpkgs/pkgs/test/make-binary-wrapper/combination.env b/nixpkgs/pkgs/test/make-binary-wrapper/combination.env
new file mode 100644
index 000000000000..886420c01d1e
--- /dev/null
+++ b/nixpkgs/pkgs/test/make-binary-wrapper/combination.env
@@ -0,0 +1,8 @@
+MESSAGE=HELLO
+PATH=/usr/bin/:/usr/local/bin/
+MESSAGE2=WORLD
+CWD=SUBST_CWD
+my-wrapper
+-x
+-y
+-z
diff --git a/nixpkgs/pkgs/test/make-binary-wrapper/cross.nix b/nixpkgs/pkgs/test/make-binary-wrapper/cross.nix
new file mode 100644
index 000000000000..64912364b863
--- /dev/null
+++ b/nixpkgs/pkgs/test/make-binary-wrapper/cross.nix
@@ -0,0 +1,28 @@
+{ stdenv
+, runCommand
+, makeBinaryWrapper
+, binutils
+, lib
+, expectedArch ? stdenv.hostPlatform.parsed.cpu.name
+}:
+
+
+runCommand "make-binary-wrapper-test-cross" {
+  nativeBuildInputs = [
+    makeBinaryWrapper
+    binutils
+  ];
+  # For x86_64-linux the machine field is
+  # Advanced Micro Devices X86-64
+  # and uses a dash instead of a underscore unlike x86_64-linux in hostPlatform.parsed.cpu.name
+  expectedArch = lib.replaceStrings ["_"] ["-"] expectedArch;
+} ''
+  touch prog
+  chmod +x prog
+  makeWrapper prog $out
+  read -r _ arch < <($READELF --file-header $out | grep Machine:)
+  if [[ ''${arch,,} != *"''${expectedArch,,}"* ]]; then
+    echo "expected $expectedArch, got $arch"
+    exit 1
+  fi
+''
diff --git a/nixpkgs/pkgs/test/make-binary-wrapper/default.nix b/nixpkgs/pkgs/test/make-binary-wrapper/default.nix
new file mode 100644
index 000000000000..4c6fffd100a7
--- /dev/null
+++ b/nixpkgs/pkgs/test/make-binary-wrapper/default.nix
@@ -0,0 +1,61 @@
+{ lib
+, stdenv
+, pkgsCross
+, makeBinaryWrapper
+, writeText
+, runCommand
+, runCommandCC
+}:
+
+let
+  env = { nativeBuildInputs = [ makeBinaryWrapper ]; };
+  envCheck = runCommandCC "envcheck" env ''
+    cc -Wall -Werror -Wpedantic -o $out ${./envcheck.c}
+  '';
+  makeGoldenTest = testname: runCommand "make-binary-wrapper-test-${testname}" env ''
+    mkdir -p tmp/foo # for the chdir test
+
+    params=$(<"${./.}/${testname}.cmdline")
+    eval "makeCWrapper /send/me/flags $params" > wrapper.c
+
+    diff wrapper.c "${./.}/${testname}.c"
+
+    if [ -f "${./.}/${testname}.env" ]; then
+      eval "makeWrapper ${envCheck} wrapped $params"
+      env -i ./wrapped > env.txt
+      sed "s#SUBST_ARGV0#${envCheck}#;s#SUBST_CWD#$PWD#" \
+        "${./.}/${testname}.env" > golden-env.txt
+      if ! diff env.txt golden-env.txt; then
+        echo "env/argv should be:"
+        cat golden-env.txt
+        echo "env/argv output is:"
+        cat env.txt
+        exit 1
+      fi
+    else
+      # without a golden env, we expect the wrapper compilation to fail
+      ! eval "makeWrapper ${envCheck} wrapped $params" &> error.txt
+    fi
+
+    cp wrapper.c $out
+  '';
+  tests = lib.genAttrs [
+    "add-flags"
+    "argv0"
+    "basic"
+    "chdir"
+    "combination"
+    "env"
+    "inherit-argv0"
+    "invalid-env"
+    "overlength-strings"
+    "prefix"
+    "suffix"
+  ] makeGoldenTest // lib.optionalAttrs (! stdenv.isDarwin) {
+    cross = pkgsCross.${if stdenv.buildPlatform.isAarch64 then "gnu64" else "aarch64-multiplatform"}.callPackage ./cross.nix { };
+  };
+in
+
+writeText "make-binary-wrapper-tests" ''
+  ${lib.concatStringsSep "\n" (builtins.attrValues tests)}
+'' // tests
diff --git a/nixpkgs/pkgs/test/make-binary-wrapper/env.c b/nixpkgs/pkgs/test/make-binary-wrapper/env.c
new file mode 100644
index 000000000000..7e0422dee3bd
--- /dev/null
+++ b/nixpkgs/pkgs/test/make-binary-wrapper/env.c
@@ -0,0 +1,14 @@
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#define assert_success(e) do { if ((e) < 0) { perror(#e); abort(); } } while (0)
+
+int main(int argc, char **argv) {
+    putenv("PART1=HELLO");
+    assert_success(setenv("PART2", "WORLD", 0));
+    assert_success(unsetenv("SOME_OTHER_VARIABLE"));
+    putenv("PART3=\"!!\n\"");
+    argv[0] = "/send/me/flags";
+    return execv("/send/me/flags", argv);
+}
diff --git a/nixpkgs/pkgs/test/make-binary-wrapper/env.cmdline b/nixpkgs/pkgs/test/make-binary-wrapper/env.cmdline
new file mode 100644
index 000000000000..3c89f33e2dce
--- /dev/null
+++ b/nixpkgs/pkgs/test/make-binary-wrapper/env.cmdline
@@ -0,0 +1,4 @@
+    --set PART1 HELLO \
+    --set-default PART2 WORLD \
+    --unset SOME_OTHER_VARIABLE \
+    --set PART3 $'"!!\n"'
diff --git a/nixpkgs/pkgs/test/make-binary-wrapper/env.env b/nixpkgs/pkgs/test/make-binary-wrapper/env.env
new file mode 100644
index 000000000000..c7661e165e09
--- /dev/null
+++ b/nixpkgs/pkgs/test/make-binary-wrapper/env.env
@@ -0,0 +1,6 @@
+PART1=HELLO
+PART2=WORLD
+PART3="!!
+"
+CWD=SUBST_CWD
+SUBST_ARGV0
diff --git a/nixpkgs/pkgs/test/make-binary-wrapper/envcheck.c b/nixpkgs/pkgs/test/make-binary-wrapper/envcheck.c
new file mode 100644
index 000000000000..848fbdaa80f2
--- /dev/null
+++ b/nixpkgs/pkgs/test/make-binary-wrapper/envcheck.c
@@ -0,0 +1,22 @@
+#include <limits.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int main(int argc, char **argv, char **envp) {
+  for (char **env = envp; *env != 0; ++env) {
+    puts(*env);
+  }
+
+   char cwd[PATH_MAX];
+   if (getcwd(cwd, sizeof(cwd))) {
+     printf("CWD=%s\n", cwd);
+   } else {
+     perror("getcwd() error");
+     return 1;
+   }
+
+  for (int i=0; i < argc; ++i) {
+    puts(argv[i]);
+  }
+  return 0;
+}
diff --git a/nixpkgs/pkgs/test/make-binary-wrapper/inherit-argv0.c b/nixpkgs/pkgs/test/make-binary-wrapper/inherit-argv0.c
new file mode 100644
index 000000000000..e1c2bc926aa7
--- /dev/null
+++ b/nixpkgs/pkgs/test/make-binary-wrapper/inherit-argv0.c
@@ -0,0 +1,6 @@
+#include <unistd.h>
+#include <stdlib.h>
+
+int main(int argc, char **argv) {
+    return execv("/send/me/flags", argv);
+}
diff --git a/nixpkgs/pkgs/test/make-binary-wrapper/inherit-argv0.cmdline b/nixpkgs/pkgs/test/make-binary-wrapper/inherit-argv0.cmdline
new file mode 100644
index 000000000000..088076799835
--- /dev/null
+++ b/nixpkgs/pkgs/test/make-binary-wrapper/inherit-argv0.cmdline
@@ -0,0 +1 @@
+    --inherit-argv0
diff --git a/nixpkgs/pkgs/test/make-binary-wrapper/inherit-argv0.env b/nixpkgs/pkgs/test/make-binary-wrapper/inherit-argv0.env
new file mode 100644
index 000000000000..c46ca95eefbc
--- /dev/null
+++ b/nixpkgs/pkgs/test/make-binary-wrapper/inherit-argv0.env
@@ -0,0 +1,2 @@
+CWD=SUBST_CWD
+./wrapped
diff --git a/nixpkgs/pkgs/test/make-binary-wrapper/invalid-env.c b/nixpkgs/pkgs/test/make-binary-wrapper/invalid-env.c
new file mode 100644
index 000000000000..4dfd36fb68a0
--- /dev/null
+++ b/nixpkgs/pkgs/test/make-binary-wrapper/invalid-env.c
@@ -0,0 +1,14 @@
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#define assert_success(e) do { if ((e) < 0) { perror(#e); abort(); } } while (0)
+
+int main(int argc, char **argv) {
+    putenv("==TEST1");
+    #error Illegal environment variable name `=` (cannot contain `=`)
+    assert_success(setenv("", "TEST2", 0));
+    #error Environment variable name can't be empty.
+    argv[0] = "/send/me/flags";
+    return execv("/send/me/flags", argv);
+}
diff --git a/nixpkgs/pkgs/test/make-binary-wrapper/invalid-env.cmdline b/nixpkgs/pkgs/test/make-binary-wrapper/invalid-env.cmdline
new file mode 100644
index 000000000000..a03b001e754e
--- /dev/null
+++ b/nixpkgs/pkgs/test/make-binary-wrapper/invalid-env.cmdline
@@ -0,0 +1,2 @@
+    --set "=" "TEST1" \
+    --set-default "" "TEST2"
diff --git a/nixpkgs/pkgs/test/make-binary-wrapper/overlength-strings.c b/nixpkgs/pkgs/test/make-binary-wrapper/overlength-strings.c
new file mode 100644
index 000000000000..579705d33e94
--- /dev/null
+++ b/nixpkgs/pkgs/test/make-binary-wrapper/overlength-strings.c
@@ -0,0 +1,25 @@
+#define _GNU_SOURCE         /* See feature_test_macros(7) */
+#include <unistd.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <stdio.h>
+
+#define assert_success(e) do { if ((e) < 0) { perror(#e); abort(); } } while (0)
+
+void set_env_prefix(char *env, char *sep, char *prefix) {
+    char *existing = getenv(env);
+    if (existing) {
+        char *val;
+        assert_success(asprintf(&val, "%s%s%s", prefix, sep, existing));
+        assert_success(setenv(env, val, 1));
+        free(val);
+    } else {
+        assert_success(setenv(env, prefix, 1));
+    }
+}
+
+int main(int argc, char **argv) {
+    set_env_prefix("PATH", ":", "/nix/store/00000000000000000000000000000000-loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong");
+    argv[0] = "/send/me/flags";
+    return execv("/send/me/flags", argv);
+}
diff --git a/nixpkgs/pkgs/test/make-binary-wrapper/overlength-strings.cmdline b/nixpkgs/pkgs/test/make-binary-wrapper/overlength-strings.cmdline
new file mode 100644
index 000000000000..686abbb1cdb9
--- /dev/null
+++ b/nixpkgs/pkgs/test/make-binary-wrapper/overlength-strings.cmdline
@@ -0,0 +1 @@
+    --prefix PATH : /nix/store/00000000000000000000000000000000-loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong
diff --git a/nixpkgs/pkgs/test/make-binary-wrapper/overlength-strings.env b/nixpkgs/pkgs/test/make-binary-wrapper/overlength-strings.env
new file mode 100644
index 000000000000..83a02f5f8343
--- /dev/null
+++ b/nixpkgs/pkgs/test/make-binary-wrapper/overlength-strings.env
@@ -0,0 +1,3 @@
+PATH=/nix/store/00000000000000000000000000000000-loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong
+CWD=SUBST_CWD
+SUBST_ARGV0
diff --git a/nixpkgs/pkgs/test/make-binary-wrapper/prefix.c b/nixpkgs/pkgs/test/make-binary-wrapper/prefix.c
new file mode 100644
index 000000000000..ea8fbdc64a84
--- /dev/null
+++ b/nixpkgs/pkgs/test/make-binary-wrapper/prefix.c
@@ -0,0 +1,26 @@
+#define _GNU_SOURCE         /* See feature_test_macros(7) */
+#include <unistd.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <stdio.h>
+
+#define assert_success(e) do { if ((e) < 0) { perror(#e); abort(); } } while (0)
+
+void set_env_prefix(char *env, char *sep, char *prefix) {
+    char *existing = getenv(env);
+    if (existing) {
+        char *val;
+        assert_success(asprintf(&val, "%s%s%s", prefix, sep, existing));
+        assert_success(setenv(env, val, 1));
+        free(val);
+    } else {
+        assert_success(setenv(env, prefix, 1));
+    }
+}
+
+int main(int argc, char **argv) {
+    set_env_prefix("PATH", ":", "/usr/bin/");
+    set_env_prefix("PATH", ":", "/usr/local/bin/");
+    argv[0] = "/send/me/flags";
+    return execv("/send/me/flags", argv);
+}
diff --git a/nixpkgs/pkgs/test/make-binary-wrapper/prefix.cmdline b/nixpkgs/pkgs/test/make-binary-wrapper/prefix.cmdline
new file mode 100644
index 000000000000..99cebf9503f4
--- /dev/null
+++ b/nixpkgs/pkgs/test/make-binary-wrapper/prefix.cmdline
@@ -0,0 +1,2 @@
+    --prefix PATH : /usr/bin/ \
+    --prefix PATH : /usr/local/bin/
diff --git a/nixpkgs/pkgs/test/make-binary-wrapper/prefix.env b/nixpkgs/pkgs/test/make-binary-wrapper/prefix.env
new file mode 100644
index 000000000000..033676457c57
--- /dev/null
+++ b/nixpkgs/pkgs/test/make-binary-wrapper/prefix.env
@@ -0,0 +1,3 @@
+PATH=/usr/local/bin/:/usr/bin/
+CWD=SUBST_CWD
+SUBST_ARGV0
diff --git a/nixpkgs/pkgs/test/make-binary-wrapper/suffix.c b/nixpkgs/pkgs/test/make-binary-wrapper/suffix.c
new file mode 100644
index 000000000000..d33f86c070ca
--- /dev/null
+++ b/nixpkgs/pkgs/test/make-binary-wrapper/suffix.c
@@ -0,0 +1,26 @@
+#define _GNU_SOURCE         /* See feature_test_macros(7) */
+#include <unistd.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <stdio.h>
+
+#define assert_success(e) do { if ((e) < 0) { perror(#e); abort(); } } while (0)
+
+void set_env_suffix(char *env, char *sep, char *suffix) {
+    char *existing = getenv(env);
+    if (existing) {
+        char *val;
+        assert_success(asprintf(&val, "%s%s%s", existing, sep, suffix));
+        assert_success(setenv(env, val, 1));
+        free(val);
+    } else {
+        assert_success(setenv(env, suffix, 1));
+    }
+}
+
+int main(int argc, char **argv) {
+    set_env_suffix("PATH", ":", "/usr/bin/");
+    set_env_suffix("PATH", ":", "/usr/local/bin/");
+    argv[0] = "/send/me/flags";
+    return execv("/send/me/flags", argv);
+}
diff --git a/nixpkgs/pkgs/test/make-binary-wrapper/suffix.cmdline b/nixpkgs/pkgs/test/make-binary-wrapper/suffix.cmdline
new file mode 100644
index 000000000000..95d291f3c169
--- /dev/null
+++ b/nixpkgs/pkgs/test/make-binary-wrapper/suffix.cmdline
@@ -0,0 +1,2 @@
+    --suffix PATH : /usr/bin/ \
+    --suffix PATH : /usr/local/bin/
diff --git a/nixpkgs/pkgs/test/make-binary-wrapper/suffix.env b/nixpkgs/pkgs/test/make-binary-wrapper/suffix.env
new file mode 100644
index 000000000000..3ce4cc54de41
--- /dev/null
+++ b/nixpkgs/pkgs/test/make-binary-wrapper/suffix.env
@@ -0,0 +1,3 @@
+PATH=/usr/bin/:/usr/local/bin/
+CWD=SUBST_CWD
+SUBST_ARGV0
diff --git a/nixpkgs/pkgs/test/make-hardcode-gsettings-patch/default.nix b/nixpkgs/pkgs/test/make-hardcode-gsettings-patch/default.nix
new file mode 100644
index 000000000000..e27a6f179208
--- /dev/null
+++ b/nixpkgs/pkgs/test/make-hardcode-gsettings-patch/default.nix
@@ -0,0 +1,77 @@
+{ runCommandLocal
+, lib
+, git
+, clang-tools
+, makeHardcodeGsettingsPatch
+}:
+
+let
+  mkTest =
+    {
+      name,
+      expected,
+      src,
+      patches ? [ ],
+      schemaIdToVariableMapping,
+    }:
+
+    let
+      patch = makeHardcodeGsettingsPatch ({
+        inherit src schemaIdToVariableMapping;
+        inherit patches;
+      });
+    in
+    runCommandLocal
+      "makeHardcodeGsettingsPatch-tests-${name}"
+
+      {
+        nativeBuildInputs = [
+          git
+          clang-tools
+        ];
+      }
+
+      ''
+        cp -r --no-preserve=all "${src}" src
+        cp -r --no-preserve=all "${expected}" src-expected
+
+        pushd src
+        for patch in ${lib.escapeShellArgs (builtins.map (p: "${p}") patches)}; do
+            patch < "$patch"
+        done
+        patch < "${patch}"
+        popd
+
+        find src -name '*.c' -print0 | while read -d $'\0' sourceFile; do
+          sourceFile=''${sourceFile#src/}
+          clang-format -style='{BasedOnStyle: InheritParentConfig, ColumnLimit: 240}' -i "src/$sourceFile" "src-expected/$sourceFile"
+          git diff --no-index "src/$sourceFile" "src-expected/$sourceFile" | cat
+        done
+        touch "$out"
+      '';
+in
+{
+  basic = mkTest {
+    name = "basic";
+    src = ./fixtures/example-project;
+    schemaIdToVariableMapping = {
+      "org.gnome.evolution-data-server.addressbook" = "EDS";
+      "org.gnome.evolution.calendar" = "EVO";
+      "org.gnome.seahorse.nautilus.window" = "SEANAUT";
+    };
+    expected = ./fixtures/example-project-patched;
+  };
+
+  patches = mkTest {
+    name = "patches";
+    src = ./fixtures/example-project-wrapped-settings-constructor;
+    patches = [
+      # Avoid using wrapper function, which the generator cannot handle.
+      ./fixtures/example-project-wrapped-settings-constructor-resolve.patch
+    ];
+    schemaIdToVariableMapping = {
+      "org.gnome.evolution-data-server.addressbook" = "EDS";
+    };
+    expected = ./fixtures/example-project-wrapped-settings-constructor-patched;
+  };
+}
diff --git a/nixpkgs/pkgs/test/make-hardcode-gsettings-patch/fixtures/example-project-patched/main.c b/nixpkgs/pkgs/test/make-hardcode-gsettings-patch/fixtures/example-project-patched/main.c
new file mode 100644
index 000000000000..7822a42b840a
--- /dev/null
+++ b/nixpkgs/pkgs/test/make-hardcode-gsettings-patch/fixtures/example-project-patched/main.c
@@ -0,0 +1,85 @@
+#include <gio/gio.h>
+#include <glib-object.h>
+
+void schema_id_literal() {
+  GSettings *settings;
+  {
+    g_autoptr(GSettingsSchemaSource) schema_source;
+    g_autoptr(GSettingsSchema) schema;
+    schema_source = g_settings_schema_source_new_from_directory("@EDS@", g_settings_schema_source_get_default(), TRUE, NULL);
+    schema = g_settings_schema_source_lookup(schema_source, "org.gnome.evolution-data-server.addressbook", FALSE);
+    settings = g_settings_new_full(schema, NULL, NULL);
+  }
+  g_object_unref(settings);
+}
+
+#define SELF_UID_PATH_ID "org.gnome.evolution-data-server.addressbook"
+int schema_id_from_constant() {
+  GSettings *settings;
+  {
+    g_autoptr(GSettingsSchemaSource) schema_source;
+    g_autoptr(GSettingsSchema) schema;
+    schema_source = g_settings_schema_source_new_from_directory("@EDS@", g_settings_schema_source_get_default(), TRUE, NULL);
+    schema = g_settings_schema_source_lookup(schema_source, SELF_UID_PATH_ID, FALSE);
+    settings = g_settings_new_full(schema, NULL, NULL);
+  }
+  g_object_unref(settings);
+}
+
+void schema_id_autoptr() {
+  g_autoptr(GSettings) settings = NULL;
+  {
+    g_autoptr(GSettingsSchemaSource) schema_source;
+    g_autoptr(GSettingsSchema) schema;
+    schema_source = g_settings_schema_source_new_from_directory("@EVO@", g_settings_schema_source_get_default(), TRUE, NULL);
+    schema = g_settings_schema_source_lookup(schema_source, "org.gnome.evolution.calendar", FALSE);
+    settings = g_settings_new_full(schema, NULL, NULL);
+  }
+}
+
+void schema_id_with_backend() {
+  GSettings *settings;
+  {
+    g_autoptr(GSettingsSchemaSource) schema_source;
+    g_autoptr(GSettingsSchema) schema;
+    schema_source = g_settings_schema_source_new_from_directory("@EDS@", g_settings_schema_source_get_default(), TRUE, NULL);
+    schema = g_settings_schema_source_lookup(schema_source, "org.gnome.evolution-data-server.addressbook", FALSE);
+    settings = g_settings_new_full(schema, g_settings_backend_get_default(), NULL);
+  }
+  g_object_unref(settings);
+}
+
+void schema_id_with_backend_and_path() {
+  GSettings *settings;
+  {
+    g_autoptr(GSettingsSchemaSource) schema_source;
+    g_autoptr(GSettingsSchema) schema;
+    schema_source = g_settings_schema_source_new_from_directory("@SEANAUT@", g_settings_schema_source_get_default(), TRUE, NULL);
+    schema = g_settings_schema_source_lookup(schema_source, "org.gnome.seahorse.nautilus.window", FALSE);
+    settings = g_settings_new_full(schema, g_settings_backend_get_default(), "/org/gnome/seahorse/nautilus/windows/123/");
+  }
+  g_object_unref(settings);
+}
+
+void schema_id_with_path() {
+  GSettings *settings;
+  {
+    g_autoptr(GSettingsSchemaSource) schema_source;
+    g_autoptr(GSettingsSchema) schema;
+    schema_source = g_settings_schema_source_new_from_directory("@SEANAUT@", g_settings_schema_source_get_default(), TRUE, NULL);
+    schema = g_settings_schema_source_lookup(schema_source, "org.gnome.seahorse.nautilus.window", FALSE);
+    settings = g_settings_new_full(schema, NULL, "/org/gnome/seahorse/nautilus/windows/123/");
+  }
+  g_object_unref(settings);
+}
+
+int main() {
+  schema_id_literal();
+  schema_id_from_constant();
+  schema_id_autoptr();
+  schema_id_with_backend();
+  schema_id_with_backend_and_path();
+  schema_id_with_path();
+
+  return 0;
+}
diff --git a/nixpkgs/pkgs/test/make-hardcode-gsettings-patch/fixtures/example-project-wrapped-settings-constructor-patched/main.c b/nixpkgs/pkgs/test/make-hardcode-gsettings-patch/fixtures/example-project-wrapped-settings-constructor-patched/main.c
new file mode 100644
index 000000000000..b0894614de7c
--- /dev/null
+++ b/nixpkgs/pkgs/test/make-hardcode-gsettings-patch/fixtures/example-project-wrapped-settings-constructor-patched/main.c
@@ -0,0 +1,15 @@
+#include <gio/gio.h>
+#include <glib-object.h>
+
+int main() {
+  g_autoptr(GSettings) settings;
+  {
+    g_autoptr(GSettingsSchemaSource) schema_source;
+    g_autoptr(GSettingsSchema) schema;
+    schema_source = g_settings_schema_source_new_from_directory("@EDS@", g_settings_schema_source_get_default(), TRUE, NULL);
+    schema = g_settings_schema_source_lookup(schema_source, "org.gnome.evolution-data-server.addressbook", FALSE);
+    settings = g_settings_new_full(schema, NULL, NULL);
+  }
+
+  return 0;
+}
diff --git a/nixpkgs/pkgs/test/make-hardcode-gsettings-patch/fixtures/example-project-wrapped-settings-constructor-resolve.patch b/nixpkgs/pkgs/test/make-hardcode-gsettings-patch/fixtures/example-project-wrapped-settings-constructor-resolve.patch
new file mode 100644
index 000000000000..70b80a9e8627
--- /dev/null
+++ b/nixpkgs/pkgs/test/make-hardcode-gsettings-patch/fixtures/example-project-wrapped-settings-constructor-resolve.patch
@@ -0,0 +1,17 @@
+--- a/main.c
++++ b/main.c
+@@ -1,13 +1,9 @@
+ #include <gio/gio.h>
+ #include <glib-object.h>
+ 
+-void my_settings_new(const char *schema_id) {
+-  return g_settings_new(schema_id);
+-}
+-
+ int main() {
+   g_autoptr (GSettings) settings;
+-  settings = my_settings_new("org.gnome.evolution-data-server.addressbook");
++  settings = g_settings_new("org.gnome.evolution-data-server.addressbook");
+ 
+   return 0;
+ }
diff --git a/nixpkgs/pkgs/test/make-hardcode-gsettings-patch/fixtures/example-project-wrapped-settings-constructor/main.c b/nixpkgs/pkgs/test/make-hardcode-gsettings-patch/fixtures/example-project-wrapped-settings-constructor/main.c
new file mode 100644
index 000000000000..0821097f350b
--- /dev/null
+++ b/nixpkgs/pkgs/test/make-hardcode-gsettings-patch/fixtures/example-project-wrapped-settings-constructor/main.c
@@ -0,0 +1,13 @@
+#include <gio/gio.h>
+#include <glib-object.h>
+
+void my_settings_new(const char *schema_id) {
+  return g_settings_new(schema_id);
+}
+
+int main() {
+  g_autoptr (GSettings) settings;
+  settings = my_settings_new("org.gnome.evolution-data-server.addressbook");
+
+  return 0;
+}
diff --git a/nixpkgs/pkgs/test/make-hardcode-gsettings-patch/fixtures/example-project/main.c b/nixpkgs/pkgs/test/make-hardcode-gsettings-patch/fixtures/example-project/main.c
new file mode 100644
index 000000000000..afcb3686ec84
--- /dev/null
+++ b/nixpkgs/pkgs/test/make-hardcode-gsettings-patch/fixtures/example-project/main.c
@@ -0,0 +1,49 @@
+#include <gio/gio.h>
+#include <glib-object.h>
+
+void schema_id_literal() {
+  GSettings *settings;
+  settings = g_settings_new("org.gnome.evolution-data-server.addressbook");
+  g_object_unref(settings);
+}
+
+#define SELF_UID_PATH_ID "org.gnome.evolution-data-server.addressbook"
+int schema_id_from_constant() {
+  GSettings *settings;
+  settings = g_settings_new(SELF_UID_PATH_ID);
+  g_object_unref(settings);
+}
+
+void schema_id_autoptr() {
+  g_autoptr(GSettings) settings = NULL;
+  settings = g_settings_new("org.gnome.evolution.calendar");
+}
+
+void schema_id_with_backend() {
+  GSettings *settings;
+  settings = g_settings_new_with_backend("org.gnome.evolution-data-server.addressbook", g_settings_backend_get_default());
+  g_object_unref(settings);
+}
+
+void schema_id_with_backend_and_path() {
+  GSettings *settings;
+  settings = g_settings_new_with_backend_and_path("org.gnome.seahorse.nautilus.window", g_settings_backend_get_default(), "/org/gnome/seahorse/nautilus/windows/123/");
+  g_object_unref(settings);
+}
+
+void schema_id_with_path() {
+  GSettings *settings;
+  settings = g_settings_new_with_path("org.gnome.seahorse.nautilus.window", "/org/gnome/seahorse/nautilus/windows/123/");
+  g_object_unref(settings);
+}
+
+int main() {
+  schema_id_literal();
+  schema_id_from_constant();
+  schema_id_autoptr();
+  schema_id_with_backend();
+  schema_id_with_backend_and_path();
+  schema_id_with_path();
+
+  return 0;
+}
diff --git a/nixpkgs/pkgs/test/make-wrapper/default.nix b/nixpkgs/pkgs/test/make-wrapper/default.nix
new file mode 100644
index 000000000000..5cc7cee5a864
--- /dev/null
+++ b/nixpkgs/pkgs/test/make-wrapper/default.nix
@@ -0,0 +1,139 @@
+{ lib
+, writeText
+, writeCBin
+, writeShellScript
+, makeWrapper
+, runCommand
+, which
+, ...
+}:
+
+let
+  # Testfiles
+  foofile = writeText "foofile" "foo";
+  barfile = writeText "barfile" "bar";
+
+  # Wrapped binaries
+  wrappedArgv0 = writeCBin "wrapped-argv0" ''
+    #include <stdio.h>
+    #include <stdlib.h>
+
+    void main(int argc, char** argv) {
+      printf("argv0=%s", argv[0]);
+      exit(0);
+    }
+  '';
+  wrappedBinaryVar = writeShellScript "wrapped-var" ''
+    echo "VAR=$VAR"
+  '';
+  wrappedBinaryArgs = writeShellScript "wrapped-args" ''
+    echo "$@"
+  '';
+
+  mkWrapperBinary = { name, args, wrapped ? wrappedBinaryVar }: runCommand name
+    {
+      nativeBuildInputs = [ makeWrapper ];
+    } ''
+    mkdir -p $out/bin
+    makeWrapper "${wrapped}" "$out/bin/${name}" ${lib.escapeShellArgs args}
+  '';
+
+  mkTest = cmd: toExpect: ''
+    output="$(${cmd})"
+    if [[ "$output" != '${toExpect}' ]]; then
+      echo "test failed: the output of ${cmd} was '$output', expected '${toExpect}'"
+      echo "the wrapper contents:"
+      for i in ${cmd}; do
+        if [[ $i =~ ^test- ]]; then
+          cat $(which $i)
+        fi
+      done
+      exit 1
+    fi
+  '';
+in
+runCommand "make-wrapper-test"
+{
+  nativeBuildInputs = [
+    which
+    (mkWrapperBinary { name = "test-argv0"; args = [ "--argv0" "foo" ]; wrapped = "${wrappedArgv0}/bin/wrapped-argv0"; })
+    (mkWrapperBinary { name = "test-set"; args = [ "--set" "VAR" "abc" ]; })
+    (mkWrapperBinary { name = "test-set-default"; args = [ "--set-default" "VAR" "abc" ]; })
+    (mkWrapperBinary { name = "test-unset"; args = [ "--unset" "VAR" ]; })
+    (mkWrapperBinary { name = "test-run"; args = [ "--run" "echo bar" ]; })
+    (mkWrapperBinary { name = "test-run-and-set"; args = [ "--run" "export VAR=foo" "--set" "VAR" "bar" ]; })
+    (mkWrapperBinary { name = "test-args"; args = [ "--add-flags" "abc" "--append-flags" "xyz" ]; wrapped = wrappedBinaryArgs; })
+    (mkWrapperBinary { name = "test-prefix"; args = [ "--prefix" "VAR" ":" "abc" ]; })
+    (mkWrapperBinary { name = "test-prefix-noglob"; args = [ "--prefix" "VAR" ":" "./*" ]; })
+    (mkWrapperBinary { name = "test-suffix"; args = [ "--suffix" "VAR" ":" "abc" ]; })
+    (mkWrapperBinary { name = "test-prefix-and-suffix"; args = [ "--prefix" "VAR" ":" "foo" "--suffix" "VAR" ":" "bar" ]; })
+    (mkWrapperBinary { name = "test-prefix-multi"; args = [ "--prefix" "VAR" ":" "abc:foo:foo" ]; })
+    (mkWrapperBinary { name = "test-suffix-each"; args = [ "--suffix-each" "VAR" ":" "foo bar:def" ]; })
+    (mkWrapperBinary { name = "test-prefix-each"; args = [ "--prefix-each" "VAR" ":" "foo bar:def" ]; })
+    (mkWrapperBinary { name = "test-suffix-contents"; args = [ "--suffix-contents" "VAR" ":" "${foofile} ${barfile}" ]; })
+    (mkWrapperBinary { name = "test-prefix-contents"; args = [ "--prefix-contents" "VAR" ":" "${foofile} ${barfile}" ]; })
+  ];
+}
+  (
+    # --argv0 works
+    mkTest "test-argv0" "argv0=foo"
+
+    # --set works
+    + mkTest "test-set" "VAR=abc"
+    # --set overwrites the variable
+    + mkTest "VAR=foo test-set" "VAR=abc"
+    # --set-default works
+    + mkTest "test-set-default" "VAR=abc"
+    # --set-default doesn"t overwrite the variable
+    + mkTest "VAR=foo test-set-default" "VAR=foo"
+    # --unset works
+    + mkTest "VAR=foo test-unset" "VAR="
+
+    # --add-flags and --append-flags work
+    + mkTest "test-args" "abc xyz"
+    # given flags are kept
+    + mkTest "test-args foo" "abc foo xyz"
+
+    # --run works
+    + mkTest "test-run" "bar\nVAR="
+    # --run & --set works
+    + mkTest "test-run-and-set" "VAR=bar"
+
+    # --prefix works
+    + mkTest "VAR=foo test-prefix" "VAR=abc:foo"
+    # sets variable if not set yet
+    + mkTest "test-prefix" "VAR=abc"
+    # prepends value only once
+    + mkTest "VAR=abc test-prefix" "VAR=abc"
+    # Moves value to the front if it already existed
+    + mkTest "VAR=foo:abc test-prefix" "VAR=abc:foo"
+    + mkTest "VAR=abc:foo:bar test-prefix-multi" "VAR=abc:foo:bar"
+    # Doesn't overwrite parts of the string
+    + mkTest "VAR=test:abcde:test test-prefix" "VAR=abc:test:abcde:test"
+    # Only append the value once when given multiple times in a parameter
+    # to makeWrapper
+    + mkTest "test-prefix" "VAR=abc"
+    # --prefix doesn't expand globs
+    + mkTest "VAR=f?oo test-prefix-noglob" "VAR=./*:f?oo"
+
+
+    # --suffix works
+    + mkTest "VAR=foo test-suffix" "VAR=foo:abc"
+    # sets variable if not set yet
+    + mkTest "test-suffix" "VAR=abc"
+    # adds the same value only once
+    + mkTest "VAR=abc test-suffix" "VAR=abc"
+    + mkTest "VAR=abc:foo test-suffix" "VAR=abc:foo"
+    # --prefix in combination with --suffix
+    + mkTest "VAR=abc test-prefix-and-suffix" "VAR=foo:abc:bar"
+
+    # --suffix-each works
+    + mkTest "VAR=abc test-suffix-each" "VAR=abc:foo:bar:def"
+    # --prefix-each works
+    + mkTest "VAR=abc test-prefix-each" "VAR=bar:def:foo:abc"
+    # --suffix-contents works
+    + mkTest "VAR=abc test-suffix-contents" "VAR=abc:foo:bar"
+    # --prefix-contents works
+    + mkTest "VAR=abc test-prefix-contents" "VAR=bar:foo:abc"
+    + "touch $out"
+  )
diff --git a/nixpkgs/pkgs/test/nixos-functions/default.nix b/nixpkgs/pkgs/test/nixos-functions/default.nix
new file mode 100644
index 000000000000..bdd5e3c6d8b4
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixos-functions/default.nix
@@ -0,0 +1,31 @@
+/*
+
+This file is a test that makes sure that the `pkgs.nixos` and
+`pkgs.testers.nixosTest` functions work. It's far from a perfect test suite,
+but better than not checking them at all on hydra.
+
+To run this test:
+
+    nixpkgs$ nix-build -A tests.nixos-functions
+
+ */
+{ pkgs, lib, stdenv, ... }:
+
+let
+  dummyVersioning = {
+    revision = "test";
+    versionSuffix = "test";
+    label = "test";
+  };
+in lib.optionalAttrs stdenv.hostPlatform.isLinux (
+  pkgs.recurseIntoAttrs {
+
+    nixos-test = (pkgs.nixos {
+      system.nixos = dummyVersioning;
+      boot.loader.grub.enable = false;
+      fileSystems."/".device = "/dev/null";
+      system.stateVersion = lib.trivial.release;
+    }).toplevel;
+
+  }
+)
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/.envrc b/nixpkgs/pkgs/test/nixpkgs-check-by-name/.envrc
new file mode 100644
index 000000000000..1d953f4bd735
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/.envrc
@@ -0,0 +1 @@
+use nix
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/.gitignore b/nixpkgs/pkgs/test/nixpkgs-check-by-name/.gitignore
new file mode 100644
index 000000000000..75e92a908987
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/.gitignore
@@ -0,0 +1,2 @@
+target
+.direnv
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/Cargo.lock b/nixpkgs/pkgs/test/nixpkgs-check-by-name/Cargo.lock
new file mode 100644
index 000000000000..19435c2ab76e
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/Cargo.lock
@@ -0,0 +1,643 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "aho-corasick"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6748e8def348ed4d14996fa801f4122cd763fff530258cdc03f64b25f89d3a5a"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "anstream"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1f58811cfac344940f1a400b6e6231ce35171f614f26439e80f8c1465c5cc0c"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "15c4c2c83f81532e5845a733998b6971faca23490340a418e9b72a3ec9de12ea"
+
+[[package]]
+name = "anstyle-parse"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
+dependencies = [
+ "windows-sys",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd"
+dependencies = [
+ "anstyle",
+ "windows-sys",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.75"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bitflags"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
+
+[[package]]
+name = "cc"
+version = "1.0.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "clap"
+version = "4.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d5f1946157a96594eb2d2c10eb7ad9a2b27518cb3000209dec700c35df9197d"
+dependencies = [
+ "clap_builder",
+ "clap_derive",
+ "once_cell",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78116e32a042dd73c2901f0dc30790d20ff3447f3e3472fad359e8c3d282bcd6"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "clap_lex",
+ "strsim",
+]
+
+[[package]]
+name = "clap_derive"
+version = "4.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c9fd1a5729c4548118d7d70ff234a44868d00489a4b6597b0b020918a0e91a1a"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961"
+
+[[package]]
+name = "colorchoice"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
+
+[[package]]
+name = "colored"
+version = "2.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2674ec482fbc38012cf31e6c42ba0177b431a0cb6f15fe40efa5aab1bda516f6"
+dependencies = [
+ "is-terminal",
+ "lazy_static",
+ "windows-sys",
+]
+
+[[package]]
+name = "countme"
+version = "3.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7704b5fdd17b18ae31c4c1da5a2e0305a2bf17b5249300a9ee9ed7b72114c636"
+
+[[package]]
+name = "either"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
+
+[[package]]
+name = "errno"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f"
+dependencies = [
+ "errno-dragonfly",
+ "libc",
+ "windows-sys",
+]
+
+[[package]]
+name = "errno-dragonfly"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
+dependencies = [
+ "cc",
+ "libc",
+]
+
+[[package]]
+name = "fastrand"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764"
+
+[[package]]
+name = "hashbrown"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+
+[[package]]
+name = "heck"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
+
+[[package]]
+name = "hermit-abi"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
+
+[[package]]
+name = "indoc"
+version = "2.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e186cfbae8084e513daff4240b4797e342f988cecda4fb6c939150f96315fd8"
+
+[[package]]
+name = "is-terminal"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
+dependencies = [
+ "hermit-abi",
+ "rustix",
+ "windows-sys",
+]
+
+[[package]]
+name = "itertools"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "libc"
+version = "0.2.147"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503"
+
+[[package]]
+name = "lock_api"
+version = "0.4.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
+[[package]]
+name = "memchr"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+
+[[package]]
+name = "memoffset"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "nixpkgs-check-by-name"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "clap",
+ "colored",
+ "indoc",
+ "itertools",
+ "lazy_static",
+ "regex",
+ "relative-path",
+ "rnix",
+ "rowan",
+ "serde",
+ "serde_json",
+ "temp-env",
+ "tempfile",
+ "textwrap",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
+
+[[package]]
+name = "parking_lot"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "windows-targets",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.66"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
+dependencies = [
+ "bitflags 1.3.2",
+]
+
+[[package]]
+name = "regex"
+version = "1.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81bc1d4caf89fac26a70747fe603c130093b53c773888797a6329091246d651a"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fed1ceff11a1dddaee50c9dc8e4938bd106e9d89ae372f192311e7da498e3b69"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2"
+
+[[package]]
+name = "relative-path"
+version = "1.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e898588f33fdd5b9420719948f9f2a32c922a246964576f71ba7f24f80610fbc"
+
+[[package]]
+name = "rnix"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb35cedbeb70e0ccabef2a31bcff0aebd114f19566086300b8f42c725fc2cb5f"
+dependencies = [
+ "rowan",
+]
+
+[[package]]
+name = "rowan"
+version = "0.15.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "64449cfef9483a475ed56ae30e2da5ee96448789fb2aa240a04beb6a055078bf"
+dependencies = [
+ "countme",
+ "hashbrown",
+ "memoffset",
+ "rustc-hash",
+ "text-size",
+]
+
+[[package]]
+name = "rustc-hash"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
+
+[[package]]
+name = "rustix"
+version = "0.38.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f"
+dependencies = [
+ "bitflags 2.4.0",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
+
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
+[[package]]
+name = "serde"
+version = "1.0.186"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f5db24220c009de9bd45e69fb2938f4b6d2df856aa9304ce377b3180f83b7c1"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.186"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ad697f7e0b65af4983a4ce8f56ed5b357e8d3c36651bf6a7e13639c17b8e670"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.105"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a"
+
+[[package]]
+name = "smawk"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c"
+
+[[package]]
+name = "strsim"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
+
+[[package]]
+name = "syn"
+version = "2.0.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "temp-env"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e010429b1f3ea1311190c658c7570100f03c1dab05c16cfab774181c648d656a"
+dependencies = [
+ "parking_lot",
+]
+
+[[package]]
+name = "tempfile"
+version = "3.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef"
+dependencies = [
+ "cfg-if",
+ "fastrand",
+ "redox_syscall",
+ "rustix",
+ "windows-sys",
+]
+
+[[package]]
+name = "text-size"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f18aa187839b2bdb1ad2fa35ead8c4c2976b64e4363c386d45ac0f7ee85c9233"
+
+[[package]]
+name = "textwrap"
+version = "0.16.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9"
+dependencies = [
+ "smawk",
+ "unicode-linebreak",
+ "unicode-width",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
+
+[[package]]
+name = "unicode-linebreak"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f"
+
+[[package]]
+name = "unicode-width"
+version = "0.1.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
+
+[[package]]
+name = "utf8parse"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/Cargo.toml b/nixpkgs/pkgs/test/nixpkgs-check-by-name/Cargo.toml
new file mode 100644
index 000000000000..50cdabb7e2dd
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/Cargo.toml
@@ -0,0 +1,23 @@
+[package]
+name = "nixpkgs-check-by-name"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+rnix = "0.11.0"
+regex = "1.9.3"
+clap = { version = "4.3.23", features = ["derive"] }
+serde_json = "1.0.105"
+tempfile = "3.8.0"
+serde = { version = "1.0.185", features = ["derive"] }
+anyhow = "1.0"
+lazy_static = "1.4.0"
+colored = "2.0.4"
+itertools = "0.11.0"
+rowan = "0.15.11"
+indoc = "2.0.4"
+relative-path = "1.9.2"
+textwrap = "0.16.1"
+
+[dev-dependencies]
+temp-env = "0.3.5"
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/README.md b/nixpkgs/pkgs/test/nixpkgs-check-by-name/README.md
new file mode 100644
index 000000000000..1aa256978416
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/README.md
@@ -0,0 +1,102 @@
+# Nixpkgs pkgs/by-name checker
+
+This directory implements a program to check the [validity](#validity-checks) of the `pkgs/by-name` Nixpkgs directory.
+This is part of the implementation of [RFC 140](https://github.com/NixOS/rfcs/pull/140).
+
+A [pinned version](./scripts/pinned-tool.json) of this tool is used by [this GitHub Actions workflow](../../../.github/workflows/check-by-name.yml).
+See [./scripts](./scripts/README.md#update-pinned-toolsh) for how to update the pinned version.
+
+The source of the tool being right inside Nixpkgs allows any Nixpkgs committer to make updates to it.
+
+## Interface
+
+The interface of the tool is shown with `--help`:
+```
+cargo run -- --help
+```
+
+The interface may be changed over time only if the CI workflow making use of it is adjusted to deal with the change appropriately.
+
+## Validity checks
+
+These checks are performed by this tool:
+
+### File structure checks
+- `pkgs/by-name` must only contain subdirectories of the form `${shard}/${name}`, called _package directories_.
+- The `name`'s of package directories must be unique when lowercased.
+- `name` is a string only consisting of the ASCII characters `a-z`, `A-Z`, `0-9`, `-` or `_`.
+- `shard` is the lowercased first two letters of `name`, expressed in Nix: `shard = toLower (substring 0 2 name)`.
+- Each package directory must contain a `package.nix` file and may contain arbitrary other files.
+
+### Nix parser checks
+- Each package directory must not refer to files outside itself using symlinks or Nix path expressions.
+
+### Nix evaluation checks
+
+Evaluate Nixpkgs with `system` set to `x86_64-linux` and check that:
+- For each package directory, the `pkgs.${name}` attribute must be defined as `callPackage pkgs/by-name/${shard}/${name}/package.nix args` for some `args`.
+- For each package directory, `pkgs.lib.isDerivation pkgs.${name}` must be `true`.
+
+### Ratchet checks
+
+Furthermore, this tool implements certain [ratchet](https://qntm.org/ratchet) checks.
+This allows gradually phasing out deprecated patterns without breaking the base branch or having to migrate it all at once.
+It works by not allowing new instances of the pattern to be introduced, but allowing already existing instances.
+The existing instances are coming from `<BASE_NIXPKGS>`, which is then checked against `<NIXPKGS>` for new instances.
+Ratchets should be removed eventually once the pattern is not used anymore.
+
+The current ratchets are:
+
+- New manual definitions of `pkgs.${name}` (e.g. in `pkgs/top-level/all-packages.nix`) with `args = { }`
+  (see [nix evaluation checks](#nix-evaluation-checks)) must not be introduced.
+- New top-level packages defined using `pkgs.callPackage` must be defined with a package directory.
+  - Once a top-level package uses `pkgs/by-name`, it also can't be moved back out of it.
+
+## Development
+
+Enter the development environment in this directory either automatically with `direnv` or with
+```
+nix-shell
+```
+
+Then use `cargo`:
+```
+cargo build
+cargo test
+cargo fmt
+cargo clippy
+```
+
+## Tests
+
+Tests are declared in [`./tests`](./tests) as subdirectories imitating Nixpkgs with these files:
+- `default.nix`:
+  Always contains
+  ```nix
+  import <test-nixpkgs> { root = ./.; }
+  ```
+  which makes
+  ```
+  nix-instantiate <subdir> --eval -A <attr> --arg overlays <overlays>
+  ```
+  work very similarly to the real Nixpkgs, just enough for the program to be able to test it.
+- `pkgs/by-name`:
+  The `pkgs/by-name` directory to check.
+
+- `all-packages.nix` (optional):
+  Contains an overlay of the form
+  ```nix
+  self: super: {
+    # ...
+  }
+  ```
+  allowing the simulation of package overrides to the real [`pkgs/top-level/all-packages.nix`](../../top-level/all-packages.nix`).
+  The default is an empty overlay.
+
+- `base` (optional):
+  Contains another subdirectory imitating Nixpkgs with potentially any of the above structures.
+  This is used for [ratchet checks](#ratchet-checks).
+
+- `expected` (optional):
+  A file containing the expected standard output.
+  The default is expecting an empty standard output.
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/default.nix
new file mode 100644
index 000000000000..8836da1f403f
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/default.nix
@@ -0,0 +1,82 @@
+{
+  lib,
+  rustPlatform,
+  nix,
+  rustfmt,
+  clippy,
+  mkShell,
+  makeWrapper,
+  runCommand,
+}:
+let
+  runtimeExprPath = ./src/eval.nix;
+  nixpkgsLibPath = ../../../lib;
+  testNixpkgsPath = ./tests/mock-nixpkgs.nix;
+
+  # Needed to make Nix evaluation work inside nix builds
+  initNix = ''
+    export TEST_ROOT=$(pwd)/test-tmp
+    export NIX_CONF_DIR=$TEST_ROOT/etc
+    export NIX_LOCALSTATE_DIR=$TEST_ROOT/var
+    export NIX_LOG_DIR=$TEST_ROOT/var/log/nix
+    export NIX_STATE_DIR=$TEST_ROOT/var/nix
+    export NIX_STORE_DIR=$TEST_ROOT/store
+
+    # Ensure that even if tests run in parallel, we don't get an error
+    # We'd run into https://github.com/NixOS/nix/issues/2706 unless the store is initialised first
+    nix-store --init
+  '';
+
+  fs = lib.fileset;
+
+  package =
+    rustPlatform.buildRustPackage {
+      name = "nixpkgs-check-by-name";
+      src = fs.toSource {
+        root = ./.;
+        fileset = fs.unions [
+          ./Cargo.lock
+          ./Cargo.toml
+          ./src
+          ./tests
+        ];
+      };
+      cargoLock.lockFile = ./Cargo.lock;
+      nativeBuildInputs = [
+        nix
+        rustfmt
+        clippy
+        makeWrapper
+      ];
+      env.NIX_CHECK_BY_NAME_EXPR_PATH = "${runtimeExprPath}";
+      env.NIX_PATH = "test-nixpkgs=${testNixpkgsPath}:test-nixpkgs/lib=${nixpkgsLibPath}";
+      preCheck = initNix;
+      postCheck = ''
+        cargo fmt --check
+        cargo clippy -- -D warnings
+      '';
+      postInstall = ''
+        wrapProgram $out/bin/nixpkgs-check-by-name \
+          --set NIX_CHECK_BY_NAME_EXPR_PATH "$NIX_CHECK_BY_NAME_EXPR_PATH"
+      '';
+      passthru.shell = mkShell {
+        env.NIX_CHECK_BY_NAME_EXPR_PATH = toString runtimeExprPath;
+        env.NIX_PATH = "test-nixpkgs=${toString testNixpkgsPath}:test-nixpkgs/lib=${toString nixpkgsLibPath}";
+        inputsFrom = [ package ];
+      };
+
+      # Tests the tool on the current Nixpkgs tree, this is a good sanity check
+      passthru.tests.nixpkgs = runCommand "test-nixpkgs-check-by-name" {
+        nativeBuildInputs = [
+          package
+          nix
+        ];
+        nixpkgsPath = lib.cleanSource ../../..;
+      } ''
+        ${initNix}
+        nixpkgs-check-by-name --base "$nixpkgsPath" "$nixpkgsPath"
+        touch $out
+      '';
+    };
+in
+package
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/scripts/README.md b/nixpkgs/pkgs/test/nixpkgs-check-by-name/scripts/README.md
new file mode 100644
index 000000000000..ccd4108ea288
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/scripts/README.md
@@ -0,0 +1,38 @@
+# CI-related Scripts
+
+This directory contains scripts and files used and related to the CI running the `pkgs/by-name` checks in Nixpkgs.
+See also the [CI GitHub Action](../../../../.github/workflows/check-by-name.yml).
+
+## `./run-local.sh BASE_BRANCH [REPOSITORY]`
+
+Runs the `pkgs/by-name` check on the HEAD commit, closely matching what CI does.
+
+Note that this can't do exactly the same as CI,
+because CI needs to rely on GitHub's server-side Git history to compute the mergeability of PRs before the check can be started.
+In turn when running locally, we don't want to have to push commits to test them,
+and we can also rely on the local Git history to do the mergeability check.
+
+Arguments:
+- `BASE_BRANCH`: The base branch to use, e.g. master or release-23.11
+- `REPOSITORY`: The repository to fetch the base branch from, defaults to https://github.com/NixOS/nixpkgs.git
+
+## `./update-pinned-tool.sh`
+
+Updates the pinned CI tool in [`./pinned-tool.json`](./pinned-tool.json) to the
+[latest version from the `nixos-unstable` channel](https://hydra.nixos.org/job/nixos/trunk-combined/nixpkgs.tests.nixpkgs-check-by-name.x86_64-linux).
+
+This script needs to be called manually when the CI tooling needs to be updated.
+
+The `pinned-tool.json` file gets populated with both:
+- The `/nix/store` path for `x86_64-linux`, such that CI doesn't have to evaluate Nixpkgs and can directly fetch it from the cache instead.
+- The Nixpkgs revision, such that the `./run-local.sh` script can be used to run the checks locally on any system.
+
+To ensure that the tool is always pre-built for `x86_64-linux` in the `nixos-unstable` channel,
+it's included in the `tested` jobset description in [`nixos/release-combined.nix`](../../../nixos/release-combined.nix).
+
+Why not just build the tooling right from the PRs Nixpkgs version?
+- Because it allows CI to check all PRs, even if they would break the CI tooling.
+- Because it makes the CI check very fast, since no Nix builds need to be done, even for mass rebuilds.
+- Because it improves security, since we don't have to build potentially untrusted code from PRs.
+  The tool only needs a very minimal Nix evaluation at runtime, which can work with [readonly-mode](https://nixos.org/manual/nix/stable/command-ref/opt-common.html#opt-readonly-mode) and [restrict-eval](https://nixos.org/manual/nix/stable/command-ref/conf-file.html#conf-restrict-eval).
+
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/scripts/pinned-tool.json b/nixpkgs/pkgs/test/nixpkgs-check-by-name/scripts/pinned-tool.json
new file mode 100644
index 000000000000..350e71bffc79
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/scripts/pinned-tool.json
@@ -0,0 +1,4 @@
+{
+  "rev": "b8697e57f10292a6165a20f03d2f42920dfaf973",
+  "ci-path": "/nix/store/w6w7khwfq6qzm4bsyijhg7m2kqv9f9jl-nixpkgs-check-by-name"
+}
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/scripts/run-local.sh b/nixpkgs/pkgs/test/nixpkgs-check-by-name/scripts/run-local.sh
new file mode 100755
index 000000000000..46cbd5e858e2
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/scripts/run-local.sh
@@ -0,0 +1,82 @@
+#!/usr/bin/env nix-shell
+#!nix-shell -i bash -p jq
+
+set -o pipefail -o errexit -o nounset
+
+trace() { echo >&2 "$@"; }
+
+tmp=$(mktemp -d)
+cleanup() {
+    # Don't exit early if anything fails to cleanup
+    set +o errexit
+
+    trace -n "Cleaning up.. "
+
+    [[ -e "$tmp/base" ]] && git worktree remove --force "$tmp/base"
+    [[ -e "$tmp/merged" ]] && git worktree remove --force "$tmp/merged"
+    [[ -e "$tmp/tool-nixpkgs" ]] && git worktree remove --force "$tmp/tool-nixpkgs"
+
+    rm -rf "$tmp"
+
+    trace "Done"
+}
+trap cleanup exit
+
+
+repo=https://github.com/NixOS/nixpkgs.git
+
+if (( $# != 0 )); then
+    baseBranch=$1
+    shift
+else
+    trace "Usage: $0 BASE_BRANCH [REPOSITORY]"
+    trace "BASE_BRANCH: The base branch to use, e.g. master or release-23.11"
+    trace "REPOSITORY: The repository to fetch the base branch from, defaults to $repo"
+    exit 1
+fi
+
+if (( $# != 0 )); then
+    repo=$1
+    shift
+fi
+
+if [[ -n "$(git status --porcelain)" ]]; then
+    trace -e "\e[33mWarning: Dirty tree, uncommitted changes won't be taken into account\e[0m"
+fi
+headSha=$(git rev-parse HEAD)
+trace -e "Using HEAD commit \e[34m$headSha\e[0m"
+
+trace -n "Creating Git worktree for the HEAD commit in $tmp/merged.. "
+git worktree add --detach -q "$tmp/merged" HEAD
+trace "Done"
+
+trace -n "Fetching base branch $baseBranch to compare against.. "
+git fetch -q "$repo" refs/heads/"$baseBranch"
+baseSha=$(git rev-parse FETCH_HEAD)
+trace -e "\e[34m$baseSha\e[0m"
+
+trace -n "Creating Git worktree for the base branch in $tmp/base.. "
+git worktree add -q "$tmp/base" "$baseSha"
+trace "Done"
+
+trace -n "Merging base branch into the HEAD commit in $tmp/merged.. "
+git -C "$tmp/merged" merge -q --no-edit "$baseSha"
+trace -e "\e[34m$(git -C "$tmp/merged" rev-parse HEAD)\e[0m"
+
+trace -n "Reading pinned nixpkgs-check-by-name revision from pinned-tool.json.. "
+toolSha=$(jq -r .rev "$tmp/merged/pkgs/test/nixpkgs-check-by-name/scripts/pinned-tool.json")
+trace -e "\e[34m$toolSha\e[0m"
+
+trace -n "Creating Git worktree for the nixpkgs-check-by-name revision in $tmp/tool-nixpkgs.. "
+git worktree add -q "$tmp/tool-nixpkgs" "$toolSha"
+trace "Done"
+
+trace "Building/fetching nixpkgs-check-by-name.."
+nix-build -o "$tmp/tool" "$tmp/tool-nixpkgs" \
+    -A tests.nixpkgs-check-by-name \
+    --arg config '{}' \
+    --arg overlays '[]' \
+    -j 0
+
+trace "Running nixpkgs-check-by-name.."
+"$tmp/tool/bin/nixpkgs-check-by-name" --base "$tmp/base" "$tmp/merged"
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/scripts/update-pinned-tool.sh b/nixpkgs/pkgs/test/nixpkgs-check-by-name/scripts/update-pinned-tool.sh
new file mode 100755
index 000000000000..dbc6e91df08a
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/scripts/update-pinned-tool.sh
@@ -0,0 +1,40 @@
+#!/usr/bin/env nix-shell
+#!nix-shell -i bash -p jq
+
+set -o pipefail -o errexit -o nounset
+
+trace() { echo >&2 "$@"; }
+
+SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
+
+# Determined by `runs-on: ubuntu-latest` in .github/workflows/check-by-name.yml
+CI_SYSTEM=x86_64-linux
+
+channel=nixos-unstable
+pin_file=$SCRIPT_DIR/pinned-tool.json
+
+trace -n "Fetching latest version of channel $channel.. "
+# This is probably the easiest way to get Nix to output the path to a downloaded channel!
+nixpkgs=$(nix-instantiate --find-file nixpkgs -I nixpkgs=channel:"$channel")
+trace "$nixpkgs"
+
+# This file only exists in channels
+rev=$(<"$nixpkgs/.git-revision")
+trace -e "Git revision of channel $channel is \e[34m$rev\e[0m"
+
+trace -n "Fetching the prebuilt version of nixpkgs-check-by-name for $CI_SYSTEM.. "
+# This is the architecture used by CI, we want to prefetch the exact path to avoid having to evaluate Nixpkgs
+ci_path=$(nix-build --no-out-link "$nixpkgs" \
+    -A tests.nixpkgs-check-by-name \
+    --arg config '{}' \
+    --argstr system "$CI_SYSTEM" \
+    --arg overlays '[]' \
+    -j 0 \
+    | tee /dev/stderr)
+
+trace "Updating $pin_file"
+jq -n \
+    --arg rev "$rev" \
+    --arg ci-path "$ci_path" \
+    '$ARGS.named' \
+    > "$pin_file"
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/shell.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/shell.nix
new file mode 100644
index 000000000000..33bcf45b8d05
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/shell.nix
@@ -0,0 +1,6 @@
+let
+  pkgs = import ../../.. {
+    config = {};
+    overlays = [];
+  };
+in pkgs.tests.nixpkgs-check-by-name.shell
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/src/eval.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/src/eval.nix
new file mode 100644
index 000000000000..ab1c41e0b145
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/src/eval.nix
@@ -0,0 +1,116 @@
+# Takes a path to nixpkgs and a path to the json-encoded list of `pkgs/by-name` attributes.
+# Returns a value containing information on all Nixpkgs attributes
+# which is decoded on the Rust side.
+# See ./eval.rs for the meaning of the returned values
+{
+  attrsPath,
+  nixpkgsPath,
+}:
+let
+  attrs = builtins.fromJSON (builtins.readFile attrsPath);
+
+  # We need to check whether attributes are defined manually e.g. in
+  # `all-packages.nix`, automatically by the `pkgs/by-name` overlay, or
+  # neither. The only way to do so is to override `callPackage` and
+  # `_internalCallByNamePackageFile` with our own version that adds this
+  # information to the result, and then try to access it.
+  overlay = final: prev: {
+
+    # Adds information to each attribute about whether it's manually defined using `callPackage`
+    callPackage = fn: args:
+      addVariantInfo (prev.callPackage fn args) {
+        # This is a manual definition of the attribute, and it's a callPackage, specifically a semantic callPackage
+        ManualDefinition.is_semantic_call_package = true;
+      };
+
+    # Adds information to each attribute about whether it's automatically
+    # defined by the `pkgs/by-name` overlay. This internal attribute is only
+    # used by that overlay.
+    # This overrides the above `callPackage` information (we don't need that
+    # one, since `pkgs/by-name` always uses `callPackage` underneath.
+    _internalCallByNamePackageFile = file:
+      addVariantInfo (prev._internalCallByNamePackageFile file) {
+        AutoDefinition = null;
+      };
+
+  };
+
+  # We can't just replace attribute values with their info in the overlay,
+  # because attributes can depend on other attributes, so this would break evaluation.
+  addVariantInfo = value: variant:
+    if builtins.isAttrs value then
+      value // {
+        _callPackageVariant = variant;
+      }
+    else
+      # It's very rare that callPackage doesn't return an attribute set, but it can occur.
+      # In such a case we can't really return anything sensible that would include the info,
+      # so just don't return the value directly and treat it as if it wasn't a callPackage.
+      value;
+
+  pkgs = import nixpkgsPath {
+    # Don't let the users home directory influence this result
+    config = { };
+    overlays = [ overlay ];
+    # We check evaluation and callPackage only for x86_64-linux.
+    # Not ideal, but hard to fix
+    system = "x86_64-linux";
+  };
+
+  # See AttributeInfo in ./eval.rs for the meaning of this
+  attrInfo = name: value: {
+    location = builtins.unsafeGetAttrPos name pkgs;
+    attribute_variant =
+      if ! builtins.isAttrs value then
+        { NonAttributeSet = null; }
+      else
+        {
+          AttributeSet = {
+            is_derivation = pkgs.lib.isDerivation value;
+            definition_variant =
+              if ! value ? _callPackageVariant then
+                { ManualDefinition.is_semantic_call_package = false; }
+              else
+                value._callPackageVariant;
+          };
+        };
+  };
+
+  # Information on all attributes that are in pkgs/by-name.
+  byNameAttrs = builtins.listToAttrs (map (name: {
+    inherit name;
+    value.ByName =
+      if ! pkgs ? ${name} then
+        { Missing = null; }
+      else
+        # Evaluation failures are not allowed, so don't try to catch them
+        { Existing = attrInfo name pkgs.${name}; };
+  }) attrs);
+
+  # Information on all attributes that exist but are not in pkgs/by-name.
+  # We need this to enforce pkgs/by-name for new packages
+  nonByNameAttrs = builtins.mapAttrs (name: value:
+    let
+      # Packages outside `pkgs/by-name` often fail evaluation,
+      # so we need to handle that
+      output = attrInfo name value;
+      result = builtins.tryEval (builtins.deepSeq output null);
+    in
+    {
+      NonByName =
+        if result.success then
+          { EvalSuccess = output; }
+        else
+          { EvalFailure = null; };
+    }
+  ) (builtins.removeAttrs pkgs attrs);
+
+  # All attributes
+  attributes = byNameAttrs // nonByNameAttrs;
+in
+# We output them in the form [ [ <name> <value> ] ]` such that the Rust side
+# doesn't need to sort them again to get deterministic behavior (good for testing)
+map (name: [
+  name
+  attributes.${name}
+]) (builtins.attrNames attributes)
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/src/eval.rs b/nixpkgs/pkgs/test/nixpkgs-check-by-name/src/eval.rs
new file mode 100644
index 000000000000..094508f595d8
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/src/eval.rs
@@ -0,0 +1,559 @@
+use crate::nix_file::CallPackageArgumentInfo;
+use crate::nixpkgs_problem::NixpkgsProblem;
+use crate::ratchet;
+use crate::ratchet::RatchetState::Loose;
+use crate::ratchet::RatchetState::Tight;
+use crate::structure;
+use crate::utils;
+use crate::validation::ResultIteratorExt as _;
+use crate::validation::{self, Validation::Success};
+use crate::NixFileStore;
+use relative_path::RelativePathBuf;
+use std::path::Path;
+
+use anyhow::Context;
+use serde::Deserialize;
+use std::path::PathBuf;
+use std::process;
+use tempfile::NamedTempFile;
+
+/// Attribute set of this structure is returned by eval.nix
+#[derive(Deserialize)]
+enum Attribute {
+    /// An attribute that should be defined via pkgs/by-name
+    ByName(ByNameAttribute),
+    /// An attribute not defined via pkgs/by-name
+    NonByName(NonByNameAttribute),
+}
+
+#[derive(Deserialize)]
+enum NonByNameAttribute {
+    /// The attribute doesn't evaluate
+    EvalFailure,
+    EvalSuccess(AttributeInfo),
+}
+
+#[derive(Deserialize)]
+enum ByNameAttribute {
+    /// The attribute doesn't exist at all
+    Missing,
+    Existing(AttributeInfo),
+}
+
+#[derive(Deserialize)]
+struct AttributeInfo {
+    /// The location of the attribute as returned by `builtins.unsafeGetAttrPos`
+    location: Option<Location>,
+    attribute_variant: AttributeVariant,
+}
+
+/// The structure returned by a successful `builtins.unsafeGetAttrPos`
+#[derive(Deserialize, Clone, Debug)]
+struct Location {
+    pub file: PathBuf,
+    pub line: usize,
+    pub column: usize,
+}
+
+impl Location {
+    // Returns the [file] field, but relative to Nixpkgs
+    fn relative_file(&self, nixpkgs_path: &Path) -> anyhow::Result<RelativePathBuf> {
+        let path = self.file.strip_prefix(nixpkgs_path).with_context(|| {
+            format!(
+                "The file ({}) is outside Nixpkgs ({})",
+                self.file.display(),
+                nixpkgs_path.display()
+            )
+        })?;
+        Ok(RelativePathBuf::from_path(path).expect("relative path"))
+    }
+}
+
+#[derive(Deserialize)]
+pub enum AttributeVariant {
+    /// The attribute is not an attribute set, we're limited in the amount of information we can get
+    /// from it (though it's obviously not a derivation)
+    NonAttributeSet,
+    AttributeSet {
+        /// Whether the attribute is a derivation (`lib.isDerivation`)
+        is_derivation: bool,
+        /// The type of callPackage
+        definition_variant: DefinitionVariant,
+    },
+}
+
+#[derive(Deserialize)]
+pub enum DefinitionVariant {
+    /// An automatic definition by the `pkgs/by-name` overlay
+    /// Though it's detected using the internal _internalCallByNamePackageFile attribute,
+    /// which can in theory also be used by other code
+    AutoDefinition,
+    /// A manual definition of the attribute, typically in `all-packages.nix`
+    ManualDefinition {
+        /// Whether the attribute is defined as `pkgs.callPackage ...` or something else.
+        is_semantic_call_package: bool,
+    },
+}
+
+/// Check that the Nixpkgs attribute values corresponding to the packages in pkgs/by-name are
+/// of the form `callPackage <package_file> { ... }`.
+/// See the `eval.nix` file for how this is achieved on the Nix side
+pub fn check_values(
+    nixpkgs_path: &Path,
+    nix_file_store: &mut NixFileStore,
+    package_names: Vec<String>,
+    keep_nix_path: bool,
+) -> validation::Result<ratchet::Nixpkgs> {
+    // Write the list of packages we need to check into a temporary JSON file.
+    // This can then get read by the Nix evaluation.
+    let attrs_file = NamedTempFile::new().with_context(|| "Failed to create a temporary file")?;
+    // We need to canonicalise this path because if it's a symlink (which can be the case on
+    // Darwin), Nix would need to read both the symlink and the target path, therefore need 2
+    // NIX_PATH entries for restrict-eval. But if we resolve the symlinks then only one predictable
+    // entry is needed.
+    let attrs_file_path = attrs_file.path().canonicalize()?;
+
+    serde_json::to_writer(&attrs_file, &package_names).with_context(|| {
+        format!(
+            "Failed to serialise the package names to the temporary path {}",
+            attrs_file_path.display()
+        )
+    })?;
+
+    let expr_path = std::env::var("NIX_CHECK_BY_NAME_EXPR_PATH")
+        .with_context(|| "Could not get environment variable NIX_CHECK_BY_NAME_EXPR_PATH")?;
+    // With restrict-eval, only paths in NIX_PATH can be accessed, so we explicitly specify the
+    // ones needed needed
+    let mut command = process::Command::new("nix-instantiate");
+    command
+        // Inherit stderr so that error messages always get shown
+        .stderr(process::Stdio::inherit())
+        .args([
+            "--eval",
+            "--json",
+            "--strict",
+            "--readonly-mode",
+            "--restrict-eval",
+            "--show-trace",
+        ])
+        // Pass the path to the attrs_file as an argument and add it to the NIX_PATH so it can be
+        // accessed in restrict-eval mode
+        .args(["--arg", "attrsPath"])
+        .arg(&attrs_file_path)
+        .arg("-I")
+        .arg(&attrs_file_path)
+        // Same for the nixpkgs to test
+        .args(["--arg", "nixpkgsPath"])
+        .arg(nixpkgs_path)
+        .arg("-I")
+        .arg(nixpkgs_path);
+
+    // Clear NIX_PATH to be sure it doesn't influence the result
+    // But not when requested to keep it, used so that the tests can pass extra Nix files
+    if !keep_nix_path {
+        command.env_remove("NIX_PATH");
+    }
+
+    command.args(["-I", &expr_path]);
+    command.arg(expr_path);
+
+    let result = command
+        .output()
+        .with_context(|| format!("Failed to run command {command:?}"))?;
+
+    if !result.status.success() {
+        anyhow::bail!("Failed to run command {command:?}");
+    }
+    // Parse the resulting JSON value
+    let attributes: Vec<(String, Attribute)> = serde_json::from_slice(&result.stdout)
+        .with_context(|| {
+            format!(
+                "Failed to deserialise {}",
+                String::from_utf8_lossy(&result.stdout)
+            )
+        })?;
+
+    let check_result = validation::sequence(
+        attributes
+            .into_iter()
+            .map(|(attribute_name, attribute_value)| {
+                let check_result = match attribute_value {
+                    Attribute::NonByName(non_by_name_attribute) => handle_non_by_name_attribute(
+                        nixpkgs_path,
+                        nix_file_store,
+                        &attribute_name,
+                        non_by_name_attribute,
+                    )?,
+                    Attribute::ByName(by_name_attribute) => by_name(
+                        nix_file_store,
+                        nixpkgs_path,
+                        &attribute_name,
+                        by_name_attribute,
+                    )?,
+                };
+                Ok::<_, anyhow::Error>(check_result.map(|value| (attribute_name.clone(), value)))
+            })
+            .collect_vec()?,
+    );
+
+    Ok(check_result.map(|elems| ratchet::Nixpkgs {
+        package_names: elems.iter().map(|(name, _)| name.to_owned()).collect(),
+        package_map: elems.into_iter().collect(),
+    }))
+}
+
+/// Handles the evaluation result for an attribute in `pkgs/by-name`,
+/// turning it into a validation result.
+fn by_name(
+    nix_file_store: &mut NixFileStore,
+    nixpkgs_path: &Path,
+    attribute_name: &str,
+    by_name_attribute: ByNameAttribute,
+) -> validation::Result<ratchet::Package> {
+    use ratchet::RatchetState::*;
+    use ByNameAttribute::*;
+
+    let relative_package_file = structure::relative_file_for_package(attribute_name);
+
+    // At this point we know that `pkgs/by-name/fo/foo/package.nix` has to exists.
+    // This match decides whether the attribute `foo` is defined accordingly
+    // and whether a legacy manual definition could be removed
+    let manual_definition_result = match by_name_attribute {
+        // The attribute is missing
+        Missing => {
+            // This indicates a bug in the `pkgs/by-name` overlay, because it's supposed to
+            // automatically defined attributes in `pkgs/by-name`
+            NixpkgsProblem::UndefinedAttr {
+                relative_package_file: relative_package_file.to_owned(),
+                package_name: attribute_name.to_owned(),
+            }
+            .into()
+        }
+        // The attribute exists
+        Existing(AttributeInfo {
+            // But it's not an attribute set, which limits the amount of information we can get
+            // about this attribute (see ./eval.nix)
+            attribute_variant: AttributeVariant::NonAttributeSet,
+            location: _location,
+        }) => {
+            // The only thing we know is that it's definitely not a derivation, since those are
+            // always attribute sets.
+            //
+            // We can't know whether the attribute is automatically or manually defined for sure,
+            // and while we could check the location, the error seems clear enough as is.
+            NixpkgsProblem::NonDerivation {
+                relative_package_file: relative_package_file.to_owned(),
+                package_name: attribute_name.to_owned(),
+            }
+            .into()
+        }
+        // The attribute exists
+        Existing(AttributeInfo {
+            // And it's an attribute set, which allows us to get more information about it
+            attribute_variant:
+                AttributeVariant::AttributeSet {
+                    is_derivation,
+                    definition_variant,
+                },
+            location,
+        }) => {
+            // Only derivations are allowed in `pkgs/by-name`
+            let is_derivation_result = if is_derivation {
+                Success(())
+            } else {
+                NixpkgsProblem::NonDerivation {
+                    relative_package_file: relative_package_file.to_owned(),
+                    package_name: attribute_name.to_owned(),
+                }
+                .into()
+            };
+
+            // If the definition looks correct
+            let variant_result = match definition_variant {
+                // An automatic `callPackage` by the `pkgs/by-name` overlay.
+                // Though this gets detected by checking whether the internal
+                // `_internalCallByNamePackageFile` was used
+                DefinitionVariant::AutoDefinition => {
+                    if let Some(_location) = location {
+                        // Such an automatic definition should definitely not have a location
+                        // Having one indicates that somebody is using `_internalCallByNamePackageFile`,
+                        NixpkgsProblem::InternalCallPackageUsed {
+                            attr_name: attribute_name.to_owned(),
+                        }
+                        .into()
+                    } else {
+                        Success(Tight)
+                    }
+                }
+                // The attribute is manually defined, e.g. in `all-packages.nix`.
+                // This means we need to enforce it to look like this:
+                //   callPackage ../pkgs/by-name/fo/foo/package.nix { ... }
+                DefinitionVariant::ManualDefinition {
+                    is_semantic_call_package,
+                } => {
+                    // We should expect manual definitions to have a location, otherwise we can't
+                    // enforce the expected format
+                    if let Some(location) = location {
+                        // Parse the Nix file in the location
+                        let nix_file = nix_file_store.get(&location.file)?;
+
+                        // The relative path of the Nix file, for error messages
+                        let relative_location_file = location.relative_file(nixpkgs_path).with_context(|| {
+                            format!("Failed to resolve the file where attribute {attribute_name} is defined")
+                        })?;
+
+                        // Figure out whether it's an attribute definition of the form `= callPackage <arg1> <arg2>`,
+                        // returning the arguments if so.
+                        let (optional_syntactic_call_package, definition) = nix_file
+                            .call_package_argument_info_at(location.line, location.column, nixpkgs_path)
+                            .with_context(|| {
+                                format!("Failed to get the definition info for attribute {attribute_name}")
+                            })?;
+
+                        by_name_override(
+                            attribute_name,
+                            relative_package_file,
+                            is_semantic_call_package,
+                            optional_syntactic_call_package,
+                            definition,
+                            location,
+                            relative_location_file,
+                        )
+                    } else {
+                        // If manual definitions don't have a location, it's likely `mapAttrs`'d
+                        // over, e.g. if it's defined in aliases.nix.
+                        // We can't verify whether its of the expected `callPackage`, so error out
+                        NixpkgsProblem::CannotDetermineAttributeLocation {
+                            attr_name: attribute_name.to_owned(),
+                        }
+                        .into()
+                    }
+                }
+            };
+
+            // Independently report problems about whether it's a derivation and the callPackage variant
+            is_derivation_result.and(variant_result)
+        }
+    };
+    Ok(
+        // Packages being checked in this function are _always_ already defined in `pkgs/by-name`,
+        // so instead of repeating ourselves all the time to define `uses_by_name`, just set it
+        // once at the end with a map
+        manual_definition_result.map(|manual_definition| ratchet::Package {
+            manual_definition,
+            uses_by_name: Tight,
+        }),
+    )
+}
+
+/// Handles the case for packages in `pkgs/by-name` that are manually overridden, e.g. in
+/// all-packages.nix
+fn by_name_override(
+    attribute_name: &str,
+    expected_package_file: RelativePathBuf,
+    is_semantic_call_package: bool,
+    optional_syntactic_call_package: Option<CallPackageArgumentInfo>,
+    definition: String,
+    location: Location,
+    relative_location_file: RelativePathBuf,
+) -> validation::Validation<ratchet::RatchetState<ratchet::ManualDefinition>> {
+    // At this point, we completed two different checks for whether it's a
+    // `callPackage`
+    match (is_semantic_call_package, optional_syntactic_call_package) {
+        // Something like `<attr> = foo`
+        (_, None) => NixpkgsProblem::NonSyntacticCallPackage {
+            package_name: attribute_name.to_owned(),
+            file: relative_location_file,
+            line: location.line,
+            column: location.column,
+            definition,
+        }
+        .into(),
+        // Something like `<attr> = pythonPackages.callPackage ...`
+        (false, Some(_)) => NixpkgsProblem::NonToplevelCallPackage {
+            package_name: attribute_name.to_owned(),
+            file: relative_location_file,
+            line: location.line,
+            column: location.column,
+            definition,
+        }
+        .into(),
+        // Something like `<attr> = pkgs.callPackage ...`
+        (true, Some(syntactic_call_package)) => {
+            if let Some(actual_package_file) = syntactic_call_package.relative_path {
+                if actual_package_file != expected_package_file {
+                    // Wrong path
+                    NixpkgsProblem::WrongCallPackagePath {
+                        package_name: attribute_name.to_owned(),
+                        file: relative_location_file,
+                        line: location.line,
+                        actual_path: actual_package_file,
+                        expected_path: expected_package_file,
+                    }
+                    .into()
+                } else {
+                    // Manual definitions with empty arguments are not allowed
+                    // anymore, but existing ones should continue to be allowed
+                    let manual_definition_ratchet = if syntactic_call_package.empty_arg {
+                        // This is the state to migrate away from
+                        Loose(NixpkgsProblem::EmptyArgument {
+                            package_name: attribute_name.to_owned(),
+                            file: relative_location_file,
+                            line: location.line,
+                            column: location.column,
+                            definition,
+                        })
+                    } else {
+                        // This is the state to migrate to
+                        Tight
+                    };
+
+                    Success(manual_definition_ratchet)
+                }
+            } else {
+                // No path
+                NixpkgsProblem::NonPath {
+                    package_name: attribute_name.to_owned(),
+                    file: relative_location_file,
+                    line: location.line,
+                    column: location.column,
+                    definition,
+                }
+                .into()
+            }
+        }
+    }
+}
+
+/// Handles the evaluation result for an attribute _not_ in `pkgs/by-name`,
+/// turning it into a validation result.
+fn handle_non_by_name_attribute(
+    nixpkgs_path: &Path,
+    nix_file_store: &mut NixFileStore,
+    attribute_name: &str,
+    non_by_name_attribute: NonByNameAttribute,
+) -> validation::Result<ratchet::Package> {
+    use ratchet::RatchetState::*;
+    use NonByNameAttribute::*;
+
+    // The ratchet state whether this attribute uses `pkgs/by-name`.
+    // This is never `Tight`, because we only either:
+    // - Know that the attribute _could_ be migrated to `pkgs/by-name`, which is `Loose`
+    // - Or we're unsure, in which case we use NonApplicable
+    let uses_by_name =
+        // This is a big ol' match on various properties of the attribute
+
+        // First, it needs to succeed evaluation. We can't know whether an attribute could be
+        // migrated to `pkgs/by-name` if it doesn't evaluate, since we need to check that it's a
+        // derivation.
+        //
+        // This only has the minor negative effect that if a PR that breaks evaluation
+        // gets merged, fixing those failures won't force anything into `pkgs/by-name`.
+        //
+        // For now this isn't our problem, but in the future we
+        // might have another check to enforce that evaluation must not be broken.
+        //
+        // The alternative of assuming that failing attributes would have been fit for `pkgs/by-name`
+        // has the problem that if a package evaluation gets broken temporarily,
+        // fixing it requires a move to pkgs/by-name, which could happen more
+        // often and isn't really justified.
+        if let EvalSuccess(AttributeInfo {
+            // We're only interested in attributes that are attribute sets (which includes
+            // derivations). Anything else can't be in `pkgs/by-name`.
+            attribute_variant: AttributeVariant::AttributeSet {
+                // Indeed, we only care about derivations, non-derivation attribute sets can't be
+                // in `pkgs/by-name`
+                is_derivation: true,
+                // Of the two definition variants, really only the manual one makes sense here.
+                // Special cases are:
+                // - Manual aliases to auto-called packages are not treated as manual definitions,
+                //   due to limitations in the semantic callPackage detection. So those should be
+                //   ignored.
+                // - Manual definitions using the internal _internalCallByNamePackageFile are
+                //   not treated as manual definitions, since _internalCallByNamePackageFile is
+                //   used to detect automatic ones. We can't distinguish from the above case, so we
+                //   just need to ignore this one too, even if that internal attribute should never
+                //   be called manually.
+                definition_variant: DefinitionVariant::ManualDefinition { is_semantic_call_package }
+            },
+            // We need the location of the manual definition, because otherwise
+            // we can't figure out whether it's a syntactic callPackage
+            location: Some(location),
+        }) = non_by_name_attribute {
+
+        // Parse the Nix file in the location
+        let nix_file = nix_file_store.get(&location.file)?;
+
+        // The relative path of the Nix file, for error messages
+        let relative_location_file = location.relative_file(nixpkgs_path).with_context(|| {
+            format!("Failed to resolve the file where attribute {attribute_name} is defined")
+        })?;
+
+        // Figure out whether it's an attribute definition of the form `= callPackage <arg1> <arg2>`,
+        // returning the arguments if so.
+        let (optional_syntactic_call_package, _definition) = nix_file
+            .call_package_argument_info_at(
+                location.line,
+                location.column,
+                // Passing the Nixpkgs path here both checks that the <arg1> is within Nixpkgs, and
+                // strips the absolute Nixpkgs path from it, such that
+                // syntactic_call_package.relative_path is relative to Nixpkgs
+                nixpkgs_path
+                )
+            .with_context(|| {
+                format!("Failed to get the definition info for attribute {attribute_name}")
+            })?;
+
+        // At this point, we completed two different checks for whether it's a
+        // `callPackage`
+        match (is_semantic_call_package, optional_syntactic_call_package) {
+            // Something like `<attr> = { }`
+            (false, None)
+            // Something like `<attr> = pythonPackages.callPackage ...`
+            | (false, Some(_))
+            // Something like `<attr> = bar` where `bar = pkgs.callPackage ...`
+            | (true, None) => {
+                // In all of these cases, it's not possible to migrate the package to `pkgs/by-name`
+                NonApplicable
+            }
+            // Something like `<attr> = pkgs.callPackage ...`
+            (true, Some(syntactic_call_package)) => {
+                // It's only possible to migrate such a definitions if..
+                match syntactic_call_package.relative_path {
+                    Some(ref rel_path) if rel_path.starts_with(utils::BASE_SUBPATH) => {
+                        // ..the path is not already within `pkgs/by-name` like
+                        //
+                        //   foo-variant = callPackage ../by-name/fo/foo/package.nix {
+                        //     someFlag = true;
+                        //   }
+                        //
+                        // While such definitions could be moved to `pkgs/by-name` by using
+                        // `.override { someFlag = true; }` instead, this changes the semantics in
+                        // relation with overlays, so migration is generally not possible.
+                        //
+                        // See also "package variants" in RFC 140:
+                        // https://github.com/NixOS/rfcs/blob/master/rfcs/0140-simple-package-paths.md#package-variants
+                        NonApplicable
+                    }
+                    _ => {
+                        // Otherwise, the path is outside `pkgs/by-name`, which means it can be
+                        // migrated
+                        Loose((syntactic_call_package, relative_location_file))
+                    }
+                }
+            }
+        }
+    } else {
+        // This catches all the cases not matched by the above `if let`, falling back to not being
+        // able to migrate such attributes
+        NonApplicable
+    };
+    Ok(Success(ratchet::Package {
+        // Packages being checked in this function _always_ need a manual definition, because
+        // they're not using `pkgs/by-name` which would allow avoiding it.
+        // so instead of repeating ourselves all the time to define `manual_definition`,
+        // just set it once at the end here
+        manual_definition: Tight,
+        uses_by_name,
+    }))
+}
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/src/main.rs b/nixpkgs/pkgs/test/nixpkgs-check-by-name/src/main.rs
new file mode 100644
index 000000000000..dcc9cb9e716d
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/src/main.rs
@@ -0,0 +1,293 @@
+use crate::nix_file::NixFileStore;
+use std::panic;
+mod eval;
+mod nix_file;
+mod nixpkgs_problem;
+mod ratchet;
+mod references;
+mod structure;
+mod utils;
+mod validation;
+
+use crate::structure::check_structure;
+use crate::validation::Validation::Failure;
+use crate::validation::Validation::Success;
+use anyhow::Context;
+use clap::Parser;
+use colored::Colorize;
+use std::io;
+use std::path::{Path, PathBuf};
+use std::process::ExitCode;
+use std::thread;
+
+/// Program to check the validity of pkgs/by-name
+///
+/// This CLI interface may be changed over time if the CI workflow making use of
+/// it is adjusted to deal with the change appropriately.
+///
+/// Exit code:
+/// - `0`: If the validation is successful
+/// - `1`: If the validation is not successful
+/// - `2`: If an unexpected I/O error occurs
+///
+/// Standard error:
+/// - Informative messages
+/// - Detected problems if validation is not successful
+#[derive(Parser, Debug)]
+#[command(about, verbatim_doc_comment)]
+pub struct Args {
+    /// Path to the main Nixpkgs to check.
+    /// For PRs, this should be set to a checkout of the PR branch.
+    nixpkgs: PathBuf,
+
+    /// Path to the base Nixpkgs to run ratchet checks against.
+    /// For PRs, this should be set to a checkout of the PRs base branch.
+    #[arg(long)]
+    base: PathBuf,
+}
+
+fn main() -> ExitCode {
+    let args = Args::parse();
+    match process(args.base, args.nixpkgs, false, &mut io::stderr()) {
+        Ok(true) => ExitCode::SUCCESS,
+        Ok(false) => ExitCode::from(1),
+        Err(e) => {
+            eprintln!("{} {:#}", "I/O error: ".yellow(), e);
+            ExitCode::from(2)
+        }
+    }
+}
+
+/// Does the actual work. This is the abstraction used both by `main` and the tests.
+///
+/// # Arguments
+/// - `base_nixpkgs`: Path to the base Nixpkgs to run ratchet checks against.
+/// - `main_nixpkgs`: Path to the main Nixpkgs to check.
+/// - `keep_nix_path`: Whether the value of the NIX_PATH environment variable should be kept for
+/// the evaluation stage, allowing its contents to be accessed.
+///   This is used to allow the tests to access e.g. the mock-nixpkgs.nix file
+/// - `error_writer`: An `io::Write` value to write validation errors to, if any.
+///
+/// # Return value
+/// - `Err(e)` if an I/O-related error `e` occurred.
+/// - `Ok(false)` if there are problems, all of which will be written to `error_writer`.
+/// - `Ok(true)` if there are no problems
+pub fn process<W: io::Write>(
+    base_nixpkgs: PathBuf,
+    main_nixpkgs: PathBuf,
+    keep_nix_path: bool,
+    error_writer: &mut W,
+) -> anyhow::Result<bool> {
+    // Very easy to parallelise this, since it's totally independent
+    let base_thread = thread::spawn(move || check_nixpkgs(&base_nixpkgs, keep_nix_path));
+    let main_result = check_nixpkgs(&main_nixpkgs, keep_nix_path)?;
+
+    let base_result = match base_thread.join() {
+        Ok(res) => res?,
+        Err(e) => panic::resume_unwind(e),
+    };
+
+    match (base_result, main_result) {
+        (Failure(_), Failure(errors)) => {
+            // Base branch fails and the PR doesn't fix it and may also introduce additional problems
+            for error in errors {
+                writeln!(error_writer, "{}", error.to_string().red())?
+            }
+            writeln!(error_writer, "{}", "The base branch is broken and still has above problems with this PR, which need to be fixed first.\nConsider reverting the PR that introduced these problems in order to prevent more failures of unrelated PRs.".yellow())?;
+            Ok(false)
+        }
+        (Failure(_), Success(_)) => {
+            writeln!(
+                error_writer,
+                "{}",
+                "The base branch is broken, but this PR fixes it. Nice job!".green()
+            )?;
+            Ok(true)
+        }
+        (Success(_), Failure(errors)) => {
+            for error in errors {
+                writeln!(error_writer, "{}", error.to_string().red())?
+            }
+            writeln!(
+                error_writer,
+                "{}",
+                "This PR introduces the problems listed above. Please fix them before merging, otherwise the base branch would break."
+                    .yellow()
+            )?;
+            Ok(false)
+        }
+        (Success(base), Success(main)) => {
+            // Both base and main branch succeed, check ratchet state
+            match ratchet::Nixpkgs::compare(base, main) {
+                Failure(errors) => {
+                    for error in errors {
+                        writeln!(error_writer, "{}", error.to_string().red())?
+                    }
+                    writeln!(error_writer, "{}", "This PR introduces additional instances of discouraged patterns as listed above. Merging is discouraged but would not break the base branch.".yellow())?;
+
+                    Ok(false)
+                }
+                Success(()) => {
+                    writeln!(error_writer, "{}", "Validated successfully".green())?;
+                    Ok(true)
+                }
+            }
+        }
+    }
+}
+
+/// Checks whether the pkgs/by-name structure in Nixpkgs is valid.
+///
+/// This does not include ratchet checks, see ../README.md#ratchet-checks
+/// Instead a `ratchet::Nixpkgs` value is returned, whose `compare` method allows performing the
+/// ratchet check against another result.
+pub fn check_nixpkgs(
+    nixpkgs_path: &Path,
+    keep_nix_path: bool,
+) -> validation::Result<ratchet::Nixpkgs> {
+    let mut nix_file_store = NixFileStore::default();
+
+    Ok({
+        let nixpkgs_path = nixpkgs_path.canonicalize().with_context(|| {
+            format!(
+                "Nixpkgs path {} could not be resolved",
+                nixpkgs_path.display()
+            )
+        })?;
+
+        if !nixpkgs_path.join(utils::BASE_SUBPATH).exists() {
+            // No pkgs/by-name directory, always valid
+            Success(ratchet::Nixpkgs::default())
+        } else {
+            check_structure(&nixpkgs_path, &mut nix_file_store)?.result_map(|package_names|
+                // Only if we could successfully parse the structure, we do the evaluation checks
+                eval::check_values(&nixpkgs_path, &mut nix_file_store, package_names, keep_nix_path))?
+        }
+    })
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::process;
+    use crate::utils;
+    use anyhow::Context;
+    use std::fs;
+    use std::path::Path;
+    use tempfile::{tempdir_in, TempDir};
+
+    #[test]
+    fn tests_dir() -> anyhow::Result<()> {
+        for entry in Path::new("tests").read_dir()? {
+            let entry = entry?;
+            let path = entry.path();
+            let name = entry.file_name().to_string_lossy().into_owned();
+
+            if !path.is_dir() {
+                continue;
+            }
+
+            let expected_errors = fs::read_to_string(path.join("expected"))
+                .expect("No expected file for test {name}");
+
+            test_nixpkgs(&name, &path, &expected_errors)?;
+        }
+        Ok(())
+    }
+
+    // tempfile::tempdir needs to be wrapped in temp_env lock
+    // because it accesses TMPDIR environment variable.
+    pub fn tempdir() -> anyhow::Result<TempDir> {
+        let empty_list: [(&str, Option<&str>); 0] = [];
+        Ok(temp_env::with_vars(empty_list, tempfile::tempdir)?)
+    }
+
+    // We cannot check case-conflicting files into Nixpkgs (the channel would fail to
+    // build), so we generate the case-conflicting file instead.
+    #[test]
+    fn test_case_sensitive() -> anyhow::Result<()> {
+        let temp_nixpkgs = tempdir()?;
+        let path = temp_nixpkgs.path();
+
+        if is_case_insensitive_fs(&path)? {
+            eprintln!("We're on a case-insensitive filesystem, skipping case-sensitivity test");
+            return Ok(());
+        }
+
+        let base = path.join(utils::BASE_SUBPATH);
+
+        fs::create_dir_all(base.join("fo/foo"))?;
+        fs::write(base.join("fo/foo/package.nix"), "{ someDrv }: someDrv")?;
+
+        fs::create_dir_all(base.join("fo/foO"))?;
+        fs::write(base.join("fo/foO/package.nix"), "{ someDrv }: someDrv")?;
+
+        test_nixpkgs(
+            "case_sensitive",
+            &path,
+            "pkgs/by-name/fo: Duplicate case-sensitive package directories \"foO\" and \"foo\".\nThis PR introduces the problems listed above. Please fix them before merging, otherwise the base branch would break.\n",
+        )?;
+
+        Ok(())
+    }
+
+    /// Tests symlinked temporary directories.
+    /// This is needed because on darwin, `/tmp` is a symlink to `/private/tmp`, and Nix's
+    /// restrict-eval doesn't also allow access to the canonical path when you allow the
+    /// non-canonical one.
+    ///
+    /// The error if we didn't do this would look like this:
+    /// error: access to canonical path '/private/var/folders/[...]/.tmpFbcNO0' is forbidden in restricted mode
+    #[test]
+    fn test_symlinked_tmpdir() -> anyhow::Result<()> {
+        // Create a directory with two entries:
+        // - actual (dir)
+        // - symlinked -> actual (symlink)
+        let temp_root = tempdir()?;
+        fs::create_dir(temp_root.path().join("actual"))?;
+        std::os::unix::fs::symlink("actual", temp_root.path().join("symlinked"))?;
+        let tmpdir = temp_root.path().join("symlinked");
+
+        temp_env::with_var("TMPDIR", Some(&tmpdir), || {
+            test_nixpkgs(
+                "symlinked_tmpdir",
+                Path::new("tests/success"),
+                "Validated successfully\n",
+            )
+        })
+    }
+
+    fn test_nixpkgs(name: &str, path: &Path, expected_errors: &str) -> anyhow::Result<()> {
+        let base_path = path.join("base");
+        let base_nixpkgs = if base_path.exists() {
+            base_path.as_path()
+        } else {
+            Path::new("tests/empty-base")
+        };
+
+        // We don't want coloring to mess up the tests
+        let writer = temp_env::with_var("NO_COLOR", Some("1"), || -> anyhow::Result<_> {
+            let mut writer = vec![];
+            process(base_nixpkgs.to_owned(), path.to_owned(), true, &mut writer)
+                .with_context(|| format!("Failed test case {name}"))?;
+            Ok(writer)
+        })?;
+
+        let actual_errors = String::from_utf8_lossy(&writer);
+
+        if actual_errors != expected_errors {
+            panic!(
+                "Failed test case {name}, expected these errors:\n=======\n{}\n=======\nbut got these:\n=======\n{}\n=======",
+                expected_errors, actual_errors
+            );
+        }
+        Ok(())
+    }
+
+    /// Check whether a path is in a case-insensitive filesystem
+    fn is_case_insensitive_fs(path: &Path) -> anyhow::Result<bool> {
+        let dir = tempdir_in(path)?;
+        let base = dir.path();
+        fs::write(base.join("aaa"), "")?;
+        Ok(base.join("AAA").exists())
+    }
+}
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/src/nix_file.rs b/nixpkgs/pkgs/test/nixpkgs-check-by-name/src/nix_file.rs
new file mode 100644
index 000000000000..e2dc1e196141
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/src/nix_file.rs
@@ -0,0 +1,555 @@
+//! This is a utility module for interacting with the syntax of Nix files
+
+use crate::utils::LineIndex;
+use anyhow::Context;
+use itertools::Either::{self, Left, Right};
+use relative_path::RelativePathBuf;
+use rnix::ast;
+use rnix::ast::Expr;
+use rnix::ast::HasEntry;
+use rowan::ast::AstNode;
+use rowan::TextSize;
+use rowan::TokenAtOffset;
+use std::collections::hash_map::Entry;
+use std::collections::HashMap;
+use std::fs::read_to_string;
+use std::path::Path;
+use std::path::PathBuf;
+
+/// A structure to store parse results of Nix files in memory,
+/// making sure that the same file never has to be parsed twice
+#[derive(Default)]
+pub struct NixFileStore {
+    entries: HashMap<PathBuf, NixFile>,
+}
+
+impl NixFileStore {
+    /// Get the store entry for a Nix file if it exists, otherwise parse the file, insert it into
+    /// the store, and return the value
+    ///
+    /// Note that this function only gives an anyhow::Result::Err for I/O errors.
+    /// A parse error is anyhow::Result::Ok(Result::Err(error))
+    pub fn get(&mut self, path: &Path) -> anyhow::Result<&NixFile> {
+        match self.entries.entry(path.to_owned()) {
+            Entry::Occupied(entry) => Ok(entry.into_mut()),
+            Entry::Vacant(entry) => Ok(entry.insert(NixFile::new(path)?)),
+        }
+    }
+}
+
+/// A structure for storing a successfully parsed Nix file
+pub struct NixFile {
+    /// The parent directory of the Nix file, for more convenient error handling
+    pub parent_dir: PathBuf,
+    /// The path to the file itself, for errors
+    pub path: PathBuf,
+    pub syntax_root: rnix::Root,
+    pub line_index: LineIndex,
+}
+
+impl NixFile {
+    /// Creates a new NixFile, failing for I/O or parse errors
+    fn new(path: impl AsRef<Path>) -> anyhow::Result<NixFile> {
+        let Some(parent_dir) = path.as_ref().parent() else {
+            anyhow::bail!("Could not get parent of path {}", path.as_ref().display())
+        };
+
+        let contents = read_to_string(&path)
+            .with_context(|| format!("Could not read file {}", path.as_ref().display()))?;
+        let line_index = LineIndex::new(&contents);
+
+        // NOTE: There's now another Nixpkgs CI check to make sure all changed Nix files parse
+        // correctly, though that uses mainline Nix instead of rnix, so it doesn't give the same
+        // errors. In the future we should unify these two checks, ideally moving the other CI
+        // check into this tool as well and checking for both mainline Nix and rnix.
+        rnix::Root::parse(&contents)
+            // rnix's ::ok returns Result<_, _> , so no error is thrown away like it would be with
+            // std::result's ::ok
+            .ok()
+            .map(|syntax_root| NixFile {
+                parent_dir: parent_dir.to_path_buf(),
+                path: path.as_ref().to_owned(),
+                syntax_root,
+                line_index,
+            })
+            .with_context(|| format!("Could not parse file {} with rnix", path.as_ref().display()))
+    }
+}
+
+/// Information about callPackage arguments
+#[derive(Debug, PartialEq)]
+pub struct CallPackageArgumentInfo {
+    /// The relative path of the first argument, or `None` if it's not a path.
+    pub relative_path: Option<RelativePathBuf>,
+    /// Whether the second argument is an empty attribute set
+    pub empty_arg: bool,
+}
+
+impl NixFile {
+    /// Returns information about callPackage arguments for an attribute at a specific line/column
+    /// index.
+    /// If the definition at the given location is not of the form `<attr> = callPackage <arg1> <arg2>;`,
+    /// `Ok((None, String))` is returned, with `String` being the definition itself.
+    ///
+    /// This function only returns `Err` for problems that can't be caused by the Nix contents,
+    /// but rather problems in this programs code itself.
+    ///
+    /// This is meant to be used with the location returned from `builtins.unsafeGetAttrPos`, e.g.:
+    /// - Create file `default.nix` with contents
+    ///   ```nix
+    ///   self: {
+    ///     foo = self.callPackage ./default.nix { };
+    ///   }
+    ///   ```
+    /// - Evaluate
+    ///   ```nix
+    ///   builtins.unsafeGetAttrPos "foo" (import ./default.nix { })
+    ///   ```
+    ///   results in `{ file = ./default.nix; line = 2; column = 3; }`
+    /// - Get the NixFile for `.file` from a `NixFileStore`
+    /// - Call this function with `.line`, `.column` and `relative_to` as the (absolute) current directory
+    ///
+    /// You'll get back
+    /// ```rust
+    /// Ok((
+    ///   Some(CallPackageArgumentInfo { path = Some("default.nix"), empty_arg: true }),
+    ///   "foo = self.callPackage ./default.nix { };",
+    /// ))
+    /// ```
+    ///
+    /// Note that this also returns the same for `pythonPackages.callPackage`. It doesn't make an
+    /// attempt at distinguishing this.
+    pub fn call_package_argument_info_at(
+        &self,
+        line: usize,
+        column: usize,
+        relative_to: &Path,
+    ) -> anyhow::Result<(Option<CallPackageArgumentInfo>, String)> {
+        Ok(match self.attrpath_value_at(line, column)? {
+            Left(definition) => (None, definition),
+            Right(attrpath_value) => {
+                let definition = attrpath_value.to_string();
+                let attrpath_value =
+                    self.attrpath_value_call_package_argument_info(attrpath_value, relative_to)?;
+                (attrpath_value, definition)
+            }
+        })
+    }
+
+    // Internal function mainly to make it independently testable
+    fn attrpath_value_at(
+        &self,
+        line: usize,
+        column: usize,
+    ) -> anyhow::Result<Either<String, ast::AttrpathValue>> {
+        let index = self.line_index.fromlinecolumn(line, column);
+
+        let token_at_offset = self
+            .syntax_root
+            .syntax()
+            .token_at_offset(TextSize::from(index as u32));
+
+        // The token_at_offset function takes indices to mean a location _between_ characters,
+        // which in this case is some spacing followed by the attribute name:
+        //
+        //   foo = 10;
+        //  /\
+        //  This is the token offset, we get both the (newline + indentation) on the left side,
+        //  and the attribute name on the right side.
+        let TokenAtOffset::Between(_space, token) = token_at_offset else {
+            anyhow::bail!("Line {line} column {column} in {} is not the start of a token, but rather {token_at_offset:?}", self.path.display())
+        };
+
+        // token looks like "foo"
+        let Some(node) = token.parent() else {
+            anyhow::bail!(
+                "Token on line {line} column {column} in {} does not have a parent node: {token:?}",
+                self.path.display()
+            )
+        };
+
+        if ast::Attr::can_cast(node.kind()) {
+            // Something like `foo`, `"foo"` or `${"foo"}`
+        } else if ast::Inherit::can_cast(node.kind()) {
+            // Something like `inherit <attr>` or `inherit (<source>) <attr>`
+            // This is the only other way how `builtins.unsafeGetAttrPos` can return
+            // attribute positions, but we only look for ones like `<attr-path> = <value>`, so
+            // ignore this
+            return Ok(Left(node.to_string()));
+        } else {
+            // However, anything else is not expected and smells like a bug
+            anyhow::bail!(
+                "Node in {} is neither an attribute node nor an inherit node: {node:?}",
+                self.path.display()
+            )
+        }
+
+        // node looks like "foo"
+        let Some(attrpath_node) = node.parent() else {
+            anyhow::bail!(
+                "Node in {} does not have a parent node: {node:?}",
+                self.path.display()
+            )
+        };
+
+        if !ast::Attrpath::can_cast(attrpath_node.kind()) {
+            // We know that `node` is an attribute, its parent should be an attribute path
+            anyhow::bail!(
+                "In {}, attribute parent node is not an attribute path node: {attrpath_node:?}",
+                self.path.display()
+            )
+        }
+
+        // attrpath_node looks like "foo.bar"
+        let Some(attrpath_value_node) = attrpath_node.parent() else {
+            anyhow::bail!(
+                "Attribute path node in {} does not have a parent node: {attrpath_node:?}",
+                self.path.display()
+            )
+        };
+
+        if !ast::AttrpathValue::can_cast(attrpath_value_node.kind()) {
+            anyhow::bail!(
+                "Node in {} is not an attribute path value node: {attrpath_value_node:?}",
+                self.path.display()
+            )
+        }
+        // attrpath_value_node looks like "foo.bar = 10;"
+
+        // unwrap is fine because we confirmed that we can cast with the above check.
+        // We could avoid this `unwrap` for a `clone`, since `cast` consumes the argument,
+        // but we still need it for the error message when the cast fails.
+        Ok(Right(
+            ast::AttrpathValue::cast(attrpath_value_node).unwrap(),
+        ))
+    }
+
+    // Internal function mainly to make attrpath_value_at independently testable
+    fn attrpath_value_call_package_argument_info(
+        &self,
+        attrpath_value: ast::AttrpathValue,
+        relative_to: &Path,
+    ) -> anyhow::Result<Option<CallPackageArgumentInfo>> {
+        let Some(attrpath) = attrpath_value.attrpath() else {
+            anyhow::bail!("attrpath value node doesn't have an attrpath: {attrpath_value:?}")
+        };
+
+        // At this point we know it's something like `foo...bar = ...`
+
+        if attrpath.attrs().count() > 1 {
+            // If the attribute path has multiple entries, the left-most entry is an attribute and
+            // can't be a `callPackage`.
+            //
+            // FIXME: `builtins.unsafeGetAttrPos` will return the same position for all attribute
+            // paths and we can't really know which one it is. We could have a case like
+            // `foo.bar = callPackage ... { }` and trying to determine if `bar` is a `callPackage`,
+            // where this is not correct.
+            // However, this case typically doesn't occur anyways,
+            // because top-level packages wouldn't be nested under an attribute set.
+            return Ok(None);
+        }
+        let Some(value) = attrpath_value.value() else {
+            anyhow::bail!("attrpath value node doesn't have a value: {attrpath_value:?}")
+        };
+
+        // At this point we know it's something like `foo = ...`
+
+        let Expr::Apply(apply1) = value else {
+            // Not even a function call, instead something like `foo = null`
+            return Ok(None);
+        };
+        let Some(function1) = apply1.lambda() else {
+            anyhow::bail!("apply node doesn't have a lambda: {apply1:?}")
+        };
+        let Some(arg1) = apply1.argument() else {
+            anyhow::bail!("apply node doesn't have an argument: {apply1:?}")
+        };
+
+        // At this point we know it's something like `foo = <fun> <arg>`.
+        // For a callPackage, `<fun>` would be `callPackage ./file` and `<arg>` would be `{ }`
+
+        let empty_arg = if let Expr::AttrSet(attrset) = arg1 {
+            // We can only statically determine whether the argument is empty if it's an attribute
+            // set _expression_, even though other kind of expressions could evaluate to an attribute
+            // set _value_. But this is what we want anyways
+            attrset.entries().next().is_none()
+        } else {
+            false
+        };
+
+        // Because callPackage takes two curried arguments, the first function needs to be a
+        // function call itself
+        let Expr::Apply(apply2) = function1 else {
+            // Not a callPackage, instead something like `foo = import ./foo`
+            return Ok(None);
+        };
+        let Some(function2) = apply2.lambda() else {
+            anyhow::bail!("apply node doesn't have a lambda: {apply2:?}")
+        };
+        let Some(arg2) = apply2.argument() else {
+            anyhow::bail!("apply node doesn't have an argument: {apply2:?}")
+        };
+
+        // At this point we know it's something like `foo = <fun2> <arg2> <arg1>`.
+        // For a callPackage, `<fun2>` would be `callPackage`, `<arg2>` would be `./file`
+
+        // Check that <arg2> is a path expression
+        let path = if let Expr::Path(actual_path) = arg2 {
+            // Try to statically resolve the path and turn it into a nixpkgs-relative path
+            if let ResolvedPath::Within(p) = self.static_resolve_path(actual_path, relative_to) {
+                Some(p)
+            } else {
+                // We can't statically know an existing path inside Nixpkgs used as <arg2>
+                None
+            }
+        } else {
+            // <arg2> is not a path, but rather e.g. an inline expression
+            None
+        };
+
+        // Check that <fun2> is an identifier, or an attribute path with an identifier at the end
+        let ident = match function2 {
+            Expr::Ident(ident) => {
+                // This means it's something like `foo = callPackage <arg2> <arg1>`
+                ident
+            }
+            Expr::Select(select) => {
+                // This means it's something like `foo = self.callPackage <arg2> <arg1>`.
+                // We also end up here for e.g. `pythonPackages.callPackage`, but the
+                // callPackage-mocking method will take care of not triggering for this case.
+
+                if select.default_expr().is_some() {
+                    // Very odd case, but this would be `foo = self.callPackage or true ./test.nix {}
+                    // (yes this is valid Nix code)
+                    return Ok(None);
+                }
+                let Some(attrpath) = select.attrpath() else {
+                    anyhow::bail!("select node doesn't have an attrpath: {select:?}")
+                };
+                let Some(last) = attrpath.attrs().last() else {
+                    // This case shouldn't be possible, it would be `foo = self. ./test.nix {}`,
+                    // which shouldn't parse
+                    anyhow::bail!("select node has an empty attrpath: {select:?}")
+                };
+                if let ast::Attr::Ident(ident) = last {
+                    ident
+                } else {
+                    // Here it's something like `foo = self."callPackage" /test.nix {}`
+                    // which we're not gonna bother with
+                    return Ok(None);
+                }
+            }
+            // Any other expression we're not gonna treat as callPackage
+            _ => return Ok(None),
+        };
+
+        let Some(token) = ident.ident_token() else {
+            anyhow::bail!("ident node doesn't have a token: {ident:?}")
+        };
+
+        if token.text() == "callPackage" {
+            Ok(Some(CallPackageArgumentInfo {
+                relative_path: path,
+                empty_arg,
+            }))
+        } else {
+            Ok(None)
+        }
+    }
+}
+
+/// The result of trying to statically resolve a Nix path expression
+pub enum ResolvedPath {
+    /// Something like `./foo/${bar}/baz`, can't be known statically
+    Interpolated,
+    /// Something like `<nixpkgs>`, can't be known statically
+    SearchPath,
+    /// Path couldn't be resolved due to an IO error,
+    /// e.g. if the path doesn't exist or you don't have the right permissions
+    Unresolvable(std::io::Error),
+    /// The path is outside the given absolute path
+    Outside,
+    /// The path is within the given absolute path.
+    /// The `RelativePathBuf` is the relative path under the given absolute path.
+    Within(RelativePathBuf),
+}
+
+impl NixFile {
+    /// Statically resolves a Nix path expression and checks that it's within an absolute path
+    ///
+    /// E.g. for the path expression `./bar.nix` in `./foo.nix` and an absolute path of the
+    /// current directory, the function returns `ResolvedPath::Within(./bar.nix)`
+    pub fn static_resolve_path(&self, node: ast::Path, relative_to: &Path) -> ResolvedPath {
+        if node.parts().count() != 1 {
+            // If there's more than 1 interpolated part, it's of the form `./foo/${bar}/baz`.
+            return ResolvedPath::Interpolated;
+        }
+
+        let text = node.to_string();
+
+        if text.starts_with('<') {
+            // A search path like `<nixpkgs>`. There doesn't appear to be better way to detect
+            // these in rnix
+            return ResolvedPath::SearchPath;
+        }
+
+        // Join the file's parent directory and the path expression, then resolve it
+        // FIXME: Expressions like `../../../../foo/bar/baz/qux` or absolute paths
+        // may resolve close to the original file, but may have left the relative_to.
+        // That should be checked more strictly
+        match self.parent_dir.join(Path::new(&text)).canonicalize() {
+            Err(resolution_error) => ResolvedPath::Unresolvable(resolution_error),
+            Ok(resolved) => {
+                // Check if it's within relative_to
+                match resolved.strip_prefix(relative_to) {
+                    Err(_prefix_error) => ResolvedPath::Outside,
+                    Ok(suffix) => ResolvedPath::Within(
+                        RelativePathBuf::from_path(suffix).expect("a relative path"),
+                    ),
+                }
+            }
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::tests;
+    use indoc::indoc;
+
+    #[test]
+    fn detects_attributes() -> anyhow::Result<()> {
+        let temp_dir = tests::tempdir()?;
+        let file = temp_dir.path().join("file.nix");
+        let contents = indoc! {r#"
+            toInherit: {
+              foo = 1;
+              "bar" = 2;
+              ${"baz"} = 3;
+              "${"qux"}" = 4;
+
+              # A
+              quux
+              # B
+              =
+              # C
+              5
+              # D
+              ;
+              # E
+
+              /**/quuux/**/=/**/5/**/;/*E*/
+
+              inherit toInherit;
+              inherit (toInherit) toInherit;
+            }
+        "#};
+
+        std::fs::write(&file, contents)?;
+
+        let nix_file = NixFile::new(&file)?;
+
+        // These are builtins.unsafeGetAttrPos locations for the attributes
+        let cases = [
+            (2, 3, Right("foo = 1;")),
+            (3, 3, Right(r#""bar" = 2;"#)),
+            (4, 3, Right(r#"${"baz"} = 3;"#)),
+            (5, 3, Right(r#""${"qux"}" = 4;"#)),
+            (8, 3, Right("quux\n  # B\n  =\n  # C\n  5\n  # D\n  ;")),
+            (17, 7, Right("quuux/**/=/**/5/**/;")),
+            (19, 10, Left("inherit toInherit;")),
+            (20, 22, Left("inherit (toInherit) toInherit;")),
+        ];
+
+        for (line, column, expected_result) in cases {
+            let actual_result = nix_file
+                .attrpath_value_at(line, column)
+                .context(format!("line {line}, column {column}"))?
+                .map_right(|node| node.to_string());
+            let owned_expected_result = expected_result
+                .map(|x| x.to_string())
+                .map_left(|x| x.to_string());
+            assert_eq!(
+                actual_result, owned_expected_result,
+                "line {line}, column {column}"
+            );
+        }
+
+        Ok(())
+    }
+
+    #[test]
+    fn detects_call_package() -> anyhow::Result<()> {
+        let temp_dir = tests::tempdir()?;
+        let file = temp_dir.path().join("file.nix");
+        let contents = indoc! {r#"
+            self: with self; {
+              a.sub = null;
+              b = null;
+              c = import ./file.nix;
+              d = import ./file.nix { };
+              e = pythonPackages.callPackage ./file.nix { };
+              f = callPackage ./file.nix { };
+              g = callPackage ({ }: { }) { };
+              h = callPackage ./file.nix { x = 0; };
+              i = callPackage ({ }: { }) (let in { });
+            }
+        "#};
+
+        std::fs::write(&file, contents)?;
+
+        let nix_file = NixFile::new(&file)?;
+
+        let cases = [
+            (2, None),
+            (3, None),
+            (4, None),
+            (5, None),
+            (
+                6,
+                Some(CallPackageArgumentInfo {
+                    relative_path: Some(RelativePathBuf::from("file.nix")),
+                    empty_arg: true,
+                }),
+            ),
+            (
+                7,
+                Some(CallPackageArgumentInfo {
+                    relative_path: Some(RelativePathBuf::from("file.nix")),
+                    empty_arg: true,
+                }),
+            ),
+            (
+                8,
+                Some(CallPackageArgumentInfo {
+                    relative_path: None,
+                    empty_arg: true,
+                }),
+            ),
+            (
+                9,
+                Some(CallPackageArgumentInfo {
+                    relative_path: Some(RelativePathBuf::from("file.nix")),
+                    empty_arg: false,
+                }),
+            ),
+            (
+                10,
+                Some(CallPackageArgumentInfo {
+                    relative_path: None,
+                    empty_arg: false,
+                }),
+            ),
+        ];
+
+        for (line, expected_result) in cases {
+            let (actual_result, _definition) = nix_file
+                .call_package_argument_info_at(line, 3, temp_dir.path())
+                .context(format!("line {line}"))?;
+            assert_eq!(actual_result, expected_result, "line {line}");
+        }
+
+        Ok(())
+    }
+}
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/src/nixpkgs_problem.rs b/nixpkgs/pkgs/test/nixpkgs-check-by-name/src/nixpkgs_problem.rs
new file mode 100644
index 000000000000..7e257c0ed5d8
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/src/nixpkgs_problem.rs
@@ -0,0 +1,425 @@
+use crate::structure;
+use crate::utils::PACKAGE_NIX_FILENAME;
+use indoc::writedoc;
+use relative_path::RelativePath;
+use relative_path::RelativePathBuf;
+use std::ffi::OsString;
+use std::fmt;
+
+/// Any problem that can occur when checking Nixpkgs
+/// All paths are relative to Nixpkgs such that the error messages can't be influenced by Nixpkgs absolute
+/// location
+#[derive(Clone)]
+pub enum NixpkgsProblem {
+    ShardNonDir {
+        relative_shard_path: RelativePathBuf,
+    },
+    InvalidShardName {
+        relative_shard_path: RelativePathBuf,
+        shard_name: String,
+    },
+    PackageNonDir {
+        relative_package_dir: RelativePathBuf,
+    },
+    CaseSensitiveDuplicate {
+        relative_shard_path: RelativePathBuf,
+        first: OsString,
+        second: OsString,
+    },
+    InvalidPackageName {
+        relative_package_dir: RelativePathBuf,
+        package_name: String,
+    },
+    IncorrectShard {
+        relative_package_dir: RelativePathBuf,
+        correct_relative_package_dir: RelativePathBuf,
+    },
+    PackageNixNonExistent {
+        relative_package_dir: RelativePathBuf,
+    },
+    PackageNixDir {
+        relative_package_dir: RelativePathBuf,
+    },
+    UndefinedAttr {
+        relative_package_file: RelativePathBuf,
+        package_name: String,
+    },
+    EmptyArgument {
+        package_name: String,
+        file: RelativePathBuf,
+        line: usize,
+        column: usize,
+        definition: String,
+    },
+    NonToplevelCallPackage {
+        package_name: String,
+        file: RelativePathBuf,
+        line: usize,
+        column: usize,
+        definition: String,
+    },
+    NonPath {
+        package_name: String,
+        file: RelativePathBuf,
+        line: usize,
+        column: usize,
+        definition: String,
+    },
+    WrongCallPackagePath {
+        package_name: String,
+        file: RelativePathBuf,
+        line: usize,
+        actual_path: RelativePathBuf,
+        expected_path: RelativePathBuf,
+    },
+    NonSyntacticCallPackage {
+        package_name: String,
+        file: RelativePathBuf,
+        line: usize,
+        column: usize,
+        definition: String,
+    },
+    NonDerivation {
+        relative_package_file: RelativePathBuf,
+        package_name: String,
+    },
+    OutsideSymlink {
+        relative_package_dir: RelativePathBuf,
+        subpath: RelativePathBuf,
+    },
+    UnresolvableSymlink {
+        relative_package_dir: RelativePathBuf,
+        subpath: RelativePathBuf,
+        io_error: String,
+    },
+    PathInterpolation {
+        relative_package_dir: RelativePathBuf,
+        subpath: RelativePathBuf,
+        line: usize,
+        text: String,
+    },
+    SearchPath {
+        relative_package_dir: RelativePathBuf,
+        subpath: RelativePathBuf,
+        line: usize,
+        text: String,
+    },
+    OutsidePathReference {
+        relative_package_dir: RelativePathBuf,
+        subpath: RelativePathBuf,
+        line: usize,
+        text: String,
+    },
+    UnresolvablePathReference {
+        relative_package_dir: RelativePathBuf,
+        subpath: RelativePathBuf,
+        line: usize,
+        text: String,
+        io_error: String,
+    },
+    MovedOutOfByNameEmptyArg {
+        package_name: String,
+        call_package_path: Option<RelativePathBuf>,
+        file: RelativePathBuf,
+    },
+    MovedOutOfByNameNonEmptyArg {
+        package_name: String,
+        call_package_path: Option<RelativePathBuf>,
+        file: RelativePathBuf,
+    },
+    NewPackageNotUsingByNameEmptyArg {
+        package_name: String,
+        call_package_path: Option<RelativePathBuf>,
+        file: RelativePathBuf,
+    },
+    NewPackageNotUsingByNameNonEmptyArg {
+        package_name: String,
+        call_package_path: Option<RelativePathBuf>,
+        file: RelativePathBuf,
+    },
+    InternalCallPackageUsed {
+        attr_name: String,
+    },
+    CannotDetermineAttributeLocation {
+        attr_name: String,
+    },
+}
+
+impl fmt::Display for NixpkgsProblem {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            NixpkgsProblem::ShardNonDir { relative_shard_path } =>
+                write!(
+                    f,
+                    "{relative_shard_path}: This is a file, but it should be a directory.",
+                ),
+            NixpkgsProblem::InvalidShardName { relative_shard_path, shard_name } =>
+                write!(
+                    f,
+                    "{relative_shard_path}: Invalid directory name \"{shard_name}\", must be at most 2 ASCII characters consisting of a-z, 0-9, \"-\" or \"_\".",
+                ),
+            NixpkgsProblem::PackageNonDir { relative_package_dir } =>
+                write!(
+                    f,
+                    "{relative_package_dir}: This path is a file, but it should be a directory.",
+                ),
+            NixpkgsProblem::CaseSensitiveDuplicate { relative_shard_path, first, second } =>
+                write!(
+                    f,
+                    "{relative_shard_path}: Duplicate case-sensitive package directories {first:?} and {second:?}.",
+                ),
+            NixpkgsProblem::InvalidPackageName { relative_package_dir, package_name } =>
+                write!(
+                    f,
+                    "{relative_package_dir}: Invalid package directory name \"{package_name}\", must be ASCII characters consisting of a-z, A-Z, 0-9, \"-\" or \"_\".",
+                ),
+            NixpkgsProblem::IncorrectShard { relative_package_dir, correct_relative_package_dir } =>
+                write!(
+                    f,
+                    "{relative_package_dir}: Incorrect directory location, should be {correct_relative_package_dir} instead.",
+                ),
+            NixpkgsProblem::PackageNixNonExistent { relative_package_dir } =>
+                write!(
+                    f,
+                    "{relative_package_dir}: Missing required \"{PACKAGE_NIX_FILENAME}\" file.",
+                ),
+            NixpkgsProblem::PackageNixDir { relative_package_dir } =>
+                write!(
+                    f,
+                    "{relative_package_dir}: \"{PACKAGE_NIX_FILENAME}\" must be a file.",
+                ),
+            NixpkgsProblem::UndefinedAttr { relative_package_file, package_name } =>
+                write!(
+                    f,
+                    "pkgs.{package_name}: This attribute is not defined but it should be defined automatically as {relative_package_file}",
+                ),
+            NixpkgsProblem::EmptyArgument { package_name, file, line, column, definition } => {
+                let relative_package_dir = structure::relative_dir_for_package(package_name);
+                let relative_package_file = structure::relative_file_for_package(package_name);
+                let indented_definition = indent_definition(*column, definition.clone());
+                writedoc!(
+                    f,
+                    "
+                    - Because {relative_package_dir} exists, the attribute `pkgs.{package_name}` must be defined like
+
+                        {package_name} = callPackage ./{relative_package_file} {{ /* ... */ }};
+
+                      However, in this PR, the second argument is empty. See the definition in {file}:{line}:
+
+                    {indented_definition}
+
+                      Such a definition is provided automatically and therefore not necessary. Please remove it.
+                    ",
+                )
+            }
+            NixpkgsProblem::NonToplevelCallPackage { package_name, file, line, column, definition } => {
+                let relative_package_dir = structure::relative_dir_for_package(package_name);
+                let relative_package_file = structure::relative_file_for_package(package_name);
+                let indented_definition = indent_definition(*column, definition.clone());
+                writedoc!(
+                    f,
+                    "
+                    - Because {relative_package_dir} exists, the attribute `pkgs.{package_name}` must be defined like
+
+                        {package_name} = callPackage ./{relative_package_file} {{ /* ... */ }};
+
+                      However, in this PR, a different `callPackage` is used. See the definition in {file}:{line}:
+
+                    {indented_definition}
+                    ",
+                )
+            }
+            NixpkgsProblem::NonPath { package_name, file, line, column, definition } => {
+                let relative_package_dir = structure::relative_dir_for_package(package_name);
+                let relative_package_file = structure::relative_file_for_package(package_name);
+                let indented_definition = indent_definition(*column, definition.clone());
+                writedoc!(
+                    f,
+                    "
+                    - Because {relative_package_dir} exists, the attribute `pkgs.{package_name}` must be defined like
+
+                        {package_name} = callPackage ./{relative_package_file} {{ /* ... */ }};
+
+                      However, in this PR, the first `callPackage` argument is not a path. See the definition in {file}:{line}:
+
+                    {indented_definition}
+                    ",
+                )
+            }
+            NixpkgsProblem::WrongCallPackagePath { package_name, file, line, actual_path, expected_path } => {
+                let relative_package_dir = structure::relative_dir_for_package(package_name);
+                let expected_path_expr = create_path_expr(file, expected_path);
+                let actual_path_expr = create_path_expr(file, actual_path);
+                writedoc! {
+                    f,
+                    "
+                    - Because {relative_package_dir} exists, the attribute `pkgs.{package_name}` must be defined like
+
+                        {package_name} = callPackage {expected_path_expr} {{ /* ... */ }};
+
+                      However, in this PR, the first `callPackage` argument is the wrong path. See the definition in {file}:{line}:
+
+                        {package_name} = callPackage {actual_path_expr} {{ /* ... */ }};
+                    ",
+                }
+            }
+            NixpkgsProblem::NonSyntacticCallPackage { package_name, file, line, column, definition } => {
+                let relative_package_dir = structure::relative_dir_for_package(package_name);
+                let relative_package_file = structure::relative_file_for_package(package_name);
+                let indented_definition = indent_definition(*column, definition.clone());
+                writedoc!(
+                    f,
+                    "
+                    - Because {relative_package_dir} exists, the attribute `pkgs.{package_name}` must be defined like
+
+                        {package_name} = callPackage ./{relative_package_file} {{ /* ... */ }};
+
+                      However, in this PR, it isn't defined that way. See the definition in {file}:{line}
+
+                    {indented_definition}
+                    ",
+                )
+            }
+            NixpkgsProblem::NonDerivation { relative_package_file, package_name } =>
+                write!(
+                    f,
+                    "pkgs.{package_name}: This attribute defined by {relative_package_file} is not a derivation",
+                ),
+            NixpkgsProblem::OutsideSymlink { relative_package_dir, subpath } =>
+                write!(
+                    f,
+                    "{relative_package_dir}: Path {subpath} is a symlink pointing to a path outside the directory of that package.",
+                ),
+            NixpkgsProblem::UnresolvableSymlink { relative_package_dir, subpath, io_error } =>
+                write!(
+                    f,
+                    "{relative_package_dir}: Path {subpath} is a symlink which cannot be resolved: {io_error}.",
+                ),
+            NixpkgsProblem::PathInterpolation { relative_package_dir, subpath, line, text } =>
+                write!(
+                    f,
+                    "{relative_package_dir}: File {subpath} at line {line} contains the path expression \"{text}\", which is not yet supported and may point outside the directory of that package.",
+                ),
+            NixpkgsProblem::SearchPath { relative_package_dir, subpath, line, text } =>
+                write!(
+                    f,
+                    "{relative_package_dir}: File {subpath} at line {line} contains the nix search path expression \"{text}\" which may point outside the directory of that package.",
+                ),
+            NixpkgsProblem::OutsidePathReference { relative_package_dir, subpath, line, text } =>
+                write!(
+                    f,
+                    "{relative_package_dir}: File {subpath} at line {line} contains the path expression \"{text}\" which may point outside the directory of that package.",
+                ),
+            NixpkgsProblem::UnresolvablePathReference { relative_package_dir, subpath, line, text, io_error } =>
+                write!(
+                    f,
+                    "{relative_package_dir}: File {subpath} at line {line} contains the path expression \"{text}\" which cannot be resolved: {io_error}.",
+                ),
+            NixpkgsProblem::MovedOutOfByNameEmptyArg { package_name, call_package_path, file } => {
+                let call_package_arg =
+                    if let Some(path) = &call_package_path {
+                        format!("./{path}")
+                    } else {
+                        "...".into()
+                    };
+                let relative_package_file = structure::relative_file_for_package(package_name);
+                writedoc!(
+                    f,
+                    "
+                    - Attribute `pkgs.{package_name}` was previously defined in {relative_package_file}, but is now manually defined as `callPackage {call_package_arg} {{ /* ... */ }}` in {file}.
+                      Please move the package back and remove the manual `callPackage`.
+                    ",
+                )
+            },
+            NixpkgsProblem::MovedOutOfByNameNonEmptyArg { package_name, call_package_path, file } => {
+                let call_package_arg =
+                    if let Some(path) = &call_package_path {
+                        format!("./{}", path)
+                    } else {
+                        "...".into()
+                    };
+                let relative_package_file = structure::relative_file_for_package(package_name);
+                // This can happen if users mistakenly assume that for custom arguments,
+                // pkgs/by-name can't be used.
+                writedoc!(
+                    f,
+                    "
+                    - Attribute `pkgs.{package_name}` was previously defined in {relative_package_file}, but is now manually defined as `callPackage {call_package_arg} {{ ... }}` in {file}.
+                      While the manual `callPackage` is still needed, it's not necessary to move the package files.
+                    ",
+                )
+            },
+            NixpkgsProblem::NewPackageNotUsingByNameEmptyArg { package_name, call_package_path, file } => {
+                let call_package_arg =
+                    if let Some(path) = &call_package_path {
+                        format!("./{}", path)
+                    } else {
+                        "...".into()
+                    };
+                let relative_package_file = structure::relative_file_for_package(package_name);
+                writedoc!(
+                    f,
+                    "
+                    - Attribute `pkgs.{package_name}` is a new top-level package using `pkgs.callPackage {call_package_arg} {{ /* ... */ }}`.
+                      Please define it in {relative_package_file} instead.
+                      See `pkgs/by-name/README.md` for more details.
+                      Since the second `callPackage` argument is `{{ }}`, no manual `callPackage` in {file} is needed anymore.
+                    ",
+                )
+            },
+            NixpkgsProblem::NewPackageNotUsingByNameNonEmptyArg { package_name, call_package_path, file } => {
+                let call_package_arg =
+                    if let Some(path) = &call_package_path {
+                        format!("./{}", path)
+                    } else {
+                        "...".into()
+                    };
+                let relative_package_file = structure::relative_file_for_package(package_name);
+                writedoc!(
+                    f,
+                    "
+                    - Attribute `pkgs.{package_name}` is a new top-level package using `pkgs.callPackage {call_package_arg} {{ /* ... */ }}`.
+                      Please define it in {relative_package_file} instead.
+                      See `pkgs/by-name/README.md` for more details.
+                      Since the second `callPackage` argument is not `{{ }}`, the manual `callPackage` in {file} is still needed.
+                    ",
+                )
+            },
+            NixpkgsProblem::InternalCallPackageUsed { attr_name } =>
+                write!(
+                    f,
+                    "pkgs.{attr_name}: This attribute is defined using `_internalCallByNamePackageFile`, which is an internal function not intended for manual use.",
+                ),
+            NixpkgsProblem::CannotDetermineAttributeLocation { attr_name } =>
+                write!(
+                    f,
+                    "pkgs.{attr_name}: Cannot determine the location of this attribute using `builtins.unsafeGetAttrPos`.",
+                ),
+       }
+    }
+}
+
+fn indent_definition(column: usize, definition: String) -> String {
+    // The entire code should be indented 4 spaces
+    textwrap::indent(
+        // But first we want to strip the code's natural indentation
+        &textwrap::dedent(
+            // The definition _doesn't_ include the leading spaces, but we can
+            // recover those from the column
+            &format!("{}{definition}", " ".repeat(column - 1)),
+        ),
+        "    ",
+    )
+}
+
+/// Creates a Nix path expression that when put into Nix file `from_file`, would point to the `to_file`.
+fn create_path_expr(
+    from_file: impl AsRef<RelativePath>,
+    to_file: impl AsRef<RelativePath>,
+) -> String {
+    // This `expect` calls should never trigger because we only call this function with files.
+    // That's why we `expect` them!
+    let from = from_file.as_ref().parent().expect("a parent for this path");
+    let rel = from.relative(to_file);
+    format!("./{rel}")
+}
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/src/ratchet.rs b/nixpkgs/pkgs/test/nixpkgs-check-by-name/src/ratchet.rs
new file mode 100644
index 000000000000..8136d641c351
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/src/ratchet.rs
@@ -0,0 +1,184 @@
+//! This module implements the ratchet checks, see ../README.md#ratchet-checks
+//!
+//! Each type has a `compare` method that validates the ratchet checks for that item.
+
+use crate::nix_file::CallPackageArgumentInfo;
+use crate::nixpkgs_problem::NixpkgsProblem;
+use crate::validation::{self, Validation, Validation::Success};
+use relative_path::RelativePathBuf;
+use std::collections::HashMap;
+
+/// The ratchet value for the entirety of Nixpkgs.
+#[derive(Default)]
+pub struct Nixpkgs {
+    /// Sorted list of packages in package_map
+    pub package_names: Vec<String>,
+    /// The ratchet values for all packages
+    pub package_map: HashMap<String, Package>,
+}
+
+impl Nixpkgs {
+    /// Validates the ratchet checks for Nixpkgs
+    pub fn compare(from: Self, to: Self) -> Validation<()> {
+        validation::sequence_(
+            // We only loop over the current attributes,
+            // we don't need to check ones that were removed
+            to.package_names.into_iter().map(|name| {
+                Package::compare(&name, from.package_map.get(&name), &to.package_map[&name])
+            }),
+        )
+    }
+}
+
+/// The ratchet value for a top-level package
+pub struct Package {
+    /// The ratchet value for the check for non-auto-called empty arguments
+    pub manual_definition: RatchetState<ManualDefinition>,
+
+    /// The ratchet value for the check for new packages using pkgs/by-name
+    pub uses_by_name: RatchetState<UsesByName>,
+}
+
+impl Package {
+    /// Validates the ratchet checks for a top-level package
+    pub fn compare(name: &str, optional_from: Option<&Self>, to: &Self) -> Validation<()> {
+        validation::sequence_([
+            RatchetState::<ManualDefinition>::compare(
+                name,
+                optional_from.map(|x| &x.manual_definition),
+                &to.manual_definition,
+            ),
+            RatchetState::<UsesByName>::compare(
+                name,
+                optional_from.map(|x| &x.uses_by_name),
+                &to.uses_by_name,
+            ),
+        ])
+    }
+}
+
+/// The ratchet state of a generic ratchet check.
+pub enum RatchetState<Ratchet: ToNixpkgsProblem> {
+    /// The ratchet is loose, it can be tightened more.
+    /// In other words, this is the legacy state we're trying to move away from.
+    /// Introducing new instances is not allowed but previous instances will continue to be allowed.
+    /// The `Context` is context for error messages in case a new instance of this state is
+    /// introduced
+    Loose(Ratchet::ToContext),
+    /// The ratchet is tight, it can't be tightened any further.
+    /// This is either because we already use the latest state, or because the ratchet isn't
+    /// relevant.
+    Tight,
+    /// This ratchet can't be applied.
+    /// State transitions from/to NonApplicable are always allowed
+    NonApplicable,
+}
+
+/// A trait that can convert an attribute-specific error context into a NixpkgsProblem
+pub trait ToNixpkgsProblem {
+    /// Context relating to the Nixpkgs that is being transitioned _to_
+    type ToContext;
+
+    /// How to convert an attribute-specific error context into a NixpkgsProblem
+    fn to_nixpkgs_problem(
+        name: &str,
+        optional_from: Option<()>,
+        to: &Self::ToContext,
+    ) -> NixpkgsProblem;
+}
+
+impl<Context: ToNixpkgsProblem> RatchetState<Context> {
+    /// Compare the previous ratchet state of an attribute to the new state.
+    /// The previous state may be `None` in case the attribute is new.
+    fn compare(name: &str, optional_from: Option<&Self>, to: &Self) -> Validation<()> {
+        match (optional_from, to) {
+            // Loosening a ratchet is now allowed
+            (Some(RatchetState::Tight), RatchetState::Loose(loose_context)) => {
+                Context::to_nixpkgs_problem(name, Some(()), loose_context).into()
+            }
+
+            // Introducing a loose ratchet is also not allowed
+            (None, RatchetState::Loose(loose_context)) => {
+                Context::to_nixpkgs_problem(name, None, loose_context).into()
+            }
+
+            // Everything else is allowed, including:
+            // - Loose -> Loose (grandfathering policy for a loose ratchet)
+            // - -> Tight (always okay to keep or make the ratchet tight)
+            // - Anything involving NotApplicable, where we can't really make any good calls
+            _ => Success(()),
+        }
+    }
+}
+
+/// The ratchet to check whether a top-level attribute has/needs
+/// a manual definition, e.g. in all-packages.nix.
+///
+/// This ratchet is only tight for attributes that:
+/// - Are not defined in `pkgs/by-name`, and rely on a manual definition
+/// - Are defined in `pkgs/by-name` without any manual definition,
+///   (no custom argument overrides)
+/// - Are defined with `pkgs/by-name` with a manual definition that can't be removed
+///   because it provides custom argument overrides
+///
+/// In comparison, this ratchet is loose for attributes that:
+/// - Are defined in `pkgs/by-name` with a manual definition
+///   that doesn't have any custom argument overrides
+pub enum ManualDefinition {}
+
+impl ToNixpkgsProblem for ManualDefinition {
+    type ToContext = NixpkgsProblem;
+
+    fn to_nixpkgs_problem(
+        _name: &str,
+        _optional_from: Option<()>,
+        to: &Self::ToContext,
+    ) -> NixpkgsProblem {
+        (*to).clone()
+    }
+}
+
+/// The ratchet value of an attribute
+/// for the check that new packages use pkgs/by-name
+///
+/// This checks that all new package defined using callPackage must be defined via pkgs/by-name
+/// It also checks that once a package uses pkgs/by-name, it can't switch back to all-packages.nix
+pub enum UsesByName {}
+
+impl ToNixpkgsProblem for UsesByName {
+    type ToContext = (CallPackageArgumentInfo, RelativePathBuf);
+
+    fn to_nixpkgs_problem(
+        name: &str,
+        optional_from: Option<()>,
+        (to, file): &Self::ToContext,
+    ) -> NixpkgsProblem {
+        if let Some(()) = optional_from {
+            if to.empty_arg {
+                NixpkgsProblem::MovedOutOfByNameEmptyArg {
+                    package_name: name.to_owned(),
+                    call_package_path: to.relative_path.clone(),
+                    file: file.to_owned(),
+                }
+            } else {
+                NixpkgsProblem::MovedOutOfByNameNonEmptyArg {
+                    package_name: name.to_owned(),
+                    call_package_path: to.relative_path.clone(),
+                    file: file.to_owned(),
+                }
+            }
+        } else if to.empty_arg {
+            NixpkgsProblem::NewPackageNotUsingByNameEmptyArg {
+                package_name: name.to_owned(),
+                call_package_path: to.relative_path.clone(),
+                file: file.to_owned(),
+            }
+        } else {
+            NixpkgsProblem::NewPackageNotUsingByNameNonEmptyArg {
+                package_name: name.to_owned(),
+                call_package_path: to.relative_path.clone(),
+                file: file.to_owned(),
+            }
+        }
+    }
+}
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/src/references.rs b/nixpkgs/pkgs/test/nixpkgs-check-by-name/src/references.rs
new file mode 100644
index 000000000000..e2319163ccc6
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/src/references.rs
@@ -0,0 +1,176 @@
+use crate::nixpkgs_problem::NixpkgsProblem;
+use crate::utils;
+use crate::validation::{self, ResultIteratorExt, Validation::Success};
+use crate::NixFileStore;
+use relative_path::RelativePath;
+
+use anyhow::Context;
+use rowan::ast::AstNode;
+use std::ffi::OsStr;
+use std::path::Path;
+
+/// Check that every package directory in pkgs/by-name doesn't link to outside that directory.
+/// Both symlinks and Nix path expressions are checked.
+pub fn check_references(
+    nix_file_store: &mut NixFileStore,
+    relative_package_dir: &RelativePath,
+    absolute_package_dir: &Path,
+) -> validation::Result<()> {
+    // The first subpath to check is the package directory itself, which we can represent as an
+    // empty path, since the absolute package directory gets prepended to this.
+    // We don't use `./.` to keep the error messages cleaner
+    // (there's no canonicalisation going on underneath)
+    let subpath = RelativePath::new("");
+    check_path(
+        nix_file_store,
+        relative_package_dir,
+        absolute_package_dir,
+        subpath,
+    )
+    .with_context(|| {
+        format!(
+            "While checking the references in package directory {}",
+            relative_package_dir
+        )
+    })
+}
+
+/// Checks for a specific path to not have references outside
+///
+/// The subpath is the relative path within the package directory we're currently checking.
+/// A relative path so that the error messages don't get absolute paths (which are messy in CI).
+/// The absolute package directory gets prepended before doing anything with it though.
+fn check_path(
+    nix_file_store: &mut NixFileStore,
+    relative_package_dir: &RelativePath,
+    absolute_package_dir: &Path,
+    subpath: &RelativePath,
+) -> validation::Result<()> {
+    let path = subpath.to_path(absolute_package_dir);
+
+    Ok(if path.is_symlink() {
+        // Check whether the symlink resolves to outside the package directory
+        match path.canonicalize() {
+            Ok(target) => {
+                // No need to handle the case of it being inside the directory, since we scan through the
+                // entire directory recursively anyways
+                if let Err(_prefix_error) = target.strip_prefix(absolute_package_dir) {
+                    NixpkgsProblem::OutsideSymlink {
+                        relative_package_dir: relative_package_dir.to_owned(),
+                        subpath: subpath.to_owned(),
+                    }
+                    .into()
+                } else {
+                    Success(())
+                }
+            }
+            Err(io_error) => NixpkgsProblem::UnresolvableSymlink {
+                relative_package_dir: relative_package_dir.to_owned(),
+                subpath: subpath.to_owned(),
+                io_error: io_error.to_string(),
+            }
+            .into(),
+        }
+    } else if path.is_dir() {
+        // Recursively check each entry
+        validation::sequence_(
+            utils::read_dir_sorted(&path)?
+                .into_iter()
+                .map(|entry| {
+                    check_path(
+                        nix_file_store,
+                        relative_package_dir,
+                        absolute_package_dir,
+                        // TODO: The relative_path crate doesn't seem to support OsStr
+                        &subpath.join(entry.file_name().to_string_lossy().to_string()),
+                    )
+                })
+                .collect_vec()
+                .with_context(|| format!("Error while recursing into {}", subpath))?,
+        )
+    } else if path.is_file() {
+        // Only check Nix files
+        if let Some(ext) = path.extension() {
+            if ext == OsStr::new("nix") {
+                check_nix_file(
+                    nix_file_store,
+                    relative_package_dir,
+                    absolute_package_dir,
+                    subpath,
+                )
+                .with_context(|| format!("Error while checking Nix file {}", subpath))?
+            } else {
+                Success(())
+            }
+        } else {
+            Success(())
+        }
+    } else {
+        // This should never happen, git doesn't support other file types
+        anyhow::bail!("Unsupported file type for path {}", subpath);
+    })
+}
+
+/// Check whether a nix file contains path expression references pointing outside the package
+/// directory
+fn check_nix_file(
+    nix_file_store: &mut NixFileStore,
+    relative_package_dir: &RelativePath,
+    absolute_package_dir: &Path,
+    subpath: &RelativePath,
+) -> validation::Result<()> {
+    let path = subpath.to_path(absolute_package_dir);
+
+    let nix_file = nix_file_store.get(&path)?;
+
+    Ok(validation::sequence_(
+        nix_file.syntax_root.syntax().descendants().map(|node| {
+            let text = node.text().to_string();
+            let line = nix_file.line_index.line(node.text_range().start().into());
+
+            // We're only interested in Path expressions
+            let Some(path) = rnix::ast::Path::cast(node) else {
+                return Success(());
+            };
+
+            use crate::nix_file::ResolvedPath;
+
+            match nix_file.static_resolve_path(path, absolute_package_dir) {
+                ResolvedPath::Interpolated => NixpkgsProblem::PathInterpolation {
+                    relative_package_dir: relative_package_dir.to_owned(),
+                    subpath: subpath.to_owned(),
+                    line,
+                    text,
+                }
+                .into(),
+                ResolvedPath::SearchPath => NixpkgsProblem::SearchPath {
+                    relative_package_dir: relative_package_dir.to_owned(),
+                    subpath: subpath.to_owned(),
+                    line,
+                    text,
+                }
+                .into(),
+                ResolvedPath::Outside => NixpkgsProblem::OutsidePathReference {
+                    relative_package_dir: relative_package_dir.to_owned(),
+                    subpath: subpath.to_owned(),
+                    line,
+                    text,
+                }
+                .into(),
+                ResolvedPath::Unresolvable(e) => NixpkgsProblem::UnresolvablePathReference {
+                    relative_package_dir: relative_package_dir.to_owned(),
+                    subpath: subpath.to_owned(),
+                    line,
+                    text,
+                    io_error: e.to_string(),
+                }
+                .into(),
+                ResolvedPath::Within(..) => {
+                    // No need to handle the case of it being inside the directory, since we scan through the
+                    // entire directory recursively anyways
+                    Success(())
+                }
+            }
+        }),
+    ))
+}
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/src/structure.rs b/nixpkgs/pkgs/test/nixpkgs-check-by-name/src/structure.rs
new file mode 100644
index 000000000000..09806bc3d4dc
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/src/structure.rs
@@ -0,0 +1,184 @@
+use crate::nixpkgs_problem::NixpkgsProblem;
+use crate::references;
+use crate::utils;
+use crate::utils::{BASE_SUBPATH, PACKAGE_NIX_FILENAME};
+use crate::validation::{self, ResultIteratorExt, Validation::Success};
+use crate::NixFileStore;
+use itertools::concat;
+use lazy_static::lazy_static;
+use regex::Regex;
+use relative_path::RelativePathBuf;
+use std::fs::DirEntry;
+use std::path::Path;
+
+lazy_static! {
+    static ref SHARD_NAME_REGEX: Regex = Regex::new(r"^[a-z0-9_-]{1,2}$").unwrap();
+    static ref PACKAGE_NAME_REGEX: Regex = Regex::new(r"^[a-zA-Z0-9_-]+$").unwrap();
+}
+
+// Some utility functions for the basic structure
+
+pub fn shard_for_package(package_name: &str) -> String {
+    package_name.to_lowercase().chars().take(2).collect()
+}
+
+pub fn relative_dir_for_shard(shard_name: &str) -> RelativePathBuf {
+    RelativePathBuf::from(format!("{BASE_SUBPATH}/{shard_name}"))
+}
+
+pub fn relative_dir_for_package(package_name: &str) -> RelativePathBuf {
+    relative_dir_for_shard(&shard_for_package(package_name)).join(package_name)
+}
+
+pub fn relative_file_for_package(package_name: &str) -> RelativePathBuf {
+    relative_dir_for_package(package_name).join(PACKAGE_NIX_FILENAME)
+}
+
+/// Check the structure of Nixpkgs, returning the attribute names that are defined in
+/// `pkgs/by-name`
+pub fn check_structure(
+    path: &Path,
+    nix_file_store: &mut NixFileStore,
+) -> validation::Result<Vec<String>> {
+    let base_dir = path.join(BASE_SUBPATH);
+
+    let shard_results = utils::read_dir_sorted(&base_dir)?
+        .into_iter()
+        .map(|shard_entry| -> validation::Result<_> {
+            let shard_path = shard_entry.path();
+            let shard_name = shard_entry.file_name().to_string_lossy().into_owned();
+            let relative_shard_path = relative_dir_for_shard(&shard_name);
+
+            Ok(if shard_name == "README.md" {
+                // README.md is allowed to be a file and not checked
+
+                Success(vec![])
+            } else if !shard_path.is_dir() {
+                NixpkgsProblem::ShardNonDir {
+                    relative_shard_path: relative_shard_path.clone(),
+                }
+                .into()
+                // we can't check for any other errors if it's a file, since there's no subdirectories to check
+            } else {
+                let shard_name_valid = SHARD_NAME_REGEX.is_match(&shard_name);
+                let result = if !shard_name_valid {
+                    NixpkgsProblem::InvalidShardName {
+                        relative_shard_path: relative_shard_path.clone(),
+                        shard_name: shard_name.clone(),
+                    }
+                    .into()
+                } else {
+                    Success(())
+                };
+
+                let entries = utils::read_dir_sorted(&shard_path)?;
+
+                let duplicate_results = entries
+                    .iter()
+                    .zip(entries.iter().skip(1))
+                    .filter(|(l, r)| {
+                        l.file_name().to_ascii_lowercase() == r.file_name().to_ascii_lowercase()
+                    })
+                    .map(|(l, r)| {
+                        NixpkgsProblem::CaseSensitiveDuplicate {
+                            relative_shard_path: relative_shard_path.clone(),
+                            first: l.file_name(),
+                            second: r.file_name(),
+                        }
+                        .into()
+                    });
+
+                let result = result.and(validation::sequence_(duplicate_results));
+
+                let package_results = entries
+                    .into_iter()
+                    .map(|package_entry| {
+                        check_package(
+                            nix_file_store,
+                            path,
+                            &shard_name,
+                            shard_name_valid,
+                            package_entry,
+                        )
+                    })
+                    .collect_vec()?;
+
+                result.and(validation::sequence(package_results))
+            })
+        })
+        .collect_vec()?;
+
+    // Combine the package names conatained within each shard into a longer list
+    Ok(validation::sequence(shard_results).map(concat))
+}
+
+fn check_package(
+    nix_file_store: &mut NixFileStore,
+    path: &Path,
+    shard_name: &str,
+    shard_name_valid: bool,
+    package_entry: DirEntry,
+) -> validation::Result<String> {
+    let package_path = package_entry.path();
+    let package_name = package_entry.file_name().to_string_lossy().into_owned();
+    let relative_package_dir =
+        RelativePathBuf::from(format!("{BASE_SUBPATH}/{shard_name}/{package_name}"));
+
+    Ok(if !package_path.is_dir() {
+        NixpkgsProblem::PackageNonDir {
+            relative_package_dir: relative_package_dir.clone(),
+        }
+        .into()
+    } else {
+        let package_name_valid = PACKAGE_NAME_REGEX.is_match(&package_name);
+        let result = if !package_name_valid {
+            NixpkgsProblem::InvalidPackageName {
+                relative_package_dir: relative_package_dir.clone(),
+                package_name: package_name.clone(),
+            }
+            .into()
+        } else {
+            Success(())
+        };
+
+        let correct_relative_package_dir = relative_dir_for_package(&package_name);
+        let result = result.and(if relative_package_dir != correct_relative_package_dir {
+            // Only show this error if we have a valid shard and package name
+            // Because if one of those is wrong, you should fix that first
+            if shard_name_valid && package_name_valid {
+                NixpkgsProblem::IncorrectShard {
+                    relative_package_dir: relative_package_dir.clone(),
+                    correct_relative_package_dir: correct_relative_package_dir.clone(),
+                }
+                .into()
+            } else {
+                Success(())
+            }
+        } else {
+            Success(())
+        });
+
+        let package_nix_path = package_path.join(PACKAGE_NIX_FILENAME);
+        let result = result.and(if !package_nix_path.exists() {
+            NixpkgsProblem::PackageNixNonExistent {
+                relative_package_dir: relative_package_dir.clone(),
+            }
+            .into()
+        } else if package_nix_path.is_dir() {
+            NixpkgsProblem::PackageNixDir {
+                relative_package_dir: relative_package_dir.clone(),
+            }
+            .into()
+        } else {
+            Success(())
+        });
+
+        let result = result.and(references::check_references(
+            nix_file_store,
+            &relative_package_dir,
+            &relative_package_dir.to_path(path),
+        )?);
+
+        result.map(|_| package_name.clone())
+    })
+}
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/src/utils.rs b/nixpkgs/pkgs/test/nixpkgs-check-by-name/src/utils.rs
new file mode 100644
index 000000000000..9a5d12748918
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/src/utils.rs
@@ -0,0 +1,95 @@
+use anyhow::Context;
+use std::fs;
+use std::io;
+use std::path::Path;
+
+pub const BASE_SUBPATH: &str = "pkgs/by-name";
+pub const PACKAGE_NIX_FILENAME: &str = "package.nix";
+
+/// Deterministic file listing so that tests are reproducible
+pub fn read_dir_sorted(base_dir: &Path) -> anyhow::Result<Vec<fs::DirEntry>> {
+    let listing = base_dir
+        .read_dir()
+        .with_context(|| format!("Could not list directory {}", base_dir.display()))?;
+    let mut shard_entries = listing
+        .collect::<io::Result<Vec<_>>>()
+        .with_context(|| format!("Could not list directory {}", base_dir.display()))?;
+    shard_entries.sort_by_key(|entry| entry.file_name());
+    Ok(shard_entries)
+}
+
+/// A simple utility for calculating the line for a string offset.
+/// This doesn't do any Unicode handling, though that probably doesn't matter
+/// because newlines can't split up Unicode characters. Also this is only used
+/// for error reporting
+pub struct LineIndex {
+    /// Stores the indices of newlines
+    newlines: Vec<usize>,
+}
+
+impl LineIndex {
+    pub fn new(s: &str) -> LineIndex {
+        let mut newlines = vec![];
+        let mut index = 0;
+        // Iterates over all newline-split parts of the string, adding the index of the newline to
+        // the vec
+        for split in s.split_inclusive('\n') {
+            index += split.len();
+            newlines.push(index - 1);
+        }
+        LineIndex { newlines }
+    }
+
+    /// Returns the line number for a string index.
+    /// If the index points to a newline, returns the line number before the newline
+    pub fn line(&self, index: usize) -> usize {
+        match self.newlines.binary_search(&index) {
+            // +1 because lines are 1-indexed
+            Ok(x) => x + 1,
+            Err(x) => x + 1,
+        }
+    }
+
+    /// Returns the string index for a line and column.
+    pub fn fromlinecolumn(&self, line: usize, column: usize) -> usize {
+        // If it's the 1th line, the column is the index
+        if line == 1 {
+            // But columns are 1-indexed
+            column - 1
+        } else {
+            // For the nth line, we add the index of the (n-1)st newline to the column,
+            // and remove one more from the index since arrays are 0-indexed.
+            // Then add the 1-indexed column to get not the newline index itself,
+            // but rather the index of the position on the next line
+            self.newlines[line - 2] + column
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn line_index() {
+        let line_index = LineIndex::new("a\nbc\n\ndef\n");
+
+        let pairs = [
+            (0, 1, 1),
+            (1, 1, 2),
+            (2, 2, 1),
+            (3, 2, 2),
+            (4, 2, 3),
+            (5, 3, 1),
+            (6, 4, 1),
+            (7, 4, 2),
+            (8, 4, 3),
+            (9, 4, 4),
+        ];
+
+        for (index, line, column) in pairs {
+            assert_eq!(line_index.line(index), line);
+            assert_eq!(line_index.fromlinecolumn(line, column), index);
+        }
+    }
+}
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/src/validation.rs b/nixpkgs/pkgs/test/nixpkgs-check-by-name/src/validation.rs
new file mode 100644
index 000000000000..b14bfb92eb2e
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/src/validation.rs
@@ -0,0 +1,111 @@
+use crate::nixpkgs_problem::NixpkgsProblem;
+use itertools::concat;
+use itertools::{
+    Either::{Left, Right},
+    Itertools,
+};
+use Validation::*;
+
+/// The validation result of a check.
+/// Instead of exiting at the first failure,
+/// this type can accumulate multiple failures.
+/// This can be achieved using the functions `and`, `sequence` and `sequence_`
+///
+/// This leans on https://hackage.haskell.org/package/validation
+pub enum Validation<A> {
+    Failure(Vec<NixpkgsProblem>),
+    Success(A),
+}
+
+impl<A> From<NixpkgsProblem> for Validation<A> {
+    /// Create a `Validation<A>` from a single check problem
+    fn from(value: NixpkgsProblem) -> Self {
+        Failure(vec![value])
+    }
+}
+
+/// A type alias representing the result of a check, either:
+/// - Err(anyhow::Error): A fatal failure, typically I/O errors.
+///   Such failures are not caused by the files in Nixpkgs.
+///   This hints at a bug in the code or a problem with the deployment.
+/// - Ok(Failure(Vec<NixpkgsProblem>)): A non-fatal validation problem with the Nixpkgs files.
+///   Further checks can be run even with this result type.
+///   Such problems can be fixed by changing the Nixpkgs files.
+/// - Ok(Success(A)): A successful (potentially intermediate) result with an arbitrary value.
+///   No fatal errors have occurred and no validation problems have been found with Nixpkgs.
+pub type Result<A> = anyhow::Result<Validation<A>>;
+
+pub trait ResultIteratorExt<A, E>: Sized + Iterator<Item = std::result::Result<A, E>> {
+    fn collect_vec(self) -> std::result::Result<Vec<A>, E>;
+}
+
+impl<I, A, E> ResultIteratorExt<A, E> for I
+where
+    I: Sized + Iterator<Item = std::result::Result<A, E>>,
+{
+    /// A convenience version of `collect` specialised to a vector
+    fn collect_vec(self) -> std::result::Result<Vec<A>, E> {
+        self.collect()
+    }
+}
+
+impl<A> Validation<A> {
+    /// Map a `Validation<A>` to a `Validation<B>` by applying a function to the
+    /// potentially contained value in case of success.
+    pub fn map<B>(self, f: impl FnOnce(A) -> B) -> Validation<B> {
+        match self {
+            Failure(err) => Failure(err),
+            Success(value) => Success(f(value)),
+        }
+    }
+
+    /// Map a `Validation<A>` to a `Result<B>` by applying a function `A -> Result<B>`
+    /// only if there is a `Success` value
+    pub fn result_map<B>(self, f: impl FnOnce(A) -> Result<B>) -> Result<B> {
+        match self {
+            Failure(err) => Ok(Failure(err)),
+            Success(value) => f(value),
+        }
+    }
+}
+
+impl Validation<()> {
+    /// Combine two validations, both of which need to be successful for the return value to be successful.
+    /// The `NixpkgsProblem`s of both sides are returned concatenated.
+    pub fn and<A>(self, other: Validation<A>) -> Validation<A> {
+        match (self, other) {
+            (Success(_), Success(right_value)) => Success(right_value),
+            (Failure(errors), Success(_)) => Failure(errors),
+            (Success(_), Failure(errors)) => Failure(errors),
+            (Failure(errors_l), Failure(errors_r)) => Failure(concat([errors_l, errors_r])),
+        }
+    }
+}
+
+/// Combine many validations into a single one.
+/// All given validations need to be successful in order for the returned validation to be successful,
+/// in which case the returned validation value contains a `Vec` of each individual value.
+/// Otherwise the `NixpkgsProblem`s of all validations are returned concatenated.
+pub fn sequence<A>(check_results: impl IntoIterator<Item = Validation<A>>) -> Validation<Vec<A>> {
+    let (errors, values): (Vec<Vec<NixpkgsProblem>>, Vec<A>) = check_results
+        .into_iter()
+        .partition_map(|validation| match validation {
+            Failure(err) => Left(err),
+            Success(value) => Right(value),
+        });
+
+    // To combine the errors from the results we flatten all the error Vec's into a new Vec
+    // This is not very efficient, but doesn't matter because generally we should have no errors
+    let flattened_errors = errors.into_iter().concat();
+
+    if flattened_errors.is_empty() {
+        Success(values)
+    } else {
+        Failure(flattened_errors)
+    }
+}
+
+/// Like `sequence`, but without any containing value, for convenience
+pub fn sequence_(validations: impl IntoIterator<Item = Validation<()>>) -> Validation<()> {
+    sequence(validations).map(|_| ())
+}
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/aliases/aliases.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/aliases/aliases.nix
new file mode 100644
index 000000000000..c70ec23478ea
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/aliases/aliases.nix
@@ -0,0 +1,3 @@
+self: super: {
+  baz = self.foo;
+}
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/aliases/all-packages.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/aliases/all-packages.nix
new file mode 100644
index 000000000000..2cdfcef08fa6
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/aliases/all-packages.nix
@@ -0,0 +1,3 @@
+self: super: {
+  bar = self.foo;
+}
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/aliases/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/aliases/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/aliases/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/aliases/expected b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/aliases/expected
new file mode 100644
index 000000000000..defae2634c34
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/aliases/expected
@@ -0,0 +1 @@
+Validated successfully
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/aliases/pkgs/by-name/fo/foo/package.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/aliases/pkgs/by-name/fo/foo/package.nix
new file mode 100644
index 000000000000..a1b92efbbadb
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/aliases/pkgs/by-name/fo/foo/package.nix
@@ -0,0 +1 @@
+{ someDrv }: someDrv
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/alt-callPackage/all-packages.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/alt-callPackage/all-packages.nix
new file mode 100644
index 000000000000..399f8eee0a18
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/alt-callPackage/all-packages.nix
@@ -0,0 +1,7 @@
+self: super: {
+
+  alt.callPackage = self.lib.callPackageWith {};
+
+  foo = self.alt.callPackage ({ }: self.someDrv) { };
+
+}
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/alt-callPackage/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/alt-callPackage/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/alt-callPackage/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/alt-callPackage/expected b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/alt-callPackage/expected
new file mode 100644
index 000000000000..1d92e652200e
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/alt-callPackage/expected
@@ -0,0 +1,9 @@
+- Because pkgs/by-name/fo/foo exists, the attribute `pkgs.foo` must be defined like
+
+    foo = callPackage ./pkgs/by-name/fo/foo/package.nix { /* ... */ };
+
+  However, in this PR, a different `callPackage` is used. See the definition in all-packages.nix:5:
+
+    foo = self.alt.callPackage ({ }: self.someDrv) { };
+
+This PR introduces the problems listed above. Please fix them before merging, otherwise the base branch would break.
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/alt-callPackage/pkgs/by-name/fo/foo/package.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/alt-callPackage/pkgs/by-name/fo/foo/package.nix
new file mode 100644
index 000000000000..a1b92efbbadb
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/alt-callPackage/pkgs/by-name/fo/foo/package.nix
@@ -0,0 +1 @@
+{ someDrv }: someDrv
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/base-fixed/base/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/base-fixed/base/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/base-fixed/base/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/base-fixed/base/pkgs/by-name/foo b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/base-fixed/base/pkgs/by-name/foo
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/base-fixed/base/pkgs/by-name/foo
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/base-fixed/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/base-fixed/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/base-fixed/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/base-fixed/expected b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/base-fixed/expected
new file mode 100644
index 000000000000..e209e1855314
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/base-fixed/expected
@@ -0,0 +1 @@
+The base branch is broken, but this PR fixes it. Nice job!
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/base-fixed/pkgs/by-name/README.md b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/base-fixed/pkgs/by-name/README.md
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/base-fixed/pkgs/by-name/README.md
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/base-still-broken/base/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/base-still-broken/base/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/base-still-broken/base/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/base-still-broken/base/pkgs/by-name/foo b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/base-still-broken/base/pkgs/by-name/foo
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/base-still-broken/base/pkgs/by-name/foo
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/base-still-broken/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/base-still-broken/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/base-still-broken/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/base-still-broken/expected b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/base-still-broken/expected
new file mode 100644
index 000000000000..c25f06b4150e
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/base-still-broken/expected
@@ -0,0 +1,3 @@
+pkgs/by-name/bar: This is a file, but it should be a directory.
+The base branch is broken and still has above problems with this PR, which need to be fixed first.
+Consider reverting the PR that introduced these problems in order to prevent more failures of unrelated PRs.
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/base-still-broken/pkgs/by-name/bar b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/base-still-broken/pkgs/by-name/bar
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/base-still-broken/pkgs/by-name/bar
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/broken-autocall/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/broken-autocall/default.nix
new file mode 100644
index 000000000000..bd4825f8bad8
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/broken-autocall/default.nix
@@ -0,0 +1,4 @@
+args:
+builtins.removeAttrs
+  (import <test-nixpkgs> { root = ./.; } args)
+  [ "foo" ]
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/broken-autocall/expected b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/broken-autocall/expected
new file mode 100644
index 000000000000..15b3e3ff6ede
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/broken-autocall/expected
@@ -0,0 +1,2 @@
+pkgs.foo: This attribute is not defined but it should be defined automatically as pkgs/by-name/fo/foo/package.nix
+This PR introduces the problems listed above. Please fix them before merging, otherwise the base branch would break.
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/broken-autocall/pkgs/by-name/fo/foo/package.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/broken-autocall/pkgs/by-name/fo/foo/package.nix
new file mode 100644
index 000000000000..a1b92efbbadb
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/broken-autocall/pkgs/by-name/fo/foo/package.nix
@@ -0,0 +1 @@
+{ someDrv }: someDrv
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/callPackage-syntax/all-packages.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/callPackage-syntax/all-packages.nix
new file mode 100644
index 000000000000..306d719c9e9d
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/callPackage-syntax/all-packages.nix
@@ -0,0 +1,7 @@
+self: super: {
+  set = self.callPackages ({ callPackage }: {
+    foo = callPackage ({ someDrv }: someDrv) { };
+  }) { };
+
+  inherit (self.set) foo;
+}
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/callPackage-syntax/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/callPackage-syntax/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/callPackage-syntax/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/callPackage-syntax/expected b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/callPackage-syntax/expected
new file mode 100644
index 000000000000..defae2634c34
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/callPackage-syntax/expected
@@ -0,0 +1 @@
+Validated successfully
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/callPackage-syntax/pkgs/by-name/README.md b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/callPackage-syntax/pkgs/by-name/README.md
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/callPackage-syntax/pkgs/by-name/README.md
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/empty-base/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/empty-base/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/empty-base/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/empty-base/expected b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/empty-base/expected
new file mode 100644
index 000000000000..defae2634c34
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/empty-base/expected
@@ -0,0 +1 @@
+Validated successfully
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/empty-base/pkgs/by-name/README.md b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/empty-base/pkgs/by-name/README.md
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/empty-base/pkgs/by-name/README.md
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/incorrect-shard/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/incorrect-shard/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/incorrect-shard/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/incorrect-shard/expected b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/incorrect-shard/expected
new file mode 100644
index 000000000000..3b0146cdc146
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/incorrect-shard/expected
@@ -0,0 +1,2 @@
+pkgs/by-name/aa/FOO: Incorrect directory location, should be pkgs/by-name/fo/FOO instead.
+This PR introduces the problems listed above. Please fix them before merging, otherwise the base branch would break.
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/incorrect-shard/pkgs/by-name/aa/FOO/package.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/incorrect-shard/pkgs/by-name/aa/FOO/package.nix
new file mode 100644
index 000000000000..a1b92efbbadb
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/incorrect-shard/pkgs/by-name/aa/FOO/package.nix
@@ -0,0 +1 @@
+{ someDrv }: someDrv
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/internalCallPackage/all-packages.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/internalCallPackage/all-packages.nix
new file mode 100644
index 000000000000..3fbe2d5e51dc
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/internalCallPackage/all-packages.nix
@@ -0,0 +1,4 @@
+self: super: {
+  foo = self._internalCallByNamePackageFile ./foo.nix;
+  bar = self._internalCallByNamePackageFile ./foo.nix;
+}
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/internalCallPackage/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/internalCallPackage/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/internalCallPackage/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/internalCallPackage/expected b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/internalCallPackage/expected
new file mode 100644
index 000000000000..b3d0c6fc1a40
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/internalCallPackage/expected
@@ -0,0 +1,2 @@
+pkgs.foo: This attribute is defined using `_internalCallByNamePackageFile`, which is an internal function not intended for manual use.
+This PR introduces the problems listed above. Please fix them before merging, otherwise the base branch would break.
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/internalCallPackage/foo.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/internalCallPackage/foo.nix
new file mode 100644
index 000000000000..a1b92efbbadb
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/internalCallPackage/foo.nix
@@ -0,0 +1 @@
+{ someDrv }: someDrv
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/internalCallPackage/pkgs/by-name/README.md b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/internalCallPackage/pkgs/by-name/README.md
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/internalCallPackage/pkgs/by-name/README.md
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/internalCallPackage/pkgs/by-name/fo/foo/package.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/internalCallPackage/pkgs/by-name/fo/foo/package.nix
new file mode 100644
index 000000000000..a1b92efbbadb
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/internalCallPackage/pkgs/by-name/fo/foo/package.nix
@@ -0,0 +1 @@
+{ someDrv }: someDrv
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/invalid-package-name/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/invalid-package-name/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/invalid-package-name/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/invalid-package-name/expected b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/invalid-package-name/expected
new file mode 100644
index 000000000000..80f6e7dd5998
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/invalid-package-name/expected
@@ -0,0 +1,2 @@
+pkgs/by-name/fo/fo@: Invalid package directory name "fo@", must be ASCII characters consisting of a-z, A-Z, 0-9, "-" or "_".
+This PR introduces the problems listed above. Please fix them before merging, otherwise the base branch would break.
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/invalid-package-name/pkgs/by-name/fo/fo@/package.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/invalid-package-name/pkgs/by-name/fo/fo@/package.nix
new file mode 100644
index 000000000000..a1b92efbbadb
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/invalid-package-name/pkgs/by-name/fo/fo@/package.nix
@@ -0,0 +1 @@
+{ someDrv }: someDrv
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/invalid-shard-name/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/invalid-shard-name/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/invalid-shard-name/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/invalid-shard-name/expected b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/invalid-shard-name/expected
new file mode 100644
index 000000000000..7ca9ff8565bd
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/invalid-shard-name/expected
@@ -0,0 +1,2 @@
+pkgs/by-name/A: Invalid directory name "A", must be at most 2 ASCII characters consisting of a-z, 0-9, "-" or "_".
+This PR introduces the problems listed above. Please fix them before merging, otherwise the base branch would break.
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/invalid-shard-name/pkgs/by-name/A/A/.git-keep b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/invalid-shard-name/pkgs/by-name/A/A/.git-keep
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/invalid-shard-name/pkgs/by-name/A/A/.git-keep
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/invalid-shard-name/pkgs/by-name/A/A/package.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/invalid-shard-name/pkgs/by-name/A/A/package.nix
new file mode 100644
index 000000000000..a1b92efbbadb
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/invalid-shard-name/pkgs/by-name/A/A/package.nix
@@ -0,0 +1 @@
+{ someDrv }: someDrv
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/manual-definition/all-packages.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/manual-definition/all-packages.nix
new file mode 100644
index 000000000000..07b2caaab4e7
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/manual-definition/all-packages.nix
@@ -0,0 +1,10 @@
+self: super: {
+  nonAttributeSet = self.callPackage ({ someDrv }: someDrv) { };
+  nonCallPackage = self.callPackage ({ someDrv }: someDrv) { };
+  internalCallByName = self.callPackage ({ someDrv }: someDrv) { };
+  nonDerivation = self.callPackage ({ someDrv }: someDrv) { };
+
+  onlyMove = self.callPackage ./pkgs/by-name/on/onlyMove/package.nix { };
+
+  noEval = self.callPackage ./pkgs/by-name/no/noEval/package.nix { };
+}
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/manual-definition/base/all-packages.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/manual-definition/base/all-packages.nix
new file mode 100644
index 000000000000..75efb5952e7a
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/manual-definition/base/all-packages.nix
@@ -0,0 +1,9 @@
+self: super: {
+  nonAttributeSet = null;
+  nonCallPackage = self.someDrv;
+  internalCallByName = self._internalCallByNamePackageFile ./some-pkg.nix;
+  nonDerivation = self.callPackage ({ }: { }) { };
+
+  onlyMove = self.callPackage ({ someDrv }: someDrv) { };
+  noEval = throw "foo";
+}
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/manual-definition/base/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/manual-definition/base/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/manual-definition/base/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/manual-definition/base/pkgs/by-name/README.md b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/manual-definition/base/pkgs/by-name/README.md
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/manual-definition/base/pkgs/by-name/README.md
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/manual-definition/base/some-pkg.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/manual-definition/base/some-pkg.nix
new file mode 100644
index 000000000000..a1b92efbbadb
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/manual-definition/base/some-pkg.nix
@@ -0,0 +1 @@
+{ someDrv }: someDrv
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/manual-definition/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/manual-definition/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/manual-definition/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/manual-definition/expected b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/manual-definition/expected
new file mode 100644
index 000000000000..4d906ec0d086
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/manual-definition/expected
@@ -0,0 +1,21 @@
+- Because pkgs/by-name/no/noEval exists, the attribute `pkgs.noEval` must be defined like
+
+    noEval = callPackage ./pkgs/by-name/no/noEval/package.nix { /* ... */ };
+
+  However, in this PR, the second argument is empty. See the definition in all-packages.nix:9:
+
+    noEval = self.callPackage ./pkgs/by-name/no/noEval/package.nix { };
+
+  Such a definition is provided automatically and therefore not necessary. Please remove it.
+
+- Because pkgs/by-name/on/onlyMove exists, the attribute `pkgs.onlyMove` must be defined like
+
+    onlyMove = callPackage ./pkgs/by-name/on/onlyMove/package.nix { /* ... */ };
+
+  However, in this PR, the second argument is empty. See the definition in all-packages.nix:7:
+
+    onlyMove = self.callPackage ./pkgs/by-name/on/onlyMove/package.nix { };
+
+  Such a definition is provided automatically and therefore not necessary. Please remove it.
+
+This PR introduces additional instances of discouraged patterns as listed above. Merging is discouraged but would not break the base branch.
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/manual-definition/pkgs/by-name/no/noEval/package.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/manual-definition/pkgs/by-name/no/noEval/package.nix
new file mode 100644
index 000000000000..a1b92efbbadb
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/manual-definition/pkgs/by-name/no/noEval/package.nix
@@ -0,0 +1 @@
+{ someDrv }: someDrv
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/manual-definition/pkgs/by-name/on/onlyMove/package.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/manual-definition/pkgs/by-name/on/onlyMove/package.nix
new file mode 100644
index 000000000000..a1b92efbbadb
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/manual-definition/pkgs/by-name/on/onlyMove/package.nix
@@ -0,0 +1 @@
+{ someDrv }: someDrv
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/missing-package-nix/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/missing-package-nix/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/missing-package-nix/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/missing-package-nix/expected b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/missing-package-nix/expected
new file mode 100644
index 000000000000..1b67704817cf
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/missing-package-nix/expected
@@ -0,0 +1,2 @@
+pkgs/by-name/fo/foo: Missing required "package.nix" file.
+This PR introduces the problems listed above. Please fix them before merging, otherwise the base branch would break.
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/missing-package-nix/pkgs/by-name/fo/foo/.git-keep b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/missing-package-nix/pkgs/by-name/fo/foo/.git-keep
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/missing-package-nix/pkgs/by-name/fo/foo/.git-keep
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/mock-nixpkgs.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/mock-nixpkgs.nix
new file mode 100644
index 000000000000..fbd51f656138
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/mock-nixpkgs.nix
@@ -0,0 +1,110 @@
+/*
+This file returns a mocked version of Nixpkgs' default.nix for testing purposes.
+It does not depend on Nixpkgs itself for the sake of simplicity.
+
+It takes one attribute as an argument:
+- `root`: The root of Nixpkgs to read other files from, including:
+  - `./pkgs/by-name`: The `pkgs/by-name` directory to test
+  - `./all-packages.nix`: A file containing an overlay to mirror the real `pkgs/top-level/all-packages.nix`.
+    This allows adding overrides on top of the auto-called packages in `pkgs/by-name`.
+
+It returns a Nixpkgs-like function that can be auto-called and evaluates to an attribute set.
+*/
+{
+  root,
+}:
+# The arguments for the Nixpkgs function
+{
+  # Passed by the checker to modify `callPackage`
+  overlays ? [],
+  # Passed by the checker to make sure a real Nixpkgs isn't influenced by impurities
+  config ? {},
+  # Passed by the checker to make sure a real Nixpkgs isn't influenced by impurities
+  system ? null,
+}:
+let
+
+  # Simplified versions of lib functions
+  lib = import <test-nixpkgs/lib>;
+
+  # The base fixed-point function to populate the resulting attribute set
+  pkgsFun =
+    self: {
+      inherit lib;
+      newScope = extra: lib.callPackageWith (self // extra);
+      callPackage = self.newScope { };
+      callPackages = lib.callPackagesWith self;
+    }
+    # This mapAttrs is a very hacky workaround necessary because for all attributes defined in Nixpkgs,
+    # the files that define them are verified to be within Nixpkgs.
+    # This is usually a very safe assumption, but it fails in these tests for someDrv,
+    # because it's technically defined outside the Nixpkgs directories of each test case.
+    # By using `mapAttrs`, `builtins.unsafeGetAttrPos` just returns `null`,
+    # which then doesn't trigger this check
+    // lib.mapAttrs (name: value: value) {
+      someDrv = { type = "derivation"; };
+    };
+
+  baseDirectory = root + "/pkgs/by-name";
+
+  # Generates { <name> = <file>; } entries mapping package names to their `package.nix` files in `pkgs/by-name`.
+  # Could be more efficient, but this is only for testing.
+  autoCalledPackageFiles =
+    let
+      entries = builtins.readDir baseDirectory;
+
+      namesForShard = shard:
+        if entries.${shard} != "directory" then
+          # Only README.md is allowed to be a file, but it's not this code's job to check for that
+          { }
+        else
+          builtins.mapAttrs
+            (name: _: baseDirectory + "/${shard}/${name}/package.nix")
+            (builtins.readDir (baseDirectory + "/${shard}"));
+
+    in
+    builtins.foldl'
+      (acc: el: acc // el)
+      { }
+      (map namesForShard (builtins.attrNames entries));
+
+  # Turns autoCalledPackageFiles into an overlay that `callPackage`'s all of them
+  autoCalledPackages = self: super:
+    {
+      # Needed to be able to detect empty arguments in all-packages.nix
+      # See a more detailed description in pkgs/top-level/by-name-overlay.nix
+      _internalCallByNamePackageFile = file: self.callPackage file { };
+    }
+    // builtins.mapAttrs
+      (name: self._internalCallByNamePackageFile)
+      autoCalledPackageFiles;
+
+  # A list optionally containing the `all-packages.nix` file from the test case as an overlay
+  optionalAllPackagesOverlay =
+    if builtins.pathExists (root + "/all-packages.nix") then
+      [ (import (root + "/all-packages.nix")) ]
+    else
+      [ ];
+
+  # A list optionally containing the `aliases.nix` file from the test case as an overlay
+  # But only if config.allowAliases is not false
+  optionalAliasesOverlay =
+    if (config.allowAliases or true) && builtins.pathExists (root + "/aliases.nix") then
+      [ (import (root + "/aliases.nix")) ]
+    else
+      [ ];
+
+  # All the overlays in the right order, including the user-supplied ones
+  allOverlays =
+    [
+      autoCalledPackages
+    ]
+    ++ optionalAllPackagesOverlay
+    ++ optionalAliasesOverlay
+    ++ overlays;
+
+  # Apply all the overlays in order to the base fixed-point function pkgsFun
+  f = builtins.foldl' (f: overlay: lib.extends overlay f) pkgsFun allOverlays;
+in
+# Evaluate the fixed-point
+lib.fix f
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/move-to-non-by-name/all-packages.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/move-to-non-by-name/all-packages.nix
new file mode 100644
index 000000000000..16834c4f7856
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/move-to-non-by-name/all-packages.nix
@@ -0,0 +1,10 @@
+self: super: {
+  foo1 = self.callPackage ({ someDrv }: someDrv) { };
+  foo2 = self.callPackage ./without-config.nix { };
+  foo3 = self.callPackage ({ someDrv, enableFoo }: someDrv) {
+    enableFoo = null;
+  };
+  foo4 = self.callPackage ./with-config.nix {
+    enableFoo = null;
+  };
+}
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/move-to-non-by-name/base/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/move-to-non-by-name/base/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/move-to-non-by-name/base/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/move-to-non-by-name/base/pkgs/by-name/fo/foo1/package.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/move-to-non-by-name/base/pkgs/by-name/fo/foo1/package.nix
new file mode 100644
index 000000000000..a1b92efbbadb
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/move-to-non-by-name/base/pkgs/by-name/fo/foo1/package.nix
@@ -0,0 +1 @@
+{ someDrv }: someDrv
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/move-to-non-by-name/base/pkgs/by-name/fo/foo2/package.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/move-to-non-by-name/base/pkgs/by-name/fo/foo2/package.nix
new file mode 100644
index 000000000000..a1b92efbbadb
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/move-to-non-by-name/base/pkgs/by-name/fo/foo2/package.nix
@@ -0,0 +1 @@
+{ someDrv }: someDrv
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/move-to-non-by-name/base/pkgs/by-name/fo/foo3/package.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/move-to-non-by-name/base/pkgs/by-name/fo/foo3/package.nix
new file mode 100644
index 000000000000..a1b92efbbadb
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/move-to-non-by-name/base/pkgs/by-name/fo/foo3/package.nix
@@ -0,0 +1 @@
+{ someDrv }: someDrv
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/move-to-non-by-name/base/pkgs/by-name/fo/foo4/package.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/move-to-non-by-name/base/pkgs/by-name/fo/foo4/package.nix
new file mode 100644
index 000000000000..a1b92efbbadb
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/move-to-non-by-name/base/pkgs/by-name/fo/foo4/package.nix
@@ -0,0 +1 @@
+{ someDrv }: someDrv
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/move-to-non-by-name/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/move-to-non-by-name/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/move-to-non-by-name/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/move-to-non-by-name/expected b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/move-to-non-by-name/expected
new file mode 100644
index 000000000000..123e24daab8a
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/move-to-non-by-name/expected
@@ -0,0 +1,13 @@
+- Attribute `pkgs.foo1` was previously defined in pkgs/by-name/fo/foo1/package.nix, but is now manually defined as `callPackage ... { /* ... */ }` in all-packages.nix.
+  Please move the package back and remove the manual `callPackage`.
+
+- Attribute `pkgs.foo2` was previously defined in pkgs/by-name/fo/foo2/package.nix, but is now manually defined as `callPackage ./without-config.nix { /* ... */ }` in all-packages.nix.
+  Please move the package back and remove the manual `callPackage`.
+
+- Attribute `pkgs.foo3` was previously defined in pkgs/by-name/fo/foo3/package.nix, but is now manually defined as `callPackage ... { ... }` in all-packages.nix.
+  While the manual `callPackage` is still needed, it's not necessary to move the package files.
+
+- Attribute `pkgs.foo4` was previously defined in pkgs/by-name/fo/foo4/package.nix, but is now manually defined as `callPackage ./with-config.nix { ... }` in all-packages.nix.
+  While the manual `callPackage` is still needed, it's not necessary to move the package files.
+
+This PR introduces additional instances of discouraged patterns as listed above. Merging is discouraged but would not break the base branch.
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/move-to-non-by-name/pkgs/by-name/README.md b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/move-to-non-by-name/pkgs/by-name/README.md
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/move-to-non-by-name/pkgs/by-name/README.md
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/move-to-non-by-name/with-config.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/move-to-non-by-name/with-config.nix
new file mode 100644
index 000000000000..7cca53882ea5
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/move-to-non-by-name/with-config.nix
@@ -0,0 +1 @@
+{ someDrv, enableFoo }: someDrv
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/move-to-non-by-name/without-config.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/move-to-non-by-name/without-config.nix
new file mode 100644
index 000000000000..a1b92efbbadb
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/move-to-non-by-name/without-config.nix
@@ -0,0 +1 @@
+{ someDrv }: someDrv
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/multiple-failures/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/multiple-failures/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/multiple-failures/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/multiple-failures/expected b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/multiple-failures/expected
new file mode 100644
index 000000000000..0105b19078c7
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/multiple-failures/expected
@@ -0,0 +1,14 @@
+pkgs/by-name/A: Invalid directory name "A", must be at most 2 ASCII characters consisting of a-z, 0-9, "-" or "_".
+pkgs/by-name/A/fo@: Invalid package directory name "fo@", must be ASCII characters consisting of a-z, A-Z, 0-9, "-" or "_".
+pkgs/by-name/A/fo@: Path foo is a symlink which cannot be resolved: No such file or directory (os error 2).
+pkgs/by-name/A/fo@: Path package.nix is a symlink pointing to a path outside the directory of that package.
+pkgs/by-name/aa: This is a file, but it should be a directory.
+pkgs/by-name/ba/bar: This path is a file, but it should be a directory.
+pkgs/by-name/ba/baz: "package.nix" must be a file.
+pkgs/by-name/ba/foo: Incorrect directory location, should be pkgs/by-name/fo/foo instead.
+pkgs/by-name/ba/foo: File package.nix at line 2 contains the path expression "/bar" which cannot be resolved: No such file or directory (os error 2).
+pkgs/by-name/ba/foo: File package.nix at line 3 contains the path expression "../." which may point outside the directory of that package.
+pkgs/by-name/ba/foo: File package.nix at line 4 contains the nix search path expression "<nixpkgs>" which may point outside the directory of that package.
+pkgs/by-name/ba/foo: File package.nix at line 5 contains the path expression "./${"test"}", which is not yet supported and may point outside the directory of that package.
+pkgs/by-name/fo/foo: Missing required "package.nix" file.
+This PR introduces the problems listed above. Please fix them before merging, otherwise the base branch would break.
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/multiple-failures/pkgs/by-name/A/fo@/foo b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/multiple-failures/pkgs/by-name/A/fo@/foo
new file mode 120000
index 000000000000..c86c3f3551ec
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/multiple-failures/pkgs/by-name/A/fo@/foo
@@ -0,0 +1 @@
+none
\ No newline at end of file
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/multiple-failures/pkgs/by-name/A/fo@/package.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/multiple-failures/pkgs/by-name/A/fo@/package.nix
new file mode 120000
index 000000000000..f079163d158a
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/multiple-failures/pkgs/by-name/A/fo@/package.nix
@@ -0,0 +1 @@
+../../../../someDrv.nix
\ No newline at end of file
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/multiple-failures/pkgs/by-name/aa b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/multiple-failures/pkgs/by-name/aa
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/multiple-failures/pkgs/by-name/aa
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/multiple-failures/pkgs/by-name/ba/bar b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/multiple-failures/pkgs/by-name/ba/bar
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/multiple-failures/pkgs/by-name/ba/bar
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/multiple-failures/pkgs/by-name/ba/baz/package.nix/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/multiple-failures/pkgs/by-name/ba/baz/package.nix/default.nix
new file mode 100644
index 000000000000..a1b92efbbadb
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/multiple-failures/pkgs/by-name/ba/baz/package.nix/default.nix
@@ -0,0 +1 @@
+{ someDrv }: someDrv
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/multiple-failures/pkgs/by-name/ba/foo/package.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/multiple-failures/pkgs/by-name/ba/foo/package.nix
new file mode 100644
index 000000000000..31b4742f5914
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/multiple-failures/pkgs/by-name/ba/foo/package.nix
@@ -0,0 +1,6 @@
+{ someDrv }: someDrv // {
+  escapeAbsolute = /bar;
+  escapeRelative = ../.;
+  nixPath = <nixpkgs>;
+  pathWithSubexpr = ./${"test"};
+}
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/multiple-failures/pkgs/by-name/fo/foo/.git-keep b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/multiple-failures/pkgs/by-name/fo/foo/.git-keep
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/multiple-failures/pkgs/by-name/fo/foo/.git-keep
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/multiple-failures/someDrv.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/multiple-failures/someDrv.nix
new file mode 100644
index 000000000000..a1b92efbbadb
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/multiple-failures/someDrv.nix
@@ -0,0 +1 @@
+{ someDrv }: someDrv
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/new-package-non-by-name/all-packages.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/new-package-non-by-name/all-packages.nix
new file mode 100644
index 000000000000..069119ad4c7b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/new-package-non-by-name/all-packages.nix
@@ -0,0 +1,11 @@
+self: super: {
+  before = self.callPackage ({ someDrv }: someDrv) { };
+  new1 = self.callPackage ({ someDrv }: someDrv) { };
+  new2 = self.callPackage ./without-config.nix { };
+  new3 = self.callPackage ({ someDrv, enableNew }: someDrv) {
+    enableNew = null;
+  };
+  new4 = self.callPackage ./with-config.nix {
+    enableNew = null;
+  };
+}
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/new-package-non-by-name/base/all-packages.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/new-package-non-by-name/base/all-packages.nix
new file mode 100644
index 000000000000..c2665d04d5f2
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/new-package-non-by-name/base/all-packages.nix
@@ -0,0 +1,5 @@
+self: super: {
+
+  before = self.callPackage ({ someDrv }: someDrv) { };
+
+}
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/new-package-non-by-name/base/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/new-package-non-by-name/base/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/new-package-non-by-name/base/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/new-package-non-by-name/base/pkgs/by-name/README.md b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/new-package-non-by-name/base/pkgs/by-name/README.md
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/new-package-non-by-name/base/pkgs/by-name/README.md
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/new-package-non-by-name/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/new-package-non-by-name/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/new-package-non-by-name/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/new-package-non-by-name/expected b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/new-package-non-by-name/expected
new file mode 100644
index 000000000000..92668a231b48
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/new-package-non-by-name/expected
@@ -0,0 +1,21 @@
+- Attribute `pkgs.new1` is a new top-level package using `pkgs.callPackage ... { /* ... */ }`.
+  Please define it in pkgs/by-name/ne/new1/package.nix instead.
+  See `pkgs/by-name/README.md` for more details.
+  Since the second `callPackage` argument is `{ }`, no manual `callPackage` in all-packages.nix is needed anymore.
+
+- Attribute `pkgs.new2` is a new top-level package using `pkgs.callPackage ./without-config.nix { /* ... */ }`.
+  Please define it in pkgs/by-name/ne/new2/package.nix instead.
+  See `pkgs/by-name/README.md` for more details.
+  Since the second `callPackage` argument is `{ }`, no manual `callPackage` in all-packages.nix is needed anymore.
+
+- Attribute `pkgs.new3` is a new top-level package using `pkgs.callPackage ... { /* ... */ }`.
+  Please define it in pkgs/by-name/ne/new3/package.nix instead.
+  See `pkgs/by-name/README.md` for more details.
+  Since the second `callPackage` argument is not `{ }`, the manual `callPackage` in all-packages.nix is still needed.
+
+- Attribute `pkgs.new4` is a new top-level package using `pkgs.callPackage ./with-config.nix { /* ... */ }`.
+  Please define it in pkgs/by-name/ne/new4/package.nix instead.
+  See `pkgs/by-name/README.md` for more details.
+  Since the second `callPackage` argument is not `{ }`, the manual `callPackage` in all-packages.nix is still needed.
+
+This PR introduces additional instances of discouraged patterns as listed above. Merging is discouraged but would not break the base branch.
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/new-package-non-by-name/pkgs/by-name/README.md b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/new-package-non-by-name/pkgs/by-name/README.md
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/new-package-non-by-name/pkgs/by-name/README.md
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/new-package-non-by-name/with-config.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/new-package-non-by-name/with-config.nix
new file mode 100644
index 000000000000..65bcbf9928a2
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/new-package-non-by-name/with-config.nix
@@ -0,0 +1 @@
+{ someDrv, enableNew }: someDrv
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/new-package-non-by-name/without-config.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/new-package-non-by-name/without-config.nix
new file mode 100644
index 000000000000..a1b92efbbadb
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/new-package-non-by-name/without-config.nix
@@ -0,0 +1 @@
+{ someDrv }: someDrv
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/no-by-name/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/no-by-name/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/no-by-name/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/no-by-name/expected b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/no-by-name/expected
new file mode 100644
index 000000000000..defae2634c34
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/no-by-name/expected
@@ -0,0 +1 @@
+Validated successfully
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/no-eval/all-packages.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/no-eval/all-packages.nix
new file mode 100644
index 000000000000..38762c6de1cc
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/no-eval/all-packages.nix
@@ -0,0 +1,5 @@
+self: super: {
+  iDontEval = throw "I don't eval";
+
+  futureEval = self.callPackage ({ someDrv }: someDrv) { };
+}
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/no-eval/base/all-packages.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/no-eval/base/all-packages.nix
new file mode 100644
index 000000000000..ac763b454eb0
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/no-eval/base/all-packages.nix
@@ -0,0 +1,3 @@
+self: super: {
+  futureEval = throw "foo";
+}
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/no-eval/base/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/no-eval/base/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/no-eval/base/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/no-eval/base/pkgs/by-name/README.md b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/no-eval/base/pkgs/by-name/README.md
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/no-eval/base/pkgs/by-name/README.md
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/no-eval/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/no-eval/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/no-eval/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/no-eval/expected b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/no-eval/expected
new file mode 100644
index 000000000000..defae2634c34
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/no-eval/expected
@@ -0,0 +1 @@
+Validated successfully
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/no-eval/pkgs/by-name/README.md b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/no-eval/pkgs/by-name/README.md
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/no-eval/pkgs/by-name/README.md
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/non-attrs/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/non-attrs/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/non-attrs/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/non-attrs/expected b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/non-attrs/expected
new file mode 100644
index 000000000000..1705d22be798
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/non-attrs/expected
@@ -0,0 +1,2 @@
+pkgs.nonDerivation: This attribute defined by pkgs/by-name/no/nonDerivation/package.nix is not a derivation
+This PR introduces the problems listed above. Please fix them before merging, otherwise the base branch would break.
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/non-attrs/pkgs/by-name/no/nonDerivation/package.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/non-attrs/pkgs/by-name/no/nonDerivation/package.nix
new file mode 100644
index 000000000000..bd68dba1ded5
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/non-attrs/pkgs/by-name/no/nonDerivation/package.nix
@@ -0,0 +1 @@
+{ }: null
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/non-derivation/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/non-derivation/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/non-derivation/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/non-derivation/expected b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/non-derivation/expected
new file mode 100644
index 000000000000..1705d22be798
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/non-derivation/expected
@@ -0,0 +1,2 @@
+pkgs.nonDerivation: This attribute defined by pkgs/by-name/no/nonDerivation/package.nix is not a derivation
+This PR introduces the problems listed above. Please fix them before merging, otherwise the base branch would break.
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/non-derivation/pkgs/by-name/no/nonDerivation/package.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/non-derivation/pkgs/by-name/no/nonDerivation/package.nix
new file mode 100644
index 000000000000..b021e28c2145
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/non-derivation/pkgs/by-name/no/nonDerivation/package.nix
@@ -0,0 +1 @@
+{ }: { }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/non-syntactical-callPackage-by-name/all-packages.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/non-syntactical-callPackage-by-name/all-packages.nix
new file mode 100644
index 000000000000..3e0ea20c2281
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/non-syntactical-callPackage-by-name/all-packages.nix
@@ -0,0 +1,6 @@
+self: super: {
+
+  bar = (x: x) self.callPackage ./pkgs/by-name/fo/foo/package.nix { someFlag = true; };
+  foo = self.bar;
+
+}
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/non-syntactical-callPackage-by-name/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/non-syntactical-callPackage-by-name/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/non-syntactical-callPackage-by-name/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/non-syntactical-callPackage-by-name/expected b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/non-syntactical-callPackage-by-name/expected
new file mode 100644
index 000000000000..e09e931bb658
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/non-syntactical-callPackage-by-name/expected
@@ -0,0 +1,9 @@
+- Because pkgs/by-name/fo/foo exists, the attribute `pkgs.foo` must be defined like
+
+    foo = callPackage ./pkgs/by-name/fo/foo/package.nix { /* ... */ };
+
+  However, in this PR, it isn't defined that way. See the definition in all-packages.nix:4
+
+    foo = self.bar;
+
+This PR introduces the problems listed above. Please fix them before merging, otherwise the base branch would break.
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/non-syntactical-callPackage-by-name/pkgs/by-name/fo/foo/package.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/non-syntactical-callPackage-by-name/pkgs/by-name/fo/foo/package.nix
new file mode 100644
index 000000000000..5ad6ea5e24d6
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/non-syntactical-callPackage-by-name/pkgs/by-name/fo/foo/package.nix
@@ -0,0 +1 @@
+{ someDrv, someFlag }: someDrv
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/one-letter/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/one-letter/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/one-letter/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/one-letter/expected b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/one-letter/expected
new file mode 100644
index 000000000000..defae2634c34
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/one-letter/expected
@@ -0,0 +1 @@
+Validated successfully
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/one-letter/pkgs/by-name/a/a/package.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/one-letter/pkgs/by-name/a/a/package.nix
new file mode 100644
index 000000000000..a1b92efbbadb
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/one-letter/pkgs/by-name/a/a/package.nix
@@ -0,0 +1 @@
+{ someDrv }: someDrv
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/only-callPackage-derivations/all-packages.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/only-callPackage-derivations/all-packages.nix
new file mode 100644
index 000000000000..5b1ed9d2ccda
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/only-callPackage-derivations/all-packages.nix
@@ -0,0 +1,16 @@
+self: super: {
+  alternateCallPackage = self.myScope.callPackage ({ myScopeValue, someDrv }:
+    assert myScopeValue;
+    someDrv
+  ) { };
+
+  myScope = self.lib.makeScope self.newScope (self: {
+    myScopeValue = true;
+  });
+
+  myPackages = self.callPackages ({ someDrv }: {
+    a = someDrv;
+    b = someDrv;
+  }) { };
+  inherit (self.myPackages) a b;
+}
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/only-callPackage-derivations/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/only-callPackage-derivations/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/only-callPackage-derivations/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/only-callPackage-derivations/expected b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/only-callPackage-derivations/expected
new file mode 100644
index 000000000000..defae2634c34
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/only-callPackage-derivations/expected
@@ -0,0 +1 @@
+Validated successfully
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/only-callPackage-derivations/pkgs/by-name/README.md b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/only-callPackage-derivations/pkgs/by-name/README.md
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/only-callPackage-derivations/pkgs/by-name/README.md
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-different-file/all-packages.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-different-file/all-packages.nix
new file mode 100644
index 000000000000..8bedb90d89a7
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-different-file/all-packages.nix
@@ -0,0 +1,3 @@
+self: super: {
+  nonDerivation = self.callPackage ./someDrv.nix { };
+}
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-different-file/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-different-file/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-different-file/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-different-file/expected b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-different-file/expected
new file mode 100644
index 000000000000..16292c0c0eb1
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-different-file/expected
@@ -0,0 +1,9 @@
+- Because pkgs/by-name/no/nonDerivation exists, the attribute `pkgs.nonDerivation` must be defined like
+
+    nonDerivation = callPackage ./pkgs/by-name/no/nonDerivation/package.nix { /* ... */ };
+
+  However, in this PR, the first `callPackage` argument is the wrong path. See the definition in all-packages.nix:2:
+
+    nonDerivation = callPackage ./someDrv.nix { /* ... */ };
+
+This PR introduces the problems listed above. Please fix them before merging, otherwise the base branch would break.
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-different-file/pkgs/by-name/no/nonDerivation/package.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-different-file/pkgs/by-name/no/nonDerivation/package.nix
new file mode 100644
index 000000000000..bd68dba1ded5
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-different-file/pkgs/by-name/no/nonDerivation/package.nix
@@ -0,0 +1 @@
+{ }: null
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-different-file/someDrv.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-different-file/someDrv.nix
new file mode 100644
index 000000000000..a1b92efbbadb
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-different-file/someDrv.nix
@@ -0,0 +1 @@
+{ someDrv }: someDrv
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg-gradual/all-packages.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg-gradual/all-packages.nix
new file mode 100644
index 000000000000..d369dd7228dc
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg-gradual/all-packages.nix
@@ -0,0 +1,3 @@
+self: super: {
+  nonDerivation = self.callPackage ./pkgs/by-name/no/nonDerivation/package.nix { };
+}
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg-gradual/base/all-packages.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg-gradual/base/all-packages.nix
new file mode 100644
index 000000000000..d369dd7228dc
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg-gradual/base/all-packages.nix
@@ -0,0 +1,3 @@
+self: super: {
+  nonDerivation = self.callPackage ./pkgs/by-name/no/nonDerivation/package.nix { };
+}
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg-gradual/base/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg-gradual/base/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg-gradual/base/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg-gradual/base/pkgs/by-name/no/nonDerivation/package.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg-gradual/base/pkgs/by-name/no/nonDerivation/package.nix
new file mode 100644
index 000000000000..a1b92efbbadb
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg-gradual/base/pkgs/by-name/no/nonDerivation/package.nix
@@ -0,0 +1 @@
+{ someDrv }: someDrv
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg-gradual/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg-gradual/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg-gradual/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg-gradual/expected b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg-gradual/expected
new file mode 100644
index 000000000000..defae2634c34
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg-gradual/expected
@@ -0,0 +1 @@
+Validated successfully
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg-gradual/pkgs/by-name/no/nonDerivation/package.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg-gradual/pkgs/by-name/no/nonDerivation/package.nix
new file mode 100644
index 000000000000..a1b92efbbadb
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg-gradual/pkgs/by-name/no/nonDerivation/package.nix
@@ -0,0 +1 @@
+{ someDrv }: someDrv
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg/all-packages.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg/all-packages.nix
new file mode 100644
index 000000000000..d369dd7228dc
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg/all-packages.nix
@@ -0,0 +1,3 @@
+self: super: {
+  nonDerivation = self.callPackage ./pkgs/by-name/no/nonDerivation/package.nix { };
+}
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg/base/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg/base/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg/base/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg/base/pkgs/by-name/README.md b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg/base/pkgs/by-name/README.md
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg/base/pkgs/by-name/README.md
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg/expected b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg/expected
new file mode 100644
index 000000000000..f3306aadbbb7
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg/expected
@@ -0,0 +1,11 @@
+- Because pkgs/by-name/no/nonDerivation exists, the attribute `pkgs.nonDerivation` must be defined like
+
+    nonDerivation = callPackage ./pkgs/by-name/no/nonDerivation/package.nix { /* ... */ };
+
+  However, in this PR, the second argument is empty. See the definition in all-packages.nix:2:
+
+    nonDerivation = self.callPackage ./pkgs/by-name/no/nonDerivation/package.nix { };
+
+  Such a definition is provided automatically and therefore not necessary. Please remove it.
+
+This PR introduces additional instances of discouraged patterns as listed above. Merging is discouraged but would not break the base branch.
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg/pkgs/by-name/no/nonDerivation/package.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg/pkgs/by-name/no/nonDerivation/package.nix
new file mode 100644
index 000000000000..a1b92efbbadb
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-empty-arg/pkgs/by-name/no/nonDerivation/package.nix
@@ -0,0 +1 @@
+{ someDrv }: someDrv
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-no-call-package/all-packages.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-no-call-package/all-packages.nix
new file mode 100644
index 000000000000..853c3a87db56
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-no-call-package/all-packages.nix
@@ -0,0 +1,3 @@
+self: super: {
+  nonDerivation = self.someDrv;
+}
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-no-call-package/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-no-call-package/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-no-call-package/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-no-call-package/expected b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-no-call-package/expected
new file mode 100644
index 000000000000..d91d58d629f2
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-no-call-package/expected
@@ -0,0 +1,9 @@
+- Because pkgs/by-name/no/nonDerivation exists, the attribute `pkgs.nonDerivation` must be defined like
+
+    nonDerivation = callPackage ./pkgs/by-name/no/nonDerivation/package.nix { /* ... */ };
+
+  However, in this PR, it isn't defined that way. See the definition in all-packages.nix:2
+
+    nonDerivation = self.someDrv;
+
+This PR introduces the problems listed above. Please fix them before merging, otherwise the base branch would break.
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-no-call-package/pkgs/by-name/no/nonDerivation/package.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-no-call-package/pkgs/by-name/no/nonDerivation/package.nix
new file mode 100644
index 000000000000..bd68dba1ded5
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-no-call-package/pkgs/by-name/no/nonDerivation/package.nix
@@ -0,0 +1 @@
+{ }: null
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-no-file/all-packages.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-no-file/all-packages.nix
new file mode 100644
index 000000000000..dc07f69b40ee
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-no-file/all-packages.nix
@@ -0,0 +1,3 @@
+self: super: {
+  nonDerivation = self.callPackage ({ someDrv }: someDrv) { };
+}
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-no-file/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-no-file/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-no-file/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-no-file/expected b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-no-file/expected
new file mode 100644
index 000000000000..807c440dd3d2
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-no-file/expected
@@ -0,0 +1,9 @@
+- Because pkgs/by-name/no/nonDerivation exists, the attribute `pkgs.nonDerivation` must be defined like
+
+    nonDerivation = callPackage ./pkgs/by-name/no/nonDerivation/package.nix { /* ... */ };
+
+  However, in this PR, the first `callPackage` argument is not a path. See the definition in all-packages.nix:2:
+
+    nonDerivation = self.callPackage ({ someDrv }: someDrv) { };
+
+This PR introduces the problems listed above. Please fix them before merging, otherwise the base branch would break.
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-no-file/pkgs/by-name/no/nonDerivation/package.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-no-file/pkgs/by-name/no/nonDerivation/package.nix
new file mode 100644
index 000000000000..bd68dba1ded5
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-no-file/pkgs/by-name/no/nonDerivation/package.nix
@@ -0,0 +1 @@
+{ }: null
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-non-path/all-packages.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-non-path/all-packages.nix
new file mode 100644
index 000000000000..f07e7a42744a
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-non-path/all-packages.nix
@@ -0,0 +1,5 @@
+self: super: {
+
+  foo = self.callPackage ({ someDrv, someFlag }: someDrv) { someFlag = true; };
+
+}
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-non-path/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-non-path/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-non-path/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-non-path/expected b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-non-path/expected
new file mode 100644
index 000000000000..4adbaf66edc0
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-non-path/expected
@@ -0,0 +1,9 @@
+- Because pkgs/by-name/fo/foo exists, the attribute `pkgs.foo` must be defined like
+
+    foo = callPackage ./pkgs/by-name/fo/foo/package.nix { /* ... */ };
+
+  However, in this PR, the first `callPackage` argument is not a path. See the definition in all-packages.nix:3:
+
+    foo = self.callPackage ({ someDrv, someFlag }: someDrv) { someFlag = true; };
+
+This PR introduces the problems listed above. Please fix them before merging, otherwise the base branch would break.
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-non-path/pkgs/by-name/fo/foo/package.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-non-path/pkgs/by-name/fo/foo/package.nix
new file mode 100644
index 000000000000..a1b92efbbadb
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-non-path/pkgs/by-name/fo/foo/package.nix
@@ -0,0 +1 @@
+{ someDrv }: someDrv
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-success/all-packages.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-success/all-packages.nix
new file mode 100644
index 000000000000..6b323756ae41
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-success/all-packages.nix
@@ -0,0 +1,5 @@
+self: super: {
+  foo = self.callPackage ./pkgs/by-name/fo/foo/package.nix {
+    enableBar = true;
+  };
+}
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-success/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-success/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-success/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-success/expected b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-success/expected
new file mode 100644
index 000000000000..defae2634c34
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-success/expected
@@ -0,0 +1 @@
+Validated successfully
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-success/pkgs/by-name/fo/foo/package.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-success/pkgs/by-name/fo/foo/package.nix
new file mode 100644
index 000000000000..c811a7215a28
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/override-success/pkgs/by-name/fo/foo/package.nix
@@ -0,0 +1,8 @@
+{
+  someDrv,
+  enableBar ? false,
+}:
+if enableBar then
+  someDrv
+else
+  {}
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-dir-is-file/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-dir-is-file/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-dir-is-file/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-dir-is-file/expected b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-dir-is-file/expected
new file mode 100644
index 000000000000..adee1d0137fa
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-dir-is-file/expected
@@ -0,0 +1,2 @@
+pkgs/by-name/fo/foo: This path is a file, but it should be a directory.
+This PR introduces the problems listed above. Please fix them before merging, otherwise the base branch would break.
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-dir-is-file/pkgs/by-name/fo/foo b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-dir-is-file/pkgs/by-name/fo/foo
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-dir-is-file/pkgs/by-name/fo/foo
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-nix-dir/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-nix-dir/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-nix-dir/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-nix-dir/expected b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-nix-dir/expected
new file mode 100644
index 000000000000..d03e1eceea26
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-nix-dir/expected
@@ -0,0 +1,2 @@
+pkgs/by-name/fo/foo: "package.nix" must be a file.
+This PR introduces the problems listed above. Please fix them before merging, otherwise the base branch would break.
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-nix-dir/pkgs/by-name/fo/foo/package.nix/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-nix-dir/pkgs/by-name/fo/foo/package.nix/default.nix
new file mode 100644
index 000000000000..a1b92efbbadb
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-nix-dir/pkgs/by-name/fo/foo/package.nix/default.nix
@@ -0,0 +1 @@
+{ someDrv }: someDrv
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-variants/all-packages.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-variants/all-packages.nix
new file mode 100644
index 000000000000..85f8c6138c5c
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-variants/all-packages.nix
@@ -0,0 +1,5 @@
+self: super: {
+  foo-variant-unvarianted = self.callPackage ./package.nix { };
+
+  foo-variant-new = self.callPackage ./pkgs/by-name/fo/foo/package.nix { };
+}
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-variants/base/all-packages.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-variants/base/all-packages.nix
new file mode 100644
index 000000000000..734604360073
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-variants/base/all-packages.nix
@@ -0,0 +1,3 @@
+self: super: {
+  foo-variant-unvarianted = self.callPackage ./pkgs/by-name/fo/foo/package.nix { };
+}
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-variants/base/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-variants/base/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-variants/base/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-variants/base/pkgs/by-name/fo/foo/package.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-variants/base/pkgs/by-name/fo/foo/package.nix
new file mode 100644
index 000000000000..a1b92efbbadb
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-variants/base/pkgs/by-name/fo/foo/package.nix
@@ -0,0 +1 @@
+{ someDrv }: someDrv
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-variants/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-variants/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-variants/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-variants/expected b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-variants/expected
new file mode 100644
index 000000000000..defae2634c34
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-variants/expected
@@ -0,0 +1 @@
+Validated successfully
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-variants/package.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-variants/package.nix
new file mode 100644
index 000000000000..a1b92efbbadb
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-variants/package.nix
@@ -0,0 +1 @@
+{ someDrv }: someDrv
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-variants/pkgs/by-name/fo/foo/package.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-variants/pkgs/by-name/fo/foo/package.nix
new file mode 100644
index 000000000000..a1b92efbbadb
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/package-variants/pkgs/by-name/fo/foo/package.nix
@@ -0,0 +1 @@
+{ someDrv }: someDrv
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-absolute/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-absolute/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-absolute/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-absolute/expected b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-absolute/expected
new file mode 100644
index 000000000000..0bdb00f20cb9
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-absolute/expected
@@ -0,0 +1,2 @@
+pkgs/by-name/aa/aa: File package.nix at line 2 contains the path expression "/foo" which cannot be resolved: No such file or directory (os error 2).
+This PR introduces the problems listed above. Please fix them before merging, otherwise the base branch would break.
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-absolute/pkgs/by-name/aa/aa/package.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-absolute/pkgs/by-name/aa/aa/package.nix
new file mode 100644
index 000000000000..7a51ba1ec719
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-absolute/pkgs/by-name/aa/aa/package.nix
@@ -0,0 +1,3 @@
+{ someDrv }: someDrv // {
+  escape = /foo;
+}
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-escape/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-escape/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-escape/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-escape/expected b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-escape/expected
new file mode 100644
index 000000000000..2e4338ccc7ae
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-escape/expected
@@ -0,0 +1,2 @@
+pkgs/by-name/aa/aa: File package.nix at line 2 contains the path expression "../." which may point outside the directory of that package.
+This PR introduces the problems listed above. Please fix them before merging, otherwise the base branch would break.
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-escape/pkgs/by-name/aa/aa/package.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-escape/pkgs/by-name/aa/aa/package.nix
new file mode 100644
index 000000000000..5989f52eb899
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-escape/pkgs/by-name/aa/aa/package.nix
@@ -0,0 +1,3 @@
+{ someDrv }: someDrv // {
+  escape = ../.;
+}
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-nix-path/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-nix-path/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-nix-path/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-nix-path/expected b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-nix-path/expected
new file mode 100644
index 000000000000..30125570794a
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-nix-path/expected
@@ -0,0 +1,2 @@
+pkgs/by-name/aa/aa: File package.nix at line 2 contains the nix search path expression "<nixpkgs>" which may point outside the directory of that package.
+This PR introduces the problems listed above. Please fix them before merging, otherwise the base branch would break.
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-nix-path/pkgs/by-name/aa/aa/package.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-nix-path/pkgs/by-name/aa/aa/package.nix
new file mode 100644
index 000000000000..864fdce13319
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-nix-path/pkgs/by-name/aa/aa/package.nix
@@ -0,0 +1,3 @@
+{ someDrv }: someDrv // {
+  nixPath = <nixpkgs>;
+}
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-path-subexpr/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-path-subexpr/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-path-subexpr/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-path-subexpr/expected b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-path-subexpr/expected
new file mode 100644
index 000000000000..6567439b77f8
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-path-subexpr/expected
@@ -0,0 +1,2 @@
+pkgs/by-name/aa/aa: File package.nix at line 2 contains the path expression "./${"test"}", which is not yet supported and may point outside the directory of that package.
+This PR introduces the problems listed above. Please fix them before merging, otherwise the base branch would break.
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-path-subexpr/pkgs/by-name/aa/aa/package.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-path-subexpr/pkgs/by-name/aa/aa/package.nix
new file mode 100644
index 000000000000..a94ba7541263
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-path-subexpr/pkgs/by-name/aa/aa/package.nix
@@ -0,0 +1,3 @@
+{ someDrv }: someDrv // {
+  pathWithSubexpr = ./${"test"};
+}
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-success/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-success/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-success/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-success/expected b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-success/expected
new file mode 100644
index 000000000000..defae2634c34
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-success/expected
@@ -0,0 +1 @@
+Validated successfully
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-success/pkgs/by-name/aa/aa/dir/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-success/pkgs/by-name/aa/aa/dir/default.nix
new file mode 100644
index 000000000000..7e4a7548fec7
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-success/pkgs/by-name/aa/aa/dir/default.nix
@@ -0,0 +1,2 @@
+# Recursive
+../package.nix
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-success/pkgs/by-name/aa/aa/file b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-success/pkgs/by-name/aa/aa/file
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-success/pkgs/by-name/aa/aa/file
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-success/pkgs/by-name/aa/aa/file.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-success/pkgs/by-name/aa/aa/file.nix
new file mode 100644
index 000000000000..bd55e601bf64
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-success/pkgs/by-name/aa/aa/file.nix
@@ -0,0 +1,2 @@
+# Recursive test
+import ./file.nix
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-success/pkgs/by-name/aa/aa/package.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-success/pkgs/by-name/aa/aa/package.nix
new file mode 100644
index 000000000000..474db5b0ebfc
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/ref-success/pkgs/by-name/aa/aa/package.nix
@@ -0,0 +1,5 @@
+{ someDrv }: someDrv // {
+  nixFile = ./file.nix;
+  nonNixFile = ./file;
+  directory = ./dir;
+}
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/shard-file/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/shard-file/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/shard-file/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/shard-file/expected b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/shard-file/expected
new file mode 100644
index 000000000000..689cee41f1e3
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/shard-file/expected
@@ -0,0 +1,2 @@
+pkgs/by-name/fo: This is a file, but it should be a directory.
+This PR introduces the problems listed above. Please fix them before merging, otherwise the base branch would break.
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/shard-file/pkgs/by-name/fo b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/shard-file/pkgs/by-name/fo
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/shard-file/pkgs/by-name/fo
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/sorted-order/all-packages.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/sorted-order/all-packages.nix
new file mode 100644
index 000000000000..688f52b9358f
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/sorted-order/all-packages.nix
@@ -0,0 +1,6 @@
+self: super: {
+  a = self.callPackage ./pkgs/by-name/a/a/package.nix { };
+  b = self.callPackage ({ someDrv }: someDrv) { };
+  c = self.callPackage ./pkgs/by-name/c/c/package.nix { };
+  d = self.callPackage ({ someDrv }: someDrv) { };
+}
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/sorted-order/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/sorted-order/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/sorted-order/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/sorted-order/expected b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/sorted-order/expected
new file mode 100644
index 000000000000..8a8104b73720
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/sorted-order/expected
@@ -0,0 +1,31 @@
+- Because pkgs/by-name/a/a exists, the attribute `pkgs.a` must be defined like
+
+    a = callPackage ./pkgs/by-name/a/a/package.nix { /* ... */ };
+
+  However, in this PR, the second argument is empty. See the definition in all-packages.nix:2:
+
+    a = self.callPackage ./pkgs/by-name/a/a/package.nix { };
+
+  Such a definition is provided automatically and therefore not necessary. Please remove it.
+
+- Attribute `pkgs.b` is a new top-level package using `pkgs.callPackage ... { /* ... */ }`.
+  Please define it in pkgs/by-name/b/b/package.nix instead.
+  See `pkgs/by-name/README.md` for more details.
+  Since the second `callPackage` argument is `{ }`, no manual `callPackage` in all-packages.nix is needed anymore.
+
+- Because pkgs/by-name/c/c exists, the attribute `pkgs.c` must be defined like
+
+    c = callPackage ./pkgs/by-name/c/c/package.nix { /* ... */ };
+
+  However, in this PR, the second argument is empty. See the definition in all-packages.nix:4:
+
+    c = self.callPackage ./pkgs/by-name/c/c/package.nix { };
+
+  Such a definition is provided automatically and therefore not necessary. Please remove it.
+
+- Attribute `pkgs.d` is a new top-level package using `pkgs.callPackage ... { /* ... */ }`.
+  Please define it in pkgs/by-name/d/d/package.nix instead.
+  See `pkgs/by-name/README.md` for more details.
+  Since the second `callPackage` argument is `{ }`, no manual `callPackage` in all-packages.nix is needed anymore.
+
+This PR introduces additional instances of discouraged patterns as listed above. Merging is discouraged but would not break the base branch.
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/sorted-order/pkgs/by-name/a/a/package.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/sorted-order/pkgs/by-name/a/a/package.nix
new file mode 100644
index 000000000000..a1b92efbbadb
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/sorted-order/pkgs/by-name/a/a/package.nix
@@ -0,0 +1 @@
+{ someDrv }: someDrv
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/sorted-order/pkgs/by-name/c/c/package.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/sorted-order/pkgs/by-name/c/c/package.nix
new file mode 100644
index 000000000000..a1b92efbbadb
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/sorted-order/pkgs/by-name/c/c/package.nix
@@ -0,0 +1 @@
+{ someDrv }: someDrv
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/success/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/success/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/success/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/success/expected b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/success/expected
new file mode 100644
index 000000000000..defae2634c34
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/success/expected
@@ -0,0 +1 @@
+Validated successfully
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/success/pkgs/by-name/fo/foo/package.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/success/pkgs/by-name/fo/foo/package.nix
new file mode 100644
index 000000000000..a1b92efbbadb
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/success/pkgs/by-name/fo/foo/package.nix
@@ -0,0 +1 @@
+{ someDrv }: someDrv
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/symlink-escape/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/symlink-escape/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/symlink-escape/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/symlink-escape/expected b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/symlink-escape/expected
new file mode 100644
index 000000000000..cd555c658a09
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/symlink-escape/expected
@@ -0,0 +1,2 @@
+pkgs/by-name/fo/foo: Path package.nix is a symlink pointing to a path outside the directory of that package.
+This PR introduces the problems listed above. Please fix them before merging, otherwise the base branch would break.
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/symlink-escape/pkgs/by-name/fo/foo/package.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/symlink-escape/pkgs/by-name/fo/foo/package.nix
new file mode 120000
index 000000000000..f079163d158a
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/symlink-escape/pkgs/by-name/fo/foo/package.nix
@@ -0,0 +1 @@
+../../../../someDrv.nix
\ No newline at end of file
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/symlink-escape/someDrv.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/symlink-escape/someDrv.nix
new file mode 100644
index 000000000000..a1b92efbbadb
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/symlink-escape/someDrv.nix
@@ -0,0 +1 @@
+{ someDrv }: someDrv
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/symlink-invalid/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/symlink-invalid/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/symlink-invalid/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/symlink-invalid/expected b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/symlink-invalid/expected
new file mode 100644
index 000000000000..1b06bcf4972b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/symlink-invalid/expected
@@ -0,0 +1,2 @@
+pkgs/by-name/fo/foo: Path foo is a symlink which cannot be resolved: No such file or directory (os error 2).
+This PR introduces the problems listed above. Please fix them before merging, otherwise the base branch would break.
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/symlink-invalid/pkgs/by-name/fo/foo/foo b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/symlink-invalid/pkgs/by-name/fo/foo/foo
new file mode 120000
index 000000000000..c86c3f3551ec
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/symlink-invalid/pkgs/by-name/fo/foo/foo
@@ -0,0 +1 @@
+none
\ No newline at end of file
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/symlink-invalid/pkgs/by-name/fo/foo/package.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/symlink-invalid/pkgs/by-name/fo/foo/package.nix
new file mode 100644
index 000000000000..a1b92efbbadb
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/symlink-invalid/pkgs/by-name/fo/foo/package.nix
@@ -0,0 +1 @@
+{ someDrv }: someDrv
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/symlink-invalid/someDrv.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/symlink-invalid/someDrv.nix
new file mode 100644
index 000000000000..a1b92efbbadb
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/symlink-invalid/someDrv.nix
@@ -0,0 +1 @@
+{ someDrv }: someDrv
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/unknown-location/all-packages.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/unknown-location/all-packages.nix
new file mode 100644
index 000000000000..3398e974cb6b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/unknown-location/all-packages.nix
@@ -0,0 +1,3 @@
+self: super: builtins.mapAttrs (name: value: value) {
+  foo = self.someDrv;
+}
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/unknown-location/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/unknown-location/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/unknown-location/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/unknown-location/expected b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/unknown-location/expected
new file mode 100644
index 000000000000..608843d93903
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/unknown-location/expected
@@ -0,0 +1,2 @@
+pkgs.foo: Cannot determine the location of this attribute using `builtins.unsafeGetAttrPos`.
+This PR introduces the problems listed above. Please fix them before merging, otherwise the base branch would break.
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/unknown-location/pkgs/by-name/fo/foo/package.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/unknown-location/pkgs/by-name/fo/foo/package.nix
new file mode 100644
index 000000000000..a1b92efbbadb
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/unknown-location/pkgs/by-name/fo/foo/package.nix
@@ -0,0 +1 @@
+{ someDrv }: someDrv
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/uppercase/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/uppercase/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/uppercase/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/uppercase/expected b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/uppercase/expected
new file mode 100644
index 000000000000..defae2634c34
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/uppercase/expected
@@ -0,0 +1 @@
+Validated successfully
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/uppercase/pkgs/by-name/fo/FOO/package.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/uppercase/pkgs/by-name/fo/FOO/package.nix
new file mode 100644
index 000000000000..a1b92efbbadb
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/uppercase/pkgs/by-name/fo/FOO/package.nix
@@ -0,0 +1 @@
+{ someDrv }: someDrv
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/with-readme/default.nix b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/with-readme/default.nix
new file mode 100644
index 000000000000..861260cdca4b
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/with-readme/default.nix
@@ -0,0 +1 @@
+import <test-nixpkgs> { root = ./.; }
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/with-readme/expected b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/with-readme/expected
new file mode 100644
index 000000000000..defae2634c34
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/with-readme/expected
@@ -0,0 +1 @@
+Validated successfully
diff --git a/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/with-readme/pkgs/by-name/README.md b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/with-readme/pkgs/by-name/README.md
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/nixpkgs/pkgs/test/nixpkgs-check-by-name/tests/with-readme/pkgs/by-name/README.md
diff --git a/nixpkgs/pkgs/test/overriding.nix b/nixpkgs/pkgs/test/overriding.nix
new file mode 100644
index 000000000000..f2519088f138
--- /dev/null
+++ b/nixpkgs/pkgs/test/overriding.nix
@@ -0,0 +1,66 @@
+{ lib, pkgs, stdenvNoCC }:
+
+let
+  tests =
+    let
+      p = pkgs.python3Packages.xpybutil.overridePythonAttrs (_: { dontWrapPythonPrograms = true; });
+    in
+    [
+      ({
+        name = "overridePythonAttrs";
+        expr = !lib.hasInfix "wrapPythonPrograms" p.postFixup;
+        expected = true;
+      })
+      ({
+        name = "repeatedOverrides-pname";
+        expr = repeatedOverrides.pname == "a-better-hello-with-blackjack";
+        expected = true;
+      })
+      ({
+        name = "repeatedOverrides-entangled-pname";
+        expr = repeatedOverrides.entangled.pname == "a-better-figlet-with-blackjack";
+        expected = true;
+      })
+      ({
+        name = "overriding-using-only-attrset";
+        expr = (pkgs.hello.overrideAttrs { pname = "hello-overriden"; }).pname == "hello-overriden";
+        expected = true;
+      })
+      ({
+        name = "overriding-using-only-attrset-no-final-attrs";
+        expr = ((stdenvNoCC.mkDerivation { pname = "hello-no-final-attrs"; }).overrideAttrs { pname = "hello-no-final-attrs-overridden"; }).pname == "hello-no-final-attrs-overridden";
+        expected = true;
+      })
+    ];
+
+  addEntangled = origOverrideAttrs: f:
+    origOverrideAttrs (
+      lib.composeExtensions f (self: super: {
+        passthru = super.passthru // {
+          entangled = super.passthru.entangled.overrideAttrs f;
+          overrideAttrs = addEntangled self.overrideAttrs;
+        };
+      })
+    );
+
+  entangle = pkg1: pkg2: pkg1.overrideAttrs (self: super: {
+    passthru = super.passthru // {
+      entangled = pkg2;
+      overrideAttrs = addEntangled self.overrideAttrs;
+    };
+  });
+
+  example = entangle pkgs.hello pkgs.figlet;
+
+  overrides1 = example.overrideAttrs (_: super: { pname = "a-better-${super.pname}"; });
+
+  repeatedOverrides = overrides1.overrideAttrs (_: super: { pname = "${super.pname}-with-blackjack"; });
+in
+
+stdenvNoCC.mkDerivation {
+  name = "test-overriding";
+  passthru = { inherit tests; };
+  buildCommand = ''
+    touch $out
+  '' + lib.concatMapStringsSep "\n" (t: "([[ ${lib.boolToString t.expr} == ${lib.boolToString t.expected} ]] && echo '${t.name} success') || (echo '${t.name} fail' && exit 1)") tests;
+}
diff --git a/nixpkgs/pkgs/test/php/default.nix b/nixpkgs/pkgs/test/php/default.nix
new file mode 100644
index 000000000000..3c6c8f61b6db
--- /dev/null
+++ b/nixpkgs/pkgs/test/php/default.nix
@@ -0,0 +1,116 @@
+{ lib
+, php
+, runCommand
+}:
+
+let
+  runTest = name: body: runCommand name { } ''
+    testFailed=
+    checking() {
+      echo -n "Checking $1... " > /dev/stderr
+    }
+    ok() {
+      echo ok > /dev/stderr
+    }
+    nok() {
+      echo fail > /dev/stderr
+      testFailed=1
+    }
+
+    ${body}
+
+    if test -n "$testFailed"; then
+      exit 1
+    fi
+
+    touch $out
+  '';
+
+  check = cond: if cond then "ok" else "nok";
+in
+{
+  withExtensions-enables-previously-disabled-extensions = runTest "php-test-withExtensions-enables-previously-disabled-extensions" ''
+    php="${php}"
+
+    checking "that imagick is not present by default"
+    $php/bin/php -r 'exit(extension_loaded("imagick") ? 1 : 0);' && ok || nok
+
+    phpWithImagick="${php.withExtensions ({ all, ... }: [ all.imagick ])}"
+    checking "that imagick extension is present when enabled"
+    $phpWithImagick/bin/php -r 'exit(extension_loaded("imagick") ? 0 : 1);' && ok || nok
+  '';
+
+  overrideAttrs-preserves-enabled-extensions =
+    let
+      customPhp =
+        (php.withExtensions ({ all, ... }: [ all.imagick ])).overrideAttrs (attrs: {
+          postInstall = attrs.postInstall or "" + ''
+            touch "$out/oApee-was-here"
+          '';
+        });
+    in
+    runTest "php-test-overrideAttrs-preserves-enabled-extensions" ''
+      php="${customPhp}"
+      phpUnwrapped="${customPhp.unwrapped}"
+
+      checking "if overrides took hold"
+      test -f "$phpUnwrapped/oApee-was-here" && ok || nok
+
+      checking "if imagick extension is still present"
+      $php/bin/php -r 'exit(extension_loaded("imagick") ? 0 : 1);' && ok || nok
+
+      checking "if imagick extension is linked against the overridden PHP"
+      echo $php
+      $php/bin/php -r 'exit(extension_loaded("imagick") ? 0 : 1);' && ok || nok
+    '';
+
+  unwrapped-overrideAttrs-stacks =
+    let
+      customPhp =
+        lib.pipe php.unwrapped [
+          (pkg: pkg.overrideAttrs (attrs: {
+            postInstall = attrs.postInstall or "" + ''
+              touch "$out/oAs-first"
+            '';
+          }))
+
+          (pkg: pkg.overrideAttrs (attrs: {
+            postInstall = attrs.postInstall or "" + ''
+              touch "$out/oAs-second"
+            '';
+          }))
+        ];
+    in
+    runTest "php-test-unwrapped-overrideAttrs-stacks" ''
+      checking "if first override remained"
+      ${check (builtins.match ".*oAs-first.*" customPhp.postInstall != null)}
+
+      checking "if second override is there"
+      ${check (builtins.match ".*oAs-second.*" customPhp.postInstall != null)}
+    '';
+
+  wrapped-overrideAttrs-stacks =
+    let
+      customPhp =
+        lib.pipe php [
+          (pkg: pkg.overrideAttrs (attrs: {
+            postInstall = attrs.postInstall or "" + ''
+              touch "$out/oAs-first"
+            '';
+          }))
+
+          (pkg: pkg.overrideAttrs (attrs: {
+            postInstall = attrs.postInstall or "" + ''
+              touch "$out/oAs-second"
+            '';
+          }))
+        ];
+    in
+    runTest "php-test-wrapped-overrideAttrs-stacks" ''
+      checking "if first override remained"
+      ${check (builtins.match ".*oAs-first.*" customPhp.unwrapped.postInstall != null)}
+
+      checking "if second override is there"
+      ${check (builtins.match ".*oAs-second.*" customPhp.unwrapped.postInstall != null)}
+    '';
+}
diff --git a/nixpkgs/pkgs/test/release/default.nix b/nixpkgs/pkgs/test/release/default.nix
new file mode 100644
index 000000000000..2ab730b5c482
--- /dev/null
+++ b/nixpkgs/pkgs/test/release/default.nix
@@ -0,0 +1,46 @@
+# Adapted from lib/tests/release.nix
+{ pkgs-path ? ../../..
+, pkgs ? import pkgs-path {}
+, lib ? pkgs.lib
+, nix ? pkgs.nix
+}:
+
+#
+# This verifies that release-attrpaths-superset.nix does not encounter
+# infinite recursion or non-tryEval-able failures.
+#
+pkgs.runCommand "all-attrs-eval-under-tryEval" {
+  nativeBuildInputs = [
+    nix
+    pkgs.gitMinimal
+  ] ++ lib.optional pkgs.stdenv.isLinux pkgs.inotify-tools;
+  strictDeps = true;
+}
+''
+  datadir="${nix}/share"
+  export TEST_ROOT=$(pwd)/test-tmp
+  export HOME=$(mktemp -d)
+  export NIX_BUILD_HOOK=
+  export NIX_CONF_DIR=$TEST_ROOT/etc
+  export NIX_LOCALSTATE_DIR=$TEST_ROOT/var
+  export NIX_LOG_DIR=$TEST_ROOT/var/log/nix
+  export NIX_STATE_DIR=$TEST_ROOT/var/nix
+  export NIX_STORE_DIR=$TEST_ROOT/store
+  export PAGER=cat
+  cacheDir=$TEST_ROOT/binary-cache
+
+  nix-store --init
+
+  cp -r ${pkgs-path}/lib lib
+  cp -r ${pkgs-path}/pkgs pkgs
+  cp -r ${pkgs-path}/default.nix default.nix
+  cp -r ${pkgs-path}/nixos nixos
+  cp -r ${pkgs-path}/maintainers maintainers
+  cp -r ${pkgs-path}/.version .version
+  cp -r ${pkgs-path}/doc doc
+  echo "Running pkgs/top-level/release-attrpaths-superset.nix"
+  nix-instantiate --eval --strict --json pkgs/top-level/release-attrpaths-superset.nix -A names > /dev/null
+
+  mkdir $out
+  echo success > $out/${nix.version}
+''
diff --git a/nixpkgs/pkgs/test/simple/builder.sh b/nixpkgs/pkgs/test/simple/builder.sh
new file mode 100644
index 000000000000..0b09a109bead
--- /dev/null
+++ b/nixpkgs/pkgs/test/simple/builder.sh
@@ -0,0 +1,43 @@
+if [ -e "$NIX_ATTRS_SH_FILE" ]; then . "$NIX_ATTRS_SH_FILE"; elif [ -f .attrs.sh ]; then . .attrs.sh; fi
+set -x
+
+export NIX_DEBUG=1
+
+source $stdenv/setup
+
+export NIX_ENFORCE_PURITY=1
+
+mkdir $out
+mkdir $out/bin
+
+cat > hello.c <<EOF
+#include <stdio.h>
+
+int main(int argc, char * * argv)
+{
+    printf("Hello World!\n");
+    return 0;
+}
+EOF
+
+#gcc -I/nix/store/foo -I /nix/store/foo -I/usr/lib -I /usr/lib hello.c -o $out/bin/hello
+gcc -I`pwd` -L /nix/store/abcd/lib -isystem /usr/lib hello.c -o $out/bin/hello
+
+$out/bin/hello
+
+cat > hello2.cc <<EOF
+#include <iostream>
+
+int main(int argc, char * * argv)
+{
+    std::cout << "Hello World!\n";
+    std::cout << VALUE << std::endl;
+    return 0;
+}
+EOF
+
+g++ hello2.cc -o $out/bin/hello2 -DVALUE="1 + 2 * 3"
+
+$out/bin/hello2
+
+ld -v
diff --git a/nixpkgs/pkgs/test/stdenv-inputs/bar.c b/nixpkgs/pkgs/test/stdenv-inputs/bar.c
new file mode 100644
index 000000000000..2d7299c2d462
--- /dev/null
+++ b/nixpkgs/pkgs/test/stdenv-inputs/bar.c
@@ -0,0 +1,3 @@
+unsigned int bar(void) {
+  return 42;
+}
diff --git a/nixpkgs/pkgs/test/stdenv-inputs/cc-main.c b/nixpkgs/pkgs/test/stdenv-inputs/cc-main.c
new file mode 100644
index 000000000000..06f28bc33c69
--- /dev/null
+++ b/nixpkgs/pkgs/test/stdenv-inputs/cc-main.c
@@ -0,0 +1,7 @@
+#include <stdio.h>
+
+int main(int argc, char **argv)
+{
+  fprintf(stderr, "ok\n");
+  return 0;
+}
diff --git a/nixpkgs/pkgs/test/stdenv-inputs/default.nix b/nixpkgs/pkgs/test/stdenv-inputs/default.nix
new file mode 100644
index 000000000000..6a2e441d0191
--- /dev/null
+++ b/nixpkgs/pkgs/test/stdenv-inputs/default.nix
@@ -0,0 +1,68 @@
+{ lib, stdenv }:
+
+let
+  foo = stdenv.mkDerivation {
+    name = "foo-test";
+
+    dontUnpack = true;
+
+    installPhase = ''
+      mkdir -p $out/bin $out/include $out/lib
+      $CC -o $out/bin/foo ${./cc-main.c}
+      chmod +x $out/bin/foo
+      cp ${./foo.c} $out/include/foo.h
+      $CC -shared \
+        ${lib.optionalString stdenv.isDarwin "-Wl,-install_name,$out/lib/libfoo.dylib"} \
+        -o $out/lib/libfoo${stdenv.hostPlatform.extensions.sharedLibrary} \
+        ${./foo.c}
+    '';
+  };
+
+  bar = stdenv.mkDerivation {
+    name = "bar-test";
+    outputs = [ "out" "dev" ];
+
+    dontUnpack = true;
+
+    installPhase = ''
+      mkdir -p $out/bin $dev/include $dev/lib
+      $CC -o $out/bin/bar ${./cc-main.c}
+      chmod +x $out/bin/bar
+      cp ${./bar.c} $dev/include/bar.h
+      $CC -shared \
+        ${lib.optionalString stdenv.isDarwin "-Wl,-install_name,$dev/lib/libbar.dylib"} \
+        -o $dev/lib/libbar${stdenv.hostPlatform.extensions.sharedLibrary} \
+        ${./bar.c}
+    '';
+  };
+in
+
+stdenv.mkDerivation {
+  name = "stdenv-inputs-test";
+  phases = [ "buildPhase" ];
+
+  buildInputs = [ foo bar ];
+
+  buildPhase = ''
+    env
+
+    printf "checking whether binaries are available... " >&2
+    foo && bar
+
+    printf "checking whether compiler can find headers... " >&2
+    $CC -o include-check ${./include-main.c}
+    ./include-check
+
+    printf "checking whether compiler can find headers... " >&2
+    $CC -o include-check ${./include-main.c}
+    ./include-check
+
+    printf "checking whether compiler can find libraries... " >&2
+    $CC -lfoo -lbar -o lib-check ${./lib-main.c}
+    ./lib-check
+
+    touch $out
+  '';
+
+  meta.platforms = lib.platforms.all;
+}
diff --git a/nixpkgs/pkgs/test/stdenv-inputs/foo.c b/nixpkgs/pkgs/test/stdenv-inputs/foo.c
new file mode 100644
index 000000000000..0253a26d5d2c
--- /dev/null
+++ b/nixpkgs/pkgs/test/stdenv-inputs/foo.c
@@ -0,0 +1,3 @@
+unsigned int foo(void) {
+  return 42;
+}
diff --git a/nixpkgs/pkgs/test/stdenv-inputs/include-main.c b/nixpkgs/pkgs/test/stdenv-inputs/include-main.c
new file mode 100644
index 000000000000..35e5ee0d90f7
--- /dev/null
+++ b/nixpkgs/pkgs/test/stdenv-inputs/include-main.c
@@ -0,0 +1,13 @@
+#include <stdio.h>
+#include <foo.h>
+#include <bar.h>
+
+int main(int argc, char **argv)
+{
+  if (foo() != 42)
+    return 1;
+  if (bar() != 42)
+    return 1;
+  fprintf(stderr, "ok\n");
+  return 0;
+}
diff --git a/nixpkgs/pkgs/test/stdenv-inputs/lib-main.c b/nixpkgs/pkgs/test/stdenv-inputs/lib-main.c
new file mode 100644
index 000000000000..c9488fe43e55
--- /dev/null
+++ b/nixpkgs/pkgs/test/stdenv-inputs/lib-main.c
@@ -0,0 +1,14 @@
+#include <stdio.h>
+
+extern unsigned int foo(void);
+extern unsigned int bar(void);
+
+int main(int argc, char **argv)
+{
+  if (foo() != 42)
+    return 1;
+  if (bar() != 42)
+    return 1;
+  fprintf(stderr, "ok\n");
+  return 0;
+}
diff --git a/nixpkgs/pkgs/test/stdenv/default.nix b/nixpkgs/pkgs/test/stdenv/default.nix
new file mode 100644
index 000000000000..3882eb2b625c
--- /dev/null
+++ b/nixpkgs/pkgs/test/stdenv/default.nix
@@ -0,0 +1,302 @@
+# To run these tests:
+# nix-build -A tests.stdenv
+
+{ stdenv
+, pkgs
+, lib
+, testers
+}:
+
+let
+  # early enough not to rebuild gcc but late enough to have patchelf
+  earlyPkgs = stdenv.__bootPackages.stdenv.__bootPackages;
+  earlierPkgs = stdenv.__bootPackages.stdenv.__bootPackages.stdenv.__bootPackages.stdenv.__bootPackages.stdenv.__bootPackages;
+  # use a early stdenv so when hacking on stdenv this test can be run quickly
+  bootStdenv = stdenv.__bootPackages.stdenv.__bootPackages.stdenv.__bootPackages.stdenv.__bootPackages.stdenv;
+  pkgsStructured = import pkgs.path { config = { structuredAttrsByDefault = true; }; inherit (stdenv.hostPlatform) system; };
+  bootStdenvStructuredAttrsByDefault = pkgsStructured.stdenv.__bootPackages.stdenv.__bootPackages.stdenv.__bootPackages.stdenv.__bootPackages.stdenv;
+
+  runCommand = earlierPkgs.runCommand;
+
+
+  ccWrapperSubstitutionsTest = { name, stdenv', extraAttrs ? { } }:
+
+    stdenv'.cc.overrideAttrs (previousAttrs: ({
+      inherit name;
+
+      postFixup = previousAttrs.postFixup + ''
+        declare -p wrapperName
+        echo "env.wrapperName = $wrapperName"
+        [[ $wrapperName == "CC_WRAPPER" ]] || (echo "'\$wrapperName' was not 'CC_WRAPPER'" && false)
+        declare -p suffixSalt
+        echo "env.suffixSalt = $suffixSalt"
+        [[ $suffixSalt == "${stdenv'.cc.suffixSalt}" ]] || (echo "'\$suffxSalt' was not '${stdenv'.cc.suffixSalt}'" && false)
+
+        grep -q "@out@" $out/bin/cc || echo "@out@ in $out/bin/cc was substituted"
+        grep -q "@suffixSalt@" $out/bin/cc && (echo "$out/bin/cc contains unsubstituted variables" && false)
+
+        touch $out
+      '';
+    } // extraAttrs));
+
+  testEnvAttrset = { name, stdenv', extraAttrs ? { } }:
+    stdenv'.mkDerivation
+      ({
+        inherit name;
+        env = {
+          string = "testing-string";
+        };
+
+        passAsFile = [ "buildCommand" ];
+        buildCommand = ''
+          declare -p string
+          echo "env.string = $string"
+          [[ $string == "testing-string" ]] || (echo "'\$string' was not 'testing-string'" && false)
+          [[ "$(declare -p string)" == 'declare -x string="testing-string"' ]] || (echo "'\$string' was not exported" && false)
+          touch $out
+        '';
+      } // extraAttrs);
+
+  testPrependAndAppendToVar = { name, stdenv', extraAttrs ? { } }:
+    stdenv'.mkDerivation
+      ({
+        inherit name;
+        env = {
+          string = "testing-string";
+        };
+
+        passAsFile = [ "buildCommand" ] ++ lib.optionals (extraAttrs ? extraTest) [ "extraTest" ];
+        buildCommand = ''
+          declare -p string
+          appendToVar string hello
+          # test that quoted strings work
+          prependToVar string "world"
+          declare -p string
+
+          declare -A associativeArray=(["X"]="Y")
+          [[ $(appendToVar associativeArray "fail" 2>&1) =~ "trying to use" ]] || (echo "prependToVar did not catch prepending associativeArray" && false)
+          [[ $(prependToVar associativeArray "fail" 2>&1) =~ "trying to use" ]] || (echo "prependToVar did not catch prepending associativeArray" && false)
+
+          [[ $string == "world testing-string hello" ]] || (echo "'\$string' was not 'world testing-string hello'" && false)
+
+          # test appending to a unset variable
+          appendToVar nonExistant created hello
+          typeset -p nonExistant
+          if [[ -n $__structuredAttrs ]]; then
+            [[ "''${nonExistant[@]}" == "created hello" ]]
+          else
+            # there's a extra " " in front here and a extra " " in the end of prependToVar
+            # shouldn't matter because these functions will mostly be used for $*Flags and the Flag variable will in most cases already exit
+            [[ "$nonExistant" == " created hello" ]]
+          fi
+
+          eval "$extraTest"
+
+          touch $out
+        '';
+      } // extraAttrs);
+
+in
+
+{
+  # tests for hooks in `stdenv.defaultNativeBuildInputs`
+  hooks = lib.recurseIntoAttrs (import ./hooks.nix { stdenv = bootStdenv; pkgs = earlyPkgs; inherit lib; });
+
+  outputs-no-out = runCommand "outputs-no-out-assert" {
+    result = earlierPkgs.testers.testBuildFailure (bootStdenv.mkDerivation {
+      NIX_DEBUG = 1;
+      name = "outputs-no-out";
+      outputs = ["foo"];
+      buildPhase = ":";
+      installPhase = ''
+        touch $foo
+      '';
+    });
+
+    # Assumption: the first output* variable to be configured is
+    #   _overrideFirst outputDev "dev" "out"
+    expectedMsg = "error: _assignFirst: could not find a non-empty variable whose name to assign to outputDev.\n       The following variables were all unset or empty:\n           dev out";
+  } ''
+    grep -F "$expectedMsg" $result/testBuildFailure.log >/dev/null
+    touch $out
+  '';
+
+  test-env-attrset = testEnvAttrset { name = "test-env-attrset"; stdenv' = bootStdenv; };
+
+  # Test compatibility with derivations using `env` as a regular variable.
+  test-env-derivation = bootStdenv.mkDerivation rec {
+    name = "test-env-derivation";
+    env = bootStdenv.mkDerivation {
+      name = "foo";
+      buildCommand = ''
+        mkdir "$out"
+        touch "$out/bar"
+      '';
+    };
+
+    passAsFile = [ "buildCommand" ];
+    buildCommand = ''
+      declare -p env
+      [[ $env == "${env}" ]]
+      touch "$out"
+    '';
+  };
+
+  # Check that mkDerivation rejects MD5 hashes
+  rejectedHashes = lib.recurseIntoAttrs {
+    md5 =
+      let drv = runCommand "md5 outputHash rejected" {
+        outputHash = "md5-fPt7dxVVP7ffY3MxkQdwVw==";
+      } "true";
+      in assert !(builtins.tryEval drv).success; {};
+  };
+
+  test-inputDerivation = let
+    inherit (stdenv.mkDerivation {
+      dep1 = derivation { name = "dep1"; builder = "/bin/sh"; args = [ "-c" ": > $out" ]; system = builtins.currentSystem; };
+      dep2 = derivation { name = "dep2"; builder = "/bin/sh"; args = [ "-c" ": > $out" ]; system = builtins.currentSystem; };
+      passAsFile = [ "dep2" ];
+    }) inputDerivation;
+  in
+    runCommand "test-inputDerivation" {
+      exportReferencesGraph = [ "graph" inputDerivation ];
+    } ''
+      grep ${inputDerivation.dep1} graph
+      grep ${inputDerivation.dep2} graph
+      touch $out
+    '';
+
+  test-prepend-append-to-var = testPrependAndAppendToVar {
+    name = "test-prepend-append-to-var";
+    stdenv' = bootStdenv;
+  };
+
+  test-structured-env-attrset = testEnvAttrset {
+    name = "test-structured-env-attrset";
+    stdenv' = bootStdenv;
+    extraAttrs = { __structuredAttrs = true; };
+  };
+
+  test-cc-wrapper-substitutions = ccWrapperSubstitutionsTest {
+    name = "test-cc-wrapper-substitutions";
+    stdenv' = bootStdenv;
+  };
+
+  structuredAttrsByDefault = lib.recurseIntoAttrs {
+
+    hooks = lib.recurseIntoAttrs (import ./hooks.nix { stdenv = bootStdenvStructuredAttrsByDefault; pkgs = earlyPkgs; inherit lib; });
+
+    test-cc-wrapper-substitutions = ccWrapperSubstitutionsTest {
+      name = "test-cc-wrapper-substitutions-structuredAttrsByDefault";
+      stdenv' = bootStdenvStructuredAttrsByDefault;
+    };
+
+    test-structured-env-attrset = testEnvAttrset {
+      name = "test-structured-env-attrset-structuredAttrsByDefault";
+      stdenv' = bootStdenvStructuredAttrsByDefault;
+    };
+
+    test-prepend-append-to-var = testPrependAndAppendToVar {
+      name = "test-prepend-append-to-var-structuredAttrsByDefault";
+      stdenv' = bootStdenvStructuredAttrsByDefault;
+      extraAttrs = {
+        # will be a bash indexed array in attrs.sh
+        # declare -a list=('a' 'b' )
+        # and a json array in attrs.json
+        # "list":["a","b"]
+        list = [ "a" "b" ];
+        # will be a bash associative array(dictionary) in attrs.sh
+        # declare -A array=(['a']='1' ['b']='2' )
+        # and a json object in attrs.json
+        # {"array":{"a":"1","b":"2"}
+        array = { a = "1"; b = "2"; };
+        extraTest = ''
+          declare -p array
+          array+=(["c"]="3")
+          declare -p array
+
+          [[ "''${array[c]}" == "3" ]] || (echo "c element of '\$array' was not '3'" && false)
+
+          declare -p list
+          prependToVar list hello
+          # test that quoted strings work
+          appendToVar list "world"
+          declare -p list
+
+          [[ "''${list[0]}" == "hello" ]] || (echo "first element of '\$list' was not 'hello'" && false)
+          [[ "''${list[1]}" == "a" ]] || (echo "first element of '\$list' was not 'a'" && false)
+          [[ "''${list[-1]}" == "world" ]] || (echo "last element of '\$list' was not 'world'" && false)
+        '';
+      };
+    };
+
+    test-golden-example-structuredAttrs =
+      let
+        goldenSh = earlyPkgs.writeText "goldenSh" ''
+          declare -A EXAMPLE_ATTRS=(['foo']='bar' )
+          declare EXAMPLE_BOOL_FALSE=
+          declare EXAMPLE_BOOL_TRUE=1
+          declare EXAMPLE_INT=123
+          declare EXAMPLE_INT_NEG=-123
+          declare -a EXAMPLE_LIST=('foo' 'bar' )
+          declare EXAMPLE_STR='foo bar'
+        '';
+        goldenJson = earlyPkgs.writeText "goldenSh" ''
+          {
+            "EXAMPLE_ATTRS": {
+              "foo": "bar"
+            },
+            "EXAMPLE_BOOL_FALSE": false,
+            "EXAMPLE_BOOL_TRUE": true,
+            "EXAMPLE_INT": 123,
+            "EXAMPLE_INT_NEG": -123,
+            "EXAMPLE_LIST": [
+              "foo",
+              "bar"
+            ],
+            "EXAMPLE_NESTED_ATTRS": {
+              "foo": {
+                "bar": "baz"
+              }
+            },
+            "EXAMPLE_NESTED_LIST": [
+              [
+                "foo",
+                "bar"
+              ],
+              [
+                "baz"
+              ]
+            ],
+            "EXAMPLE_STR": "foo bar"
+          }
+        '';
+      in
+      bootStdenvStructuredAttrsByDefault.mkDerivation {
+        name = "test-golden-example-structuredAttrsByDefault";
+        nativeBuildInputs = [ earlyPkgs.jq ];
+
+        EXAMPLE_BOOL_TRUE = true;
+        EXAMPLE_BOOL_FALSE = false;
+        EXAMPLE_INT = 123;
+        EXAMPLE_INT_NEG = -123;
+        EXAMPLE_STR = "foo bar";
+        EXAMPLE_LIST = [ "foo" "bar" ];
+        EXAMPLE_NESTED_LIST = [ [ "foo" "bar" ] [ "baz" ] ];
+        EXAMPLE_ATTRS = { foo = "bar"; };
+        EXAMPLE_NESTED_ATTRS = { foo.bar = "baz"; };
+
+        inherit goldenSh;
+        inherit goldenJson;
+
+        buildCommand = ''
+          mkdir -p $out
+          cat $NIX_ATTRS_SH_FILE | grep "EXAMPLE" | grep -v -E 'installPhase|jq' > $out/sh
+          jq 'with_entries(select(.key|match("EXAMPLE")))' $NIX_ATTRS_JSON_FILE > $out/json
+          diff $out/sh $goldenSh
+          diff $out/json $goldenJson
+        '';
+      };
+
+  };
+}
diff --git a/nixpkgs/pkgs/test/stdenv/gcc-stageCompare.nix b/nixpkgs/pkgs/test/stdenv/gcc-stageCompare.nix
new file mode 100644
index 000000000000..e5c2ed5921b3
--- /dev/null
+++ b/nixpkgs/pkgs/test/stdenv/gcc-stageCompare.nix
@@ -0,0 +1,32 @@
+# This test *must* be run prior to releasing any build of either stdenv or the
+# gcc that it exports!  This check should also be part of CI for any PR that
+# causes a rebuild of `stdenv.cc`.
+#
+# When we used gcc's internal bootstrap it did this check as part of (and
+# serially with) the gcc derivation.  Now that we bootstrap externally this
+# check can be done in parallel with any/all of stdenv's referrers.  But we
+# must remember to do the check.
+#
+
+{ stdenv
+, pkgs
+, lib
+}:
+
+assert stdenv.cc.isGNU;
+with pkgs;
+# rebuild gcc using the "final" stdenv
+let gcc-stageCompare = (gcc-unwrapped.override {
+      reproducibleBuild = true;
+      profiledCompiler = false;
+      stdenv = overrideCC stdenv (wrapCCWith {
+        cc = stdenv.cc;
+      });
+    }).overrideAttrs(_: {
+      NIX_OUTPATH_USED_AS_RANDOM_SEED = stdenv.cc.cc.out;
+    });
+in (runCommand "gcc-stageCompare" {} ''
+  diff -sr ${pkgs.gcc-unwrapped.checksum}/checksums ${gcc-stageCompare.checksum}/checksums && touch $out
+'').overrideAttrs (a: {
+  meta = (a.meta or { }) // { platforms = lib.platforms.linux; };
+})
diff --git a/nixpkgs/pkgs/test/stdenv/hooks.nix b/nixpkgs/pkgs/test/stdenv/hooks.nix
new file mode 100644
index 000000000000..eb1b3f61bda6
--- /dev/null
+++ b/nixpkgs/pkgs/test/stdenv/hooks.nix
@@ -0,0 +1,136 @@
+{ stdenv, pkgs, lib }:
+
+# ordering should match defaultNativeBuildInputs
+
+{
+  # TODO: add audit-tmpdir
+  compress-man-pages =
+    let
+      manFile = pkgs.writeText "small-man" ''
+        .TH HELLO "1" "May 2022" "hello 2.12.1" "User Commands"
+        .SH NAME
+        hello - friendly greeting program
+      '';
+    in
+    stdenv.mkDerivation {
+      name = "test-compress-man-pages";
+      buildCommand = ''
+        mkdir -p $out/share/man
+        cp ${manFile} $out/share/man/small-man.1
+        compressManPages $out
+        [[ -e $out/share/man/small-man.1.gz ]]
+      '';
+    };
+  make-symlinks-relative = stdenv.mkDerivation {
+    name = "test-make-symlinks-relative";
+    outputs = [ "out" "man" ];
+    buildCommand = ''
+      mkdir -p $out/{bar,baz}
+      mkdir -p $man/share/{x,y}
+      source1="$out/bar/foo"
+      destination1="$out/baz/foo"
+      source2="$man/share/x/file1"
+      destination2="$man/share/y/file2"
+      echo foo > $source1
+      echo foo > $source2
+      ln -s $source1 $destination1
+      ln -s $source2 $destination2
+      echo "symlink before patching: $(readlink $destination1)"
+      echo "symlink before patching: $(readlink $destination2)"
+
+      _makeSymlinksRelativeInAllOutputs
+
+      echo "symlink after patching: $(readlink $destination1)"
+      ([[ -e $destination1 ]] && echo "symlink isn't broken") || (echo "symlink is broken" && exit 1)
+      ([[ $(readlink $destination1) == "../bar/foo" ]] && echo "absolute symlink was made relative") || (echo "symlink was not made relative" && exit 1)
+      echo "symlink after patching: $(readlink $destination2)"
+      ([[ -e $destination2 ]] && echo "symlink isn't broken") || (echo "symlink is broken" && exit 1)
+      ([[ $(readlink $destination2) == "../x/file1" ]] && echo "absolute symlink was made relative") || (echo "symlink was not made relative" && exit 1)
+    '';
+  };
+  move-docs = stdenv.mkDerivation {
+    name = "test-move-docs";
+    buildCommand = ''
+      mkdir -p $out/{man,doc,info}
+      touch $out/{man,doc,info}/foo
+      cat $out/{man,doc,info}/foo
+
+      _moveToShare
+
+      (cat $out/share/{man,doc,info}/foo 2>/dev/null && echo "man,doc,info were moved") || (echo "man,doc,info were not moved" && exit 1)
+    '';
+  };
+  move-lib64 = stdenv.mkDerivation {
+    name = "test-move-lib64";
+    buildCommand = ''
+      mkdir -p $out/lib64
+      touch $out/lib64/foo
+      cat $out/lib64/foo
+
+      _moveLib64
+
+      # check symlink
+      [[ -h $out/lib64 ]]
+      ([[ -e $out/lib64 ]] && echo "symlink isn't broken") || (echo "symlink is broken" && exit 1)
+      [[ -e $out/lib/foo ]]
+    '';
+  };
+  move-sbin = stdenv.mkDerivation {
+    name = "test-move-sbin";
+    buildCommand = ''
+      mkdir -p $out/sbin
+      touch $out/sbin/foo
+      cat $out/sbin/foo
+
+      _moveSbin
+
+      # check symlink
+      [[ -h $out/sbin ]]
+      ([[ -e $out/sbin ]] && echo "symlink isn't broken") || (echo "symlink is broken" && exit 1)
+      [[ -e $out/bin/foo ]]
+    '';
+  };
+  # TODO: add multiple-outputs
+  patch-shebangs = import ./patch-shebangs.nix { inherit stdenv lib pkgs; };
+  prune-libtool-files =
+    let
+      libFoo = pkgs.writeText "libFoo" ''
+        # Generated by libtool (GNU libtool) 2.4.6
+        old_library='''
+        dependency_libs=' -Lbar.la -Lbaz.la'
+      '';
+    in
+    stdenv.mkDerivation {
+      name = "test-prune-libtool-files";
+      buildCommand = ''
+        mkdir -p $out/lib
+        cp ${libFoo} $out/lib/libFoo.la
+        _pruneLibtoolFiles
+        grep "^dependency_libs=''' #pruned" $out/lib/libFoo.la
+        # confirm file doesn't only contain the above
+        grep "^old_library='''" $out/lib/libFoo.la
+      '';
+    };
+  reproducible-builds = stdenv.mkDerivation {
+    name = "test-reproducible-builds";
+    buildCommand = ''
+      # can't be tested more precisely because the value of random-seed changes depending on the output
+      [[ $NIX_CFLAGS_COMPILE =~ "-frandom-seed=" ]]
+      touch $out
+    '';
+  };
+  set-source-date-epoch-to-latest = stdenv.mkDerivation {
+    name = "test-set-source-date-epoch-to-latest";
+    buildCommand = ''
+      sourceRoot=$NIX_BUILD_TOP/source
+      mkdir -p $sourceRoot
+      touch --date=1/1/2015 $sourceRoot/foo
+
+      _updateSourceDateEpochFromSourceRoot
+
+      [[ $SOURCE_DATE_EPOCH == "1420070400" ]]
+      touch $out
+    '';
+  };
+  # TODO: add strip
+}
diff --git a/nixpkgs/pkgs/test/stdenv/patch-shebangs.nix b/nixpkgs/pkgs/test/stdenv/patch-shebangs.nix
new file mode 100644
index 000000000000..db9ca2fcaafe
--- /dev/null
+++ b/nixpkgs/pkgs/test/stdenv/patch-shebangs.nix
@@ -0,0 +1,130 @@
+{ lib, stdenv, pkgs }:
+
+# since the tests are using a early stdenv, the stdenv will have dontPatchShebangs=1, so it has to be unset
+# https://github.com/NixOS/nixpkgs/blob/768a982bfc9d29a6bd3beb963ed4b054451ce3d0/pkgs/stdenv/linux/default.nix#L148-L153
+
+# strictDeps has to be disabled because the shell isn't in buildInputs
+
+let
+  tests = {
+    bad-shebang = stdenv.mkDerivation {
+      name = "bad-shebang";
+      strictDeps = false;
+      dontUnpack = true;
+      installPhase = ''
+        mkdir -p $out/bin
+        echo "#!/bin/bash" > $out/bin/test
+        echo "echo -n hello" >> $out/bin/test
+        chmod +x $out/bin/test
+        dontPatchShebangs=
+      '';
+      passthru = {
+        assertion = "grep '^#!${stdenv.shell}' $out/bin/test > /dev/null";
+      };
+    };
+
+    ignores-nix-store = stdenv.mkDerivation {
+      name = "ignores-nix-store";
+      strictDeps = false;
+      dontUnpack = true;
+      installPhase = ''
+        mkdir -p $out/bin
+        echo "#!$NIX_STORE/path/to/bash" > $out/bin/test
+        echo "echo -n hello" >> $out/bin/test
+        chmod +x $out/bin/test
+        dontPatchShebangs=
+      '';
+      passthru = {
+        assertion = "grep \"^#!$NIX_STORE/path/to/bash\" $out/bin/test > /dev/null";
+      };
+    };
+
+    updates-nix-store = stdenv.mkDerivation {
+      name = "updates-nix-store";
+      strictDeps = false;
+      dontUnpack = true;
+      installPhase = ''
+        mkdir -p $out/bin
+        echo "#!$NIX_STORE/path/to/bash" > $out/bin/test
+        echo "echo -n hello" >> $out/bin/test
+        chmod +x $out/bin/test
+        patchShebangs --update $out/bin/test
+        dontPatchShebangs=1
+      '';
+      passthru = {
+        assertion = "grep '^#!${stdenv.shell}' $out/bin/test > /dev/null";
+      };
+    };
+
+    split-string = stdenv.mkDerivation {
+      name = "split-string";
+      strictDeps = false;
+      dontUnpack = true;
+      installPhase = ''
+        mkdir -p $out/bin
+        echo "#!/usr/bin/env -S bash --posix" > $out/bin/test
+        echo "echo -n hello" >> $out/bin/test
+        chmod +x $out/bin/test
+        dontPatchShebangs=
+      '';
+      passthru = {
+        assertion = "grep -v '^#!${pkgs.coreutils}/bin/env -S ${stdenv.shell} --posix' $out/bin/test > /dev/null";
+      };
+    };
+
+    without-trailing-newline = stdenv.mkDerivation {
+      name = "without-trailing-newline";
+      strictDeps = false;
+      dontUnpack = true;
+      installPhase = ''
+        mkdir -p $out/bin
+        printf "#!/bin/bash" > $out/bin/test
+        chmod +x $out/bin/test
+        dontPatchShebangs=
+      '';
+      passthru = {
+        assertion = "grep '^#!${stdenv.shell}' $out/bin/test > /dev/null";
+      };
+    };
+
+  };
+in
+stdenv.mkDerivation {
+  name = "test-patch-shebangs";
+  passthru = { inherit (tests) bad-shebang ignores-nix-store updates-nix-store split-string without-trailing-newline; };
+  buildCommand = ''
+    validate() {
+      local name=$1
+      local testout=$2
+      local assertion=$3
+
+      echo -n "... $name: " >&2
+
+      local rc=0
+      (out=$testout eval "$assertion") || rc=1
+
+      if [ "$rc" -eq 0 ]; then
+        echo "yes" >&2
+      else
+        echo "no" >&2
+      fi
+
+      return "$rc"
+    }
+
+    echo "checking whether patchShebangs works properly... ">&2
+
+    fail=
+    ${lib.concatStringsSep "\n" (lib.mapAttrsToList (_: test: ''
+      validate "${test.name}" "${test}" ${lib.escapeShellArg test.assertion} || fail=1
+    '') tests)}
+
+    if [ "$fail" ]; then
+      echo "failed"
+      exit 1
+    else
+      echo "succeeded"
+      touch $out
+    fi
+  '';
+}
diff --git a/nixpkgs/pkgs/test/substitute/default.nix b/nixpkgs/pkgs/test/substitute/default.nix
new file mode 100644
index 000000000000..3ff346b14658
--- /dev/null
+++ b/nixpkgs/pkgs/test/substitute/default.nix
@@ -0,0 +1,96 @@
+{ substitute, testers, runCommand }:
+let
+  # Ofborg doesn't allow any traces on stderr,
+  # so mock `lib` to not trace warnings,
+  # because substitute gives a deprecation warning
+  substituteSilent = substitute.override (prevArgs: {
+    lib = prevArgs.lib.extend (finalLib: prevLib: {
+      trivial = prevLib.trivial // {
+        warn = msg: value: value;
+      };
+    });
+  });
+in {
+
+  substitutions = testers.testEqualContents {
+    assertion = "substitutions-spaces";
+    actual = substitute {
+      src = builtins.toFile "source" ''
+        Hello world!
+      '';
+      substitutions = [
+        "--replace-fail"
+        "Hello world!"
+        "Yo peter!"
+      ];
+    };
+    expected = builtins.toFile "expected" ''
+      Yo peter!
+    '';
+  };
+
+  legacySingleReplace = testers.testEqualContents {
+    assertion = "substitute-single-replace";
+    actual = substituteSilent {
+      src = builtins.toFile "source" ''
+        Hello world!
+      '';
+      replacements = [
+        "--replace-fail" "world" "paul"
+      ];
+    };
+    expected = builtins.toFile "expected" ''
+      Hello paul!
+    '';
+  };
+
+  legacyString = testers.testEqualContents {
+    assertion = "substitute-string";
+    actual = substituteSilent {
+      src = builtins.toFile "source" ''
+        Hello world!
+      '';
+      # Not great that this works at all, but is supported
+      replacements = "--replace-fail world string";
+    };
+    expected = builtins.toFile "expected" ''
+      Hello string!
+    '';
+  };
+
+  legacySingleArg = testers.testEqualContents {
+    assertion = "substitute-single-arg";
+    actual = substituteSilent {
+      src = builtins.toFile "source" ''
+        Hello world!
+      '';
+      # Not great that this works at all, but is supported
+      replacements = [
+        "--replace-fail world list"
+      ];
+    };
+    expected = builtins.toFile "expected" ''
+      Hello list!
+    '';
+  };
+
+  legacyVar = testers.testEqualContents {
+    assertion = "substitute-var";
+    actual = substituteSilent {
+      src = builtins.toFile "source" ''
+        @greeting@ @name@!
+      '';
+      # Not great that this works at all, but is supported
+      replacements = [
+        "--subst-var name"
+        "--subst-var-by greeting Yo"
+      ];
+      name = "peter";
+    };
+    expected = builtins.toFile "expected" ''
+      Yo peter!
+    '';
+  };
+
+
+}
diff --git a/nixpkgs/pkgs/test/systemd/default.nix b/nixpkgs/pkgs/test/systemd/default.nix
new file mode 100644
index 000000000000..2208bf794481
--- /dev/null
+++ b/nixpkgs/pkgs/test/systemd/default.nix
@@ -0,0 +1,5 @@
+{ lib, callPackage }:
+
+lib.recurseIntoAttrs {
+  nixos = callPackage ./nixos { };
+}
diff --git a/nixpkgs/pkgs/test/systemd/nixos/default.nix b/nixpkgs/pkgs/test/systemd/nixos/default.nix
new file mode 100644
index 000000000000..e45399b63516
--- /dev/null
+++ b/nixpkgs/pkgs/test/systemd/nixos/default.nix
@@ -0,0 +1,37 @@
+{ pkgs, lib, stdenv, ... }:
+
+lib.runTests {
+  # Merging two non-list definitions must still result in an error
+  # about a conflicting definition.
+  test-unitOption-merging-non-lists-conflict =
+    let nixos = pkgs.nixos {
+        system.stateVersion = lib.trivial.release;
+        systemd.services.systemd-test-nixos = {
+          serviceConfig = lib.mkMerge [
+            { StateDirectory = "foo"; }
+            { StateDirectory = "bar"; }
+          ];
+        };
+      };
+    in {
+    expr = (builtins.tryEval (nixos.config.systemd.services.systemd-test-nixos.serviceConfig.StateDirectory)).success;
+    expected = false;
+  };
+
+  # Merging must lift non-list definitions to a list
+  # if at least one of them is a list.
+  test-unitOption-merging-list-non-list-append =
+    let nixos = pkgs.nixos {
+        system.stateVersion = lib.trivial.release;
+        systemd.services.systemd-test-nixos = {
+          serviceConfig = lib.mkMerge [
+            { StateDirectory = "foo"; }
+            { StateDirectory = ["bar"]; }
+          ];
+        };
+      };
+    in {
+    expr = nixos.config.systemd.services.systemd-test-nixos.serviceConfig.StateDirectory;
+    expected = [ "foo" "bar" ];
+  };
+}
diff --git a/nixpkgs/pkgs/test/texlive/default.nix b/nixpkgs/pkgs/test/texlive/default.nix
new file mode 100644
index 000000000000..5f7067543932
--- /dev/null
+++ b/nixpkgs/pkgs/test/texlive/default.nix
@@ -0,0 +1,695 @@
+{ lib, stdenv, buildEnv, runCommand, fetchurl, file, texlive, writeShellScript, writeText, texliveInfraOnly, texliveConTeXt, texliveSmall, texliveMedium, texliveFull }:
+
+rec {
+
+  mkTeXTest = lib.makeOverridable (
+    { name
+    , format
+    , text
+    , texLive ? texliveSmall
+    , options ? "-interaction=errorstopmode"
+    , preTest ? ""
+    , postTest ? ""
+    , ...
+    }@attrs: runCommand "texlive-test-tex-${name}"
+      ({
+        nativeBuildInputs = [ texLive ] ++ attrs.nativeBuildInputs or [ ];
+        text = builtins.toFile "${name}.tex" text;
+      } // builtins.removeAttrs attrs [ "nativeBuildInputs" "text" "texLive" ])
+      ''
+        export HOME="$(mktemp -d)"
+        mkdir "$out"
+        cd "$out"
+        cp "$text" "$name.tex"
+        ${preTest}
+        $format $options "$name.tex"
+        ${postTest}
+      ''
+  );
+
+  tlpdbNix = runCommand "texlive-test-tlpdb-nix" {
+    nixpkgsTlpdbNix = ../../tools/typesetting/tex/texlive/tlpdb.nix;
+    tlpdbNix = texlive.tlpdb.nix;
+  }
+  ''
+    mkdir -p "$out"
+    diff -u "''${nixpkgsTlpdbNix}" "''${tlpdbNix}" | tee "$out/tlpdb.nix.patch"
+  '';
+
+  # test two completely different font discovery mechanisms, both of which were once broken:
+  #  - lualatex uses its own luaotfload script (#220228)
+  #  - xelatex uses fontconfig (#228196)
+  opentype-fonts = lib.recurseIntoAttrs rec {
+    lualatex = mkTeXTest {
+      name = "opentype-fonts-lualatex";
+      format = "lualatex";
+      texLive = texliveSmall.withPackages (ps: [ ps.libertinus-fonts ]);
+      text = ''
+        \documentclass{article}
+        \usepackage{fontspec}
+        \setmainfont{Libertinus Serif}
+        \begin{document}
+          \LaTeX{} is great
+        \end{document}
+      '';
+    };
+    xelatex = lualatex.override {
+      name = "opentype-fonts-xelatex";
+      format = "xelatex";
+    };
+  };
+
+  chktex = runCommand "texlive-test-chktex" {
+    nativeBuildInputs = [
+      (texlive.withPackages (ps: [ ps.chktex ]))
+    ];
+    input = builtins.toFile "chktex-sample.tex" ''
+      \documentclass{article}
+      \begin{document}
+        \LaTeX is great
+      \end{document}
+    '';
+  } ''
+    # chktex is supposed to return 2 when it (successfully) finds warnings, but no errors,
+    # see http://git.savannah.nongnu.org/cgit/chktex.git/commit/?id=ec0fb9b58f02a62ff0bfec98b997208e9d7a5998
+    (set +e; chktex -v -nall -w1 "$input" 2>&1; [ $? = 2 ] || exit 1; set -e)  | tee "$out"
+    # also check that the output does indeed contain "One warning printed"
+    grep "One warning printed" "$out"
+  '';
+
+  context = mkTeXTest {
+    name = "texlive-test-context";
+    format = "context";
+    texLive = texliveConTeXt;
+    text = ''
+      \starttext
+      \startsection[title={ConTeXt test document}]
+        This is an {\em incredibly} simple ConTeXt document.
+      \stopsection
+      \stoptext
+    '';
+  };
+
+  dvipng = lib.recurseIntoAttrs {
+    # https://github.com/NixOS/nixpkgs/issues/75605
+    basic = runCommand "texlive-test-dvipng-basic" {
+      nativeBuildInputs = [ file texliveMedium ];
+      input = fetchurl {
+        name = "test_dvipng.tex";
+        url = "http://git.savannah.nongnu.org/cgit/dvipng.git/plain/test_dvipng.tex?id=b872753590a18605260078f56cbd6f28d39dc035";
+        sha256 = "1pjpf1jvwj2pv5crzdgcrzvbmn7kfmgxa39pcvskl4pa0c9hl88n";
+      };
+    } ''
+      cp "$input" ./document.tex
+
+      latex document.tex
+      dvipng -T tight -strict -picky document.dvi
+      for f in document*.png; do
+        file "$f" | tee output
+        grep PNG output
+      done
+
+      mkdir "$out"
+      mv document*.png "$out"/
+    '';
+
+    # test dvipng's limited capability to render postscript specials via GS
+    ghostscript = runCommand "texlive-test-ghostscript" {
+      nativeBuildInputs = [ file (texliveSmall.withPackages (ps: [ ps.dvipng ])) ];
+      input = builtins.toFile "postscript-sample.tex" ''
+        \documentclass{minimal}
+        \begin{document}
+          Ni
+          \special{ps:
+            newpath
+            0 0 moveto
+            7 7 rlineto
+            0 7 moveto
+            7 -7 rlineto
+            stroke
+            showpage
+          }
+        \end{document}
+      '';
+      gs_trap = writeShellScript "gs_trap.sh" ''
+        exit 1
+      '';
+    } ''
+      cp "$gs_trap" ./gs
+      export PATH=$PWD:$PATH
+      # check that the trap works
+      gs && exit 1
+
+      cp "$input" ./document.tex
+
+      latex document.tex
+      dvipng -T 1in,1in -strict -picky document.dvi
+      for f in document*.png; do
+        file "$f" | tee output
+        grep PNG output
+      done
+
+      mkdir "$out"
+      mv document*.png "$out"/
+    '';
+  };
+
+  # https://github.com/NixOS/nixpkgs/issues/75070
+  dvisvgm = runCommand "texlive-test-dvisvgm" {
+    nativeBuildInputs = [ file texliveMedium ];
+    input = builtins.toFile "dvisvgm-sample.tex" ''
+      \documentclass{article}
+      \begin{document}
+        mwe
+      \end{document}
+    '';
+  } ''
+    cp "$input" ./document.tex
+
+    latex document.tex
+    dvisvgm document.dvi -n -o document_dvi.svg
+    cat document_dvi.svg
+    file document_dvi.svg | grep SVG
+
+    pdflatex document.tex
+    dvisvgm -P document.pdf -n -o document_pdf.svg
+    cat document_pdf.svg
+    file document_pdf.svg | grep SVG
+
+    mkdir "$out"
+    mv document*.svg "$out"/
+  '';
+
+  texdoc = runCommand "texlive-test-texdoc" {
+    nativeBuildInputs = [
+      (texlive.withPackages (ps: with ps; [ luatex ps.texdoc ps.texdoc.texdoc ]))
+    ];
+  } ''
+    texdoc --version
+
+    texdoc --debug --list texdoc | tee "$out"
+    grep texdoc.pdf "$out"
+  '';
+
+  # check that the default language is US English
+  defaultLanguage = lib.recurseIntoAttrs rec {
+    # language.def
+    etex = mkTeXTest {
+      name = "default-language-etex";
+      format = "etex";
+      text = ''
+        \catcode`\@=11
+        \ifnum\language=\lang@USenglish \message{[tests.texlive] Default language is US English.}
+        \else\errmessage{[tests.texlive] Error: default language is NOT US English.}\fi
+        \ifnum\language=0\message{[tests.texlive] Default language has id 0.}
+        \else\errmessage{[tests.texlive] Error: default language does NOT have id 0.}\fi
+        \bye
+      '';
+    };
+    # language.dat
+    latex = mkTeXTest {
+      name = "default-language-latex";
+      format = "latex";
+      text = ''
+        \makeatletter
+        \ifnum\language=\l@USenglish \GenericWarning{}{[tests.texlive] Default language is US English}
+        \else\GenericError{}{[tests.texlive] Error: default language is NOT US English}{}{}\fi
+        \ifnum\language=0\GenericWarning{}{[tests.texlive] Default language has id 0}
+        \else\GenericError{}{[tests.texlive] Error: default language does NOT have id 0}{}{}\fi
+        \stop
+      '';
+    };
+    # language.dat.lua
+    luatex = etex.override {
+      name = "default-language-luatex";
+      format = "luatex";
+    };
+  };
+
+  # check that all languages are available, including synonyms
+  allLanguages = let hyphenBase = texlive.pkgs.hyphen-base; texLive = texliveFull; in
+    lib.recurseIntoAttrs {
+      # language.def
+      etex = mkTeXTest {
+        name = "all-languages-etex";
+        format = "etex";
+        inherit hyphenBase texLive;
+        text = ''
+          \catcode`\@=11
+          \input kvsetkeys.sty
+          \def\CheckLang#1{
+            \ifcsname lang@#1\endcsname\message{[tests.texlive] Found language #1}
+            \else\errmessage{[tests.texlive] Error: missing language #1}\fi
+          }
+          \comma@parse{@texLanguages@}\CheckLang
+          \bye
+        '';
+        preTest = ''
+          texLanguages="$(sed -n -E 's/^\\addlanguage\s*\{([^}]+)\}.*$/\1/p' < "$hyphenBase"/tex/generic/config/language.def)"
+          texLanguages="''${texLanguages//$'\n'/,}"
+          substituteInPlace "$name.tex" --subst-var texLanguages
+        '';
+      };
+      # language.dat
+      latex = mkTeXTest {
+        name = "all-languages-latex";
+        format = "latex";
+        inherit hyphenBase texLive;
+        text = ''
+          \makeatletter
+          \@for\Lang:=italian,@texLanguages@\do{
+            \ifcsname l@\Lang\endcsname
+              \GenericWarning{}{[tests.texlive] Found language \Lang}
+            \else
+              \GenericError{}{[tests.texlive] Error: missing language \Lang}{}{}
+            \fi
+          }
+          \stop
+        '';
+        preTest = ''
+          texLanguages="$(sed -n -E 's/^([^%= \t]+).*$/\1/p' < "$hyphenBase"/tex/generic/config/language.dat)"
+          texLanguages="''${texLanguages//$'\n'/,}"
+          substituteInPlace "$name.tex" --subst-var texLanguages
+        '';
+      };
+      # language.dat.lua
+      luatex = mkTeXTest {
+        name = "all-languages-luatex";
+        format = "luatex";
+        inherit hyphenBase texLive;
+        text = ''
+          \directlua{
+            require('luatex-hyphen.lua')
+            langs = '@texLanguages@,'
+            texio.write('\string\n')
+            for l in langs:gmatch('([^,]+),') do
+              if luatexhyphen.lookupname(l) \string~= nil then
+                texio.write('[tests.texlive] Found language '..l..'.\string\n')
+              else
+                error('[tests.texlive] Error: missing language '..l..'.', 2)
+              end
+            end
+          }
+          \bye
+        '';
+        preTest = ''
+          texLanguages="$(sed -n -E 's/^.*\[("|'\''')(.*)("|'\''')].*$/\2/p' < "$hyphenBase"/tex/generic/config/language.dat.lua)"
+          texLanguages="''${texLanguages//$'\n'/,}"
+          substituteInPlace "$name.tex" --subst-var texLanguages
+        '';
+      };
+    };
+
+  # test that language files are generated as expected
+  hyphen-base = runCommand "texlive-test-hyphen-base" {
+    hyphenBase = texlive.pkgs.hyphen-base;
+    schemeFull = texliveFull;
+    schemeInfraOnly = texliveInfraOnly;
+  } ''
+    mkdir -p "$out"/{scheme-infraonly,scheme-full}
+
+    # create language files with no hyphenation patterns
+    cat "$hyphenBase"/tex/generic/config/language.us >language.dat
+    cat "$hyphenBase"/tex/generic/config/language.us.def >language.def
+    cat "$hyphenBase"/tex/generic/config/language.us.lua >language.dat.lua
+
+    cat >>language.dat.lua <<EOF
+    }
+    EOF
+
+    cat >>language.def <<EOF
+    %%% No changes may be made beyond this point.
+
+    \uselanguage {USenglish}             %%% This MUST be the last line of the file.
+    EOF
+
+    for fname in language.{dat,def,dat.lua} ; do
+      diff --ignore-matching-lines='^\(%\|--\) Generated by ' -u \
+        {"$hyphenBase","$schemeFull"/share/texmf-var}/tex/generic/config/"$fname" \
+        | tee "$out/scheme-full/$fname.patch"
+      diff --ignore-matching-lines='^\(%\|--\) Generated by ' -u \
+        {,"$schemeInfraOnly"/share/texmf-var/tex/generic/config/}"$fname" \
+        | tee "$out/scheme-infraonly/$fname.patch"
+    done
+  '';
+
+  # test that fmtutil.cnf is fully regenerated on scheme-full
+  fmtutilCnf = runCommand "texlive-test-fmtutil.cnf" {
+    kpathsea = texlive.pkgs.kpathsea.tex;
+    schemeFull = texliveFull;
+  } ''
+    mkdir -p "$out"
+
+    diff --ignore-matching-lines='^# Generated by ' -u \
+      {"$kpathsea","$schemeFull"/share/texmf-var}/web2c/fmtutil.cnf \
+      | tee "$out/fmtutil.cnf.patch"
+  '';
+
+  # verify that the restricted mode gets enabled when
+  # needed (detected by checking if it disallows --gscmd)
+  repstopdf = runCommand "texlive-test-repstopdf" {
+    nativeBuildInputs = [ (texlive.withPackages (ps: [ ps.epstopdf ])) ];
+  } ''
+    ! (epstopdf --gscmd echo /dev/null 2>&1 || true) | grep forbidden >/dev/null
+    (repstopdf --gscmd echo /dev/null 2>&1 || true) | grep forbidden >/dev/null
+    mkdir "$out"
+  '';
+
+  # verify that the restricted mode gets enabled when
+  # needed (detected by checking if it disallows --gscmd)
+  rpdfcrop = runCommand "texlive-test-rpdfcrop" {
+    nativeBuildInputs = [ (texlive.withPackages (ps: [ ps.pdfcrop ])) ];
+  } ''
+    ! (pdfcrop --gscmd echo $(command -v pdfcrop) 2>&1 || true) | grep 'restricted mode' >/dev/null
+    (rpdfcrop --gscmd echo $(command -v pdfcrop) 2>&1 || true) | grep 'restricted mode' >/dev/null
+    mkdir "$out"
+  '';
+
+  # check that all binaries run successfully, in the following sense:
+  # (1) run --version, -v, --help, -h successfully; or
+  # (2) run --help, -h, or no argument with error code but show help text; or
+  # (3) run successfully on a test.tex or similar file
+  # we ignore the binaries that cannot be tested as above, and are either
+  # compiled binaries or trivial shell wrappers
+  binaries = let
+      # TODO known broken binaries
+      broken = [
+        # *.inc files in source container rather than run
+        "texaccents"
+
+        # 'Error initialising QuantumRenderer: no suitable pipeline found'
+        "tlcockpit"
+      ] ++ lib.optional stdenv.isDarwin "epspdftk";  # wish shebang is a script, not a binary!
+
+      # (1) binaries requiring -v
+      shortVersion = [ "devnag" "diadia" "pmxchords" "ptex2pdf" "simpdftex" "ttf2afm" ];
+      # (1) binaries requiring --help or -h
+      help = [ "arlatex" "bundledoc" "cachepic" "checklistings" "dvipos" "extractres" "fig4latex" "fragmaster"
+        "kpsewhere" "latex-git-log" "ltxfileinfo" "mendex" "perltex" "pn2pdf" "psbook" "psnup" "psresize" "purifyeps"
+        "simpdftex" "tex2xindy" "texluac" "texluajitc" "upmendex" "urlbst" "yplan" ];
+      shortHelp = [ "adhocfilelist" "authorindex" "bbl2bib" "bibdoiadd" "bibmradd" "biburl2doi" "bibzbladd" "ctanupload"
+        "disdvi" "dvibook" "dviconcat" "getmapdl" "latex2man" "listings-ext.sh" "pygmentex" ];
+      # (2) binaries that return non-zero exit code even if correctly asked for help
+      ignoreExitCode = [ "authorindex" "dvibook" "dviconcat" "dvipos" "extractres" "fig4latex" "fragmaster" "latex2man"
+        "latex-git-log" "listings-ext.sh" "psbook" "psnup" "psresize" "purifyeps" "tex2xindy"  "texluac"
+        "texluajitc" ];
+      # (2) binaries that print help on no argument, returning non-zero exit code
+      noArg = [ "a2ping" "bg5+latex" "bg5+pdflatex" "bg5latex" "bg5pdflatex" "cef5latex" "cef5pdflatex" "ceflatex"
+        "cefpdflatex" "cefslatex" "cefspdflatex" "chkdvifont" "dvi2fax" "dvired" "dviselect" "dvitodvi" "epsffit"
+        "findhyph" "gbklatex" "gbkpdflatex" "komkindex" "kpsepath" "listbib" "listings-ext" "mag" "mathspic" "mf2pt1"
+        "mk4ht" "mkt1font" "mkgrkindex" "musixflx" "pdf2ps" "pdfclose" "pdftosrc" "pdfxup" "pedigree" "pfb2pfa" "pk2bm"
+        "prepmx" "ps2pk" "psselect" "pstops" "rubibtex" "rubikrotation" "sjislatex" "sjispdflatex" "srcredact" "t4ht"
+        "teckit_compile" "tex4ht" "texdiff" "texdirflatten" "texplate" "tie" "ttf2kotexfont" "ttfdump" "vlna" "vpl2ovp"
+        "vpl2vpl" "yplan" ];
+      # (3) binaries requiring a .tex file
+      contextTest = [ "htcontext" ];
+      latexTest = [ "de-macro" "e2pall" "htlatex" "htxelatex" "makeindex" "pslatex" "rumakeindex" "tpic2pdftex"
+        "wordcount" "xhlatex" ];
+      texTest = [ "fontinst" "htmex" "httex" "httexi" "htxetex" ];
+      # tricky binaries or scripts that are obviously working but are hard to test
+      # (e.g. because they expect user input no matter the arguments)
+      # (printafm comes from ghostscript, not texlive)
+      ignored = [
+        # compiled binaries
+        "dt2dv" "dv2dt" "dvi2tty" "dvidvi" "dvispc" "otp2ocp" "outocp" "pmxab"
+
+        # GUI scripts that accept no argument or crash without a graphics server; please test manualy
+        "epspdftk" "texdoctk" "tlshell" "xasy"
+
+        # requires Cinderella, not open source and not distributed via Nixpkgs
+        "ketcindy"
+      ];
+      # binaries that need a combined scheme and cannot work standalone
+      needScheme = [
+        # pfarrei: require working kpse to find lua module
+        "a5toa4"
+
+        # bibexport: requires kpsewhich
+        "bibexport"
+
+        # crossrefware: require bibtexperllibs under TEXMFROOT
+        "bbl2bib" "bibdoiadd" "bibmradd" "biburl2doi" "bibzbladd" "checkcites" "ltx2crossrefxml"
+
+        # epstopdf: requires kpsewhich
+        "epstopdf" "repstopdf"
+
+        # requires kpsewhich
+        "memoize-extract.pl" "memoize-extract.py"
+
+        # require other texlive binaries in PATH
+        "allcm" "allec" "chkweb" "fontinst" "ht*" "installfont-tl" "kanji-config-updmap-sys" "kanji-config-updmap-user"
+        "kpse*" "latexfileversion" "mkocp" "mkofm" "mtxrunjit" "pdftex-quiet" "pslatex" "rumakeindex" "texconfig"
+        "texconfig-sys" "texexec" "texlinks" "texmfstart" "typeoutfileinfo" "wordcount" "xdvi" "xhlatex"
+
+        # misc luatex binaries searching for luatex in PATH
+        "citeproc-lua" "context" "contextjit" "ctanbib" "digestif" "epspdf" "l3build" "luafindfont" "luaotfload-tool"
+        "luatools" "make4ht" "pmxchords" "tex4ebook" "texblend" "texdoc" "texfindpkg" "texlogsieve" "xindex"
+
+        # requires full TEXMFROOT (e.g. for config)
+        "mktexfmt" "mktexmf" "mktexpk" "mktextfm" "psnup" "psresize" "pstops" "tlmgr" "updmap" "webquiz"
+
+        # texlive-scripts: requires texlive.infra's TeXLive::TLUtils under TEXMFROOT
+        "fmtutil" "fmtutil-sys" "fmtutil-user"
+
+        # texlive-scripts: not used in nixpkgs, need updmap in PATH
+        "updmap-sys" "updmap-user"
+      ];
+
+      # simple test files
+      contextTestTex = writeText "context-test.tex" ''
+        \starttext
+          A simple test file.
+        \stoptext
+      '';
+      latexTestTex = writeText "latex-test.tex" ''
+        \documentclass{article}
+        \begin{document}
+          A simple test file.
+        \end{document}
+      '';
+      texTestTex = writeText "tex-test.tex" ''
+        Hello.
+        \bye
+      '';
+
+      # link all binaries in single derivation
+      binPackages = lib.catAttrs "out" (lib.attrValues texlive.pkgs);
+      binaries = buildEnv { name = "texlive-binaries"; paths = binPackages; };
+    in
+    runCommand "texlive-test-binaries"
+      {
+        inherit binaries contextTestTex latexTestTex texTestTex;
+        texliveScheme = texliveFull;
+      }
+      ''
+        loadables="$(command -v bash)"
+        loadables="''${loadables%/bin/bash}/lib/bash"
+        enable -f "$loadables/realpath" realpath
+        mkdir -p "$out"
+        export HOME="$(mktemp -d)"
+        declare -i binCount=0 ignoredCount=0 brokenCount=0 failedCount=0
+        cp "$contextTestTex" context-test.tex
+        cp "$latexTestTex" latex-test.tex
+        cp "$texTestTex" tex-test.tex
+
+        testBin () {
+          path="$(realpath "$bin")"
+          path="''${path##*/}"
+          if [[ -z "$ignoreExitCode" ]] ; then
+            PATH="$path" "$bin" $args >"$out/$base.log" 2>&1
+            ret=$?
+            if [[ $ret == 0 ]] && grep -i 'command not found' "$out/$base.log" >/dev/null ; then
+              echo "command not found when running '$base''${args:+ $args}'"
+              return 1
+            fi
+            return $ret
+          else
+            PATH="$path" "$bin" $args >"$out/$base.log" 2>&1
+            ret=$?
+            if [[ $ret == 0 ]] && grep -i 'command not found' "$out/$base.log" >/dev/null ; then
+              echo "command not found when running '$base''${args:+ $args}'"
+              return 1
+            fi
+            if ! grep -Ei '(Example:|Options:|Syntax:|Usage:|improper command|SYNOPSIS)' "$out/$base.log" >/dev/null ; then
+              echo "did not find usage info when running '$base''${args:+ $args}'"
+              return $ret
+            fi
+          fi
+        }
+
+        for bin in "$binaries"/bin/* ; do
+          base="''${bin##*/}"
+          args=
+          ignoreExitCode=
+          binCount=$((binCount + 1))
+
+          # ignore non-executable files (such as context.lua)
+          if [[ ! -x "$bin" ]] ; then
+            ignoredCount=$((ignoredCount + 1))
+            continue
+          fi
+
+          case "$base" in
+            ${lib.concatStringsSep "|" ignored})
+              ignoredCount=$((ignoredCount + 1))
+              continue ;;
+            ${lib.concatStringsSep "|" broken})
+              brokenCount=$((brokenCount + 1))
+              continue ;;
+            ${lib.concatStringsSep "|" help})
+              args=--help ;;
+            ${lib.concatStringsSep "|" shortHelp})
+              args=-h ;;
+            ${lib.concatStringsSep "|" noArg})
+              ;;
+            ${lib.concatStringsSep "|" contextTest})
+              args=context-test.tex ;;
+            ${lib.concatStringsSep "|" latexTest})
+              args=latex-test.tex ;;
+            ${lib.concatStringsSep "|" texTest})
+              args=tex-test.tex ;;
+            ${lib.concatStringsSep "|" shortVersion})
+              args=-v ;;
+            ebong)
+              touch empty
+              args=empty ;;
+            ht)
+              args='latex latex-test.tex' ;;
+            pdf2dsc)
+              args='--help --help --help' ;;
+            typeoutfileinfo)
+              args=/dev/null ;;
+            *)
+              args=--version ;;
+          esac
+
+          case "$base" in
+            ${lib.concatStringsSep "|" (ignoreExitCode ++ noArg)})
+              ignoreExitCode=1 ;;
+          esac
+
+          case "$base" in
+            ${lib.concatStringsSep "|" needScheme})
+              bin="$texliveScheme/bin/$base"
+              if [[ ! -f "$bin" ]] ; then
+                ignoredCount=$((ignoredCount + 1))
+                continue
+              fi ;;
+          esac
+
+          if testBin ; then : ; else # preserve exit code
+            echo "failed '$base''${args:+ $args}' (exit code: $?)"
+            sed 's/^/  > /' < "$out/$base.log"
+            failedCount=$((failedCount + 1))
+          fi
+        done
+
+        echo "tested $binCount binaries: $ignoredCount ignored, $brokenCount broken, $failedCount failed"
+        [[ $failedCount = 0 ]]
+      '';
+
+  # check that all scripts have a Nix shebang
+  shebangs = let
+      binPackages = lib.catAttrs "out" (lib.attrValues texlive.pkgs);
+    in
+    runCommand "texlive-test-shebangs" { }
+      (''
+        echo "checking that all texlive scripts shebangs are in '$NIX_STORE'"
+        declare -i scriptCount=0 invalidCount=0
+      '' +
+      (lib.concatMapStrings
+        (pkg: ''
+          for bin in '${pkg.outPath}'/bin/* ; do
+            grep -I -q . "$bin" || continue  # ignore binary files
+            [[ -x "$bin" ]] || continue # ignore non-executable files (such as context.lua)
+            scriptCount=$((scriptCount + 1))
+            read -r cmdline < "$bin"
+            read -r interp <<< "$cmdline"
+            if [[ "$interp" != "#!$NIX_STORE"/* && "$interp" != "#! $NIX_STORE"/* ]] ; then
+              echo "error: non-nix shebang '$interp' in script '$bin'"
+              invalidCount=$((invalidCount + 1))
+            fi
+          done
+        '')
+        binPackages)
+      + ''
+        echo "checked $scriptCount scripts, found $invalidCount non-nix shebangs"
+        [[ $invalidCount -gt 0 ]] && exit 1
+        mkdir -p "$out"
+      ''
+      );
+
+  # verify that the precomputed licensing information in default.nix
+  # does indeed match the metadata of the individual packages.
+  #
+  # This is part of the test suite (and not the normal evaluation) to save
+  # time for "normal" evaluations. To be more in line with the other tests, this
+  # also builds a derivation, even though it is essentially an eval-time assertion.
+  licenses =
+    let
+        concatLicenses = builtins.foldl' (acc: el: if builtins.elem el acc then acc else acc ++ [ el ]);
+        # converts a license to its attribute name in lib.licenses
+        licenseToAttrName = license:
+          builtins.head (builtins.attrNames
+            (lib.filterAttrs (n: v: license == v) lib.licenses));
+        lt = (a: b: a < b);
+
+        savedLicenses = scheme: scheme.meta.license;
+        savedLicensesAttrNames = scheme: map licenseToAttrName (savedLicenses scheme);
+
+        correctLicenses = scheme: builtins.foldl'
+                (acc: pkg: concatLicenses acc (lib.toList (pkg.meta.license or [])))
+                []
+                scheme.passthru.requiredTeXPackages;
+        correctLicensesAttrNames = scheme:
+          lib.sort lt
+            (map licenseToAttrName (correctLicenses scheme));
+
+        hasLicenseMismatch = scheme:
+          (lib.isDerivation scheme) &&
+          (savedLicensesAttrNames scheme) != (correctLicensesAttrNames scheme);
+        incorrectSchemes = lib.filterAttrs
+          (n: hasLicenseMismatch)
+          (texlive.combined // texlive.schemes);
+        prettyPrint = name: scheme:
+          ''
+            license info for ${name} is incorrect! Note that order is enforced.
+            saved: [ ${lib.concatStringsSep " " (savedLicensesAttrNames scheme)} ]
+            correct: [ ${lib.concatStringsSep " " (correctLicensesAttrNames scheme)} ]
+          '';
+        errorText = lib.concatStringsSep "\n\n" (lib.mapAttrsToList prettyPrint incorrectSchemes);
+      in
+        runCommand "texlive-test-license" {
+          inherit errorText;
+        }
+        (if (incorrectSchemes == {})
+        then "echo everything is fine! > $out"
+        else ''
+          echo "$errorText"
+          false
+        '');
+
+  # verify that all fixed hashes are present
+  # this is effectively an eval-time assertion, converted into a derivation for
+  # ease of testing
+  fixedHashes = with lib; let
+    fods = lib.concatMap
+      (p: lib.optional (p ? tex && isDerivation p.tex) p.tex
+        ++ lib.optional (p ? texdoc) p.texdoc
+        ++ lib.optional (p ? texsource) p.texsource
+        ++ lib.optional (p ? tlpkg) p.tlpkg)
+      (attrValues texlive.pkgs);
+    errorText = concatMapStrings (p: optionalString (! p ? outputHash) "${p.pname}-${p.tlOutputName} does not have a fixed output hash\n") fods;
+  in runCommand "texlive-test-fixed-hashes" {
+    inherit errorText;
+    passAsFile = [ "errorText" ];
+  } ''
+    if [[ -s "$errorTextPath" ]] ; then
+      cat "$errorTextPath"
+      echo Failed: some TeX Live packages do not have fixed output hashes. Please read UPGRADING.md for how to generate a new fixed-hashes.nix.
+      exit 1
+    else
+      touch "$out"
+    fi
+  '';
+}
diff --git a/nixpkgs/pkgs/test/top-level/default.nix b/nixpkgs/pkgs/test/top-level/default.nix
new file mode 100644
index 000000000000..fdb9fe09a88b
--- /dev/null
+++ b/nixpkgs/pkgs/test/top-level/default.nix
@@ -0,0 +1,47 @@
+{ lib, pkgs, ... }:
+let
+  nixpkgsFun = import ../../top-level;
+in
+lib.recurseIntoAttrs {
+  platformEquality =
+    let
+      configsLocal = [
+        # crossSystem is implicitly set to localSystem.
+        {
+          localSystem = { system = "x86_64-linux"; };
+        }
+        {
+          localSystem = { system = "aarch64-linux"; };
+          crossSystem = null;
+        }
+        # Both systems explicitly set to the same string.
+        {
+          localSystem = { system = "x86_64-linux"; };
+          crossSystem = { system = "x86_64-linux"; };
+        }
+        # Vendor and ABI inferred from system double.
+        {
+          localSystem = { system = "aarch64-linux"; };
+          crossSystem = { config = "aarch64-unknown-linux-gnu"; };
+        }
+      ];
+      configsCross = [
+        # GNU is inferred from double, but config explicitly requests musl.
+        {
+          localSystem = { system = "aarch64-linux"; };
+          crossSystem = { config = "aarch64-unknown-linux-musl"; };
+        }
+        # Cross-compile from AArch64 to x86-64.
+        {
+          localSystem = { system = "aarch64-linux"; };
+          crossSystem = { system = "x86_64-unknown-linux-gnu"; };
+        }
+      ];
+
+      pkgsLocal = map nixpkgsFun configsLocal;
+      pkgsCross = map nixpkgsFun configsCross;
+    in
+    assert lib.all (p: p.buildPlatform == p.hostPlatform) pkgsLocal;
+    assert lib.all (p: p.buildPlatform != p.hostPlatform) pkgsCross;
+    pkgs.emptyFile;
+}
diff --git a/nixpkgs/pkgs/test/vim/default.nix b/nixpkgs/pkgs/test/vim/default.nix
new file mode 100644
index 000000000000..33e1e551d4f9
--- /dev/null
+++ b/nixpkgs/pkgs/test/vim/default.nix
@@ -0,0 +1,26 @@
+{ vimUtils, vim-full, writeText, vimPlugins
+, lib, fetchFromGitHub
+, pkgs
+}:
+let
+  inherit (vimUtils) buildVimPlugin;
+
+  packages.myVimPackage.start = with vimPlugins; [ vim-nix ];
+
+in
+  pkgs.recurseIntoAttrs (rec {
+  vim_empty_config = vimUtils.vimrcFile { beforePlugins = ""; customRC = ""; };
+
+  ### vim tests
+  ##################
+
+  test_vim_with_vim_nix_using_plug = vim-full.customize {
+    name = "vim-with-vim-addon-nix-using-plug";
+    vimrcConfig.plug.plugins = with vimPlugins; [ vim-nix ];
+  };
+
+  test_vim_with_vim_nix = vim-full.customize {
+    name = "vim-with-vim-addon-nix";
+    vimrcConfig.packages.myVimPackage.start = with vimPlugins; [ vim-nix ];
+  };
+})