diff --git a/.github/workflows/test.yml b/.github/workflows/ci.yml
similarity index 63%
rename from .github/workflows/test.yml
rename to .github/workflows/ci.yml
index 1b655e27d..09436b7e3 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/ci.yml
@@ -1,4 +1,4 @@
-name: "Test"
+name: "CI"
on:
pull_request:
@@ -25,7 +25,7 @@ jobs:
name: '${{ env.CACHIX_NAME }}'
signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}'
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
- - run: nix-build -A checks.$(nix-instantiate --eval -E '(builtins.currentSystem)')
+ - run: nix --experimental-features 'nix-command flakes' flake check -L
check_cachix:
name: Cachix secret present for installer tests
@@ -74,3 +74,35 @@ jobs:
install_url: '${{needs.installer.outputs.installerURL}}'
install_options: "--tarball-url-prefix https://${{ env.CACHIX_NAME }}.cachix.org/serve"
- run: nix-instantiate -E 'builtins.currentTime' --eval
+
+ docker_push_image:
+ needs: [check_cachix, tests]
+ if: >-
+ github.event_name == 'push' &&
+ github.ref_name == 'master' &&
+ needs.check_cachix.outputs.secret == 'true'
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2.4.0
+ with:
+ fetch-depth: 0
+ - uses: cachix/install-nix-action@v16
+ - run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
+ - run: echo NIX_VERSION="$(nix-instantiate --eval -E '(import ./default.nix).defaultPackage.${builtins.currentSystem}.version' | tr -d \")" >> $GITHUB_ENV
+ - uses: cachix/cachix-action@v10
+ if: needs.check_cachix.outputs.secret == 'true'
+ with:
+ name: '${{ env.CACHIX_NAME }}'
+ signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}'
+ authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
+ - run: nix --experimental-features 'nix-command flakes' build .#dockerImage -L
+ - run: docker load -i ./result/image.tar.gz
+ - run: docker tag nix:$NIX_VERSION nixos/nix:$NIX_VERSION
+ - run: docker tag nix:$NIX_VERSION nixos/nix:master
+ - name: Login to Docker Hub
+ uses: docker/login-action@v1
+ with:
+ username: ${{ secrets.DOCKERHUB_USERNAME }}
+ password: ${{ secrets.DOCKERHUB_TOKEN }}
+ - run: docker push nixos/nix:$NIX_VERSION
+ - run: docker push nixos/nix:master
diff --git a/.gitignore b/.gitignore
index 2889a56eb..4b290425a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -120,3 +120,7 @@ GTAGS
compile_commands.json
nix-rust/target
+
+result
+
+.vscode/
diff --git a/.version b/.version
index 914ec9671..9aa34646d 100644
--- a/.version
+++ b/.version
@@ -1 +1 @@
-2.6.0
\ No newline at end of file
+2.7.0
\ No newline at end of file
diff --git a/Makefile b/Makefile
index e6ce50cbd..5040d2884 100644
--- a/Makefile
+++ b/Makefile
@@ -10,7 +10,6 @@ makefiles = \
src/libexpr/local.mk \
src/libcmd/local.mk \
src/nix/local.mk \
- src/nlohmann/local.mk \
src/resolve-system-dependencies/local.mk \
scripts/local.mk \
misc/bash/local.mk \
diff --git a/Makefile.config.in b/Makefile.config.in
index c8c4446b4..3505f337e 100644
--- a/Makefile.config.in
+++ b/Makefile.config.in
@@ -16,6 +16,7 @@ LDFLAGS = @LDFLAGS@
LIBARCHIVE_LIBS = @LIBARCHIVE_LIBS@
LIBBROTLI_LIBS = @LIBBROTLI_LIBS@
LIBCURL_LIBS = @LIBCURL_LIBS@
+LOWDOWN_LIBS = @LOWDOWN_LIBS@
OPENSSL_LIBS = @OPENSSL_LIBS@
LIBSECCOMP_LIBS = @LIBSECCOMP_LIBS@
PACKAGE_NAME = @PACKAGE_NAME@
diff --git a/configure.ac b/configure.ac
index c35065704..8a01c33ec 100644
--- a/configure.ac
+++ b/configure.ac
@@ -262,13 +262,17 @@ fi
PKG_CHECK_MODULES([GTEST], [gtest_main])
+# Look for nlohmann/json.
+PKG_CHECK_MODULES([NLOHMANN_JSON], [nlohmann_json >= 3.9])
+
+
# documentation generation switch
AC_ARG_ENABLE(doc-gen, AS_HELP_STRING([--disable-doc-gen],[disable documentation generation]),
doc_generate=$enableval, doc_generate=yes)
AC_SUBST(doc_generate)
# Look for lowdown library.
-PKG_CHECK_MODULES([LOWDOWN], [lowdown >= 0.8.0], [CXXFLAGS="$LOWDOWN_CFLAGS $CXXFLAGS"])
+PKG_CHECK_MODULES([LOWDOWN], [lowdown >= 0.9.0], [CXXFLAGS="$LOWDOWN_CFLAGS $CXXFLAGS"])
# Setuid installations.
AC_CHECK_FUNCS([setresuid setreuid lchown])
diff --git a/default.nix b/default.nix
index 71d1a80ad..00ec5b617 100644
--- a/default.nix
+++ b/default.nix
@@ -1,3 +1,3 @@
-(import (fetchTarball https://github.com/edolstra/flake-compat/archive/master.tar.gz) {
+(import (fetchTarball "https://github.com/edolstra/flake-compat/archive/master.tar.gz") {
src = ./.;
}).defaultNix
diff --git a/doc/manual/generate-builtins.nix b/doc/manual/generate-builtins.nix
index 92c7b1a31..6c8b88da2 100644
--- a/doc/manual/generate-builtins.nix
+++ b/doc/manual/generate-builtins.nix
@@ -6,9 +6,9 @@ builtins:
concatStrings (map
(name:
let builtin = builtins.${name}; in
- "
${name} "
+ "${name} "
+ concatStringsSep " " (map (s: "${s}") builtin.args)
- + ""
+ + ""
+ "\n\n"
+ builtin.doc
+ "\n\n"
diff --git a/doc/manual/generate-options.nix b/doc/manual/generate-options.nix
index 9a77f4d36..84d90beb6 100644
--- a/doc/manual/generate-options.nix
+++ b/doc/manual/generate-options.nix
@@ -20,7 +20,7 @@ concatStrings (map
# JSON, but that converts to "{ }" here.
(if isAttrs option.value then "`\"\"`"
else "`" + toString option.value + "`")) + "\n\n"
- else " **Default:** *machine-specific*")
+ else " **Default:** *machine-specific*\n")
+ (if option.aliases != []
then " **Deprecated alias:** " + (concatStringsSep ", " (map (s: "`${s}`") option.aliases)) + "\n\n"
else "")
diff --git a/doc/manual/local.mk b/doc/manual/local.mk
index 6b232a736..c1ce8aaeb 100644
--- a/doc/manual/local.mk
+++ b/doc/manual/local.mk
@@ -72,6 +72,7 @@ $(d)/builtins.json: $(bindir)/nix
@mv $@.tmp $@
# Generate the HTML manual.
+html: $(docdir)/manual/index.html
install: $(docdir)/manual/index.html
# Generate 'nix' manpages.
diff --git a/doc/manual/src/SUMMARY.md.in b/doc/manual/src/SUMMARY.md.in
index 2876be52a..4e2afa20e 100644
--- a/doc/manual/src/SUMMARY.md.in
+++ b/doc/manual/src/SUMMARY.md.in
@@ -72,6 +72,7 @@
- [CLI guideline](contributing/cli-guideline.md)
- [Release Notes](release-notes/release-notes.md)
- [Release X.Y (202?-??-??)](release-notes/rl-next.md)
+ - [Release 2.6 (2022-01-24)](release-notes/rl-2.6.md)
- [Release 2.5 (2021-12-13)](release-notes/rl-2.5.md)
- [Release 2.4 (2021-11-01)](release-notes/rl-2.4.md)
- [Release 2.3 (2019-09-04)](release-notes/rl-2.3.md)
diff --git a/doc/manual/src/command-ref/nix-shell.md b/doc/manual/src/command-ref/nix-shell.md
index 873311649..a2b6d8a8e 100644
--- a/doc/manual/src/command-ref/nix-shell.md
+++ b/doc/manual/src/command-ref/nix-shell.md
@@ -101,7 +101,8 @@ The following common options are supported:
- `NIX_BUILD_SHELL`\
Shell used to start the interactive environment. Defaults to the
- `bash` found in `PATH`.
+ `bash` found in ``, falling back to the `bash` found in
+ `PATH` if not found.
# Examples
diff --git a/doc/manual/src/command-ref/nix-store.md b/doc/manual/src/command-ref/nix-store.md
index 26292f1bb..7db9f0c1c 100644
--- a/doc/manual/src/command-ref/nix-store.md
+++ b/doc/manual/src/command-ref/nix-store.md
@@ -321,8 +321,8 @@ symlink.
This query has one option:
- `--include-outputs`
- Also include the output path of store derivations, and their
- closures.
+ Also include the existing output paths of store derivations,
+ and their closures.
This query can be used to implement various kinds of deployment. A
*source deployment* is obtained by distributing the closure of a
diff --git a/doc/manual/src/expressions/language-constructs.md b/doc/manual/src/expressions/language-constructs.md
index cb0169239..1c01f2cc7 100644
--- a/doc/manual/src/expressions/language-constructs.md
+++ b/doc/manual/src/expressions/language-constructs.md
@@ -284,6 +284,10 @@ The points of interest are:
function is called with the `localServer` argument set to `true` but
the `db4` argument set to `null`, then the evaluation fails.
+ Note that `->` is the [logical
+ implication](https://en.wikipedia.org/wiki/Truth_table#Logical_implication)
+ Boolean operation.
+
2. This is a more subtle condition: if Subversion is built with Apache
(`httpServer`) support, then the Expat library (an XML library) used
by Subversion should be same as the one used by Apache. This is
diff --git a/doc/manual/src/release-notes/rl-2.4.md b/doc/manual/src/release-notes/rl-2.4.md
index 0f632f100..8b566fc7b 100644
--- a/doc/manual/src/release-notes/rl-2.4.md
+++ b/doc/manual/src/release-notes/rl-2.4.md
@@ -276,6 +276,9 @@ more than 2800 commits from 195 contributors since release 2.3.
* Plugins can now register `nix` subcommands.
+* The `--indirect` flag to `nix-store --add-root` has become a no-op.
+ `--add-root` will always generate indirect GC roots from now on.
+
## Incompatible changes
* The `nix` command is now marked as an experimental feature. This
diff --git a/doc/manual/src/release-notes/rl-2.6.md b/doc/manual/src/release-notes/rl-2.6.md
new file mode 100644
index 000000000..280faead1
--- /dev/null
+++ b/doc/manual/src/release-notes/rl-2.6.md
@@ -0,0 +1,21 @@
+# Release 2.6 (2022-01-24)
+
+* The Nix CLI now searches for a `flake.nix` up until the root of the current
+ Git repository or a filesystem boundary rather than just in the current
+ directory.
+* The TOML parser used by `builtins.fromTOML` has been replaced by [a
+ more compliant one](https://github.com/ToruNiina/toml11).
+* Added `:st`/`:show-trace` commands to `nix repl`, which are used to
+ set or toggle display of error traces.
+* New builtin function `builtins.zipAttrsWith` with the same
+ functionality as `lib.zipAttrsWith` from Nixpkgs, but much more
+ efficient.
+* New command `nix store copy-log` to copy build logs from one store
+ to another.
+* The `commit-lockfile-summary` option can be set to a non-empty
+ string to override the commit summary used when commiting an updated
+ lockfile. This may be used in conjunction with the `nixConfig`
+ attribute in `flake.nix` to better conform to repository
+ conventions.
+* `docker run -ti nixos/nix:master` will place you in the Docker
+ container with the latest version of Nix from the `master` branch.
diff --git a/doc/manual/src/release-notes/rl-next.md b/doc/manual/src/release-notes/rl-next.md
index a516887b1..7dd8387d8 100644
--- a/doc/manual/src/release-notes/rl-next.md
+++ b/doc/manual/src/release-notes/rl-next.md
@@ -1,8 +1,28 @@
# Release X.Y (202?-??-??)
-* The TOML parser used by `builtins.fromTOML` has been replaced by [a
- more compliant one](https://github.com/ToruNiina/toml11).
-* Added `:st`/`:show-trace` commands to nix repl, which are used to
- set or toggle display of error traces.
-* New builtin function `builtins.zipAttrsWith` with same functionality
- as `lib.zipAttrsWith` from nixpkgs, but much more efficient.
+* A number of "default" flake output attributes have been
+ renamed. These are:
+
+ * `defaultPackage.` → `packages..default`
+ * `defaultApps.` → `apps..default`
+ * `defaultTemplate` → `templates.default`
+ * `defaultBundler.` → `bundlers..default`
+ * `overlay` → `overlays.default`
+ * `devShell.` → `devShells..default`
+
+ The old flake output attributes still work, but `nix flake check`
+ will warn about them.
+
+* `nix bundle` breaking API change now supports bundlers of the form
+ `bundler..= derivation: another-derivation;`. This supports
+ additional functionality to inspect evaluation information during bundling. A
+ new [repository](https://github.com/NixOS/bundlers) has various bundlers
+ implemented.
+
+* `nix store ping` now reports the version of the remote Nix daemon.
+
+* `nix flake {init,new}` now display information about which files have been
+ created.
+
+* Templates can now define a `welcomeText` attribute, which is printed out by
+ `nix flake {init,new} --template `.
diff --git a/docker.nix b/docker.nix
index 28745fc5d..251bd2f46 100644
--- a/docker.nix
+++ b/docker.nix
@@ -21,6 +21,7 @@ let
cacert.out
findutils
iana-etc
+ git
];
users = {
@@ -200,6 +201,8 @@ let
mkdir $out/tmp
+ mkdir -p $out/var/tmp
+
mkdir -p $out/etc/nix
cat $nixConfContentsPath > $out/etc/nix/nix.conf
@@ -235,6 +238,7 @@ pkgs.dockerTools.buildLayeredImageWithNixDb {
'';
fakeRootCommands = ''
chmod 1777 tmp
+ chmod 1777 var/tmp
'';
config = {
diff --git a/flake.lock b/flake.lock
index 861af1c54..61eccb73c 100644
--- a/flake.lock
+++ b/flake.lock
@@ -31,10 +31,26 @@
"type": "indirect"
}
},
+ "nixpkgs-regression": {
+ "locked": {
+ "lastModified": 1643052045,
+ "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
+ "type": "github"
+ },
+ "original": {
+ "id": "nixpkgs",
+ "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
+ "type": "indirect"
+ }
+ },
"root": {
"inputs": {
"lowdown-src": "lowdown-src",
- "nixpkgs": "nixpkgs"
+ "nixpkgs": "nixpkgs",
+ "nixpkgs-regression": "nixpkgs-regression"
}
}
},
diff --git a/flake.nix b/flake.nix
index f4fae2a00..87b00edf4 100644
--- a/flake.nix
+++ b/flake.nix
@@ -2,9 +2,10 @@
description = "The purely functional package manager";
inputs.nixpkgs.url = "nixpkgs/nixos-21.05-small";
+ inputs.nixpkgs-regression.url = "nixpkgs/215d4d0fd80ca5163643b03a33fde804a29cc1e2";
inputs.lowdown-src = { url = "github:kristapsdz/lowdown"; flake = false; };
- outputs = { self, nixpkgs, lowdown-src }:
+ outputs = { self, nixpkgs, nixpkgs-regression, lowdown-src }:
let
@@ -132,6 +133,7 @@
./boehmgc-coroutine-sp-fallback.diff
];
}))
+ nlohmann_json
];
perlDeps =
@@ -140,8 +142,8 @@
];
};
- installScriptFor = systems:
- with nixpkgsFor.x86_64-linux;
+ installScriptFor = systems:
+ with nixpkgsFor.x86_64-linux;
runCommand "installer-script"
{ buildInputs = [ nix ];
}
@@ -205,188 +207,204 @@
installCheckPhase = "make installcheck -j$NIX_BUILD_CORES -l$NIX_BUILD_CORES";
};
- binaryTarball = buildPackages: nix: pkgs: let
- inherit (pkgs) cacert;
- installerClosureInfo = buildPackages.closureInfo { rootPaths = [ nix cacert ]; };
- in
+ binaryTarball = buildPackages: nix: pkgs:
+ let
+ inherit (pkgs) cacert;
+ installerClosureInfo = buildPackages.closureInfo { rootPaths = [ nix cacert ]; };
+ in
- buildPackages.runCommand "nix-binary-tarball-${version}"
- { #nativeBuildInputs = lib.optional (system != "aarch64-linux") shellcheck;
- meta.description = "Distribution-independent Nix bootstrap binaries for ${pkgs.system}";
- }
- ''
- cp ${installerClosureInfo}/registration $TMPDIR/reginfo
- cp ${./scripts/create-darwin-volume.sh} $TMPDIR/create-darwin-volume.sh
- substitute ${./scripts/install-nix-from-closure.sh} $TMPDIR/install \
- --subst-var-by nix ${nix} \
- --subst-var-by cacert ${cacert}
+ buildPackages.runCommand "nix-binary-tarball-${version}"
+ { #nativeBuildInputs = lib.optional (system != "aarch64-linux") shellcheck;
+ meta.description = "Distribution-independent Nix bootstrap binaries for ${pkgs.system}";
+ }
+ ''
+ cp ${installerClosureInfo}/registration $TMPDIR/reginfo
+ cp ${./scripts/create-darwin-volume.sh} $TMPDIR/create-darwin-volume.sh
+ substitute ${./scripts/install-nix-from-closure.sh} $TMPDIR/install \
+ --subst-var-by nix ${nix} \
+ --subst-var-by cacert ${cacert}
- substitute ${./scripts/install-darwin-multi-user.sh} $TMPDIR/install-darwin-multi-user.sh \
- --subst-var-by nix ${nix} \
- --subst-var-by cacert ${cacert}
- substitute ${./scripts/install-systemd-multi-user.sh} $TMPDIR/install-systemd-multi-user.sh \
- --subst-var-by nix ${nix} \
- --subst-var-by cacert ${cacert}
- substitute ${./scripts/install-multi-user.sh} $TMPDIR/install-multi-user \
- --subst-var-by nix ${nix} \
- --subst-var-by cacert ${cacert}
+ substitute ${./scripts/install-darwin-multi-user.sh} $TMPDIR/install-darwin-multi-user.sh \
+ --subst-var-by nix ${nix} \
+ --subst-var-by cacert ${cacert}
+ substitute ${./scripts/install-systemd-multi-user.sh} $TMPDIR/install-systemd-multi-user.sh \
+ --subst-var-by nix ${nix} \
+ --subst-var-by cacert ${cacert}
+ substitute ${./scripts/install-multi-user.sh} $TMPDIR/install-multi-user \
+ --subst-var-by nix ${nix} \
+ --subst-var-by cacert ${cacert}
- if type -p shellcheck; then
- # SC1090: Don't worry about not being able to find
- # $nix/etc/profile.d/nix.sh
- shellcheck --exclude SC1090 $TMPDIR/install
- shellcheck $TMPDIR/create-darwin-volume.sh
- shellcheck $TMPDIR/install-darwin-multi-user.sh
- shellcheck $TMPDIR/install-systemd-multi-user.sh
+ if type -p shellcheck; then
+ # SC1090: Don't worry about not being able to find
+ # $nix/etc/profile.d/nix.sh
+ shellcheck --exclude SC1090 $TMPDIR/install
+ shellcheck $TMPDIR/create-darwin-volume.sh
+ shellcheck $TMPDIR/install-darwin-multi-user.sh
+ shellcheck $TMPDIR/install-systemd-multi-user.sh
- # SC1091: Don't panic about not being able to source
- # /etc/profile
- # SC2002: Ignore "useless cat" "error", when loading
- # .reginfo, as the cat is a much cleaner
- # implementation, even though it is "useless"
- # SC2116: Allow ROOT_HOME=$(echo ~root) for resolving
- # root's home directory
- shellcheck --external-sources \
- --exclude SC1091,SC2002,SC2116 $TMPDIR/install-multi-user
- fi
+ # SC1091: Don't panic about not being able to source
+ # /etc/profile
+ # SC2002: Ignore "useless cat" "error", when loading
+ # .reginfo, as the cat is a much cleaner
+ # implementation, even though it is "useless"
+ # SC2116: Allow ROOT_HOME=$(echo ~root) for resolving
+ # root's home directory
+ shellcheck --external-sources \
+ --exclude SC1091,SC2002,SC2116 $TMPDIR/install-multi-user
+ fi
- chmod +x $TMPDIR/install
- chmod +x $TMPDIR/create-darwin-volume.sh
- chmod +x $TMPDIR/install-darwin-multi-user.sh
- chmod +x $TMPDIR/install-systemd-multi-user.sh
- chmod +x $TMPDIR/install-multi-user
- dir=nix-${version}-${pkgs.system}
- fn=$out/$dir.tar.xz
- mkdir -p $out/nix-support
- echo "file binary-dist $fn" >> $out/nix-support/hydra-build-products
- tar cvfJ $fn \
- --owner=0 --group=0 --mode=u+rw,uga+r \
- --absolute-names \
- --hard-dereference \
- --transform "s,$TMPDIR/install,$dir/install," \
- --transform "s,$TMPDIR/create-darwin-volume.sh,$dir/create-darwin-volume.sh," \
- --transform "s,$TMPDIR/reginfo,$dir/.reginfo," \
- --transform "s,$NIX_STORE,$dir/store,S" \
- $TMPDIR/install \
- $TMPDIR/create-darwin-volume.sh \
- $TMPDIR/install-darwin-multi-user.sh \
- $TMPDIR/install-systemd-multi-user.sh \
- $TMPDIR/install-multi-user \
- $TMPDIR/reginfo \
- $(cat ${installerClosureInfo}/store-paths)
- '';
-
- overlayFor = getStdenv: final: prev:
- let currentStdenv = getStdenv final; in
- {
- nixStable = prev.nix;
-
- # Forward from the previous stage as we don’t want it to pick the lowdown override
- nixUnstable = prev.nixUnstable;
-
- nix = with final; with commonDeps pkgs; currentStdenv.mkDerivation {
- name = "nix-${version}";
- inherit version;
-
- src = self;
-
- VERSION_SUFFIX = versionSuffix;
-
- outputs = [ "out" "dev" "doc" ];
-
- nativeBuildInputs = nativeBuildDeps;
- buildInputs = buildDeps ++ awsDeps;
-
- propagatedBuildInputs = propagatedDeps;
-
- preConfigure =
- ''
- # Copy libboost_context so we don't get all of Boost in our closure.
- # https://github.com/NixOS/nixpkgs/issues/45462
- mkdir -p $out/lib
- cp -pd ${boost}/lib/{libboost_context*,libboost_thread*,libboost_system*} $out/lib
- rm -f $out/lib/*.a
- ${lib.optionalString currentStdenv.isLinux ''
- chmod u+w $out/lib/*.so.*
- patchelf --set-rpath $out/lib:${currentStdenv.cc.cc.lib}/lib $out/lib/libboost_thread.so.*
- ''}
- '';
-
- configureFlags = configureFlags ++
- [ "--sysconfdir=/etc" ];
-
- enableParallelBuilding = true;
-
- makeFlags = "profiledir=$(out)/etc/profile.d PRECOMPILE_HEADERS=1";
-
- doCheck = true;
-
- installFlags = "sysconfdir=$(out)/etc";
-
- postInstall = ''
- mkdir -p $doc/nix-support
- echo "doc manual $doc/share/doc/nix/manual" >> $doc/nix-support/hydra-build-products
+ chmod +x $TMPDIR/install
+ chmod +x $TMPDIR/create-darwin-volume.sh
+ chmod +x $TMPDIR/install-darwin-multi-user.sh
+ chmod +x $TMPDIR/install-systemd-multi-user.sh
+ chmod +x $TMPDIR/install-multi-user
+ dir=nix-${version}-${pkgs.system}
+ fn=$out/$dir.tar.xz
+ mkdir -p $out/nix-support
+ echo "file binary-dist $fn" >> $out/nix-support/hydra-build-products
+ tar cvfJ $fn \
+ --owner=0 --group=0 --mode=u+rw,uga+r \
+ --absolute-names \
+ --hard-dereference \
+ --transform "s,$TMPDIR/install,$dir/install," \
+ --transform "s,$TMPDIR/create-darwin-volume.sh,$dir/create-darwin-volume.sh," \
+ --transform "s,$TMPDIR/reginfo,$dir/.reginfo," \
+ --transform "s,$NIX_STORE,$dir/store,S" \
+ $TMPDIR/install \
+ $TMPDIR/create-darwin-volume.sh \
+ $TMPDIR/install-darwin-multi-user.sh \
+ $TMPDIR/install-systemd-multi-user.sh \
+ $TMPDIR/install-multi-user \
+ $TMPDIR/reginfo \
+ $(cat ${installerClosureInfo}/store-paths)
'';
- doInstallCheck = true;
- installCheckFlags = "sysconfdir=$(out)/etc";
+ overlayFor = getStdenv: final: prev:
+ let currentStdenv = getStdenv final; in
+ {
+ nixStable = prev.nix;
- separateDebugInfo = true;
+ # Forward from the previous stage as we don’t want it to pick the lowdown override
+ nixUnstable = prev.nixUnstable;
- strictDeps = true;
-
- passthru.perl-bindings = with final; currentStdenv.mkDerivation {
- name = "nix-perl-${version}";
+ nix = with final; with commonDeps pkgs; currentStdenv.mkDerivation {
+ name = "nix-${version}";
+ inherit version;
src = self;
- nativeBuildInputs =
- [ buildPackages.autoconf-archive
- buildPackages.autoreconfHook
- buildPackages.pkg-config
- ];
+ VERSION_SUFFIX = versionSuffix;
- buildInputs =
- [ nix
- curl
- bzip2
- xz
- pkgs.perl
- boost
- ]
- ++ lib.optional (currentStdenv.isLinux || currentStdenv.isDarwin) libsodium
- ++ lib.optional currentStdenv.isDarwin darwin.apple_sdk.frameworks.Security;
+ outputs = [ "out" "dev" "doc" ];
- configureFlags = ''
- --with-dbi=${perlPackages.DBI}/${pkgs.perl.libPrefix}
- --with-dbd-sqlite=${perlPackages.DBDSQLite}/${pkgs.perl.libPrefix}
- '';
+ nativeBuildInputs = nativeBuildDeps;
+ buildInputs = buildDeps ++ awsDeps;
+
+ propagatedBuildInputs = propagatedDeps;
+
+ disallowedReferences = [ boost ];
+
+ preConfigure =
+ ''
+ # Copy libboost_context so we don't get all of Boost in our closure.
+ # https://github.com/NixOS/nixpkgs/issues/45462
+ mkdir -p $out/lib
+ cp -pd ${boost}/lib/{libboost_context*,libboost_thread*,libboost_system*} $out/lib
+ rm -f $out/lib/*.a
+ ${lib.optionalString currentStdenv.isLinux ''
+ chmod u+w $out/lib/*.so.*
+ patchelf --set-rpath $out/lib:${currentStdenv.cc.cc.lib}/lib $out/lib/libboost_thread.so.*
+ ''}
+ ${lib.optionalString currentStdenv.isDarwin ''
+ for LIB in $out/lib/*.dylib; do
+ chmod u+w $LIB
+ install_name_tool -id $LIB $LIB
+ done
+ install_name_tool -change ${boost}/lib/libboost_system.dylib $out/lib/libboost_system.dylib $out/lib/libboost_thread.dylib
+ ''}
+ '';
+
+ configureFlags = configureFlags ++
+ [ "--sysconfdir=/etc" ];
enableParallelBuilding = true;
- postUnpack = "sourceRoot=$sourceRoot/perl";
+ makeFlags = "profiledir=$(out)/etc/profile.d PRECOMPILE_HEADERS=1";
+
+ doCheck = true;
+
+ installFlags = "sysconfdir=$(out)/etc";
+
+ postInstall = ''
+ mkdir -p $doc/nix-support
+ echo "doc manual $doc/share/doc/nix/manual" >> $doc/nix-support/hydra-build-products
+ ${lib.optionalString currentStdenv.isDarwin ''
+ install_name_tool \
+ -change ${boost}/lib/libboost_context.dylib \
+ $out/lib/libboost_context.dylib \
+ $out/lib/libnixutil.dylib
+ ''}
+ '';
+
+ doInstallCheck = true;
+ installCheckFlags = "sysconfdir=$(out)/etc";
+
+ separateDebugInfo = true;
+
+ strictDeps = true;
+
+ passthru.perl-bindings = with final; currentStdenv.mkDerivation {
+ name = "nix-perl-${version}";
+
+ src = self;
+
+ nativeBuildInputs =
+ [ buildPackages.autoconf-archive
+ buildPackages.autoreconfHook
+ buildPackages.pkg-config
+ ];
+
+ buildInputs =
+ [ nix
+ curl
+ bzip2
+ xz
+ pkgs.perl
+ boost
+ ]
+ ++ lib.optional (currentStdenv.isLinux || currentStdenv.isDarwin) libsodium
+ ++ lib.optional currentStdenv.isDarwin darwin.apple_sdk.frameworks.Security;
+
+ configureFlags = ''
+ --with-dbi=${perlPackages.DBI}/${pkgs.perl.libPrefix}
+ --with-dbd-sqlite=${perlPackages.DBDSQLite}/${pkgs.perl.libPrefix}
+ '';
+
+ enableParallelBuilding = true;
+
+ postUnpack = "sourceRoot=$sourceRoot/perl";
+ };
+
};
+ lowdown-nix = with final; currentStdenv.mkDerivation rec {
+ name = "lowdown-0.9.0";
+
+ src = lowdown-src;
+
+ outputs = [ "out" "bin" "dev" ];
+
+ nativeBuildInputs = [ buildPackages.which ];
+
+ configurePhase = ''
+ ${if (currentStdenv.isDarwin && currentStdenv.isAarch64) then "echo \"HAVE_SANDBOX_INIT=false\" > configure.local" else ""}
+ ./configure \
+ PREFIX=${placeholder "dev"} \
+ BINDIR=${placeholder "bin"}/bin
+ '';
+ };
};
- lowdown-nix = with final; currentStdenv.mkDerivation rec {
- name = "lowdown-0.9.0";
-
- src = lowdown-src;
-
- outputs = [ "out" "bin" "dev" ];
-
- nativeBuildInputs = [ buildPackages.which ];
-
- configurePhase = ''
- ${if (currentStdenv.isDarwin && currentStdenv.isAarch64) then "echo \"HAVE_SANDBOX_INIT=false\" > configure.local" else ""}
- ./configure \
- PREFIX=${placeholder "dev"} \
- BINDIR=${placeholder "bin"}/bin
- '';
- };
- };
-
in {
# A Nixpkgs overlay that overrides the 'nix' and
@@ -429,19 +447,7 @@
installerScriptForGHA = installScriptFor [ "x86_64-linux" "x86_64-darwin" "armv6l-linux" "armv7l-linux"];
# docker image with Nix inside
- dockerImage = nixpkgs.lib.genAttrs linux64BitSystems (system:
- let
- pkgs = nixpkgsFor.${system};
- image = import ./docker.nix { inherit pkgs; tag = version; };
- in pkgs.runCommand "docker-image-tarball-${version}"
- { meta.description = "Docker image with Nix for ${system}";
- }
- ''
- mkdir -p $out/nix-support
- image=$out/image.tar.gz
- ln -s ${image} $image
- echo "file binary-dist $image" >> $out/nix-support/hydra-build-products
- '');
+ dockerImage = nixpkgs.lib.genAttrs linux64BitSystems (system: self.packages.${system}.dockerImage);
# Line coverage analysis.
coverage =
@@ -495,6 +501,12 @@
inherit (self) overlay;
});
+ tests.sourcehutFlakes = (import ./tests/sourcehut-flakes.nix rec {
+ system = "x86_64-linux";
+ inherit nixpkgs;
+ inherit (self) overlay;
+ });
+
tests.setuid = nixpkgs.lib.genAttrs
["i686-linux" "x86_64-linux"]
(system:
@@ -503,29 +515,23 @@
inherit (self) overlay;
});
- /*
- # Check whether we can still evaluate all of Nixpkgs.
+ # Make sure that nix-env still produces the exact same result
+ # on a particular version of Nixpkgs.
tests.evalNixpkgs =
- import (nixpkgs + "/pkgs/top-level/make-tarball.nix") {
- # FIXME: fix pkgs/top-level/make-tarball.nix in NixOS to not require a revCount.
- inherit nixpkgs;
- pkgs = nixpkgsFor.x86_64-linux;
- officialRelease = false;
- };
-
- # Check whether we can still evaluate NixOS.
- tests.evalNixOS =
with nixpkgsFor.x86_64-linux;
runCommand "eval-nixos" { buildInputs = [ nix ]; }
''
- export NIX_STATE_DIR=$TMPDIR
-
- nix-instantiate ${nixpkgs}/nixos/release-combined.nix -A tested --dry-run \
- --arg nixpkgs '{ outPath = ${nixpkgs}; revCount = 123; shortRev = "abcdefgh"; }'
-
- touch $out
+ type -p nix-env
+ # Note: we're filtering out nixos-install-tools because https://github.com/NixOS/nixpkgs/pull/153594#issuecomment-1020530593.
+ time nix-env --store dummy:// -f ${nixpkgs-regression} -qaP --drv-path | sort | grep -v nixos-install-tools > packages
+ [[ $(sha1sum < packages | cut -c1-40) = ff451c521e61e4fe72bdbe2d0ca5d1809affa733 ]]
+ mkdir $out
'';
- */
+
+ metrics.nixpkgs = import "${nixpkgs-regression}/pkgs/top-level/metrics.nix" {
+ pkgs = nixpkgsFor.x86_64-linux;
+ nixpkgs = nixpkgs-regression;
+ };
installTests = forAllSystems (system:
let pkgs = nixpkgsFor.${system}; in
@@ -547,9 +553,9 @@
binaryTarball = self.hydraJobs.binaryTarball.${system};
perlBindings = self.hydraJobs.perlBindings.${system};
installTests = self.hydraJobs.installTests.${system};
- } // (if system == "x86_64-linux" then {
+ } // (nixpkgs.lib.optionalAttrs (builtins.elem system linux64BitSystems)) {
dockerImage = self.hydraJobs.dockerImage.${system};
- } else {}));
+ });
packages = forAllSystems (system: {
inherit (nixpkgsFor.${system}) nix;
@@ -594,6 +600,20 @@
hardeningDisable = [ "pie" ];
};
+ dockerImage =
+ let
+ pkgs = nixpkgsFor.${system};
+ image = import ./docker.nix { inherit pkgs; tag = version; };
+ in
+ pkgs.runCommand
+ "docker-image-tarball-${version}"
+ { meta.description = "Docker image with Nix for ${system}"; }
+ ''
+ mkdir -p $out/nix-support
+ image=$out/image.tar.gz
+ ln -s ${image} $image
+ echo "file binary-dist $image" >> $out/nix-support/hydra-build-products
+ '';
} // builtins.listToAttrs (map (crossSystem: {
name = "nix-${crossSystem}";
value = let
@@ -634,11 +654,10 @@
installCheckFlags = "sysconfdir=$(out)/etc";
};
}) crossSystems)) // (builtins.listToAttrs (map (stdenvName:
- nixpkgsFor.${system}.lib.nameValuePair
- "nix-${stdenvName}"
- nixpkgsFor.${system}."${stdenvName}Packages".nix
- ) stdenvs))
- );
+ nixpkgsFor.${system}.lib.nameValuePair
+ "nix-${stdenvName}"
+ nixpkgsFor.${system}."${stdenvName}Packages".nix
+ ) stdenvs)));
defaultPackage = forAllSystems (system: self.packages.${system}.nix);
diff --git a/maintainers/upload-release.pl b/maintainers/upload-release.pl
index 18ab33424..d3ef63db8 100755
--- a/maintainers/upload-release.pl
+++ b/maintainers/upload-release.pl
@@ -55,6 +55,11 @@ my $releaseDir = "nix/$releaseName";
my $tmpDir = "$TMPDIR/nix-release/$releaseName";
File::Path::make_path($tmpDir);
+my $narCache = "$TMPDIR/nar-cache";
+File::Path::make_path($narCache);
+
+my $binaryCache = "https://cache.nixos.org/?local-nar-cache=$narCache";
+
# S3 setup.
my $aws_access_key_id = $ENV{'AWS_ACCESS_KEY_ID'} or die "No AWS_ACCESS_KEY_ID given.";
my $aws_secret_access_key = $ENV{'AWS_SECRET_ACCESS_KEY'} or die "No AWS_SECRET_ACCESS_KEY given.";
@@ -80,6 +85,7 @@ sub downloadFile {
my ($jobName, $productNr, $dstName) = @_;
my $buildInfo = decode_json(fetch("$evalUrl/job/$jobName", 'application/json'));
+ #print STDERR "$jobName: ", Dumper($buildInfo), "\n";
my $srcFile = $buildInfo->{buildproducts}->{$productNr}->{path} or die "job '$jobName' lacks product $productNr\n";
$dstName //= basename($srcFile);
@@ -87,19 +93,27 @@ sub downloadFile {
if (!-e $tmpFile) {
print STDERR "downloading $srcFile to $tmpFile...\n";
- system("NIX_REMOTE=https://cache.nixos.org/ nix store cat '$srcFile' > '$tmpFile'") == 0
+
+ my $fileInfo = decode_json(`NIX_REMOTE=$binaryCache nix store ls --json '$srcFile'`);
+
+ $srcFile = $fileInfo->{target} if $fileInfo->{type} eq 'symlink';
+
+ #print STDERR $srcFile, " ", Dumper($fileInfo), "\n";
+
+ system("NIX_REMOTE=$binaryCache nix store cat '$srcFile' > '$tmpFile'.tmp") == 0
or die "unable to fetch $srcFile\n";
+ rename("$tmpFile.tmp", $tmpFile) or die;
}
- my $sha256_expected = $buildInfo->{buildproducts}->{$productNr}->{sha256hash} or die;
+ my $sha256_expected = $buildInfo->{buildproducts}->{$productNr}->{sha256hash};
my $sha256_actual = `nix hash file --base16 --type sha256 '$tmpFile'`;
chomp $sha256_actual;
- if ($sha256_expected ne $sha256_actual) {
+ if (defined($sha256_expected) && $sha256_expected ne $sha256_actual) {
print STDERR "file $tmpFile is corrupt, got $sha256_actual, expected $sha256_expected\n";
exit 1;
}
- write_file("$tmpFile.sha256", $sha256_expected);
+ write_file("$tmpFile.sha256", $sha256_actual);
if (! -e "$tmpFile.asc") {
system("gpg2 --detach-sign --armor $tmpFile") == 0 or die "unable to sign $tmpFile\n";
@@ -117,6 +131,60 @@ downloadFile("binaryTarballCross.x86_64-linux.armv6l-linux", "1");
downloadFile("binaryTarballCross.x86_64-linux.armv7l-linux", "1");
downloadFile("installerScript", "1");
+# Upload docker images to dockerhub.
+my $dockerManifest = "";
+my $dockerManifestLatest = "";
+
+for my $platforms (["x86_64-linux", "amd64"], ["aarch64-linux", "arm64"]) {
+ my $system = $platforms->[0];
+ my $dockerPlatform = $platforms->[1];
+ my $fn = "nix-$version-docker-image-$dockerPlatform.tar.gz";
+ downloadFile("dockerImage.$system", "1", $fn);
+
+ print STDERR "loading docker image for $dockerPlatform...\n";
+ system("docker load -i $tmpDir/$fn") == 0 or die;
+
+ my $tag = "nixos/nix:$version-$dockerPlatform";
+ my $latestTag = "nixos/nix:latest-$dockerPlatform";
+
+ print STDERR "tagging $version docker image for $dockerPlatform...\n";
+ system("docker tag nix:$version $tag") == 0 or die;
+
+ if ($isLatest) {
+ print STDERR "tagging latest docker image for $dockerPlatform...\n";
+ system("docker tag nix:$version $latestTag") == 0 or die;
+ }
+
+ print STDERR "pushing $version docker image for $dockerPlatform...\n";
+ system("docker push -q $tag") == 0 or die;
+
+ if ($isLatest) {
+ print STDERR "pushing latest docker image for $dockerPlatform...\n";
+ system("docker push -q $latestTag") == 0 or die;
+ }
+
+ $dockerManifest .= " --amend $tag";
+ $dockerManifestLatest .= " --amend $latestTag"
+}
+
+print STDERR "creating multi-platform docker manifest...\n";
+system("docker manifest rm nixos/nix:$version");
+system("docker manifest create nixos/nix:$version $dockerManifest") == 0 or die;
+if ($isLatest) {
+ print STDERR "creating latest multi-platform docker manifest...\n";
+ system("docker manifest rm nixos/nix:latest");
+ system("docker manifest create nixos/nix:latest $dockerManifestLatest") == 0 or die;
+}
+
+print STDERR "pushing multi-platform docker manifest...\n";
+system("docker manifest push nixos/nix:$version") == 0 or die;
+
+if ($isLatest) {
+ print STDERR "pushing latest multi-platform docker manifest...\n";
+ system("docker manifest push nixos/nix:latest") == 0 or die;
+}
+
+# Upload release files to S3.
for my $fn (glob "$tmpDir/*") {
my $name = basename($fn);
my $dstKey = "$releaseDir/" . $name;
diff --git a/misc/bash/completion.sh b/misc/bash/completion.sh
index 8fc224792..045053dee 100644
--- a/misc/bash/completion.sh
+++ b/misc/bash/completion.sh
@@ -15,7 +15,7 @@ function _complete_nix {
else
COMPREPLY+=("$completion")
fi
- done < <(NIX_GET_COMPLETIONS=$cword "${words[@]/#\~/$HOME}")
+ done < <(NIX_GET_COMPLETIONS=$cword "${words[@]/#\~/$HOME}" 2>/dev/null)
__ltrim_colon_completions "$cur"
}
diff --git a/misc/zsh/completion.zsh b/misc/zsh/completion.zsh
index a902e37dc..e702c721e 100644
--- a/misc/zsh/completion.zsh
+++ b/misc/zsh/completion.zsh
@@ -4,7 +4,7 @@ function _nix() {
local ifs_bk="$IFS"
local input=("${(Q)words[@]}")
IFS=$'\n'
- local res=($(NIX_GET_COMPLETIONS=$((CURRENT - 1)) "$input[@]"))
+ local res=($(NIX_GET_COMPLETIONS=$((CURRENT - 1)) "$input[@]" 2>/dev/null))
IFS="$ifs_bk"
local tpe="${${res[1]}%%> *}"
local -a suggestions
diff --git a/nix-rust/Cargo.lock b/nix-rust/Cargo.lock
deleted file mode 100644
index 957c01e5a..000000000
--- a/nix-rust/Cargo.lock
+++ /dev/null
@@ -1,399 +0,0 @@
-# This file is automatically @generated by Cargo.
-# It is not intended for manual editing.
-[[package]]
-name = "assert_matches"
-version = "1.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "autocfg"
-version = "0.1.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "bit-set"
-version = "0.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "bit-vec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "bit-vec"
-version = "0.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "bitflags"
-version = "1.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "byteorder"
-version = "1.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "c2-chacha"
-version = "0.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "cfg-if"
-version = "0.1.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "cloudabi"
-version = "0.0.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "fnv"
-version = "1.0.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "fuchsia-cprng"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "getrandom"
-version = "0.1.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
- "wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "hex"
-version = "0.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "lazy_static"
-version = "1.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "libc"
-version = "0.2.66"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "nix-rust"
-version = "0.1.0"
-dependencies = [
- "assert_matches 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
- "proptest 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "num-traits"
-version = "0.2.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "ppv-lite86"
-version = "0.2.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "proptest"
-version = "0.9.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "bit-set 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
- "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
- "rusty-fork 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "quick-error"
-version = "1.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "rand"
-version = "0.6.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
- "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "rand"
-version = "0.7.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
- "rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "rand_chacha"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
- "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "rand_chacha"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "rand_core"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "rand_core"
-version = "0.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "rand_core"
-version = "0.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "rand_hc"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "rand_hc"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "rand_isaac"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "rand_jitter"
-version = "0.1.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
- "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "rand_os"
-version = "0.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
- "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "rand_pcg"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
- "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "rand_xorshift"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "rdrand"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "redox_syscall"
-version = "0.1.56"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "regex-syntax"
-version = "0.6.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "remove_dir_all"
-version = "0.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "rusty-fork"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "wait-timeout 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "tempfile"
-version = "3.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
- "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)",
- "remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "wait-timeout"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "wasi"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "winapi"
-version = "0.3.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "winapi-i686-pc-windows-gnu"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "winapi-x86_64-pc-windows-gnu"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[metadata]
-"checksum assert_matches 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7deb0a829ca7bcfaf5da70b073a8d128619259a7be8216a355e23f00763059e5"
-"checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"
-"checksum bit-set 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e84c238982c4b1e1ee668d136c510c67a13465279c0cb367ea6baf6310620a80"
-"checksum bit-vec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f59bbe95d4e52a6398ec21238d31577f2b28a9d86807f06ca59d191d8440d0bb"
-"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
-"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5"
-"checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb"
-"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
-"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
-"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3"
-"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
-"checksum getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e7db7ca94ed4cd01190ceee0d8a8052f08a247aa1b469a7f68c6a3b71afcf407"
-"checksum hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77"
-"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
-"checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558"
-"checksum num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c81ffc11c212fa327657cb19dd85eb7419e163b5b076bede2bdb5c974c07e4"
-"checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b"
-"checksum proptest 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cf147e022eacf0c8a054ab864914a7602618adba841d800a9a9868a5237a529f"
-"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0"
-"checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca"
-"checksum rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3ae1b169243eaf61759b8475a998f0a385e42042370f3a7dbaf35246eacc8412"
-"checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef"
-"checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853"
-"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
-"checksum rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
-"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
-"checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4"
-"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
-"checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08"
-"checksum rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b"
-"checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071"
-"checksum rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44"
-"checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c"
-"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
-"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
-"checksum regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716"
-"checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e"
-"checksum rusty-fork 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3dd93264e10c577503e926bd1430193eeb5d21b059148910082245309b424fae"
-"checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
-"checksum wait-timeout 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
-"checksum wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d"
-"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
-"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
-"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
diff --git a/nix-rust/Cargo.toml b/nix-rust/Cargo.toml
deleted file mode 100644
index 1372e5a73..000000000
--- a/nix-rust/Cargo.toml
+++ /dev/null
@@ -1,23 +0,0 @@
-[package]
-name = "nix-rust"
-version = "0.1.0"
-authors = ["Eelco Dolstra "]
-edition = "2018"
-
-[lib]
-name = "nixrust"
-crate-type = ["cdylib"]
-
-[dependencies]
-libc = "0.2"
-#futures-preview = { version = "=0.3.0-alpha.19" }
-#hyper = "0.13.0-alpha.4"
-#http = "0.1"
-#tokio = { version = "0.2.0-alpha.6", default-features = false, features = ["rt-full"] }
-lazy_static = "1.4"
-#byteorder = "1.3"
-
-[dev-dependencies]
-hex = "0.3"
-assert_matches = "1.3"
-proptest = "0.9"
diff --git a/nix-rust/local.mk b/nix-rust/local.mk
deleted file mode 100644
index 538244594..000000000
--- a/nix-rust/local.mk
+++ /dev/null
@@ -1,48 +0,0 @@
-ifeq ($(OPTIMIZE), 1)
- RUST_MODE = --release
- RUST_DIR = release
-else
- RUST_MODE =
- RUST_DIR = debug
-endif
-
-libnixrust_PATH := $(d)/target/$(RUST_DIR)/libnixrust.$(SO_EXT)
-libnixrust_INSTALL_PATH := $(libdir)/libnixrust.$(SO_EXT)
-libnixrust_LDFLAGS_USE := -L$(d)/target/$(RUST_DIR) -lnixrust
-libnixrust_LDFLAGS_USE_INSTALLED := -L$(libdir) -lnixrust
-
-ifdef HOST_LINUX
-libnixrust_LDFLAGS_USE += -ldl
-libnixrust_LDFLAGS_USE_INSTALLED += -ldl
-endif
-
-ifdef HOST_DARWIN
-libnixrust_BUILD_FLAGS = NIX_LDFLAGS="-undefined dynamic_lookup"
-else
-libnixrust_LDFLAGS_USE += -Wl,-rpath,$(abspath $(d)/target/$(RUST_DIR))
-libnixrust_LDFLAGS_USE_INSTALLED += -Wl,-rpath,$(libdir)
-endif
-
-$(libnixrust_PATH): $(call rwildcard, $(d)/src, *.rs) $(d)/Cargo.toml
- $(trace-gen) cd nix-rust && CARGO_HOME=$$(if [[ -d vendor ]]; then echo vendor; fi) \
- $(libnixrust_BUILD_FLAGS) \
- cargo build $(RUST_MODE) $$(if [[ -d vendor ]]; then echo --offline; fi) \
- && touch target/$(RUST_DIR)/libnixrust.$(SO_EXT)
-
-$(libnixrust_INSTALL_PATH): $(libnixrust_PATH)
- $(target-gen) cp $^ $@
-ifdef HOST_DARWIN
- install_name_tool -id $@ $@
-endif
-
-clean: clean-rust
-
-clean-rust:
- $(suppress) rm -rfv nix-rust/target
-
-ifndef HOST_DARWIN
-check: rust-tests
-
-rust-tests:
- $(trace-test) cd nix-rust && CARGO_HOME=$$(if [[ -d vendor ]]; then echo vendor; fi) cargo test --release $$(if [[ -d vendor ]]; then echo --offline; fi)
-endif
diff --git a/nix-rust/src/c.rs b/nix-rust/src/c.rs
deleted file mode 100644
index c1358545f..000000000
--- a/nix-rust/src/c.rs
+++ /dev/null
@@ -1,77 +0,0 @@
-use super::{error, store::path, store::StorePath, util};
-
-#[no_mangle]
-pub unsafe extern "C" fn ffi_String_new(s: &str, out: *mut String) {
- // FIXME: check whether 's' is valid UTF-8?
- out.write(s.to_string())
-}
-
-#[no_mangle]
-pub unsafe extern "C" fn ffi_String_drop(self_: *mut String) {
- std::ptr::drop_in_place(self_);
-}
-
-#[no_mangle]
-pub extern "C" fn ffi_StorePath_new(
- path: &str,
- store_dir: &str,
-) -> Result {
- StorePath::new(std::path::Path::new(path), std::path::Path::new(store_dir))
- .map_err(|err| err.into())
-}
-
-#[no_mangle]
-pub extern "C" fn ffi_StorePath_new2(
- hash: &[u8; crate::store::path::STORE_PATH_HASH_BYTES],
- name: &str,
-) -> Result {
- StorePath::from_parts(*hash, name).map_err(|err| err.into())
-}
-
-#[no_mangle]
-pub extern "C" fn ffi_StorePath_fromBaseName(
- base_name: &str,
-) -> Result {
- StorePath::new_from_base_name(base_name).map_err(|err| err.into())
-}
-
-#[no_mangle]
-pub unsafe extern "C" fn ffi_StorePath_drop(self_: *mut StorePath) {
- std::ptr::drop_in_place(self_);
-}
-
-#[no_mangle]
-pub extern "C" fn ffi_StorePath_to_string(self_: &StorePath) -> Vec {
- let mut buf = vec![0; path::STORE_PATH_HASH_CHARS + 1 + self_.name.name().len()];
- util::base32::encode_into(self_.hash.hash(), &mut buf[0..path::STORE_PATH_HASH_CHARS]);
- buf[path::STORE_PATH_HASH_CHARS] = b'-';
- buf[path::STORE_PATH_HASH_CHARS + 1..].clone_from_slice(self_.name.name().as_bytes());
- buf
-}
-
-#[no_mangle]
-pub extern "C" fn ffi_StorePath_less_than(a: &StorePath, b: &StorePath) -> bool {
- a < b
-}
-
-#[no_mangle]
-pub extern "C" fn ffi_StorePath_eq(a: &StorePath, b: &StorePath) -> bool {
- a == b
-}
-
-#[no_mangle]
-pub extern "C" fn ffi_StorePath_clone(self_: &StorePath) -> StorePath {
- self_.clone()
-}
-
-#[no_mangle]
-pub extern "C" fn ffi_StorePath_name(self_: &StorePath) -> &str {
- self_.name.name()
-}
-
-#[no_mangle]
-pub extern "C" fn ffi_StorePath_hash_data(
- self_: &StorePath,
-) -> &[u8; crate::store::path::STORE_PATH_HASH_BYTES] {
- self_.hash.hash()
-}
diff --git a/nix-rust/src/error.rs b/nix-rust/src/error.rs
deleted file mode 100644
index bb0c9a933..000000000
--- a/nix-rust/src/error.rs
+++ /dev/null
@@ -1,118 +0,0 @@
-use std::fmt;
-
-#[derive(Debug)]
-pub enum Error {
- InvalidPath(crate::store::StorePath),
- BadStorePath(std::path::PathBuf),
- NotInStore(std::path::PathBuf),
- BadNarInfo,
- BadBase32,
- StorePathNameEmpty,
- StorePathNameTooLong,
- BadStorePathName,
- NarSizeFieldTooBig,
- BadNarString,
- BadNarPadding,
- BadNarVersionMagic,
- MissingNarOpenTag,
- MissingNarCloseTag,
- MissingNarField,
- BadNarField(String),
- BadExecutableField,
- IOError(std::io::Error),
- #[cfg(unused)]
- HttpError(hyper::error::Error),
- Misc(String),
- #[cfg(not(test))]
- Foreign(CppException),
- BadTarFileMemberName(String),
-}
-
-impl From for Error {
- fn from(err: std::io::Error) -> Self {
- Error::IOError(err)
- }
-}
-
-#[cfg(unused)]
-impl From for Error {
- fn from(err: hyper::error::Error) -> Self {
- Error::HttpError(err)
- }
-}
-
-impl fmt::Display for Error {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- Error::InvalidPath(_) => write!(f, "invalid path"),
- Error::BadNarInfo => write!(f, ".narinfo file is corrupt"),
- Error::BadStorePath(path) => write!(f, "path '{}' is not a store path", path.display()),
- Error::NotInStore(path) => {
- write!(f, "path '{}' is not in the Nix store", path.display())
- }
- Error::BadBase32 => write!(f, "invalid base32 string"),
- Error::StorePathNameEmpty => write!(f, "store path name is empty"),
- Error::StorePathNameTooLong => {
- write!(f, "store path name is longer than 211 characters")
- }
- Error::BadStorePathName => write!(f, "store path name contains forbidden character"),
- Error::NarSizeFieldTooBig => write!(f, "size field in NAR is too big"),
- Error::BadNarString => write!(f, "NAR string is not valid UTF-8"),
- Error::BadNarPadding => write!(f, "NAR padding is not zero"),
- Error::BadNarVersionMagic => write!(f, "unsupported NAR version"),
- Error::MissingNarOpenTag => write!(f, "NAR open tag is missing"),
- Error::MissingNarCloseTag => write!(f, "NAR close tag is missing"),
- Error::MissingNarField => write!(f, "expected NAR field is missing"),
- Error::BadNarField(s) => write!(f, "unrecognized NAR field '{}'", s),
- Error::BadExecutableField => write!(f, "bad 'executable' field in NAR"),
- Error::IOError(err) => write!(f, "I/O error: {}", err),
- #[cfg(unused)]
- Error::HttpError(err) => write!(f, "HTTP error: {}", err),
- #[cfg(not(test))]
- Error::Foreign(_) => write!(f, ""), // FIXME
- Error::Misc(s) => write!(f, "{}", s),
- Error::BadTarFileMemberName(s) => {
- write!(f, "tar archive contains illegal file name '{}'", s)
- }
- }
- }
-}
-
-#[cfg(not(test))]
-impl From for CppException {
- fn from(err: Error) -> Self {
- match err {
- Error::Foreign(ex) => ex,
- _ => CppException::new(&err.to_string()),
- }
- }
-}
-
-#[cfg(not(test))]
-#[repr(C)]
-#[derive(Debug)]
-pub struct CppException(*const libc::c_void); // == std::exception_ptr*
-
-#[cfg(not(test))]
-impl CppException {
- fn new(s: &str) -> Self {
- Self(unsafe { make_error(s) })
- }
-}
-
-#[cfg(not(test))]
-impl Drop for CppException {
- fn drop(&mut self) {
- unsafe {
- destroy_error(self.0);
- }
- }
-}
-
-#[cfg(not(test))]
-extern "C" {
- #[allow(improper_ctypes)] // YOLO
- fn make_error(s: &str) -> *const libc::c_void;
-
- fn destroy_error(exc: *const libc::c_void);
-}
diff --git a/nix-rust/src/lib.rs b/nix-rust/src/lib.rs
deleted file mode 100644
index 101de106f..000000000
--- a/nix-rust/src/lib.rs
+++ /dev/null
@@ -1,10 +0,0 @@
-#[allow(improper_ctypes_definitions)]
-#[cfg(not(test))]
-mod c;
-mod error;
-#[cfg(unused)]
-mod nar;
-mod store;
-mod util;
-
-pub use error::Error;
diff --git a/nix-rust/src/nar.rs b/nix-rust/src/nar.rs
deleted file mode 100644
index cb520935e..000000000
--- a/nix-rust/src/nar.rs
+++ /dev/null
@@ -1,126 +0,0 @@
-use crate::Error;
-use byteorder::{LittleEndian, ReadBytesExt};
-use std::convert::TryFrom;
-use std::io::Read;
-
-pub fn parse(input: &mut R) -> Result<(), Error> {
- if String::read(input)? != NAR_VERSION_MAGIC {
- return Err(Error::BadNarVersionMagic);
- }
-
- parse_file(input)
-}
-
-const NAR_VERSION_MAGIC: &str = "nix-archive-1";
-
-fn parse_file(input: &mut R) -> Result<(), Error> {
- if String::read(input)? != "(" {
- return Err(Error::MissingNarOpenTag);
- }
-
- if String::read(input)? != "type" {
- return Err(Error::MissingNarField);
- }
-
- match String::read(input)?.as_ref() {
- "regular" => {
- let mut _executable = false;
- let mut tag = String::read(input)?;
- if tag == "executable" {
- _executable = true;
- if String::read(input)? != "" {
- return Err(Error::BadExecutableField);
- }
- tag = String::read(input)?;
- }
- if tag != "contents" {
- return Err(Error::MissingNarField);
- }
- let _contents = Vec::::read(input)?;
- if String::read(input)? != ")" {
- return Err(Error::MissingNarCloseTag);
- }
- }
- "directory" => loop {
- match String::read(input)?.as_ref() {
- "entry" => {
- if String::read(input)? != "(" {
- return Err(Error::MissingNarOpenTag);
- }
- if String::read(input)? != "name" {
- return Err(Error::MissingNarField);
- }
- let _name = String::read(input)?;
- if String::read(input)? != "node" {
- return Err(Error::MissingNarField);
- }
- parse_file(input)?;
- let tag = String::read(input)?;
- if tag != ")" {
- return Err(Error::MissingNarCloseTag);
- }
- }
- ")" => break,
- s => return Err(Error::BadNarField(s.into())),
- }
- },
- "symlink" => {
- if String::read(input)? != "target" {
- return Err(Error::MissingNarField);
- }
- let _target = String::read(input)?;
- if String::read(input)? != ")" {
- return Err(Error::MissingNarCloseTag);
- }
- }
- s => return Err(Error::BadNarField(s.into())),
- }
-
- Ok(())
-}
-
-trait Deserialize: Sized {
- fn read(input: &mut R) -> Result;
-}
-
-impl Deserialize for String {
- fn read(input: &mut R) -> Result {
- let buf = Deserialize::read(input)?;
- Ok(String::from_utf8(buf).map_err(|_| Error::BadNarString)?)
- }
-}
-
-impl Deserialize for Vec {
- fn read(input: &mut R) -> Result {
- let n: usize = Deserialize::read(input)?;
- let mut buf = vec![0; n];
- input.read_exact(&mut buf)?;
- skip_padding(input, n)?;
- Ok(buf)
- }
-}
-
-fn skip_padding(input: &mut R, len: usize) -> Result<(), Error> {
- if len % 8 != 0 {
- let mut buf = [0; 8];
- let buf = &mut buf[0..8 - (len % 8)];
- input.read_exact(buf)?;
- if !buf.iter().all(|b| *b == 0) {
- return Err(Error::BadNarPadding);
- }
- }
- Ok(())
-}
-
-impl Deserialize for u64 {
- fn read(input: &mut R) -> Result {
- Ok(input.read_u64::()?)
- }
-}
-
-impl Deserialize for usize {
- fn read(input: &mut R) -> Result {
- let n: u64 = Deserialize::read(input)?;
- Ok(usize::try_from(n).map_err(|_| Error::NarSizeFieldTooBig)?)
- }
-}
diff --git a/nix-rust/src/store/binary_cache_store.rs b/nix-rust/src/store/binary_cache_store.rs
deleted file mode 100644
index 9e1e88b7c..000000000
--- a/nix-rust/src/store/binary_cache_store.rs
+++ /dev/null
@@ -1,48 +0,0 @@
-use super::{PathInfo, Store, StorePath};
-use crate::Error;
-use hyper::client::Client;
-
-pub struct BinaryCacheStore {
- base_uri: String,
- client: Client,
-}
-
-impl BinaryCacheStore {
- pub fn new(base_uri: String) -> Self {
- Self {
- base_uri,
- client: Client::new(),
- }
- }
-}
-
-impl Store for BinaryCacheStore {
- fn query_path_info(
- &self,
- path: &StorePath,
- ) -> std::pin::Pin> + Send>> {
- let uri = format!("{}/{}.narinfo", self.base_uri.clone(), path.hash);
- let path = path.clone();
- let client = self.client.clone();
- let store_dir = self.store_dir().to_string();
-
- Box::pin(async move {
- let response = client.get(uri.parse::().unwrap()).await?;
-
- if response.status() == hyper::StatusCode::NOT_FOUND
- || response.status() == hyper::StatusCode::FORBIDDEN
- {
- return Err(Error::InvalidPath(path));
- }
-
- let mut body = response.into_body();
-
- let mut bytes = Vec::new();
- while let Some(next) = body.next().await {
- bytes.extend(next?);
- }
-
- PathInfo::parse_nar_info(std::str::from_utf8(&bytes).unwrap(), &store_dir)
- })
- }
-}
diff --git a/nix-rust/src/store/mod.rs b/nix-rust/src/store/mod.rs
deleted file mode 100644
index da972482c..000000000
--- a/nix-rust/src/store/mod.rs
+++ /dev/null
@@ -1,17 +0,0 @@
-pub mod path;
-
-#[cfg(unused)]
-mod binary_cache_store;
-#[cfg(unused)]
-mod path_info;
-#[cfg(unused)]
-mod store;
-
-pub use path::{StorePath, StorePathHash, StorePathName};
-
-#[cfg(unused)]
-pub use binary_cache_store::BinaryCacheStore;
-#[cfg(unused)]
-pub use path_info::PathInfo;
-#[cfg(unused)]
-pub use store::Store;
diff --git a/nix-rust/src/store/path.rs b/nix-rust/src/store/path.rs
deleted file mode 100644
index 99f7a1f18..000000000
--- a/nix-rust/src/store/path.rs
+++ /dev/null
@@ -1,224 +0,0 @@
-use crate::error::Error;
-use crate::util::base32;
-use std::fmt;
-use std::path::Path;
-
-#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
-pub struct StorePath {
- pub hash: StorePathHash,
- pub name: StorePathName,
-}
-
-pub const STORE_PATH_HASH_BYTES: usize = 20;
-pub const STORE_PATH_HASH_CHARS: usize = 32;
-
-impl StorePath {
- pub fn new(path: &Path, store_dir: &Path) -> Result {
- if path.parent() != Some(store_dir) {
- return Err(Error::NotInStore(path.into()));
- }
- Self::new_from_base_name(
- path.file_name()
- .ok_or_else(|| Error::BadStorePath(path.into()))?
- .to_str()
- .ok_or_else(|| Error::BadStorePath(path.into()))?,
- )
- }
-
- pub fn from_parts(hash: [u8; STORE_PATH_HASH_BYTES], name: &str) -> Result {
- Ok(StorePath {
- hash: StorePathHash(hash),
- name: StorePathName::new(name)?,
- })
- }
-
- pub fn new_from_base_name(base_name: &str) -> Result {
- if base_name.len() < STORE_PATH_HASH_CHARS + 1
- || base_name.as_bytes()[STORE_PATH_HASH_CHARS] != b'-'
- {
- return Err(Error::BadStorePath(base_name.into()));
- }
-
- Ok(StorePath {
- hash: StorePathHash::new(&base_name[0..STORE_PATH_HASH_CHARS])?,
- name: StorePathName::new(&base_name[STORE_PATH_HASH_CHARS + 1..])?,
- })
- }
-}
-
-impl fmt::Display for StorePath {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "{}-{}", self.hash, self.name)
- }
-}
-
-#[derive(Clone, PartialEq, Eq, Debug)]
-pub struct StorePathHash([u8; STORE_PATH_HASH_BYTES]);
-
-impl StorePathHash {
- pub fn new(s: &str) -> Result {
- assert_eq!(s.len(), STORE_PATH_HASH_CHARS);
- let v = base32::decode(s)?;
- assert_eq!(v.len(), STORE_PATH_HASH_BYTES);
- let mut bytes: [u8; 20] = Default::default();
- bytes.copy_from_slice(&v[0..STORE_PATH_HASH_BYTES]);
- Ok(Self(bytes))
- }
-
- pub fn hash(&self) -> &[u8; STORE_PATH_HASH_BYTES] {
- &self.0
- }
-}
-
-impl fmt::Display for StorePathHash {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- let mut buf = vec![0; STORE_PATH_HASH_CHARS];
- base32::encode_into(&self.0, &mut buf);
- f.write_str(std::str::from_utf8(&buf).unwrap())
- }
-}
-
-impl Ord for StorePathHash {
- fn cmp(&self, other: &Self) -> std::cmp::Ordering {
- // Historically we've sorted store paths by their base32
- // serialization, but our base32 encodes bytes in reverse
- // order. So compare them in reverse order as well.
- self.0.iter().rev().cmp(other.0.iter().rev())
- }
-}
-
-impl PartialOrd for StorePathHash {
- fn partial_cmp(&self, other: &Self) -> Option {
- Some(self.cmp(other))
- }
-}
-
-#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
-pub struct StorePathName(String);
-
-impl StorePathName {
- pub fn new(s: &str) -> Result {
- if s.is_empty() {
- return Err(Error::StorePathNameEmpty);
- }
-
- if s.len() > 211 {
- return Err(Error::StorePathNameTooLong);
- }
-
- let is_good_path_name = s.chars().all(|c| {
- c.is_ascii_alphabetic()
- || c.is_ascii_digit()
- || c == '+'
- || c == '-'
- || c == '.'
- || c == '_'
- || c == '?'
- || c == '='
- });
- if s.starts_with('.') || !is_good_path_name {
- return Err(Error::BadStorePathName);
- }
-
- Ok(Self(s.to_string()))
- }
-
- pub fn name(&self) -> &str {
- &self.0
- }
-}
-
-impl fmt::Display for StorePathName {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.write_str(&self.0)
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use assert_matches::assert_matches;
-
- #[test]
- fn test_parse() {
- let s = "7h7qgvs4kgzsn8a6rb273saxyqh4jxlz-konsole-18.12.3";
- let p = StorePath::new_from_base_name(&s).unwrap();
- assert_eq!(p.name.0, "konsole-18.12.3");
- assert_eq!(
- p.hash.0,
- [
- 0x9f, 0x76, 0x49, 0x20, 0xf6, 0x5d, 0xe9, 0x71, 0xc4, 0xca, 0x46, 0x21, 0xab, 0xff,
- 0x9b, 0x44, 0xef, 0x87, 0x0f, 0x3c
- ]
- );
- }
-
- #[test]
- fn test_no_name() {
- let s = "7h7qgvs4kgzsn8a6rb273saxyqh4jxlz-";
- assert_matches!(
- StorePath::new_from_base_name(&s),
- Err(Error::StorePathNameEmpty)
- );
- }
-
- #[test]
- fn test_no_dash() {
- let s = "7h7qgvs4kgzsn8a6rb273saxyqh4jxlz";
- assert_matches!(
- StorePath::new_from_base_name(&s),
- Err(Error::BadStorePath(_))
- );
- }
-
- #[test]
- fn test_short_hash() {
- let s = "7h7qgvs4kgzsn8a6rb273saxyqh4jxl-konsole-18.12.3";
- assert_matches!(
- StorePath::new_from_base_name(&s),
- Err(Error::BadStorePath(_))
- );
- }
-
- #[test]
- fn test_invalid_hash() {
- let s = "7h7qgvs4kgzsn8e6rb273saxyqh4jxlz-konsole-18.12.3";
- assert_matches!(StorePath::new_from_base_name(&s), Err(Error::BadBase32));
- }
-
- #[test]
- fn test_long_name() {
- let s = "7h7qgvs4kgzsn8a6rb273saxyqh4jxlz-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
- assert_matches!(StorePath::new_from_base_name(&s), Ok(_));
- }
-
- #[test]
- fn test_too_long_name() {
- let s = "7h7qgvs4kgzsn8a6rb273saxyqh4jxlz-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
- assert_matches!(
- StorePath::new_from_base_name(&s),
- Err(Error::StorePathNameTooLong)
- );
- }
-
- #[test]
- fn test_bad_name() {
- let s = "7h7qgvs4kgzsn8a6rb273saxyqh4jxlz-foo bar";
- assert_matches!(
- StorePath::new_from_base_name(&s),
- Err(Error::BadStorePathName)
- );
-
- let s = "7h7qgvs4kgzsn8a6rb273saxyqh4jxlz-kónsole";
- assert_matches!(
- StorePath::new_from_base_name(&s),
- Err(Error::BadStorePathName)
- );
- }
-
- #[test]
- fn test_roundtrip() {
- let s = "7h7qgvs4kgzsn8a6rb273saxyqh4jxlz-konsole-18.12.3";
- assert_eq!(StorePath::new_from_base_name(&s).unwrap().to_string(), s);
- }
-}
diff --git a/nix-rust/src/store/path_info.rs b/nix-rust/src/store/path_info.rs
deleted file mode 100644
index c2903ed29..000000000
--- a/nix-rust/src/store/path_info.rs
+++ /dev/null
@@ -1,70 +0,0 @@
-use crate::store::StorePath;
-use crate::Error;
-use std::collections::BTreeSet;
-
-#[derive(Clone, Debug)]
-pub struct PathInfo {
- pub path: StorePath,
- pub references: BTreeSet,
- pub nar_size: u64,
- pub deriver: Option,
-
- // Additional binary cache info.
- pub url: Option,
- pub compression: Option,
- pub file_size: Option,
-}
-
-impl PathInfo {
- pub fn parse_nar_info(nar_info: &str, store_dir: &str) -> Result {
- let mut path = None;
- let mut references = BTreeSet::new();
- let mut nar_size = None;
- let mut deriver = None;
- let mut url = None;
- let mut compression = None;
- let mut file_size = None;
-
- for line in nar_info.lines() {
- let colon = line.find(':').ok_or(Error::BadNarInfo)?;
-
- let (name, value) = line.split_at(colon);
-
- if !value.starts_with(": ") {
- return Err(Error::BadNarInfo);
- }
-
- let value = &value[2..];
-
- if name == "StorePath" {
- path = Some(StorePath::new(std::path::Path::new(value), store_dir)?);
- } else if name == "NarSize" {
- nar_size = Some(u64::from_str_radix(value, 10).map_err(|_| Error::BadNarInfo)?);
- } else if name == "References" {
- if !value.is_empty() {
- for r in value.split(' ') {
- references.insert(StorePath::new_from_base_name(r)?);
- }
- }
- } else if name == "Deriver" {
- deriver = Some(StorePath::new_from_base_name(value)?);
- } else if name == "URL" {
- url = Some(value.into());
- } else if name == "Compression" {
- compression = Some(value.into());
- } else if name == "FileSize" {
- file_size = Some(u64::from_str_radix(value, 10).map_err(|_| Error::BadNarInfo)?);
- }
- }
-
- Ok(PathInfo {
- path: path.ok_or(Error::BadNarInfo)?,
- references,
- nar_size: nar_size.ok_or(Error::BadNarInfo)?,
- deriver,
- url: Some(url.ok_or(Error::BadNarInfo)?),
- compression,
- file_size,
- })
- }
-}
diff --git a/nix-rust/src/store/store.rs b/nix-rust/src/store/store.rs
deleted file mode 100644
index c33dc4a90..000000000
--- a/nix-rust/src/store/store.rs
+++ /dev/null
@@ -1,53 +0,0 @@
-use super::{PathInfo, StorePath};
-use crate::Error;
-use std::collections::{BTreeMap, BTreeSet};
-use std::path::Path;
-
-pub trait Store: Send + Sync {
- fn store_dir(&self) -> &str {
- "/nix/store"
- }
-
- fn query_path_info(
- &self,
- store_path: &StorePath,
- ) -> std::pin::Pin> + Send>>;
-}
-
-impl dyn Store {
- pub fn parse_store_path(&self, path: &Path) -> Result {
- StorePath::new(path, self.store_dir())
- }
-
- pub async fn compute_path_closure(
- &self,
- roots: BTreeSet,
- ) -> Result, Error> {
- let mut done = BTreeSet::new();
- let mut result = BTreeMap::new();
- let mut pending = vec![];
-
- for root in roots {
- pending.push(self.query_path_info(&root));
- done.insert(root);
- }
-
- while !pending.is_empty() {
- let (info, _, remaining) = futures::future::select_all(pending).await;
- pending = remaining;
-
- let info = info?;
-
- for path in &info.references {
- if !done.contains(path) {
- pending.push(self.query_path_info(&path));
- done.insert(path.clone());
- }
- }
-
- result.insert(info.path.clone(), info);
- }
-
- Ok(result)
- }
-}
diff --git a/nix-rust/src/util/base32.rs b/nix-rust/src/util/base32.rs
deleted file mode 100644
index 7e71dc920..000000000
--- a/nix-rust/src/util/base32.rs
+++ /dev/null
@@ -1,160 +0,0 @@
-use crate::error::Error;
-use lazy_static::lazy_static;
-
-pub fn encoded_len(input_len: usize) -> usize {
- if input_len == 0 {
- 0
- } else {
- (input_len * 8 - 1) / 5 + 1
- }
-}
-
-pub fn decoded_len(input_len: usize) -> usize {
- input_len * 5 / 8
-}
-
-static BASE32_CHARS: &[u8; 32] = &b"0123456789abcdfghijklmnpqrsvwxyz";
-
-lazy_static! {
- static ref BASE32_CHARS_REVERSE: Box<[u8; 256]> = {
- let mut xs = [0xffu8; 256];
- for (n, c) in BASE32_CHARS.iter().enumerate() {
- xs[*c as usize] = n as u8;
- }
- Box::new(xs)
- };
-}
-
-pub fn encode(input: &[u8]) -> String {
- let mut buf = vec![0; encoded_len(input.len())];
- encode_into(input, &mut buf);
- std::str::from_utf8(&buf).unwrap().to_string()
-}
-
-pub fn encode_into(input: &[u8], output: &mut [u8]) {
- let len = encoded_len(input.len());
- assert_eq!(len, output.len());
-
- let mut nr_bits_left: usize = 0;
- let mut bits_left: u16 = 0;
- let mut pos = len;
-
- for b in input {
- bits_left |= (*b as u16) << nr_bits_left;
- nr_bits_left += 8;
- while nr_bits_left > 5 {
- output[pos - 1] = BASE32_CHARS[(bits_left & 0x1f) as usize];
- pos -= 1;
- bits_left >>= 5;
- nr_bits_left -= 5;
- }
- }
-
- if nr_bits_left > 0 {
- output[pos - 1] = BASE32_CHARS[(bits_left & 0x1f) as usize];
- pos -= 1;
- }
-
- assert_eq!(pos, 0);
-}
-
-pub fn decode(input: &str) -> Result, crate::Error> {
- let mut res = Vec::with_capacity(decoded_len(input.len()));
-
- let mut nr_bits_left: usize = 0;
- let mut bits_left: u16 = 0;
-
- for c in input.chars().rev() {
- let b = BASE32_CHARS_REVERSE[c as usize];
- if b == 0xff {
- return Err(Error::BadBase32);
- }
- bits_left |= (b as u16) << nr_bits_left;
- nr_bits_left += 5;
- if nr_bits_left >= 8 {
- res.push((bits_left & 0xff) as u8);
- bits_left >>= 8;
- nr_bits_left -= 8;
- }
- }
-
- if nr_bits_left > 0 && bits_left != 0 {
- return Err(Error::BadBase32);
- }
-
- Ok(res)
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use assert_matches::assert_matches;
- use hex;
- use proptest::proptest;
-
- #[test]
- fn test_encode() {
- assert_eq!(encode(&[]), "");
-
- assert_eq!(
- encode(&hex::decode("0839703786356bca59b0f4a32987eb2e6de43ae8").unwrap()),
- "x0xf8v9fxf3jk8zln1cwlsrmhqvp0f88"
- );
-
- assert_eq!(
- encode(
- &hex::decode("ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad")
- .unwrap()
- ),
- "1b8m03r63zqhnjf7l5wnldhh7c134ap5vpj0850ymkq1iyzicy5s"
- );
-
- assert_eq!(
- encode(
- &hex::decode("ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f")
- .unwrap()
- ),
- "2gs8k559z4rlahfx0y688s49m2vvszylcikrfinm30ly9rak69236nkam5ydvly1ai7xac99vxfc4ii84hawjbk876blyk1jfhkbbyx"
- );
- }
-
- #[test]
- fn test_decode() {
- assert_eq!(hex::encode(decode("").unwrap()), "");
-
- assert_eq!(
- hex::encode(decode("x0xf8v9fxf3jk8zln1cwlsrmhqvp0f88").unwrap()),
- "0839703786356bca59b0f4a32987eb2e6de43ae8"
- );
-
- assert_eq!(
- hex::encode(decode("1b8m03r63zqhnjf7l5wnldhh7c134ap5vpj0850ymkq1iyzicy5s").unwrap()),
- "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
- );
-
- assert_eq!(
- hex::encode(decode("2gs8k559z4rlahfx0y688s49m2vvszylcikrfinm30ly9rak69236nkam5ydvly1ai7xac99vxfc4ii84hawjbk876blyk1jfhkbbyx").unwrap()),
- "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"
- );
-
- assert_matches!(
- decode("xoxf8v9fxf3jk8zln1cwlsrmhqvp0f88"),
- Err(Error::BadBase32)
- );
- assert_matches!(
- decode("2b8m03r63zqhnjf7l5wnldhh7c134ap5vpj0850ymkq1iyzicy5s"),
- Err(Error::BadBase32)
- );
- assert_matches!(decode("2"), Err(Error::BadBase32));
- assert_matches!(decode("2gs"), Err(Error::BadBase32));
- assert_matches!(decode("2gs8"), Err(Error::BadBase32));
- }
-
- proptest! {
-
- #[test]
- fn roundtrip(s: Vec) {
- assert_eq!(s, decode(&encode(&s)).unwrap());
- }
- }
-}
diff --git a/nix-rust/src/util/mod.rs b/nix-rust/src/util/mod.rs
deleted file mode 100644
index eaad9d406..000000000
--- a/nix-rust/src/util/mod.rs
+++ /dev/null
@@ -1 +0,0 @@
-pub mod base32;
diff --git a/perl/lib/Nix/Store.xs b/perl/lib/Nix/Store.xs
index edbf12f7c..54ad1680c 100644
--- a/perl/lib/Nix/Store.xs
+++ b/perl/lib/Nix/Store.xs
@@ -240,7 +240,7 @@ SV * convertHash(char * algo, char * s, int toBase32)
PPCODE:
try {
auto h = Hash::parseAny(s, parseHashType(algo));
- string s = h.to_string(toBase32 ? Base32 : Base16, false);
+ auto s = h.to_string(toBase32 ? Base32 : Base16, false);
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
} catch (Error & e) {
croak("%s", e.what());
diff --git a/scripts/check-hydra-status.sh b/scripts/check-hydra-status.sh
index c1d2d7c40..5e2f03429 100644
--- a/scripts/check-hydra-status.sh
+++ b/scripts/check-hydra-status.sh
@@ -16,12 +16,17 @@ someBuildFailed=0
for buildId in $BUILDS_FOR_LATEST_EVAL; do
buildInfo=$(curl -sS -H 'Accept: application/json' "https://hydra.nixos.org/build/$buildId")
- buildStatus=$(echo "$buildInfo" | \
- jq -r '.buildstatus')
+ finished=$(echo "$buildInfo" | jq -r '.finished')
- if [[ "$buildStatus" -ne 0 ]]; then
+ if [[ $finished = 0 ]]; then
+ continue
+ fi
+
+ buildStatus=$(echo "$buildInfo" | jq -r '.buildstatus')
+
+ if [[ $buildStatus != 0 ]]; then
someBuildFailed=1
- echo "Job “$(echo "$buildInfo" | jq -r '.job')” failed on hydra"
+ echo "Job “$(echo "$buildInfo" | jq -r '.job')” failed on hydra: $buildInfo"
fi
done
diff --git a/scripts/install-multi-user.sh b/scripts/install-multi-user.sh
index 3a24296ee..d3ed53d09 100644
--- a/scripts/install-multi-user.sh
+++ b/scripts/install-multi-user.sh
@@ -576,21 +576,40 @@ create_directories() {
# since this bit is cross-platform:
# - first try with `command -vp` to try and find
# chown in the usual places
+ # * to work around some sort of deficiency in
+ # `command -p` in macOS bash 3.2, we also add
+ # PATH="$(getconf PATH 2>/dev/null)". As long as
+ # getconf is found, this should set a sane PATH
+ # which `command -p` in bash 3.2 appears to use.
+ # A bash with a properly-working `command -p`
+ # should ignore this hard-set PATH in favor of
+ # whatever it obtains internally. See
+ # github.com/NixOS/nix/issues/5768
# - fall back on `command -v` which would find
# any chown on path
# if we don't find one, the command is already
# hiding behind || true, and the general state
# should be one the user can repair once they
# figure out where chown is...
- local get_chr_own="$(command -vp chown)"
+ local get_chr_own="$(PATH="$(getconf PATH 2>/dev/null)" command -vp chown)"
if [[ -z "$get_chr_own" ]]; then
get_chr_own="$(command -v chown)"
fi
- _sudo "to take root ownership of existing Nix store files" \
- "$get_chr_own" -R "root:$NIX_BUILD_GROUP_NAME" "$NIX_ROOT" || true
+
+ if [[ -z "$get_chr_own" ]]; then
+ reminder < drvPath;
- string storeUri;
+ std::string storeUri;
while (true) {
@@ -183,7 +183,7 @@ static int main_build_remote(int argc, char * * argv)
else
{
// build the hint template.
- string errorText =
+ std::string errorText =
"Failed to find a machine for remote build!\n"
"derivation: %s\nrequired (system, features): (%s, %s)";
errorText += "\n%s available machines:";
@@ -193,7 +193,7 @@ static int main_build_remote(int argc, char * * argv)
errorText += "\n(%s, %s, %s, %s)";
// add the template values.
- string drvstr;
+ std::string drvstr;
if (drvPath.has_value())
drvstr = drvPath->to_string();
else
@@ -208,7 +208,7 @@ static int main_build_remote(int argc, char * * argv)
for (auto & m : machines)
error
- % concatStringsSep>(", ", m.systemTypes)
+ % concatStringsSep>(", ", m.systemTypes)
% m.maxJobs
% concatStringsSep(", ", m.supportedFeatures)
% concatStringsSep(", ", m.mandatoryFeatures);
diff --git a/src/libcmd/command.cc b/src/libcmd/command.cc
index 429cd32cc..6d183dfad 100644
--- a/src/libcmd/command.cc
+++ b/src/libcmd/command.cc
@@ -54,6 +54,36 @@ void StoreCommand::run()
run(getStore());
}
+CopyCommand::CopyCommand()
+{
+ addFlag({
+ .longName = "from",
+ .description = "URL of the source Nix store.",
+ .labels = {"store-uri"},
+ .handler = {&srcUri},
+ });
+
+ addFlag({
+ .longName = "to",
+ .description = "URL of the destination Nix store.",
+ .labels = {"store-uri"},
+ .handler = {&dstUri},
+ });
+}
+
+ref CopyCommand::createStore()
+{
+ return srcUri.empty() ? StoreCommand::createStore() : openStore(srcUri);
+}
+
+ref CopyCommand::getDstStore()
+{
+ if (srcUri.empty() && dstUri.empty())
+ throw UsageError("you must pass '--from' and/or '--to'");
+
+ return dstUri.empty() ? openStore() : openStore(dstUri);
+}
+
EvalCommand::EvalCommand()
{
}
@@ -73,13 +103,16 @@ ref EvalCommand::getEvalStore()
ref EvalCommand::getEvalState()
{
- if (!evalState) evalState =
-#if HAVE_BOEHMGC
- std::allocate_shared(traceable_allocator(),
-#else
- std::make_shared(
-#endif
- searchPath, getEvalStore(), getStore());
+ if (!evalState)
+ evalState =
+ #if HAVE_BOEHMGC
+ std::allocate_shared(traceable_allocator(),
+ searchPath, getEvalStore(), getStore())
+ #else
+ std::make_shared(
+ searchPath, getEvalStore(), getStore())
+ #endif
+ ;
return ref(evalState);
}
diff --git a/src/libcmd/command.hh b/src/libcmd/command.hh
index 07f398468..bd2a0a7ee 100644
--- a/src/libcmd/command.hh
+++ b/src/libcmd/command.hh
@@ -43,6 +43,19 @@ private:
std::shared_ptr _store;
};
+/* A command that copies something between `--from` and `--to`
+ stores. */
+struct CopyCommand : virtual StoreCommand
+{
+ std::string srcUri, dstUri;
+
+ CopyCommand();
+
+ ref createStore() override;
+
+ ref getDstStore();
+};
+
struct EvalCommand : virtual StoreCommand, MixEvalArgs
{
EvalCommand();
diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc
index dfd8757db..9bce00426 100644
--- a/src/libcmd/installables.cc
+++ b/src/libcmd/installables.cc
@@ -97,7 +97,7 @@ MixFlakeOptions::MixFlakeOptions()
lockFlags.writeLockFile = false;
lockFlags.inputOverrides.insert_or_assign(
flake::parseInputPath(inputPath),
- parseFlakeRef(flakeRef, absPath(".")));
+ parseFlakeRef(flakeRef, absPath("."), true));
}}
});
@@ -158,7 +158,10 @@ SourceExprCommand::SourceExprCommand()
Strings SourceExprCommand::getDefaultFlakeAttrPaths()
{
- return {"defaultPackage." + settings.thisSystem.get()};
+ return {
+ "packages." + settings.thisSystem.get() + ".default",
+ "defaultPackage." + settings.thisSystem.get()
+ };
}
Strings SourceExprCommand::getDefaultFlakeAttrPathPrefixes()
@@ -198,8 +201,9 @@ void SourceExprCommand::completeInstallable(std::string_view prefix)
prefix_ = "";
}
- Value &v1(*findAlongAttrPath(*state, prefix_, *autoArgs, root).first);
- state->forceValue(v1);
+ auto [v, pos] = findAlongAttrPath(*state, prefix_, *autoArgs, root);
+ Value &v1(*v);
+ state->forceValue(v1, pos);
Value v2;
state->autoCallFunction(*autoArgs, v1, v2);
@@ -345,6 +349,18 @@ Installable::getCursor(EvalState & state)
return cursors[0];
}
+static StorePath getDeriver(
+ ref store,
+ const Installable & i,
+ const StorePath & drvPath)
+{
+ auto derivers = store->queryValidDerivers(drvPath);
+ if (derivers.empty())
+ throw Error("'%s' does not have a known deriver", i.what());
+ // FIXME: use all derivers?
+ return *derivers.begin();
+}
+
struct InstallableStorePath : Installable
{
ref store;
@@ -353,7 +369,7 @@ struct InstallableStorePath : Installable
InstallableStorePath(ref store, StorePath && storePath)
: store(store), storePath(std::move(storePath)) { }
- std::string what() override { return store->printStorePath(storePath); }
+ std::string what() const override { return store->printStorePath(storePath); }
DerivedPaths toDerivedPaths() override
{
@@ -374,6 +390,15 @@ struct InstallableStorePath : Installable
}
}
+ StorePathSet toDrvPaths(ref store) override
+ {
+ if (storePath.isDerivation()) {
+ return {storePath};
+ } else {
+ return {getDeriver(store, *this, storePath)};
+ }
+ }
+
std::optional getStorePath() override
{
return storePath;
@@ -402,6 +427,14 @@ DerivedPaths InstallableValue::toDerivedPaths()
return res;
}
+StorePathSet InstallableValue::toDrvPaths(ref store)
+{
+ StorePathSet res;
+ for (auto & drv : toDerivations())
+ res.insert(drv.drvPath);
+ return res;
+}
+
struct InstallableAttrPath : InstallableValue
{
SourceExprCommand & cmd;
@@ -412,12 +445,12 @@ struct InstallableAttrPath : InstallableValue
: InstallableValue(state), cmd(cmd), v(allocRootValue(v)), attrPath(attrPath)
{ }
- std::string what() override { return attrPath; }
+ std::string what() const override { return attrPath; }
std::pair toValue(EvalState & state) override
{
auto [vRes, pos] = findAlongAttrPath(state, attrPath, *cmd.getAutoArgs(state), **v);
- state.forceValue(*vRes);
+ state.forceValue(*vRes, pos);
return {vRes, pos};
}
@@ -467,7 +500,7 @@ Value * InstallableFlake::getFlakeOutputs(EvalState & state, const flake::Locked
auto aOutputs = vFlake->attrs->get(state.symbols.create("outputs"));
assert(aOutputs);
- state.forceValue(*aOutputs->value);
+ state.forceValue(*aOutputs->value, [&]() { return aOutputs->value->determinePos(noPos); });
return aOutputs->value;
}
@@ -492,7 +525,7 @@ ref openEvalCache(
auto vFlake = state.allocValue();
flake::callFlake(state, *lockedFlake, *vFlake);
- state.forceAttrs(*vFlake);
+ state.forceAttrs(*vFlake, noPos);
auto aOutputs = vFlake->attrs->get(state.symbols.create("outputs"));
assert(aOutputs);
@@ -515,13 +548,14 @@ InstallableFlake::InstallableFlake(
SourceExprCommand * cmd,
ref state,
FlakeRef && flakeRef,
- Strings && attrPaths,
- Strings && prefixes,
+ std::string_view fragment,
+ Strings attrPaths,
+ Strings prefixes,
const flake::LockFlags & lockFlags)
: InstallableValue(state),
flakeRef(flakeRef),
- attrPaths(attrPaths),
- prefixes(prefixes),
+ attrPaths(fragment == "" ? attrPaths : Strings{(std::string) fragment}),
+ prefixes(fragment == "" ? Strings{} : prefixes),
lockFlags(lockFlags)
{
if (cmd && cmd->getAutoArgs(*state)->size())
@@ -538,6 +572,8 @@ std::tuple InstallableF
auto root = cache->getRoot();
for (auto & attrPath : getActualAttrPaths()) {
+ debug("trying flake output attribute '%s'", attrPath);
+
auto attr = root->findAlongAttrPath(
parseAttrPath(*state, attrPath),
true
@@ -581,7 +617,7 @@ std::pair InstallableFlake::toValue(EvalState & state)
for (auto & attrPath : getActualAttrPaths()) {
try {
auto [v, pos] = findAlongAttrPath(state, attrPath, *emptyArgs, *vOutputs);
- state.forceValue(*v);
+ state.forceValue(*v, pos);
return {v, pos};
} catch (AttrPathNotFound & e) {
}
@@ -680,7 +716,8 @@ std::vector> SourceExprCommand::parseInstallables(
this,
getEvalState(),
std::move(flakeRef),
- fragment == "" ? getDefaultFlakeAttrPaths() : Strings{fragment},
+ fragment,
+ getDefaultFlakeAttrPaths(),
getDefaultFlakeAttrPathPrefixes(),
lockFlags));
continue;
@@ -838,11 +875,7 @@ StorePathSet toDerivations(
[&](const DerivedPath::Opaque & bo) {
if (!useDeriver)
throw Error("argument '%s' did not evaluate to a derivation", i->what());
- auto derivers = store->queryValidDerivers(bo.path);
- if (derivers.empty())
- throw Error("'%s' does not have a known deriver", i->what());
- // FIXME: use all derivers?
- drvPaths.insert(*derivers.begin());
+ drvPaths.insert(getDeriver(store, *i, bo.path));
},
[&](const DerivedPath::Built & bfd) {
drvPaths.insert(bfd.drvPath);
diff --git a/src/libcmd/installables.hh b/src/libcmd/installables.hh
index 79931ad3e..3d2563e4b 100644
--- a/src/libcmd/installables.hh
+++ b/src/libcmd/installables.hh
@@ -33,10 +33,15 @@ struct Installable
{
virtual ~Installable() { }
- virtual std::string what() = 0;
+ virtual std::string what() const = 0;
virtual DerivedPaths toDerivedPaths() = 0;
+ virtual StorePathSet toDrvPaths(ref store)
+ {
+ throw Error("'%s' cannot be converted to a derivation path", what());
+ }
+
DerivedPath toDerivedPath();
UnresolvedApp toApp(EvalState & state);
@@ -81,6 +86,8 @@ struct InstallableValue : Installable
virtual std::vector toDerivations() = 0;
DerivedPaths toDerivedPaths() override;
+
+ StorePathSet toDrvPaths(ref store) override;
};
struct InstallableFlake : InstallableValue
@@ -95,11 +102,12 @@ struct InstallableFlake : InstallableValue
SourceExprCommand * cmd,
ref state,
FlakeRef && flakeRef,
- Strings && attrPaths,
- Strings && prefixes,
+ std::string_view fragment,
+ Strings attrPaths,
+ Strings prefixes,
const flake::LockFlags & lockFlags);
- std::string what() override { return flakeRef.to_string() + "#" + *attrPaths.begin(); }
+ std::string what() const override { return flakeRef.to_string() + "#" + *attrPaths.begin(); }
std::vector getActualAttrPaths();
diff --git a/src/libcmd/local.mk b/src/libcmd/local.mk
index 8b0662753..7a2f83cc7 100644
--- a/src/libcmd/local.mk
+++ b/src/libcmd/local.mk
@@ -8,7 +8,7 @@ libcmd_SOURCES := $(wildcard $(d)/*.cc)
libcmd_CXXFLAGS += -I src/libutil -I src/libstore -I src/libexpr -I src/libmain -I src/libfetchers
-libcmd_LDFLAGS += -llowdown -pthread
+libcmd_LDFLAGS += $(LOWDOWN_LIBS) -pthread
libcmd_LIBS = libstore libutil libexpr libmain libfetchers
diff --git a/src/libexpr/attr-path.cc b/src/libexpr/attr-path.cc
index c50c6d92b..eb0e706c7 100644
--- a/src/libexpr/attr-path.cc
+++ b/src/libexpr/attr-path.cc
@@ -9,7 +9,7 @@ namespace nix {
static Strings parseAttrPath(std::string_view s)
{
Strings res;
- string cur;
+ std::string cur;
auto i = s.begin();
while (i != s.end()) {
if (*i == '.') {
@@ -41,7 +41,7 @@ std::vector parseAttrPath(EvalState & state, std::string_view s)
}
-std::pair findAlongAttrPath(EvalState & state, const string & attrPath,
+std::pair findAlongAttrPath(EvalState & state, const std::string & attrPath,
Bindings & autoArgs, Value & vIn)
{
Strings tokens = parseAttrPath(attrPath);
@@ -58,7 +58,7 @@ std::pair findAlongAttrPath(EvalState & state, const string & attr
Value * vNew = state.allocValue();
state.autoCallFunction(autoArgs, *v, *vNew);
v = vNew;
- state.forceValue(*v);
+ state.forceValue(*v, noPos);
/* It should evaluate to either a set or an expression,
according to what is specified in the attrPath. */
@@ -121,7 +121,7 @@ Pos findPackageFilename(EvalState & state, Value & v, std::string what)
std::string filename(pos, 0, colon);
unsigned int lineno;
try {
- lineno = std::stoi(std::string(pos, colon + 1));
+ lineno = std::stoi(std::string(pos, colon + 1, std::string::npos));
} catch (std::invalid_argument & e) {
throw ParseError("cannot parse line number '%s'", pos);
}
diff --git a/src/libexpr/attr-path.hh b/src/libexpr/attr-path.hh
index 2ee3ea089..ff1135a06 100644
--- a/src/libexpr/attr-path.hh
+++ b/src/libexpr/attr-path.hh
@@ -10,8 +10,11 @@ namespace nix {
MakeError(AttrPathNotFound, Error);
MakeError(NoPositionInfo, Error);
-std::pair findAlongAttrPath(EvalState & state, const string & attrPath,
- Bindings & autoArgs, Value & vIn);
+std::pair findAlongAttrPath(
+ EvalState & state,
+ const std::string & attrPath,
+ Bindings & autoArgs,
+ Value & vIn);
/* Heuristic to find the filename and lineno or a nix value. */
Pos findPackageFilename(EvalState & state, Value & v, std::string what);
diff --git a/src/libexpr/attr-set.hh b/src/libexpr/attr-set.hh
index 82c348287..cad9743ea 100644
--- a/src/libexpr/attr-set.hh
+++ b/src/libexpr/attr-set.hh
@@ -105,7 +105,7 @@ public:
for (size_t n = 0; n < size_; n++)
res.emplace_back(&attrs[n]);
std::sort(res.begin(), res.end(), [](const Attr * a, const Attr * b) {
- return (const string &) a->name < (const string &) b->name;
+ return (const std::string &) a->name < (const std::string &) b->name;
});
return res;
}
@@ -121,6 +121,8 @@ class BindingsBuilder
Bindings * bindings;
public:
+ // needed by std::back_inserter
+ using value_type = Attr;
EvalState & state;
@@ -134,6 +136,11 @@ public:
}
void insert(const Attr & attr)
+ {
+ push_back(attr);
+ }
+
+ void push_back(const Attr & attr)
{
bindings->push_back(attr);
}
diff --git a/src/libexpr/common-eval-args.cc b/src/libexpr/common-eval-args.cc
index fffca4ac5..e50ff244c 100644
--- a/src/libexpr/common-eval-args.cc
+++ b/src/libexpr/common-eval-args.cc
@@ -77,7 +77,7 @@ Bindings * MixEvalArgs::getAutoArgs(EvalState & state)
for (auto & i : autoArgs) {
auto v = state.allocValue();
if (i.second[0] == 'E')
- state.mkThunk_(*v, state.parseExprFromString(string(i.second, 1), absPath(".")));
+ state.mkThunk_(*v, state.parseExprFromString(i.second.substr(1), absPath(".")));
else
v->mkString(((std::string_view) i.second).substr(1));
res.insert(state.symbols.create(i.first), v);
@@ -85,17 +85,17 @@ Bindings * MixEvalArgs::getAutoArgs(EvalState & state)
return res.finish();
}
-Path lookupFileArg(EvalState & state, string s)
+Path lookupFileArg(EvalState & state, std::string_view s)
{
if (isUri(s)) {
return state.store->toRealPath(
fetchers::downloadTarball(
state.store, resolveUri(s), "source", false).first.storePath);
} else if (s.size() > 2 && s.at(0) == '<' && s.at(s.size() - 1) == '>') {
- Path p = s.substr(1, s.size() - 2);
+ Path p(s.substr(1, s.size() - 2));
return state.findFile(p);
} else
- return absPath(s);
+ return absPath(std::string(s));
}
}
diff --git a/src/libexpr/common-eval-args.hh b/src/libexpr/common-eval-args.hh
index 0e113fff1..03fa226aa 100644
--- a/src/libexpr/common-eval-args.hh
+++ b/src/libexpr/common-eval-args.hh
@@ -22,6 +22,6 @@ private:
std::map autoArgs;
};
-Path lookupFileArg(EvalState & state, string s);
+Path lookupFileArg(EvalState & state, std::string_view s);
}
diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc
index d7e21783d..00d0749f9 100644
--- a/src/libexpr/eval-cache.cc
+++ b/src/libexpr/eval-cache.cc
@@ -336,7 +336,7 @@ Value & AttrCursor::getValue()
if (!_value) {
if (parent) {
auto & vParent = parent->first->getValue();
- root->state.forceAttrs(vParent);
+ root->state.forceAttrs(vParent, noPos);
auto attr = vParent.attrs->get(parent->second);
if (!attr)
throw Error("attribute '%s' is unexpectedly missing", getAttrPathStr());
@@ -381,7 +381,7 @@ Value & AttrCursor::forceValue()
auto & v = getValue();
try {
- root->state.forceValue(v);
+ root->state.forceValue(v, noPos);
} catch (EvalError &) {
debug("setting '%s' to failed", getAttrPathStr());
if (root->db)
@@ -596,7 +596,7 @@ std::vector AttrCursor::getAttrs()
for (auto & attr : *getValue().attrs)
attrs.push_back(attr.name);
std::sort(attrs.begin(), attrs.end(), [](const Symbol & a, const Symbol & b) {
- return (const string &) a < (const string &) b;
+ return (const std::string &) a < (const std::string &) b;
});
if (root->db)
diff --git a/src/libexpr/eval-inline.hh b/src/libexpr/eval-inline.hh
index 655408cd3..aef1f6351 100644
--- a/src/libexpr/eval-inline.hh
+++ b/src/libexpr/eval-inline.hh
@@ -15,12 +15,6 @@ LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s))
});
}
-LocalNoInlineNoReturn(void throwTypeError(const char * s, const Value & v))
-{
- throw TypeError(s, showType(v));
-}
-
-
LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const Value & v))
{
throw TypeError({
@@ -31,6 +25,13 @@ LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const
void EvalState::forceValue(Value & v, const Pos & pos)
+{
+ forceValue(v, [&]() { return pos; });
+}
+
+
+template
+void EvalState::forceValue(Value & v, Callable getPos)
{
if (v.isThunk()) {
Env * env = v.thunk.env;
@@ -47,31 +48,22 @@ void EvalState::forceValue(Value & v, const Pos & pos)
else if (v.isApp())
callFunction(*v.app.left, *v.app.right, v, noPos);
else if (v.isBlackhole())
- throwEvalError(pos, "infinite recursion encountered");
-}
-
-
-inline void EvalState::forceAttrs(Value & v)
-{
- forceValue(v);
- if (v.type() != nAttrs)
- throwTypeError("value is %1% while a set was expected", v);
+ throwEvalError(getPos(), "infinite recursion encountered");
}
inline void EvalState::forceAttrs(Value & v, const Pos & pos)
{
- forceValue(v, pos);
- if (v.type() != nAttrs)
- throwTypeError(pos, "value is %1% while a set was expected", v);
+ forceAttrs(v, [&]() { return pos; });
}
-inline void EvalState::forceList(Value & v)
+template
+inline void EvalState::forceAttrs(Value & v, Callable getPos)
{
- forceValue(v);
- if (!v.isList())
- throwTypeError("value is %1% while a list was expected", v);
+ forceValue(v, getPos);
+ if (v.type() != nAttrs)
+ throwTypeError(getPos(), "value is %1% while a set was expected", v);
}
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index 81aa86641..60f0bf08c 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -1,5 +1,6 @@
#include "eval.hh"
#include "hash.hh"
+#include "types.hh"
#include "util.hh"
#include "store-api.hh"
#include "derivations.hh"
@@ -36,6 +37,19 @@
namespace nix {
+static char * allocString(size_t size)
+{
+ char * t;
+#if HAVE_BOEHMGC
+ t = (char *) GC_MALLOC_ATOMIC(size);
+#else
+ t = malloc(size);
+#endif
+ if (!t) throw std::bad_alloc();
+ return t;
+}
+
+
static char * dupString(const char * s)
{
char * t;
@@ -160,7 +174,7 @@ std::ostream & operator << (std::ostream & str, const Value & v)
}
-const Value *getPrimOp(const Value &v) {
+const Value * getPrimOp(const Value &v) {
const Value * primOp = &v;
while (primOp->isPrimOpApp()) {
primOp = primOp->primOpApp.left;
@@ -169,7 +183,7 @@ const Value *getPrimOp(const Value &v) {
return primOp;
}
-string showType(ValueType type)
+std::string_view showType(ValueType type)
{
switch (type) {
case nInt: return "an integer";
@@ -188,24 +202,24 @@ string showType(ValueType type)
}
-string showType(const Value & v)
+std::string showType(const Value & v)
{
switch (v.internalType) {
case tString: return v.string.context ? "a string with context" : "a string";
case tPrimOp:
- return fmt("the built-in function '%s'", string(v.primOp->name));
+ return fmt("the built-in function '%s'", std::string(v.primOp->name));
case tPrimOpApp:
- return fmt("the partially applied built-in function '%s'", string(getPrimOp(v)->primOp->name));
+ return fmt("the partially applied built-in function '%s'", std::string(getPrimOp(v)->primOp->name));
case tExternal: return v.external->showType();
case tThunk: return "a thunk";
case tApp: return "a function application";
case tBlackhole: return "a black hole";
default:
- return showType(v.type());
+ return std::string(showType(v.type()));
}
}
-Pos Value::determinePos(const Pos &pos) const
+Pos Value::determinePos(const Pos & pos) const
{
switch (internalType) {
case tAttrs: return *attrs->pos;
@@ -342,7 +356,7 @@ void initGC()
/* Very hacky way to parse $NIX_PATH, which is colon-separated, but
can contain URLs (e.g. "nixpkgs=https://bla...:foo=https://"). */
-static Strings parseNixPath(const string & s)
+static Strings parseNixPath(const std::string & s)
{
Strings res;
@@ -412,11 +426,21 @@ EvalState::EvalState(
, sDescription(symbols.create("description"))
, sSelf(symbols.create("self"))
, sEpsilon(symbols.create(""))
+ , sStartSet(symbols.create("startSet"))
+ , sOperator(symbols.create("operator"))
+ , sKey(symbols.create("key"))
+ , sPath(symbols.create("path"))
+ , sPrefix(symbols.create("prefix"))
, repair(NoRepair)
, emptyBindings(0)
, store(store)
, buildStore(buildStore ? buildStore : store)
, regexCache(makeRegexCache())
+#if HAVE_BOEHMGC
+ , valueAllocCache(std::allocate_shared(traceable_allocator(), nullptr))
+#else
+ , valueAllocCache(std::make_shared(nullptr))
+#endif
, baseEnv(allocEnv(128))
, staticBaseEnv(false, 0)
{
@@ -582,7 +606,7 @@ Path EvalState::toRealPath(const Path & path, const PathSet & context)
}
-Value * EvalState::addConstant(const string & name, Value & v)
+Value * EvalState::addConstant(const std::string & name, Value & v)
{
Value * v2 = allocValue();
*v2 = v;
@@ -591,19 +615,19 @@ Value * EvalState::addConstant(const string & name, Value & v)
}
-void EvalState::addConstant(const string & name, Value * v)
+void EvalState::addConstant(const std::string & name, Value * v)
{
staticBaseEnv.vars.emplace_back(symbols.create(name), baseEnvDispl);
baseEnv.values[baseEnvDispl++] = v;
- string name2 = string(name, 0, 2) == "__" ? string(name, 2) : name;
+ auto name2 = name.substr(0, 2) == "__" ? name.substr(2) : name;
baseEnv.values[0]->attrs->push_back(Attr(symbols.create(name2), v));
}
-Value * EvalState::addPrimOp(const string & name,
+Value * EvalState::addPrimOp(const std::string & name,
size_t arity, PrimOpFun primOp)
{
- auto name2 = string(name, 0, 2) == "__" ? string(name, 2) : name;
+ auto name2 = name.substr(0, 2) == "__" ? name.substr(2) : name;
Symbol sym = symbols.create(name2);
/* Hack to make constants lazy: turn them into a application of
@@ -651,7 +675,7 @@ Value * EvalState::addPrimOp(PrimOp && primOp)
}
-Value & EvalState::getBuiltin(const string & name)
+Value & EvalState::getBuiltin(const std::string & name)
{
return *baseEnv.values[0]->attrs->find(symbols.create(name))->value;
}
@@ -679,12 +703,12 @@ std::optional EvalState::getDoc(Value & v)
evaluator. So here are some helper functions for throwing
exceptions. */
-LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2))
+LocalNoInlineNoReturn(void throwEvalError(const char * s, const std::string & s2))
{
throw EvalError(s, s2);
}
-LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const string & s2))
+LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const std::string & s2))
{
throw EvalError({
.msg = hintfmt(s, s2),
@@ -692,12 +716,12 @@ LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const
});
}
-LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2, const string & s3))
+LocalNoInlineNoReturn(void throwEvalError(const char * s, const std::string & s2, const std::string & s3))
{
throw EvalError(s, s2, s3);
}
-LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const string & s2, const string & s3))
+LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const std::string & s2, const std::string & s3))
{
throw EvalError({
.msg = hintfmt(s, s2, s3),
@@ -730,7 +754,12 @@ LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const
});
}
-LocalNoInlineNoReturn(void throwAssertionError(const Pos & pos, const char * s, const string & s1))
+LocalNoInlineNoReturn(void throwTypeError(const char * s, const Value & v))
+{
+ throw TypeError(s, showType(v));
+}
+
+LocalNoInlineNoReturn(void throwAssertionError(const Pos & pos, const char * s, const std::string & s1))
{
throw AssertionError({
.msg = hintfmt(s, s1),
@@ -738,7 +767,7 @@ LocalNoInlineNoReturn(void throwAssertionError(const Pos & pos, const char * s,
});
}
-LocalNoInlineNoReturn(void throwUndefinedVarError(const Pos & pos, const char * s, const string & s1))
+LocalNoInlineNoReturn(void throwUndefinedVarError(const Pos & pos, const char * s, const std::string & s1))
{
throw UndefinedVarError({
.msg = hintfmt(s, s1),
@@ -746,7 +775,7 @@ LocalNoInlineNoReturn(void throwUndefinedVarError(const Pos & pos, const char *
});
}
-LocalNoInlineNoReturn(void throwMissingArgumentError(const Pos & pos, const char * s, const string & s1))
+LocalNoInlineNoReturn(void throwMissingArgumentError(const Pos & pos, const char * s, const std::string & s1))
{
throw MissingArgumentError({
.msg = hintfmt(s, s1),
@@ -754,12 +783,12 @@ LocalNoInlineNoReturn(void throwMissingArgumentError(const Pos & pos, const char
});
}
-LocalNoInline(void addErrorTrace(Error & e, const char * s, const string & s2))
+LocalNoInline(void addErrorTrace(Error & e, const char * s, const std::string & s2))
{
e.addTrace(std::nullopt, s, s2);
}
-LocalNoInline(void addErrorTrace(Error & e, const Pos & pos, const char * s, const string & s2))
+LocalNoInline(void addErrorTrace(Error & e, const Pos & pos, const char * s, const std::string & s2))
{
e.addTrace(pos, s, s2);
}
@@ -771,17 +800,28 @@ void Value::mkString(std::string_view s)
}
+static void copyContextToValue(Value & v, const PathSet & context)
+{
+ if (!context.empty()) {
+ size_t n = 0;
+ v.string.context = (const char * *)
+ allocBytes((context.size() + 1) * sizeof(char *));
+ for (auto & i : context)
+ v.string.context[n++] = dupString(i.c_str());
+ v.string.context[n] = 0;
+ }
+}
+
void Value::mkString(std::string_view s, const PathSet & context)
{
mkString(s);
- if (!context.empty()) {
- size_t n = 0;
- string.context = (const char * *)
- allocBytes((context.size() + 1) * sizeof(char *));
- for (auto & i : context)
- string.context[n++] = dupString(i.c_str());
- string.context[n] = 0;
- }
+ copyContextToValue(*this, context);
+}
+
+void Value::mkStringMove(const char * s, const PathSet & context)
+{
+ mkString(s);
+ copyContextToValue(*this, context);
}
@@ -823,15 +863,15 @@ Value * EvalState::allocValue()
GC_malloc_many returns a linked list of objects of the given size, where the first word
of each object is also the pointer to the next object in the list. This also means that we
have to explicitly clear the first word of every object we take. */
- if (!valueAllocCache) {
- valueAllocCache = GC_malloc_many(sizeof(Value));
- if (!valueAllocCache) throw std::bad_alloc();
+ if (!*valueAllocCache) {
+ *valueAllocCache = GC_malloc_many(sizeof(Value));
+ if (!*valueAllocCache) throw std::bad_alloc();
}
/* GC_NEXT is a convenience macro for accessing the first word of an object.
Take the first list item, advance the list to the next item, and clear the next pointer. */
- void * p = valueAllocCache;
- GC_PTR_STORE_AND_DIRTY(&valueAllocCache, GC_NEXT(p));
+ void * p = *valueAllocCache;
+ GC_PTR_STORE_AND_DIRTY(&*valueAllocCache, GC_NEXT(p));
GC_NEXT(p) = nullptr;
nrValues++;
@@ -1103,7 +1143,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
Hence we need __overrides.) */
if (hasOverrides) {
Value * vOverrides = (*v.attrs)[overrides->second.displ].value;
- state.forceAttrs(*vOverrides);
+ state.forceAttrs(*vOverrides, [&]() { return vOverrides->determinePos(noPos); });
Bindings * newBnds = state.allocBindings(v.attrs->capacity() + vOverrides->attrs->size());
for (auto & i : *v.attrs)
newBnds->push_back(i);
@@ -1181,7 +1221,7 @@ void ExprVar::eval(EvalState & state, Env & env, Value & v)
}
-static string showAttrPath(EvalState & state, Env & env, const AttrPath & attrPath)
+static std::string showAttrPath(EvalState & state, Env & env, const AttrPath & attrPath)
{
std::ostringstream out;
bool first = true;
@@ -1251,7 +1291,7 @@ void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v)
e->eval(state, env, vTmp);
for (auto & i : attrPath) {
- state.forceValue(*vAttrs);
+ state.forceValue(*vAttrs, noPos);
Bindings::iterator j;
Symbol name = getName(i, state, env);
if (vAttrs->type() != nAttrs ||
@@ -1339,7 +1379,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
/* Nope, so show the first unexpected argument to the
user. */
for (auto & i : *args[0]->attrs)
- if (!lambda.formals->argNames.count(i.name))
+ if (!lambda.formals->has(i.name))
throwTypeError(pos, "%1% called with unexpected argument '%2%'", lambda, i.name);
abort(); // can't happen
}
@@ -1355,7 +1395,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
if (loggerSettings.showTrace.get()) {
addErrorTrace(e, lambda.pos, "while evaluating %s",
(lambda.name.set()
- ? "'" + (string) lambda.name + "'"
+ ? "'" + (const std::string &) lambda.name + "'"
: "anonymous lambda"));
addErrorTrace(e, pos, "from call site%s", "");
}
@@ -1465,14 +1505,16 @@ void EvalState::incrFunctionCall(ExprLambda * fun)
void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res)
{
- forceValue(fun);
+ auto pos = fun.determinePos(noPos);
+
+ forceValue(fun, pos);
if (fun.type() == nAttrs) {
auto found = fun.attrs->find(sFunctor);
if (found != fun.attrs->end()) {
Value * v = allocValue();
- callFunction(*found->value, fun, *v, noPos);
- forceValue(*v);
+ callFunction(*found->value, fun, *v, pos);
+ forceValue(*v, pos);
return autoCallFunction(args, *v, res);
}
}
@@ -1660,15 +1702,39 @@ void EvalState::concatLists(Value & v, size_t nrLists, Value * * lists, const Po
void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
{
PathSet context;
- std::ostringstream s;
+ std::vector s;
+ size_t sSize = 0;
NixInt n = 0;
NixFloat nf = 0;
bool first = !forceString;
ValueType firstType = nString;
+ const auto str = [&] {
+ std::string result;
+ result.reserve(sSize);
+ for (const auto & part : s) result += *part;
+ return result;
+ };
+ /* c_str() is not str().c_str() because we want to create a string
+ Value. allocating a GC'd string directly and moving it into a
+ Value lets us avoid an allocation and copy. */
+ const auto c_str = [&] {
+ char * result = allocString(sSize + 1);
+ char * tmp = result;
+ for (const auto & part : s) {
+ memcpy(tmp, part->data(), part->size());
+ tmp += part->size();
+ }
+ *tmp = 0;
+ return result;
+ };
+
+ Value values[es->size()];
+ Value * vTmpP = values;
+
for (auto & [i_pos, i] : *es) {
- Value vTmp;
+ Value & vTmp = *vTmpP++;
i->eval(state, env, vTmp);
/* If the first element is a path, then the result will also
@@ -1696,11 +1762,15 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
nf += vTmp.fpoint;
} else
throwEvalError(i_pos, "cannot add %1% to a float", showType(vTmp));
- } else
+ } else {
+ if (s.empty()) s.reserve(es->size());
/* skip canonization of first path, which would only be not
canonized in the first place if it's coming from a ./${foo} type
path */
- s << state.coerceToString(i_pos, vTmp, context, false, firstType == nString, !first);
+ auto part = state.coerceToString(i_pos, vTmp, context, false, firstType == nString, !first);
+ sSize += part->size();
+ s.emplace_back(std::move(part));
+ }
first = false;
}
@@ -1712,9 +1782,9 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
else if (firstType == nPath) {
if (!context.empty())
throwEvalError(pos, "a string that refers to a store path cannot be appended to a path");
- v.mkPath(canonPath(s.str()));
+ v.mkPath(canonPath(str()));
} else
- v.mkString(s.str(), context);
+ v.mkStringMove(c_str(), context);
}
@@ -1733,7 +1803,7 @@ void EvalState::forceValueDeep(Value & v)
recurse = [&](Value & v) {
if (!seen.insert(&v).second) return;
- forceValue(v);
+ forceValue(v, [&]() { return v.determinePos(noPos); });
if (v.type() == nAttrs) {
for (auto & i : *v.attrs)
@@ -1798,7 +1868,7 @@ void EvalState::forceFunction(Value & v, const Pos & pos)
}
-string EvalState::forceString(Value & v, const Pos & pos)
+std::string_view EvalState::forceString(Value & v, const Pos & pos)
{
forceValue(v, pos);
if (v.type() != nString) {
@@ -1807,13 +1877,13 @@ string EvalState::forceString(Value & v, const Pos & pos)
else
throwTypeError("value is %1% while a string was expected", v);
}
- return string(v.string.s);
+ return v.string.s;
}
/* Decode a context string ‘!!’ into a pair . */
-std::pair decodeContext(std::string_view s)
+std::pair decodeContext(std::string_view s)
{
if (s.at(0) == '!') {
size_t index = s.find("!", 1);
@@ -1842,17 +1912,17 @@ std::vector> Value::getContext()
}
-string EvalState::forceString(Value & v, PathSet & context, const Pos & pos)
+std::string_view EvalState::forceString(Value & v, PathSet & context, const Pos & pos)
{
- string s = forceString(v, pos);
+ auto s = forceString(v, pos);
copyContext(v, context);
return s;
}
-string EvalState::forceStringNoCtx(Value & v, const Pos & pos)
+std::string_view EvalState::forceStringNoCtx(Value & v, const Pos & pos)
{
- string s = forceString(v, pos);
+ auto s = forceString(v, pos);
if (v.string.context) {
if (pos)
throwEvalError(pos, "the string '%1%' is not allowed to refer to a store path (such as '%2%')",
@@ -1870,47 +1940,48 @@ bool EvalState::isDerivation(Value & v)
if (v.type() != nAttrs) return false;
Bindings::iterator i = v.attrs->find(sType);
if (i == v.attrs->end()) return false;
- forceValue(*i->value);
+ forceValue(*i->value, *i->pos);
if (i->value->type() != nString) return false;
return strcmp(i->value->string.s, "derivation") == 0;
}
-std::optional EvalState::tryAttrsToString(const Pos & pos, Value & v,
+std::optional EvalState::tryAttrsToString(const Pos & pos, Value & v,
PathSet & context, bool coerceMore, bool copyToStore)
{
auto i = v.attrs->find(sToString);
if (i != v.attrs->end()) {
Value v1;
callFunction(*i->value, v, v1, pos);
- return coerceToString(pos, v1, context, coerceMore, copyToStore);
+ return coerceToString(pos, v1, context, coerceMore, copyToStore).toOwned();
}
return {};
}
-string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
+BackedStringView EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
bool coerceMore, bool copyToStore, bool canonicalizePath)
{
forceValue(v, pos);
- string s;
-
if (v.type() == nString) {
copyContext(v, context);
- return v.string.s;
+ return std::string_view(v.string.s);
}
if (v.type() == nPath) {
- Path path(canonicalizePath ? canonPath(v.path) : v.path);
- return copyToStore ? copyPathToStore(context, path) : path;
+ BackedStringView path(PathView(v.path));
+ if (canonicalizePath)
+ path = canonPath(*path);
+ if (copyToStore)
+ path = copyPathToStore(context, std::move(path).toOwned());
+ return path;
}
if (v.type() == nAttrs) {
auto maybeString = tryAttrsToString(pos, v, context, coerceMore, copyToStore);
- if (maybeString) {
- return *maybeString;
- }
+ if (maybeString)
+ return std::move(*maybeString);
auto i = v.attrs->find(sOutPath);
if (i == v.attrs->end()) throwTypeError(pos, "cannot coerce a set to a string");
return coerceToString(pos, *i->value, context, coerceMore, copyToStore);
@@ -1930,16 +2001,15 @@ string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
if (v.type() == nNull) return "";
if (v.isList()) {
- string result;
+ std::string result;
for (auto [n, v2] : enumerate(v.listItems())) {
- result += coerceToString(pos, *v2,
- context, coerceMore, copyToStore);
+ result += *coerceToString(pos, *v2, context, coerceMore, copyToStore);
if (n < v.listSize() - 1
/* !!! not quite correct */
&& (!v2->isList() || v2->listSize() != 0))
result += " ";
}
- return result;
+ return std::move(result);
}
}
@@ -1947,7 +2017,7 @@ string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
}
-string EvalState::copyPathToStore(PathSet & context, const Path & path)
+std::string EvalState::copyPathToStore(PathSet & context, const Path & path)
{
if (nix::isDerivation(path))
throwEvalError("file names are not allowed to end in '%1%'", drvExtension);
@@ -1973,7 +2043,7 @@ string EvalState::copyPathToStore(PathSet & context, const Path & path)
Path EvalState::coerceToPath(const Pos & pos, Value & v, PathSet & context)
{
- string path = coerceToString(pos, v, context, false, false);
+ auto path = coerceToString(pos, v, context, false, false).toOwned();
if (path == "" || path[0] != '/')
throwEvalError(pos, "string '%1%' doesn't represent an absolute path", path);
return path;
@@ -1982,8 +2052,8 @@ Path EvalState::coerceToPath(const Pos & pos, Value & v, PathSet & context)
bool EvalState::eqValues(Value & v1, Value & v2)
{
- forceValue(v1);
- forceValue(v2);
+ forceValue(v1, noPos);
+ forceValue(v2, noPos);
/* !!! Hack to support some old broken code that relies on pointer
equality tests between sets. (Specifically, builderDefs calls
@@ -2143,11 +2213,11 @@ void EvalState::printStats()
for (auto & i : functionCalls) {
auto obj = list.object();
if (i.first->name.set())
- obj.attr("name", (const string &) i.first->name);
+ obj.attr("name", (const std::string &) i.first->name);
else
obj.attr("name", nullptr);
if (i.first->pos) {
- obj.attr("file", (const string &) i.first->pos.file);
+ obj.attr("file", (const std::string &) i.first->pos.file);
obj.attr("line", i.first->pos.line);
obj.attr("column", i.first->pos.column);
}
@@ -2159,7 +2229,7 @@ void EvalState::printStats()
for (auto & i : attrSelects) {
auto obj = list.object();
if (i.first) {
- obj.attr("file", (const string &) i.first.file);
+ obj.attr("file", (const std::string &) i.first.file);
obj.attr("line", i.first.line);
obj.attr("column", i.first.column);
}
@@ -2176,7 +2246,7 @@ void EvalState::printStats()
}
-string ExternalValueBase::coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore) const
+std::string ExternalValueBase::coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore) const
{
throw TypeError({
.msg = hintfmt("cannot coerce %1% to a string", showType()),
diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh
index 89814785e..1f0e97b2e 100644
--- a/src/libexpr/eval.hh
+++ b/src/libexpr/eval.hh
@@ -1,6 +1,7 @@
#pragma once
#include "attr-set.hh"
+#include "types.hh"
#include "value.hh"
#include "nixexpr.hh"
#include "symbol-table.hh"
@@ -80,7 +81,8 @@ public:
sContentAddressed,
sOutputHash, sOutputHashAlgo, sOutputHashMode,
sRecurseForDerivations,
- sDescription, sSelf, sEpsilon;
+ sDescription, sSelf, sEpsilon, sStartSet, sOperator, sKey, sPath,
+ sPrefix;
Symbol sDerivationNix;
/* If set, force copying files to the Nix store even if they
@@ -132,7 +134,7 @@ private:
std::shared_ptr regexCache;
/* Allocation cache for GC'd Value objects. */
- void * valueAllocCache = nullptr;
+ std::shared_ptr valueAllocCache;
public:
@@ -148,7 +150,7 @@ public:
const Pos & pos
);
- void addToSearchPath(const string & s);
+ void addToSearchPath(const std::string & s);
SearchPath getSearchPath() { return searchPath; }
@@ -179,8 +181,8 @@ public:
Expr * parseExprFromFile(const Path & path, StaticEnv & staticEnv);
/* Parse a Nix expression from the specified string. */
- Expr * parseExprFromString(std::string_view s, const Path & basePath, StaticEnv & staticEnv);
- Expr * parseExprFromString(std::string_view s, const Path & basePath);
+ Expr * parseExprFromString(std::string s, const Path & basePath, StaticEnv & staticEnv);
+ Expr * parseExprFromString(std::string s, const Path & basePath);
Expr * parseStdin();
@@ -200,8 +202,8 @@ public:
void resetFileCache();
/* Look up a file in the search path. */
- Path findFile(const string & path);
- Path findFile(SearchPath & searchPath, const string & path, const Pos & pos = noPos);
+ Path findFile(const std::string_view path);
+ Path findFile(SearchPath & searchPath, const std::string_view path, const Pos & pos = noPos);
/* If the specified search path element is a URI, download it. */
std::pair resolveSearchPathElem(const SearchPathElem & elem);
@@ -220,7 +222,10 @@ public:
of the evaluation of the thunk. If `v' is a delayed function
application, call the function and overwrite `v' with the
result. Otherwise, this is a no-op. */
- inline void forceValue(Value & v, const Pos & pos = noPos);
+ inline void forceValue(Value & v, const Pos & pos);
+
+ template
+ inline void forceValue(Value & v, Callable getPos);
/* Force a value, then recursively force list elements and
attributes. */
@@ -230,31 +235,34 @@ public:
NixInt forceInt(Value & v, const Pos & pos);
NixFloat forceFloat(Value & v, const Pos & pos);
bool forceBool(Value & v, const Pos & pos);
- inline void forceAttrs(Value & v);
- inline void forceAttrs(Value & v, const Pos & pos);
- inline void forceList(Value & v);
+
+ void forceAttrs(Value & v, const Pos & pos);
+
+ template
+ inline void forceAttrs(Value & v, Callable getPos);
+
inline void forceList(Value & v, const Pos & pos);
void forceFunction(Value & v, const Pos & pos); // either lambda or primop
- string forceString(Value & v, const Pos & pos = noPos);
- string forceString(Value & v, PathSet & context, const Pos & pos = noPos);
- string forceStringNoCtx(Value & v, const Pos & pos = noPos);
+ std::string_view forceString(Value & v, const Pos & pos = noPos);
+ std::string_view forceString(Value & v, PathSet & context, const Pos & pos = noPos);
+ std::string_view forceStringNoCtx(Value & v, const Pos & pos = noPos);
/* Return true iff the value `v' denotes a derivation (i.e. a
set with attribute `type = "derivation"'). */
bool isDerivation(Value & v);
- std::optional tryAttrsToString(const Pos & pos, Value & v,
+ std::optional tryAttrsToString(const Pos & pos, Value & v,
PathSet & context, bool coerceMore = false, bool copyToStore = true);
/* String coercion. Converts strings, paths and derivations to a
string. If `coerceMore' is set, also converts nulls, integers,
booleans and lists to a string. If `copyToStore' is set,
referenced paths are copied to the Nix store as a side effect. */
- string coerceToString(const Pos & pos, Value & v, PathSet & context,
+ BackedStringView coerceToString(const Pos & pos, Value & v, PathSet & context,
bool coerceMore = false, bool copyToStore = true,
bool canonicalizePath = true);
- string copyPathToStore(PathSet & context, const Path & path);
+ std::string copyPathToStore(PathSet & context, const Path & path);
/* Path coercion. Converts strings, paths and derivations to a
path. The result is guaranteed to be a canonicalised, absolute
@@ -276,18 +284,18 @@ private:
void createBaseEnv();
- Value * addConstant(const string & name, Value & v);
+ Value * addConstant(const std::string & name, Value & v);
- void addConstant(const string & name, Value * v);
+ void addConstant(const std::string & name, Value * v);
- Value * addPrimOp(const string & name,
+ Value * addPrimOp(const std::string & name,
size_t arity, PrimOpFun primOp);
Value * addPrimOp(PrimOp && primOp);
public:
- Value & getBuiltin(const string & name);
+ Value & getBuiltin(const std::string & name);
struct Doc
{
@@ -308,8 +316,8 @@ private:
friend struct ExprAttrs;
friend struct ExprLet;
- Expr * parse(const char * text, FileOrigin origin, const Path & path,
- const Path & basePath, StaticEnv & staticEnv);
+ Expr * parse(char * text, size_t length, FileOrigin origin, const PathView path,
+ const PathView basePath, StaticEnv & staticEnv);
public:
@@ -399,18 +407,19 @@ private:
friend struct ExprSelect;
friend void prim_getAttr(EvalState & state, const Pos & pos, Value * * args, Value & v);
friend void prim_match(EvalState & state, const Pos & pos, Value * * args, Value & v);
+ friend void prim_split(EvalState & state, const Pos & pos, Value * * args, Value & v);
friend struct Value;
};
/* Return a string representing the type of the value `v'. */
-string showType(ValueType type);
-string showType(const Value & v);
+std::string_view showType(ValueType type);
+std::string showType(const Value & v);
/* Decode a context string ‘!!’ into a pair . */
-std::pair decodeContext(std::string_view s);
+std::pair decodeContext(std::string_view s);
/* If `path' refers to a directory, then append "/default.nix". */
Path resolveExprPath(Path path);
diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc
index bf7731429..252c1ddc3 100644
--- a/src/libexpr/flake/flake.cc
+++ b/src/libexpr/flake/flake.cc
@@ -89,11 +89,11 @@ static void expectType(EvalState & state, ValueType type,
static std::map parseFlakeInputs(
EvalState & state, Value * value, const Pos & pos,
- const std::optional & baseDir);
+ const std::optional & baseDir, InputPath lockRootPath);
static FlakeInput parseFlakeInput(EvalState & state,
const std::string & inputName, Value * value, const Pos & pos,
- const std::optional & baseDir)
+ const std::optional & baseDir, InputPath lockRootPath)
{
expectType(state, nAttrs, *value, pos);
@@ -117,10 +117,12 @@ static FlakeInput parseFlakeInput(EvalState & state,
expectType(state, nBool, *attr.value, *attr.pos);
input.isFlake = attr.value->boolean;
} else if (attr.name == sInputs) {
- input.overrides = parseFlakeInputs(state, attr.value, *attr.pos, baseDir);
+ input.overrides = parseFlakeInputs(state, attr.value, *attr.pos, baseDir, lockRootPath);
} else if (attr.name == sFollows) {
expectType(state, nString, *attr.value, *attr.pos);
- input.follows = parseInputPath(attr.value->string.s);
+ auto follows(parseInputPath(attr.value->string.s));
+ follows.insert(follows.begin(), lockRootPath.begin(), lockRootPath.end());
+ input.follows = follows;
} else {
switch (attr.value->type()) {
case nString:
@@ -166,7 +168,7 @@ static FlakeInput parseFlakeInput(EvalState & state,
static std::map parseFlakeInputs(
EvalState & state, Value * value, const Pos & pos,
- const std::optional & baseDir)
+ const std::optional & baseDir, InputPath lockRootPath)
{
std::map inputs;
@@ -178,7 +180,8 @@ static std::map parseFlakeInputs(
inputAttr.name,
inputAttr.value,
*inputAttr.pos,
- baseDir));
+ baseDir,
+ lockRootPath));
}
return inputs;
@@ -188,7 +191,8 @@ static Flake getFlake(
EvalState & state,
const FlakeRef & originalRef,
bool allowLookup,
- FlakeCache & flakeCache)
+ FlakeCache & flakeCache,
+ InputPath lockRootPath)
{
auto [sourceInfo, resolvedRef, lockedRef] = fetchOrSubstituteTree(
state, originalRef, allowLookup, flakeCache);
@@ -223,7 +227,7 @@ static Flake getFlake(
auto sInputs = state.symbols.create("inputs");
if (auto inputs = vInfo.attrs->get(sInputs))
- flake.inputs = parseFlakeInputs(state, inputs->value, *inputs->pos, flakeDir);
+ flake.inputs = parseFlakeInputs(state, inputs->value, *inputs->pos, flakeDir, lockRootPath);
auto sOutputs = state.symbols.create("outputs");
@@ -250,10 +254,12 @@ static Flake getFlake(
for (auto & setting : *nixConfig->value->attrs) {
forceTrivialValue(state, *setting.value, *setting.pos);
if (setting.value->type() == nString)
- flake.config.settings.insert({setting.name, state.forceStringNoCtx(*setting.value, *setting.pos)});
+ flake.config.settings.insert({setting.name, std::string(state.forceStringNoCtx(*setting.value, *setting.pos))});
else if (setting.value->type() == nPath) {
PathSet emptyContext = {};
- flake.config.settings.insert({setting.name, state.coerceToString(*setting.pos, *setting.value, emptyContext, false, true, true)});
+ flake.config.settings.emplace(
+ setting.name,
+ state.coerceToString(*setting.pos, *setting.value, emptyContext, false, true, true) .toOwned());
}
else if (setting.value->type() == nInt)
flake.config.settings.insert({setting.name, state.forceInt(*setting.value, *setting.pos)});
@@ -265,7 +271,7 @@ static Flake getFlake(
if (elem->type() != nString)
throw TypeError("list element in flake configuration setting '%s' is %s while a string is expected",
setting.name, showType(*setting.value));
- ss.push_back(state.forceStringNoCtx(*elem, *setting.pos));
+ ss.emplace_back(state.forceStringNoCtx(*elem, *setting.pos));
}
flake.config.settings.insert({setting.name, ss});
}
@@ -287,6 +293,11 @@ static Flake getFlake(
return flake;
}
+Flake getFlake(EvalState & state, const FlakeRef & originalRef, bool allowLookup, FlakeCache & flakeCache)
+{
+ return getFlake(state, originalRef, allowLookup, flakeCache, {});
+}
+
Flake getFlake(EvalState & state, const FlakeRef & originalRef, bool allowLookup)
{
FlakeCache flakeCache;
@@ -334,22 +345,12 @@ LockedFlake lockFlake(
std::vector parents;
- struct LockParent {
- /* The path to this parent. */
- InputPath path;
-
- /* Whether we are currently inside a top-level lockfile
- (inputs absolute) or subordinate lockfile (inputs
- relative). */
- bool absolute;
- };
-
std::function node,
const InputPath & inputPathPrefix,
std::shared_ptr oldNode,
- const LockParent & parent,
+ const InputPath & lockRootPath,
const Path & parentPath,
bool trustLock)>
computeLocks;
@@ -359,7 +360,7 @@ LockedFlake lockFlake(
std::shared_ptr node,
const InputPath & inputPathPrefix,
std::shared_ptr oldNode,
- const LockParent & parent,
+ const InputPath & lockRootPath,
const Path & parentPath,
bool trustLock)
{
@@ -404,17 +405,7 @@ LockedFlake lockFlake(
if (input.follows) {
InputPath target;
- if (parent.absolute && !hasOverride) {
- target = *input.follows;
- } else {
- if (hasOverride) {
- target = inputPathPrefix;
- target.pop_back();
- } else
- target = parent.path;
-
- for (auto & i : *input.follows) target.push_back(i);
- }
+ target.insert(target.end(), input.follows->begin(), input.follows->end());
debug("input '%s' follows '%s'", inputPathS, printInputPath(target));
node->inputs.insert_or_assign(id, target);
@@ -487,30 +478,32 @@ LockedFlake lockFlake(
break;
}
}
+ auto absoluteFollows(lockRootPath);
+ absoluteFollows.insert(absoluteFollows.end(), follows->begin(), follows->end());
fakeInputs.emplace(i.first, FlakeInput {
- .follows = *follows,
+ .follows = absoluteFollows,
});
}
}
}
- LockParent newParent {
- .path = inputPath,
- .absolute = true
- };
-
+ auto localPath(parentPath);
+ // If this input is a path, recurse it down.
+ // This allows us to resolve path inputs relative to the current flake.
+ if ((*input.ref).input.getType() == "path")
+ localPath = absPath(*input.ref->input.getSourcePath(), parentPath);
computeLocks(
mustRefetch
- ? getFlake(state, oldLock->lockedRef, false, flakeCache).inputs
+ ? getFlake(state, oldLock->lockedRef, false, flakeCache, inputPath).inputs
: fakeInputs,
- childNode, inputPath, oldLock, newParent, parentPath, !mustRefetch);
+ childNode, inputPath, oldLock, lockRootPath, parentPath, !mustRefetch);
} else {
/* We need to create a new lock file entry. So fetch
this input. */
debug("creating new input '%s'", inputPathS);
- if (!lockFlags.allowMutable && !input.ref->input.isImmutable())
+ if (!lockFlags.allowMutable && !input.ref->input.isLocked())
throw Error("cannot update flake input '%s' in pure mode", inputPathS);
if (input.isFlake) {
@@ -522,7 +515,7 @@ LockedFlake lockFlake(
if (localRef.input.getType() == "path")
localPath = absPath(*input.ref->input.getSourcePath(), parentPath);
- auto inputFlake = getFlake(state, localRef, useRegistries, flakeCache);
+ auto inputFlake = getFlake(state, localRef, useRegistries, flakeCache, inputPath);
/* Note: in case of an --override-input, we use
the *original* ref (input2.ref) for the
@@ -543,13 +536,6 @@ LockedFlake lockFlake(
parents.push_back(*input.ref);
Finally cleanup([&]() { parents.pop_back(); });
- // Follows paths from existing inputs in the top-level lockfile are absolute,
- // whereas paths in subordinate lockfiles are relative to those lockfiles.
- LockParent newParent {
- .path = inputPath,
- .absolute = oldLock ? true : false
- };
-
/* Recursively process the inputs of this
flake. Also, unless we already have this flake
in the top-level lock file, use this flake's
@@ -560,7 +546,7 @@ LockedFlake lockFlake(
? std::dynamic_pointer_cast(oldLock)
: LockFile::read(
inputFlake.sourceInfo->actualPath + "/" + inputFlake.lockedRef.subdir + "/flake.lock").root,
- newParent, localPath, false);
+ oldLock ? lockRootPath : inputPath, localPath, false);
}
else {
@@ -578,17 +564,12 @@ LockedFlake lockFlake(
}
};
- LockParent parent {
- .path = {},
- .absolute = true
- };
-
// Bring in the current ref for relative path resolution if we have it
auto parentPath = canonPath(flake.sourceInfo->actualPath + "/" + flake.lockedRef.subdir, true);
computeLocks(
flake.inputs, newLockFile.root, {},
- lockFlags.recreateLockFile ? nullptr : oldLockFile.root, parent, parentPath, false);
+ lockFlags.recreateLockFile ? nullptr : oldLockFile.root, {}, parentPath, false);
for (auto & i : lockFlags.inputOverrides)
if (!overridesUsed.count(i.first))
@@ -635,12 +616,24 @@ LockedFlake lockFlake(
newLockFile.write(path);
+ std::optional commitMessage = std::nullopt;
+ if (lockFlags.commitLockFile) {
+ std::string cm;
+
+ cm = settings.commitLockFileSummary.get();
+
+ if (cm == "") {
+ cm = fmt("%s: %s", relPath, lockFileExists ? "Update" : "Add");
+ }
+
+ cm += "\n\nFlake lock file updates:\n\n";
+ cm += filterANSIEscapes(diff, true);
+ commitMessage = cm;
+ }
+
topRef.input.markChangedFile(
(topRef.subdir == "" ? "" : topRef.subdir + "/") + "flake.lock",
- lockFlags.commitLockFile
- ? std::optional(fmt("%s: %s\n\nFlake lock file changes:\n\n%s",
- relPath, lockFileExists ? "Update" : "Add", filterANSIEscapes(diff, true)))
- : std::nullopt);
+ commitMessage);
/* Rewriting the lockfile changed the top-level
repo, so we should re-read it. FIXME: we could
@@ -659,7 +652,7 @@ LockedFlake lockFlake(
now. Corner case: we could have reverted from a
dirty to a clean tree! */
if (flake.lockedRef.input == prevLockedRef.input
- && !flake.lockedRef.input.isImmutable())
+ && !flake.lockedRef.input.isLocked())
throw Error("'%s' did not change after I updated its 'flake.lock' file; is 'flake.lock' under version control?", flake.originalRef);
}
} else
@@ -716,10 +709,10 @@ static void prim_getFlake(EvalState & state, const Pos & pos, Value * * args, Va
{
state.requireExperimentalFeatureOnEvaluation(Xp::Flakes, "builtins.getFlake", pos);
- auto flakeRefS = state.forceStringNoCtx(*args[0], pos);
+ std::string flakeRefS(state.forceStringNoCtx(*args[0], pos));
auto flakeRef = parseFlakeRef(flakeRefS, {}, true);
- if (evalSettings.pureEval && !flakeRef.input.isImmutable())
- throw Error("cannot call 'getFlake' on mutable flake reference '%s', at %s (use --impure to override)", flakeRefS, pos);
+ if (evalSettings.pureEval && !flakeRef.input.isLocked())
+ throw Error("cannot call 'getFlake' on unlocked flake reference '%s', at %s (use --impure to override)", flakeRefS, pos);
callFlake(state,
lockFlake(state, flakeRef,
diff --git a/src/libexpr/flake/flakeref.cc b/src/libexpr/flake/flakeref.cc
index c3b74e0fe..930ed9ccd 100644
--- a/src/libexpr/flake/flakeref.cc
+++ b/src/libexpr/flake/flakeref.cc
@@ -122,6 +122,28 @@ std::pair parseFlakeRefWithFragment(
if (isFlake) {
+ if (!allowMissing && !pathExists(path + "/flake.nix")){
+ notice("path '%s' does not contain a 'flake.nix', searching up",path);
+
+ // Save device to detect filesystem boundary
+ dev_t device = lstat(path).st_dev;
+ bool found = false;
+ while (path != "/") {
+ if (pathExists(path + "/flake.nix")) {
+ found = true;
+ break;
+ } else if (pathExists(path + "/.git"))
+ throw Error("path '%s' is not part of a flake (neither it nor its parent directories contain a 'flake.nix' file)", path);
+ else {
+ if (lstat(path).st_dev != device)
+ throw Error("unable to find a flake before encountering filesystem boundary at '%s'", path);
+ }
+ path = dirOf(path);
+ }
+ if (!found)
+ throw BadURL("could not find a flake.nix file");
+ }
+
if (!S_ISDIR(lstat(path).st_mode))
throw BadURL("path '%s' is not a flake (because it's not a directory)", path);
diff --git a/src/libexpr/flake/lockfile.cc b/src/libexpr/flake/lockfile.cc
index fda340789..60b52d578 100644
--- a/src/libexpr/flake/lockfile.cc
+++ b/src/libexpr/flake/lockfile.cc
@@ -35,7 +35,7 @@ LockedNode::LockedNode(const nlohmann::json & json)
, originalRef(getFlakeRef(json, "original", nullptr))
, isFlake(json.find("flake") != json.end() ? (bool) json["flake"] : true)
{
- if (!lockedRef.input.isImmutable())
+ if (!lockedRef.input.isLocked())
throw Error("lockfile contains mutable lock '%s'",
fetchers::attrsToJSON(lockedRef.input.toAttrs()));
}
@@ -220,7 +220,7 @@ bool LockFile::isImmutable() const
for (auto & i : nodes) {
if (i == root) continue;
auto lockedNode = std::dynamic_pointer_cast(i);
- if (lockedNode && !lockedNode->lockedRef.input.isImmutable()) return false;
+ if (lockedNode && !lockedNode->lockedRef.input.isLocked()) return false;
}
return true;
diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc
index 25fd9b949..8393e4225 100644
--- a/src/libexpr/get-drvs.cc
+++ b/src/libexpr/get-drvs.cc
@@ -11,8 +11,8 @@
namespace nix {
-DrvInfo::DrvInfo(EvalState & state, const string & attrPath, Bindings * attrs)
- : state(&state), attrs(attrs), attrPath(attrPath)
+DrvInfo::DrvInfo(EvalState & state, std::string attrPath, Bindings * attrs)
+ : state(&state), attrs(attrs), attrPath(std::move(attrPath))
{
}
@@ -47,7 +47,7 @@ DrvInfo::DrvInfo(EvalState & state, ref store, const std::string & drvPat
}
-string DrvInfo::queryName() const
+std::string DrvInfo::queryName() const
{
if (name == "" && attrs) {
auto i = attrs->find(state->sName);
@@ -58,7 +58,7 @@ string DrvInfo::queryName() const
}
-string DrvInfo::querySystem() const
+std::string DrvInfo::querySystem() const
{
if (system == "" && attrs) {
auto i = attrs->find(state->sSystem);
@@ -68,7 +68,7 @@ string DrvInfo::querySystem() const
}
-string DrvInfo::queryDrvPath() const
+std::string DrvInfo::queryDrvPath() const
{
if (drvPath == "" && attrs) {
Bindings::iterator i = attrs->find(state->sDrvPath);
@@ -79,7 +79,7 @@ string DrvInfo::queryDrvPath() const
}
-string DrvInfo::queryOutPath() const
+std::string DrvInfo::queryOutPath() const
{
if (!outPath && attrs) {
Bindings::iterator i = attrs->find(state->sOutPath);
@@ -104,10 +104,10 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool onlyOutputsToInstall)
/* For each output... */
for (auto elem : i->value->listItems()) {
/* Evaluate the corresponding set. */
- string name = state->forceStringNoCtx(*elem, *i->pos);
+ std::string name(state->forceStringNoCtx(*elem, *i->pos));
Bindings::iterator out = attrs->find(state->symbols.create(name));
if (out == attrs->end()) continue; // FIXME: throw error?
- state->forceAttrs(*out->value);
+ state->forceAttrs(*out->value, *i->pos);
/* And evaluate its ‘outPath’ attribute. */
Bindings::iterator outPath = out->value->attrs->find(state->sOutPath);
@@ -138,7 +138,7 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool onlyOutputsToInstall)
}
-string DrvInfo::queryOutputName() const
+std::string DrvInfo::queryOutputName() const
{
if (outputName == "" && attrs) {
Bindings::iterator i = attrs->find(state->sOutputName);
@@ -172,7 +172,7 @@ StringSet DrvInfo::queryMetaNames()
bool DrvInfo::checkMeta(Value & v)
{
- state->forceValue(v);
+ state->forceValue(v, [&]() { return v.determinePos(noPos); });
if (v.type() == nList) {
for (auto elem : v.listItems())
if (!checkMeta(*elem)) return false;
@@ -190,7 +190,7 @@ bool DrvInfo::checkMeta(Value & v)
}
-Value * DrvInfo::queryMeta(const string & name)
+Value * DrvInfo::queryMeta(const std::string & name)
{
if (!getMeta()) return 0;
Bindings::iterator a = meta->find(state->symbols.create(name));
@@ -199,7 +199,7 @@ Value * DrvInfo::queryMeta(const string & name)
}
-string DrvInfo::queryMetaString(const string & name)
+std::string DrvInfo::queryMetaString(const std::string & name)
{
Value * v = queryMeta(name);
if (!v || v->type() != nString) return "";
@@ -207,7 +207,7 @@ string DrvInfo::queryMetaString(const string & name)
}
-NixInt DrvInfo::queryMetaInt(const string & name, NixInt def)
+NixInt DrvInfo::queryMetaInt(const std::string & name, NixInt def)
{
Value * v = queryMeta(name);
if (!v) return def;
@@ -221,7 +221,7 @@ NixInt DrvInfo::queryMetaInt(const string & name, NixInt def)
return def;
}
-NixFloat DrvInfo::queryMetaFloat(const string & name, NixFloat def)
+NixFloat DrvInfo::queryMetaFloat(const std::string & name, NixFloat def)
{
Value * v = queryMeta(name);
if (!v) return def;
@@ -236,7 +236,7 @@ NixFloat DrvInfo::queryMetaFloat(const string & name, NixFloat def)
}
-bool DrvInfo::queryMetaBool(const string & name, bool def)
+bool DrvInfo::queryMetaBool(const std::string & name, bool def)
{
Value * v = queryMeta(name);
if (!v) return def;
@@ -251,7 +251,7 @@ bool DrvInfo::queryMetaBool(const string & name, bool def)
}
-void DrvInfo::setMeta(const string & name, Value * v)
+void DrvInfo::setMeta(const std::string & name, Value * v)
{
getMeta();
auto attrs = state->buildBindings(1 + (meta ? meta->size() : 0));
@@ -266,7 +266,7 @@ void DrvInfo::setMeta(const string & name, Value * v)
/* Cache for already considered attrsets. */
-typedef set Done;
+typedef std::set Done;
/* Evaluate value `v'. If it evaluates to a set of type `derivation',
@@ -274,11 +274,11 @@ typedef set Done;
The result boolean indicates whether it makes sense
for the caller to recursively search for derivations in `v'. */
static bool getDerivation(EvalState & state, Value & v,
- const string & attrPath, DrvInfos & drvs, Done & done,
+ const std::string & attrPath, DrvInfos & drvs, Done & done,
bool ignoreAssertionFailures)
{
try {
- state.forceValue(v);
+ state.forceValue(v, [&]() { return v.determinePos(noPos); });
if (!state.isDerivation(v)) return true;
/* Remove spurious duplicates (e.g., a set like `rec { x =
@@ -311,7 +311,7 @@ std::optional getDerivation(EvalState & state, Value & v,
}
-static string addToPath(const string & s1, const string & s2)
+static std::string addToPath(const std::string & s1, const std::string & s2)
{
return s1.empty() ? s2 : s1 + "." + s2;
}
@@ -321,7 +321,7 @@ static std::regex attrRegex("[A-Za-z_][A-Za-z0-9-_+]*");
static void getDerivations(EvalState & state, Value & vIn,
- const string & pathPrefix, Bindings & autoArgs,
+ const std::string & pathPrefix, Bindings & autoArgs,
DrvInfos & drvs, Done & done,
bool ignoreAssertionFailures)
{
@@ -346,7 +346,7 @@ static void getDerivations(EvalState & state, Value & vIn,
debug("evaluating attribute '%1%'", i->name);
if (!std::regex_match(std::string(i->name), attrRegex))
continue;
- string pathPrefix2 = addToPath(pathPrefix, i->name);
+ std::string pathPrefix2 = addToPath(pathPrefix, i->name);
if (combineChannels)
getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
else if (getDerivation(state, *i->value, pathPrefix2, drvs, done, ignoreAssertionFailures)) {
@@ -364,7 +364,7 @@ static void getDerivations(EvalState & state, Value & vIn,
else if (v.type() == nList) {
for (auto [n, elem] : enumerate(v.listItems())) {
- string pathPrefix2 = addToPath(pathPrefix, fmt("%d", n));
+ std::string pathPrefix2 = addToPath(pathPrefix, fmt("%d", n));
if (getDerivation(state, *elem, pathPrefix2, drvs, done, ignoreAssertionFailures))
getDerivations(state, *elem, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
}
@@ -374,7 +374,7 @@ static void getDerivations(EvalState & state, Value & vIn,
}
-void getDerivations(EvalState & state, Value & v, const string & pathPrefix,
+void getDerivations(EvalState & state, Value & v, const std::string & pathPrefix,
Bindings & autoArgs, DrvInfos & drvs, bool ignoreAssertionFailures)
{
Done done;
diff --git a/src/libexpr/get-drvs.hh b/src/libexpr/get-drvs.hh
index 29bb6a660..d13847785 100644
--- a/src/libexpr/get-drvs.hh
+++ b/src/libexpr/get-drvs.hh
@@ -12,16 +12,16 @@ namespace nix {
struct DrvInfo
{
public:
- typedef std::map Outputs;
+ typedef std::map Outputs;
private:
EvalState * state;
- mutable string name;
- mutable string system;
- mutable string drvPath;
- mutable std::optional outPath;
- mutable string outputName;
+ mutable std::string name;
+ mutable std::string system;
+ mutable std::string drvPath;
+ mutable std::optional outPath;
+ mutable std::string outputName;
Outputs outputs;
bool failed = false; // set if we get an AssertionError
@@ -33,36 +33,36 @@ private:
bool checkMeta(Value & v);
public:
- string attrPath; /* path towards the derivation */
+ std::string attrPath; /* path towards the derivation */
DrvInfo(EvalState & state) : state(&state) { };
- DrvInfo(EvalState & state, const string & attrPath, Bindings * attrs);
+ DrvInfo(EvalState & state, std::string attrPath, Bindings * attrs);
DrvInfo(EvalState & state, ref store, const std::string & drvPathWithOutputs);
- string queryName() const;
- string querySystem() const;
- string queryDrvPath() const;
- string queryOutPath() const;
- string queryOutputName() const;
+ std::string queryName() const;
+ std::string querySystem() const;
+ std::string queryDrvPath() const;
+ std::string queryOutPath() const;
+ std::string queryOutputName() const;
/** Return the list of outputs. The "outputs to install" are determined by `meta.outputsToInstall`. */
Outputs queryOutputs(bool onlyOutputsToInstall = false);
StringSet queryMetaNames();
- Value * queryMeta(const string & name);
- string queryMetaString(const string & name);
- NixInt queryMetaInt(const string & name, NixInt def);
- NixFloat queryMetaFloat(const string & name, NixFloat def);
- bool queryMetaBool(const string & name, bool def);
- void setMeta(const string & name, Value * v);
+ Value * queryMeta(const std::string & name);
+ std::string queryMetaString(const std::string & name);
+ NixInt queryMetaInt(const std::string & name, NixInt def);
+ NixFloat queryMetaFloat(const std::string & name, NixFloat def);
+ bool queryMetaBool(const std::string & name, bool def);
+ void setMeta(const std::string & name, Value * v);
/*
MetaInfo queryMetaInfo(EvalState & state) const;
MetaValue queryMetaInfo(EvalState & state, const string & name) const;
*/
- void setName(const string & s) { name = s; }
- void setDrvPath(const string & s) { drvPath = s; }
- void setOutPath(const string & s) { outPath = s; }
+ void setName(const std::string & s) { name = s; }
+ void setDrvPath(const std::string & s) { drvPath = s; }
+ void setOutPath(const std::string & s) { outPath = s; }
void setFailed() { failed = true; };
bool hasFailed() { return failed; };
@@ -70,9 +70,9 @@ public:
#if HAVE_BOEHMGC
-typedef list > DrvInfos;
+typedef std::list > DrvInfos;
#else
-typedef list DrvInfos;
+typedef std::list DrvInfos;
#endif
@@ -81,7 +81,7 @@ typedef list DrvInfos;
std::optional getDerivation(EvalState & state,
Value & v, bool ignoreAssertionFailures);
-void getDerivations(EvalState & state, Value & v, const string & pathPrefix,
+void getDerivations(EvalState & state, Value & v, const std::string & pathPrefix,
Bindings & autoArgs, DrvInfos & drvs,
bool ignoreAssertionFailures);
diff --git a/src/libexpr/json-to-value.cc b/src/libexpr/json-to-value.cc
index 88716250c..99a475ff9 100644
--- a/src/libexpr/json-to-value.cc
+++ b/src/libexpr/json-to-value.cc
@@ -163,7 +163,7 @@ public:
}
};
-void parseJSON(EvalState & state, const string & s_, Value & v)
+void parseJSON(EvalState & state, const std::string_view & s_, Value & v)
{
JSONSax parser(state, v);
bool res = json::sax_parse(s_, &parser);
diff --git a/src/libexpr/json-to-value.hh b/src/libexpr/json-to-value.hh
index 3b0fdae11..84bec4eba 100644
--- a/src/libexpr/json-to-value.hh
+++ b/src/libexpr/json-to-value.hh
@@ -8,6 +8,6 @@ namespace nix {
MakeError(JSONParseError, EvalError);
-void parseJSON(EvalState & state, const string & s, Value & v);
+void parseJSON(EvalState & state, const std::string_view & s, Value & v);
}
diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l
index c18877e29..e276b0467 100644
--- a/src/libexpr/lexer.l
+++ b/src/libexpr/lexer.l
@@ -64,29 +64,32 @@ static void adjustLoc(YYLTYPE * loc, const char * s, size_t len)
}
-// FIXME: optimize
-static Expr * unescapeStr(SymbolTable & symbols, const char * s, size_t length)
+// we make use of the fact that the parser receives a private copy of the input
+// string and can munge around in it.
+static StringToken unescapeStr(SymbolTable & symbols, char * s, size_t length)
{
- string t;
- t.reserve(length);
+ char * result = s;
+ char * t = s;
char c;
+ // the input string is terminated with *two* NULs, so we can safely take
+ // *one* character after the one being checked against.
while ((c = *s++)) {
if (c == '\\') {
- assert(*s);
c = *s++;
- if (c == 'n') t += '\n';
- else if (c == 'r') t += '\r';
- else if (c == 't') t += '\t';
- else t += c;
+ if (c == 'n') *t = '\n';
+ else if (c == 'r') *t = '\r';
+ else if (c == 't') *t = '\t';
+ else *t = c;
}
else if (c == '\r') {
/* Normalise CR and CR/LF into LF. */
- t += '\n';
+ *t = '\n';
if (*s == '\n') s++; /* cr/lf */
}
- else t += c;
+ else *t = c;
+ t++;
}
- return new ExprString(symbols.create(t));
+ return {result, size_t(t - result)};
}
@@ -139,7 +142,7 @@ or { return OR_KW; }
\/\/ { return UPDATE; }
\+\+ { return CONCAT; }
-{ID} { yylval->id = strdup(yytext); return ID; }
+{ID} { yylval->id = {yytext, (size_t) yyleng}; return ID; }
{INT} { errno = 0;
try {
yylval->n = boost::lexical_cast(yytext);
@@ -173,7 +176,7 @@ or { return OR_KW; }
/* It is impossible to match strings ending with '$' with one
regex because trailing contexts are only valid at the end
of a rule. (A sane but undocumented limitation.) */
- yylval->e = unescapeStr(data->symbols, yytext, yyleng);
+ yylval->str = unescapeStr(data->symbols, yytext, yyleng);
return STR;
}
\$\{ { PUSH_STATE(DEFAULT); return DOLLAR_CURLY; }
@@ -188,26 +191,26 @@ or { return OR_KW; }
\'\'(\ *\n)? { PUSH_STATE(IND_STRING); return IND_STRING_OPEN; }
([^\$\']|\$[^\{\']|\'[^\'\$])+ {
- yylval->e = new ExprIndStr(yytext);
+ yylval->str = {yytext, (size_t) yyleng, true};
return IND_STR;
}
\'\'\$ |
\$ {
- yylval->e = new ExprIndStr("$");
+ yylval->str = {"$", 1};
return IND_STR;
}
\'\'\' {
- yylval->e = new ExprIndStr("''");
+ yylval->str = {"''", 2};
return IND_STR;
}
\'\'\\{ANY} {
- yylval->e = unescapeStr(data->symbols, yytext + 2, yyleng - 2);
+ yylval->str = unescapeStr(data->symbols, yytext + 2, yyleng - 2);
return IND_STR;
}
\$\{ { PUSH_STATE(DEFAULT); return DOLLAR_CURLY; }
\'\' { POP_STATE(); return IND_STRING_CLOSE; }
\' {
- yylval->e = new ExprIndStr("'");
+ yylval->str = {"'", 1};
return IND_STR;
}
@@ -221,14 +224,14 @@ or { return OR_KW; }
{PATH_SEG} {
POP_STATE();
PUSH_STATE(INPATH_SLASH);
- yylval->path = strdup(yytext);
+ yylval->path = {yytext, (size_t) yyleng};
return PATH;
}
{HPATH_START} {
POP_STATE();
PUSH_STATE(INPATH_SLASH);
- yylval->path = strdup(yytext);
+ yylval->path = {yytext, (size_t) yyleng};
return HPATH;
}
@@ -237,7 +240,7 @@ or { return OR_KW; }
PUSH_STATE(INPATH_SLASH);
else
PUSH_STATE(INPATH);
- yylval->path = strdup(yytext);
+ yylval->path = {yytext, (size_t) yyleng};
return PATH;
}
{HPATH} {
@@ -245,7 +248,7 @@ or { return OR_KW; }
PUSH_STATE(INPATH_SLASH);
else
PUSH_STATE(INPATH);
- yylval->path = strdup(yytext);
+ yylval->path = {yytext, (size_t) yyleng};
return HPATH;
}
@@ -261,7 +264,7 @@ or { return OR_KW; }
PUSH_STATE(INPATH_SLASH);
else
PUSH_STATE(INPATH);
- yylval->e = new ExprString(data->symbols.create(string(yytext)));
+ yylval->str = {yytext, (size_t) yyleng};
return STR;
}
{ANY} |
@@ -280,8 +283,8 @@ or { return OR_KW; }
throw ParseError("path has a trailing slash");
}
-{SPATH} { yylval->path = strdup(yytext); return SPATH; }
-{URI} { yylval->uri = strdup(yytext); return URI; }
+{SPATH} { yylval->path = {yytext, (size_t) yyleng}; return SPATH; }
+{URI} { yylval->uri = {yytext, (size_t) yyleng}; return URI; }
[ \t\r\n]+ /* eat up whitespace */
\#[^\r\n]* /* single-line comments */
diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc
index a75357871..a2def65a6 100644
--- a/src/libexpr/nixexpr.cc
+++ b/src/libexpr/nixexpr.cc
@@ -16,10 +16,10 @@ std::ostream & operator << (std::ostream & str, const Expr & e)
return str;
}
-static void showString(std::ostream & str, const string & s)
+static void showString(std::ostream & str, std::string_view s)
{
str << '"';
- for (auto c : (string) s)
+ for (auto c : s)
if (c == '"' || c == '\\' || c == '$') str << "\\" << c;
else if (c == '\n') str << "\\n";
else if (c == '\r') str << "\\r";
@@ -28,7 +28,7 @@ static void showString(std::ostream & str, const string & s)
str << '"';
}
-static void showId(std::ostream & str, const string & s)
+static void showId(std::ostream & str, std::string_view s)
{
if (s.empty())
str << "\"\"";
@@ -103,11 +103,18 @@ void ExprAttrs::show(std::ostream & str) const
{
if (recursive) str << "rec ";
str << "{ ";
- for (auto & i : attrs)
- if (i.second.inherited)
- str << "inherit " << i.first << " " << "; ";
+ typedef const decltype(attrs)::value_type * Attr;
+ std::vector sorted;
+ for (auto & i : attrs) sorted.push_back(&i);
+ std::sort(sorted.begin(), sorted.end(), [](Attr a, Attr b) {
+ return (const std::string &) a->first < (const std::string &) b->first;
+ });
+ for (auto & i : sorted) {
+ if (i->second.inherited)
+ str << "inherit " << i->first << " " << "; ";
else
- str << i.first << " = " << *i.second.e << "; ";
+ str << i->first << " = " << *i->second.e << "; ";
+ }
for (auto & i : dynamicAttrs)
str << "\"${" << *i.nameExpr << "}\" = " << *i.valueExpr << "; ";
str << "}";
@@ -191,7 +198,7 @@ void ExprConcatStrings::show(std::ostream & str) const
str << "(";
for (auto & i : *es) {
if (first) first = false; else str << " + ";
- str << i.second;
+ str << *i.second;
}
str << ")";
}
@@ -211,7 +218,7 @@ std::ostream & operator << (std::ostream & str, const Pos & pos)
auto f = format(ANSI_BOLD "%1%" ANSI_NORMAL ":%2%:%3%");
switch (pos.origin) {
case foFile:
- f % (string) pos.file;
+ f % (const std::string &) pos.file;
break;
case foStdin:
case foString:
@@ -227,7 +234,7 @@ std::ostream & operator << (std::ostream & str, const Pos & pos)
}
-string showAttrPath(const AttrPath & attrPath)
+std::string showAttrPath(const AttrPath & attrPath)
{
std::ostringstream out;
bool first = true;
@@ -461,9 +468,9 @@ void ExprLambda::setName(Symbol & name)
}
-string ExprLambda::showNamePos() const
+std::string ExprLambda::showNamePos() const
{
- return (format("%1% at %2%") % (name.set() ? "'" + (string) name + "'" : "anonymous function") % pos).str();
+ return fmt("%1% at %2%", name.set() ? "'" + (std::string) name + "'" : "anonymous function", pos);
}
@@ -473,7 +480,7 @@ string ExprLambda::showNamePos() const
size_t SymbolTable::totalSize() const
{
size_t n = 0;
- for (auto & i : symbols)
+ for (auto & i : store)
n += i.size();
return n;
}
diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh
index 0a60057e5..12b54b8eb 100644
--- a/src/libexpr/nixexpr.hh
+++ b/src/libexpr/nixexpr.hh
@@ -26,18 +26,21 @@ struct Pos
FileOrigin origin;
Symbol file;
unsigned int line, column;
- Pos() : origin(foString), line(0), column(0) { };
+
+ Pos() : origin(foString), line(0), column(0) { }
Pos(FileOrigin origin, const Symbol & file, unsigned int line, unsigned int column)
- : origin(origin), file(file), line(line), column(column) { };
+ : origin(origin), file(file), line(line), column(column) { }
+
operator bool() const
{
return line != 0;
}
+
bool operator < (const Pos & p2) const
{
if (!line) return p2.line;
if (!p2.line) return false;
- int d = ((string) file).compare((string) p2.file);
+ int d = ((const std::string &) file).compare((const std::string &) p2.file);
if (d < 0) return true;
if (d > 0) return false;
if (line < p2.line) return true;
@@ -68,7 +71,7 @@ struct AttrName
typedef std::vector AttrPath;
-string showAttrPath(const AttrPath & attrPath);
+std::string showAttrPath(const AttrPath & attrPath);
/* Abstract syntax of Nix expressions. */
@@ -110,25 +113,18 @@ struct ExprFloat : Expr
struct ExprString : Expr
{
- Symbol s;
+ std::string s;
Value v;
- ExprString(const Symbol & s) : s(s) { v.mkString(s); };
+ ExprString(std::string s) : s(std::move(s)) { v.mkString(this->s.data()); };
COMMON_METHODS
Value * maybeThunk(EvalState & state, Env & env);
};
-/* Temporary class used during parsing of indented strings. */
-struct ExprIndStr : Expr
-{
- string s;
- ExprIndStr(const string & s) : s(s) { };
-};
-
struct ExprPath : Expr
{
- string s;
+ std::string s;
Value v;
- ExprPath(const string & s) : s(s) { v.mkPath(this->s.c_str()); };
+ ExprPath(std::string s) : s(std::move(s)) { v.mkPath(this->s.c_str()); };
COMMON_METHODS
Value * maybeThunk(EvalState & state, Env & env);
};
@@ -223,10 +219,25 @@ struct Formal
struct Formals
{
- typedef std::list Formals_;
+ typedef std::vector Formals_;
Formals_ formals;
- std::set argNames; // used during parsing
bool ellipsis;
+
+ bool has(Symbol arg) const {
+ auto it = std::lower_bound(formals.begin(), formals.end(), arg,
+ [] (const Formal & f, const Symbol & sym) { return f.name < sym; });
+ return it != formals.end() && it->name == arg;
+ }
+
+ std::vector lexicographicOrder() const
+ {
+ std::vector result(formals.begin(), formals.end());
+ std::sort(result.begin(), result.end(),
+ [] (const Formal & a, const Formal & b) {
+ return std::string_view(a.name) < std::string_view(b.name);
+ });
+ return result;
+ }
};
struct ExprLambda : Expr
@@ -239,14 +250,9 @@ struct ExprLambda : Expr
ExprLambda(const Pos & pos, const Symbol & arg, Formals * formals, Expr * body)
: pos(pos), arg(arg), formals(formals), body(body)
{
- if (!arg.empty() && formals && formals->argNames.find(arg) != formals->argNames.end())
- throw ParseError({
- .msg = hintfmt("duplicate formal function argument '%1%'", arg),
- .errPos = pos
- });
};
void setName(Symbol & name);
- string showNamePos() const;
+ std::string showNamePos() const;
inline bool hasFormals() const { return formals != nullptr; }
COMMON_METHODS
};
@@ -332,8 +338,8 @@ struct ExprConcatStrings : Expr
{
Pos pos;
bool forceString;
- vector > * es;
- ExprConcatStrings(const Pos & pos, bool forceString, vector > * es)
+ std::vector > * es;
+ ExprConcatStrings(const Pos & pos, bool forceString, std::vector > * es)
: pos(pos), forceString(forceString), es(es) { };
COMMON_METHODS
};
@@ -364,15 +370,19 @@ struct StaticEnv
void sort()
{
- std::sort(vars.begin(), vars.end(),
+ std::stable_sort(vars.begin(), vars.end(),
[](const Vars::value_type & a, const Vars::value_type & b) { return a.first < b.first; });
}
void deduplicate()
{
- const auto last = std::unique(vars.begin(), vars.end(),
- [] (const Vars::value_type & a, const Vars::value_type & b) { return a.first == b.first; });
- vars.erase(last, vars.end());
+ auto it = vars.begin(), jt = it, end = vars.end();
+ while (jt != end) {
+ *it = *jt++;
+ while (jt != end && it->first == jt->first) *it = *jt++;
+ it++;
+ }
+ vars.erase(it, end);
}
Vars::const_iterator find(const Symbol & name) const
diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y
index f8aaea582..919b9cfae 100644
--- a/src/libexpr/parser.y
+++ b/src/libexpr/parser.y
@@ -16,6 +16,8 @@
#ifndef BISON_HEADER
#define BISON_HEADER
+#include
+
#include "util.hh"
#include "nixexpr.hh"
@@ -39,8 +41,22 @@ namespace nix {
{ };
};
+ struct ParserFormals {
+ std::vector formals;
+ bool ellipsis = false;
+ };
+
}
+// using C a struct allows us to avoid having to define the special
+// members that using string_view here would implicitly delete.
+struct StringToken {
+ const char * p;
+ size_t l;
+ bool hasIndentation;
+ operator std::string_view() const { return {p, l}; }
+};
+
#define YY_DECL int yylex \
(YYSTYPE * yylval_param, YYLTYPE * yylloc_param, yyscan_t yyscanner, nix::ParseData * data)
@@ -140,21 +156,46 @@ static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
}
-static void addFormal(const Pos & pos, Formals * formals, const Formal & formal)
+static Formals * toFormals(ParseData & data, ParserFormals * formals,
+ Pos pos = noPos, Symbol arg = {})
{
- if (!formals->argNames.insert(formal.name).second)
+ std::sort(formals->formals.begin(), formals->formals.end(),
+ [] (const auto & a, const auto & b) {
+ return std::tie(a.name, a.pos) < std::tie(b.name, b.pos);
+ });
+
+ std::optional> duplicate;
+ for (size_t i = 0; i + 1 < formals->formals.size(); i++) {
+ if (formals->formals[i].name != formals->formals[i + 1].name)
+ continue;
+ std::pair thisDup{formals->formals[i].name, formals->formals[i + 1].pos};
+ duplicate = std::min(thisDup, duplicate.value_or(thisDup));
+ }
+ if (duplicate)
throw ParseError({
- .msg = hintfmt("duplicate formal function argument '%1%'",
- formal.name),
+ .msg = hintfmt("duplicate formal function argument '%1%'", duplicate->first),
+ .errPos = duplicate->second
+ });
+
+ Formals result;
+ result.ellipsis = formals->ellipsis;
+ result.formals = std::move(formals->formals);
+
+ if (arg.set() && result.has(arg))
+ throw ParseError({
+ .msg = hintfmt("duplicate formal function argument '%1%'", arg),
.errPos = pos
});
- formals->formals.push_front(formal);
+
+ delete formals;
+ return new Formals(std::move(result));
}
-static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector > & es)
+static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols,
+ std::vector > > & es)
{
- if (es.empty()) return new ExprString(symbols.create(""));
+ if (es.empty()) return new ExprString("");
/* Figure out the minimum indentation. Note that by design
whitespace-only final lines are not taken into account. (So
@@ -163,20 +204,20 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector(i);
- if (!e) {
- /* Anti-quotations end the current start-of-line whitespace. */
+ auto * str = std::get_if(&i);
+ if (!str || !str->hasIndentation) {
+ /* Anti-quotations and escaped characters end the current start-of-line whitespace. */
if (atStartOfLine) {
atStartOfLine = false;
if (curIndent < minIndent) minIndent = curIndent;
}
continue;
}
- for (size_t j = 0; j < e->s.size(); ++j) {
+ for (size_t j = 0; j < str->l; ++j) {
if (atStartOfLine) {
- if (e->s[j] == ' ')
+ if (str->p[j] == ' ')
curIndent++;
- else if (e->s[j] == '\n') {
+ else if (str->p[j] == '\n') {
/* Empty line, doesn't influence minimum
indentation. */
curIndent = 0;
@@ -184,7 +225,7 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vectors[j] == '\n') {
+ } else if (str->p[j] == '\n') {
atStartOfLine = true;
curIndent = 0;
}
@@ -192,49 +233,50 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector > * es2 = new vector >;
+ std::vector > * es2 = new std::vector >;
atStartOfLine = true;
size_t curDropped = 0;
size_t n = es.size();
- for (vector >::iterator i = es.begin(); i != es.end(); ++i, --n) {
- ExprIndStr * e = dynamic_cast(i->second);
- if (!e) {
- atStartOfLine = false;
- curDropped = 0;
- es2->push_back(*i);
- continue;
- }
-
- string s2;
- for (size_t j = 0; j < e->s.size(); ++j) {
+ auto i = es.begin();
+ const auto trimExpr = [&] (Expr * e) {
+ atStartOfLine = false;
+ curDropped = 0;
+ es2->emplace_back(i->first, e);
+ };
+ const auto trimString = [&] (const StringToken & t) {
+ std::string s2;
+ for (size_t j = 0; j < t.l; ++j) {
if (atStartOfLine) {
- if (e->s[j] == ' ') {
+ if (t.p[j] == ' ') {
if (curDropped++ >= minIndent)
- s2 += e->s[j];
+ s2 += t.p[j];
}
- else if (e->s[j] == '\n') {
+ else if (t.p[j] == '\n') {
curDropped = 0;
- s2 += e->s[j];
+ s2 += t.p[j];
} else {
atStartOfLine = false;
curDropped = 0;
- s2 += e->s[j];
+ s2 += t.p[j];
}
} else {
- s2 += e->s[j];
- if (e->s[j] == '\n') atStartOfLine = true;
+ s2 += t.p[j];
+ if (t.p[j] == '\n') atStartOfLine = true;
}
}
/* Remove the last line if it is empty and consists only of
spaces. */
if (n == 1) {
- string::size_type p = s2.find_last_of('\n');
- if (p != string::npos && s2.find_first_not_of(' ', p + 1) == string::npos)
- s2 = string(s2, 0, p + 1);
+ std::string::size_type p = s2.find_last_of('\n');
+ if (p != std::string::npos && s2.find_first_not_of(' ', p + 1) == std::string::npos)
+ s2 = std::string(s2, 0, p + 1);
}
- es2->emplace_back(i->first, new ExprString(symbols.create(s2)));
+ es2->emplace_back(i->first, new ExprString(s2));
+ };
+ for (; i != es.end(); ++i, --n) {
+ std::visit(overloaded { trimExpr, trimString }, i->second);
}
/* If this is a single string, then don't do a concatenation. */
@@ -269,15 +311,17 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
nix::Expr * e;
nix::ExprList * list;
nix::ExprAttrs * attrs;
- nix::Formals * formals;
+ nix::ParserFormals * formals;
nix::Formal * formal;
nix::NixInt n;
nix::NixFloat nf;
- const char * id; // !!! -> Symbol
- char * path;
- char * uri;
+ StringToken id; // !!! -> Symbol
+ StringToken path;
+ StringToken uri;
+ StringToken str;
std::vector * attrNames;
std::vector > * string_parts;
+ std::vector > > * ind_string_parts;
}
%type start expr expr_function expr_if expr_op
@@ -287,11 +331,12 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
%type formals
%type formal
%type attrs attrpath
-%type string_parts_interpolated ind_string_parts
+%type string_parts_interpolated
+%type ind_string_parts
%type path_start string_parts string_attr
%type attr
%token ID ATTRPATH
-%token STR IND_STR
+%token STR IND_STR
%token INT
%token FLOAT
%token PATH HPATH SPATH PATH_END
@@ -324,11 +369,17 @@ expr_function
: ID ':' expr_function
{ $$ = new ExprLambda(CUR_POS, data->symbols.create($1), 0, $3); }
| '{' formals '}' ':' expr_function
- { $$ = new ExprLambda(CUR_POS, data->symbols.create(""), $2, $5); }
+ { $$ = new ExprLambda(CUR_POS, data->symbols.create(""), toFormals(*data, $2), $5); }
| '{' formals '}' '@' ID ':' expr_function
- { $$ = new ExprLambda(CUR_POS, data->symbols.create($5), $2, $7); }
+ {
+ Symbol arg = data->symbols.create($5);
+ $$ = new ExprLambda(CUR_POS, arg, toFormals(*data, $2, CUR_POS, arg), $7);
+ }
| ID '@' '{' formals '}' ':' expr_function
- { $$ = new ExprLambda(CUR_POS, data->symbols.create($1), $4, $7); }
+ {
+ Symbol arg = data->symbols.create($1);
+ $$ = new ExprLambda(CUR_POS, arg, toFormals(*data, $4, CUR_POS, arg), $7);
+ }
| ASSERT expr ';' expr_function
{ $$ = new ExprAssert(CUR_POS, $2, $4); }
| WITH expr ';' expr_function
@@ -364,7 +415,7 @@ expr_op
| expr_op UPDATE expr_op { $$ = new ExprOpUpdate(CUR_POS, $1, $3); }
| expr_op '?' attrpath { $$ = new ExprOpHasAttr($1, *$3); }
| expr_op '+' expr_op
- { $$ = new ExprConcatStrings(CUR_POS, false, new vector >({{makeCurPos(@1, data), $1}, {makeCurPos(@3, data), $3}})); }
+ { $$ = new ExprConcatStrings(CUR_POS, false, new std::vector >({{makeCurPos(@1, data), $1}, {makeCurPos(@3, data), $3}})); }
| expr_op '-' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__sub")), {$1, $3}); }
| expr_op '*' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__mul")), {$1, $3}); }
| expr_op '/' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__div")), {$1, $3}); }
@@ -397,7 +448,8 @@ expr_select
expr_simple
: ID {
- if (strcmp($1, "__curPos") == 0)
+ std::string_view s = "__curPos";
+ if ($1.l == s.size() && strncmp($1.p, s.data(), s.size()) == 0)
$$ = new ExprPos(CUR_POS);
else
$$ = new ExprVar(CUR_POS, data->symbols.create($1));
@@ -414,11 +466,11 @@ expr_simple
$$ = new ExprConcatStrings(CUR_POS, false, $2);
}
| SPATH {
- string path($1 + 1, strlen($1) - 2);
+ std::string path($1.p + 1, $1.l - 2);
$$ = new ExprCall(CUR_POS,
new ExprVar(data->symbols.create("__findFile")),
{new ExprVar(data->symbols.create("__nixPath")),
- new ExprString(data->symbols.create(path))});
+ new ExprString(path)});
}
| URI {
static bool noURLLiterals = settings.isExperimentalFeatureEnabled(Xp::NoUrlLiterals);
@@ -427,7 +479,7 @@ expr_simple
.msg = hintfmt("URL literals are disabled"),
.errPos = CUR_POS
});
- $$ = new ExprString(data->symbols.create($1));
+ $$ = new ExprString(std::string($1));
}
| '(' expr ')' { $$ = $2; }
/* Let expressions `let {..., body = ...}' are just desugared
@@ -442,32 +494,33 @@ expr_simple
;
string_parts
- : STR
+ : STR { $$ = new ExprString(std::string($1)); }
| string_parts_interpolated { $$ = new ExprConcatStrings(CUR_POS, true, $1); }
- | { $$ = new ExprString(data->symbols.create("")); }
+ | { $$ = new ExprString(""); }
;
string_parts_interpolated
- : string_parts_interpolated STR { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $2); }
+ : string_parts_interpolated STR
+ { $$ = $1; $1->emplace_back(makeCurPos(@2, data), new ExprString(std::string($2))); }
| string_parts_interpolated DOLLAR_CURLY expr '}' { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $3); }
- | DOLLAR_CURLY expr '}' { $$ = new vector >; $$->emplace_back(makeCurPos(@1, data), $2); }
+ | DOLLAR_CURLY expr '}' { $$ = new std::vector >; $$->emplace_back(makeCurPos(@1, data), $2); }
| STR DOLLAR_CURLY expr '}' {
- $$ = new vector >;
- $$->emplace_back(makeCurPos(@1, data), $1);
+ $$ = new std::vector >;
+ $$->emplace_back(makeCurPos(@1, data), new ExprString(std::string($1)));
$$->emplace_back(makeCurPos(@2, data), $3);
}
;
path_start
: PATH {
- Path path(absPath($1, data->basePath));
+ Path path(absPath({$1.p, $1.l}, data->basePath));
/* add back in the trailing '/' to the first segment */
- if ($1[strlen($1)-1] == '/' && strlen($1) > 1)
+ if ($1.p[$1.l-1] == '/' && $1.l > 1)
path += "/";
$$ = new ExprPath(path);
}
| HPATH {
- Path path(getHome() + string($1 + 1));
+ Path path(getHome() + std::string($1.p + 1, $1.l - 1));
$$ = new ExprPath(path);
}
;
@@ -475,7 +528,7 @@ path_start
ind_string_parts
: ind_string_parts IND_STR { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $2); }
| ind_string_parts DOLLAR_CURLY expr '}' { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $3); }
- | { $$ = new vector >; }
+ | { $$ = new std::vector > >; }
;
binds
@@ -507,7 +560,7 @@ attrs
{ $$ = $1;
ExprString * str = dynamic_cast($2);
if (str) {
- $$->push_back(AttrName(str->s));
+ $$->push_back(AttrName(data->symbols.create(str->s)));
delete str;
} else
throw ParseError({
@@ -524,17 +577,17 @@ attrpath
{ $$ = $1;
ExprString * str = dynamic_cast($3);
if (str) {
- $$->push_back(AttrName(str->s));
+ $$->push_back(AttrName(data->symbols.create(str->s)));
delete str;
} else
$$->push_back(AttrName($3));
}
- | attr { $$ = new vector; $$->push_back(AttrName(data->symbols.create($1))); }
+ | attr { $$ = new std::vector; $$->push_back(AttrName(data->symbols.create($1))); }
| string_attr
- { $$ = new vector;
+ { $$ = new std::vector;
ExprString *str = dynamic_cast($1);
if (str) {
- $$->push_back(AttrName(str->s));
+ $$->push_back(AttrName(data->symbols.create(str->s)));
delete str;
} else
$$->push_back(AttrName($1));
@@ -543,7 +596,7 @@ attrpath
attr
: ID { $$ = $1; }
- | OR_KW { $$ = "or"; }
+ | OR_KW { $$ = {"or", 2}; }
;
string_attr
@@ -558,13 +611,13 @@ expr_list
formals
: formal ',' formals
- { $$ = $3; addFormal(CUR_POS, $$, *$1); }
+ { $$ = $3; $$->formals.push_back(*$1); }
| formal
- { $$ = new Formals; addFormal(CUR_POS, $$, *$1); $$->ellipsis = false; }
+ { $$ = new ParserFormals; $$->formals.push_back(*$1); $$->ellipsis = false; }
|
- { $$ = new Formals; $$->ellipsis = false; }
+ { $$ = new ParserFormals; $$->ellipsis = false; }
| ELLIPSIS
- { $$ = new Formals; $$->ellipsis = true; }
+ { $$ = new ParserFormals; $$->ellipsis = true; }
;
formal
@@ -589,8 +642,8 @@ formal
namespace nix {
-Expr * EvalState::parse(const char * text, FileOrigin origin,
- const Path & path, const Path & basePath, StaticEnv & staticEnv)
+Expr * EvalState::parse(char * text, size_t length, FileOrigin origin,
+ const PathView path, const PathView basePath, StaticEnv & staticEnv)
{
yyscan_t scanner;
ParseData data(*this);
@@ -609,7 +662,7 @@ Expr * EvalState::parse(const char * text, FileOrigin origin,
data.basePath = basePath;
yylex_init(&scanner);
- yy_scan_string(text, scanner);
+ yy_scan_buffer(text, length, scanner);
int res = yyparse(scanner, &data);
yylex_destroy(scanner);
@@ -655,63 +708,70 @@ Expr * EvalState::parseExprFromFile(const Path & path)
Expr * EvalState::parseExprFromFile(const Path & path, StaticEnv & staticEnv)
{
- return parse(readFile(path).c_str(), foFile, path, dirOf(path), staticEnv);
+ auto buffer = readFile(path);
+ // readFile should have left some extra space for terminators
+ buffer.append("\0\0", 2);
+ return parse(buffer.data(), buffer.size(), foFile, path, dirOf(path), staticEnv);
}
-Expr * EvalState::parseExprFromString(std::string_view s, const Path & basePath, StaticEnv & staticEnv)
+Expr * EvalState::parseExprFromString(std::string s, const Path & basePath, StaticEnv & staticEnv)
{
- return parse(s.data(), foString, "", basePath, staticEnv);
+ s.append("\0\0", 2);
+ return parse(s.data(), s.size(), foString, "", basePath, staticEnv);
}
-Expr * EvalState::parseExprFromString(std::string_view s, const Path & basePath)
+Expr * EvalState::parseExprFromString(std::string s, const Path & basePath)
{
- return parseExprFromString(s, basePath, staticBaseEnv);
+ return parseExprFromString(std::move(s), basePath, staticBaseEnv);
}
Expr * EvalState::parseStdin()
{
//Activity act(*logger, lvlTalkative, format("parsing standard input"));
- return parse(drainFD(0).data(), foStdin, "", absPath("."), staticBaseEnv);
+ auto buffer = drainFD(0);
+ // drainFD should have left some extra space for terminators
+ buffer.append("\0\0", 2);
+ return parse(buffer.data(), buffer.size(), foStdin, "", absPath("."), staticBaseEnv);
}
-void EvalState::addToSearchPath(const string & s)
+void EvalState::addToSearchPath(const std::string & s)
{
size_t pos = s.find('=');
- string prefix;
+ std::string prefix;
Path path;
- if (pos == string::npos) {
+ if (pos == std::string::npos) {
path = s;
} else {
- prefix = string(s, 0, pos);
- path = string(s, pos + 1);
+ prefix = std::string(s, 0, pos);
+ path = std::string(s, pos + 1);
}
searchPath.emplace_back(prefix, path);
}
-Path EvalState::findFile(const string & path)
+Path EvalState::findFile(const std::string_view path)
{
return findFile(searchPath, path);
}
-Path EvalState::findFile(SearchPath & searchPath, const string & path, const Pos & pos)
+Path EvalState::findFile(SearchPath & searchPath, const std::string_view path, const Pos & pos)
{
for (auto & i : searchPath) {
std::string suffix;
if (i.first.empty())
- suffix = "/" + path;
+ suffix = concatStrings("/", path);
else {
auto s = i.first.size();
if (path.compare(0, s, i.first) != 0 ||
(path.size() > s && path[s] != '/'))
continue;
- suffix = path.size() == s ? "" : "/" + string(path, s);
+ suffix = path.size() == s ? "" : concatStrings("/", path.substr(s));
}
auto r = resolveSearchPathElem(i);
if (!r.first) continue;
@@ -720,7 +780,7 @@ Path EvalState::findFile(SearchPath & searchPath, const string & path, const Pos
}
if (hasPrefix(path, "nix/"))
- return corepkgsPrefix + path.substr(4);
+ return concatStrings(corepkgsPrefix, path.substr(4));
throw ThrownError({
.msg = hintfmt(evalSettings.pureEval
diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc
index d7382af6b..aa22c3b61 100644
--- a/src/libexpr/primops.cc
+++ b/src/libexpr/primops.cc
@@ -12,6 +12,8 @@
#include "value-to-xml.hh"
#include "primops.hh"
+#include
+
#include
#include
#include
@@ -90,8 +92,6 @@ StringMap EvalState::realiseContext(const PathSet & context)
}
struct RealisePathFlags {
- // Whether to check whether the path is a valid absolute path
- bool requireAbsolutePath = true;
// Whether to check that the path is allowed in pure eval mode
bool checkForPureEval = true;
};
@@ -100,17 +100,28 @@ static Path realisePath(EvalState & state, const Pos & pos, Value & v, const Rea
{
PathSet context;
- Path path = flags.requireAbsolutePath
- ? state.coerceToPath(pos, v, context)
- : state.coerceToString(pos, v, context, false, false);
+ auto path = [&]()
+ {
+ try {
+ return state.coerceToPath(pos, v, context);
+ } catch (Error & e) {
+ e.addTrace(pos, "while realising the context of a path");
+ throw;
+ }
+ }();
- StringMap rewrites = state.realiseContext(context);
+ try {
+ StringMap rewrites = state.realiseContext(context);
- auto realPath = state.toRealPath(rewriteStrings(path, rewrites), context);
+ auto realPath = state.toRealPath(rewriteStrings(path, rewrites), context);
- return flags.checkForPureEval
- ? state.checkSourcePath(realPath)
- : realPath;
+ return flags.checkForPureEval
+ ? state.checkSourcePath(realPath)
+ : realPath;
+ } catch (Error & e) {
+ e.addTrace(pos, "while realising the context of path '%s'", path);
+ throw;
+ }
}
/* Add and attribute to the given attribute map from the output name to
@@ -130,7 +141,7 @@ static void mkOutputString(
BindingsBuilder & attrs,
const StorePath & drvPath,
const BasicDerivation & drv,
- const std::pair & o)
+ const std::pair & o)
{
auto optOutputPath = o.second.path(*state.store, drv.name, o.first);
attrs.alloc(o.first).mkString(
@@ -148,18 +159,7 @@ static void mkOutputString(
argument. */
static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vScope, Value & v)
{
- Path path;
- try {
- path = realisePath(state, pos, vPath);
- } catch (InvalidPathError & e) {
- throw EvalError({
- .msg = hintfmt("cannot import '%1%', since path '%2%' is not valid", path, e.path),
- .errPos = pos
- });
- } catch (Error & e) {
- e.addTrace(pos, "while importing '%s'", path);
- throw;
- }
+ auto path = realisePath(state, pos, vPath);
// FIXME
auto isValidDerivationInStore = [&]() -> std::optional {
@@ -210,7 +210,7 @@ static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vS
if (!vScope)
state.evalFile(path, v);
else {
- state.forceAttrs(*vScope);
+ state.forceAttrs(*vScope, pos);
Env * env = &state.allocEnv(vScope->attrs->size());
env->up = &state.baseEnv;
@@ -312,20 +312,9 @@ extern "C" typedef void (*ValueInitializer)(EvalState & state, Value & v);
/* Load a ValueInitializer from a DSO and return whatever it initializes */
void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
- Path path;
- try {
- path = realisePath(state, pos, *args[0]);
- } catch (InvalidPathError & e) {
- throw EvalError({
- .msg = hintfmt("cannot import '%1%', since path '%2%' is not valid", path, e.path),
- .errPos = pos
- });
- } catch (Error & e) {
- e.addTrace(pos, "while importing '%s'", path);
- throw;
- }
+ auto path = realisePath(state, pos, *args[0]);
- string sym = state.forceStringNoCtx(*args[1], pos);
+ std::string sym(state.forceStringNoCtx(*args[1], pos));
void *handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL);
if (!handle)
@@ -361,10 +350,11 @@ void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v)
});
}
PathSet context;
- auto program = state.coerceToString(pos, *elems[0], context, false, false);
+ auto program = state.coerceToString(pos, *elems[0], context, false, false).toOwned();
Strings commandArgs;
- for (unsigned int i = 1; i < args[0]->listSize(); ++i)
- commandArgs.emplace_back(state.coerceToString(pos, *elems[i], context, false, false));
+ for (unsigned int i = 1; i < args[0]->listSize(); ++i) {
+ commandArgs.push_back(state.coerceToString(pos, *elems[i], context, false, false).toOwned());
+ }
try {
auto _ = state.realiseContext(context); // FIXME: Handle CA derivations
} catch (InvalidPathError & e) {
@@ -378,7 +368,7 @@ void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v)
auto output = runProgram(program, true, commandArgs);
Expr * parsed;
try {
- parsed = state.parseExprFromString(output, pos.file);
+ parsed = state.parseExprFromString(std::move(output), pos.file);
} catch (Error & e) {
e.addTrace(pos, "While parsing the output from '%1%'", program);
throw;
@@ -396,7 +386,7 @@ void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v)
static void prim_typeOf(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
state.forceValue(*args[0], pos);
- string t;
+ std::string t;
switch (args[0]->type()) {
case nInt: t = "int"; break;
case nBool: t = "bool"; break;
@@ -584,24 +574,24 @@ struct CompareValues
#if HAVE_BOEHMGC
-typedef list > ValueList;
+typedef std::list > ValueList;
#else
-typedef list ValueList;
+typedef std::list ValueList;
#endif
static Bindings::iterator getAttr(
EvalState & state,
- string funcName,
- string attrName,
+ std::string_view funcName,
+ Symbol attrSym,
Bindings * attrSet,
const Pos & pos)
{
- Bindings::iterator value = attrSet->find(state.symbols.create(attrName));
+ Bindings::iterator value = attrSet->find(attrSym);
if (value == attrSet->end()) {
hintformat errorMsg = hintfmt(
"attribute '%s' missing for call to '%s'",
- attrName,
+ attrSym,
funcName
);
@@ -635,7 +625,7 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
Bindings::iterator startSet = getAttr(
state,
"genericClosure",
- "startSet",
+ state.sStartSet,
args[0]->attrs,
pos
);
@@ -650,7 +640,7 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
Bindings::iterator op = getAttr(
state,
"genericClosure",
- "operator",
+ state.sOperator,
args[0]->attrs,
pos
);
@@ -664,7 +654,7 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
// `doneKeys' doesn't need to be a GC root, because its values are
// reachable from res.
auto cmp = CompareValues(state);
- set doneKeys(cmp);
+ std::set doneKeys(cmp);
while (!workSet.empty()) {
Value * e = *(workSet.begin());
workSet.pop_front();
@@ -672,7 +662,7 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
state.forceAttrs(*e, pos);
Bindings::iterator key =
- e->attrs->find(state.symbols.create("key"));
+ e->attrs->find(state.sKey);
if (key == e->attrs->end())
throw EvalError({
.msg = hintfmt("attribute 'key' required"),
@@ -717,7 +707,7 @@ static RegisterPrimOp primop_abort({
.fun = [](EvalState & state, const Pos & pos, Value * * args, Value & v)
{
PathSet context;
- string s = state.coerceToString(pos, *args[0], context);
+ auto s = state.coerceToString(pos, *args[0], context).toOwned();
throw Abort("evaluation aborted with the following error message: '%1%'", s);
}
});
@@ -735,7 +725,7 @@ static RegisterPrimOp primop_throw({
.fun = [](EvalState & state, const Pos & pos, Value * * args, Value & v)
{
PathSet context;
- string s = state.coerceToString(pos, *args[0], context);
+ auto s = state.coerceToString(pos, *args[0], context).toOwned();
throw ThrownError(s);
}
});
@@ -747,7 +737,7 @@ static void prim_addErrorContext(EvalState & state, const Pos & pos, Value * * a
v = *args[1];
} catch (Error & e) {
PathSet context;
- e.addTrace(std::nullopt, state.coerceToString(pos, *args[0], context));
+ e.addTrace(std::nullopt, state.coerceToString(pos, *args[0], context).toOwned());
throw;
}
}
@@ -836,7 +826,7 @@ static RegisterPrimOp primop_tryEval({
/* Return an environment variable. Use with care. */
static void prim_getEnv(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
- string name = state.forceStringNoCtx(*args[0], pos);
+ std::string name(state.forceStringNoCtx(*args[0], pos));
v.mkString(evalSettings.restrictEval || evalSettings.pureEval ? "" : getEnv(name).value_or(""));
}
@@ -945,7 +935,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
pos
);
- string drvName;
+ std::string drvName;
Pos & posDrvName(*attr->pos);
try {
drvName = state.forceStringNoCtx(*attr->value, pos);
@@ -983,10 +973,10 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
for (auto & i : args[0]->attrs->lexicographicOrder()) {
if (i->name == state.sIgnoreNulls) continue;
- const string & key = i->name;
+ const std::string & key = i->name;
vomit("processing attribute '%1%'", key);
- auto handleHashMode = [&](const std::string & s) {
+ auto handleHashMode = [&](const std::string_view s) {
if (s == "recursive") ingestionMethod = FileIngestionMethod::Recursive;
else if (s == "flat") ingestionMethod = FileIngestionMethod::Flat;
else
@@ -1041,7 +1031,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
else if (i->name == state.sArgs) {
state.forceList(*i->value, pos);
for (auto elem : i->value->listItems()) {
- string s = state.coerceToString(posDrvName, *elem, context, true);
+ auto s = state.coerceToString(posDrvName, *elem, context, true).toOwned();
drv.args.push_back(s);
}
}
@@ -1077,12 +1067,12 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
}
} else {
- auto s = state.coerceToString(*i->pos, *i->value, context, true);
+ auto s = state.coerceToString(*i->pos, *i->value, context, true).toOwned();
drv.env.emplace(key, s);
- if (i->name == state.sBuilder) drv.builder = s;
- else if (i->name == state.sSystem) drv.platform = s;
- else if (i->name == state.sOutputHash) outputHash = s;
- else if (i->name == state.sOutputHashAlgo) outputHashAlgo = s;
+ if (i->name == state.sBuilder) drv.builder = std::move(s);
+ else if (i->name == state.sSystem) drv.platform = std::move(s);
+ else if (i->name == state.sOutputHash) outputHash = std::move(s);
+ else if (i->name == state.sOutputHashAlgo) outputHashAlgo = std::move(s);
else if (i->name == state.sOutputHashMode) handleHashMode(s);
else if (i->name == state.sOutputs)
handleOutputs(tokenizeString(s));
@@ -1128,7 +1118,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
/* Handle derivation outputs of the form ‘!!’. */
else if (path.at(0) == '!') {
- std::pair ctx = decodeContext(path);
+ auto ctx = decodeContext(path);
drv.inputDrvs[state.store->parseStorePath(ctx.first)].insert(ctx.second);
}
@@ -1190,7 +1180,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
drv.outputs.insert_or_assign(i, DerivationOutput {
.output = DerivationOutputCAFloating {
.method = ingestionMethod,
- .hashType = std::move(ht),
+ .hashType = ht,
},
});
}
@@ -1280,7 +1270,7 @@ static RegisterPrimOp primop_derivationStrict(RegisterPrimOp::Info {
substituted by the corresponding output path at build time. For
example, 'placeholder "out"' returns the string
/1rz4g4znpzjwh1xymhjpm42vipw92pr73vdgl6xs1hycac8kf2n9. At build
- time, any occurence of this string in an derivation attribute will
+ time, any occurrence of this string in an derivation attribute will
be replaced with the concrete path in the Nix store of the output
‘out’. */
static void prim_placeholder(EvalState & state, const Pos & pos, Value * * args, Value & v)
@@ -1377,22 +1367,12 @@ static RegisterPrimOp primop_storePath({
static void prim_pathExists(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
- Path path;
- try {
- // We don’t check the path right now, because we don’t want to throw if
- // the path isn’t allowed, but just return false
- // (and we can’t just catch the exception here because we still want to
- // throw if something in the evaluation of `*args[0]` tries to access an
- // unauthorized path)
- path = realisePath(state, pos, *args[0], { .checkForPureEval = false });
- } catch (InvalidPathError & e) {
- throw EvalError({
- .msg = hintfmt(
- "cannot check the existence of '%1%', since path '%2%' is not valid",
- path, e.path),
- .errPos = pos
- });
- }
+ /* We don’t check the path right now, because we don’t want to
+ throw if the path isn’t allowed, but just return false (and we
+ can’t just catch the exception here because we still want to
+ throw if something in the evaluation of `*args[0]` tries to
+ access an unauthorized path). */
+ auto path = realisePath(state, pos, *args[0], { .checkForPureEval = false });
try {
v.mkBool(pathExists(state.checkSourcePath(path)));
@@ -1420,7 +1400,7 @@ static RegisterPrimOp primop_pathExists({
static void prim_baseNameOf(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
PathSet context;
- v.mkString(baseNameOf(state.coerceToString(pos, *args[0], context, false, false)), context);
+ v.mkString(baseNameOf(*state.coerceToString(pos, *args[0], context, false, false)), context);
}
static RegisterPrimOp primop_baseNameOf({
@@ -1440,7 +1420,8 @@ static RegisterPrimOp primop_baseNameOf({
static void prim_dirOf(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
PathSet context;
- Path dir = dirOf(state.coerceToString(pos, *args[0], context, false, false));
+ auto path = state.coerceToString(pos, *args[0], context, false, false);
+ auto dir = dirOf(*path);
if (args[0]->type() == nPath) v.mkPath(dir); else v.mkString(dir, context);
}
@@ -1458,19 +1439,19 @@ static RegisterPrimOp primop_dirOf({
/* Return the contents of a file as a string. */
static void prim_readFile(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
- Path path;
- try {
- path = realisePath(state, pos, *args[0]);
- } catch (InvalidPathError & e) {
- throw EvalError({
- .msg = hintfmt("cannot read '%1%', since path '%2%' is not valid", path, e.path),
- .errPos = pos
- });
- }
- string s = readFile(path);
- if (s.find((char) 0) != string::npos)
+ auto path = realisePath(state, pos, *args[0]);
+ auto s = readFile(path);
+ if (s.find((char) 0) != std::string::npos)
throw Error("the contents of the file '%1%' cannot be represented as a Nix string", path);
- v.mkString(s);
+ StorePathSet refs;
+ if (state.store->isInStore(path)) {
+ try {
+ refs = state.store->queryPathInfo(state.store->toStorePath(path).first)->references;
+ } catch (Error &) { // FIXME: should be InvalidPathError
+ }
+ }
+ auto context = state.store->printStorePathSet(refs);
+ v.mkString(s, context);
}
static RegisterPrimOp primop_readFile({
@@ -1493,23 +1474,25 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va
for (auto v2 : args[0]->listItems()) {
state.forceAttrs(*v2, pos);
- string prefix;
- Bindings::iterator i = v2->attrs->find(state.symbols.create("prefix"));
+ std::string prefix;
+ Bindings::iterator i = v2->attrs->find(state.sPrefix);
if (i != v2->attrs->end())
prefix = state.forceStringNoCtx(*i->value, pos);
i = getAttr(
state,
"findFile",
- "path",
+ state.sPath,
v2->attrs,
pos
);
- Path path;
+ PathSet context;
+ auto path = state.coerceToString(pos, *i->value, context, false, false).toOwned();
try {
- path = realisePath(state, pos, *i->value, { .requireAbsolutePath = false });
+ auto rewrites = state.realiseContext(context);
+ path = rewriteStrings(path, rewrites);
} catch (InvalidPathError & e) {
throw EvalError({
.msg = hintfmt("cannot find '%1%', since path '%2%' is not valid", path, e.path),
@@ -1517,10 +1500,11 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va
});
}
+
searchPath.emplace_back(prefix, path);
}
- string path = state.forceStringNoCtx(*args[1], pos);
+ auto path = state.forceStringNoCtx(*args[1], pos);
v.mkPath(state.checkSourcePath(state.findFile(searchPath, path, pos)));
}
@@ -1534,7 +1518,7 @@ static RegisterPrimOp primop_findFile(RegisterPrimOp::Info {
/* Return the cryptographic hash of a file in base-16. */
static void prim_hashFile(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
- string type = state.forceStringNoCtx(*args[0], pos);
+ auto type = state.forceStringNoCtx(*args[0], pos);
std::optional