Merge branch 'master' into make
Conflicts: src/libexpr/eval.cc
This commit is contained in:
		
						commit
						81628a6ccc
					
				
					 34 changed files with 473 additions and 103 deletions
				
			
		|  | @ -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> | ||||||
|  |  | ||||||
|  | @ -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 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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=) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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
									
								
							
							
						
						
									
										42
									
								
								perl/lib/Nix/Crypto.pm
									
										
									
									
									
										Normal 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; | ||||||
|  | @ -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; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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) { | ||||||
|  |  | ||||||
							
								
								
									
										16
									
								
								release.nix
									
										
									
									
									
								
							
							
						
						
									
										16
									
								
								release.nix
									
										
									
									
									
								
							|  | @ -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 | ||||||
|         ]; |         ]; | ||||||
|  |  | ||||||
|  | @ -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"; | ||||||
|  |  | ||||||
|  | @ -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; | ||||||
|  |  | ||||||
|  | @ -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 | ||||||
|  |  | ||||||
|  | @ -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"; | ||||||
|  |  | ||||||
|  | @ -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); | ||||||
|  |  | ||||||
|  | @ -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 | ||||||
|  |  | ||||||
|  | @ -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) | ||||||
|  |  | ||||||
|  | @ -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 \ | ||||||
|     { \ |     { \ | ||||||
|  |  | ||||||
|  | @ -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 (j != attrs->attrs.end()) { |         if (i->symbol.set()) { | ||||||
|             if (!j->second.inherited) { |             ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(i->symbol); | ||||||
|                 ExprAttrs * attrs2 = dynamic_cast<ExprAttrs *>(j->second.e); |             if (j != attrs->attrs.end()) { | ||||||
|                 if (!attrs2 || n == attrPath.size()) dupAttr(attrPath, pos, j->second.pos); |                 if (!j->second.inherited) { | ||||||
|                 attrs = attrs2; |                     ExprAttrs * attrs2 = dynamic_cast<ExprAttrs *>(j->second.e); | ||||||
|             } else |                     if (!attrs2) dupAttr(attrPath, pos, j->second.pos); | ||||||
|                 dupAttr(attrPath, pos, j->second.pos); |                     attrs = attrs2; | ||||||
|         } else { |                 } else | ||||||
|             if (n == attrPath.size()) |                     dupAttr(attrPath, pos, j->second.pos); | ||||||
|                 attrs->attrs[*i] = ExprAttrs::AttrDef(e, pos); |             } else { | ||||||
|             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; |                 attrs = nested; | ||||||
|             } |             } | ||||||
|  |         } else { | ||||||
|  |             ExprAttrs *nested = new ExprAttrs; | ||||||
|  |             attrs->dynamicAttrs.push_back(ExprAttrs::DynamicAttrDef(i->expr, nested, pos)); | ||||||
|  |             attrs = nested; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     e->setName(attrPath.back()); |     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)); | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -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 | ||||||
|  |  | ||||||
|  | @ -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
 | ||||||
|  |  | ||||||
|  | @ -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; | ||||||
|  |  | ||||||
|  | @ -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(); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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); | ||||||
|  |  | ||||||
|  | @ -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; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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,8 +140,9 @@ 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); | ||||||
|             foreach (PathSet::iterator, j, paths) |             if (!noOutput) | ||||||
|                 cout << format("%1%\n") % *j; |                 foreach (PathSet::iterator, j, paths) | ||||||
|  |                     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 */ | ||||||
|  |  | ||||||
|  | @ -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 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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 | ||||||
|  |  | ||||||
							
								
								
									
										1
									
								
								tests/lang/eval-okay-dynamic-attrs-2.exp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tests/lang/eval-okay-dynamic-attrs-2.exp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | ||||||
|  | true | ||||||
							
								
								
									
										1
									
								
								tests/lang/eval-okay-dynamic-attrs-2.nix
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tests/lang/eval-okay-dynamic-attrs-2.nix
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | ||||||
|  | { a."${"b"}" = true; a."${"c"}" = false; }.a.b | ||||||
							
								
								
									
										1
									
								
								tests/lang/eval-okay-dynamic-attrs-bare.exp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tests/lang/eval-okay-dynamic-attrs-bare.exp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | ||||||
|  | { binds = true; hasAttrs = true; multiAttrs = true; recBinds = true; selectAttrs = true; selectOrAttrs = true; } | ||||||
							
								
								
									
										17
									
								
								tests/lang/eval-okay-dynamic-attrs-bare.nix
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								tests/lang/eval-okay-dynamic-attrs-bare.nix
									
										
									
									
									
										Normal 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; | ||||||
|  | } | ||||||
							
								
								
									
										1
									
								
								tests/lang/eval-okay-dynamic-attrs.exp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tests/lang/eval-okay-dynamic-attrs.exp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | ||||||
|  | { binds = true; hasAttrs = true; multiAttrs = true; recBinds = true; selectAttrs = true; selectOrAttrs = true; } | ||||||
							
								
								
									
										17
									
								
								tests/lang/eval-okay-dynamic-attrs.nix
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								tests/lang/eval-okay-dynamic-attrs.nix
									
										
									
									
									
										Normal 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; | ||||||
|  | } | ||||||
							
								
								
									
										1
									
								
								tests/lang/eval-okay-redefine-builtin.exp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tests/lang/eval-okay-redefine-builtin.exp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | ||||||
|  | false | ||||||
							
								
								
									
										3
									
								
								tests/lang/eval-okay-redefine-builtin.nix
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								tests/lang/eval-okay-redefine-builtin.nix
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,3 @@ | ||||||
|  | let | ||||||
|  |   throw = abort "Error!"; | ||||||
|  | in (builtins.tryEval <foobaz>).success | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue