forked from lix-project/lix
Merge remote-tracking branch 'upstream/master' into ca-drv-exotic
This commit is contained in:
commit
195daa8299
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -40,6 +40,7 @@ perl/Makefile.config
|
||||||
|
|
||||||
# /src/libstore/
|
# /src/libstore/
|
||||||
*.gen.*
|
*.gen.*
|
||||||
|
/src/libstore/tests/libstore-tests
|
||||||
|
|
||||||
# /src/libutil/
|
# /src/libutil/
|
||||||
/src/libutil/tests/libutil-tests
|
/src/libutil/tests/libutil-tests
|
||||||
|
|
1
Makefile
1
Makefile
|
@ -4,6 +4,7 @@ makefiles = \
|
||||||
src/libutil/local.mk \
|
src/libutil/local.mk \
|
||||||
src/libutil/tests/local.mk \
|
src/libutil/tests/local.mk \
|
||||||
src/libstore/local.mk \
|
src/libstore/local.mk \
|
||||||
|
src/libstore/tests/local.mk \
|
||||||
src/libfetchers/local.mk \
|
src/libfetchers/local.mk \
|
||||||
src/libmain/local.mk \
|
src/libmain/local.mk \
|
||||||
src/libexpr/local.mk \
|
src/libexpr/local.mk \
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
command:
|
{ command, renderLinks ? false }:
|
||||||
|
|
||||||
with builtins;
|
with builtins;
|
||||||
with import ./utils.nix;
|
with import ./utils.nix;
|
||||||
|
@ -20,7 +20,11 @@ let
|
||||||
categories = sort (x: y: x.id < y.id) (unique (map (cmd: cmd.category) (attrValues def.commands)));
|
categories = sort (x: y: x.id < y.id) (unique (map (cmd: cmd.category) (attrValues def.commands)));
|
||||||
listCommands = cmds:
|
listCommands = cmds:
|
||||||
concatStrings (map (name:
|
concatStrings (map (name:
|
||||||
"* [`${command} ${name}`](./${appendName filename name}.md) - ${cmds.${name}.description}\n")
|
"* "
|
||||||
|
+ (if renderLinks
|
||||||
|
then "[`${command} ${name}`](./${appendName filename name}.md)"
|
||||||
|
else "`${command} ${name}`")
|
||||||
|
+ " - ${cmds.${name}.description}\n")
|
||||||
(attrNames cmds));
|
(attrNames cmds));
|
||||||
in
|
in
|
||||||
"where *subcommand* is one of the following:\n\n"
|
"where *subcommand* is one of the following:\n\n"
|
||||||
|
|
|
@ -44,7 +44,7 @@ $(d)/src/SUMMARY.md: $(d)/src/SUMMARY.md.in $(d)/src/command-ref/new-cli
|
||||||
|
|
||||||
$(d)/src/command-ref/new-cli: $(d)/nix.json $(d)/generate-manpage.nix $(bindir)/nix
|
$(d)/src/command-ref/new-cli: $(d)/nix.json $(d)/generate-manpage.nix $(bindir)/nix
|
||||||
@rm -rf $@
|
@rm -rf $@
|
||||||
$(trace-gen) $(nix-eval) --write-to $@ --expr 'import doc/manual/generate-manpage.nix (builtins.readFile $<)'
|
$(trace-gen) $(nix-eval) --write-to $@ --expr 'import doc/manual/generate-manpage.nix { command = builtins.readFile $<; renderLinks = true; }'
|
||||||
|
|
||||||
$(d)/src/command-ref/conf-file.md: $(d)/conf-file.json $(d)/generate-options.nix $(d)/src/command-ref/conf-file-prefix.md $(bindir)/nix
|
$(d)/src/command-ref/conf-file.md: $(d)/conf-file.json $(d)/generate-options.nix $(d)/src/command-ref/conf-file-prefix.md $(bindir)/nix
|
||||||
@cat doc/manual/src/command-ref/conf-file-prefix.md > $@.tmp
|
@cat doc/manual/src/command-ref/conf-file-prefix.md > $@.tmp
|
||||||
|
|
|
@ -40,7 +40,7 @@ export NIX_SSL_CERT_FILE=/etc/ssl/my-certificate-bundle.crt
|
||||||
> **Note**
|
> **Note**
|
||||||
>
|
>
|
||||||
> You must not add the export and then do the install, as the Nix
|
> You must not add the export and then do the install, as the Nix
|
||||||
> installer will detect the presense of Nix configuration, and abort.
|
> installer will detect the presence of Nix configuration, and abort.
|
||||||
|
|
||||||
## `NIX_SSL_CERT_FILE` with macOS and the Nix daemon
|
## `NIX_SSL_CERT_FILE` with macOS and the Nix daemon
|
||||||
|
|
||||||
|
|
2
doc/manual/src/release-notes/rl-2.5.md
Normal file
2
doc/manual/src/release-notes/rl-2.5.md
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
# Release 2.5 (2021-XX-XX)
|
||||||
|
|
|
@ -3,11 +3,11 @@
|
||||||
"lowdown-src": {
|
"lowdown-src": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1632468475,
|
"lastModified": 1633514407,
|
||||||
"narHash": "sha256-NNOm9CbdA8cuwbvaBHslGbPTiU6bh1Ao+MpEPx4rSGo=",
|
"narHash": "sha256-Dw32tiMjdK9t3ETl5fzGrutQTzh2rufgZV4A/BbxuD4=",
|
||||||
"owner": "kristapsdz",
|
"owner": "kristapsdz",
|
||||||
"repo": "lowdown",
|
"repo": "lowdown",
|
||||||
"rev": "6bd668af3fd098bdd07a1bedd399564141e275da",
|
"rev": "d2c2b44ff6c27b936ec27358a2653caaef8f73b8",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
29
flake.nix
29
flake.nix
|
@ -78,7 +78,7 @@
|
||||||
|
|
||||||
# Tests
|
# Tests
|
||||||
buildPackages.git
|
buildPackages.git
|
||||||
buildPackages.mercurial
|
buildPackages.mercurial # FIXME: remove? only needed for tests
|
||||||
buildPackages.jq
|
buildPackages.jq
|
||||||
]
|
]
|
||||||
++ lib.optionals stdenv.hostPlatform.isLinux [(buildPackages.util-linuxMinimal or buildPackages.utillinuxMinimal)];
|
++ lib.optionals stdenv.hostPlatform.isLinux [(buildPackages.util-linuxMinimal or buildPackages.utillinuxMinimal)];
|
||||||
|
@ -126,8 +126,7 @@
|
||||||
''
|
''
|
||||||
mkdir -p $out/nix-support
|
mkdir -p $out/nix-support
|
||||||
|
|
||||||
# Converts /nix/store/50p3qk8kka9dl6wyq40vydq945k0j3kv-nix-2.4pre20201102_550e11f/bin/nix
|
# Converts /nix/store/50p3qk8k...-nix-2.4pre20201102_550e11f/bin/nix to 50p3qk8k.../bin/nix.
|
||||||
# To 50p3qk8kka9dl6wyq40vydq945k0j3kv/bin/nix
|
|
||||||
tarballPath() {
|
tarballPath() {
|
||||||
# Remove the store prefix
|
# Remove the store prefix
|
||||||
local path=''${1#${builtins.storeDir}/}
|
local path=''${1#${builtins.storeDir}/}
|
||||||
|
@ -153,13 +152,15 @@
|
||||||
echo "file installer $out/install" >> $out/nix-support/hydra-build-products
|
echo "file installer $out/install" >> $out/nix-support/hydra-build-products
|
||||||
'';
|
'';
|
||||||
|
|
||||||
testNixVersions = pkgs: client: daemon: with commonDeps pkgs; pkgs.stdenv.mkDerivation {
|
testNixVersions = pkgs: client: daemon: with commonDeps pkgs; with pkgs.lib; pkgs.stdenv.mkDerivation {
|
||||||
NIX_DAEMON_PACKAGE = daemon;
|
NIX_DAEMON_PACKAGE = daemon;
|
||||||
NIX_CLIENT_PACKAGE = client;
|
NIX_CLIENT_PACKAGE = client;
|
||||||
# Must keep this name short as OSX has a rather strict limit on the
|
name =
|
||||||
# socket path length, and this name appears in the path of the
|
"nix-tests"
|
||||||
# nix-daemon socket used in the tests
|
+ optionalString
|
||||||
name = "nix-tests";
|
(versionAtLeast daemon.version "2.4pre20211005" &&
|
||||||
|
versionAtLeast client.version "2.4pre20211005")
|
||||||
|
"-${client.version}-against-${daemon.version}";
|
||||||
inherit version;
|
inherit version;
|
||||||
|
|
||||||
src = self;
|
src = self;
|
||||||
|
@ -443,6 +444,12 @@
|
||||||
inherit (self) overlay;
|
inherit (self) overlay;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
tests.nssPreload = (import ./tests/nss-preload.nix rec {
|
||||||
|
system = "x86_64-linux";
|
||||||
|
inherit nixpkgs;
|
||||||
|
inherit (self) overlay;
|
||||||
|
});
|
||||||
|
|
||||||
tests.githubFlakes = (import ./tests/github-flakes.nix rec {
|
tests.githubFlakes = (import ./tests/github-flakes.nix rec {
|
||||||
system = "x86_64-linux";
|
system = "x86_64-linux";
|
||||||
inherit nixpkgs;
|
inherit nixpkgs;
|
||||||
|
@ -490,7 +497,11 @@
|
||||||
let pkgs = nixpkgsFor.${system}; in
|
let pkgs = nixpkgsFor.${system}; in
|
||||||
pkgs.runCommand "install-tests" {
|
pkgs.runCommand "install-tests" {
|
||||||
againstSelf = testNixVersions pkgs pkgs.nix pkgs.pkgs.nix;
|
againstSelf = testNixVersions pkgs pkgs.nix pkgs.pkgs.nix;
|
||||||
againstCurrentUnstable = testNixVersions pkgs pkgs.nix pkgs.nixUnstable;
|
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
|
# Disabled because the latest stable version doesn't handle
|
||||||
# `NIX_DAEMON_SOCKET_PATH` which is required for the tests to work
|
# `NIX_DAEMON_SOCKET_PATH` which is required for the tests to work
|
||||||
# againstLatestStable = testNixVersions pkgs pkgs.nix pkgs.nixStable;
|
# againstLatestStable = testNixVersions pkgs pkgs.nix pkgs.nixStable;
|
||||||
|
|
|
@ -12,6 +12,7 @@ use LWP::UserAgent;
|
||||||
use Net::Amazon::S3;
|
use Net::Amazon::S3;
|
||||||
|
|
||||||
my $evalId = $ARGV[0] or die "Usage: $0 EVAL-ID\n";
|
my $evalId = $ARGV[0] or die "Usage: $0 EVAL-ID\n";
|
||||||
|
my $nixRev = $ARGV[1] or die; # FIXME
|
||||||
|
|
||||||
my $releasesBucketName = "nix-releases";
|
my $releasesBucketName = "nix-releases";
|
||||||
my $channelsBucketName = "nix-channels";
|
my $channelsBucketName = "nix-channels";
|
||||||
|
@ -19,6 +20,8 @@ my $nixpkgsDir = "/home/eelco/Dev/nixpkgs-pristine";
|
||||||
|
|
||||||
my $TMPDIR = $ENV{'TMPDIR'} // "/tmp";
|
my $TMPDIR = $ENV{'TMPDIR'} // "/tmp";
|
||||||
|
|
||||||
|
my $isLatest = ($ENV{'IS_LATEST'} // "") eq "1";
|
||||||
|
|
||||||
# FIXME: cut&paste from nixos-channel-scripts.
|
# FIXME: cut&paste from nixos-channel-scripts.
|
||||||
sub fetch {
|
sub fetch {
|
||||||
my ($url, $type) = @_;
|
my ($url, $type) = @_;
|
||||||
|
@ -33,14 +36,15 @@ sub fetch {
|
||||||
}
|
}
|
||||||
|
|
||||||
my $evalUrl = "https://hydra.nixos.org/eval/$evalId";
|
my $evalUrl = "https://hydra.nixos.org/eval/$evalId";
|
||||||
my $evalInfo = decode_json(fetch($evalUrl, 'application/json'));
|
#my $evalInfo = decode_json(fetch($evalUrl, 'application/json'));
|
||||||
#print Dumper($evalInfo);
|
#print Dumper($evalInfo);
|
||||||
|
|
||||||
my $nixRev = $evalInfo->{jobsetevalinputs}->{nix}->{revision} or die;
|
#my $nixRev = $evalInfo->{jobsetevalinputs}->{nix}->{revision} or die;
|
||||||
|
|
||||||
my $tarballInfo = decode_json(fetch("$evalUrl/job/tarball", 'application/json'));
|
my $buildInfo = decode_json(fetch("$evalUrl/job/build.x86_64-linux", 'application/json'));
|
||||||
|
#print Dumper($buildInfo);
|
||||||
|
|
||||||
my $releaseName = $tarballInfo->{releasename};
|
my $releaseName = $buildInfo->{nixname};
|
||||||
$releaseName =~ /nix-(.*)$/ or die;
|
$releaseName =~ /nix-(.*)$/ or die;
|
||||||
my $version = $1;
|
my $version = $1;
|
||||||
|
|
||||||
|
@ -104,8 +108,6 @@ sub downloadFile {
|
||||||
return $sha256_expected;
|
return $sha256_expected;
|
||||||
}
|
}
|
||||||
|
|
||||||
downloadFile("tarball", "2"); # .tar.bz2
|
|
||||||
my $tarballHash = downloadFile("tarball", "3"); # .tar.xz
|
|
||||||
downloadFile("binaryTarball.i686-linux", "1");
|
downloadFile("binaryTarball.i686-linux", "1");
|
||||||
downloadFile("binaryTarball.x86_64-linux", "1");
|
downloadFile("binaryTarball.x86_64-linux", "1");
|
||||||
downloadFile("binaryTarball.aarch64-linux", "1");
|
downloadFile("binaryTarball.aarch64-linux", "1");
|
||||||
|
@ -134,42 +136,43 @@ for my $fn (glob "$tmpDir/*") {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exit if $version =~ /pre/;
|
|
||||||
|
|
||||||
# Update nix-fallback-paths.nix.
|
# Update nix-fallback-paths.nix.
|
||||||
system("cd $nixpkgsDir && git pull") == 0 or die;
|
if ($isLatest) {
|
||||||
|
system("cd $nixpkgsDir && git pull") == 0 or die;
|
||||||
|
|
||||||
sub getStorePath {
|
sub getStorePath {
|
||||||
my ($jobName) = @_;
|
my ($jobName) = @_;
|
||||||
my $buildInfo = decode_json(fetch("$evalUrl/job/$jobName", 'application/json'));
|
my $buildInfo = decode_json(fetch("$evalUrl/job/$jobName", 'application/json'));
|
||||||
for my $product (values %{$buildInfo->{buildproducts}}) {
|
for my $product (values %{$buildInfo->{buildproducts}}) {
|
||||||
next unless $product->{type} eq "nix-build";
|
next unless $product->{type} eq "nix-build";
|
||||||
next if $product->{path} =~ /[a-z]+$/;
|
next if $product->{path} =~ /[a-z]+$/;
|
||||||
return $product->{path};
|
return $product->{path};
|
||||||
|
}
|
||||||
|
die;
|
||||||
}
|
}
|
||||||
die;
|
|
||||||
|
write_file("$nixpkgsDir/nixos/modules/installer/tools/nix-fallback-paths.nix",
|
||||||
|
"{\n" .
|
||||||
|
" x86_64-linux = \"" . getStorePath("build.x86_64-linux") . "\";\n" .
|
||||||
|
" i686-linux = \"" . getStorePath("build.i686-linux") . "\";\n" .
|
||||||
|
" aarch64-linux = \"" . getStorePath("build.aarch64-linux") . "\";\n" .
|
||||||
|
" x86_64-darwin = \"" . getStorePath("build.x86_64-darwin") . "\";\n" .
|
||||||
|
" aarch64-darwin = \"" . getStorePath("build.aarch64-darwin") . "\";\n" .
|
||||||
|
"}\n");
|
||||||
|
|
||||||
|
system("cd $nixpkgsDir && git commit -a -m 'nix-fallback-paths.nix: Update to $version'") == 0 or die;
|
||||||
}
|
}
|
||||||
|
|
||||||
write_file("$nixpkgsDir/nixos/modules/installer/tools/nix-fallback-paths.nix",
|
|
||||||
"{\n" .
|
|
||||||
" x86_64-linux = \"" . getStorePath("build.x86_64-linux") . "\";\n" .
|
|
||||||
" i686-linux = \"" . getStorePath("build.i686-linux") . "\";\n" .
|
|
||||||
" aarch64-linux = \"" . getStorePath("build.aarch64-linux") . "\";\n" .
|
|
||||||
" x86_64-darwin = \"" . getStorePath("build.x86_64-darwin") . "\";\n" .
|
|
||||||
" aarch64-darwin = \"" . getStorePath("build.aarch64-darwin") . "\";\n" .
|
|
||||||
"}\n");
|
|
||||||
|
|
||||||
system("cd $nixpkgsDir && git commit -a -m 'nix-fallback-paths.nix: Update to $version'") == 0 or die;
|
|
||||||
|
|
||||||
# Update the "latest" symlink.
|
# Update the "latest" symlink.
|
||||||
$channelsBucket->add_key(
|
$channelsBucket->add_key(
|
||||||
"nix-latest/install", "",
|
"nix-latest/install", "",
|
||||||
{ "x-amz-website-redirect-location" => "https://releases.nixos.org/$releaseDir/install" })
|
{ "x-amz-website-redirect-location" => "https://releases.nixos.org/$releaseDir/install" })
|
||||||
or die $channelsBucket->err . ": " . $channelsBucket->errstr;
|
or die $channelsBucket->err . ": " . $channelsBucket->errstr
|
||||||
|
if $isLatest;
|
||||||
|
|
||||||
# Tag the release in Git.
|
# Tag the release in Git.
|
||||||
chdir("/home/eelco/Dev/nix-pristine") or die;
|
chdir("/home/eelco/Dev/nix-pristine") or die;
|
||||||
system("git remote update origin") == 0 or die;
|
system("git remote update origin") == 0 or die;
|
||||||
system("git tag --force --sign $version $nixRev -m 'Tagging release $version'") == 0 or die;
|
system("git tag --force --sign $version $nixRev -m 'Tagging release $version'") == 0 or die;
|
||||||
system("git push --tags") == 0 or die;
|
system("git push --tags") == 0 or die;
|
||||||
system("git push --force-with-lease origin $nixRev:refs/heads/latest-release") == 0 or die;
|
system("git push --force-with-lease origin $nixRev:refs/heads/latest-release") == 0 or die if $isLatest;
|
||||||
|
|
|
@ -91,7 +91,7 @@ define build-library
|
||||||
$(1)_PATH := $$(_d)/$$($(1)_NAME).$(SO_EXT)
|
$(1)_PATH := $$(_d)/$$($(1)_NAME).$(SO_EXT)
|
||||||
|
|
||||||
$$($(1)_PATH): $$($(1)_OBJS) $$(_libs) | $$(_d)/
|
$$($(1)_PATH): $$($(1)_OBJS) $$(_libs) | $$(_d)/
|
||||||
$$(trace-ld) $(CXX) -o $$(abspath $$@) -shared $$(foreach lib, $$($(1)_LIBS), $$($$(lib)_LDFLAGS_USE)) $$($(1)_LDFLAGS_UNINSTALLED) $$(LDFLAGS) $$(GLOBAL_LDFLAGS) $$($(1)_OBJS) $$($(1)_LDFLAGS) $$($(1)_LDFLAGS_PROPAGATED)
|
$$(trace-ld) $(CXX) -o $$(abspath $$@) -shared $$(LDFLAGS) $$(GLOBAL_LDFLAGS) $$($(1)_OBJS) $$($(1)_LDFLAGS) $$($(1)_LDFLAGS_PROPAGATED) $$(foreach lib, $$($(1)_LIBS), $$($$(lib)_LDFLAGS_USE)) $$($(1)_LDFLAGS_UNINSTALLED)
|
||||||
|
|
||||||
ifndef HOST_DARWIN
|
ifndef HOST_DARWIN
|
||||||
$(1)_LDFLAGS_USE += -Wl,-rpath,$$(abspath $$(_d))
|
$(1)_LDFLAGS_USE += -Wl,-rpath,$$(abspath $$(_d))
|
||||||
|
@ -105,7 +105,7 @@ define build-library
|
||||||
$$(eval $$(call create-dir, $$($(1)_INSTALL_DIR)))
|
$$(eval $$(call create-dir, $$($(1)_INSTALL_DIR)))
|
||||||
|
|
||||||
$$($(1)_INSTALL_PATH): $$($(1)_OBJS) $$(_libs_final) | $(DESTDIR)$$($(1)_INSTALL_DIR)/
|
$$($(1)_INSTALL_PATH): $$($(1)_OBJS) $$(_libs_final) | $(DESTDIR)$$($(1)_INSTALL_DIR)/
|
||||||
$$(trace-ld) $(CXX) -o $$@ -shared $$(foreach lib, $$($(1)_LIBS), $$($$(lib)_LDFLAGS_USE_INSTALLED)) $$(LDFLAGS) $$(GLOBAL_LDFLAGS) $$($(1)_OBJS) $$($(1)_LDFLAGS) $$($(1)_LDFLAGS_PROPAGATED)
|
$$(trace-ld) $(CXX) -o $$@ -shared $$(LDFLAGS) $$(GLOBAL_LDFLAGS) $$($(1)_OBJS) $$($(1)_LDFLAGS) $$($(1)_LDFLAGS_PROPAGATED) $$(foreach lib, $$($(1)_LIBS), $$($$(lib)_LDFLAGS_USE_INSTALLED))
|
||||||
|
|
||||||
$(1)_LDFLAGS_USE_INSTALLED += -L$$(DESTDIR)$$($(1)_INSTALL_DIR) -l$$(patsubst lib%,%,$$(strip $$($(1)_NAME)))
|
$(1)_LDFLAGS_USE_INSTALLED += -L$$(DESTDIR)$$($(1)_INSTALL_DIR) -l$$(patsubst lib%,%,$$(strip $$($(1)_NAME)))
|
||||||
ifndef HOST_DARWIN
|
ifndef HOST_DARWIN
|
||||||
|
|
|
@ -32,7 +32,7 @@ define build-program
|
||||||
$$(eval $$(call create-dir, $$(_d)))
|
$$(eval $$(call create-dir, $$(_d)))
|
||||||
|
|
||||||
$$($(1)_PATH): $$($(1)_OBJS) $$(_libs) | $$(_d)/
|
$$($(1)_PATH): $$($(1)_OBJS) $$(_libs) | $$(_d)/
|
||||||
$$(trace-ld) $(CXX) -o $$@ $$(foreach lib, $$($(1)_LIBS), $$($$(lib)_LDFLAGS_USE)) $$(LDFLAGS) $$(GLOBAL_LDFLAGS) $$($(1)_OBJS) $$($(1)_LDFLAGS)
|
$$(trace-ld) $(CXX) -o $$@ $$(LDFLAGS) $$(GLOBAL_LDFLAGS) $$($(1)_OBJS) $$($(1)_LDFLAGS) $$(foreach lib, $$($(1)_LIBS), $$($$(lib)_LDFLAGS_USE))
|
||||||
|
|
||||||
$(1)_INSTALL_DIR ?= $$(bindir)
|
$(1)_INSTALL_DIR ?= $$(bindir)
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ define build-program
|
||||||
_libs_final := $$(foreach lib, $$($(1)_LIBS), $$($$(lib)_INSTALL_PATH))
|
_libs_final := $$(foreach lib, $$($(1)_LIBS), $$($$(lib)_INSTALL_PATH))
|
||||||
|
|
||||||
$(DESTDIR)$$($(1)_INSTALL_PATH): $$($(1)_OBJS) $$(_libs_final) | $(DESTDIR)$$($(1)_INSTALL_DIR)/
|
$(DESTDIR)$$($(1)_INSTALL_PATH): $$($(1)_OBJS) $$(_libs_final) | $(DESTDIR)$$($(1)_INSTALL_DIR)/
|
||||||
$$(trace-ld) $(CXX) -o $$@ $$(foreach lib, $$($(1)_LIBS), $$($$(lib)_LDFLAGS_USE_INSTALLED)) $$(LDFLAGS) $$(GLOBAL_LDFLAGS) $$($(1)_OBJS) $$($(1)_LDFLAGS)
|
$$(trace-ld) $(CXX) -o $$@ $$(LDFLAGS) $$(GLOBAL_LDFLAGS) $$($(1)_OBJS) $$($(1)_LDFLAGS) $$(foreach lib, $$($(1)_LIBS), $$($$(lib)_LDFLAGS_USE_INSTALLED))
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
|
|
|
@ -13,3 +13,7 @@ define run-install-test
|
||||||
endef
|
endef
|
||||||
|
|
||||||
.PHONY: check installcheck
|
.PHONY: check installcheck
|
||||||
|
|
||||||
|
print-top-help += \
|
||||||
|
echo " check: Run unit tests"; \
|
||||||
|
echo " installcheck: Run functional tests";
|
||||||
|
|
|
@ -445,12 +445,12 @@ EvalState::EvalState(
|
||||||
StorePathSet closure;
|
StorePathSet closure;
|
||||||
store->computeFSClosure(store->toStorePath(r.second).first, closure);
|
store->computeFSClosure(store->toStorePath(r.second).first, closure);
|
||||||
for (auto & path : closure)
|
for (auto & path : closure)
|
||||||
allowedPaths->insert(store->printStorePath(path));
|
allowPath(path);
|
||||||
} catch (InvalidPath &) {
|
} catch (InvalidPath &) {
|
||||||
allowedPaths->insert(r.second);
|
allowPath(r.second);
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
allowedPaths->insert(r.second);
|
allowPath(r.second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -482,6 +482,18 @@ void EvalState::requireExperimentalFeatureOnEvaluation(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EvalState::allowPath(const Path & path)
|
||||||
|
{
|
||||||
|
if (allowedPaths)
|
||||||
|
allowedPaths->insert(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EvalState::allowPath(const StorePath & storePath)
|
||||||
|
{
|
||||||
|
if (allowedPaths)
|
||||||
|
allowedPaths->insert(store->toRealPath(storePath));
|
||||||
|
}
|
||||||
|
|
||||||
Path EvalState::checkSourcePath(const Path & path_)
|
Path EvalState::checkSourcePath(const Path & path_)
|
||||||
{
|
{
|
||||||
if (!allowedPaths) return path_;
|
if (!allowedPaths) return path_;
|
||||||
|
@ -1316,13 +1328,13 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v, const Pos & po
|
||||||
|
|
||||||
auto size =
|
auto size =
|
||||||
(lambda.arg.empty() ? 0 : 1) +
|
(lambda.arg.empty() ? 0 : 1) +
|
||||||
(lambda.matchAttrs ? lambda.formals->formals.size() : 0);
|
(lambda.hasFormals() ? lambda.formals->formals.size() : 0);
|
||||||
Env & env2(allocEnv(size));
|
Env & env2(allocEnv(size));
|
||||||
env2.up = fun.lambda.env;
|
env2.up = fun.lambda.env;
|
||||||
|
|
||||||
size_t displ = 0;
|
size_t displ = 0;
|
||||||
|
|
||||||
if (!lambda.matchAttrs)
|
if (!lambda.hasFormals())
|
||||||
env2.values[displ++] = &arg;
|
env2.values[displ++] = &arg;
|
||||||
|
|
||||||
else {
|
else {
|
||||||
|
@ -1402,7 +1414,7 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fun.isLambda() || !fun.lambda.fun->matchAttrs) {
|
if (!fun.isLambda() || !fun.lambda.fun->hasFormals()) {
|
||||||
res = fun;
|
res = fun;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1889,6 +1901,7 @@ string EvalState::copyPathToStore(PathSet & context, const Path & path)
|
||||||
? store->computeStorePathForPath(std::string(baseNameOf(path)), checkSourcePath(path)).first
|
? store->computeStorePathForPath(std::string(baseNameOf(path)), checkSourcePath(path)).first
|
||||||
: store->addToStore(std::string(baseNameOf(path)), checkSourcePath(path), FileIngestionMethod::Recursive, htSHA256, defaultPathFilter, repair);
|
: store->addToStore(std::string(baseNameOf(path)), checkSourcePath(path), FileIngestionMethod::Recursive, htSHA256, defaultPathFilter, repair);
|
||||||
dstPath = store->printStorePath(p);
|
dstPath = store->printStorePath(p);
|
||||||
|
allowPath(p);
|
||||||
srcToStore.insert_or_assign(path, std::move(p));
|
srcToStore.insert_or_assign(path, std::move(p));
|
||||||
printMsg(lvlChatty, "copied source '%1%' -> '%2%'", path, dstPath);
|
printMsg(lvlChatty, "copied source '%1%' -> '%2%'", path, dstPath);
|
||||||
}
|
}
|
||||||
|
|
|
@ -150,6 +150,15 @@ public:
|
||||||
|
|
||||||
SearchPath getSearchPath() { return searchPath; }
|
SearchPath getSearchPath() { return searchPath; }
|
||||||
|
|
||||||
|
/* Allow access to a path. */
|
||||||
|
void allowPath(const Path & path);
|
||||||
|
|
||||||
|
/* Allow access to a store path. Note that this gets remapped to
|
||||||
|
the real store path if `store` is a chroot store. */
|
||||||
|
void allowPath(const StorePath & storePath);
|
||||||
|
|
||||||
|
/* Check whether access to a path is allowed and throw an error if
|
||||||
|
not. Otherwise return the canonicalised path. */
|
||||||
Path checkSourcePath(const Path & path);
|
Path checkSourcePath(const Path & path);
|
||||||
|
|
||||||
void checkURI(const std::string & uri);
|
void checkURI(const std::string & uri);
|
||||||
|
|
|
@ -64,8 +64,7 @@ static std::tuple<fetchers::Tree, FlakeRef, FlakeRef> fetchOrSubstituteTree(
|
||||||
debug("got tree '%s' from '%s'",
|
debug("got tree '%s' from '%s'",
|
||||||
state.store->printStorePath(tree.storePath), lockedRef);
|
state.store->printStorePath(tree.storePath), lockedRef);
|
||||||
|
|
||||||
if (state.allowedPaths)
|
state.allowPath(tree.storePath);
|
||||||
state.allowedPaths->insert(tree.actualPath);
|
|
||||||
|
|
||||||
assert(!originalRef.input.getNarHash() || tree.storePath == originalRef.input.computeStorePath(*state.store));
|
assert(!originalRef.input.getNarHash() || tree.storePath == originalRef.input.computeStorePath(*state.store));
|
||||||
|
|
||||||
|
@ -231,7 +230,7 @@ static Flake getFlake(
|
||||||
if (auto outputs = vInfo.attrs->get(sOutputs)) {
|
if (auto outputs = vInfo.attrs->get(sOutputs)) {
|
||||||
expectType(state, nFunction, *outputs->value, *outputs->pos);
|
expectType(state, nFunction, *outputs->value, *outputs->pos);
|
||||||
|
|
||||||
if (outputs->value->isLambda() && outputs->value->lambda.fun->matchAttrs) {
|
if (outputs->value->isLambda() && outputs->value->lambda.fun->hasFormals()) {
|
||||||
for (auto & formal : outputs->value->lambda.fun->formals->formals) {
|
for (auto & formal : outputs->value->lambda.fun->formals->formals) {
|
||||||
if (formal.name != state.sSelf)
|
if (formal.name != state.sSelf)
|
||||||
flake.inputs.emplace(formal.name, FlakeInput {
|
flake.inputs.emplace(formal.name, FlakeInput {
|
||||||
|
|
|
@ -124,7 +124,7 @@ void ExprList::show(std::ostream & str) const
|
||||||
void ExprLambda::show(std::ostream & str) const
|
void ExprLambda::show(std::ostream & str) const
|
||||||
{
|
{
|
||||||
str << "(";
|
str << "(";
|
||||||
if (matchAttrs) {
|
if (hasFormals()) {
|
||||||
str << "{ ";
|
str << "{ ";
|
||||||
bool first = true;
|
bool first = true;
|
||||||
for (auto & i : formals->formals) {
|
for (auto & i : formals->formals) {
|
||||||
|
@ -348,7 +348,7 @@ void ExprLambda::bindVars(const StaticEnv & env)
|
||||||
|
|
||||||
if (!arg.empty()) newEnv.vars[arg] = displ++;
|
if (!arg.empty()) newEnv.vars[arg] = displ++;
|
||||||
|
|
||||||
if (matchAttrs) {
|
if (hasFormals()) {
|
||||||
for (auto & i : formals->formals)
|
for (auto & i : formals->formals)
|
||||||
newEnv.vars[i.name] = displ++;
|
newEnv.vars[i.name] = displ++;
|
||||||
|
|
||||||
|
|
|
@ -233,11 +233,10 @@ struct ExprLambda : Expr
|
||||||
Pos pos;
|
Pos pos;
|
||||||
Symbol name;
|
Symbol name;
|
||||||
Symbol arg;
|
Symbol arg;
|
||||||
bool matchAttrs;
|
|
||||||
Formals * formals;
|
Formals * formals;
|
||||||
Expr * body;
|
Expr * body;
|
||||||
ExprLambda(const Pos & pos, const Symbol & arg, bool matchAttrs, Formals * formals, Expr * body)
|
ExprLambda(const Pos & pos, const Symbol & arg, Formals * formals, Expr * body)
|
||||||
: pos(pos), arg(arg), matchAttrs(matchAttrs), formals(formals), body(body)
|
: pos(pos), arg(arg), formals(formals), body(body)
|
||||||
{
|
{
|
||||||
if (!arg.empty() && formals && formals->argNames.find(arg) != formals->argNames.end())
|
if (!arg.empty() && formals && formals->argNames.find(arg) != formals->argNames.end())
|
||||||
throw ParseError({
|
throw ParseError({
|
||||||
|
@ -247,6 +246,7 @@ struct ExprLambda : Expr
|
||||||
};
|
};
|
||||||
void setName(Symbol & name);
|
void setName(Symbol & name);
|
||||||
string showNamePos() const;
|
string showNamePos() const;
|
||||||
|
inline bool hasFormals() const { return formals != nullptr; }
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -324,13 +324,13 @@ expr: expr_function;
|
||||||
|
|
||||||
expr_function
|
expr_function
|
||||||
: ID ':' expr_function
|
: ID ':' expr_function
|
||||||
{ $$ = new ExprLambda(CUR_POS, data->symbols.create($1), false, 0, $3); }
|
{ $$ = new ExprLambda(CUR_POS, data->symbols.create($1), 0, $3); }
|
||||||
| '{' formals '}' ':' expr_function
|
| '{' formals '}' ':' expr_function
|
||||||
{ $$ = new ExprLambda(CUR_POS, data->symbols.create(""), true, $2, $5); }
|
{ $$ = new ExprLambda(CUR_POS, data->symbols.create(""), $2, $5); }
|
||||||
| '{' formals '}' '@' ID ':' expr_function
|
| '{' formals '}' '@' ID ':' expr_function
|
||||||
{ $$ = new ExprLambda(CUR_POS, data->symbols.create($5), true, $2, $7); }
|
{ $$ = new ExprLambda(CUR_POS, data->symbols.create($5), $2, $7); }
|
||||||
| ID '@' '{' formals '}' ':' expr_function
|
| ID '@' '{' formals '}' ':' expr_function
|
||||||
{ $$ = new ExprLambda(CUR_POS, data->symbols.create($1), true, $4, $7); }
|
{ $$ = new ExprLambda(CUR_POS, data->symbols.create($1), $4, $7); }
|
||||||
| ASSERT expr ';' expr_function
|
| ASSERT expr ';' expr_function
|
||||||
{ $$ = new ExprAssert(CUR_POS, $2, $4); }
|
{ $$ = new ExprAssert(CUR_POS, $2, $4); }
|
||||||
| WITH expr ';' expr_function
|
| WITH expr ';' expr_function
|
||||||
|
|
|
@ -56,13 +56,9 @@ void EvalState::realiseContext(const PathSet & context)
|
||||||
"cannot build '%1%' during evaluation because the option 'allow-import-from-derivation' is disabled",
|
"cannot build '%1%' during evaluation because the option 'allow-import-from-derivation' is disabled",
|
||||||
store->printStorePath(drvs.begin()->drvPath));
|
store->printStorePath(drvs.begin()->drvPath));
|
||||||
|
|
||||||
/* For performance, prefetch all substitute info. */
|
/* Build/substitute the context. */
|
||||||
StorePathSet willBuild, willSubstitute, unknown;
|
|
||||||
uint64_t downloadSize, narSize;
|
|
||||||
std::vector<DerivedPath> buildReqs;
|
std::vector<DerivedPath> buildReqs;
|
||||||
for (auto & d : drvs) buildReqs.emplace_back(DerivedPath { d });
|
for (auto & d : drvs) buildReqs.emplace_back(DerivedPath { d });
|
||||||
store->queryMissing(buildReqs, willBuild, willSubstitute, unknown, downloadSize, narSize);
|
|
||||||
|
|
||||||
store->buildPaths(buildReqs);
|
store->buildPaths(buildReqs);
|
||||||
|
|
||||||
/* Add the output of this derivations to the allowed
|
/* Add the output of this derivations to the allowed
|
||||||
|
@ -414,7 +410,7 @@ static RegisterPrimOp primop_isNull({
|
||||||
Return `true` if *e* evaluates to `null`, and `false` otherwise.
|
Return `true` if *e* evaluates to `null`, and `false` otherwise.
|
||||||
|
|
||||||
> **Warning**
|
> **Warning**
|
||||||
>
|
>
|
||||||
> This function is *deprecated*; just write `e == null` instead.
|
> This function is *deprecated*; just write `e == null` instead.
|
||||||
)",
|
)",
|
||||||
.fun = prim_isNull,
|
.fun = prim_isNull,
|
||||||
|
@ -1851,56 +1847,87 @@ static RegisterPrimOp primop_toFile({
|
||||||
.fun = prim_toFile,
|
.fun = prim_toFile,
|
||||||
});
|
});
|
||||||
|
|
||||||
static void addPath(EvalState & state, const Pos & pos, const string & name, const Path & path_,
|
static void addPath(
|
||||||
Value * filterFun, FileIngestionMethod method, const std::optional<Hash> expectedHash, Value & v)
|
EvalState & state,
|
||||||
|
const Pos & pos,
|
||||||
|
const string & name,
|
||||||
|
Path path,
|
||||||
|
Value * filterFun,
|
||||||
|
FileIngestionMethod method,
|
||||||
|
const std::optional<Hash> expectedHash,
|
||||||
|
Value & v,
|
||||||
|
const PathSet & context)
|
||||||
{
|
{
|
||||||
const auto path = evalSettings.pureEval && expectedHash ?
|
try {
|
||||||
path_ :
|
// FIXME: handle CA derivation outputs (where path needs to
|
||||||
state.checkSourcePath(path_);
|
// be rewritten to the actual output).
|
||||||
PathFilter filter = filterFun ? ([&](const Path & path) {
|
state.realiseContext(context);
|
||||||
auto st = lstat(path);
|
|
||||||
|
|
||||||
/* Call the filter function. The first argument is the path,
|
if (state.store->isInStore(path)) {
|
||||||
the second is a string indicating the type of the file. */
|
auto [storePath, subPath] = state.store->toStorePath(path);
|
||||||
Value arg1;
|
auto info = state.store->queryPathInfo(storePath);
|
||||||
mkString(arg1, path);
|
if (!info->references.empty())
|
||||||
|
throw EvalError("store path '%s' is not allowed to have references",
|
||||||
|
state.store->printStorePath(storePath));
|
||||||
|
path = state.store->toRealPath(storePath) + subPath;
|
||||||
|
}
|
||||||
|
|
||||||
Value fun2;
|
path = evalSettings.pureEval && expectedHash
|
||||||
state.callFunction(*filterFun, arg1, fun2, noPos);
|
? path
|
||||||
|
: state.checkSourcePath(path);
|
||||||
|
|
||||||
Value arg2;
|
PathFilter filter = filterFun ? ([&](const Path & path) {
|
||||||
mkString(arg2,
|
auto st = lstat(path);
|
||||||
S_ISREG(st.st_mode) ? "regular" :
|
|
||||||
S_ISDIR(st.st_mode) ? "directory" :
|
|
||||||
S_ISLNK(st.st_mode) ? "symlink" :
|
|
||||||
"unknown" /* not supported, will fail! */);
|
|
||||||
|
|
||||||
Value res;
|
/* Call the filter function. The first argument is the path,
|
||||||
state.callFunction(fun2, arg2, res, noPos);
|
the second is a string indicating the type of the file. */
|
||||||
|
Value arg1;
|
||||||
|
mkString(arg1, path);
|
||||||
|
|
||||||
return state.forceBool(res, pos);
|
Value fun2;
|
||||||
}) : defaultPathFilter;
|
state.callFunction(*filterFun, arg1, fun2, noPos);
|
||||||
|
|
||||||
std::optional<StorePath> expectedStorePath;
|
Value arg2;
|
||||||
if (expectedHash)
|
mkString(arg2,
|
||||||
expectedStorePath = state.store->makeFixedOutputPath(name, FixedOutputInfo {
|
S_ISREG(st.st_mode) ? "regular" :
|
||||||
{
|
S_ISDIR(st.st_mode) ? "directory" :
|
||||||
.method = method,
|
S_ISLNK(st.st_mode) ? "symlink" :
|
||||||
.hash = *expectedHash,
|
"unknown" /* not supported, will fail! */);
|
||||||
},
|
|
||||||
{},
|
|
||||||
});
|
|
||||||
Path dstPath;
|
|
||||||
if (!expectedHash || !state.store->isValidPath(*expectedStorePath)) {
|
|
||||||
dstPath = state.store->printStorePath(settings.readOnlyMode
|
|
||||||
? state.store->computeStorePathForPath(name, path, method, htSHA256, filter).first
|
|
||||||
: state.store->addToStore(name, path, method, htSHA256, filter, state.repair));
|
|
||||||
if (expectedHash && expectedStorePath != state.store->parseStorePath(dstPath))
|
|
||||||
throw Error("store path mismatch in (possibly filtered) path added from '%s'", path);
|
|
||||||
} else
|
|
||||||
dstPath = state.store->printStorePath(*expectedStorePath);
|
|
||||||
|
|
||||||
mkString(v, dstPath, {dstPath});
|
Value res;
|
||||||
|
state.callFunction(fun2, arg2, res, noPos);
|
||||||
|
|
||||||
|
return state.forceBool(res, pos);
|
||||||
|
}) : defaultPathFilter;
|
||||||
|
|
||||||
|
std::optional<StorePath> expectedStorePath;
|
||||||
|
if (expectedHash)
|
||||||
|
expectedStorePath = state.store->makeFixedOutputPath(name, FixedOutputInfo {
|
||||||
|
{
|
||||||
|
.method = method,
|
||||||
|
.hash = *expectedHash,
|
||||||
|
},
|
||||||
|
{},
|
||||||
|
});
|
||||||
|
|
||||||
|
Path dstPath;
|
||||||
|
if (!expectedHash || !state.store->isValidPath(*expectedStorePath)) {
|
||||||
|
dstPath = state.store->printStorePath(settings.readOnlyMode
|
||||||
|
? state.store->computeStorePathForPath(name, path, method, htSHA256, filter).first
|
||||||
|
: state.store->addToStore(name, path, method, htSHA256, filter, state.repair));
|
||||||
|
if (expectedHash && expectedStorePath != state.store->parseStorePath(dstPath))
|
||||||
|
throw Error("store path mismatch in (possibly filtered) path added from '%s'", path);
|
||||||
|
} else
|
||||||
|
dstPath = state.store->printStorePath(*expectedStorePath);
|
||||||
|
|
||||||
|
mkString(v, dstPath, {dstPath});
|
||||||
|
|
||||||
|
state.allowPath(dstPath);
|
||||||
|
|
||||||
|
} catch (Error & e) {
|
||||||
|
e.addTrace(pos, "while adding path '%s'", path);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1908,11 +1935,6 @@ static void prim_filterSource(EvalState & state, const Pos & pos, Value * * args
|
||||||
{
|
{
|
||||||
PathSet context;
|
PathSet context;
|
||||||
Path path = state.coerceToPath(pos, *args[1], context);
|
Path path = state.coerceToPath(pos, *args[1], context);
|
||||||
if (!context.empty())
|
|
||||||
throw EvalError({
|
|
||||||
.msg = hintfmt("string '%1%' cannot refer to other paths", path),
|
|
||||||
.errPos = pos
|
|
||||||
});
|
|
||||||
|
|
||||||
state.forceValue(*args[0], pos);
|
state.forceValue(*args[0], pos);
|
||||||
if (args[0]->type() != nFunction)
|
if (args[0]->type() != nFunction)
|
||||||
|
@ -1923,13 +1945,26 @@ static void prim_filterSource(EvalState & state, const Pos & pos, Value * * args
|
||||||
.errPos = pos
|
.errPos = pos
|
||||||
});
|
});
|
||||||
|
|
||||||
addPath(state, pos, std::string(baseNameOf(path)), path, args[0], FileIngestionMethod::Recursive, std::nullopt, v);
|
addPath(state, pos, std::string(baseNameOf(path)), path, args[0], FileIngestionMethod::Recursive, std::nullopt, v, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
static RegisterPrimOp primop_filterSource({
|
static RegisterPrimOp primop_filterSource({
|
||||||
.name = "__filterSource",
|
.name = "__filterSource",
|
||||||
.args = {"e1", "e2"},
|
.args = {"e1", "e2"},
|
||||||
.doc = R"(
|
.doc = R"(
|
||||||
|
> **Warning**
|
||||||
|
>
|
||||||
|
> `filterSource` should not be used to filter store paths. Since
|
||||||
|
> `filterSource` uses the name of the input directory while naming
|
||||||
|
> the output directory, doing so will produce a directory name in
|
||||||
|
> the form of `<hash2>-<hash>-<name>`, where `<hash>-<name>` is
|
||||||
|
> the name of the input directory. Since `<hash>` depends on the
|
||||||
|
> unfiltered directory, the name of the output directory will
|
||||||
|
> indirectly depend on files that are filtered out by the
|
||||||
|
> function. This will trigger a rebuild even when a filtered out
|
||||||
|
> file is changed. Use `builtins.path` instead, which allows
|
||||||
|
> specifying the name of the output directory.
|
||||||
|
|
||||||
This function allows you to copy sources into the Nix store while
|
This function allows you to copy sources into the Nix store while
|
||||||
filtering certain files. For instance, suppose that you want to use
|
filtering certain files. For instance, suppose that you want to use
|
||||||
the directory `source-dir` as an input to a Nix expression, e.g.
|
the directory `source-dir` as an input to a Nix expression, e.g.
|
||||||
|
@ -1976,18 +2011,13 @@ static void prim_path(EvalState & state, const Pos & pos, Value * * args, Value
|
||||||
Value * filterFun = nullptr;
|
Value * filterFun = nullptr;
|
||||||
auto method = FileIngestionMethod::Recursive;
|
auto method = FileIngestionMethod::Recursive;
|
||||||
std::optional<Hash> expectedHash;
|
std::optional<Hash> expectedHash;
|
||||||
|
PathSet context;
|
||||||
|
|
||||||
for (auto & attr : *args[0]->attrs) {
|
for (auto & attr : *args[0]->attrs) {
|
||||||
const string & n(attr.name);
|
const string & n(attr.name);
|
||||||
if (n == "path") {
|
if (n == "path")
|
||||||
PathSet context;
|
|
||||||
path = state.coerceToPath(*attr.pos, *attr.value, context);
|
path = state.coerceToPath(*attr.pos, *attr.value, context);
|
||||||
if (!context.empty())
|
else if (attr.name == state.sName)
|
||||||
throw EvalError({
|
|
||||||
.msg = hintfmt("string '%1%' cannot refer to other paths", path),
|
|
||||||
.errPos = *attr.pos
|
|
||||||
});
|
|
||||||
} else if (attr.name == state.sName)
|
|
||||||
name = state.forceStringNoCtx(*attr.value, *attr.pos);
|
name = state.forceStringNoCtx(*attr.value, *attr.pos);
|
||||||
else if (n == "filter") {
|
else if (n == "filter") {
|
||||||
state.forceValue(*attr.value, pos);
|
state.forceValue(*attr.value, pos);
|
||||||
|
@ -2010,7 +2040,7 @@ static void prim_path(EvalState & state, const Pos & pos, Value * * args, Value
|
||||||
if (name.empty())
|
if (name.empty())
|
||||||
name = baseNameOf(path);
|
name = baseNameOf(path);
|
||||||
|
|
||||||
addPath(state, pos, name, path, filterFun, method, expectedHash, v);
|
addPath(state, pos, name, path, filterFun, method, expectedHash, v, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
static RegisterPrimOp primop_path({
|
static RegisterPrimOp primop_path({
|
||||||
|
@ -2375,7 +2405,7 @@ static void prim_functionArgs(EvalState & state, const Pos & pos, Value * * args
|
||||||
.errPos = pos
|
.errPos = pos
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!args[0]->lambda.fun->matchAttrs) {
|
if (!args[0]->lambda.fun->hasFormals()) {
|
||||||
state.mkAttrs(v, 0);
|
state.mkAttrs(v, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -2530,7 +2560,7 @@ static RegisterPrimOp primop_tail({
|
||||||
the argument isn’t a list or is an empty list.
|
the argument isn’t a list or is an empty list.
|
||||||
|
|
||||||
> **Warning**
|
> **Warning**
|
||||||
>
|
>
|
||||||
> This function should generally be avoided since it's inefficient:
|
> This function should generally be avoided since it's inefficient:
|
||||||
> unlike Haskell's `tail`, it takes O(n) time, so recursing over a
|
> unlike Haskell's `tail`, it takes O(n) time, so recursing over a
|
||||||
> list by repeatedly calling `tail` takes O(n^2) time.
|
> list by repeatedly calling `tail` takes O(n^2) time.
|
||||||
|
|
|
@ -84,8 +84,7 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar
|
||||||
mkInt(*state.allocAttr(v, state.symbols.create("revCount")), *revCount);
|
mkInt(*state.allocAttr(v, state.symbols.create("revCount")), *revCount);
|
||||||
v.attrs->sort();
|
v.attrs->sort();
|
||||||
|
|
||||||
if (state.allowedPaths)
|
state.allowPath(tree.storePath);
|
||||||
state.allowedPaths->insert(tree.actualPath);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static RegisterPrimOp r_fetchMercurial("fetchMercurial", 1, prim_fetchMercurial);
|
static RegisterPrimOp r_fetchMercurial("fetchMercurial", 1, prim_fetchMercurial);
|
||||||
|
|
|
@ -66,7 +66,7 @@ void emitTreeAttrs(
|
||||||
v.attrs->sort();
|
v.attrs->sort();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string fixURI(std::string uri, EvalState &state, const std::string & defaultScheme = "file")
|
std::string fixURI(std::string uri, EvalState & state, const std::string & defaultScheme = "file")
|
||||||
{
|
{
|
||||||
state.checkURI(uri);
|
state.checkURI(uri);
|
||||||
return uri.find("://") != std::string::npos ? uri : defaultScheme + "://" + uri;
|
return uri.find("://") != std::string::npos ? uri : defaultScheme + "://" + uri;
|
||||||
|
@ -81,23 +81,17 @@ std::string fixURIForGit(std::string uri, EvalState & state)
|
||||||
return fixURI(uri, state);
|
return fixURI(uri, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
void addURI(EvalState &state, fetchers::Attrs &attrs, Symbol name, std::string v)
|
|
||||||
{
|
|
||||||
string n(name);
|
|
||||||
attrs.emplace(name, n == "url" ? fixURI(v, state) : v);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct FetchTreeParams {
|
struct FetchTreeParams {
|
||||||
bool emptyRevFallback = false;
|
bool emptyRevFallback = false;
|
||||||
bool allowNameArgument = false;
|
bool allowNameArgument = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void fetchTree(
|
static void fetchTree(
|
||||||
EvalState &state,
|
EvalState & state,
|
||||||
const Pos &pos,
|
const Pos & pos,
|
||||||
Value **args,
|
Value * * args,
|
||||||
Value &v,
|
Value & v,
|
||||||
const std::optional<std::string> type,
|
std::optional<std::string> type,
|
||||||
const FetchTreeParams & params = FetchTreeParams{}
|
const FetchTreeParams & params = FetchTreeParams{}
|
||||||
) {
|
) {
|
||||||
fetchers::Input input;
|
fetchers::Input input;
|
||||||
|
@ -110,17 +104,33 @@ static void fetchTree(
|
||||||
|
|
||||||
fetchers::Attrs attrs;
|
fetchers::Attrs attrs;
|
||||||
|
|
||||||
|
if (auto aType = args[0]->attrs->get(state.sType)) {
|
||||||
|
if (type)
|
||||||
|
throw Error({
|
||||||
|
.msg = hintfmt("unexpected attribute 'type'"),
|
||||||
|
.errPos = pos
|
||||||
|
});
|
||||||
|
type = state.forceStringNoCtx(*aType->value, *aType->pos);
|
||||||
|
} else if (!type)
|
||||||
|
throw Error({
|
||||||
|
.msg = hintfmt("attribute 'type' is missing in call to 'fetchTree'"),
|
||||||
|
.errPos = pos
|
||||||
|
});
|
||||||
|
|
||||||
|
attrs.emplace("type", type.value());
|
||||||
|
|
||||||
for (auto & attr : *args[0]->attrs) {
|
for (auto & attr : *args[0]->attrs) {
|
||||||
|
if (attr.name == state.sType) continue;
|
||||||
state.forceValue(*attr.value);
|
state.forceValue(*attr.value);
|
||||||
if (attr.value->type() == nPath || attr.value->type() == nString)
|
if (attr.value->type() == nPath || attr.value->type() == nString) {
|
||||||
addURI(
|
auto s = state.coerceToString(*attr.pos, *attr.value, context, false, false);
|
||||||
state,
|
attrs.emplace(attr.name,
|
||||||
attrs,
|
attr.name == "url"
|
||||||
attr.name,
|
? type == "git"
|
||||||
state.coerceToString(*attr.pos, *attr.value, context, false, false)
|
? fixURIForGit(s, state)
|
||||||
);
|
: fixURI(s, state)
|
||||||
else if (attr.value->type() == nString)
|
: s);
|
||||||
addURI(state, attrs, attr.name, attr.value->string.s);
|
}
|
||||||
else if (attr.value->type() == nBool)
|
else if (attr.value->type() == nBool)
|
||||||
attrs.emplace(attr.name, Explicit<bool>{attr.value->boolean});
|
attrs.emplace(attr.name, Explicit<bool>{attr.value->boolean});
|
||||||
else if (attr.value->type() == nInt)
|
else if (attr.value->type() == nInt)
|
||||||
|
@ -130,15 +140,6 @@ static void fetchTree(
|
||||||
attr.name, showType(*attr.value));
|
attr.name, showType(*attr.value));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type)
|
|
||||||
attrs.emplace("type", type.value());
|
|
||||||
|
|
||||||
if (!attrs.count("type"))
|
|
||||||
throw Error({
|
|
||||||
.msg = hintfmt("attribute 'type' is missing in call to 'fetchTree'"),
|
|
||||||
.errPos = pos
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!params.allowNameArgument)
|
if (!params.allowNameArgument)
|
||||||
if (auto nameIter = attrs.find("name"); nameIter != attrs.end())
|
if (auto nameIter = attrs.find("name"); nameIter != attrs.end())
|
||||||
throw Error({
|
throw Error({
|
||||||
|
@ -146,7 +147,6 @@ static void fetchTree(
|
||||||
.errPos = pos
|
.errPos = pos
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
input = fetchers::Input::fromAttrs(std::move(attrs));
|
input = fetchers::Input::fromAttrs(std::move(attrs));
|
||||||
} else {
|
} else {
|
||||||
auto url = state.coerceToString(pos, *args[0], context, false, false);
|
auto url = state.coerceToString(pos, *args[0], context, false, false);
|
||||||
|
@ -169,8 +169,7 @@ static void fetchTree(
|
||||||
|
|
||||||
auto [tree, input2] = input.fetch(state.store);
|
auto [tree, input2] = input.fetch(state.store);
|
||||||
|
|
||||||
if (state.allowedPaths)
|
state.allowPath(tree.storePath);
|
||||||
state.allowedPaths->insert(tree.actualPath);
|
|
||||||
|
|
||||||
emitTreeAttrs(state, tree, input2, v, params.emptyRevFallback, false);
|
emitTreeAttrs(state, tree, input2, v, params.emptyRevFallback, false);
|
||||||
}
|
}
|
||||||
|
@ -234,19 +233,16 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
|
||||||
? fetchers::downloadTarball(state.store, *url, name, (bool) expectedHash).first.storePath
|
? fetchers::downloadTarball(state.store, *url, name, (bool) expectedHash).first.storePath
|
||||||
: fetchers::downloadFile(state.store, *url, name, (bool) expectedHash).storePath;
|
: fetchers::downloadFile(state.store, *url, name, (bool) expectedHash).storePath;
|
||||||
|
|
||||||
auto realPath = state.store->toRealPath(storePath);
|
|
||||||
|
|
||||||
if (expectedHash) {
|
if (expectedHash) {
|
||||||
auto hash = unpack
|
auto hash = unpack
|
||||||
? state.store->queryPathInfo(storePath)->narHash
|
? state.store->queryPathInfo(storePath)->narHash
|
||||||
: hashFile(htSHA256, realPath);
|
: hashFile(htSHA256, state.store->toRealPath(storePath));
|
||||||
if (hash != *expectedHash)
|
if (hash != *expectedHash)
|
||||||
throw Error((unsigned int) 102, "hash mismatch in file downloaded from '%s':\n specified: %s\n got: %s",
|
throw Error((unsigned int) 102, "hash mismatch in file downloaded from '%s':\n specified: %s\n got: %s",
|
||||||
*url, expectedHash->to_string(Base32, true), hash.to_string(Base32, true));
|
*url, expectedHash->to_string(Base32, true), hash.to_string(Base32, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.allowedPaths)
|
state.allowPath(storePath);
|
||||||
state.allowedPaths->insert(realPath);
|
|
||||||
|
|
||||||
auto path = state.store->printStorePath(storePath);
|
auto path = state.store->printStorePath(storePath);
|
||||||
mkString(v, path, PathSet({path}));
|
mkString(v, path, PathSet({path}));
|
||||||
|
|
|
@ -135,7 +135,7 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
|
||||||
if (location) posToXML(xmlAttrs, v.lambda.fun->pos);
|
if (location) posToXML(xmlAttrs, v.lambda.fun->pos);
|
||||||
XMLOpenElement _(doc, "function", xmlAttrs);
|
XMLOpenElement _(doc, "function", xmlAttrs);
|
||||||
|
|
||||||
if (v.lambda.fun->matchAttrs) {
|
if (v.lambda.fun->hasFormals()) {
|
||||||
XMLAttrs attrs;
|
XMLAttrs attrs;
|
||||||
if (!v.lambda.fun->arg.empty()) attrs["name"] = v.lambda.fun->arg;
|
if (!v.lambda.fun->arg.empty()) attrs["name"] = v.lambda.fun->arg;
|
||||||
if (v.lambda.fun->formals->ellipsis) attrs["ellipsis"] = "1";
|
if (v.lambda.fun->formals->ellipsis) attrs["ellipsis"] = "1";
|
||||||
|
|
|
@ -711,6 +711,7 @@ void LocalDerivationGoal::startBuilder()
|
||||||
if (!builderOut.readSide)
|
if (!builderOut.readSide)
|
||||||
throw SysError("opening pseudoterminal master");
|
throw SysError("opening pseudoterminal master");
|
||||||
|
|
||||||
|
// FIXME: not thread-safe, use ptsname_r
|
||||||
std::string slaveName(ptsname(builderOut.readSide.get()));
|
std::string slaveName(ptsname(builderOut.readSide.get()));
|
||||||
|
|
||||||
if (buildUser) {
|
if (buildUser) {
|
||||||
|
@ -754,7 +755,6 @@ void LocalDerivationGoal::startBuilder()
|
||||||
result.startTime = time(0);
|
result.startTime = time(0);
|
||||||
|
|
||||||
/* Fork a child to build the package. */
|
/* Fork a child to build the package. */
|
||||||
ProcessOptions options;
|
|
||||||
|
|
||||||
#if __linux__
|
#if __linux__
|
||||||
if (useChroot) {
|
if (useChroot) {
|
||||||
|
@ -797,8 +797,6 @@ void LocalDerivationGoal::startBuilder()
|
||||||
|
|
||||||
userNamespaceSync.create();
|
userNamespaceSync.create();
|
||||||
|
|
||||||
options.allowVfork = false;
|
|
||||||
|
|
||||||
Path maxUserNamespaces = "/proc/sys/user/max_user_namespaces";
|
Path maxUserNamespaces = "/proc/sys/user/max_user_namespaces";
|
||||||
static bool userNamespacesEnabled =
|
static bool userNamespacesEnabled =
|
||||||
pathExists(maxUserNamespaces)
|
pathExists(maxUserNamespaces)
|
||||||
|
@ -856,7 +854,7 @@ void LocalDerivationGoal::startBuilder()
|
||||||
writeFull(builderOut.writeSide.get(),
|
writeFull(builderOut.writeSide.get(),
|
||||||
fmt("%d %d\n", usingUserNamespace, child));
|
fmt("%d %d\n", usingUserNamespace, child));
|
||||||
_exit(0);
|
_exit(0);
|
||||||
}, options);
|
});
|
||||||
|
|
||||||
int res = helper.wait();
|
int res = helper.wait();
|
||||||
if (res != 0 && settings.sandboxFallback) {
|
if (res != 0 && settings.sandboxFallback) {
|
||||||
|
@ -920,10 +918,9 @@ void LocalDerivationGoal::startBuilder()
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
fallback:
|
fallback:
|
||||||
options.allowVfork = !buildUser && !drv->isBuiltin();
|
|
||||||
pid = startProcess([&]() {
|
pid = startProcess([&]() {
|
||||||
runChild();
|
runChild();
|
||||||
}, options);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/* parent */
|
/* parent */
|
||||||
|
@ -2140,8 +2137,7 @@ void LocalDerivationGoal::registerOutputs()
|
||||||
|
|
||||||
/* Pass blank Sink as we are not ready to hash data at this stage. */
|
/* Pass blank Sink as we are not ready to hash data at this stage. */
|
||||||
NullSink blank;
|
NullSink blank;
|
||||||
auto references = worker.store.parseStorePathSet(
|
auto references = scanForReferences(blank, actualPath, referenceablePaths);
|
||||||
scanForReferences(blank, actualPath, worker.store.printStorePathSet(referenceablePaths)));
|
|
||||||
|
|
||||||
outputReferencesIfUnregistered.insert_or_assign(
|
outputReferencesIfUnregistered.insert_or_assign(
|
||||||
outputName,
|
outputName,
|
||||||
|
@ -2208,7 +2204,7 @@ void LocalDerivationGoal::registerOutputs()
|
||||||
auto rewriteOutput = [&]() {
|
auto rewriteOutput = [&]() {
|
||||||
/* Apply hash rewriting if necessary. */
|
/* Apply hash rewriting if necessary. */
|
||||||
if (!outputRewrites.empty()) {
|
if (!outputRewrites.empty()) {
|
||||||
warn("rewriting hashes in '%1%'; cross fingers", actualPath);
|
debug("rewriting hashes in '%1%'; cross fingers", actualPath);
|
||||||
|
|
||||||
/* FIXME: this is in-memory. */
|
/* FIXME: this is in-memory. */
|
||||||
StringSink sink;
|
StringSink sink;
|
||||||
|
|
|
@ -239,7 +239,7 @@ void Worker::run(const Goals & _topGoals)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Call queryMissing() efficiently query substitutes. */
|
/* Call queryMissing() to efficiently query substitutes. */
|
||||||
StorePathSet willBuild, willSubstitute, unknown;
|
StorePathSet willBuild, willSubstitute, unknown;
|
||||||
uint64_t downloadSize, narSize;
|
uint64_t downloadSize, narSize;
|
||||||
store.queryMissing(topPaths, willBuild, willSubstitute, unknown, downloadSize, narSize);
|
store.queryMissing(topPaths, willBuild, willSubstitute, unknown, downloadSize, narSize);
|
||||||
|
|
|
@ -11,11 +11,13 @@
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
|
||||||
static unsigned int refLength = 32; /* characters */
|
static size_t refLength = 32; /* characters */
|
||||||
|
|
||||||
|
|
||||||
static void search(const unsigned char * s, size_t len,
|
static void search(
|
||||||
StringSet & hashes, StringSet & seen)
|
std::string_view s,
|
||||||
|
StringSet & hashes,
|
||||||
|
StringSet & seen)
|
||||||
{
|
{
|
||||||
static std::once_flag initialised;
|
static std::once_flag initialised;
|
||||||
static bool isBase32[256];
|
static bool isBase32[256];
|
||||||
|
@ -25,7 +27,7 @@ static void search(const unsigned char * s, size_t len,
|
||||||
isBase32[(unsigned char) base32Chars[i]] = true;
|
isBase32[(unsigned char) base32Chars[i]] = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
for (size_t i = 0; i + refLength <= len; ) {
|
for (size_t i = 0; i + refLength <= s.size(); ) {
|
||||||
int j;
|
int j;
|
||||||
bool match = true;
|
bool match = true;
|
||||||
for (j = refLength - 1; j >= 0; --j)
|
for (j = refLength - 1; j >= 0; --j)
|
||||||
|
@ -35,7 +37,7 @@ static void search(const unsigned char * s, size_t len,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!match) continue;
|
if (!match) continue;
|
||||||
string ref((const char *) s + i, refLength);
|
std::string ref(s.substr(i, refLength));
|
||||||
if (hashes.erase(ref)) {
|
if (hashes.erase(ref)) {
|
||||||
debug(format("found reference to '%1%' at offset '%2%'")
|
debug(format("found reference to '%1%' at offset '%2%'")
|
||||||
% ref % i);
|
% ref % i);
|
||||||
|
@ -46,69 +48,60 @@ static void search(const unsigned char * s, size_t len,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct RefScanSink : Sink
|
void RefScanSink::operator () (std::string_view data)
|
||||||
{
|
{
|
||||||
StringSet hashes;
|
/* It's possible that a reference spans the previous and current
|
||||||
StringSet seen;
|
fragment, so search in the concatenation of the tail of the
|
||||||
|
previous fragment and the start of the current fragment. */
|
||||||
|
auto s = tail;
|
||||||
|
s.append(data.data(), refLength);
|
||||||
|
search(s, hashes, seen);
|
||||||
|
|
||||||
string tail;
|
search(data, hashes, seen);
|
||||||
|
|
||||||
RefScanSink() { }
|
auto tailLen = std::min(data.size(), refLength);
|
||||||
|
auto rest = refLength - tailLen;
|
||||||
void operator () (std::string_view data) override
|
if (rest < tail.size())
|
||||||
{
|
tail = tail.substr(tail.size() - rest);
|
||||||
/* It's possible that a reference spans the previous and current
|
tail.append(data.data() + data.size() - tailLen, tailLen);
|
||||||
fragment, so search in the concatenation of the tail of the
|
}
|
||||||
previous fragment and the start of the current fragment. */
|
|
||||||
string s = tail + std::string(data, 0, refLength);
|
|
||||||
search((const unsigned char *) s.data(), s.size(), hashes, seen);
|
|
||||||
|
|
||||||
search((const unsigned char *) data.data(), data.size(), hashes, seen);
|
|
||||||
|
|
||||||
size_t tailLen = data.size() <= refLength ? data.size() : refLength;
|
|
||||||
tail = std::string(tail, tail.size() < refLength - tailLen ? 0 : tail.size() - (refLength - tailLen));
|
|
||||||
tail.append({data.data() + data.size() - tailLen, tailLen});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
std::pair<PathSet, HashResult> scanForReferences(const string & path,
|
std::pair<StorePathSet, HashResult> scanForReferences(
|
||||||
const PathSet & refs)
|
const string & path,
|
||||||
|
const StorePathSet & refs)
|
||||||
{
|
{
|
||||||
HashSink hashSink { htSHA256 };
|
HashSink hashSink { htSHA256 };
|
||||||
auto found = scanForReferences(hashSink, path, refs);
|
auto found = scanForReferences(hashSink, path, refs);
|
||||||
auto hash = hashSink.finish();
|
auto hash = hashSink.finish();
|
||||||
return std::pair<PathSet, HashResult>(found, hash);
|
return std::pair<StorePathSet, HashResult>(found, hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
PathSet scanForReferences(Sink & toTee,
|
StorePathSet scanForReferences(
|
||||||
const string & path, const PathSet & refs)
|
Sink & toTee,
|
||||||
|
const Path & path,
|
||||||
|
const StorePathSet & refs)
|
||||||
{
|
{
|
||||||
RefScanSink refsSink;
|
StringSet hashes;
|
||||||
TeeSink sink { refsSink, toTee };
|
std::map<std::string, StorePath> backMap;
|
||||||
std::map<string, Path> backMap;
|
|
||||||
|
|
||||||
for (auto & i : refs) {
|
for (auto & i : refs) {
|
||||||
auto baseName = std::string(baseNameOf(i));
|
std::string hashPart(i.hashPart());
|
||||||
string::size_type pos = baseName.find('-');
|
auto inserted = backMap.emplace(hashPart, i).second;
|
||||||
if (pos == string::npos)
|
assert(inserted);
|
||||||
throw Error("bad reference '%1%'", i);
|
hashes.insert(hashPart);
|
||||||
string s = string(baseName, 0, pos);
|
|
||||||
assert(s.size() == refLength);
|
|
||||||
assert(backMap.find(s) == backMap.end());
|
|
||||||
// parseHash(htSHA256, s);
|
|
||||||
refsSink.hashes.insert(s);
|
|
||||||
backMap[s] = i;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Look for the hashes in the NAR dump of the path. */
|
/* Look for the hashes in the NAR dump of the path. */
|
||||||
|
RefScanSink refsSink(std::move(hashes));
|
||||||
|
TeeSink sink { refsSink, toTee };
|
||||||
dumpPath(path, sink);
|
dumpPath(path, sink);
|
||||||
|
|
||||||
/* Map the hashes found back to their store paths. */
|
/* Map the hashes found back to their store paths. */
|
||||||
PathSet found;
|
StorePathSet found;
|
||||||
for (auto & i : refsSink.seen) {
|
for (auto & i : refsSink.getResult()) {
|
||||||
std::map<string, Path>::iterator j;
|
auto j = backMap.find(i);
|
||||||
if ((j = backMap.find(i)) == backMap.end()) abort();
|
assert(j != backMap.end());
|
||||||
found.insert(j->second);
|
found.insert(j->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,31 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "types.hh"
|
|
||||||
#include "hash.hh"
|
#include "hash.hh"
|
||||||
|
#include "path.hh"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
std::pair<PathSet, HashResult> scanForReferences(const Path & path, const PathSet & refs);
|
std::pair<StorePathSet, HashResult> scanForReferences(const Path & path, const StorePathSet & refs);
|
||||||
|
|
||||||
PathSet scanForReferences(Sink & toTee, const Path & path, const PathSet & refs);
|
StorePathSet scanForReferences(Sink & toTee, const Path & path, const StorePathSet & refs);
|
||||||
|
|
||||||
|
class RefScanSink : public Sink
|
||||||
|
{
|
||||||
|
StringSet hashes;
|
||||||
|
StringSet seen;
|
||||||
|
|
||||||
|
std::string tail;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
RefScanSink(StringSet && hashes) : hashes(hashes)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
StringSet & getResult()
|
||||||
|
{ return seen; }
|
||||||
|
|
||||||
|
void operator () (std::string_view data) override;
|
||||||
|
};
|
||||||
|
|
||||||
struct RewritingSink : Sink
|
struct RewritingSink : Sink
|
||||||
{
|
{
|
||||||
|
|
15
src/libstore/tests/local.mk
Normal file
15
src/libstore/tests/local.mk
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
check: libstore-tests_RUN
|
||||||
|
|
||||||
|
programs += libstore-tests
|
||||||
|
|
||||||
|
libstore-tests_DIR := $(d)
|
||||||
|
|
||||||
|
libstore-tests_INSTALL_DIR :=
|
||||||
|
|
||||||
|
libstore-tests_SOURCES := $(wildcard $(d)/*.cc)
|
||||||
|
|
||||||
|
libstore-tests_CXXFLAGS += -I src/libstore -I src/libutil
|
||||||
|
|
||||||
|
libstore-tests_LIBS = libstore libutil
|
||||||
|
|
||||||
|
libstore-tests_LDFLAGS := $(GTEST_LIBS)
|
45
src/libstore/tests/references.cc
Normal file
45
src/libstore/tests/references.cc
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
#include "references.hh"
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
TEST(references, scan)
|
||||||
|
{
|
||||||
|
std::string hash1 = "dc04vv14dak1c1r48qa0m23vr9jy8sm0";
|
||||||
|
std::string hash2 = "zc842j0rz61mjsp3h3wp5ly71ak6qgdn";
|
||||||
|
|
||||||
|
{
|
||||||
|
RefScanSink scanner(StringSet{hash1});
|
||||||
|
auto s = "foobar";
|
||||||
|
scanner(s);
|
||||||
|
ASSERT_EQ(scanner.getResult(), StringSet{});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
RefScanSink scanner(StringSet{hash1});
|
||||||
|
auto s = "foobar" + hash1 + "xyzzy";
|
||||||
|
scanner(s);
|
||||||
|
ASSERT_EQ(scanner.getResult(), StringSet{hash1});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
RefScanSink scanner(StringSet{hash1, hash2});
|
||||||
|
auto s = "foobar" + hash1 + "xyzzy" + hash2;
|
||||||
|
scanner(((std::string_view) s).substr(0, 10));
|
||||||
|
scanner(((std::string_view) s).substr(10, 5));
|
||||||
|
scanner(((std::string_view) s).substr(15, 5));
|
||||||
|
scanner(((std::string_view) s).substr(20));
|
||||||
|
ASSERT_EQ(scanner.getResult(), StringSet({hash1, hash2}));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
RefScanSink scanner(StringSet{hash1, hash2});
|
||||||
|
auto s = "foobar" + hash1 + "xyzzy" + hash2;
|
||||||
|
for (auto & i : s)
|
||||||
|
scanner(std::string(1, i));
|
||||||
|
ASSERT_EQ(scanner.getResult(), StringSet({hash1, hash2}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -65,16 +65,7 @@ ref<RemoteStore::Connection> UDSRemoteStore::openConnection()
|
||||||
throw SysError("cannot create Unix domain socket");
|
throw SysError("cannot create Unix domain socket");
|
||||||
closeOnExec(conn->fd.get());
|
closeOnExec(conn->fd.get());
|
||||||
|
|
||||||
string socketPath = path ? *path : settings.nixDaemonSocketFile;
|
nix::connect(conn->fd.get(), path ? *path : settings.nixDaemonSocketFile);
|
||||||
|
|
||||||
struct sockaddr_un addr;
|
|
||||||
addr.sun_family = AF_UNIX;
|
|
||||||
if (socketPath.size() + 1 >= sizeof(addr.sun_path))
|
|
||||||
throw Error("socket path '%1%' is too long", socketPath);
|
|
||||||
strcpy(addr.sun_path, socketPath.c_str());
|
|
||||||
|
|
||||||
if (::connect(conn->fd.get(), (struct sockaddr *) &addr, sizeof(addr)) == -1)
|
|
||||||
throw SysError("cannot connect to daemon at '%1%'", socketPath);
|
|
||||||
|
|
||||||
conn->from.fd = conn->fd.get();
|
conn->from.fd = conn->fd.get();
|
||||||
conn->to.fd = conn->fd.get();
|
conn->to.fd = conn->fd.get();
|
||||||
|
|
|
@ -42,7 +42,7 @@ static string caseHackSuffix = "~nix~case~hack~";
|
||||||
PathFilter defaultPathFilter = [](const Path &) { return true; };
|
PathFilter defaultPathFilter = [](const Path &) { return true; };
|
||||||
|
|
||||||
|
|
||||||
static void dumpContents(const Path & path, size_t size,
|
static void dumpContents(const Path & path, off_t size,
|
||||||
Sink & sink)
|
Sink & sink)
|
||||||
{
|
{
|
||||||
sink << "contents" << size;
|
sink << "contents" << size;
|
||||||
|
@ -76,7 +76,7 @@ static void dump(const Path & path, Sink & sink, PathFilter & filter)
|
||||||
sink << "type" << "regular";
|
sink << "type" << "regular";
|
||||||
if (st.st_mode & S_IXUSR)
|
if (st.st_mode & S_IXUSR)
|
||||||
sink << "executable" << "";
|
sink << "executable" << "";
|
||||||
dumpContents(path, (size_t) st.st_size, sink);
|
dumpContents(path, st.st_size, sink);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (S_ISDIR(st.st_mode)) {
|
else if (S_ISDIR(st.st_mode)) {
|
||||||
|
|
|
@ -903,7 +903,7 @@ int Pid::wait()
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
if (errno != EINTR)
|
if (errno != EINTR)
|
||||||
throw SysError("cannot get child exit status");
|
throw SysError("cannot get exit status of PID %d", pid);
|
||||||
checkInterrupt();
|
checkInterrupt();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -939,9 +939,6 @@ void killUser(uid_t uid)
|
||||||
users to which the current process can send signals. So we
|
users to which the current process can send signals. So we
|
||||||
fork a process, switch to uid, and send a mass kill. */
|
fork a process, switch to uid, and send a mass kill. */
|
||||||
|
|
||||||
ProcessOptions options;
|
|
||||||
options.allowVfork = false;
|
|
||||||
|
|
||||||
Pid pid = startProcess([&]() {
|
Pid pid = startProcess([&]() {
|
||||||
|
|
||||||
if (setuid(uid) == -1)
|
if (setuid(uid) == -1)
|
||||||
|
@ -964,7 +961,7 @@ void killUser(uid_t uid)
|
||||||
}
|
}
|
||||||
|
|
||||||
_exit(0);
|
_exit(0);
|
||||||
}, options);
|
});
|
||||||
|
|
||||||
int status = pid.wait();
|
int status = pid.wait();
|
||||||
if (status != 0)
|
if (status != 0)
|
||||||
|
@ -1085,8 +1082,7 @@ void runProgram2(const RunOptions & options)
|
||||||
// vfork implies that the environment of the main process and the fork will
|
// vfork implies that the environment of the main process and the fork will
|
||||||
// be shared (technically this is undefined, but in practice that's the
|
// be shared (technically this is undefined, but in practice that's the
|
||||||
// case), so we can't use it if we alter the environment
|
// case), so we can't use it if we alter the environment
|
||||||
if (options.environment)
|
processOptions.allowVfork = !options.environment;
|
||||||
processOptions.allowVfork = false;
|
|
||||||
|
|
||||||
/* Fork. */
|
/* Fork. */
|
||||||
Pid pid = startProcess([&]() {
|
Pid pid = startProcess([&]() {
|
||||||
|
@ -1686,16 +1682,7 @@ AutoCloseFD createUnixDomainSocket(const Path & path, mode_t mode)
|
||||||
|
|
||||||
closeOnExec(fdSocket.get());
|
closeOnExec(fdSocket.get());
|
||||||
|
|
||||||
struct sockaddr_un addr;
|
bind(fdSocket.get(), path);
|
||||||
addr.sun_family = AF_UNIX;
|
|
||||||
if (path.size() + 1 >= sizeof(addr.sun_path))
|
|
||||||
throw Error("socket path '%1%' is too long", path);
|
|
||||||
strcpy(addr.sun_path, path.c_str());
|
|
||||||
|
|
||||||
unlink(path.c_str());
|
|
||||||
|
|
||||||
if (bind(fdSocket.get(), (struct sockaddr *) &addr, sizeof(addr)) == -1)
|
|
||||||
throw SysError("cannot bind to socket '%1%'", path);
|
|
||||||
|
|
||||||
if (chmod(path.c_str(), mode) == -1)
|
if (chmod(path.c_str(), mode) == -1)
|
||||||
throw SysError("changing permissions on '%1%'", path);
|
throw SysError("changing permissions on '%1%'", path);
|
||||||
|
@ -1707,6 +1694,66 @@ AutoCloseFD createUnixDomainSocket(const Path & path, mode_t mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void bind(int fd, const std::string & path)
|
||||||
|
{
|
||||||
|
unlink(path.c_str());
|
||||||
|
|
||||||
|
struct sockaddr_un addr;
|
||||||
|
addr.sun_family = AF_UNIX;
|
||||||
|
|
||||||
|
if (path.size() + 1 >= sizeof(addr.sun_path)) {
|
||||||
|
Pid pid = startProcess([&]() {
|
||||||
|
auto dir = dirOf(path);
|
||||||
|
if (chdir(dir.c_str()) == -1)
|
||||||
|
throw SysError("chdir to '%s' failed", dir);
|
||||||
|
std::string base(baseNameOf(path));
|
||||||
|
if (base.size() + 1 >= sizeof(addr.sun_path))
|
||||||
|
throw Error("socket path '%s' is too long", base);
|
||||||
|
strcpy(addr.sun_path, base.c_str());
|
||||||
|
if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) == -1)
|
||||||
|
throw SysError("cannot bind to socket '%s'", path);
|
||||||
|
_exit(0);
|
||||||
|
});
|
||||||
|
int status = pid.wait();
|
||||||
|
if (status != 0)
|
||||||
|
throw Error("cannot bind to socket '%s'", path);
|
||||||
|
} else {
|
||||||
|
strcpy(addr.sun_path, path.c_str());
|
||||||
|
if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) == -1)
|
||||||
|
throw SysError("cannot bind to socket '%s'", path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void connect(int fd, const std::string & path)
|
||||||
|
{
|
||||||
|
struct sockaddr_un addr;
|
||||||
|
addr.sun_family = AF_UNIX;
|
||||||
|
|
||||||
|
if (path.size() + 1 >= sizeof(addr.sun_path)) {
|
||||||
|
Pid pid = startProcess([&]() {
|
||||||
|
auto dir = dirOf(path);
|
||||||
|
if (chdir(dir.c_str()) == -1)
|
||||||
|
throw SysError("chdir to '%s' failed", dir);
|
||||||
|
std::string base(baseNameOf(path));
|
||||||
|
if (base.size() + 1 >= sizeof(addr.sun_path))
|
||||||
|
throw Error("socket path '%s' is too long", base);
|
||||||
|
strcpy(addr.sun_path, base.c_str());
|
||||||
|
if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) == -1)
|
||||||
|
throw SysError("cannot connect to socket at '%s'", path);
|
||||||
|
_exit(0);
|
||||||
|
});
|
||||||
|
int status = pid.wait();
|
||||||
|
if (status != 0)
|
||||||
|
throw Error("cannot connect to socket at '%s'", path);
|
||||||
|
} else {
|
||||||
|
strcpy(addr.sun_path, path.c_str());
|
||||||
|
if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) == -1)
|
||||||
|
throw SysError("cannot connect to socket at '%s'", path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
string showBytes(uint64_t bytes)
|
string showBytes(uint64_t bytes)
|
||||||
{
|
{
|
||||||
return fmt("%.2f MiB", bytes / (1024.0 * 1024.0));
|
return fmt("%.2f MiB", bytes / (1024.0 * 1024.0));
|
||||||
|
|
|
@ -259,10 +259,10 @@ void killUser(uid_t uid);
|
||||||
pid to the caller. */
|
pid to the caller. */
|
||||||
struct ProcessOptions
|
struct ProcessOptions
|
||||||
{
|
{
|
||||||
string errorPrefix = "error: ";
|
string errorPrefix = "";
|
||||||
bool dieWithParent = true;
|
bool dieWithParent = true;
|
||||||
bool runExitHandlers = false;
|
bool runExitHandlers = false;
|
||||||
bool allowVfork = true;
|
bool allowVfork = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
pid_t startProcess(std::function<void()> fun, const ProcessOptions & options = ProcessOptions());
|
pid_t startProcess(std::function<void()> fun, const ProcessOptions & options = ProcessOptions());
|
||||||
|
@ -574,6 +574,12 @@ void commonChildInit(Pipe & logPipe);
|
||||||
/* Create a Unix domain socket in listen mode. */
|
/* Create a Unix domain socket in listen mode. */
|
||||||
AutoCloseFD createUnixDomainSocket(const Path & path, mode_t mode);
|
AutoCloseFD createUnixDomainSocket(const Path & path, mode_t mode);
|
||||||
|
|
||||||
|
/* Bind a Unix domain socket to a path. */
|
||||||
|
void bind(int fd, const std::string & path);
|
||||||
|
|
||||||
|
/* Connect to a Unix domain socket. */
|
||||||
|
void connect(int fd, const std::string & path);
|
||||||
|
|
||||||
|
|
||||||
// A Rust/Python-like enumerate() iterator adapter.
|
// A Rust/Python-like enumerate() iterator adapter.
|
||||||
// Borrowed from http://reedbeta.com/blog/python-like-enumerate-in-cpp17.
|
// Borrowed from http://reedbeta.com/blog/python-like-enumerate-in-cpp17.
|
||||||
|
|
|
@ -156,9 +156,6 @@ static void daemonLoop()
|
||||||
if (chdir("/") == -1)
|
if (chdir("/") == -1)
|
||||||
throw SysError("cannot change current directory");
|
throw SysError("cannot change current directory");
|
||||||
|
|
||||||
// Get rid of children automatically; don't let them become zombies.
|
|
||||||
setSigChldAction(true);
|
|
||||||
|
|
||||||
AutoCloseFD fdSocket;
|
AutoCloseFD fdSocket;
|
||||||
|
|
||||||
// Handle socket-based activation by systemd.
|
// Handle socket-based activation by systemd.
|
||||||
|
@ -176,6 +173,9 @@ static void daemonLoop()
|
||||||
fdSocket = createUnixDomainSocket(settings.nixDaemonSocketFile, 0666);
|
fdSocket = createUnixDomainSocket(settings.nixDaemonSocketFile, 0666);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get rid of children automatically; don't let them become zombies.
|
||||||
|
setSigChldAction(true);
|
||||||
|
|
||||||
// Loop accepting connections.
|
// Loop accepting connections.
|
||||||
while (1) {
|
while (1) {
|
||||||
|
|
||||||
|
|
|
@ -346,10 +346,10 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
auto checkOverlay = [&](const std::string & attrPath, Value & v, const Pos & pos) {
|
auto checkOverlay = [&](const std::string & attrPath, Value & v, const Pos & pos) {
|
||||||
try {
|
try {
|
||||||
state->forceValue(v, pos);
|
state->forceValue(v, pos);
|
||||||
if (!v.isLambda() || v.lambda.fun->matchAttrs || std::string(v.lambda.fun->arg) != "final")
|
if (!v.isLambda() || v.lambda.fun->hasFormals() || std::string(v.lambda.fun->arg) != "final")
|
||||||
throw Error("overlay does not take an argument named 'final'");
|
throw Error("overlay does not take an argument named 'final'");
|
||||||
auto body = dynamic_cast<ExprLambda *>(v.lambda.fun->body);
|
auto body = dynamic_cast<ExprLambda *>(v.lambda.fun->body);
|
||||||
if (!body || body->matchAttrs || std::string(body->arg) != "prev")
|
if (!body || body->hasFormals() || std::string(body->arg) != "prev")
|
||||||
throw Error("overlay does not take an argument named 'prev'");
|
throw Error("overlay does not take an argument named 'prev'");
|
||||||
// FIXME: if we have a 'nixpkgs' input, use it to
|
// FIXME: if we have a 'nixpkgs' input, use it to
|
||||||
// evaluate the overlay.
|
// evaluate the overlay.
|
||||||
|
@ -363,7 +363,7 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
try {
|
try {
|
||||||
state->forceValue(v, pos);
|
state->forceValue(v, pos);
|
||||||
if (v.isLambda()) {
|
if (v.isLambda()) {
|
||||||
if (!v.lambda.fun->matchAttrs || !v.lambda.fun->formals->ellipsis)
|
if (!v.lambda.fun->hasFormals() || !v.lambda.fun->formals->ellipsis)
|
||||||
throw Error("module must match an open attribute set ('{ config, ... }')");
|
throw Error("module must match an open attribute set ('{ config, ... }')");
|
||||||
} else if (v.type() == nAttrs) {
|
} else if (v.type() == nAttrs) {
|
||||||
for (auto & attr : *v.attrs)
|
for (auto & attr : *v.attrs)
|
||||||
|
|
|
@ -225,7 +225,7 @@ Currently the `type` attribute can be one of the following:
|
||||||
[flake:]<flake-id>(/<rev-or-ref>(/rev)?)?
|
[flake:]<flake-id>(/<rev-or-ref>(/rev)?)?
|
||||||
```
|
```
|
||||||
|
|
||||||
These perform a lookup of `<flake-id>` in the flake registry. or
|
These perform a lookup of `<flake-id>` in the flake registry. For
|
||||||
example, `nixpkgs` and `nixpkgs/release-20.09` are indirect flake
|
example, `nixpkgs` and `nixpkgs/release-20.09` are indirect flake
|
||||||
references. The specified `rev` and/or `ref` are merged with the
|
references. The specified `rev` and/or `ref` are merged with the
|
||||||
entry in the registry; see [nix registry](./nix3-registry.md) for
|
entry in the registry; see [nix registry](./nix3-registry.md) for
|
||||||
|
|
|
@ -187,11 +187,14 @@ static void showHelp(std::vector<std::string> subcommand, MultiCommand & topleve
|
||||||
, "/"),
|
, "/"),
|
||||||
*vUtils);
|
*vUtils);
|
||||||
|
|
||||||
auto vJson = state.allocValue();
|
auto vArgs = state.allocValue();
|
||||||
|
state.mkAttrs(*vArgs, 16);
|
||||||
|
auto vJson = state.allocAttr(*vArgs, state.symbols.create("command"));
|
||||||
mkString(*vJson, toplevel.toJSON().dump());
|
mkString(*vJson, toplevel.toJSON().dump());
|
||||||
|
vArgs->attrs->sort();
|
||||||
|
|
||||||
auto vRes = state.allocValue();
|
auto vRes = state.allocValue();
|
||||||
state.callFunction(*vGenerateManpage, *vJson, *vRes, noPos);
|
state.callFunction(*vGenerateManpage, *vArgs, *vRes, noPos);
|
||||||
|
|
||||||
auto attr = vRes->attrs->get(state.symbols.create(mdName + ".md"));
|
auto attr = vRes->attrs->get(state.symbols.create(mdName + ".md"));
|
||||||
if (!attr)
|
if (!attr)
|
||||||
|
|
|
@ -98,10 +98,8 @@ struct ProfileManifest
|
||||||
|
|
||||||
else if (pathExists(profile + "/manifest.nix")) {
|
else if (pathExists(profile + "/manifest.nix")) {
|
||||||
// FIXME: needed because of pure mode; ugly.
|
// FIXME: needed because of pure mode; ugly.
|
||||||
if (state.allowedPaths) {
|
state.allowPath(state.store->followLinksToStore(profile));
|
||||||
state.allowedPaths->insert(state.store->followLinksToStore(profile));
|
state.allowPath(state.store->followLinksToStore(profile + "/manifest.nix"));
|
||||||
state.allowedPaths->insert(state.store->followLinksToStore(profile + "/manifest.nix"));
|
|
||||||
}
|
|
||||||
|
|
||||||
auto drvInfos = queryInstalled(state, state.store->followLinksToStore(profile));
|
auto drvInfos = queryInstalled(state, state.store->followLinksToStore(profile));
|
||||||
|
|
||||||
|
|
|
@ -5,11 +5,6 @@ if [[ -z $(type -p git) ]]; then
|
||||||
exit 99
|
exit 99
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -z $(type -p hg) ]]; then
|
|
||||||
echo "Mercurial not installed; skipping flake tests"
|
|
||||||
exit 99
|
|
||||||
fi
|
|
||||||
|
|
||||||
clearStore
|
clearStore
|
||||||
rm -rf $TEST_HOME/.cache $TEST_HOME/.config
|
rm -rf $TEST_HOME/.cache $TEST_HOME/.config
|
||||||
|
|
||||||
|
@ -266,6 +261,8 @@ cat > $flake3Dir/flake.nix <<EOF
|
||||||
mkDerivation {
|
mkDerivation {
|
||||||
inherit system;
|
inherit system;
|
||||||
name = "fnord";
|
name = "fnord";
|
||||||
|
dummy = builtins.readFile (builtins.path { name = "source"; path = ./.; filter = path: type: baseNameOf path == "config.nix"; } + "/config.nix");
|
||||||
|
dummy2 = builtins.readFile (builtins.path { name = "source"; path = inputs.flake1; filter = path: type: baseNameOf path == "simple.nix"; } + "/simple.nix");
|
||||||
buildCommand = ''
|
buildCommand = ''
|
||||||
cat \${inputs.nonFlake}/README.md > \$out
|
cat \${inputs.nonFlake}/README.md > \$out
|
||||||
'';
|
'';
|
||||||
|
@ -579,45 +576,52 @@ nix build -o $TEST_ROOT/result git+file://$flakeGitBare
|
||||||
|
|
||||||
# Test Mercurial flakes.
|
# Test Mercurial flakes.
|
||||||
rm -rf $flake5Dir
|
rm -rf $flake5Dir
|
||||||
hg init $flake5Dir
|
mkdir $flake5Dir
|
||||||
|
|
||||||
cat > $flake5Dir/flake.nix <<EOF
|
cat > $flake5Dir/flake.nix <<EOF
|
||||||
{
|
{
|
||||||
outputs = { self, flake1 }: {
|
outputs = { self, flake1 }: {
|
||||||
defaultPackage.$system = flake1.defaultPackage.$system;
|
defaultPackage.$system = flake1.defaultPackage.$system;
|
||||||
|
|
||||||
expr = assert builtins.pathExists ./flake.lock; 123;
|
expr = assert builtins.pathExists ./flake.lock; 123;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
hg add $flake5Dir/flake.nix
|
if [[ -n $(type -p hg) ]]; then
|
||||||
hg commit --config ui.username=foobar@example.org $flake5Dir -m 'Initial commit'
|
hg init $flake5Dir
|
||||||
|
|
||||||
nix build -o $TEST_ROOT/result hg+file://$flake5Dir
|
hg add $flake5Dir/flake.nix
|
||||||
[[ -e $TEST_ROOT/result/hello ]]
|
hg commit --config ui.username=foobar@example.org $flake5Dir -m 'Initial commit'
|
||||||
|
|
||||||
(! nix flake metadata --json hg+file://$flake5Dir | jq -e -r .revision)
|
nix build -o $TEST_ROOT/result hg+file://$flake5Dir
|
||||||
|
[[ -e $TEST_ROOT/result/hello ]]
|
||||||
|
|
||||||
nix eval hg+file://$flake5Dir#expr
|
(! nix flake metadata --json hg+file://$flake5Dir | jq -e -r .revision)
|
||||||
|
|
||||||
nix eval hg+file://$flake5Dir#expr
|
nix eval hg+file://$flake5Dir#expr
|
||||||
|
|
||||||
(! nix eval hg+file://$flake5Dir#expr --no-allow-dirty)
|
nix eval hg+file://$flake5Dir#expr
|
||||||
|
|
||||||
(! nix flake metadata --json hg+file://$flake5Dir | jq -e -r .revision)
|
(! nix eval hg+file://$flake5Dir#expr --no-allow-dirty)
|
||||||
|
|
||||||
hg commit --config ui.username=foobar@example.org $flake5Dir -m 'Add lock file'
|
(! nix flake metadata --json hg+file://$flake5Dir | jq -e -r .revision)
|
||||||
|
|
||||||
nix flake metadata --json hg+file://$flake5Dir --refresh | jq -e -r .revision
|
hg commit --config ui.username=foobar@example.org $flake5Dir -m 'Add lock file'
|
||||||
nix flake metadata --json hg+file://$flake5Dir
|
|
||||||
[[ $(nix flake metadata --json hg+file://$flake5Dir | jq -e -r .revCount) = 1 ]]
|
|
||||||
|
|
||||||
nix build -o $TEST_ROOT/result hg+file://$flake5Dir --no-registries --no-allow-dirty
|
nix flake metadata --json hg+file://$flake5Dir --refresh | jq -e -r .revision
|
||||||
nix build -o $TEST_ROOT/result hg+file://$flake5Dir --no-use-registries --no-allow-dirty
|
nix flake metadata --json hg+file://$flake5Dir
|
||||||
|
[[ $(nix flake metadata --json hg+file://$flake5Dir | jq -e -r .revCount) = 1 ]]
|
||||||
|
|
||||||
# Test tarball flakes
|
nix build -o $TEST_ROOT/result hg+file://$flake5Dir --no-registries --no-allow-dirty
|
||||||
tar cfz $TEST_ROOT/flake.tar.gz -C $TEST_ROOT --exclude .hg flake5
|
nix build -o $TEST_ROOT/result hg+file://$flake5Dir --no-use-registries --no-allow-dirty
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test path flakes.
|
||||||
|
rm -rf $flake5Dir/.hg $flake5Dir/flake.lock
|
||||||
|
nix flake lock path://$flake5Dir
|
||||||
|
|
||||||
|
# Test tarball flakes.
|
||||||
|
tar cfz $TEST_ROOT/flake.tar.gz -C $TEST_ROOT flake5
|
||||||
|
|
||||||
nix build -o $TEST_ROOT/result file://$TEST_ROOT/flake.tar.gz
|
nix build -o $TEST_ROOT/result file://$TEST_ROOT/flake.tar.gz
|
||||||
|
|
||||||
|
@ -632,8 +636,8 @@ nix build -o $TEST_ROOT/result "file://$TEST_ROOT/flake.tar.gz?narHash=sha256-qQ
|
||||||
|
|
||||||
# Test --override-input.
|
# Test --override-input.
|
||||||
git -C $flake3Dir reset --hard
|
git -C $flake3Dir reset --hard
|
||||||
nix flake lock $flake3Dir --override-input flake2/flake1 flake5 -vvvvv
|
nix flake lock $flake3Dir --override-input flake2/flake1 file://$TEST_ROOT/flake.tar.gz -vvvvv
|
||||||
[[ $(jq .nodes.flake1_2.locked.url $flake3Dir/flake.lock) =~ flake5 ]]
|
[[ $(jq .nodes.flake1_2.locked.url $flake3Dir/flake.lock) =~ flake.tar.gz ]]
|
||||||
|
|
||||||
nix flake lock $flake3Dir --override-input flake2/flake1 flake1
|
nix flake lock $flake3Dir --override-input flake2/flake1 flake1
|
||||||
[[ $(jq -r .nodes.flake1_2.locked.rev $flake3Dir/flake.lock) =~ $hash2 ]]
|
[[ $(jq -r .nodes.flake1_2.locked.rev $flake3Dir/flake.lock) =~ $hash2 ]]
|
||||||
|
|
123
tests/nss-preload.nix
Normal file
123
tests/nss-preload.nix
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
{ nixpkgs, system, overlay }:
|
||||||
|
|
||||||
|
with import (nixpkgs + "/nixos/lib/testing-python.nix") {
|
||||||
|
inherit system;
|
||||||
|
extraConfigurations = [ { nixpkgs.overlays = [ overlay ]; } ];
|
||||||
|
};
|
||||||
|
|
||||||
|
makeTest (
|
||||||
|
|
||||||
|
rec {
|
||||||
|
name = "nss-preload";
|
||||||
|
|
||||||
|
nodes = {
|
||||||
|
http_dns = { lib, pkgs, config, ... }: {
|
||||||
|
networking.firewall.enable = false;
|
||||||
|
networking.interfaces.eth1.ipv6.addresses = lib.mkForce [
|
||||||
|
{ address = "fd21::1"; prefixLength = 64; }
|
||||||
|
];
|
||||||
|
networking.interfaces.eth1.ipv4.addresses = lib.mkForce [
|
||||||
|
{ address = "192.168.0.1"; prefixLength = 24; }
|
||||||
|
];
|
||||||
|
|
||||||
|
services.unbound = {
|
||||||
|
enable = true;
|
||||||
|
enableRootTrustAnchor = false;
|
||||||
|
settings = {
|
||||||
|
server = {
|
||||||
|
interface = [ "192.168.0.1" "fd21::1" "::1" "127.0.0.1" ];
|
||||||
|
access-control = [ "192.168.0.0/24 allow" "fd21::/64 allow" "::1 allow" "127.0.0.0/8 allow" ];
|
||||||
|
local-data = [
|
||||||
|
''"example.com. IN A 192.168.0.1"''
|
||||||
|
''"example.com. IN AAAA fd21::1"''
|
||||||
|
''"tarballs.nixos.org. IN A 192.168.0.1"''
|
||||||
|
''"tarballs.nixos.org. IN AAAA fd21::1"''
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
services.nginx = {
|
||||||
|
enable = true;
|
||||||
|
virtualHosts."example.com" = {
|
||||||
|
root = pkgs.runCommand "testdir" {} ''
|
||||||
|
mkdir "$out"
|
||||||
|
echo hello world > "$out/index.html"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# client consumes a remote resolver
|
||||||
|
client = { lib, nodes, pkgs, ... }: {
|
||||||
|
networking.useDHCP = false;
|
||||||
|
networking.nameservers = [
|
||||||
|
(lib.head nodes.http_dns.config.networking.interfaces.eth1.ipv6.addresses).address
|
||||||
|
(lib.head nodes.http_dns.config.networking.interfaces.eth1.ipv4.addresses).address
|
||||||
|
];
|
||||||
|
networking.interfaces.eth1.ipv6.addresses = [
|
||||||
|
{ address = "fd21::10"; prefixLength = 64; }
|
||||||
|
];
|
||||||
|
networking.interfaces.eth1.ipv4.addresses = [
|
||||||
|
{ address = "192.168.0.10"; prefixLength = 24; }
|
||||||
|
];
|
||||||
|
|
||||||
|
nix.sandboxPaths = lib.mkForce [];
|
||||||
|
nix.binaryCaches = lib.mkForce [];
|
||||||
|
nix.useSandbox = lib.mkForce true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
nix-fetch = pkgs.writeText "fetch.nix" ''
|
||||||
|
derivation {
|
||||||
|
# This derivation is an copy from what is available over at
|
||||||
|
# nix.git:corepkgs/fetchurl.nix
|
||||||
|
builder = "builtin:fetchurl";
|
||||||
|
|
||||||
|
# We're going to fetch data from the http_dns instance created before
|
||||||
|
# we expect the content to be the same as the content available there.
|
||||||
|
# ```
|
||||||
|
# $ nix-hash --type sha256 --to-base32 $(echo "hello world" | sha256sum | cut -d " " -f 1)
|
||||||
|
# 0ix4jahrkll5zg01wandq78jw3ab30q4nscph67rniqg5x7r0j59
|
||||||
|
# ```
|
||||||
|
outputHash = "0ix4jahrkll5zg01wandq78jw3ab30q4nscph67rniqg5x7r0j59";
|
||||||
|
outputHashAlgo = "sha256";
|
||||||
|
outputHashMode = "flat";
|
||||||
|
|
||||||
|
name = "example.com";
|
||||||
|
url = "http://example.com";
|
||||||
|
|
||||||
|
unpack = false;
|
||||||
|
executable = false;
|
||||||
|
|
||||||
|
system = "builtin";
|
||||||
|
|
||||||
|
preferLocalBuild = true;
|
||||||
|
|
||||||
|
impureEnvVars = [
|
||||||
|
"http_proxy" "https_proxy" "ftp_proxy" "all_proxy" "no_proxy"
|
||||||
|
];
|
||||||
|
|
||||||
|
urls = [ "http://example.com" ];
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
|
||||||
|
testScript = { nodes, ... }: ''
|
||||||
|
http_dns.wait_for_unit("nginx")
|
||||||
|
http_dns.wait_for_open_port(80)
|
||||||
|
http_dns.wait_for_unit("unbound")
|
||||||
|
http_dns.wait_for_open_port(53)
|
||||||
|
|
||||||
|
client.start()
|
||||||
|
client.wait_for_unit('multi-user.target')
|
||||||
|
|
||||||
|
with subtest("can fetch data from a remote server outside sandbox"):
|
||||||
|
client.succeed("nix --version >&2")
|
||||||
|
client.succeed("curl -vvv http://example.com/index.html >&2")
|
||||||
|
|
||||||
|
with subtest("nix-build can lookup dns and fetch data"):
|
||||||
|
client.succeed("""
|
||||||
|
nix-build ${nix-fetch} >&2
|
||||||
|
""")
|
||||||
|
'';
|
||||||
|
})
|
Loading…
Reference in a new issue