Merge branch 'master' into make

Conflicts:
	src/libexpr/eval.cc
This commit is contained in:
Eelco Dolstra 2014-01-21 15:30:01 +01:00
commit 81628a6ccc
34 changed files with 473 additions and 103 deletions

View file

@ -782,6 +782,9 @@ stdenv.mkDerivation {
<literal>true</literal> and <literal>true</literal> and
<literal>false</literal>.</para></listitem> <literal>false</literal>.</para></listitem>
<listitem><para>The null value, denoted as
<literal>null</literal>.</para></listitem>
</itemizedlist> </itemizedlist>
</para> </para>

View file

@ -152,10 +152,6 @@ cp -p misc/emacs/nix-mode.elc $RPM_BUILD_ROOT%{_emacs_sitelispdir}/
rm $RPM_BUILD_ROOT%{_defaultdocdir}/%{name}-doc-%{version}/README rm $RPM_BUILD_ROOT%{_defaultdocdir}/%{name}-doc-%{version}/README
%check
make check
%clean %clean
rm -rf $RPM_BUILD_ROOT rm -rf $RPM_BUILD_ROOT

View file

@ -1,4 +1,4 @@
PERL_MODULES = lib/Nix/Store.pm lib/Nix/Manifest.pm lib/Nix/GeneratePatches.pm lib/Nix/SSH.pm lib/Nix/CopyClosure.pm lib/Nix/Config.pm.in lib/Nix/Utils.pm PERL_MODULES = lib/Nix/Store.pm lib/Nix/Manifest.pm lib/Nix/GeneratePatches.pm lib/Nix/SSH.pm lib/Nix/CopyClosure.pm lib/Nix/Config.pm.in lib/Nix/Utils.pm lib/Nix/Crypto.pm
all: $(PERL_MODULES:.in=) all: $(PERL_MODULES:.in=)

View file

@ -13,6 +13,7 @@ $storeDir = $ENV{"NIX_STORE_DIR"} || "@storedir@";
$bzip2 = "@bzip2@"; $bzip2 = "@bzip2@";
$xz = "@xz@"; $xz = "@xz@";
$curl = "@curl@"; $curl = "@curl@";
$openssl = "@openssl@";
$useBindings = "@perlbindings@" eq "yes"; $useBindings = "@perlbindings@" eq "yes";
@ -32,7 +33,7 @@ sub readConfig {
open CONFIG, "<$config" or die "cannot open `$config'"; open CONFIG, "<$config" or die "cannot open `$config'";
while (<CONFIG>) { while (<CONFIG>) {
/^\s*([\w|-]+)\s*=\s*(.*)$/ or next; /^\s*([\w\-\.]+)\s*=\s*(.*)$/ or next;
$config{$1} = $2; $config{$1} = $2;
} }
close CONFIG; close CONFIG;

42
perl/lib/Nix/Crypto.pm Normal file
View file

@ -0,0 +1,42 @@
package Nix::Crypto;
use strict;
use MIME::Base64;
use Nix::Store;
use Nix::Config;
use IPC::Open2;
our @ISA = qw(Exporter);
our @EXPORT = qw(signString isValidSignature);
sub signString {
my ($privateKeyFile, $s) = @_;
my $hash = hashString("sha256", 0, $s);
my ($from, $to);
my $pid = open2($from, $to, $Nix::Config::openssl, "rsautl", "-sign", "-inkey", $privateKeyFile);
print $to $hash;
close $to;
local $/ = undef;
my $sig = <$from>;
close $from;
waitpid($pid, 0);
die "$0: OpenSSL returned exit code $? while signing hash\n" if $? != 0;
my $sig64 = encode_base64($sig, "");
return $sig64;
}
sub isValidSignature {
my ($publicKeyFile, $sig64, $s) = @_;
my ($from, $to);
my $pid = open2($from, $to, $Nix::Config::openssl, "rsautl", "-verify", "-inkey", $publicKeyFile, "-pubin");
print $to decode_base64($sig64);
close $to;
my $decoded = <$from>;
close $from;
waitpid($pid, 0);
return 0 if $? != 0;
my $hash = hashString("sha256", 0, $s);
return $decoded eq $hash;
}
1;

View file

@ -8,6 +8,7 @@ use File::stat;
use File::Path; use File::Path;
use Fcntl ':flock'; use Fcntl ':flock';
use Nix::Config; use Nix::Config;
use Nix::Crypto;
our @ISA = qw(Exporter); our @ISA = qw(Exporter);
our @EXPORT = qw(readManifest writeManifest updateManifestDB addPatch deleteOldManifests parseNARInfo); our @EXPORT = qw(readManifest writeManifest updateManifestDB addPatch deleteOldManifests parseNARInfo);
@ -394,9 +395,10 @@ sub deleteOldManifests {
# Parse a NAR info file. # Parse a NAR info file.
sub parseNARInfo { sub parseNARInfo {
my ($storePath, $content) = @_; my ($storePath, $content, $requireValidSig, $location) = @_;
my ($storePath2, $url, $fileHash, $fileSize, $narHash, $narSize, $deriver, $system); my ($storePath2, $url, $fileHash, $fileSize, $narHash, $narSize, $deriver, $system, $sig);
my $signedData = "";
my $compression = "bzip2"; my $compression = "bzip2";
my @refs; my @refs;
@ -412,11 +414,13 @@ sub parseNARInfo {
elsif ($1 eq "References") { @refs = split / /, $2; } elsif ($1 eq "References") { @refs = split / /, $2; }
elsif ($1 eq "Deriver") { $deriver = $2; } elsif ($1 eq "Deriver") { $deriver = $2; }
elsif ($1 eq "System") { $system = $2; } elsif ($1 eq "System") { $system = $2; }
elsif ($1 eq "Signature") { $sig = $2; last; }
$signedData .= "$line\n";
} }
return undef if $storePath ne $storePath2 || !defined $url || !defined $narHash; return undef if $storePath ne $storePath2 || !defined $url || !defined $narHash;
return my $res =
{ url => $url { url => $url
, compression => $compression , compression => $compression
, fileHash => $fileHash , fileHash => $fileHash
@ -427,6 +431,36 @@ sub parseNARInfo {
, deriver => $deriver , deriver => $deriver
, system => $system , system => $system
}; };
if ($requireValidSig) {
if (!defined $sig) {
warn "NAR info file `$location' lacks a signature; ignoring\n";
return undef;
}
my ($sigVersion, $keyName, $sig64) = split ";", $sig;
$sigVersion //= 0;
if ($sigVersion != 1) {
warn "NAR info file `$location' has unsupported version $sigVersion; ignoring\n";
return undef;
}
return undef unless defined $keyName && defined $sig64;
my $publicKeyFile = $Nix::Config::config{"binary-cache-public-key-$keyName"};
if (!defined $publicKeyFile) {
warn "NAR info file `$location' is signed by unknown key `$keyName'; ignoring\n";
return undef;
}
if (! -f $publicKeyFile) {
die "binary cache public key file `$publicKeyFile' does not exist\n";
return undef;
}
if (!isValidSignature($publicKeyFile, $sig64, $signedData)) {
warn "NAR info file `$location' has an invalid signature; ignoring\n";
return undef;
}
$res->{signedBy} = $keyName;
}
return $res;
} }

View file

@ -20,6 +20,8 @@ void doInit()
if (!store) { if (!store) {
try { try {
settings.processEnvironment(); settings.processEnvironment();
settings.loadConfFile();
settings.update();
settings.lockCPU = false; settings.lockCPU = false;
store = openStore(); store = openStore();
} catch (Error & e) { } catch (Error & e) {

View file

@ -6,7 +6,7 @@ let
pkgs = import <nixpkgs> {}; pkgs = import <nixpkgs> {};
systems = [ "x86_64-linux" "i686-linux" "x86_64-darwin" "x86_64-freebsd" "i686-freebsd" ]; systems = [ "x86_64-linux" "i686-linux" "x86_64-darwin" /* "x86_64-freebsd" "i686-freebsd" */ ];
jobs = rec { jobs = rec {
@ -23,7 +23,7 @@ let
inherit officialRelease; inherit officialRelease;
buildInputs = buildInputs =
[ curl bison flex2535 perl libxml2 libxslt w3m bzip2 [ curl bison flex perl libxml2 libxslt w3m bzip2
tetex dblatex nukeReferences pkgconfig sqlite git tetex dblatex nukeReferences pkgconfig sqlite git
]; ];
@ -175,6 +175,8 @@ let
rpm_fedora18x86_64 = makeRPM_x86_64 (diskImageFunsFun: diskImageFunsFun.fedora18x86_64) 60; rpm_fedora18x86_64 = makeRPM_x86_64 (diskImageFunsFun: diskImageFunsFun.fedora18x86_64) 60;
rpm_fedora19i386 = makeRPM_i686 (diskImageFuns: diskImageFuns.fedora19i386) 70; rpm_fedora19i386 = makeRPM_i686 (diskImageFuns: diskImageFuns.fedora19i386) 70;
rpm_fedora19x86_64 = makeRPM_x86_64 (diskImageFunsFun: diskImageFunsFun.fedora19x86_64) 70; rpm_fedora19x86_64 = makeRPM_x86_64 (diskImageFunsFun: diskImageFunsFun.fedora19x86_64) 70;
rpm_fedora20i386 = makeRPM_i686 (diskImageFuns: diskImageFuns.fedora20i386) 70;
rpm_fedora20x86_64 = makeRPM_x86_64 (diskImageFunsFun: diskImageFunsFun.fedora20x86_64) 70;
deb_debian60i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.debian60i386) 50; deb_debian60i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.debian60i386) 50;
@ -212,15 +214,15 @@ let
meta.description = "Release-critical builds"; meta.description = "Release-critical builds";
constituents = constituents =
[ tarball [ tarball
build.i686-freebsd #build.i686-freebsd
build.i686-linux build.i686-linux
build.x86_64-darwin build.x86_64-darwin
build.x86_64-freebsd #build.x86_64-freebsd
build.x86_64-linux build.x86_64-linux
binaryTarball.i686-freebsd #binaryTarball.i686-freebsd
binaryTarball.i686-linux binaryTarball.i686-linux
binaryTarball.x86_64-darwin binaryTarball.x86_64-darwin
binaryTarball.x86_64-freebsd #binaryTarball.x86_64-freebsd
binaryTarball.x86_64-linux binaryTarball.x86_64-linux
deb_debian7i386 deb_debian7i386
deb_debian7x86_64 deb_debian7x86_64
@ -230,6 +232,8 @@ let
deb_ubuntu1310x86_64 deb_ubuntu1310x86_64
rpm_fedora19i386 rpm_fedora19i386
rpm_fedora19x86_64 rpm_fedora19x86_64
rpm_fedora20i386
rpm_fedora20x86_64
tests.remote_builds tests.remote_builds
tests.nix_copy_closure tests.nix_copy_closure
]; ];

View file

@ -42,6 +42,8 @@ my $caBundle = $ENV{"CURL_CA_BUNDLE"} // $ENV{"OPENSSL_X509_CERT_FILE"};
my $userName = getpwuid($<) or die "cannot figure out user name"; my $userName = getpwuid($<) or die "cannot figure out user name";
my $requireSignedBinaryCaches = ($Nix::Config::config{"signed-binary-caches"} // "0") ne "0";
sub addRequest { sub addRequest {
my ($storePath, $url, $head) = @_; my ($storePath, $url, $head) = @_;
@ -120,9 +122,10 @@ sub processRequests {
sub initCache { sub initCache {
my $dbPath = "$Nix::Config::stateDir/binary-cache-v2.sqlite"; my $dbPath = "$Nix::Config::stateDir/binary-cache-v3.sqlite";
unlink "$Nix::Config::stateDir/binary-cache-v1.sqlite"; unlink "$Nix::Config::stateDir/binary-cache-v1.sqlite";
unlink "$Nix::Config::stateDir/binary-cache-v2.sqlite";
# Open/create the database. # Open/create the database.
$dbh = DBI->connect("dbi:SQLite:dbname=$dbPath", "", "") $dbh = DBI->connect("dbi:SQLite:dbname=$dbPath", "", "")
@ -159,7 +162,7 @@ EOF
narSize integer, narSize integer,
refs text, refs text,
deriver text, deriver text,
system text, signedBy text,
timestamp integer not null, timestamp integer not null,
primary key (cache, storePath), primary key (cache, storePath),
foreign key (cache) references BinaryCaches(id) on delete cascade foreign key (cache) references BinaryCaches(id) on delete cascade
@ -183,7 +186,7 @@ EOF
$insertNAR = $dbh->prepare( $insertNAR = $dbh->prepare(
"insert or replace into NARs(cache, storePath, url, compression, fileHash, fileSize, narHash, " . "insert or replace into NARs(cache, storePath, url, compression, fileHash, fileSize, narHash, " .
"narSize, refs, deriver, system, timestamp) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") or die; "narSize, refs, deriver, signedBy, timestamp) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") or die;
$queryNAR = $dbh->prepare("select * from NARs where cache = ? and storePath = ?") or die; $queryNAR = $dbh->prepare("select * from NARs where cache = ? and storePath = ?") or die;
@ -309,14 +312,16 @@ sub processNARInfo {
return undef; return undef;
} }
my $narInfo = parseNARInfo($storePath, $request->{content}); my $narInfo = parseNARInfo($storePath, $request->{content}, $requireSignedBinaryCaches, $request->{url});
return undef unless defined $narInfo; return undef unless defined $narInfo;
die if $requireSignedBinaryCaches && !defined $narInfo->{signedBy};
# Cache the result. # Cache the result.
$insertNAR->execute( $insertNAR->execute(
$cache->{id}, basename($storePath), $narInfo->{url}, $narInfo->{compression}, $cache->{id}, basename($storePath), $narInfo->{url}, $narInfo->{compression},
$narInfo->{fileHash}, $narInfo->{fileSize}, $narInfo->{narHash}, $narInfo->{narSize}, $narInfo->{fileHash}, $narInfo->{fileSize}, $narInfo->{narHash}, $narInfo->{narSize},
join(" ", @{$narInfo->{refs}}), $narInfo->{deriver}, $narInfo->{system}, time()) join(" ", @{$narInfo->{refs}}), $narInfo->{deriver}, $narInfo->{signedBy}, time())
if shouldCache $request->{url}; if shouldCache $request->{url};
return $narInfo; return $narInfo;
@ -330,6 +335,10 @@ sub getCachedInfoFrom {
my $res = $queryNAR->fetchrow_hashref(); my $res = $queryNAR->fetchrow_hashref();
return undef unless defined $res; return undef unless defined $res;
# We may previously have cached this info when signature checking
# was disabled. In that case, ignore the cached info.
return undef if $requireSignedBinaryCaches && !defined $res->{signedBy};
return return
{ url => $res->{url} { url => $res->{url}
, compression => $res->{compression} , compression => $res->{compression}
@ -339,6 +348,7 @@ sub getCachedInfoFrom {
, narSize => $res->{narSize} , narSize => $res->{narSize}
, refs => [ split " ", $res->{refs} ] , refs => [ split " ", $res->{refs} ]
, deriver => $res->{deriver} , deriver => $res->{deriver}
, signedBy => $res->{signedBy}
} if defined $res; } if defined $res;
} }
@ -522,7 +532,8 @@ sub downloadBinary {
next; next;
} }
my $url = "$cache->{url}/$info->{url}"; # FIXME: handle non-relative URLs my $url = "$cache->{url}/$info->{url}"; # FIXME: handle non-relative URLs
print STDERR "\n*** Downloading $url to $storePath...\n"; die if $requireSignedBinaryCaches && !defined $info->{signedBy};
print STDERR "\n*** Downloading $url ", ($requireSignedBinaryCaches ? "(signed by $info->{signedBy}) " : ""), "to $storePath...\n";
checkURL $url; checkURL $url;
if (system("$Nix::Config::curl --fail --location --insecure '$url' $decompressor | $Nix::Config::binDir/nix-store --restore $destPath") != 0) { if (system("$Nix::Config::curl --fail --location --insecure '$url' $decompressor | $Nix::Config::binDir/nix-store --restore $destPath") != 0) {
warn "download of `$url' failed" . ($! ? ": $!" : "") . "\n"; warn "download of `$url' failed" . ($! ? ": $!" : "") . "\n";
@ -530,6 +541,7 @@ sub downloadBinary {
} }
# Tell Nix about the expected hash so it can verify it. # Tell Nix about the expected hash so it can verify it.
die unless defined $info->{narHash} && $info->{narHash} ne "";
print "$info->{narHash}\n"; print "$info->{narHash}\n";
print STDERR "\n"; print STDERR "\n";

View file

@ -152,6 +152,8 @@ for (my $n = 0; $n < scalar @ARGV; $n++) {
@exprs = ("./default.nix") if scalar @exprs == 0; @exprs = ("./default.nix") if scalar @exprs == 0;
$ENV{'IN_NIX_SHELL'} = 1 if $runEnv;
foreach my $expr (@exprs) { foreach my $expr (@exprs) {
@ -177,13 +179,13 @@ foreach my $expr (@exprs) {
# Build or fetch all dependencies of the derivation. # Build or fetch all dependencies of the derivation.
my @inputDrvs = grep { my $x = $_; (grep { $x =~ $_ } @envExclude) == 0 } @{$drv->{inputDrvs}}; my @inputDrvs = grep { my $x = $_; (grep { $x =~ $_ } @envExclude) == 0 } @{$drv->{inputDrvs}};
system("$Nix::Config::binDir/nix-store -r @buildArgs @inputDrvs @{$drv->{inputSrcs}} > /dev/null") == 0 system("$Nix::Config::binDir/nix-store", "-r", "--no-output", "--no-gc-warning", @buildArgs, @inputDrvs, @{$drv->{inputSrcs}}) == 0
or die "$0: failed to build all dependencies\n"; or die "$0: failed to build all dependencies\n";
# Set the environment. # Set the environment.
if ($pure) { if ($pure) {
foreach my $name (keys %ENV) { foreach my $name (keys %ENV) {
next if $name eq "HOME" || $name eq "USER" || $name eq "LOGNAME" || $name eq "DISPLAY" || $name eq "PATH"; next if $name eq "HOME" || $name eq "USER" || $name eq "LOGNAME" || $name eq "DISPLAY" || $name eq "PATH" || $name eq "TERM" || $name eq "IN_NIX_SHELL";
delete $ENV{$name}; delete $ENV{$name};
} }
# NixOS hack: prevent /etc/bashrc from sourcing /etc/profile. # NixOS hack: prevent /etc/bashrc from sourcing /etc/profile.
@ -193,8 +195,6 @@ foreach my $expr (@exprs) {
$ENV{'NIX_STORE'} = $Nix::Config::storeDir; $ENV{'NIX_STORE'} = $Nix::Config::storeDir;
$ENV{$_} = $drv->{env}->{$_} foreach keys %{$drv->{env}}; $ENV{$_} = $drv->{env}->{$_} foreach keys %{$drv->{env}};
$ENV{'IN_NIX_SHELL'} = 1;
# Run a shell using the derivation's environment. For # Run a shell using the derivation's environment. For
# convenience, source $stdenv/setup to setup additional # convenience, source $stdenv/setup to setup additional
# environment variables and shell functions. Also don't lose # environment variables and shell functions. Also don't lose
@ -211,6 +211,7 @@ foreach my $expr (@exprs) {
'set +e; ' . 'set +e; ' .
'[ -n "$PS1" ] && PS1="\n\[\033[1;32m\][nix-shell:\w]$\[\033[0m\] "; ' . '[ -n "$PS1" ] && PS1="\n\[\033[1;32m\][nix-shell:\w]$\[\033[0m\] "; ' .
'unset NIX_ENFORCE_PURITY; ' . 'unset NIX_ENFORCE_PURITY; ' .
'unset NIX_INDENT_MAKE; ' .
'shopt -u nullglob; ' . 'shopt -u nullglob; ' .
$envCommand); $envCommand);
$ENV{BASH_ENV} = $rcfile; $ENV{BASH_ENV} = $rcfile;

View file

@ -8,10 +8,15 @@ if test -n "$HOME"; then
@coreutils@/ln -s "$_NIX_DEF_LINK" "$NIX_LINK" @coreutils@/ln -s "$_NIX_DEF_LINK" "$NIX_LINK"
fi fi
export PATH=$NIX_LINK/bin:$PATH
# Subscribe the root user to the Nixpkgs channel by default. # Subscribe the root user to the Nixpkgs channel by default.
if [ ! -e $HOME/.nix-channels ]; then if [ ! -e $HOME/.nix-channels ]; then
echo "http://nixos.org/channels/nixpkgs-unstable nixpkgs" > $HOME/.nix-channels echo "http://nixos.org/channels/nixpkgs-unstable nixpkgs" > $HOME/.nix-channels
fi fi
export PATH=$NIX_LINK/bin:$PATH # Append ~/.nix-defexpr/channels/nixpkgs to $NIX_PATH so that
# <nixpkgs> paths work when the user has fetched the Nixpkgs
# channel.
export NIX_PATH=${NIX_PATH:+$NIX_PATH:}nixpkgs=$HOME/.nix-defexpr/channels/nixpkgs
fi fi

View file

@ -10,6 +10,7 @@ use Nix::Config;
use Nix::Store; use Nix::Store;
use Nix::Manifest; use Nix::Manifest;
use Nix::Utils; use Nix::Utils;
use Nix::Crypto;
my $tmpDir = tempdir("nix-push.XXXXXX", CLEANUP => 1, TMPDIR => 1) my $tmpDir = tempdir("nix-push.XXXXXX", CLEANUP => 1, TMPDIR => 1)
or die "cannot create a temporary directory"; or die "cannot create a temporary directory";
@ -25,6 +26,8 @@ my $writeManifest = 0;
my $manifestPath; my $manifestPath;
my $archivesURL; my $archivesURL;
my $link = 0; my $link = 0;
my $privateKeyFile;
my $keyName;
my @roots; my @roots;
for (my $n = 0; $n < scalar @ARGV; $n++) { for (my $n = 0; $n < scalar @ARGV; $n++) {
@ -57,6 +60,14 @@ for (my $n = 0; $n < scalar @ARGV; $n++) {
$archivesURL = $ARGV[$n]; $archivesURL = $ARGV[$n];
} elsif ($arg eq "--link") { } elsif ($arg eq "--link") {
$link = 1; $link = 1;
} elsif ($arg eq "--key") {
$n++;
die "$0: `$arg' requires an argument\n" unless $n < scalar @ARGV;
$privateKeyFile = $ARGV[$n];
} elsif ($arg eq "--key-name") {
$n++;
die "$0: `$arg' requires an argument\n" unless $n < scalar @ARGV;
$keyName = $ARGV[$n];
} elsif (substr($arg, 0, 1) eq "-") { } elsif (substr($arg, 0, 1) eq "-") {
die "$0: unknown flag `$arg'\n"; die "$0: unknown flag `$arg'\n";
} else { } else {
@ -99,7 +110,7 @@ foreach my $storePath (@storePaths) {
my $pathHash = substr(basename($storePath), 0, 32); my $pathHash = substr(basename($storePath), 0, 32);
my $narInfoFile = "$destDir/$pathHash.narinfo"; my $narInfoFile = "$destDir/$pathHash.narinfo";
if (-e $narInfoFile) { if (-e $narInfoFile) {
my $narInfo = parseNARInfo($storePath, readFile($narInfoFile)); my $narInfo = parseNARInfo($storePath, readFile($narInfoFile), 0, $narInfoFile) or die "cannot read `$narInfoFile'\n";
my $narFile = "$destDir/$narInfo->{url}"; my $narFile = "$destDir/$narInfo->{url}";
if (-e $narFile) { if (-e $narFile) {
print STDERR "skipping existing $storePath\n"; print STDERR "skipping existing $storePath\n";
@ -245,6 +256,11 @@ for (my $n = 0; $n < scalar @storePaths2; $n++) {
} }
} }
if (defined $privateKeyFile && defined $keyName) {
my $sig = signString($privateKeyFile, $info);
$info .= "Signature: 1;$keyName;$sig\n";
}
my $pathHash = substr(basename($storePath), 0, 32); my $pathHash = substr(basename($storePath), 0, 32);
$dst = "$destDir/$pathHash.narinfo"; $dst = "$destDir/$pathHash.narinfo";

View file

@ -137,6 +137,18 @@ static void * oomHandler(size_t requested)
} }
static Symbol getName(const AttrName & name, EvalState & state, Env & env) {
if (name.symbol.set()) {
return name.symbol;
} else {
Value nameValue;
name.expr->eval(state, env, nameValue);
state.forceStringNoCtx(nameValue);
return state.symbols.create(nameValue.string.s);
}
}
EvalState::EvalState() EvalState::EvalState()
: sWith(symbols.create("<with>")) : sWith(symbols.create("<with>"))
, sOutPath(symbols.create("outPath")) , sOutPath(symbols.create("outPath"))
@ -264,6 +276,11 @@ LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2, con
throw EvalError(format(s) % s2 % s3); throw EvalError(format(s) % s2 % s3);
} }
LocalNoInlineNoReturn(void throwEvalError(const char * s, const Symbol & sym, const Pos & p1, const Pos & p2))
{
throw EvalError(format(s) % sym % p1 % p2);
}
LocalNoInlineNoReturn(void throwTypeError(const char * s)) LocalNoInlineNoReturn(void throwTypeError(const char * s))
{ {
throw TypeError(s); throw TypeError(s);
@ -574,12 +591,14 @@ void ExprPath::eval(EvalState & state, Env & env, Value & v)
void ExprAttrs::eval(EvalState & state, Env & env, Value & v) void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
{ {
state.mkAttrs(v, attrs.size()); state.mkAttrs(v, attrs.size());
Env *dynamicEnv = &env;
if (recursive) { if (recursive) {
/* Create a new environment that contains the attributes in /* Create a new environment that contains the attributes in
this `rec'. */ this `rec'. */
Env & env2(state.allocEnv(attrs.size())); Env & env2(state.allocEnv(attrs.size()));
env2.up = &env; env2.up = &env;
dynamicEnv = &env2;
AttrDefs::iterator overrides = attrs.find(state.sOverrides); AttrDefs::iterator overrides = attrs.find(state.sOverrides);
bool hasOverrides = overrides != attrs.end(); bool hasOverrides = overrides != attrs.end();
@ -622,9 +641,24 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
} }
} }
else { else
foreach (AttrDefs::iterator, i, attrs) foreach (AttrDefs::iterator, i, attrs)
v.attrs->push_back(Attr(i->first, i->second.e->maybeThunk(state, env), &i->second.pos)); v.attrs->push_back(Attr(i->first, i->second.e->maybeThunk(state, env), &i->second.pos));
/* dynamic attrs apply *after* rec and __overrides */
foreach (DynamicAttrDefs::iterator, i, dynamicAttrs) {
Value nameVal;
i->nameExpr->eval(state, *dynamicEnv, nameVal);
state.forceStringNoCtx(nameVal);
Symbol nameSym = state.symbols.create(nameVal.string.s);
Bindings::iterator j = v.attrs->find(nameSym);
if (j != v.attrs->end())
throwEvalError("dynamic attribute `%1%' at %2% already defined at %3%", nameSym, i->pos, *j->pos);
i->valueExpr->setName(nameSym);
/* Keep sorted order so find can catch duplicates */
v.attrs->insert(lower_bound(v.attrs->begin(), v.attrs->end(), Attr(nameSym, 0)),
Attr(nameSym, i->valueExpr->maybeThunk(state, *dynamicEnv), &i->pos));
} }
} }
@ -678,17 +712,18 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
foreach (AttrPath::const_iterator, i, attrPath) { foreach (AttrPath::const_iterator, i, attrPath) {
nrLookups++; nrLookups++;
Bindings::iterator j; Bindings::iterator j;
Symbol name = getName(*i, state, env);
if (def) { if (def) {
state.forceValue(*vAttrs); state.forceValue(*vAttrs);
if (vAttrs->type != tAttrs || if (vAttrs->type != tAttrs ||
(j = vAttrs->attrs->find(*i)) == vAttrs->attrs->end()) (j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
{ {
def->eval(state, env, v); def->eval(state, env, v);
return; return;
} }
} else { } else {
state.forceAttrs(*vAttrs); state.forceAttrs(*vAttrs);
if ((j = vAttrs->attrs->find(*i)) == vAttrs->attrs->end()) if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
throwEvalError("attribute `%1%' missing", showAttrPath(attrPath)); throwEvalError("attribute `%1%' missing", showAttrPath(attrPath));
} }
vAttrs = j->value; vAttrs = j->value;
@ -719,8 +754,9 @@ void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v)
foreach (AttrPath::const_iterator, i, attrPath) { foreach (AttrPath::const_iterator, i, attrPath) {
state.forceValue(*vAttrs); state.forceValue(*vAttrs);
Bindings::iterator j; Bindings::iterator j;
Symbol name = getName(*i, state, env);
if (vAttrs->type != tAttrs || if (vAttrs->type != tAttrs ||
(j = vAttrs->attrs->find(*i)) == vAttrs->attrs->end()) (j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
{ {
mkBool(v, false); mkBool(v, false);
return; return;
@ -925,6 +961,17 @@ void ExprOpNot::eval(EvalState & state, Env & env, Value & v)
} }
void ExprBuiltin::eval(EvalState & state, Env & env, Value & v)
{
// Not a hot path at all, but would be nice to access state.baseEnv directly
Env *baseEnv = &env;
while (baseEnv->up) baseEnv = baseEnv->up;
Bindings::iterator binding = baseEnv->values[0]->attrs->find(name);
assert(binding != baseEnv->values[0]->attrs->end());
v = *binding->value;
}
void ExprOpEq::eval(EvalState & state, Env & env, Value & v) void ExprOpEq::eval(EvalState & state, Env & env, Value & v)
{ {
Value v1; e1->eval(state, env, v1); Value v1; e1->eval(state, env, v1);

View file

@ -117,6 +117,8 @@ or { return OR_KW; }
return INT; return INT;
} }
\$\{ { return DOLLAR_CURLY; }
\" { BEGIN(STRING); return '"'; } \" { BEGIN(STRING); return '"'; }
<STRING>([^\$\"\\]|\$[^\{\"]|\\.)+ { <STRING>([^\$\"\\]|\$[^\{\"]|\\.)+ {
/* !!! Not quite right: we want a follow restriction on /* !!! Not quite right: we want a follow restriction on

View file

@ -61,6 +61,8 @@ void ExprAttrs::show(std::ostream & str)
str << "inherit " << i->first << " " << "; "; str << "inherit " << i->first << " " << "; ";
else else
str << i->first << " = " << *i->second.e << "; "; str << i->first << " = " << *i->second.e << "; ";
foreach (DynamicAttrDefs::iterator, i, dynamicAttrs)
str << "\"${" << *i->nameExpr << "}\" = " << *i->valueExpr << "; ";
str << "}"; str << "}";
} }
@ -121,6 +123,11 @@ void ExprOpNot::show(std::ostream & str)
str << "! " << *e; str << "! " << *e;
} }
void ExprBuiltin::show(std::ostream & str)
{
str << "builtins." << name;
}
void ExprConcatStrings::show(std::ostream & str) void ExprConcatStrings::show(std::ostream & str)
{ {
bool first = true; bool first = true;
@ -148,12 +155,19 @@ std::ostream & operator << (std::ostream & str, const Pos & pos)
string showAttrPath(const AttrPath & attrPath) string showAttrPath(const AttrPath & attrPath)
{ {
string s; std::ostringstream out;
bool first = true;
foreach (AttrPath::const_iterator, i, attrPath) { foreach (AttrPath::const_iterator, i, attrPath) {
if (!s.empty()) s += '.'; if (!first)
s += *i; out << '.';
else
first = false;
if (i->symbol.set())
out << i->symbol;
else
out << "\"${" << *i->expr << "}\"";
} }
return s; return out.str();
} }
@ -213,17 +227,25 @@ void ExprSelect::bindVars(const StaticEnv & env)
{ {
e->bindVars(env); e->bindVars(env);
if (def) def->bindVars(env); if (def) def->bindVars(env);
foreach (AttrPath::iterator, i, attrPath)
if (!i->symbol.set())
i->expr->bindVars(env);
} }
void ExprOpHasAttr::bindVars(const StaticEnv & env) void ExprOpHasAttr::bindVars(const StaticEnv & env)
{ {
e->bindVars(env); e->bindVars(env);
foreach (AttrPath::iterator, i, attrPath)
if (!i->symbol.set())
i->expr->bindVars(env);
} }
void ExprAttrs::bindVars(const StaticEnv & env) void ExprAttrs::bindVars(const StaticEnv & env)
{ {
const StaticEnv *dynamicEnv = &env;
if (recursive) { if (recursive) {
StaticEnv newEnv(false, &env); StaticEnv newEnv(false, &env);
dynamicEnv = &newEnv;
unsigned int displ = 0; unsigned int displ = 0;
foreach (AttrDefs::iterator, i, attrs) foreach (AttrDefs::iterator, i, attrs)
@ -236,6 +258,11 @@ void ExprAttrs::bindVars(const StaticEnv & env)
else else
foreach (AttrDefs::iterator, i, attrs) foreach (AttrDefs::iterator, i, attrs)
i->second.e->bindVars(env); i->second.e->bindVars(env);
foreach (DynamicAttrDefs::iterator, i, dynamicAttrs) {
i->nameExpr->bindVars(*dynamicEnv);
i->valueExpr->bindVars(*dynamicEnv);
}
} }
void ExprList::bindVars(const StaticEnv & env) void ExprList::bindVars(const StaticEnv & env)
@ -314,6 +341,10 @@ void ExprOpNot::bindVars(const StaticEnv & env)
e->bindVars(env); e->bindVars(env);
} }
void ExprBuiltin::bindVars(const StaticEnv & env)
{
}
void ExprConcatStrings::bindVars(const StaticEnv & env) void ExprConcatStrings::bindVars(const StaticEnv & env)
{ {
foreach (vector<Expr *>::iterator, i, *es) foreach (vector<Expr *>::iterator, i, *es)

View file

@ -50,10 +50,19 @@ struct Env;
struct Value; struct Value;
struct EvalState; struct EvalState;
struct StaticEnv; struct StaticEnv;
struct Expr;
/* An attribute path is a sequence of attribute names. */ /* An attribute path is a sequence of attribute names. */
typedef vector<Symbol> AttrPath; struct AttrName
{
Symbol symbol;
Expr *expr;
AttrName(const Symbol & s) : symbol(s) {};
AttrName(Expr *e) : expr(e) {};
};
typedef std::vector<AttrName> AttrPath;
string showAttrPath(const AttrPath & attrPath); string showAttrPath(const AttrPath & attrPath);
@ -138,7 +147,7 @@ struct ExprSelect : Expr
Expr * e, * def; Expr * e, * def;
AttrPath attrPath; AttrPath attrPath;
ExprSelect(Expr * e, const AttrPath & attrPath, Expr * def) : e(e), def(def), attrPath(attrPath) { }; ExprSelect(Expr * e, const AttrPath & attrPath, Expr * def) : e(e), def(def), attrPath(attrPath) { };
ExprSelect(Expr * e, const Symbol & name) : e(e), def(0) { attrPath.push_back(name); }; ExprSelect(Expr * e, const Symbol & name) : e(e), def(0) { attrPath.push_back(AttrName(name)); };
COMMON_METHODS COMMON_METHODS
}; };
@ -163,6 +172,14 @@ struct ExprAttrs : Expr
}; };
typedef std::map<Symbol, AttrDef> AttrDefs; typedef std::map<Symbol, AttrDef> AttrDefs;
AttrDefs attrs; AttrDefs attrs;
struct DynamicAttrDef {
Expr * nameExpr;
Expr * valueExpr;
Pos pos;
DynamicAttrDef(Expr * nameExpr, Expr * valueExpr, const Pos & pos) : nameExpr(nameExpr), valueExpr(valueExpr), pos(pos) { };
};
typedef std::vector<DynamicAttrDef> DynamicAttrDefs;
DynamicAttrDefs dynamicAttrs;
ExprAttrs() : recursive(false) { }; ExprAttrs() : recursive(false) { };
COMMON_METHODS COMMON_METHODS
}; };
@ -248,6 +265,13 @@ struct ExprOpNot : Expr
COMMON_METHODS COMMON_METHODS
}; };
struct ExprBuiltin : Expr
{
Symbol name;
ExprBuiltin(const Symbol & name) : name(name) { };
COMMON_METHODS
};
#define MakeBinOp(name, s) \ #define MakeBinOp(name, s) \
struct Expr##name : Expr \ struct Expr##name : Expr \
{ \ { \

View file

@ -74,37 +74,49 @@ static void dupAttr(const AttrPath & attrPath, const Pos & pos, const Pos & prev
static void dupAttr(Symbol attr, const Pos & pos, const Pos & prevPos) static void dupAttr(Symbol attr, const Pos & pos, const Pos & prevPos)
{ {
AttrPath attrPath; attrPath.push_back(attr);
throw ParseError(format("attribute `%1%' at %2% already defined at %3%") throw ParseError(format("attribute `%1%' at %2% already defined at %3%")
% showAttrPath(attrPath) % pos % prevPos); % attr % pos % prevPos);
} }
static void addAttr(ExprAttrs * attrs, AttrPath & attrPath, static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
Expr * e, const Pos & pos) Expr * e, const Pos & pos)
{ {
unsigned int n = 0; AttrPath::iterator i;
foreach (AttrPath::const_iterator, i, attrPath) { // All attrpaths have at least one attr
n++; assert(!attrPath.empty());
ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(*i); for (i = attrPath.begin(); i + 1 < attrPath.end(); i++) {
if (i->symbol.set()) {
ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(i->symbol);
if (j != attrs->attrs.end()) { if (j != attrs->attrs.end()) {
if (!j->second.inherited) { if (!j->second.inherited) {
ExprAttrs * attrs2 = dynamic_cast<ExprAttrs *>(j->second.e); ExprAttrs * attrs2 = dynamic_cast<ExprAttrs *>(j->second.e);
if (!attrs2 || n == attrPath.size()) dupAttr(attrPath, pos, j->second.pos); if (!attrs2) dupAttr(attrPath, pos, j->second.pos);
attrs = attrs2; attrs = attrs2;
} else } else
dupAttr(attrPath, pos, j->second.pos); dupAttr(attrPath, pos, j->second.pos);
} else { } else {
if (n == attrPath.size())
attrs->attrs[*i] = ExprAttrs::AttrDef(e, pos);
else {
ExprAttrs * nested = new ExprAttrs; ExprAttrs * nested = new ExprAttrs;
attrs->attrs[*i] = ExprAttrs::AttrDef(nested, pos); attrs->attrs[i->symbol] = ExprAttrs::AttrDef(nested, pos);
attrs = nested;
}
} else {
ExprAttrs *nested = new ExprAttrs;
attrs->dynamicAttrs.push_back(ExprAttrs::DynamicAttrDef(i->expr, nested, pos));
attrs = nested; attrs = nested;
} }
} }
if (i->symbol.set()) {
ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(i->symbol);
if (j != attrs->attrs.end()) {
dupAttr(attrPath, pos, j->second.pos);
} else {
attrs->attrs[i->symbol] = ExprAttrs::AttrDef(e, pos);
e->setName(i->symbol);
}
} else {
attrs->dynamicAttrs.push_back(ExprAttrs::DynamicAttrDef(i->expr, e, pos));
} }
e->setName(attrPath.back());
} }
@ -243,7 +255,7 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
char * id; // !!! -> Symbol char * id; // !!! -> Symbol
char * path; char * path;
char * uri; char * uri;
std::vector<nix::Symbol> * attrNames; std::vector<nix::AttrName> * attrNames;
std::vector<nix::Expr *> * string_parts; std::vector<nix::Expr *> * string_parts;
} }
@ -254,7 +266,8 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
%type <formals> formals %type <formals> formals
%type <formal> formal %type <formal> formal
%type <attrNames> attrs attrpath %type <attrNames> attrs attrpath
%type <string_parts> string_parts ind_string_parts %type <string_parts> string_parts_interpolated ind_string_parts
%type <e> string_parts string_attr
%type <id> attr %type <id> attr
%token <id> ID ATTRPATH %token <id> ID ATTRPATH
%token <e> STR IND_STR %token <e> STR IND_STR
@ -300,7 +313,11 @@ expr_function
| WITH expr ';' expr_function | WITH expr ';' expr_function
{ $$ = new ExprWith(CUR_POS, $2, $4); } { $$ = new ExprWith(CUR_POS, $2, $4); }
| LET binds IN expr_function | LET binds IN expr_function
{ $$ = new ExprLet($2, $4); } { if (!$2->dynamicAttrs.empty())
throw ParseError(format("dynamic attributes not allowed in let at %1%")
% CUR_POS);
$$ = new ExprLet($2, $4);
}
| expr_if | expr_if
; ;
@ -311,13 +328,13 @@ expr_if
expr_op expr_op
: '!' expr_op %prec NOT { $$ = new ExprOpNot($2); } : '!' expr_op %prec NOT { $$ = new ExprOpNot($2); }
| '-' expr_op %prec NEGATE { $$ = new ExprApp(new ExprApp(new ExprVar(noPos, data->symbols.create("__sub")), new ExprInt(0)), $2); } | '-' expr_op %prec NEGATE { $$ = new ExprApp(new ExprApp(new ExprBuiltin(data->symbols.create("sub")), new ExprInt(0)), $2); }
| expr_op EQ expr_op { $$ = new ExprOpEq($1, $3); } | expr_op EQ expr_op { $$ = new ExprOpEq($1, $3); }
| expr_op NEQ expr_op { $$ = new ExprOpNEq($1, $3); } | expr_op NEQ expr_op { $$ = new ExprOpNEq($1, $3); }
| expr_op '<' expr_op { $$ = new ExprApp(new ExprApp(new ExprVar(noPos, data->symbols.create("__lessThan")), $1), $3); } | expr_op '<' expr_op { $$ = new ExprApp(new ExprApp(new ExprBuiltin(data->symbols.create("lessThan")), $1), $3); }
| expr_op LEQ expr_op { $$ = new ExprOpNot(new ExprApp(new ExprApp(new ExprVar(noPos, data->symbols.create("__lessThan")), $3), $1)); } | expr_op LEQ expr_op { $$ = new ExprOpNot(new ExprApp(new ExprApp(new ExprBuiltin(data->symbols.create("lessThan")), $3), $1)); }
| expr_op '>' expr_op { $$ = new ExprApp(new ExprApp(new ExprVar(noPos, data->symbols.create("__lessThan")), $3), $1); } | expr_op '>' expr_op { $$ = new ExprApp(new ExprApp(new ExprBuiltin(data->symbols.create("lessThan")), $3), $1); }
| expr_op GEQ expr_op { $$ = new ExprOpNot(new ExprApp(new ExprApp(new ExprVar(noPos, data->symbols.create("__lessThan")), $1), $3)); } | expr_op GEQ expr_op { $$ = new ExprOpNot(new ExprApp(new ExprApp(new ExprBuiltin(data->symbols.create("lessThan")), $1), $3)); }
| expr_op AND expr_op { $$ = new ExprOpAnd($1, $3); } | expr_op AND expr_op { $$ = new ExprOpAnd($1, $3); }
| expr_op OR expr_op { $$ = new ExprOpOr($1, $3); } | expr_op OR expr_op { $$ = new ExprOpOr($1, $3); }
| expr_op IMPL expr_op { $$ = new ExprOpImpl($1, $3); } | expr_op IMPL expr_op { $$ = new ExprOpImpl($1, $3); }
@ -329,9 +346,9 @@ expr_op
l->push_back($3); l->push_back($3);
$$ = new ExprConcatStrings(false, l); $$ = new ExprConcatStrings(false, l);
} }
| expr_op '-' expr_op { $$ = new ExprApp(new ExprApp(new ExprVar(noPos, data->symbols.create("__sub")), $1), $3); } | expr_op '-' expr_op { $$ = new ExprApp(new ExprApp(new ExprBuiltin(data->symbols.create("sub")), $1), $3); }
| expr_op '*' expr_op { $$ = new ExprApp(new ExprApp(new ExprVar(noPos, data->symbols.create("__mul")), $1), $3); } | expr_op '*' expr_op { $$ = new ExprApp(new ExprApp(new ExprBuiltin(data->symbols.create("mul")), $1), $3); }
| expr_op '/' expr_op { $$ = new ExprApp(new ExprApp(new ExprVar(noPos, data->symbols.create("__div")), $1), $3); } | expr_op '/' expr_op { $$ = new ExprApp(new ExprApp(new ExprBuiltin(data->symbols.create("div")), $1), $3); }
| expr_op CONCAT expr_op { $$ = new ExprOpConcatLists($1, $3); } | expr_op CONCAT expr_op { $$ = new ExprOpConcatLists($1, $3); }
| expr_app | expr_app
; ;
@ -362,12 +379,7 @@ expr_simple
$$ = new ExprVar(CUR_POS, data->symbols.create($1)); $$ = new ExprVar(CUR_POS, data->symbols.create($1));
} }
| INT { $$ = new ExprInt($1); } | INT { $$ = new ExprInt($1); }
| '"' string_parts '"' { | '"' string_parts '"' { $$ = $2; }
/* For efficiency, and to simplify parse trees a bit. */
if ($2->empty()) $$ = new ExprString(data->symbols.create(""));
else if ($2->size() == 1 && dynamic_cast<ExprString *>($2->front())) $$ = $2->front();
else $$ = new ExprConcatStrings(true, $2);
}
| IND_STRING_OPEN ind_string_parts IND_STRING_CLOSE { | IND_STRING_OPEN ind_string_parts IND_STRING_CLOSE {
$$ = stripIndentation(data->symbols, *$2); $$ = stripIndentation(data->symbols, *$2);
} }
@ -381,7 +393,7 @@ expr_simple
throw. */ throw. */
$$ = path2 == "" $$ = path2 == ""
? (Expr * ) new ExprApp( ? (Expr * ) new ExprApp(
new ExprVar(noPos, data->symbols.create("throw")), new ExprBuiltin(data->symbols.create("throw")),
new ExprString(data->symbols.create( new ExprString(data->symbols.create(
(format("file `%1%' was not found in the Nix search path (add it using $NIX_PATH or -I)") % path).str()))) (format("file `%1%' was not found in the Nix search path (add it using $NIX_PATH or -I)") % path).str())))
: (Expr * ) new ExprPath(path2); : (Expr * ) new ExprPath(path2);
@ -400,9 +412,27 @@ expr_simple
; ;
string_parts string_parts
: string_parts STR { $$ = $1; $1->push_back($2); } : STR
| string_parts DOLLAR_CURLY expr '}' { backToString(scanner); $$ = $1; $1->push_back($3); } | string_parts_interpolated { $$ = new ExprConcatStrings(true, $1); }
| { $$ = new vector<Expr *>; } | { $$ = new ExprString(data->symbols.create("")) }
;
string_parts_interpolated
: string_parts_interpolated STR { $$ = $1; $1->push_back($2); }
| string_parts_interpolated DOLLAR_CURLY expr '}' { backToString(scanner); $$ = $1; $1->push_back($3); }
| STR DOLLAR_CURLY expr '}'
{
backToString(scanner);
$$ = new vector<Expr *>;
$$->push_back($1);
$$->push_back($3);
}
| DOLLAR_CURLY expr '}'
{
backToString(scanner);
$$ = new vector<Expr *>;
$$->push_back($2);
}
; ;
ind_string_parts ind_string_parts
@ -416,39 +446,70 @@ binds
| binds INHERIT attrs ';' | binds INHERIT attrs ';'
{ $$ = $1; { $$ = $1;
foreach (AttrPath::iterator, i, *$3) { foreach (AttrPath::iterator, i, *$3) {
if ($$->attrs.find(*i) != $$->attrs.end()) if ($$->attrs.find(i->symbol) != $$->attrs.end())
dupAttr(*i, makeCurPos(@3, data), $$->attrs[*i].pos); dupAttr(i->symbol, makeCurPos(@3, data), $$->attrs[i->symbol].pos);
Pos pos = makeCurPos(@3, data); Pos pos = makeCurPos(@3, data);
$$->attrs[*i] = ExprAttrs::AttrDef(new ExprVar(CUR_POS, *i), pos, true); $$->attrs[i->symbol] = ExprAttrs::AttrDef(new ExprVar(CUR_POS, i->symbol), pos, true);
} }
} }
| binds INHERIT '(' expr ')' attrs ';' | binds INHERIT '(' expr ')' attrs ';'
{ $$ = $1; { $$ = $1;
/* !!! Should ensure sharing of the expression in $4. */ /* !!! Should ensure sharing of the expression in $4. */
foreach (vector<Symbol>::iterator, i, *$6) { foreach (AttrPath::iterator, i, *$6) {
if ($$->attrs.find(*i) != $$->attrs.end()) if ($$->attrs.find(i->symbol) != $$->attrs.end())
dupAttr(*i, makeCurPos(@6, data), $$->attrs[*i].pos); dupAttr(i->symbol, makeCurPos(@6, data), $$->attrs[i->symbol].pos);
$$->attrs[*i] = ExprAttrs::AttrDef(new ExprSelect($4, *i), makeCurPos(@6, data)); $$->attrs[i->symbol] = ExprAttrs::AttrDef(new ExprSelect($4, i->symbol), makeCurPos(@6, data));
} }
} }
| { $$ = new ExprAttrs; } | { $$ = new ExprAttrs; }
; ;
attrs attrs
: attrs attr { $$ = $1; $1->push_back(data->symbols.create($2)); /* !!! dangerous */ } : attrs attr { $$ = $1; $1->push_back(AttrName(data->symbols.create($2))); }
| { $$ = new vector<Symbol>; } | attrs string_attr
{ $$ = $1;
ExprString *str = dynamic_cast<ExprString *>($2);
if (str) {
$$->push_back(AttrName(str->s));
delete str;
} else
throw ParseError(format("dynamic attributes not allowed in inherit at %1%")
% makeCurPos(@2, data));
}
| { $$ = new AttrPath; }
; ;
attrpath attrpath
: attrpath '.' attr { $$ = $1; $1->push_back(data->symbols.create($3)); } : attrpath '.' attr { $$ = $1; $1->push_back(AttrName(data->symbols.create($3))); }
| attr { $$ = new vector<Symbol>; $$->push_back(data->symbols.create($1)); } | attrpath '.' string_attr
{ $$ = $1;
ExprString *str = dynamic_cast<ExprString *>($3);
if (str) {
$$->push_back(AttrName(str->s));
delete str;
} else
$$->push_back(AttrName($3));
}
| attr { $$ = new vector<AttrName>; $$->push_back(AttrName(data->symbols.create($1))); }
| string_attr
{ $$ = new vector<AttrName>;
ExprString *str = dynamic_cast<ExprString *>($1);
if (str) {
$$->push_back(AttrName(str->s));
delete str;
} else
$$->push_back(AttrName($1));
}
; ;
attr attr
: ID { $$ = $1; } : ID { $$ = $1; }
| OR_KW { $$ = "or"; } | OR_KW { $$ = "or"; }
| '"' STR '"' ;
{ $$ = strdup(((string) ((ExprString *) $2)->s).c_str()); delete $2; }
string_attr
: '"' string_parts '"' { $$ = $2; }
| DOLLAR_CURLY expr '}' { $$ = new ExprConcatStrings(true, new vector<Expr*>(1, $2)); }
; ;
expr_list expr_list

View file

@ -582,8 +582,8 @@ static void prim_storePath(EvalState & state, Value * * args, Value & v)
if (!isInStore(path)) if (!isInStore(path))
throw EvalError(format("path `%1%' is not in the Nix store") % path); throw EvalError(format("path `%1%' is not in the Nix store") % path);
Path path2 = toStorePath(path); Path path2 = toStorePath(path);
if (!store->isValidPath(path2)) if (!settings.readOnlyMode)
throw EvalError(format("store path `%1%' is not valid") % path2); store->ensurePath(path2);
context.insert(path2); context.insert(path2);
mkString(v, path, context); mkString(v, path, context);
} }
@ -1242,7 +1242,7 @@ void EvalState::createBaseEnv()
language feature gets added. It's not necessary to increase it language feature gets added. It's not necessary to increase it
when primops get added, because you can just use `builtins ? when primops get added, because you can just use `builtins ?
primOp' to check. */ primOp' to check. */
mkInt(v, 1); mkInt(v, 2);
addConstant("__langVersion", v); addConstant("__langVersion", v);
// Miscellaneous // Miscellaneous

View file

@ -31,8 +31,11 @@ static void sigintHandler(int signo)
} }
static bool gcWarning = true;
void printGCWarning() void printGCWarning()
{ {
if (!gcWarning) return;
static bool haveWarned = false; static bool haveWarned = false;
warnOnce(haveWarned, warnOnce(haveWarned,
"you did not specify `--add-root'; " "you did not specify `--add-root'; "
@ -208,6 +211,8 @@ static void initAndRun(int argc, char * * argv)
settings.useBuildHook = false; settings.useBuildHook = false;
else if (arg == "--show-trace") else if (arg == "--show-trace")
settings.showTrace = true; settings.showTrace = true;
else if (arg == "--no-gc-warning")
gcWarning = false;
else if (arg == "--option") { else if (arg == "--option") {
++i; if (i == args.end()) throw UsageError("`--option' requires two arguments"); ++i; if (i == args.end()) throw UsageError("`--option' requires two arguments");
string name = *i; string name = *i;

View file

@ -1011,7 +1011,7 @@ void DerivationGoal::outputsSubstituted()
trace("all outputs substituted (maybe)"); trace("all outputs substituted (maybe)");
if (nrFailed > 0 && nrFailed > nrNoSubstituters + nrIncompleteClosure && !settings.tryFallback) if (nrFailed > 0 && nrFailed > nrNoSubstituters + nrIncompleteClosure && !settings.tryFallback)
throw Error(format("some substitutes for the outputs of derivation `%1%' failed; try `--fallback'") % drvPath); throw Error(format("some substitutes for the outputs of derivation `%1%' failed (usually happens due to networking issues); try `--fallback' to build derivation from source ") % drvPath);
/* If the substitutes form an incomplete closure, then we should /* If the substitutes form an incomplete closure, then we should
build the dependencies of this derivation, but after that, we build the dependencies of this derivation, but after that, we
@ -1623,10 +1623,13 @@ void DerivationGoal::startBuilder()
startNest(nest, lvlInfo, format(repair ? "repairing path(s) %1%" : "building path(s) %1%") % showPaths(missingPaths)); startNest(nest, lvlInfo, format(repair ? "repairing path(s) %1%" : "building path(s) %1%") % showPaths(missingPaths));
/* Right platform? */ /* Right platform? */
if (!canBuildLocally(drv.platform)) if (!canBuildLocally(drv.platform)) {
if (settings.printBuildTrace)
printMsg(lvlError, format("@ unsupported-platform %1% %2%") % drvPath % drv.platform);
throw Error( throw Error(
format("a `%1%' is required to build `%3%', but I am a `%2%'") format("a `%1%' is required to build `%3%', but I am a `%2%'")
% drv.platform % settings.thisSystem % drvPath); % drv.platform % settings.thisSystem % drvPath);
}
/* Construct the environment passed to the builder. */ /* Construct the environment passed to the builder. */
@ -2746,6 +2749,7 @@ void SubstitutionGoal::finished()
logPipe.readSide.close(); logPipe.readSide.close();
/* Get the hash info from stdout. */ /* Get the hash info from stdout. */
string dummy = readLine(outPipe.readSide);
string expectedHashStr = statusOk(status) ? readLine(outPipe.readSide) : ""; string expectedHashStr = statusOk(status) ? readLine(outPipe.readSide) : "";
outPipe.readSide.close(); outPipe.readSide.close();

View file

@ -734,6 +734,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
/* Allow other processes to add to the store from here on. */ /* Allow other processes to add to the store from here on. */
fdGCLock.close(); fdGCLock.close();
fds.clear();
/* Delete the trash directory. */ /* Delete the trash directory. */
printMsg(lvlInfo, format("deleting `%1%'") % state.trashDir); printMsg(lvlInfo, format("deleting `%1%'") % state.trashDir);

View file

@ -233,6 +233,15 @@ static bool isPrebuilt(EvalState & state, DrvInfo & elem)
} }
static void checkSelectorUse(DrvNames & selectors)
{
/* Check that all selectors have been used. */
foreach (DrvNames::iterator, i, selectors)
if (i->hits == 0 && i->fullName != "*")
throw Error(format("selector `%1%' matches no derivations") % i->fullName);
}
static DrvInfos filterBySelector(EvalState & state, const DrvInfos & allElems, static DrvInfos filterBySelector(EvalState & state, const DrvInfos & allElems,
const Strings & args, bool newestOnly) const Strings & args, bool newestOnly)
{ {
@ -315,11 +324,7 @@ static DrvInfos filterBySelector(EvalState & state, const DrvInfos & allElems,
} }
} }
/* Check that all selectors have been used. */ checkSelectorUse(selectors);
foreach (DrvNames::iterator, i, selectors)
if (i->hits == 0 && i->fullName != "*")
throw Error(format("selector `%1%' matches no derivations")
% i->fullName);
return elems; return elems;
} }
@ -673,11 +678,14 @@ static void opSetFlag(Globals & globals,
foreach (DrvNames::iterator, j, selectors) foreach (DrvNames::iterator, j, selectors)
if (j->matches(drvName)) { if (j->matches(drvName)) {
printMsg(lvlInfo, format("setting flag on `%1%'") % i->name); printMsg(lvlInfo, format("setting flag on `%1%'") % i->name);
j->hits++;
setMetaFlag(globals.state, *i, flagName, flagValue); setMetaFlag(globals.state, *i, flagName, flagValue);
break; break;
} }
} }
checkSelectorUse(selectors);
/* Write the new user environment. */ /* Write the new user environment. */
if (createUserEnv(globals.state, installedElems, if (createUserEnv(globals.state, installedElems,
globals.profile, settings.envKeepDerivations, lockToken)) break; globals.profile, settings.envKeepDerivations, lockToken)) break;
@ -887,7 +895,6 @@ static void queryJSON(Globals & globals, vector<DrvInfo> & elems)
static void opQuery(Globals & globals, static void opQuery(Globals & globals,
Strings args, Strings opFlags, Strings opArgs) Strings args, Strings opFlags, Strings opArgs)
{ {
typedef vector< map<string, string> > ResultSet;
Strings remaining; Strings remaining;
string attrPath; string attrPath;

View file

@ -35,6 +35,7 @@ void printHelp()
static Path gcRoot; static Path gcRoot;
static int rootNr = 0; static int rootNr = 0;
static bool indirectRoot = false; static bool indirectRoot = false;
static bool noOutput = false;
LocalStore & ensureLocalStore() LocalStore & ensureLocalStore()
@ -139,6 +140,7 @@ static void opRealise(Strings opFlags, Strings opArgs)
if (!ignoreUnknown) if (!ignoreUnknown)
foreach (Paths::iterator, i, paths) { foreach (Paths::iterator, i, paths) {
PathSet paths = realisePath(*i, false); PathSet paths = realisePath(*i, false);
if (!noOutput)
foreach (PathSet::iterator, j, paths) foreach (PathSet::iterator, j, paths)
cout << format("%1%\n") % *j; cout << format("%1%\n") % *j;
} }
@ -900,6 +902,8 @@ void run(Strings args)
} }
else if (arg == "--indirect") else if (arg == "--indirect")
indirectRoot = true; indirectRoot = true;
else if (arg == "--no-output")
noOutput = true;
else if (arg[0] == '-') { else if (arg[0] == '-') {
opFlags.push_back(arg); opFlags.push_back(arg);
if (arg == "--max-freed" || arg == "--max-links" || arg == "--max-atime") { /* !!! hack */ if (arg == "--max-freed" || arg == "--max-links" || arg == "--max-atime") { /* !!! hack */

View file

@ -35,6 +35,7 @@
-e "s^@version\@^$(VERSION)^g" \ -e "s^@version\@^$(VERSION)^g" \
-e "s^@perlbindings\@^$(perlbindings)^g" \ -e "s^@perlbindings\@^$(perlbindings)^g" \
-e "s^@testPath\@^$(coreutils):$$(dirname $$(type -p expr))^g" \ -e "s^@testPath\@^$(coreutils):$$(dirname $$(type -p expr))^g" \
-e "s^@openssl\@^$(openssl_prog)^g" \
< $< > $@ || rm $@ < $< > $@ || rm $@
if test -x $<; then chmod +x $@; fi if test -x $<; then chmod +x $@; fi

View file

@ -40,6 +40,31 @@ nix-store --check-validity $outPath
nix-store -qR $outPath | grep input-2 nix-store -qR $outPath | grep input-2
# Test whether Nix notices if the NAR doesn't match the hash in the NAR info.
clearStore
nar=$(ls $cacheDir/*.nar.xz | head -n1)
mv $nar $nar.good
mkdir -p $TEST_ROOT/empty
nix-store --dump $TEST_ROOT/empty | xz > $nar
nix-build --option binary-caches "file://$cacheDir" dependencies.nix -o $TEST_ROOT/result 2>&1 | tee $TEST_ROOT/log
grep -q "hash mismatch in downloaded path" $TEST_ROOT/log
mv $nar.good $nar
# Test whether this unsigned cache is rejected if the user requires signed caches.
clearStore
rm -f $NIX_STATE_DIR/binary-cache*
if nix-store --option binary-caches "file://$cacheDir" --option signed-binary-caches '*' -r $outPath; then
echo "unsigned binary cache incorrectly accepted"
exit 1
fi
# Test whether fallback works if we have cached info but the # Test whether fallback works if we have cached info but the
# corresponding NAR has disappeared. # corresponding NAR has disappeared.
clearStore clearStore

View file

@ -0,0 +1 @@
true

View file

@ -0,0 +1 @@
{ a."${"b"}" = true; a."${"c"}" = false; }.a.b

View file

@ -0,0 +1 @@
{ binds = true; hasAttrs = true; multiAttrs = true; recBinds = true; selectAttrs = true; selectOrAttrs = true; }

View file

@ -0,0 +1,17 @@
let
aString = "a";
bString = "b";
in {
hasAttrs = { a.b = null; } ? ${aString}.b;
selectAttrs = { a.b = true; }.a.${bString};
selectOrAttrs = { }.${aString} or true;
binds = { ${aString}."${bString}c" = true; }.a.bc;
recBinds = rec { ${bString} = a; a = true; }.b;
multiAttrs = { ${aString} = true; ${bString} = false; }.a;
}

View file

@ -0,0 +1 @@
{ binds = true; hasAttrs = true; multiAttrs = true; recBinds = true; selectAttrs = true; selectOrAttrs = true; }

View file

@ -0,0 +1,17 @@
let
aString = "a";
bString = "b";
in {
hasAttrs = { a.b = null; } ? "${aString}".b;
selectAttrs = { a.b = true; }.a."${bString}";
selectOrAttrs = { }."${aString}" or true;
binds = { "${aString}"."${bString}c" = true; }.a.bc;
recBinds = rec { "${bString}" = a; a = true; }.b;
multiAttrs = { "${aString}" = true; "${bString}" = false; }.a;
}

View file

@ -0,0 +1 @@
false

View file

@ -0,0 +1,3 @@
let
throw = abort "Error!";
in (builtins.tryEval <foobaz>).success