Compare commits

..

1 Commits

Author SHA1 Message Date
Eelco Dolstra
a1cd7e5860 Mark official release 2022-01-25 00:13:54 +01:00
136 changed files with 28024 additions and 1780 deletions

View File

@@ -6,15 +6,13 @@ on:
jobs:
build:
tests:
needs: [check_cachix]
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
timeout-minutes: 60
outputs:
flake-outputs-json: ${{ steps.list-outputs.outputs.json }}
steps:
- uses: actions/checkout@v2.4.0
with:
@@ -27,32 +25,7 @@ jobs:
name: '${{ env.CACHIX_NAME }}'
signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}'
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
- run: nix --experimental-features 'nix-command flakes' build -L
- name: List all the tests to run
id: list-outputs
run: scripts/list-tests-flake-outptus-for-gha
test:
needs: [build, check_cachix]
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
outputName: ${{ fromJson(needs.build.outputs.flake-outputs-json) }}
runs-on: ${{ matrix.os }}
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
- 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 .#checks.$(nix eval --raw --impure --expr builtins.currentSystem).${{ matrix.outputName }} -L
- run: nix-build -A checks.$(nix-instantiate --eval -E '(builtins.currentSystem)')
check_cachix:
name: Cachix secret present for installer tests
@@ -67,7 +40,7 @@ jobs:
run: echo "::set-output name=secret::${{ env._CACHIX_SECRETS != '' }}"
installer:
needs: [test, check_cachix]
needs: [tests, check_cachix]
if: github.event_name == 'push' && needs.check_cachix.outputs.secret == 'true'
runs-on: ubuntu-latest
outputs:
@@ -103,7 +76,7 @@ jobs:
- run: nix-instantiate -E 'builtins.currentTime' --eval
docker_push_image:
needs: [check_cachix, build]
needs: [check_cachix, tests]
if: >-
github.event_name == 'push' &&
github.ref_name == 'master' &&
@@ -122,7 +95,7 @@ jobs:
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: nix-build -A checks.$(nix-instantiate --eval -E 'builtins.currentSystem' --json).dockerImage
- 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

4
.gitignore vendored
View File

@@ -120,7 +120,3 @@ GTAGS
compile_commands.json
nix-rust/target
result
.vscode/

View File

@@ -1 +1 @@
2.7.0
2.6.0

View File

@@ -10,6 +10,7 @@ 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 \

View File

@@ -16,7 +16,6 @@ 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@

View File

@@ -262,17 +262,13 @@ 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.9.0], [CXXFLAGS="$LOWDOWN_CFLAGS $CXXFLAGS"])
PKG_CHECK_MODULES([LOWDOWN], [lowdown >= 0.8.0], [CXXFLAGS="$LOWDOWN_CFLAGS $CXXFLAGS"])
# Setuid installations.
AC_CHECK_FUNCS([setresuid setreuid lchown])

View File

@@ -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

View File

@@ -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*\n")
else " **Default:** *machine-specific*")
+ (if option.aliases != []
then " **Deprecated alias:** " + (concatStringsSep ", " (map (s: "`${s}`") option.aliases)) + "\n\n"
else "")

View File

@@ -71,7 +71,6 @@
- [Hacking](contributing/hacking.md)
- [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)

View File

@@ -321,8 +321,8 @@ symlink.
This query has one option:
- `--include-outputs`
Also include the existing output paths of store derivations,
and their closures.
Also include the output path 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

View File

@@ -1,15 +1,2 @@
# Release X.Y (202?-??-??)
* `nix bundle` breaking API change now supports bundlers of the form
`bundler.<system>.<name>= 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 <template>`.

399
flake.nix
View File

@@ -15,7 +15,7 @@
then ""
else "pre${builtins.substring 0 8 (self.lastModifiedDate or self.lastModified or "19700101")}_${self.shortRev or "dirty"}";
officialRelease = false;
officialRelease = true;
linux64BitSystems = [ "x86_64-linux" "aarch64-linux" ];
linuxSystems = linux64BitSystems ++ [ "i686-linux" ];
@@ -133,7 +133,6 @@
./boehmgc-coroutine-sp-fallback.diff
];
}))
nlohmann_json
];
perlDeps =
@@ -142,8 +141,8 @@
];
};
installScriptFor = systems:
with nixpkgsFor.x86_64-linux;
installScriptFor = systems:
with nixpkgsFor.x86_64-linux;
runCommand "installer-script"
{ buildInputs = [ nix ];
}
@@ -207,204 +206,203 @@
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)
'';
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;
let currentStdenv = getStdenv final; in
{
nixStable = prev.nix;
# Forward from the previous stage as we dont want it to pick the lowdown override
nixUnstable = prev.nixUnstable;
# Forward from the previous stage as we dont want it to pick the lowdown override
nixUnstable = prev.nixUnstable;
nix = with final; with commonDeps pkgs; currentStdenv.mkDerivation {
name = "nix-${version}";
inherit version;
nix = with final; with commonDeps pkgs; currentStdenv.mkDerivation {
name = "nix-${version}";
inherit version;
src = self;
src = self;
VERSION_SUFFIX = versionSuffix;
VERSION_SUFFIX = versionSuffix;
outputs = [ "out" "dev" "doc" ];
outputs = [ "out" "dev" "doc" ];
nativeBuildInputs = nativeBuildDeps;
buildInputs = buildDeps ++ awsDeps;
nativeBuildInputs = nativeBuildDeps;
buildInputs = buildDeps ++ awsDeps;
propagatedBuildInputs = propagatedDeps;
propagatedBuildInputs = propagatedDeps;
disallowedReferences = [ boost ];
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;
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
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 ''
install_name_tool \
-change ${boost}/lib/libboost_context.dylib \
$out/lib/libboost_context.dylib \
$out/lib/libnixutil.dylib
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
''}
'';
doInstallCheck = true;
installCheckFlags = "sysconfdir=$(out)/etc";
configureFlags = configureFlags ++
[ "--sysconfdir=/etc" ];
separateDebugInfo = true;
enableParallelBuilding = true;
strictDeps = true;
makeFlags = "profiledir=$(out)/etc/profile.d PRECOMPILE_HEADERS=1";
passthru.perl-bindings = with final; currentStdenv.mkDerivation {
name = "nix-perl-${version}";
doCheck = true;
src = self;
installFlags = "sysconfdir=$(out)/etc";
nativeBuildInputs =
[ buildPackages.autoconf-archive
buildPackages.autoreconfHook
buildPackages.pkg-config
];
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
''}
'';
buildInputs =
[ nix
curl
bzip2
xz
pkgs.perl
boost
]
++ lib.optional (currentStdenv.isLinux || currentStdenv.isDarwin) libsodium
++ lib.optional currentStdenv.isDarwin darwin.apple_sdk.frameworks.Security;
doInstallCheck = true;
installCheckFlags = "sysconfdir=$(out)/etc";
configureFlags = ''
--with-dbi=${perlPackages.DBI}/${pkgs.perl.libPrefix}
--with-dbd-sqlite=${perlPackages.DBDSQLite}/${pkgs.perl.libPrefix}
'';
separateDebugInfo = true;
enableParallelBuilding = true;
strictDeps = true;
postUnpack = "sourceRoot=$sourceRoot/perl";
};
passthru.perl-bindings = with final; currentStdenv.mkDerivation {
name = "nix-perl-${version}";
};
src = self;
lowdown-nix = with final; currentStdenv.mkDerivation rec {
name = "lowdown-0.9.0";
nativeBuildInputs =
[ buildPackages.autoconf-archive
buildPackages.autoreconfHook
buildPackages.pkg-config
];
src = lowdown-src;
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" "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
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
'';
};
};
in {
# A Nixpkgs overlay that overrides the 'nix' and
@@ -447,7 +445,19 @@
installerScriptForGHA = installScriptFor [ "x86_64-linux" "x86_64-darwin" "armv6l-linux" "armv7l-linux"];
# docker image with Nix inside
dockerImage = nixpkgs.lib.genAttrs linux64BitSystems (system: self.packages.${system}.dockerImage);
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
'');
# Line coverage analysis.
coverage =
@@ -501,12 +511,6 @@
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:
@@ -533,28 +537,26 @@
nixpkgs = nixpkgs-regression;
};
installTestsAgainstSelf = forAllSystems (system:
installTests = forAllSystems (system:
let pkgs = nixpkgsFor.${system}; in
testNixVersions pkgs pkgs.nix pkgs.pkgs.nix
);
installTestsAgainstCurrentUnstable = forAllSystems (system:
let pkgs = nixpkgsFor.${system}; in
# FIXME: temporarily disable this on macOS because of #3605.
if system == "x86_64-linux"
then testNixVersions pkgs pkgs.nix pkgs.nixUnstable
else pkgs.writeText "dummy" "dummy"
);
# Disabled because the latest stable version doesn't handle
# `NIX_DAEMON_SOCKET_PATH` which is required for the tests to work
# againstLatestStable = testNixVersions pkgs pkgs.nix pkgs.nixStable;
pkgs.runCommand "install-tests" {
againstSelf = testNixVersions pkgs pkgs.nix pkgs.pkgs.nix;
againstCurrentUnstable =
# FIXME: temporarily disable this on macOS because of #3605.
if system == "x86_64-linux"
then testNixVersions pkgs pkgs.nix pkgs.nixUnstable
else null;
# Disabled because the latest stable version doesn't handle
# `NIX_DAEMON_SOCKET_PATH` which is required for the tests to work
# againstLatestStable = testNixVersions pkgs pkgs.nix pkgs.nixStable;
} "touch $out");
};
checks = forAllSystems (system: {
binaryTarball = self.hydraJobs.binaryTarball.${system};
perlBindings = self.hydraJobs.perlBindings.${system};
installTestsAgainstCurrentUnstable = self.hydraJobs.installTestsAgainstCurrentUnstable.${system};
installTestsAgainstSelf = self.hydraJobs.installTestsAgainstSelf.${system};
installTests = self.hydraJobs.installTests.${system};
} // (nixpkgs.lib.optionalAttrs (builtins.elem system linux64BitSystems)) {
dockerImage = self.hydraJobs.dockerImage.${system};
});
@@ -602,20 +604,6 @@
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
@@ -656,10 +644,11 @@
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);

View File

@@ -55,11 +55,6 @@ 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.";
@@ -85,7 +80,6 @@ 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);
@@ -93,27 +87,19 @@ sub downloadFile {
if (!-e $tmpFile) {
print STDERR "downloading $srcFile to $tmpFile...\n";
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
system("NIX_REMOTE=https://cache.nixos.org/ nix store cat '$srcFile' > '$tmpFile'") == 0
or die "unable to fetch $srcFile\n";
rename("$tmpFile.tmp", $tmpFile) or die;
}
my $sha256_expected = $buildInfo->{buildproducts}->{$productNr}->{sha256hash};
my $sha256_expected = $buildInfo->{buildproducts}->{$productNr}->{sha256hash} or die;
my $sha256_actual = `nix hash file --base16 --type sha256 '$tmpFile'`;
chomp $sha256_actual;
if (defined($sha256_expected) && $sha256_expected ne $sha256_actual) {
if ($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_actual);
write_file("$tmpFile.sha256", $sha256_expected);
if (! -e "$tmpFile.asc") {
system("gpg2 --detach-sign --armor $tmpFile") == 0 or die "unable to sign $tmpFile\n";
@@ -131,60 +117,6 @@ 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;

View File

@@ -15,7 +15,7 @@ function _complete_nix {
else
COMPREPLY+=("$completion")
fi
done < <(NIX_GET_COMPLETIONS=$cword "${words[@]/#\~/$HOME}" 2>/dev/null)
done < <(NIX_GET_COMPLETIONS=$cword "${words[@]/#\~/$HOME}")
__ltrim_colon_completions "$cur"
}

View File

@@ -4,7 +4,7 @@ function _nix() {
local ifs_bk="$IFS"
local input=("${(Q)words[@]}")
IFS=$'\n'
local res=($(NIX_GET_COMPLETIONS=$((CURRENT - 1)) "$input[@]" 2>/dev/null))
local res=($(NIX_GET_COMPLETIONS=$((CURRENT - 1)) "$input[@]"))
IFS="$ifs_bk"
local tpe="${${res[1]}%%> *}"
local -a suggestions

View File

@@ -7,8 +7,6 @@ green=""
yellow=""
normal=""
TESTS_TIMER_LOG=${TESTS_TIMER_LOG:-/dev/null}
post_run_msg="ran test $1..."
if [ -t 1 ]; then
red=""
@@ -17,21 +15,14 @@ if [ -t 1 ]; then
normal=""
fi
(cd tests && env ${TESTS_ENVIRONMENT} init.sh 2>/dev/null > /dev/null)
start_time=$(date -u +%s)
echo "$(date -u +%s) $1 start" >> "$TESTS_TIMER_LOG"
log="$(cd $(dirname $1) && env ${TESTS_ENVIRONMENT} $(basename $1) 2>&1)"
status=$?
echo "$(date -u +%s) $1 stop" >> "$TESTS_TIMER_LOG"
stop_time=$(date -u +%s)
elapsed_time=$(($stop_time-$start_time))
if [ $status -eq 0 ]; then
echo "$post_run_msg [${green}PASS$normal] in ${elapsed_time}s"
echo "$post_run_msg [${green}PASS$normal]"
elif [ $status -eq 99 ]; then
echo "$post_run_msg [${yellow}SKIP$normal] after ${elapsed_time}s"
echo "$post_run_msg [${yellow}SKIP$normal]"
else
echo "$post_run_msg [${red}FAIL$normal] in ${elapsed_time}s"
echo "$post_run_msg [${red}FAIL$normal]"
echo "$log" | sed 's/^/ /'
exit "$status"
fi

399
nix-rust/Cargo.lock generated Normal file
View File

@@ -0,0 +1,399 @@
# 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"

23
nix-rust/Cargo.toml Normal file
View File

@@ -0,0 +1,23 @@
[package]
name = "nix-rust"
version = "0.1.0"
authors = ["Eelco Dolstra <edolstra@gmail.com>"]
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"

48
nix-rust/local.mk Normal file
View File

@@ -0,0 +1,48 @@
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

77
nix-rust/src/c.rs Normal file
View File

@@ -0,0 +1,77 @@
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, error::CppException> {
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, error::CppException> {
StorePath::from_parts(*hash, name).map_err(|err| err.into())
}
#[no_mangle]
pub extern "C" fn ffi_StorePath_fromBaseName(
base_name: &str,
) -> Result<StorePath, error::CppException> {
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<u8> {
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()
}

118
nix-rust/src/error.rs Normal file
View File

@@ -0,0 +1,118 @@
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<std::io::Error> for Error {
fn from(err: std::io::Error) -> Self {
Error::IOError(err)
}
}
#[cfg(unused)]
impl From<hyper::error::Error> 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, "<C++ exception>"), // FIXME
Error::Misc(s) => write!(f, "{}", s),
Error::BadTarFileMemberName(s) => {
write!(f, "tar archive contains illegal file name '{}'", s)
}
}
}
}
#[cfg(not(test))]
impl From<Error> 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);
}

10
nix-rust/src/lib.rs Normal file
View File

@@ -0,0 +1,10 @@
#[allow(improper_ctypes_definitions)]
#[cfg(not(test))]
mod c;
mod error;
#[cfg(unused)]
mod nar;
mod store;
mod util;
pub use error::Error;

126
nix-rust/src/nar.rs Normal file
View File

@@ -0,0 +1,126 @@
use crate::Error;
use byteorder::{LittleEndian, ReadBytesExt};
use std::convert::TryFrom;
use std::io::Read;
pub fn parse<R: Read>(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<R: Read>(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::<u8>::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<R: Read>(input: &mut R) -> Result<Self, Error>;
}
impl Deserialize for String {
fn read<R: Read>(input: &mut R) -> Result<Self, Error> {
let buf = Deserialize::read(input)?;
Ok(String::from_utf8(buf).map_err(|_| Error::BadNarString)?)
}
}
impl Deserialize for Vec<u8> {
fn read<R: Read>(input: &mut R) -> Result<Self, Error> {
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<R: Read>(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<R: Read>(input: &mut R) -> Result<Self, Error> {
Ok(input.read_u64::<LittleEndian>()?)
}
}
impl Deserialize for usize {
fn read<R: Read>(input: &mut R) -> Result<Self, Error> {
let n: u64 = Deserialize::read(input)?;
Ok(usize::try_from(n).map_err(|_| Error::NarSizeFieldTooBig)?)
}
}

View File

@@ -0,0 +1,48 @@
use super::{PathInfo, Store, StorePath};
use crate::Error;
use hyper::client::Client;
pub struct BinaryCacheStore {
base_uri: String,
client: Client<hyper::client::HttpConnector, hyper::Body>,
}
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<Box<dyn std::future::Future<Output = Result<PathInfo, Error>> + 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::<hyper::Uri>().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)
})
}
}

17
nix-rust/src/store/mod.rs Normal file
View File

@@ -0,0 +1,17 @@
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;

224
nix-rust/src/store/path.rs Normal file
View File

@@ -0,0 +1,224 @@
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<Self, Error> {
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<Self, Error> {
Ok(StorePath {
hash: StorePathHash(hash),
name: StorePathName::new(name)?,
})
}
pub fn new_from_base_name(base_name: &str) -> Result<Self, Error> {
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<Self, Error> {
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<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct StorePathName(String);
impl StorePathName {
pub fn new(s: &str) -> Result<Self, Error> {
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);
}
}

View File

@@ -0,0 +1,70 @@
use crate::store::StorePath;
use crate::Error;
use std::collections::BTreeSet;
#[derive(Clone, Debug)]
pub struct PathInfo {
pub path: StorePath,
pub references: BTreeSet<StorePath>,
pub nar_size: u64,
pub deriver: Option<StorePath>,
// Additional binary cache info.
pub url: Option<String>,
pub compression: Option<String>,
pub file_size: Option<u64>,
}
impl PathInfo {
pub fn parse_nar_info(nar_info: &str, store_dir: &str) -> Result<Self, Error> {
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,
})
}
}

View File

@@ -0,0 +1,53 @@
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<Box<dyn std::future::Future<Output = Result<PathInfo, Error>> + Send>>;
}
impl dyn Store {
pub fn parse_store_path(&self, path: &Path) -> Result<StorePath, Error> {
StorePath::new(path, self.store_dir())
}
pub async fn compute_path_closure(
&self,
roots: BTreeSet<StorePath>,
) -> Result<BTreeMap<StorePath, PathInfo>, 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)
}
}

160
nix-rust/src/util/base32.rs Normal file
View File

@@ -0,0 +1,160 @@
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<Vec<u8>, 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<u8>) {
assert_eq!(s, decode(&encode(&s)).unwrap());
}
}
}

1
nix-rust/src/util/mod.rs Normal file
View File

@@ -0,0 +1 @@
pub mod base32;

View File

@@ -16,17 +16,12 @@ someBuildFailed=0
for buildId in $BUILDS_FOR_LATEST_EVAL; do
buildInfo=$(curl -sS -H 'Accept: application/json' "https://hydra.nixos.org/build/$buildId")
finished=$(echo "$buildInfo" | jq -r '.finished')
buildStatus=$(echo "$buildInfo" | \
jq -r '.buildstatus')
if [[ $finished = 0 ]]; then
continue
fi
buildStatus=$(echo "$buildInfo" | jq -r '.buildstatus')
if [[ $buildStatus != 0 ]]; then
if [[ "$buildStatus" -ne 0 ]]; then
someBuildFailed=1
echo "Job “$(echo "$buildInfo" | jq -r '.job')” failed on hydra: $buildInfo"
echo "Job “$(echo "$buildInfo" | jq -r '.job')” failed on hydra"
fi
done

View File

@@ -576,40 +576,21 @@ 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="$(PATH="$(getconf PATH 2>/dev/null)" command -vp chown)"
local get_chr_own="$(command -vp chown)"
if [[ -z "$get_chr_own" ]]; then
get_chr_own="$(command -v chown)"
fi
if [[ -z "$get_chr_own" ]]; then
reminder <<EOF
I wanted to take root ownership of existing Nix store files,
but I couldn't locate 'chown'. (You may need to fix your PATH.)
To manually change file ownership, you can run:
sudo chown -R 'root:$NIX_BUILD_GROUP_NAME' '$NIX_ROOT'
EOF
else
_sudo "to take root ownership of existing Nix store files" \
"$get_chr_own" -R "root:$NIX_BUILD_GROUP_NAME" "$NIX_ROOT" || true
fi
_sudo "to take root ownership of existing Nix store files" \
"$get_chr_own" -R "root:$NIX_BUILD_GROUP_NAME" "$NIX_ROOT" || true
fi
_sudo "to make the basic directory structure of Nix (part 1)" \
install -dv -m 0755 /nix /nix/var /nix/var/log /nix/var/log/nix /nix/var/log/nix/drvs /nix/var/nix{,/db,/gcroots,/profiles,/temproots,/userpool,/daemon-socket} /nix/var/nix/{gcroots,profiles}/per-user
install -dv -m 0755 /nix /nix/var /nix/var/log /nix/var/log/nix /nix/var/log/nix/drvs /nix/var/nix{,/db,/gcroots,/profiles,/temproots,/userpool} /nix/var/nix/{gcroots,profiles}/per-user
_sudo "to make the basic directory structure of Nix (part 2)" \
install -dv -g "$NIX_BUILD_GROUP_NAME" -m 1775 /nix/store

View File

@@ -1,11 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
set -x
nix build --inputs-from . nixpkgs#jq -o jq
TEST_NAMES=$(nix flake show . --json | ./jq-bin/bin/jq -c ".checks[\"$(nix eval --raw --impure --expr builtins.currentSystem)\"] | keys")
echo "::set-output name=json::$TEST_NAMES"

View File

@@ -24,9 +24,6 @@ if [ -n "$HOME" ] && [ -n "$USER" ]; then
export NIX_SSL_CERT_FILE="$NIX_LINK/etc/ca-bundle.crt"
fi
# Only use MANPATH if it is already set. In general `man` will just simply
# pick up `.nix-profile/share/man` because is it close to `.nix-profile/bin`
# which is in the $PATH. For more info, run `manpath -d`.
if [ -n "${MANPATH-}" ]; then
export MANPATH="$NIX_LINK/share/man:$MANPATH"
fi

View File

@@ -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 = ./.;
}).shellNix

View File

@@ -208,7 +208,7 @@ static int main_build_remote(int argc, char * * argv)
for (auto & m : machines)
error
% concatStringsSep<std::vector<string>>(", ", m.systemTypes)
% concatStringsSep<vector<string>>(", ", m.systemTypes)
% m.maxJobs
% concatStringsSep<StringSet>(", ", m.supportedFeatures)
% concatStringsSep<StringSet>(", ", m.mandatoryFeatures);

View File

@@ -97,7 +97,7 @@ MixFlakeOptions::MixFlakeOptions()
lockFlags.writeLockFile = false;
lockFlags.inputOverrides.insert_or_assign(
flake::parseInputPath(inputPath),
parseFlakeRef(flakeRef, absPath("."), true));
parseFlakeRef(flakeRef, absPath(".")));
}}
});
@@ -198,9 +198,8 @@ void SourceExprCommand::completeInstallable(std::string_view prefix)
prefix_ = "";
}
auto [v, pos] = findAlongAttrPath(*state, prefix_, *autoArgs, root);
Value &v1(*v);
state->forceValue(v1, pos);
Value &v1(*findAlongAttrPath(*state, prefix_, *autoArgs, root).first);
state->forceValue(v1);
Value v2;
state->autoCallFunction(*autoArgs, v1, v2);
@@ -447,7 +446,7 @@ struct InstallableAttrPath : InstallableValue
std::pair<Value *, Pos> toValue(EvalState & state) override
{
auto [vRes, pos] = findAlongAttrPath(state, attrPath, *cmd.getAutoArgs(state), **v);
state.forceValue(*vRes, pos);
state.forceValue(*vRes);
return {vRes, pos};
}
@@ -497,7 +496,7 @@ Value * InstallableFlake::getFlakeOutputs(EvalState & state, const flake::Locked
auto aOutputs = vFlake->attrs->get(state.symbols.create("outputs"));
assert(aOutputs);
state.forceValue(*aOutputs->value, [&]() { return aOutputs->value->determinePos(noPos); });
state.forceValue(*aOutputs->value);
return aOutputs->value;
}
@@ -522,7 +521,7 @@ ref<eval_cache::EvalCache> openEvalCache(
auto vFlake = state.allocValue();
flake::callFlake(state, *lockedFlake, *vFlake);
state.forceAttrs(*vFlake, noPos);
state.forceAttrs(*vFlake);
auto aOutputs = vFlake->attrs->get(state.symbols.create("outputs"));
assert(aOutputs);
@@ -545,14 +544,13 @@ InstallableFlake::InstallableFlake(
SourceExprCommand * cmd,
ref<EvalState> state,
FlakeRef && flakeRef,
std::string_view fragment,
Strings attrPaths,
Strings prefixes,
Strings && attrPaths,
Strings && prefixes,
const flake::LockFlags & lockFlags)
: InstallableValue(state),
flakeRef(flakeRef),
attrPaths(fragment == "" ? attrPaths : Strings{(std::string) fragment}),
prefixes(fragment == "" ? Strings{} : prefixes),
attrPaths(attrPaths),
prefixes(prefixes),
lockFlags(lockFlags)
{
if (cmd && cmd->getAutoArgs(*state)->size())
@@ -567,8 +565,6 @@ std::tuple<std::string, FlakeRef, InstallableValue::DerivationInfo> InstallableF
auto root = cache->getRoot();
for (auto & attrPath : getActualAttrPaths()) {
debug("trying flake output attribute '%s'", attrPath);
auto attr = root->findAlongAttrPath(
parseAttrPath(*state, attrPath),
true
@@ -612,7 +608,7 @@ std::pair<Value *, Pos> InstallableFlake::toValue(EvalState & state)
for (auto & attrPath : getActualAttrPaths()) {
try {
auto [v, pos] = findAlongAttrPath(state, attrPath, *emptyArgs, *vOutputs);
state.forceValue(*v, pos);
state.forceValue(*v);
return {v, pos};
} catch (AttrPathNotFound & e) {
}
@@ -711,8 +707,7 @@ std::vector<std::shared_ptr<Installable>> SourceExprCommand::parseInstallables(
this,
getEvalState(),
std::move(flakeRef),
fragment,
getDefaultFlakeAttrPaths(),
fragment == "" ? getDefaultFlakeAttrPaths() : Strings{fragment},
getDefaultFlakeAttrPathPrefixes(),
lockFlags));
continue;

View File

@@ -102,9 +102,8 @@ struct InstallableFlake : InstallableValue
SourceExprCommand * cmd,
ref<EvalState> state,
FlakeRef && flakeRef,
std::string_view fragment,
Strings attrPaths,
Strings prefixes,
Strings && attrPaths,
Strings && prefixes,
const flake::LockFlags & lockFlags);
std::string what() const override { return flakeRef.to_string() + "#" + *attrPaths.begin(); }

View File

@@ -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 += $(LOWDOWN_LIBS) -pthread
libcmd_LDFLAGS += -llowdown -pthread
libcmd_LIBS = libstore libutil libexpr libmain libfetchers

View File

@@ -58,7 +58,7 @@ std::pair<Value *, Pos> findAlongAttrPath(EvalState & state, const string & attr
Value * vNew = state.allocValue();
state.autoCallFunction(autoArgs, *v, *vNew);
v = vNew;
state.forceValue(*v, noPos);
state.forceValue(*v);
/* 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, string::npos));
lineno = std::stoi(std::string(pos, colon + 1));
} catch (std::invalid_argument & e) {
throw ParseError("cannot parse line number '%s'", pos);
}

View File

@@ -336,7 +336,7 @@ Value & AttrCursor::getValue()
if (!_value) {
if (parent) {
auto & vParent = parent->first->getValue();
root->state.forceAttrs(vParent, noPos);
root->state.forceAttrs(vParent);
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, noPos);
root->state.forceValue(v);
} catch (EvalError &) {
debug("setting '%s' to failed", getAttrPathStr());
if (root->db)

View File

@@ -15,6 +15,12 @@ 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({
@@ -25,13 +31,6 @@ LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const
void EvalState::forceValue(Value & v, const Pos & pos)
{
forceValue(v, [&]() { return pos; });
}
template<typename Callable>
void EvalState::forceValue(Value & v, Callable getPos)
{
if (v.isThunk()) {
Env * env = v.thunk.env;
@@ -48,22 +47,31 @@ void EvalState::forceValue(Value & v, Callable getPos)
else if (v.isApp())
callFunction(*v.app.left, *v.app.right, v, noPos);
else if (v.isBlackhole())
throwEvalError(getPos(), "infinite recursion encountered");
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);
}
inline void EvalState::forceAttrs(Value & v, const Pos & pos)
{
forceAttrs(v, [&]() { return pos; });
forceValue(v, pos);
if (v.type() != nAttrs)
throwTypeError(pos, "value is %1% while a set was expected", v);
}
template <typename Callable>
inline void EvalState::forceAttrs(Value & v, Callable getPos)
inline void EvalState::forceList(Value & v)
{
forceValue(v, getPos);
if (v.type() != nAttrs)
throwTypeError(getPos(), "value is %1% while a set was expected", v);
forceValue(v);
if (!v.isList())
throwTypeError("value is %1% while a list was expected", v);
}

View File

@@ -1,6 +1,5 @@
#include "eval.hh"
#include "hash.hh"
#include "types.hh"
#include "util.hh"
#include "store-api.hh"
#include "derivations.hh"
@@ -219,7 +218,7 @@ string showType(const Value & v)
}
}
Pos Value::determinePos(const Pos & pos) const
Pos Value::determinePos(const Pos &pos) const
{
switch (internalType) {
case tAttrs: return *attrs->pos;
@@ -754,11 +753,6 @@ LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const
});
}
LocalNoInlineNoReturn(void throwTypeError(const char * s, const Value & v))
{
throw TypeError(s, showType(v));
}
LocalNoInlineNoReturn(void throwAssertionError(const Pos & pos, const char * s, const string & s1))
{
throw AssertionError({
@@ -1143,7 +1137,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, [&]() { return vOverrides->determinePos(noPos); });
state.forceAttrs(*vOverrides);
Bindings * newBnds = state.allocBindings(v.attrs->capacity() + vOverrides->attrs->size());
for (auto & i : *v.attrs)
newBnds->push_back(i);
@@ -1291,7 +1285,7 @@ void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v)
e->eval(state, env, vTmp);
for (auto & i : attrPath) {
state.forceValue(*vAttrs, noPos);
state.forceValue(*vAttrs);
Bindings::iterator j;
Symbol name = getName(i, state, env);
if (vAttrs->type() != nAttrs ||
@@ -1379,7 +1373,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->has(i.name))
if (!lambda.formals->argNames.count(i.name))
throwTypeError(pos, "%1% called with unexpected argument '%2%'", lambda, i.name);
abort(); // can't happen
}
@@ -1505,16 +1499,14 @@ void EvalState::incrFunctionCall(ExprLambda * fun)
void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res)
{
auto pos = fun.determinePos(noPos);
forceValue(fun, pos);
forceValue(fun);
if (fun.type() == nAttrs) {
auto found = fun.attrs->find(sFunctor);
if (found != fun.attrs->end()) {
Value * v = allocValue();
callFunction(*found->value, fun, *v, pos);
forceValue(*v, pos);
callFunction(*found->value, fun, *v, noPos);
forceValue(*v);
return autoCallFunction(args, *v, res);
}
}
@@ -1702,7 +1694,7 @@ void EvalState::concatLists(Value & v, size_t nrLists, Value * * lists, const Po
void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
{
PathSet context;
std::vector<BackedStringView> s;
std::vector<std::string> s;
size_t sSize = 0;
NixInt n = 0;
NixFloat nf = 0;
@@ -1713,7 +1705,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
const auto str = [&] {
std::string result;
result.reserve(sSize);
for (const auto & part : s) result += *part;
for (const auto & part : s) result += part;
return result;
};
/* c_str() is not str().c_str() because we want to create a string
@@ -1723,18 +1715,15 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
char * result = allocString(sSize + 1);
char * tmp = result;
for (const auto & part : s) {
memcpy(tmp, part->data(), part->size());
tmp += part->size();
memcpy(tmp, part.c_str(), part.size());
tmp += part.size();
}
*tmp = 0;
return result;
};
Value values[es->size()];
Value * vTmpP = values;
for (auto & [i_pos, i] : *es) {
Value & vTmp = *vTmpP++;
Value vTmp;
i->eval(state, env, vTmp);
/* If the first element is a path, then the result will also
@@ -1767,9 +1756,9 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
/* skip canonization of first path, which would only be not
canonized in the first place if it's coming from a ./${foo} type
path */
auto part = state.coerceToString(i_pos, vTmp, context, false, firstType == nString, !first);
sSize += part->size();
s.emplace_back(std::move(part));
s.emplace_back(
state.coerceToString(i_pos, vTmp, context, false, firstType == nString, !first));
sSize += s.back().size();
}
first = false;
@@ -1803,7 +1792,7 @@ void EvalState::forceValueDeep(Value & v)
recurse = [&](Value & v) {
if (!seen.insert(&v).second) return;
forceValue(v, [&]() { return v.determinePos(noPos); });
forceValue(v);
if (v.type() == nAttrs) {
for (auto & i : *v.attrs)
@@ -1868,7 +1857,7 @@ void EvalState::forceFunction(Value & v, const Pos & pos)
}
std::string_view EvalState::forceString(Value & v, const Pos & pos)
string EvalState::forceString(Value & v, const Pos & pos)
{
forceValue(v, pos);
if (v.type() != nString) {
@@ -1877,7 +1866,7 @@ std::string_view EvalState::forceString(Value & v, const Pos & pos)
else
throwTypeError("value is %1% while a string was expected", v);
}
return v.string.s;
return string(v.string.s);
}
@@ -1912,17 +1901,17 @@ std::vector<std::pair<Path, std::string>> Value::getContext()
}
std::string_view EvalState::forceString(Value & v, PathSet & context, const Pos & pos)
string EvalState::forceString(Value & v, PathSet & context, const Pos & pos)
{
auto s = forceString(v, pos);
string s = forceString(v, pos);
copyContext(v, context);
return s;
}
std::string_view EvalState::forceStringNoCtx(Value & v, const Pos & pos)
string EvalState::forceStringNoCtx(Value & v, const Pos & pos)
{
auto s = forceString(v, pos);
string 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%')",
@@ -1940,7 +1929,7 @@ 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, *i->pos);
forceValue(*i->value);
if (i->value->type() != nString) return false;
return strcmp(i->value->string.s, "derivation") == 0;
}
@@ -1953,35 +1942,34 @@ std::optional<string> EvalState::tryAttrsToString(const Pos & pos, Value & v,
if (i != v.attrs->end()) {
Value v1;
callFunction(*i->value, v, v1, pos);
return coerceToString(pos, v1, context, coerceMore, copyToStore).toOwned();
return coerceToString(pos, v1, context, coerceMore, copyToStore);
}
return {};
}
BackedStringView EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
string 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 std::string_view(v.string.s);
return v.string.s;
}
if (v.type() == nPath) {
BackedStringView path(PathView(v.path));
if (canonicalizePath)
path = canonPath(*path);
if (copyToStore)
path = copyPathToStore(context, std::move(path).toOwned());
return path;
Path path(canonicalizePath ? canonPath(v.path) : v.path);
return copyToStore ? copyPathToStore(context, path) : path;
}
if (v.type() == nAttrs) {
auto maybeString = tryAttrsToString(pos, v, context, coerceMore, copyToStore);
if (maybeString)
return std::move(*maybeString);
if (maybeString) {
return *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);
@@ -2003,13 +1991,14 @@ BackedStringView EvalState::coerceToString(const Pos & pos, Value & v, PathSet &
if (v.isList()) {
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 std::move(result);
return result;
}
}
@@ -2043,7 +2032,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).toOwned();
string path = coerceToString(pos, v, context, false, false);
if (path == "" || path[0] != '/')
throwEvalError(pos, "string '%1%' doesn't represent an absolute path", path);
return path;
@@ -2052,8 +2041,8 @@ Path EvalState::coerceToPath(const Pos & pos, Value & v, PathSet & context)
bool EvalState::eqValues(Value & v1, Value & v2)
{
forceValue(v1, noPos);
forceValue(v2, noPos);
forceValue(v1);
forceValue(v2);
/* !!! Hack to support some old broken code that relies on pointer
equality tests between sets. (Specifically, builderDefs calls

View File

@@ -1,7 +1,6 @@
#pragma once
#include "attr-set.hh"
#include "types.hh"
#include "value.hh"
#include "nixexpr.hh"
#include "symbol-table.hh"
@@ -202,8 +201,8 @@ public:
void resetFileCache();
/* Look up a file in the search path. */
Path findFile(const std::string_view path);
Path findFile(SearchPath & searchPath, const std::string_view path, const Pos & pos = noPos);
Path findFile(const string & path);
Path findFile(SearchPath & searchPath, const string & path, const Pos & pos = noPos);
/* If the specified search path element is a URI, download it. */
std::pair<bool, std::string> resolveSearchPathElem(const SearchPathElem & elem);
@@ -222,10 +221,7 @@ 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);
template <typename Callable>
inline void forceValue(Value & v, Callable getPos);
inline void forceValue(Value & v, const Pos & pos = noPos);
/* Force a value, then recursively force list elements and
attributes. */
@@ -235,17 +231,14 @@ public:
NixInt forceInt(Value & v, const Pos & pos);
NixFloat forceFloat(Value & v, const Pos & pos);
bool forceBool(Value & v, const Pos & pos);
void forceAttrs(Value & v, const Pos & pos);
template <typename Callable>
inline void forceAttrs(Value & v, Callable getPos);
inline void forceAttrs(Value & v);
inline void forceAttrs(Value & v, const Pos & pos);
inline void forceList(Value & v);
inline void forceList(Value & v, const Pos & pos);
void forceFunction(Value & v, const Pos & pos); // either lambda or primop
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);
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);
/* Return true iff the value `v' denotes a derivation (i.e. a
set with attribute `type = "derivation"'). */
@@ -258,7 +251,7 @@ public:
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. */
BackedStringView coerceToString(const Pos & pos, Value & v, PathSet & context,
string coerceToString(const Pos & pos, Value & v, PathSet & context,
bool coerceMore = false, bool copyToStore = true,
bool canonicalizePath = true);
@@ -316,8 +309,8 @@ private:
friend struct ExprAttrs;
friend struct ExprLet;
Expr * parse(char * text, size_t length, FileOrigin origin, const PathView path,
const PathView basePath, StaticEnv & staticEnv);
Expr * parse(char * text, size_t length, FileOrigin origin, const Path & path,
const Path & basePath, StaticEnv & staticEnv);
public:

View File

@@ -89,11 +89,11 @@ static void expectType(EvalState & state, ValueType type,
static std::map<FlakeId, FlakeInput> parseFlakeInputs(
EvalState & state, Value * value, const Pos & pos,
const std::optional<Path> & baseDir, InputPath lockRootPath);
const std::optional<Path> & baseDir);
static FlakeInput parseFlakeInput(EvalState & state,
const std::string & inputName, Value * value, const Pos & pos,
const std::optional<Path> & baseDir, InputPath lockRootPath)
const std::optional<Path> & baseDir)
{
expectType(state, nAttrs, *value, pos);
@@ -117,12 +117,10 @@ 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, lockRootPath);
input.overrides = parseFlakeInputs(state, attr.value, *attr.pos, baseDir);
} else if (attr.name == sFollows) {
expectType(state, nString, *attr.value, *attr.pos);
auto follows(parseInputPath(attr.value->string.s));
follows.insert(follows.begin(), lockRootPath.begin(), lockRootPath.end());
input.follows = follows;
input.follows = parseInputPath(attr.value->string.s);
} else {
switch (attr.value->type()) {
case nString:
@@ -168,7 +166,7 @@ static FlakeInput parseFlakeInput(EvalState & state,
static std::map<FlakeId, FlakeInput> parseFlakeInputs(
EvalState & state, Value * value, const Pos & pos,
const std::optional<Path> & baseDir, InputPath lockRootPath)
const std::optional<Path> & baseDir)
{
std::map<FlakeId, FlakeInput> inputs;
@@ -180,8 +178,7 @@ static std::map<FlakeId, FlakeInput> parseFlakeInputs(
inputAttr.name,
inputAttr.value,
*inputAttr.pos,
baseDir,
lockRootPath));
baseDir));
}
return inputs;
@@ -191,8 +188,7 @@ static Flake getFlake(
EvalState & state,
const FlakeRef & originalRef,
bool allowLookup,
FlakeCache & flakeCache,
InputPath lockRootPath)
FlakeCache & flakeCache)
{
auto [sourceInfo, resolvedRef, lockedRef] = fetchOrSubstituteTree(
state, originalRef, allowLookup, flakeCache);
@@ -227,7 +223,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, lockRootPath);
flake.inputs = parseFlakeInputs(state, inputs->value, *inputs->pos, flakeDir);
auto sOutputs = state.symbols.create("outputs");
@@ -254,12 +250,10 @@ 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, string(state.forceStringNoCtx(*setting.value, *setting.pos))});
flake.config.settings.insert({setting.name, state.forceStringNoCtx(*setting.value, *setting.pos)});
else if (setting.value->type() == nPath) {
PathSet emptyContext = {};
flake.config.settings.emplace(
setting.name,
state.coerceToString(*setting.pos, *setting.value, emptyContext, false, true, true) .toOwned());
flake.config.settings.insert({setting.name, state.coerceToString(*setting.pos, *setting.value, emptyContext, false, true, true)});
}
else if (setting.value->type() == nInt)
flake.config.settings.insert({setting.name, state.forceInt(*setting.value, *setting.pos)});
@@ -271,7 +265,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.emplace_back(state.forceStringNoCtx(*elem, *setting.pos));
ss.push_back(state.forceStringNoCtx(*elem, *setting.pos));
}
flake.config.settings.insert({setting.name, ss});
}
@@ -293,11 +287,6 @@ 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;
@@ -343,12 +332,22 @@ LockedFlake lockFlake(
std::vector<FlakeRef> 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<void(
const FlakeInputs & flakeInputs,
std::shared_ptr<Node> node,
const InputPath & inputPathPrefix,
std::shared_ptr<const Node> oldNode,
const InputPath & lockRootPath,
const LockParent & parent,
const Path & parentPath,
bool trustLock)>
computeLocks;
@@ -358,7 +357,7 @@ LockedFlake lockFlake(
std::shared_ptr<Node> node,
const InputPath & inputPathPrefix,
std::shared_ptr<const Node> oldNode,
const InputPath & lockRootPath,
const LockParent & parent,
const Path & parentPath,
bool trustLock)
{
@@ -403,7 +402,17 @@ LockedFlake lockFlake(
if (input.follows) {
InputPath target;
target.insert(target.end(), input.follows->begin(), input.follows->end());
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);
}
debug("input '%s' follows '%s'", inputPathS, printInputPath(target));
node->inputs.insert_or_assign(id, target);
@@ -476,25 +485,23 @@ LockedFlake lockFlake(
break;
}
}
auto absoluteFollows(lockRootPath);
absoluteFollows.insert(absoluteFollows.end(), follows->begin(), follows->end());
fakeInputs.emplace(i.first, FlakeInput {
.follows = absoluteFollows,
.follows = *follows,
});
}
}
}
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);
LockParent newParent {
.path = inputPath,
.absolute = true
};
computeLocks(
mustRefetch
? getFlake(state, oldLock->lockedRef, false, flakeCache, inputPath).inputs
? getFlake(state, oldLock->lockedRef, false, flakeCache).inputs
: fakeInputs,
childNode, inputPath, oldLock, lockRootPath, parentPath, !mustRefetch);
childNode, inputPath, oldLock, newParent, parentPath, !mustRefetch);
} else {
/* We need to create a new lock file entry. So fetch
@@ -513,7 +520,7 @@ LockedFlake lockFlake(
if (localRef.input.getType() == "path")
localPath = absPath(*input.ref->input.getSourcePath(), parentPath);
auto inputFlake = getFlake(state, localRef, useRegistries, flakeCache, inputPath);
auto inputFlake = getFlake(state, localRef, useRegistries, flakeCache);
/* Note: in case of an --override-input, we use
the *original* ref (input2.ref) for the
@@ -534,6 +541,13 @@ 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
@@ -544,7 +558,7 @@ LockedFlake lockFlake(
? std::dynamic_pointer_cast<const Node>(oldLock)
: LockFile::read(
inputFlake.sourceInfo->actualPath + "/" + inputFlake.lockedRef.subdir + "/flake.lock").root,
oldLock ? lockRootPath : inputPath, localPath, false);
newParent, localPath, false);
}
else {
@@ -562,12 +576,17 @@ 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, {}, parentPath, false);
lockFlags.recreateLockFile ? nullptr : oldLockFile.root, parent, parentPath, false);
for (auto & i : lockFlags.inputOverrides)
if (!overridesUsed.count(i.first))
@@ -707,7 +726,7 @@ static void prim_getFlake(EvalState & state, const Pos & pos, Value * * args, Va
{
state.requireExperimentalFeatureOnEvaluation(Xp::Flakes, "builtins.getFlake", pos);
string flakeRefS(state.forceStringNoCtx(*args[0], pos));
auto 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);

View File

@@ -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));
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, *i->pos);
state->forceAttrs(*out->value);
/* And evaluate its outPath attribute. */
Bindings::iterator outPath = out->value->attrs->find(state->sOutPath);
@@ -172,7 +172,7 @@ StringSet DrvInfo::queryMetaNames()
bool DrvInfo::checkMeta(Value & v)
{
state->forceValue(v, [&]() { return v.determinePos(noPos); });
state->forceValue(v);
if (v.type() == nList) {
for (auto elem : v.listItems())
if (!checkMeta(*elem)) return false;
@@ -266,7 +266,7 @@ void DrvInfo::setMeta(const string & name, Value * v)
/* Cache for already considered attrsets. */
typedef std::set<Bindings *> Done;
typedef set<Bindings *> Done;
/* Evaluate value `v'. If it evaluates to a set of type `derivation',
@@ -278,7 +278,7 @@ static bool getDerivation(EvalState & state, Value & v,
bool ignoreAssertionFailures)
{
try {
state.forceValue(v, [&]() { return v.determinePos(noPos); });
state.forceValue(v);
if (!state.isDerivation(v)) return true;
/* Remove spurious duplicates (e.g., a set like `rec { x =

View File

@@ -70,9 +70,9 @@ public:
#if HAVE_BOEHMGC
typedef std::list<DrvInfo, traceable_allocator<DrvInfo> > DrvInfos;
typedef list<DrvInfo, traceable_allocator<DrvInfo> > DrvInfos;
#else
typedef std::list<DrvInfo> DrvInfos;
typedef list<DrvInfo> DrvInfos;
#endif

View File

@@ -163,7 +163,7 @@ public:
}
};
void parseJSON(EvalState & state, const std::string_view & s_, Value & v)
void parseJSON(EvalState & state, const string & s_, Value & v)
{
JSONSax parser(state, v);
bool res = json::sax_parse(s_, &parser);

View File

@@ -8,6 +8,6 @@ namespace nix {
MakeError(JSONParseError, EvalError);
void parseJSON(EvalState & state, const std::string_view & s, Value & v);
void parseJSON(EvalState & state, const string & s, Value & v);
}

View File

@@ -66,7 +66,7 @@ static void adjustLoc(YYLTYPE * loc, const char * s, size_t len)
// 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)
static Expr * unescapeStr(SymbolTable & symbols, char * s, size_t length)
{
char * result = s;
char * t = s;
@@ -89,7 +89,7 @@ static StringToken unescapeStr(SymbolTable & symbols, char * s, size_t length)
else *t = c;
t++;
}
return {result, size_t(t - result)};
return new ExprString(symbols.create({result, size_t(t - result)}));
}
@@ -176,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->str = unescapeStr(data->symbols, yytext, yyleng);
yylval->e = unescapeStr(data->symbols, yytext, yyleng);
return STR;
}
<STRING>\$\{ { PUSH_STATE(DEFAULT); return DOLLAR_CURLY; }
@@ -191,26 +191,26 @@ or { return OR_KW; }
\'\'(\ *\n)? { PUSH_STATE(IND_STRING); return IND_STRING_OPEN; }
<IND_STRING>([^\$\']|\$[^\{\']|\'[^\'\$])+ {
yylval->str = {yytext, (size_t) yyleng, true};
yylval->e = new ExprIndStr(yytext);
return IND_STR;
}
<IND_STRING>\'\'\$ |
<IND_STRING>\$ {
yylval->str = {"$", 1};
yylval->e = new ExprIndStr("$");
return IND_STR;
}
<IND_STRING>\'\'\' {
yylval->str = {"''", 2};
yylval->e = new ExprIndStr("''");
return IND_STR;
}
<IND_STRING>\'\'\\{ANY} {
yylval->str = unescapeStr(data->symbols, yytext + 2, yyleng - 2);
yylval->e = unescapeStr(data->symbols, yytext + 2, yyleng - 2);
return IND_STR;
}
<IND_STRING>\$\{ { PUSH_STATE(DEFAULT); return DOLLAR_CURLY; }
<IND_STRING>\'\' { POP_STATE(); return IND_STRING_CLOSE; }
<IND_STRING>\' {
yylval->str = {"'", 1};
yylval->e = new ExprIndStr("'");
return IND_STR;
}
@@ -264,7 +264,7 @@ or { return OR_KW; }
PUSH_STATE(INPATH_SLASH);
else
PUSH_STATE(INPATH);
yylval->str = {yytext, (size_t) yyleng};
yylval->e = new ExprString(data->symbols.create(string(yytext)));
return STR;
}
<INPATH>{ANY} |

View File

@@ -110,13 +110,20 @@ struct ExprFloat : Expr
struct ExprString : Expr
{
string s;
Symbol s;
Value v;
ExprString(std::string s) : s(std::move(s)) { v.mkString(this->s.data()); };
ExprString(const Symbol & s) : s(s) { v.mkString(s); };
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;
@@ -216,25 +223,10 @@ struct Formal
struct Formals
{
typedef std::vector<Formal> Formals_;
typedef std::list<Formal> Formals_;
Formals_ formals;
std::set<Symbol> 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<Formal> lexicographicOrder() const
{
std::vector<Formal> 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
@@ -247,6 +239,11 @@ 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;
@@ -335,8 +332,8 @@ struct ExprConcatStrings : Expr
{
Pos pos;
bool forceString;
std::vector<std::pair<Pos, Expr *> > * es;
ExprConcatStrings(const Pos & pos, bool forceString, std::vector<std::pair<Pos, Expr *> > * es)
vector<std::pair<Pos, Expr *> > * es;
ExprConcatStrings(const Pos & pos, bool forceString, vector<std::pair<Pos, Expr *> > * es)
: pos(pos), forceString(forceString), es(es) { };
COMMON_METHODS
};
@@ -367,19 +364,15 @@ struct StaticEnv
void sort()
{
std::stable_sort(vars.begin(), vars.end(),
std::sort(vars.begin(), vars.end(),
[](const Vars::value_type & a, const Vars::value_type & b) { return a.first < b.first; });
}
void deduplicate()
{
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);
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());
}
Vars::const_iterator find(const Symbol & name) const

View File

@@ -16,8 +16,6 @@
#ifndef BISON_HEADER
#define BISON_HEADER
#include <variant>
#include "util.hh"
#include "nixexpr.hh"
@@ -41,22 +39,8 @@ namespace nix {
{ };
};
struct ParserFormals {
std::vector<Formal> 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)
@@ -156,46 +140,21 @@ static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
}
static Formals * toFormals(ParseData & data, ParserFormals * formals,
Pos pos = noPos, Symbol arg = {})
static void addFormal(const Pos & pos, Formals * formals, const Formal & formal)
{
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<std::pair<Symbol, Pos>> 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)
if (!formals->argNames.insert(formal.name).second)
throw ParseError({
.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),
.msg = hintfmt("duplicate formal function argument '%1%'",
formal.name),
.errPos = pos
});
delete formals;
return new Formals(std::move(result));
formals->formals.push_front(formal);
}
static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols,
std::vector<std::pair<Pos, std::variant<Expr *, StringToken> > > & es)
static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<std::pair<Pos, Expr *> > & es)
{
if (es.empty()) return new ExprString("");
if (es.empty()) return new ExprString(symbols.create(""));
/* Figure out the minimum indentation. Note that by design
whitespace-only final lines are not taken into account. (So
@@ -204,20 +163,20 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols,
size_t minIndent = 1000000;
size_t curIndent = 0;
for (auto & [i_pos, i] : es) {
auto * str = std::get_if<StringToken>(&i);
if (!str || !str->hasIndentation) {
/* Anti-quotations and escaped characters end the current start-of-line whitespace. */
ExprIndStr * e = dynamic_cast<ExprIndStr *>(i);
if (!e) {
/* Anti-quotations end the current start-of-line whitespace. */
if (atStartOfLine) {
atStartOfLine = false;
if (curIndent < minIndent) minIndent = curIndent;
}
continue;
}
for (size_t j = 0; j < str->l; ++j) {
for (size_t j = 0; j < e->s.size(); ++j) {
if (atStartOfLine) {
if (str->p[j] == ' ')
if (e->s[j] == ' ')
curIndent++;
else if (str->p[j] == '\n') {
else if (e->s[j] == '\n') {
/* Empty line, doesn't influence minimum
indentation. */
curIndent = 0;
@@ -225,7 +184,7 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols,
atStartOfLine = false;
if (curIndent < minIndent) minIndent = curIndent;
}
} else if (str->p[j] == '\n') {
} else if (e->s[j] == '\n') {
atStartOfLine = true;
curIndent = 0;
}
@@ -233,35 +192,37 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols,
}
/* Strip spaces from each line. */
std::vector<std::pair<Pos, Expr *> > * es2 = new std::vector<std::pair<Pos, Expr *> >;
vector<std::pair<Pos, Expr *> > * es2 = new vector<std::pair<Pos, Expr *> >;
atStartOfLine = true;
size_t curDropped = 0;
size_t n = es.size();
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) {
for (vector<std::pair<Pos, Expr *> >::iterator i = es.begin(); i != es.end(); ++i, --n) {
ExprIndStr * e = dynamic_cast<ExprIndStr *>(i->second);
if (!e) {
atStartOfLine = false;
curDropped = 0;
es2->push_back(*i);
continue;
}
string s2;
for (size_t j = 0; j < t.l; ++j) {
for (size_t j = 0; j < e->s.size(); ++j) {
if (atStartOfLine) {
if (t.p[j] == ' ') {
if (e->s[j] == ' ') {
if (curDropped++ >= minIndent)
s2 += t.p[j];
s2 += e->s[j];
}
else if (t.p[j] == '\n') {
else if (e->s[j] == '\n') {
curDropped = 0;
s2 += t.p[j];
s2 += e->s[j];
} else {
atStartOfLine = false;
curDropped = 0;
s2 += t.p[j];
s2 += e->s[j];
}
} else {
s2 += t.p[j];
if (t.p[j] == '\n') atStartOfLine = true;
s2 += e->s[j];
if (e->s[j] == '\n') atStartOfLine = true;
}
}
@@ -273,10 +234,7 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols,
s2 = string(s2, 0, p + 1);
}
es2->emplace_back(i->first, new ExprString(s2));
};
for (; i != es.end(); ++i, --n) {
std::visit(overloaded { trimExpr, trimString }, i->second);
es2->emplace_back(i->first, new ExprString(symbols.create(s2)));
}
/* If this is a single string, then don't do a concatenation. */
@@ -311,17 +269,22 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
nix::Expr * e;
nix::ExprList * list;
nix::ExprAttrs * attrs;
nix::ParserFormals * formals;
nix::Formals * formals;
nix::Formal * formal;
nix::NixInt n;
nix::NixFloat nf;
// 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;
operator std::string_view() const { return {p, l}; }
};
StringToken id; // !!! -> Symbol
StringToken path;
StringToken uri;
StringToken str;
std::vector<nix::AttrName> * attrNames;
std::vector<std::pair<nix::Pos, nix::Expr *> > * string_parts;
std::vector<std::pair<nix::Pos, std::variant<nix::Expr *, StringToken> > > * ind_string_parts;
}
%type <e> start expr expr_function expr_if expr_op
@@ -331,12 +294,11 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
%type <formals> formals
%type <formal> formal
%type <attrNames> attrs attrpath
%type <string_parts> string_parts_interpolated
%type <ind_string_parts> ind_string_parts
%type <string_parts> string_parts_interpolated ind_string_parts
%type <e> path_start string_parts string_attr
%type <id> attr
%token <id> ID ATTRPATH
%token <str> STR IND_STR
%token <e> STR IND_STR
%token <n> INT
%token <nf> FLOAT
%token <path> PATH HPATH SPATH PATH_END
@@ -369,17 +331,11 @@ 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(""), toFormals(*data, $2), $5); }
{ $$ = new ExprLambda(CUR_POS, data->symbols.create(""), $2, $5); }
| '{' formals '}' '@' ID ':' expr_function
{
Symbol arg = data->symbols.create($5);
$$ = new ExprLambda(CUR_POS, arg, toFormals(*data, $2, CUR_POS, arg), $7);
}
{ $$ = new ExprLambda(CUR_POS, data->symbols.create($5), $2, $7); }
| ID '@' '{' formals '}' ':' expr_function
{
Symbol arg = data->symbols.create($1);
$$ = new ExprLambda(CUR_POS, arg, toFormals(*data, $4, CUR_POS, arg), $7);
}
{ $$ = new ExprLambda(CUR_POS, data->symbols.create($1), $4, $7); }
| ASSERT expr ';' expr_function
{ $$ = new ExprAssert(CUR_POS, $2, $4); }
| WITH expr ';' expr_function
@@ -415,7 +371,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 std::vector<std::pair<Pos, Expr *> >({{makeCurPos(@1, data), $1}, {makeCurPos(@3, data), $3}})); }
{ $$ = new ExprConcatStrings(CUR_POS, false, new vector<std::pair<Pos, Expr *> >({{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}); }
@@ -449,7 +405,7 @@ expr_select
expr_simple
: ID {
std::string_view s = "__curPos";
if ($1.l == s.size() && strncmp($1.p, s.data(), s.size()) == 0)
if (strncmp($1.p, s.data(), s.size()) == 0)
$$ = new ExprPos(CUR_POS);
else
$$ = new ExprVar(CUR_POS, data->symbols.create($1));
@@ -470,7 +426,7 @@ expr_simple
$$ = new ExprCall(CUR_POS,
new ExprVar(data->symbols.create("__findFile")),
{new ExprVar(data->symbols.create("__nixPath")),
new ExprString(path)});
new ExprString(data->symbols.create(path))});
}
| URI {
static bool noURLLiterals = settings.isExperimentalFeatureEnabled(Xp::NoUrlLiterals);
@@ -479,7 +435,7 @@ expr_simple
.msg = hintfmt("URL literals are disabled"),
.errPos = CUR_POS
});
$$ = new ExprString(string($1));
$$ = new ExprString(data->symbols.create($1));
}
| '(' expr ')' { $$ = $2; }
/* Let expressions `let {..., body = ...}' are just desugared
@@ -494,19 +450,18 @@ expr_simple
;
string_parts
: STR { $$ = new ExprString(string($1)); }
: STR
| string_parts_interpolated { $$ = new ExprConcatStrings(CUR_POS, true, $1); }
| { $$ = new ExprString(""); }
| { $$ = new ExprString(data->symbols.create("")); }
;
string_parts_interpolated
: string_parts_interpolated STR
{ $$ = $1; $1->emplace_back(makeCurPos(@2, data), new ExprString(string($2))); }
: string_parts_interpolated STR { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $2); }
| string_parts_interpolated DOLLAR_CURLY expr '}' { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $3); }
| DOLLAR_CURLY expr '}' { $$ = new std::vector<std::pair<Pos, Expr *> >; $$->emplace_back(makeCurPos(@1, data), $2); }
| DOLLAR_CURLY expr '}' { $$ = new vector<std::pair<Pos, Expr *> >; $$->emplace_back(makeCurPos(@1, data), $2); }
| STR DOLLAR_CURLY expr '}' {
$$ = new std::vector<std::pair<Pos, Expr *> >;
$$->emplace_back(makeCurPos(@1, data), new ExprString(string($1)));
$$ = new vector<std::pair<Pos, Expr *> >;
$$->emplace_back(makeCurPos(@1, data), $1);
$$->emplace_back(makeCurPos(@2, data), $3);
}
;
@@ -528,7 +483,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 std::vector<std::pair<Pos, std::variant<Expr *, StringToken> > >; }
| { $$ = new vector<std::pair<Pos, Expr *> >; }
;
binds
@@ -560,7 +515,7 @@ attrs
{ $$ = $1;
ExprString * str = dynamic_cast<ExprString *>($2);
if (str) {
$$->push_back(AttrName(data->symbols.create(str->s)));
$$->push_back(AttrName(str->s));
delete str;
} else
throw ParseError({
@@ -577,17 +532,17 @@ attrpath
{ $$ = $1;
ExprString * str = dynamic_cast<ExprString *>($3);
if (str) {
$$->push_back(AttrName(data->symbols.create(str->s)));
$$->push_back(AttrName(str->s));
delete str;
} else
$$->push_back(AttrName($3));
}
| attr { $$ = new std::vector<AttrName>; $$->push_back(AttrName(data->symbols.create($1))); }
| attr { $$ = new vector<AttrName>; $$->push_back(AttrName(data->symbols.create($1))); }
| string_attr
{ $$ = new std::vector<AttrName>;
{ $$ = new vector<AttrName>;
ExprString *str = dynamic_cast<ExprString *>($1);
if (str) {
$$->push_back(AttrName(data->symbols.create(str->s)));
$$->push_back(AttrName(str->s));
delete str;
} else
$$->push_back(AttrName($1));
@@ -611,13 +566,13 @@ expr_list
formals
: formal ',' formals
{ $$ = $3; $$->formals.push_back(*$1); }
{ $$ = $3; addFormal(CUR_POS, $$, *$1); }
| formal
{ $$ = new ParserFormals; $$->formals.push_back(*$1); $$->ellipsis = false; }
{ $$ = new Formals; addFormal(CUR_POS, $$, *$1); $$->ellipsis = false; }
|
{ $$ = new ParserFormals; $$->ellipsis = false; }
{ $$ = new Formals; $$->ellipsis = false; }
| ELLIPSIS
{ $$ = new ParserFormals; $$->ellipsis = true; }
{ $$ = new Formals; $$->ellipsis = true; }
;
formal
@@ -643,7 +598,7 @@ namespace nix {
Expr * EvalState::parse(char * text, size_t length, FileOrigin origin,
const PathView path, const PathView basePath, StaticEnv & staticEnv)
const Path & path, const Path & basePath, StaticEnv & staticEnv)
{
yyscan_t scanner;
ParseData data(*this);
@@ -754,24 +709,24 @@ void EvalState::addToSearchPath(const string & s)
}
Path EvalState::findFile(const std::string_view path)
Path EvalState::findFile(const string & path)
{
return findFile(searchPath, path);
}
Path EvalState::findFile(SearchPath & searchPath, const std::string_view path, const Pos & pos)
Path EvalState::findFile(SearchPath & searchPath, const string & path, const Pos & pos)
{
for (auto & i : searchPath) {
std::string suffix;
if (i.first.empty())
suffix = concatStrings("/", path);
suffix = "/" + 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 ? "" : concatStrings("/", path.substr(s));
suffix = path.size() == s ? "" : "/" + string(path, s);
}
auto r = resolveSearchPathElem(i);
if (!r.first) continue;
@@ -780,7 +735,7 @@ Path EvalState::findFile(SearchPath & searchPath, const std::string_view path, c
}
if (hasPrefix(path, "nix/"))
return concatStrings(corepkgsPrefix, path.substr(4));
return corepkgsPrefix + path.substr(4);
throw ThrownError({
.msg = hintfmt(evalSettings.pureEval

View File

@@ -92,6 +92,8 @@ 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;
};
@@ -103,7 +105,9 @@ static Path realisePath(EvalState & state, const Pos & pos, Value & v, const Rea
auto path = [&]()
{
try {
return state.coerceToPath(pos, v, context);
return flags.requireAbsolutePath
? state.coerceToPath(pos, v, context)
: state.coerceToString(pos, v, context, false, false);
} catch (Error & e) {
e.addTrace(pos, "while realising the context of a path");
throw;
@@ -210,7 +214,7 @@ static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vS
if (!vScope)
state.evalFile(path, v);
else {
state.forceAttrs(*vScope, pos);
state.forceAttrs(*vScope);
Env * env = &state.allocEnv(vScope->attrs->size());
env->up = &state.baseEnv;
@@ -314,7 +318,7 @@ void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value
{
auto path = realisePath(state, pos, *args[0]);
string sym(state.forceStringNoCtx(*args[1], pos));
string sym = state.forceStringNoCtx(*args[1], pos);
void *handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL);
if (!handle)
@@ -350,11 +354,10 @@ void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v)
});
}
PathSet context;
auto program = state.coerceToString(pos, *elems[0], context, false, false).toOwned();
auto program = state.coerceToString(pos, *elems[0], context, false, false);
Strings commandArgs;
for (unsigned int i = 1; i < args[0]->listSize(); ++i) {
commandArgs.push_back(state.coerceToString(pos, *elems[i], context, false, false).toOwned());
}
for (unsigned int i = 1; i < args[0]->listSize(); ++i)
commandArgs.emplace_back(state.coerceToString(pos, *elems[i], context, false, false));
try {
auto _ = state.realiseContext(context); // FIXME: Handle CA derivations
} catch (InvalidPathError & e) {
@@ -574,9 +577,9 @@ struct CompareValues
#if HAVE_BOEHMGC
typedef std::list<Value *, gc_allocator<Value *> > ValueList;
typedef list<Value *, gc_allocator<Value *> > ValueList;
#else
typedef std::list<Value *> ValueList;
typedef list<Value *> ValueList;
#endif
@@ -654,7 +657,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);
std::set<Value *, decltype(cmp)> doneKeys(cmp);
set<Value *, decltype(cmp)> doneKeys(cmp);
while (!workSet.empty()) {
Value * e = *(workSet.begin());
workSet.pop_front();
@@ -707,7 +710,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).toOwned();
string s = state.coerceToString(pos, *args[0], context);
throw Abort("evaluation aborted with the following error message: '%1%'", s);
}
});
@@ -725,7 +728,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).toOwned();
string s = state.coerceToString(pos, *args[0], context);
throw ThrownError(s);
}
});
@@ -737,7 +740,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).toOwned());
e.addTrace(std::nullopt, state.coerceToString(pos, *args[0], context));
throw;
}
}
@@ -826,7 +829,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));
string name = state.forceStringNoCtx(*args[0], pos);
v.mkString(evalSettings.restrictEval || evalSettings.pureEval ? "" : getEnv(name).value_or(""));
}
@@ -976,7 +979,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
const string & key = i->name;
vomit("processing attribute '%1%'", key);
auto handleHashMode = [&](const std::string_view s) {
auto handleHashMode = [&](const std::string & s) {
if (s == "recursive") ingestionMethod = FileIngestionMethod::Recursive;
else if (s == "flat") ingestionMethod = FileIngestionMethod::Flat;
else
@@ -1031,7 +1034,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).toOwned();
string s = state.coerceToString(posDrvName, *elem, context, true);
drv.args.push_back(s);
}
}
@@ -1067,7 +1070,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
}
} else {
auto s = state.coerceToString(*i->pos, *i->value, context, true).toOwned();
auto s = state.coerceToString(*i->pos, *i->value, context, true);
drv.env.emplace(key, s);
if (i->name == state.sBuilder) drv.builder = std::move(s);
else if (i->name == state.sSystem) drv.platform = std::move(s);
@@ -1180,7 +1183,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
drv.outputs.insert_or_assign(i, DerivationOutput {
.output = DerivationOutputCAFloating {
.method = ingestionMethod,
.hashType = ht,
.hashType = std::move(ht),
},
});
}
@@ -1270,7 +1273,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 occurrence of this string in an derivation attribute will
time, any occurence 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)
@@ -1400,7 +1403,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({
@@ -1420,8 +1423,7 @@ static RegisterPrimOp primop_baseNameOf({
static void prim_dirOf(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
PathSet context;
auto path = state.coerceToString(pos, *args[0], context, false, false);
auto dir = dirOf(*path);
Path dir = dirOf(state.coerceToString(pos, *args[0], context, false, false));
if (args[0]->type() == nPath) v.mkPath(dir); else v.mkString(dir, context);
}
@@ -1487,24 +1489,12 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va
pos
);
PathSet context;
string path = state.coerceToString(pos, *i->value, context, false, false).toOwned();
try {
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),
.errPos = pos
});
}
auto path = realisePath(state, pos, *i->value, { .requireAbsolutePath = false });
searchPath.emplace_back(prefix, path);
}
auto path = state.forceStringNoCtx(*args[1], pos);
string path = state.forceStringNoCtx(*args[1], pos);
v.mkPath(state.checkSourcePath(state.findFile(searchPath, path, pos)));
}
@@ -1518,7 +1508,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)
{
auto type = state.forceStringNoCtx(*args[0], pos);
string type = state.forceStringNoCtx(*args[0], pos);
std::optional<HashType> ht = parseHashType(type);
if (!ht)
throw Error({
@@ -1725,7 +1715,7 @@ static RegisterPrimOp primop_toJSON({
/* Parse a JSON string to a value. */
static void prim_fromJSON(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
auto s = state.forceStringNoCtx(*args[0], pos);
string s = state.forceStringNoCtx(*args[0], pos);
try {
parseJSON(state, s, v);
} catch (JSONParseError &e) {
@@ -1754,8 +1744,8 @@ static RegisterPrimOp primop_fromJSON({
static void prim_toFile(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
PathSet context;
string name(state.forceStringNoCtx(*args[0], pos));
string contents(state.forceString(*args[1], context, pos));
string name = state.forceStringNoCtx(*args[0], pos);
string contents = state.forceString(*args[1], context, pos);
StorePathSet refs;
@@ -2155,7 +2145,7 @@ static RegisterPrimOp primop_attrValues({
/* Dynamic version of the `.' operator. */
void prim_getAttr(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
auto attr = state.forceStringNoCtx(*args[0], pos);
string attr = state.forceStringNoCtx(*args[0], pos);
state.forceAttrs(*args[1], pos);
Bindings::iterator i = getAttr(
state,
@@ -2185,7 +2175,7 @@ static RegisterPrimOp primop_getAttr({
/* Return position information of the specified attribute. */
static void prim_unsafeGetAttrPos(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
auto attr = state.forceStringNoCtx(*args[0], pos);
string attr = state.forceStringNoCtx(*args[0], pos);
state.forceAttrs(*args[1], pos);
Bindings::iterator i = args[1]->attrs->find(state.symbols.create(attr));
if (i == args[1]->attrs->end())
@@ -2203,7 +2193,7 @@ static RegisterPrimOp primop_unsafeGetAttrPos(RegisterPrimOp::Info {
/* Dynamic version of the `?' operator. */
static void prim_hasAttr(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
auto attr = state.forceStringNoCtx(*args[0], pos);
string attr = state.forceStringNoCtx(*args[0], pos);
state.forceAttrs(*args[1], pos);
v.mkBool(args[1]->attrs->find(state.symbols.create(attr)) != args[1]->attrs->end());
}
@@ -2281,7 +2271,7 @@ static RegisterPrimOp primop_removeAttrs({
/* Builds a set from a list specifying (name, value) pairs. To be
precise, a list [{name = "name1"; value = value1;} ... {name =
"nameN"; value = valueN;}] is transformed to {name1 = value1;
... nameN = valueN;}. In case of duplicate occurrences of the same
... nameN = valueN;}. In case of duplicate occurences of the same
name, the first takes precedence. */
static void prim_listToAttrs(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
@@ -2302,7 +2292,7 @@ static void prim_listToAttrs(EvalState & state, const Pos & pos, Value * * args,
pos
);
auto name = state.forceStringNoCtx(*j->value, *j->pos);
string name = state.forceStringNoCtx(*j->value, *j->pos);
Symbol sym = state.symbols.create(name);
if (seen.insert(sym).second) {
@@ -2502,7 +2492,7 @@ static void prim_zipAttrsWith(EvalState & state, const Pos & pos, Value * * args
for (unsigned int n = 0; n < listSize; ++n) {
Value * vElem = listElems[n];
try {
state.forceAttrs(*vElem, noPos);
state.forceAttrs(*vElem);
for (auto & attr : *vElem->attrs)
attrsSeen[attr.name].first++;
} catch (TypeError & e) {
@@ -3034,7 +3024,7 @@ static void prim_groupBy(EvalState & state, const Pos & pos, Value * * args, Val
for (auto vElem : args[1]->listItems()) {
Value res;
state.callFunction(*args[0], *vElem, res, pos);
auto name = state.forceStringNoCtx(res, pos);
string name = state.forceStringNoCtx(res, pos);
Symbol sym = state.symbols.create(name);
auto vector = attrs.try_emplace(sym, ValueVector()).first;
vector->second.push_back(vElem);
@@ -3290,8 +3280,8 @@ static RegisterPrimOp primop_lessThan({
static void prim_toString(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
PathSet context;
auto s = state.coerceToString(pos, *args[0], context, true, false);
v.mkString(*s, context);
string s = state.coerceToString(pos, *args[0], context, true, false);
v.mkString(s, context);
}
static RegisterPrimOp primop_toString({
@@ -3327,7 +3317,7 @@ static void prim_substring(EvalState & state, const Pos & pos, Value * * args, V
int start = state.forceInt(*args[0], pos);
int len = state.forceInt(*args[1], pos);
PathSet context;
auto s = state.coerceToString(pos, *args[2], context);
string s = state.coerceToString(pos, *args[2], context);
if (start < 0)
throw EvalError({
@@ -3335,7 +3325,7 @@ static void prim_substring(EvalState & state, const Pos & pos, Value * * args, V
.errPos = pos
});
v.mkString((unsigned int) start >= s->size() ? "" : s->substr(start, len), context);
v.mkString((unsigned int) start >= s.size() ? "" : string(s, start, len), context);
}
static RegisterPrimOp primop_substring({
@@ -3361,8 +3351,8 @@ static RegisterPrimOp primop_substring({
static void prim_stringLength(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
PathSet context;
auto s = state.coerceToString(pos, *args[0], context);
v.mkInt(s->size());
string s = state.coerceToString(pos, *args[0], context);
v.mkInt(s.size());
}
static RegisterPrimOp primop_stringLength({
@@ -3378,7 +3368,7 @@ static RegisterPrimOp primop_stringLength({
/* Return the cryptographic hash of a string in base-16. */
static void prim_hashString(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
auto type = state.forceStringNoCtx(*args[0], pos);
string type = state.forceStringNoCtx(*args[0], pos);
std::optional<HashType> ht = parseHashType(type);
if (!ht)
throw Error({
@@ -3387,7 +3377,7 @@ static void prim_hashString(EvalState & state, const Pos & pos, Value * * args,
});
PathSet context; // discarded
auto s = state.forceString(*args[1], context, pos);
string s = state.forceString(*args[1], context, pos);
v.mkString(hashString(*ht, s).to_string(Base16, false));
}
@@ -3405,18 +3395,7 @@ static RegisterPrimOp primop_hashString({
struct RegexCache
{
// TODO use C++20 transparent comparison when available
std::unordered_map<std::string_view, std::regex> cache;
std::list<std::string> keys;
std::regex get(std::string_view re)
{
auto it = cache.find(re);
if (it != cache.end())
return it->second;
keys.emplace_back(re);
return cache.emplace(keys.back(), std::regex(keys.back(), std::regex::extended)).first->second;
}
std::unordered_map<std::string, std::regex> cache;
};
std::shared_ptr<RegexCache> makeRegexCache()
@@ -3430,13 +3409,15 @@ void prim_match(EvalState & state, const Pos & pos, Value * * args, Value & v)
try {
auto regex = state.regexCache->get(re);
auto regex = state.regexCache->cache.find(re);
if (regex == state.regexCache->cache.end())
regex = state.regexCache->cache.emplace(re, std::regex(re, std::regex::extended)).first;
PathSet context;
const auto str = state.forceString(*args[1], context, pos);
const std::string str = state.forceString(*args[1], context, pos);
std::cmatch match;
if (!std::regex_match(str.begin(), str.end(), match, regex)) {
std::smatch match;
if (!std::regex_match(str, match, regex->second)) {
v.mkNull();
return;
}
@@ -3511,13 +3492,15 @@ void prim_split(EvalState & state, const Pos & pos, Value * * args, Value & v)
try {
auto regex = state.regexCache->get(re);
auto regex = state.regexCache->cache.find(re);
if (regex == state.regexCache->cache.end())
regex = state.regexCache->cache.emplace(re, std::regex(re, std::regex::extended)).first;
PathSet context;
const auto str = state.forceString(*args[1], context, pos);
const std::string str = state.forceString(*args[1], context, pos);
auto begin = std::cregex_iterator(str.begin(), str.end(), regex);
auto end = std::cregex_iterator();
auto begin = std::sregex_iterator(str.begin(), str.end(), regex->second);
auto end = std::sregex_iterator();
// Any matches results are surrounded by non-matching results.
const size_t len = std::distance(begin, end);
@@ -3529,9 +3512,9 @@ void prim_split(EvalState & state, const Pos & pos, Value * * args, Value & v)
return;
}
for (auto i = begin; i != end; ++i) {
for (std::sregex_iterator i = begin; i != end; ++i) {
assert(idx <= 2 * len + 1 - 3);
auto match = *i;
std::smatch match = *i;
// Add a string for non-matched characters.
(v.listElems()[idx++] = state.allocValue())->mkString(match.prefix().str());
@@ -3622,7 +3605,7 @@ static void prim_concatStringsSep(EvalState & state, const Pos & pos, Value * *
for (auto elem : args[1]->listItems()) {
if (first) first = false; else res += sep;
res += *state.coerceToString(pos, *elem, context);
res += state.coerceToString(pos, *elem, context);
}
v.mkString(res, context);
@@ -3649,17 +3632,17 @@ static void prim_replaceStrings(EvalState & state, const Pos & pos, Value * * ar
.errPos = pos
});
std::vector<string> from;
vector<string> from;
from.reserve(args[0]->listSize());
for (auto elem : args[0]->listItems())
from.emplace_back(state.forceString(*elem, pos));
from.push_back(state.forceString(*elem, pos));
std::vector<std::pair<string, PathSet>> to;
vector<std::pair<string, PathSet>> to;
to.reserve(args[1]->listSize());
for (auto elem : args[1]->listItems()) {
PathSet ctx;
auto s = state.forceString(*elem, ctx, pos);
to.emplace_back(s, std::move(ctx));
to.push_back(std::make_pair(std::move(s), std::move(ctx)));
}
PathSet context;
@@ -3721,7 +3704,7 @@ static RegisterPrimOp primop_replaceStrings({
static void prim_parseDrvName(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
auto name = state.forceStringNoCtx(*args[0], pos);
string name = state.forceStringNoCtx(*args[0], pos);
DrvName parsed(name);
auto attrs = state.buildBindings(2);
attrs.alloc(state.sName).mkString(parsed.name);
@@ -3745,8 +3728,8 @@ static RegisterPrimOp primop_parseDrvName({
static void prim_compareVersions(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
auto version1 = state.forceStringNoCtx(*args[0], pos);
auto version2 = state.forceStringNoCtx(*args[1], pos);
string version1 = state.forceStringNoCtx(*args[0], pos);
string version2 = state.forceStringNoCtx(*args[1], pos);
v.mkInt(compareVersions(version1, version2));
}
@@ -3765,14 +3748,14 @@ static RegisterPrimOp primop_compareVersions({
static void prim_splitVersion(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
auto version = state.forceStringNoCtx(*args[0], pos);
string version = state.forceStringNoCtx(*args[0], pos);
auto iter = version.cbegin();
Strings components;
while (iter != version.cend()) {
auto component = nextComponent(iter, version.cend());
if (component.empty())
break;
components.emplace_back(component);
components.emplace_back(std::move(component));
}
state.mkList(v, components.size());
for (const auto & [n, component] : enumerate(components))

View File

@@ -7,8 +7,7 @@ namespace nix {
static void prim_unsafeDiscardStringContext(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
PathSet context;
auto s = state.coerceToString(pos, *args[0], context);
v.mkString(*s);
v.mkString(state.coerceToString(pos, *args[0], context));
}
static RegisterPrimOp primop_unsafeDiscardStringContext("__unsafeDiscardStringContext", 1, prim_unsafeDiscardStringContext);
@@ -33,13 +32,13 @@ static RegisterPrimOp primop_hasContext("__hasContext", 1, prim_hasContext);
static void prim_unsafeDiscardOutputDependency(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
PathSet context;
auto s = state.coerceToString(pos, *args[0], context);
string s = state.coerceToString(pos, *args[0], context);
PathSet context2;
for (auto & p : context)
context2.insert(p.at(0) == '=' ? string(p, 1) : p);
v.mkString(*s, context2);
v.mkString(s, context2);
}
static RegisterPrimOp primop_unsafeDiscardOutputDependency("__unsafeDiscardOutputDependency", 1, prim_unsafeDiscardOutputDependency);
@@ -181,7 +180,7 @@ static void prim_appendContext(EvalState & state, const Pos & pos, Value * * arg
}
for (auto elem : iter->value->listItems()) {
auto name = state.forceStringNoCtx(*elem, *iter->pos);
context.insert(concatStrings("!", name, "!", i.name));
context.insert("!" + name + "!" + string(i.name));
}
}
}

View File

@@ -12,7 +12,7 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar
std::string url;
std::optional<Hash> rev;
std::optional<std::string> ref;
std::string_view name = "source";
std::string name = "source";
PathSet context;
state.forceValue(*args[0], pos);
@@ -22,14 +22,14 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar
state.forceAttrs(*args[0], pos);
for (auto & attr : *args[0]->attrs) {
std::string_view n(attr.name);
string n(attr.name);
if (n == "url")
url = state.coerceToString(*attr.pos, *attr.value, context, false, false).toOwned();
url = state.coerceToString(*attr.pos, *attr.value, context, false, false);
else if (n == "rev") {
// Ugly: unlike fetchGit, here the "rev" attribute can
// be both a revision or a branch/tag name.
auto value = state.forceStringNoCtx(*attr.value, *attr.pos);
if (std::regex_match(value.begin(), value.end(), revRegex))
if (std::regex_match(value, revRegex))
rev = Hash::parseAny(value, htSHA1);
else
ref = value;
@@ -50,7 +50,7 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar
});
} else
url = state.coerceToString(pos, *args[0], context, false, false).toOwned();
url = state.coerceToString(pos, *args[0], context, false, false);
// FIXME: git externals probably can be used to bypass the URI
// whitelist. Ah well.
@@ -62,7 +62,7 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar
fetchers::Attrs attrs;
attrs.insert_or_assign("type", "hg");
attrs.insert_or_assign("url", url.find("://") != std::string::npos ? url : "file://" + url);
attrs.insert_or_assign("name", string(name));
attrs.insert_or_assign("name", name);
if (ref) attrs.insert_or_assign("ref", *ref);
if (rev) attrs.insert_or_assign("rev", rev->gitRev());
auto input = fetchers::Input::fromAttrs(std::move(attrs));

View File

@@ -125,7 +125,7 @@ static void fetchTree(
if (attr.name == state.sType) continue;
state.forceValue(*attr.value, *attr.pos);
if (attr.value->type() == nPath || attr.value->type() == nString) {
auto s = state.coerceToString(*attr.pos, *attr.value, context, false, false).toOwned();
auto s = state.coerceToString(*attr.pos, *attr.value, context, false, false);
attrs.emplace(attr.name,
attr.name == "url"
? type == "git"
@@ -151,7 +151,7 @@ static void fetchTree(
input = fetchers::Input::fromAttrs(std::move(attrs));
} else {
auto url = state.coerceToString(pos, *args[0], context, false, false).toOwned();
auto url = state.coerceToString(pos, *args[0], context, false, false);
if (type == "git") {
fetchers::Attrs attrs;

View File

@@ -9,7 +9,7 @@ static void prim_fromTOML(EvalState & state, const Pos & pos, Value * * args, Va
{
auto toml = state.forceStringNoCtx(*args[0], pos);
std::istringstream tomlStream(string{toml});
std::istringstream tomlStream(toml);
std::function<void(Value &, toml::value)> visit;

View File

@@ -17,8 +17,8 @@ namespace nix {
class Symbol
{
private:
const std::string * s; // pointer into SymbolTable
Symbol(const std::string * s) : s(s) { };
const string * s; // pointer into SymbolTable
Symbol(const string * s) : s(s) { };
friend class SymbolTable;
public:
@@ -72,7 +72,7 @@ class SymbolTable
{
private:
std::unordered_map<std::string_view, Symbol> symbols;
std::list<std::string> store;
std::list<string> store;
public:
Symbol create(std::string_view s)
@@ -84,7 +84,7 @@ public:
auto it = symbols.find(s);
if (it != symbols.end()) return it->second;
auto & rawSym = store.emplace_back(s);
const string & rawSym = store.emplace_back(s);
return symbols.emplace(rawSym, Symbol(&rawSym)).first->second;
}

View File

@@ -142,7 +142,7 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
if (!v.lambda.fun->arg.empty()) attrs["name"] = v.lambda.fun->arg;
if (v.lambda.fun->formals->ellipsis) attrs["ellipsis"] = "1";
XMLOpenElement _(doc, "attrspat", attrs);
for (auto & i : v.lambda.fun->formals->lexicographicOrder())
for (auto & i : v.lambda.fun->formals->formals)
doc.writeEmptyElement("attr", singletonAttrs("name", i.name));
} else
doc.writeEmptyElement("varpat", singletonAttrs("name", v.lambda.fun->arg));

View File

@@ -77,20 +77,20 @@ class ExternalValueBase
public:
/* Return a simple string describing the type */
virtual std::string showType() const = 0;
virtual string showType() const = 0;
/* Return a string to be used in builtins.typeOf */
virtual std::string typeOf() const = 0;
virtual string typeOf() const = 0;
/* Coerce the value to a string. Defaults to uncoercable, i.e. throws an
* error.
* error
*/
virtual std::string coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore) const;
virtual string coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore) const;
/* Compare to another value of the same type. Defaults to uncomparable,
* i.e. always false.
*/
virtual bool operator ==(const ExternalValueBase & b) const;
virtual bool operator==(const ExternalValueBase & b) const;
/* Print the value as JSON. Defaults to unconvertable, i.e. throws an error */
virtual void printValueAsJSON(EvalState & state, bool strict,
@@ -361,7 +361,7 @@ public:
return internalType == tList1 ? 1 : internalType == tList2 ? 2 : bigList.size;
}
Pos determinePos(const Pos & pos) const;
Pos determinePos(const Pos &pos) const;
/* Check whether forcing this value requires a trivial amount of
computation. In particular, function applications are

View File

@@ -124,13 +124,15 @@ std::pair<Tree, Input> Input::fetch(ref<Store> store) const
debug("using substituted/cached input '%s' in '%s'",
to_string(), store->printStorePath(storePath));
return {Tree { .actualPath = store->toRealPath(storePath), .storePath = std::move(storePath) }, *this};
auto actualPath = store->toRealPath(storePath);
return {fetchers::Tree(std::move(actualPath), std::move(storePath)), *this};
} catch (Error & e) {
debug("substitution of input '%s' failed: %s", to_string(), e.what());
}
}
auto [storePath, input] = [&]() -> std::pair<StorePath, Input> {
auto [tree, input] = [&]() -> std::pair<Tree, Input> {
try {
return scheme->fetch(store, *this);
} catch (Error & e) {
@@ -139,10 +141,8 @@ std::pair<Tree, Input> Input::fetch(ref<Store> store) const
}
}();
Tree tree {
.actualPath = store->toRealPath(storePath),
.storePath = storePath,
};
if (tree.actualPath == "")
tree.actualPath = store->toRealPath(tree.storePath);
auto narHash = store->queryPathInfo(tree.storePath)->narHash;
input.attrs.insert_or_assign("narHash", narHash.to_string(SRI, true));

View File

@@ -16,6 +16,7 @@ struct Tree
{
Path actualPath;
StorePath storePath;
Tree(Path && actualPath, StorePath && storePath) : actualPath(actualPath), storePath(std::move(storePath)) {}
};
struct InputScheme;
@@ -130,7 +131,7 @@ struct InputScheme
virtual void markChangedFile(const Input & input, std::string_view file, std::optional<std::string> commitMsg);
virtual std::pair<StorePath, Input> fetch(ref<Store> store, const Input & input) = 0;
virtual std::pair<Tree, Input> fetch(ref<Store> store, const Input & input) = 0;
};
void registerInputScheme(std::shared_ptr<InputScheme> && fetcher);

View File

@@ -172,7 +172,7 @@ struct GitInputScheme : InputScheme
return {isLocal, isLocal ? url.path : url.base};
}
std::pair<StorePath, Input> fetch(ref<Store> store, const Input & _input) override
std::pair<Tree, Input> fetch(ref<Store> store, const Input & _input) override
{
Input input(_input);
@@ -197,14 +197,17 @@ struct GitInputScheme : InputScheme
};
auto makeResult = [&](const Attrs & infoAttrs, StorePath && storePath)
-> std::pair<StorePath, Input>
-> std::pair<Tree, Input>
{
assert(input.getRev());
assert(!_input.getRev() || _input.getRev() == input.getRev());
if (!shallow)
input.attrs.insert_or_assign("revCount", getIntAttr(infoAttrs, "revCount"));
input.attrs.insert_or_assign("lastModified", getIntAttr(infoAttrs, "lastModified"));
return {std::move(storePath), input};
return {
Tree(store->toRealPath(storePath), std::move(storePath)),
input
};
};
if (input.getRev()) {
@@ -282,7 +285,10 @@ struct GitInputScheme : InputScheme
"lastModified",
haveCommits ? std::stoull(runProgram("git", true, { "-C", actualUrl, "log", "-1", "--format=%ct", "--no-show-signature", "HEAD" })) : 0);
return {std::move(storePath), input};
return {
Tree(store->toRealPath(storePath), std::move(storePath)),
input
};
}
}

View File

@@ -8,7 +8,6 @@
#include <optional>
#include <nlohmann/json.hpp>
#include <fstream>
namespace nix::fetchers {
@@ -18,7 +17,7 @@ struct DownloadUrl
Headers headers;
};
// A github, gitlab, or sourcehut host
// A github or gitlab host
const static std::string hostRegexS = "[a-zA-Z0-9.]*"; // FIXME: check
std::regex hostRegex(hostRegexS, std::regex::ECMAScript);
@@ -181,7 +180,7 @@ struct GitArchiveInputScheme : InputScheme
virtual DownloadUrl getDownloadUrl(const Input & input) const = 0;
std::pair<StorePath, Input> fetch(ref<Store> store, const Input & _input) override
std::pair<Tree, Input> fetch(ref<Store> store, const Input & _input) override
{
Input input(_input);
@@ -200,7 +199,10 @@ struct GitArchiveInputScheme : InputScheme
if (auto res = getCache()->lookup(store, immutableAttrs)) {
input.attrs.insert_or_assign("lastModified", getIntAttr(res->first, "lastModified"));
return {std::move(res->second), input};
return {
Tree(store->toRealPath(res->second), std::move(res->second)),
input
};
}
auto url = getDownloadUrl(input);
@@ -219,7 +221,7 @@ struct GitArchiveInputScheme : InputScheme
tree.storePath,
true);
return {std::move(tree.storePath), input};
return {std::move(tree), input};
}
};
@@ -346,95 +348,7 @@ struct GitLabInputScheme : GitArchiveInputScheme
}
};
struct SourceHutInputScheme : GitArchiveInputScheme
{
std::string type() override { return "sourcehut"; }
std::optional<std::pair<std::string, std::string>> accessHeaderFromToken(const std::string & token) const override
{
// SourceHut supports both PAT and OAuth2. See
// https://man.sr.ht/meta.sr.ht/oauth.md
return std::pair<std::string, std::string>("Authorization", fmt("Bearer %s", token));
// Note: This currently serves no purpose, as this kind of authorization
// does not allow for downloading tarballs on sourcehut private repos.
// Once it is implemented, however, should work as expected.
}
Hash getRevFromRef(nix::ref<Store> store, const Input & input) const override
{
// TODO: In the future, when the sourcehut graphql API is implemented for mercurial
// and with anonymous access, this method should use it instead.
auto ref = *input.getRef();
auto host = maybeGetStrAttr(input.attrs, "host").value_or("git.sr.ht");
auto base_url = fmt("https://%s/%s/%s",
host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo"));
Headers headers = makeHeadersWithAuthTokens(host);
std::string ref_uri;
if (ref == "HEAD") {
auto file = store->toRealPath(
downloadFile(store, fmt("%s/HEAD", base_url), "source", false, headers).storePath);
std::ifstream is(file);
std::string line;
getline(is, line);
auto ref_index = line.find("ref: ");
if (ref_index == std::string::npos) {
throw BadURL("in '%d', couldn't resolve HEAD ref '%d'", input.to_string(), ref);
}
ref_uri = line.substr(ref_index+5, line.length()-1);
} else
ref_uri = fmt("refs/heads/%s", ref);
auto file = store->toRealPath(
downloadFile(store, fmt("%s/info/refs", base_url), "source", false, headers).storePath);
std::ifstream is(file);
std::string line;
std::string id;
while(getline(is, line)) {
auto index = line.find(ref_uri);
if (index != std::string::npos) {
id = line.substr(0, index-1);
break;
}
}
if(id.empty())
throw BadURL("in '%d', couldn't find ref '%d'", input.to_string(), ref);
auto rev = Hash::parseAny(id, htSHA1);
debug("HEAD revision for '%s' is %s", fmt("%s/%s", base_url, ref), rev.gitRev());
return rev;
}
DownloadUrl getDownloadUrl(const Input & input) const override
{
auto host = maybeGetStrAttr(input.attrs, "host").value_or("git.sr.ht");
auto url = fmt("https://%s/%s/%s/archive/%s.tar.gz",
host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo"),
input.getRev()->to_string(Base16, false));
Headers headers = makeHeadersWithAuthTokens(host);
return DownloadUrl { url, headers };
}
void clone(const Input & input, const Path & destDir) override
{
auto host = maybeGetStrAttr(input.attrs, "host").value_or("git.sr.ht");
Input::fromURL(fmt("git+https://%s/%s/%s",
host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo")))
.applyOverrides(input.getRef(), input.getRev())
.clone(destDir);
}
};
static auto rGitHubInputScheme = OnStartup([] { registerInputScheme(std::make_unique<GitHubInputScheme>()); });
static auto rGitLabInputScheme = OnStartup([] { registerInputScheme(std::make_unique<GitLabInputScheme>()); });
static auto rSourceHutInputScheme = OnStartup([] { registerInputScheme(std::make_unique<SourceHutInputScheme>()); });
}

View File

@@ -94,7 +94,7 @@ struct IndirectInputScheme : InputScheme
return input;
}
std::pair<StorePath, Input> fetch(ref<Store> store, const Input & input) override
std::pair<Tree, Input> fetch(ref<Store> store, const Input & input) override
{
throw Error("indirect input '%s' cannot be fetched directly", input.to_string());
}

View File

@@ -143,7 +143,7 @@ struct MercurialInputScheme : InputScheme
return {isLocal, isLocal ? url.path : url.base};
}
std::pair<StorePath, Input> fetch(ref<Store> store, const Input & _input) override
std::pair<Tree, Input> fetch(ref<Store> store, const Input & _input) override
{
Input input(_input);
@@ -193,7 +193,10 @@ struct MercurialInputScheme : InputScheme
auto storePath = store->addToStore(input.getName(), actualUrl, FileIngestionMethod::Recursive, htSHA256, filter);
return {std::move(storePath), input};
return {
Tree(store->toRealPath(storePath), std::move(storePath)),
input
};
}
}
@@ -209,12 +212,15 @@ struct MercurialInputScheme : InputScheme
};
auto makeResult = [&](const Attrs & infoAttrs, StorePath && storePath)
-> std::pair<StorePath, Input>
-> std::pair<Tree, Input>
{
assert(input.getRev());
assert(!_input.getRev() || _input.getRev() == input.getRev());
input.attrs.insert_or_assign("revCount", getIntAttr(infoAttrs, "revCount"));
return {std::move(storePath), input};
return {
Tree(store->toRealPath(storePath), std::move(storePath)),
input
};
};
if (input.getRev()) {

View File

@@ -80,7 +80,7 @@ struct PathInputScheme : InputScheme
// nothing to do
}
std::pair<StorePath, Input> fetch(ref<Store> store, const Input & input) override
std::pair<Tree, Input> fetch(ref<Store> store, const Input & input) override
{
std::string absPath;
auto path = getStrAttr(input.attrs, "path");
@@ -115,7 +115,10 @@ struct PathInputScheme : InputScheme
// FIXME: try to substitute storePath.
storePath = store->addToStore("source", absPath);
return {std::move(*storePath), input};
return {
Tree(store->toRealPath(*storePath), std::move(*storePath)),
input
};
}
};

View File

@@ -126,7 +126,7 @@ std::pair<Tree, time_t> downloadTarball(
if (cached && !cached->expired)
return {
Tree { .actualPath = store->toRealPath(cached->storePath), .storePath = std::move(cached->storePath) },
Tree(store->toRealPath(cached->storePath), std::move(cached->storePath)),
getIntAttr(cached->infoAttrs, "lastModified")
};
@@ -163,7 +163,7 @@ std::pair<Tree, time_t> downloadTarball(
immutable);
return {
Tree { .actualPath = store->toRealPath(*unpackedStorePath), .storePath = std::move(*unpackedStorePath) },
Tree(store->toRealPath(*unpackedStorePath), std::move(*unpackedStorePath)),
lastModified,
};
}
@@ -225,10 +225,10 @@ struct TarballInputScheme : InputScheme
return true;
}
std::pair<StorePath, Input> fetch(ref<Store> store, const Input & input) override
std::pair<Tree, Input> fetch(ref<Store> store, const Input & input) override
{
auto tree = downloadTarball(store, getStrAttr(input.attrs, "url"), input.getName(), false).first;
return {std::move(tree.storePath), input};
return {std::move(tree), input};
}
};

View File

@@ -307,7 +307,7 @@ void BinaryCacheStore::addToStore(const ValidPathInfo & info, Source & narSource
}});
}
StorePath BinaryCacheStore::addToStoreFromDump(Source & dump, std::string_view name,
StorePath BinaryCacheStore::addToStoreFromDump(Source & dump, const string & name,
FileIngestionMethod method, HashType hashAlgo, RepairFlag repair, const StorePathSet & references)
{
if (method != FileIngestionMethod::Recursive || hashAlgo != htSHA256)

View File

@@ -98,8 +98,8 @@ public:
void addToStore(const ValidPathInfo & info, Source & narSource,
RepairFlag repair, CheckSigsFlag checkSigs) override;
StorePath addToStoreFromDump(Source & dump, std::string_view name,
FileIngestionMethod method, HashType hashAlgo, RepairFlag repair, const StorePathSet & references) override;
StorePath addToStoreFromDump(Source & dump, const string & name,
FileIngestionMethod method, HashType hashAlgo, RepairFlag repair, const StorePathSet & references ) override;
StorePath addToStore(const string & name, const Path & srcPath,
FileIngestionMethod method, HashType hashAlgo,

View File

@@ -1091,7 +1091,7 @@ HookReply DerivationGoal::tryBuildHook()
/* Create the log file and pipe. */
Path logFile = openLogFile();
std::set<int> fds;
set<int> fds;
fds.insert(hook->fromHook.readSide.get());
fds.insert(hook->builderOut.readSide.get());
worker.childStarted(shared_from_this(), fds, false, false);

View File

@@ -18,8 +18,8 @@ struct CompareGoalPtrs {
};
/* Set of goals. */
typedef std::set<GoalPtr, CompareGoalPtrs> Goals;
typedef std::set<WeakGoalPtr, std::owner_less<WeakGoalPtr>> WeakGoals;
typedef set<GoalPtr, CompareGoalPtrs> Goals;
typedef set<WeakGoalPtr, std::owner_less<WeakGoalPtr>> WeakGoals;
/* A map of paths to goals (and the other way around). */
typedef std::map<StorePath, WeakGoalPtr> WeakGoalMap;

View File

@@ -912,12 +912,9 @@ void LocalDerivationGoal::startBuilder()
sandboxMountNamespace = open(fmt("/proc/%d/ns/mnt", (pid_t) pid).c_str(), O_RDONLY);
if (sandboxMountNamespace.get() == -1)
throw SysError("getting sandbox mount namespace");
if (usingUserNamespace) {
sandboxUserNamespace = open(fmt("/proc/%d/ns/user", (pid_t) pid).c_str(), O_RDONLY);
if (sandboxUserNamespace.get() == -1)
throw SysError("getting sandbox user namespace");
}
sandboxUserNamespace = open(fmt("/proc/%d/ns/user", (pid_t) pid).c_str(), O_RDONLY);
if (sandboxUserNamespace.get() == -1)
throw SysError("getting sandbox user namespace");
/* Signal the builder that we've updated its user namespace. */
writeFull(userNamespaceSync.writeSide.get(), "1");
@@ -1208,7 +1205,7 @@ struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual Lo
return path;
}
StorePath addToStoreFromDump(Source & dump, std::string_view name,
StorePath addToStoreFromDump(Source & dump, const string & name,
FileIngestionMethod method = FileIngestionMethod::Recursive, HashType hashAlgo = htSHA256, RepairFlag repair = NoRepair,
const StorePathSet & references = StorePathSet()) override
{

View File

@@ -161,7 +161,7 @@ unsigned Worker::getNrLocalBuilds()
}
void Worker::childStarted(GoalPtr goal, const std::set<int> & fds,
void Worker::childStarted(GoalPtr goal, const set<int> & fds,
bool inBuildSlot, bool respectTimeouts)
{
Child child;
@@ -377,7 +377,7 @@ void Worker::waitForInput()
GoalPtr goal = j->goal.lock();
assert(goal);
std::set<int> fds2(j->fds);
set<int> fds2(j->fds);
std::vector<unsigned char> buffer(4096);
for (auto & k : fds2) {
if (pollStatus.at(fdToPollStatus.at(k)).revents) {

View File

@@ -38,7 +38,7 @@ struct Child
{
WeakGoalPtr goal;
Goal * goal2; // ugly hackery
std::set<int> fds;
set<int> fds;
bool respectTimeouts;
bool inBuildSlot;
steady_time_point lastOutput; /* time we last got output on stdout/stderr */
@@ -167,7 +167,7 @@ public:
/* Registers a running child process. `inBuildSlot' means that
the process counts towards the jobs limit. */
void childStarted(GoalPtr goal, const std::set<int> & fds,
void childStarted(GoalPtr goal, const set<int> & fds,
bool inBuildSlot, bool respectTimeouts);
/* Unregisters a running child process. `wakeSleepers' should be

View File

@@ -981,11 +981,7 @@ void processConnection(
readInt(from);
}
if (GET_PROTOCOL_MINOR(clientVersion) >= 11)
readInt(from); // obsolete reserveSpace
if (GET_PROTOCOL_MINOR(clientVersion) >= 33)
to << nixVersion;
readInt(from); // obsolete reserveSpace
/* Send startup error messages to the client. */
tunnelLogger->startWork();

View File

@@ -699,10 +699,10 @@ void writeDerivation(Sink & out, const Store & store, const BasicDerivation & dr
}
std::string hashPlaceholder(const std::string_view outputName)
std::string hashPlaceholder(const std::string & outputName)
{
// FIXME: memoize?
return "/" + hashString(htSHA256, concatStrings("nix-output:", outputName)).to_string(Base32, false);
return "/" + hashString(htSHA256, "nix-output:" + outputName).to_string(Base32, false);
}
std::string downstreamPlaceholder(const Store & store, const StorePath & drvPath, std::string_view outputName)

View File

@@ -236,7 +236,7 @@ void writeDerivation(Sink & out, const Store & store, const BasicDerivation & dr
It is used as a placeholder to allow derivations to refer to their
own outputs without needing to use the hash of a derivation in
itself, making the hash near-impossible to calculate. */
std::string hashPlaceholder(const std::string_view outputName);
std::string hashPlaceholder(const std::string & outputName);
/* This creates an opaque and almost certainly unique string
deterministically from a derivation path and output name.

View File

@@ -128,7 +128,7 @@ struct curlFileTransfer : public FileTransfer
if (requestHeaders) curl_slist_free_all(requestHeaders);
try {
if (!done)
fail(FileTransferError(Interrupted, {}, "download of '%s' was interrupted", request.uri));
fail(FileTransferError(Interrupted, nullptr, "download of '%s' was interrupted", request.uri));
} catch (...) {
ignoreException();
}
@@ -704,7 +704,7 @@ struct curlFileTransfer : public FileTransfer
auto s3Res = s3Helper.getObject(bucketName, key);
FileTransferResult res;
if (!s3Res.data)
throw FileTransferError(NotFound, {}, "S3 object '%s' does not exist", request.uri);
throw FileTransferError(NotFound, nullptr, "S3 object '%s' does not exist", request.uri);
res.data = std::move(*s3Res.data);
callback(std::move(res));
#else

View File

@@ -970,7 +970,7 @@ public:
Setting<std::string> commitLockFileSummary{
this, "", "commit-lockfile-summary",
R"(
The commit summary to use when committing changed flake lock files. If
The commit summary to use when commiting changed flake lock files. If
empty, the summary is generated based on the action performed.
)"};
};

View File

@@ -1318,7 +1318,7 @@ void LocalStore::addToStore(const ValidPathInfo & info, Source & source,
}
StorePath LocalStore::addToStoreFromDump(Source & source0, std::string_view name,
StorePath LocalStore::addToStoreFromDump(Source & source0, const string & name,
FileIngestionMethod method, HashType hashAlgo, RepairFlag repair, const StorePathSet & references)
{
/* For computing the store path. */
@@ -1919,10 +1919,4 @@ void LocalStore::addBuildLog(const StorePath & drvPath, std::string_view log)
throw SysError("renaming '%1%' to '%2%'", tmpFile, logPath);
}
std::optional<std::string> LocalStore::getVersion()
{
return nixVersion;
}
} // namespace nix

View File

@@ -144,7 +144,7 @@ public:
void addToStore(const ValidPathInfo & info, Source & source,
RepairFlag repair, CheckSigsFlag checkSigs) override;
StorePath addToStoreFromDump(Source & dump, std::string_view name,
StorePath addToStoreFromDump(Source & dump, const string & name,
FileIngestionMethod method, HashType hashAlgo, RepairFlag repair, const StorePathSet & references) override;
StorePath addTextToStore(const string & name, const string & s,
@@ -211,8 +211,6 @@ public:
void queryRealisationUncached(const DrvOutput&,
Callback<std::shared_ptr<const Realisation>> callback) noexcept override;
std::optional<std::string> getVersion() override;
private:
int getSchema();
@@ -292,7 +290,7 @@ private:
typedef std::pair<dev_t, ino_t> Inode;
typedef std::set<Inode> InodesSeen;
typedef set<Inode> InodesSeen;
/* "Fix", or canonicalise, the meta-data of the files in a store path

View File

@@ -8,19 +8,19 @@ class Store;
struct Machine {
const std::string storeUri;
const std::vector<std::string> systemTypes;
const std::string sshKey;
const string storeUri;
const std::vector<string> systemTypes;
const string sshKey;
const unsigned int maxJobs;
const unsigned int speedFactor;
const std::set<std::string> supportedFeatures;
const std::set<std::string> mandatoryFeatures;
const std::set<string> supportedFeatures;
const std::set<string> mandatoryFeatures;
const std::string sshPublicHostKey;
bool enabled = true;
bool allSupported(const std::set<std::string> & features) const;
bool allSupported(const std::set<string> & features) const;
bool mandatoryMet(const std::set<std::string> & features) const;
bool mandatoryMet(const std::set<string> & features) const;
Machine(decltype(storeUri) storeUri,
decltype(systemTypes) systemTypes,

View File

@@ -56,8 +56,8 @@ bool DrvName::matches(const DrvName & n)
}
std::string_view nextComponent(std::string_view::const_iterator & p,
const std::string_view::const_iterator end)
string nextComponent(string::const_iterator & p,
const string::const_iterator end)
{
/* Skip any dots and dashes (component separators). */
while (p != end && (*p == '.' || *p == '-')) ++p;
@@ -67,18 +67,18 @@ std::string_view nextComponent(std::string_view::const_iterator & p,
/* If the first character is a digit, consume the longest sequence
of digits. Otherwise, consume the longest sequence of
non-digit, non-separator characters. */
auto s = p;
string s;
if (isdigit(*p))
while (p != end && isdigit(*p)) p++;
while (p != end && isdigit(*p)) s += *p++;
else
while (p != end && (!isdigit(*p) && *p != '.' && *p != '-'))
p++;
s += *p++;
return {s, size_t(p - s)};
return s;
}
static bool componentsLT(const std::string_view c1, const std::string_view c2)
static bool componentsLT(const string & c1, const string & c2)
{
auto n1 = string2Int<int>(c1);
auto n2 = string2Int<int>(c2);
@@ -94,14 +94,14 @@ static bool componentsLT(const std::string_view c1, const std::string_view c2)
}
int compareVersions(const std::string_view v1, const std::string_view v2)
int compareVersions(const string & v1, const string & v2)
{
auto p1 = v1.begin();
auto p2 = v2.begin();
string::const_iterator p1 = v1.begin();
string::const_iterator p2 = v2.begin();
while (p1 != v1.end() || p2 != v2.end()) {
auto c1 = nextComponent(p1, v1.end());
auto c2 = nextComponent(p2, v2.end());
string c1 = nextComponent(p1, v1.end());
string c2 = nextComponent(p2, v2.end());
if (componentsLT(c1, c2)) return -1;
else if (componentsLT(c2, c1)) return 1;
}

View File

@@ -10,9 +10,9 @@ struct Regex;
struct DrvName
{
std::string fullName;
std::string name;
std::string version;
string fullName;
string name;
string version;
unsigned int hits;
DrvName();
@@ -25,11 +25,11 @@ private:
std::unique_ptr<Regex> regex;
};
typedef std::list<DrvName> DrvNames;
typedef list<DrvName> DrvNames;
std::string_view nextComponent(std::string_view::const_iterator & p,
const std::string_view::const_iterator end);
int compareVersions(const std::string_view v1, const std::string_view v2);
string nextComponent(string::const_iterator & p,
const string::const_iterator end);
int compareVersions(const string & v1, const string & v2);
DrvNames drvNamesFromArgs(const Strings & opArgs);
}

View File

@@ -26,7 +26,7 @@ static void makeWritable(const Path & path)
struct MakeReadOnly
{
Path path;
MakeReadOnly(const PathView path) : path(path) { }
MakeReadOnly(const Path & path) : path(path) { }
~MakeReadOnly()
{
try {
@@ -205,13 +205,12 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats,
/* Make the containing directory writable, but only if it's not
the store itself (we don't want or need to mess with its
permissions). */
const Path dirOfPath(dirOf(path));
bool mustToggle = dirOfPath != realStoreDir.get();
if (mustToggle) makeWritable(dirOfPath);
bool mustToggle = dirOf(path) != realStoreDir.get();
if (mustToggle) makeWritable(dirOf(path));
/* When we're done, make the directory read-only again and reset
its timestamp back to 0. */
MakeReadOnly makeReadOnly(mustToggle ? dirOfPath : "");
MakeReadOnly makeReadOnly(mustToggle ? dirOf(path) : "");
Path tempLink = (format("%1%/.tmp-link-%2%-%3%")
% realStoreDir % getpid() % random()).str();

View File

@@ -170,7 +170,7 @@ std::string writeStructuredAttrsShell(const nlohmann::json & json)
auto handleSimpleType = [](const nlohmann::json & value) -> std::optional<std::string> {
if (value.is_string())
return shellEscape(value.get<std::string_view>());
return shellEscape(value);
if (value.is_number()) {
auto f = value.get<float>();

View File

@@ -20,7 +20,7 @@ class PathLocks
{
private:
typedef std::pair<int, Path> FDPair;
std::list<FDPair> fds;
list<FDPair> fds;
bool deletePaths;
public:

View File

@@ -188,12 +188,7 @@ void RemoteStore::initConnection(Connection & conn)
}
if (GET_PROTOCOL_MINOR(conn.daemonVersion) >= 11)
conn.to << false; // obsolete reserveSpace
if (GET_PROTOCOL_MINOR(conn.daemonVersion) >= 33) {
conn.to.flush();
conn.daemonNixVersion = readString(conn.from);
}
conn.to << false;
auto ex = conn.processStderr();
if (ex) std::rethrow_exception(ex);
@@ -500,7 +495,7 @@ std::optional<StorePath> RemoteStore::queryPathFromHashPart(const std::string &
ref<const ValidPathInfo> RemoteStore::addCAToStore(
Source & dump,
std::string_view name,
const string & name,
ContentAddressMethod caMethod,
const StorePathSet & references,
RepairFlag repair)
@@ -582,7 +577,7 @@ ref<const ValidPathInfo> RemoteStore::addCAToStore(
}
StorePath RemoteStore::addToStoreFromDump(Source & dump, std::string_view name,
StorePath RemoteStore::addToStoreFromDump(Source & dump, const string & name,
FileIngestionMethod method, HashType hashType, RepairFlag repair, const StorePathSet & references)
{
return addCAToStore(dump, name, FixedOutputHashMethod{ .fileIngestionMethod = method, .hashType = hashType }, references, repair)->path;
@@ -925,13 +920,6 @@ void RemoteStore::addBuildLog(const StorePath & drvPath, std::string_view log)
}
std::optional<std::string> RemoteStore::getVersion()
{
auto conn(getConnection());
return conn->daemonNixVersion;
}
void RemoteStore::connect()
{
auto conn(getConnection());

View File

@@ -66,13 +66,13 @@ public:
/* Add a content-addressable store path. `dump` will be drained. */
ref<const ValidPathInfo> addCAToStore(
Source & dump,
std::string_view name,
const string & name,
ContentAddressMethod caMethod,
const StorePathSet & references,
RepairFlag repair);
/* Add a content-addressable store path. Does not support references. `dump` will be drained. */
StorePath addToStoreFromDump(Source & dump, std::string_view name,
StorePath addToStoreFromDump(Source & dump, const string & name,
FileIngestionMethod method = FileIngestionMethod::Recursive, HashType hashAlgo = htSHA256, RepairFlag repair = NoRepair, const StorePathSet & references = StorePathSet()) override;
void addToStore(const ValidPathInfo & info, Source & nar,
@@ -118,8 +118,6 @@ public:
void addBuildLog(const StorePath & drvPath, std::string_view log) override;
std::optional<std::string> getVersion() override;
void connect() override;
unsigned int getProtocol() override;
@@ -131,7 +129,6 @@ public:
FdSink to;
FdSource from;
unsigned int daemonVersion;
std::optional<std::string> daemonNixVersion;
std::chrono::time_point<std::chrono::steady_clock> startTime;
virtual ~Connection();

View File

@@ -761,11 +761,11 @@ void Store::pathInfoToJSON(JSONPlaceholder & jsonOut, const StorePathSet & store
for (auto & storePath : storePaths) {
auto jsonPath = jsonList.object();
jsonPath.attr("path", printStorePath(storePath));
try {
auto info = queryPathInfo(storePath);
jsonPath.attr("path", printStorePath(info->path));
jsonPath
.attr("narHash", info->narHash.to_string(hashBase, true))
.attr("narSize", info->narSize);
@@ -819,7 +819,6 @@ void Store::pathInfoToJSON(JSONPlaceholder & jsonOut, const StorePathSet & store
}
} catch (InvalidPath &) {
jsonPath.attr("path", printStorePath(storePath));
jsonPath.attr("valid", false);
}
}

View File

@@ -499,7 +499,7 @@ public:
false).
`dump` may be drained */
// FIXME: remove?
virtual StorePath addToStoreFromDump(Source & dump, std::string_view name,
virtual StorePath addToStoreFromDump(Source & dump, const string & name,
FileIngestionMethod method = FileIngestionMethod::Recursive, HashType hashAlgo = htSHA256, RepairFlag repair = NoRepair,
const StorePathSet & references = StorePathSet())
{ unsupported("addToStoreFromDump"); }
@@ -765,9 +765,6 @@ public:
* (a no-op when theres no daemon)
*/
virtual void setOptions() { }
virtual std::optional<std::string> getVersion() { return {}; }
protected:
Stats stats;

View File

@@ -9,7 +9,7 @@ namespace nix {
#define WORKER_MAGIC_1 0x6e697863
#define WORKER_MAGIC_2 0x6478696f
#define PROTOCOL_VERSION (1 << 8 | 33)
#define PROTOCOL_VERSION (1 << 8 | 32)
#define GET_PROTOCOL_MAJOR(x) ((x) & 0xff00)
#define GET_PROTOCOL_MINOR(x) ((x) & 0x00ff)

View File

@@ -90,7 +90,7 @@ void AbstractConfig::applyConfig(const std::string & contents, const std::string
if (hash != string::npos)
line = string(line, 0, hash);
auto tokens = tokenizeString<std::vector<string>>(line);
vector<string> tokens = tokenizeString<vector<string> >(line);
if (tokens.empty()) continue;
if (tokens.size() < 2)
@@ -122,7 +122,7 @@ void AbstractConfig::applyConfig(const std::string & contents, const std::string
string name = tokens[0];
auto i = tokens.begin();
vector<string>::iterator i = tokens.begin();
advance(i, 2);
set(name, concatStringsSep(" ", Strings(i, tokens.end()))); // FIXME: slow

View File

@@ -259,7 +259,7 @@ Hash::Hash(std::string_view rest, HashType type, bool isSRI)
throw BadHash("hash '%s' has wrong length for hash type '%s'", rest, printHashType(this->type));
}
Hash newHashAllowEmpty(std::string_view hashStr, std::optional<HashType> ht)
Hash newHashAllowEmpty(std::string hashStr, std::optional<HashType> ht)
{
if (hashStr.empty()) {
if (!ht)

View File

@@ -107,7 +107,7 @@ public:
};
/* Helper that defaults empty hashes to the 0 hash. */
Hash newHashAllowEmpty(std::string_view hashStr, std::optional<HashType> ht);
Hash newHashAllowEmpty(std::string hashStr, std::optional<HashType> ht);
/* Print a hash in base-16 if it's MD5, or base-32 otherwise. */
string printHash16or32(const Hash & hash);

24
src/libutil/rust-ffi.cc Normal file
View File

@@ -0,0 +1,24 @@
#if 0
#include "logging.hh"
#include "rust-ffi.hh"
extern "C" std::exception_ptr * make_error(rust::StringSlice s)
{
return new std::exception_ptr(std::make_exception_ptr(nix::Error(std::string(s.ptr, s.size))));
}
extern "C" void destroy_error(std::exception_ptr * ex)
{
free(ex);
}
namespace rust {
std::ostream & operator << (std::ostream & str, const String & s)
{
str << (std::string_view) s;
return str;
}
}
#endif

189
src/libutil/rust-ffi.hh Normal file
View File

@@ -0,0 +1,189 @@
#pragma once
#if 0
#include "serialise.hh"
#include <string_view>
#include <cstring>
#include <array>
namespace rust {
typedef void (*DropFun)(void *);
/* A Rust value of N bytes. It can be moved but not copied. When it
goes out of scope, the C++ destructor will run the drop
function. */
template<std::size_t N, DropFun drop>
struct Value
{
protected:
std::array<char, N> raw;
~Value()
{
if (!isEvacuated()) {
drop(this);
evacuate();
}
}
// Must not be called directly.
Value()
{ }
Value(Value && other)
: raw(other.raw)
{
other.evacuate();
}
void operator =(Value && other)
{
if (!isEvacuated())
drop(this);
raw = other.raw;
other.evacuate();
}
private:
/* FIXME: optimize these (ideally in such a way that the compiler
can elide most calls to evacuate() / isEvacuated(). */
inline void evacuate()
{
for (auto & i : raw) i = 0;
}
inline bool isEvacuated()
{
for (auto & i : raw)
if (i != 0) return false;
return true;
}
};
/* A Rust vector. */
template<typename T, DropFun drop>
struct Vec : Value<3 * sizeof(void *), drop>
{
inline size_t size() const
{
return ((const size_t *) &this->raw)[2];
}
const T * data() const
{
return ((const T * *) &this->raw)[0];
}
};
/* A Rust slice. */
template<typename T>
struct Slice
{
const T * ptr;
size_t size;
Slice(const T * ptr, size_t size) : ptr(ptr), size(size)
{
assert(ptr);
}
};
struct StringSlice : Slice<char>
{
StringSlice(const std::string & s): Slice(s.data(), s.size()) {}
explicit StringSlice(std::string_view s): Slice(s.data(), s.size()) {}
StringSlice(const char * s): Slice(s, strlen(s)) {}
operator std::string_view() const
{
return std::string_view(ptr, size);
}
};
/* A Rust string. */
struct String;
extern "C" {
void ffi_String_new(StringSlice s, String * out);
void ffi_String_drop(void * s);
}
struct String : Vec<char, ffi_String_drop>
{
String() = delete;
String(std::string_view s)
{
ffi_String_new(StringSlice(s), this);
}
String(const char * s)
: String({s, std::strlen(s)})
{
}
operator std::string_view() const
{
return std::string_view(data(), size());
}
};
std::ostream & operator << (std::ostream & str, const String & s);
/* C++ representation of Rust's Result<T, CppException>. */
template<typename T>
struct Result
{
enum { Ok = 0, Err = 1, Uninit = 2 } tag;
union {
T data;
std::exception_ptr * exc;
};
Result() : tag(Uninit) { }; // FIXME: remove
Result(const Result &) = delete;
Result(Result && other)
: tag(other.tag)
{
other.tag = Uninit;
if (tag == Ok)
data = std::move(other.data);
else if (tag == Err)
exc = other.exc;
}
~Result()
{
if (tag == Ok)
data.~T();
else if (tag == Err)
free(exc);
else if (tag == Uninit)
;
else
abort();
}
/* Rethrow the wrapped exception or return the wrapped value. */
T unwrap()
{
if (tag == Ok) {
tag = Uninit;
return std::move(data);
}
else if (tag == Err)
std::rethrow_exception(*exc);
else
abort();
}
};
}
#endif

View File

@@ -6,22 +6,27 @@
#include <set>
#include <string>
#include <map>
#include <variant>
#include <vector>
namespace nix {
typedef std::list<std::string> Strings;
typedef std::set<std::string> StringSet;
typedef std::map<std::string, std::string> StringMap;
using std::list;
using std::set;
using std::vector;
using std::string;
typedef list<string> Strings;
typedef set<string> StringSet;
typedef std::map<string, string> StringMap;
/* Paths are just strings. */
typedef std::string Path;
typedef std::string_view PathView;
typedef std::list<Path> Paths;
typedef std::set<Path> PathSet;
typedef std::vector<std::pair<std::string, std::string>> Headers;
typedef string Path;
typedef std::string_view PathView;
typedef list<Path> Paths;
typedef set<Path> PathSet;
typedef vector<std::pair<string, string>> Headers;
/* Helper class to run code at startup. */
template<typename T>
@@ -42,63 +47,4 @@ struct Explicit {
}
};
/* This wants to be a little bit like rust's Cow type.
Some parts of the evaluator benefit greatly from being able to reuse
existing allocations for strings, but have to be able to also use
newly allocated storage for values.
We do not define implicit conversions, even with ref qualifiers,
since those can easily become ambiguous to the reader and can degrade
into copying behaviour we want to avoid. */
class BackedStringView {
private:
std::variant<std::string, std::string_view> data;
/* Needed to introduce a temporary since operator-> must return
a pointer. Without this we'd need to store the view object
even when we already own a string. */
class Ptr {
private:
std::string_view view;
public:
Ptr(std::string_view view): view(view) {}
const std::string_view * operator->() const { return &view; }
};
public:
BackedStringView(std::string && s): data(std::move(s)) {}
BackedStringView(std::string_view sv): data(sv) {}
template<size_t N>
BackedStringView(const char (& lit)[N]): data(std::string_view(lit)) {}
BackedStringView(const BackedStringView &) = delete;
BackedStringView & operator=(const BackedStringView &) = delete;
/* We only want move operations defined since the sole purpose of
this type is to avoid copies. */
BackedStringView(BackedStringView && other) = default;
BackedStringView & operator=(BackedStringView && other) = default;
bool isOwned() const
{
return std::holds_alternative<std::string>(data);
}
std::string toOwned() &&
{
return isOwned()
? std::move(std::get<std::string>(data))
: std::string(std::get<std::string_view>(data));
}
std::string_view operator*() const
{
return isOwned()
? std::get<std::string>(data)
: std::get<std::string_view>(data);
}
Ptr operator->() const { return Ptr(**this); }
};
}

View File

@@ -81,7 +81,7 @@ void replaceEnv(std::map<std::string, std::string> newEnv)
}
Path absPath(Path path, std::optional<PathView> dir, bool resolveSymlinks)
Path absPath(Path path, std::optional<Path> dir, bool resolveSymlinks)
{
if (path[0] != '/') {
if (!dir) {
@@ -95,12 +95,12 @@ Path absPath(Path path, std::optional<PathView> dir, bool resolveSymlinks)
if (!getcwd(buf, sizeof(buf)))
#endif
throw SysError("cannot get cwd");
path = concatStrings(buf, "/", path);
dir = buf;
#ifdef __GNU__
free(buf);
#endif
} else
path = concatStrings(*dir, "/", path);
}
path = *dir + "/" + path;
}
return canonPath(path, resolveSymlinks);
}
@@ -147,7 +147,7 @@ Path canonPath(PathView path, bool resolveSymlinks)
path = {};
} else {
s += path.substr(0, slash);
path = path.substr(slash);
path = path.substr(slash + 1);
}
/* If s points to a symlink, resolve it and continue from there */
@@ -172,7 +172,7 @@ Path canonPath(PathView path, bool resolveSymlinks)
}
Path dirOf(const PathView path)
Path dirOf(const Path & path)
{
Path::size_type pos = path.rfind('/');
if (pos == string::npos)
@@ -1174,7 +1174,7 @@ void runProgram2(const RunOptions & options)
}
void closeMostFDs(const std::set<int> & exceptions)
void closeMostFDs(const set<int> & exceptions)
{
#if __linux__
try {
@@ -1250,7 +1250,7 @@ template<class C> C tokenizeString(std::string_view s, std::string_view separato
template Strings tokenizeString(std::string_view s, std::string_view separators);
template StringSet tokenizeString(std::string_view s, std::string_view separators);
template std::vector<string> tokenizeString(std::string_view s, std::string_view separators);
template vector<string> tokenizeString(std::string_view s, std::string_view separators);
string chomp(std::string_view s)
@@ -1344,11 +1344,9 @@ std::string toLower(const std::string & s)
}
std::string shellEscape(const std::string_view s)
std::string shellEscape(const std::string & s)
{
std::string r;
r.reserve(s.size() + 2);
r += "'";
std::string r = "'";
for (auto & i : s)
if (i == '\'') r += "'\\''"; else r += i;
r += '\'';
@@ -1358,15 +1356,11 @@ std::string shellEscape(const std::string_view s)
void ignoreException()
{
/* Make sure no exceptions leave this function.
printError() also throws when remote is closed. */
try {
try {
throw;
} catch (std::exception & e) {
printError("error (ignored): %1%", e.what());
}
} catch (...) { }
throw;
} catch (std::exception & e) {
printError("error (ignored): %1%", e.what());
}
}
bool shouldANSI()
@@ -1565,22 +1559,7 @@ std::pair<unsigned short, unsigned short> getWindowSize()
}
/* We keep track of interrupt callbacks using integer tokens, so we can iterate
safely without having to lock the data structure while executing arbitrary
functions.
*/
struct InterruptCallbacks {
typedef int64_t Token;
/* We use unique tokens so that we can't accidentally delete the wrong
handler because of an erroneous double delete. */
Token nextToken = 0;
/* Used as a list, see InterruptCallbacks comment. */
std::map<Token, std::function<void()>> callbacks;
};
static Sync<InterruptCallbacks> _interruptCallbacks;
static Sync<std::list<std::function<void()>>> _interruptCallbacks;
static void signalHandlerThread(sigset_t set)
{
@@ -1602,19 +1581,8 @@ void triggerInterrupt()
_isInterrupted = true;
{
InterruptCallbacks::Token i = 0;
while (true) {
std::function<void()> callback;
{
auto interruptCallbacks(_interruptCallbacks.lock());
auto lb = interruptCallbacks->callbacks.lower_bound(i);
if (lb == interruptCallbacks->callbacks.end())
break;
callback = lb->second;
i = lb->first + 1;
}
auto interruptCallbacks(_interruptCallbacks.lock());
for (auto & callback : *interruptCallbacks) {
try {
callback();
} catch (...) {
@@ -1724,22 +1692,21 @@ void restoreProcessContext(bool restoreMounts)
/* RAII helper to automatically deregister a callback. */
struct InterruptCallbackImpl : InterruptCallback
{
InterruptCallbacks::Token token;
std::list<std::function<void()>>::iterator it;
~InterruptCallbackImpl() override
{
auto interruptCallbacks(_interruptCallbacks.lock());
interruptCallbacks->callbacks.erase(token);
_interruptCallbacks.lock()->erase(it);
}
};
std::unique_ptr<InterruptCallback> createInterruptCallback(std::function<void()> callback)
{
auto interruptCallbacks(_interruptCallbacks.lock());
auto token = interruptCallbacks->nextToken++;
interruptCallbacks->callbacks.emplace(token, callback);
interruptCallbacks->push_back(callback);
auto res = std::make_unique<InterruptCallbackImpl>();
res->token = token;
res->it = interruptCallbacks->end();
res->it--;
return std::unique_ptr<InterruptCallback>(res.release());
}
@@ -1784,7 +1751,7 @@ void bind(int fd, const std::string & path)
if (path.size() + 1 >= sizeof(addr.sun_path)) {
Pid pid = startProcess([&]() {
Path dir = dirOf(path);
auto dir = dirOf(path);
if (chdir(dir.c_str()) == -1)
throw SysError("chdir to '%s' failed", dir);
std::string base(baseNameOf(path));
@@ -1813,7 +1780,7 @@ void connect(int fd, const std::string & path)
if (path.size() + 1 >= sizeof(addr.sun_path)) {
Pid pid = startProcess([&]() {
Path dir = dirOf(path);
auto dir = dirOf(path);
if (chdir(dir.c_str()) == -1)
throw SysError("chdir to '%s' failed", dir);
std::string base(baseNameOf(path));

Some files were not shown because too many files have changed in this diff Show More