* Get rid of the substitutes database table (NIX-47). Instead, if we
need any info on substitutable paths, we just call the substituters (such as download-using-manifests.pl) directly. This means that it's no longer necessary for nix-pull to register substitutes or for nix-channel to clear them, which makes those operations much faster (NIX-95). Also, we don't have to worry about keeping nix-pull manifests (in /nix/var/nix/manifests) and the database in sync with each other. The downside is that there is some overhead in calling an external program to get the substitutes info. For instance, "nix-env -qas" takes a bit longer. Abolishing the substitutes table also makes the logic in local-store.cc simpler, as we don't need to store info for invalid paths. On the downside, you cannot do things like "nix-store -qR" on a substitutable but invalid path (but nobody did that anyway). * Never catch interrupts (the Interrupted exception).
This commit is contained in:
		
							parent
							
								
									4695f4edd6
								
							
						
					
					
						commit
						9e975458b4
					
				
					 24 changed files with 357 additions and 469 deletions
				
			
		|  | @ -649,36 +649,6 @@ $ gv graph.ps</screen> | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| <!--######################################################################--> |  | ||||||
| 
 |  | ||||||
| <!-- |  | ||||||
| <refsection><title>Operation <option>-XXX-substitute</option></title> |  | ||||||
| 
 |  | ||||||
| <refsection><title>Synopsis</title> |  | ||||||
| 
 |  | ||||||
| <cmdsynopsis> |  | ||||||
|   <command>nix-store</command> |  | ||||||
|   <arg choice='plain'><option>-XXX-substitute</option></arg> |  | ||||||
|   <arg choice='plain' |  | ||||||
|        rep='repeat'><replaceable>srcpath</replaceable> <replaceable>subpath</replaceable></arg> |  | ||||||
| </cmdsynopsis> |  | ||||||
| </refsection> |  | ||||||
| 
 |  | ||||||
| <refsection><title>Description</title> |  | ||||||
|              |  | ||||||
| <para>The operation <option>-XXX-substitute</option> registers that the |  | ||||||
| store path <replaceable>srcpath</replaceable> can be built by |  | ||||||
| realising the derivation expression in |  | ||||||
| <replaceable>subpath</replaceable>.  This is used to implement binary |  | ||||||
| deployment.</para> |  | ||||||
| 
 |  | ||||||
| </refsection> |  | ||||||
|              |  | ||||||
| </refsection> |  | ||||||
| --> |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| <!--######################################################################--> | <!--######################################################################--> | ||||||
| 
 | 
 | ||||||
| <refsection xml:id='refsec-nix-store-verify'><title>Operation <option>--verify</option></title> | <refsection xml:id='refsec-nix-store-verify'><title>Operation <option>--verify</option></title> | ||||||
|  |  | ||||||
|  | @ -22,16 +22,6 @@ my $tmpNar2 = "$tmpDir/nar2"; | ||||||
| END { unlink $tmpNar; unlink $tmpNar2; rmdir $tmpDir; } | END { unlink $tmpNar; unlink $tmpNar2; rmdir $tmpDir; } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| # Check the arguments. |  | ||||||
| die unless scalar @ARGV == 1; |  | ||||||
| my $targetPath = $ARGV[0]; |  | ||||||
| 
 |  | ||||||
| my $date = strftime ("%F %H:%M:%S UTC", gmtime (time)); |  | ||||||
| print LOGFILE "$$ get $targetPath $date\n"; |  | ||||||
| 
 |  | ||||||
| print "\n*** Trying to download/patch `$targetPath'\n"; |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| # Load all manifests. | # Load all manifests. | ||||||
| my %narFiles; | my %narFiles; | ||||||
| my %localPaths; | my %localPaths; | ||||||
|  | @ -46,6 +36,54 @@ for my $manifest (glob "$manifestDir/*.nixmanifest") { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | # Parse the arguments. | ||||||
|  | 
 | ||||||
|  | if ($ARGV[0] eq "--query-paths") { | ||||||
|  |     foreach my $storePath (keys %narFiles) { print "$storePath\n"; } | ||||||
|  |     foreach my $storePath (keys %localPaths) { print "$storePath\n"; } | ||||||
|  |     exit 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | elsif ($ARGV[0] eq "--query-info") { | ||||||
|  |     shift @ARGV; | ||||||
|  |     foreach my $storePath (@ARGV) { | ||||||
|  |         my $info; | ||||||
|  |         if (defined $narFiles{$storePath}) { | ||||||
|  |             $info = @{$narFiles{$storePath}}[0]; | ||||||
|  |         } | ||||||
|  |         elsif (defined $localPaths{$storePath}) { | ||||||
|  |             $info = @{$localPaths{$storePath}}[0]; | ||||||
|  |         } | ||||||
|  |         else { | ||||||
|  |             next; # not an error | ||||||
|  |         } | ||||||
|  |         print "$storePath\n"; | ||||||
|  |         print "$info->{deriver}\n"; | ||||||
|  |         my @references = split " ", $info->{references}; | ||||||
|  |         my $count = scalar @references; | ||||||
|  |         print "$count\n"; | ||||||
|  |         foreach my $reference (@references) { | ||||||
|  |             print "$reference\n"; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     exit 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | elsif ($ARGV[0] ne "--substitute") { | ||||||
|  |     die "syntax: $0 [--query-paths | --query-info PATHS... | --substitute PATH]\n"; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | die unless scalar @ARGV == 2; | ||||||
|  | my $targetPath = $ARGV[1]; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | my $date = strftime ("%F %H:%M:%S UTC", gmtime (time)); | ||||||
|  | print LOGFILE "$$ get $targetPath $date\n"; | ||||||
|  | 
 | ||||||
|  | print "\n*** Trying to download/patch `$targetPath'\n"; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| # If we can copy from a local path, do that. | # If we can copy from a local path, do that. | ||||||
| my $localPathList = $localPaths{$targetPath}; | my $localPathList = $localPaths{$targetPath}; | ||||||
| foreach my $localPath (@{$localPathList}) { | foreach my $localPath (@{$localPathList}) { | ||||||
|  |  | ||||||
|  | @ -76,10 +76,6 @@ sub removeChannel { | ||||||
| sub update { | sub update { | ||||||
|     readChannels; |     readChannels; | ||||||
| 
 | 
 | ||||||
|     # Get rid of all the old substitutes. |  | ||||||
|     system("@bindir@/nix-store", "--clear-substitutes") == 0 |  | ||||||
|         or die "cannot clear substitutes"; |  | ||||||
| 
 |  | ||||||
|     # Remove all the old manifests. |     # Remove all the old manifests. | ||||||
|     for my $manifest (glob "$stateDir/manifests/*.nixmanifest") { |     for my $manifest (glob "$stateDir/manifests/*.nixmanifest") { | ||||||
|         unlink $manifest or die "cannot remove `$manifest': $!"; |         unlink $manifest or die "cannot remove `$manifest': $!"; | ||||||
|  |  | ||||||
|  | @ -108,41 +108,3 @@ while (@ARGV) { | ||||||
| 
 | 
 | ||||||
| my $size = scalar (keys %narFiles) + scalar (keys %localPaths); | my $size = scalar (keys %narFiles) + scalar (keys %localPaths); | ||||||
| print "$size store paths in manifest\n"; | print "$size store paths in manifest\n"; | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| # Register all substitutes. |  | ||||||
| print STDERR "registering substitutes...\n"; |  | ||||||
| 
 |  | ||||||
| my $pid = open(WRITE, "|$binDir/nix-store --register-substitutes") |  | ||||||
|     or die "cannot run nix-store"; |  | ||||||
| 
 |  | ||||||
| sub writeRegistration { |  | ||||||
|     my $storePath = shift; |  | ||||||
|     my $object = shift; |  | ||||||
|     print WRITE "$storePath\n"; |  | ||||||
|     print WRITE "$object->{deriver}\n"; |  | ||||||
|     print WRITE "$libexecDir/nix/download-using-manifests.pl\n"; |  | ||||||
|     print WRITE "0\n"; |  | ||||||
|     my @references = split " ", $object->{references}; |  | ||||||
|     my $count = scalar @references; |  | ||||||
|     print WRITE "$count\n"; |  | ||||||
|     foreach my $reference (@references) { |  | ||||||
|         print WRITE "$reference\n"; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| foreach my $storePath (keys %narFiles) { |  | ||||||
|     my $narFileList = $narFiles{$storePath}; |  | ||||||
|     foreach my $narFile (@{$narFileList}) { |  | ||||||
|         writeRegistration $storePath, $narFile; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| foreach my $storePath (keys %localPaths) { |  | ||||||
|     my $localPathList = $localPaths{$storePath}; |  | ||||||
|     foreach my $localPath (@{$localPathList}) { |  | ||||||
|         writeRegistration $storePath, $localPath; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| close WRITE or die "nix-store failed: $?"; |  | ||||||
|  |  | ||||||
|  | @ -113,6 +113,12 @@ static void initAndRun(int argc, char * * argv) | ||||||
|     nixLibexecDir = canonPath(getEnv("NIX_LIBEXEC_DIR", NIX_LIBEXEC_DIR)); |     nixLibexecDir = canonPath(getEnv("NIX_LIBEXEC_DIR", NIX_LIBEXEC_DIR)); | ||||||
|     nixBinDir = canonPath(getEnv("NIX_BIN_DIR", NIX_BIN_DIR)); |     nixBinDir = canonPath(getEnv("NIX_BIN_DIR", NIX_BIN_DIR)); | ||||||
| 
 | 
 | ||||||
|  |     string subs = getEnv("NIX_SUBSTITUTERS", "default"); | ||||||
|  |     if (subs == "default") | ||||||
|  |         substituters.push_back(nixLibexecDir + "/nix/download-using-manifests.pl"); | ||||||
|  |     else | ||||||
|  |         substituters = tokenizeString(subs, ":"); | ||||||
|  | 
 | ||||||
|     /* Get some settings from the configuration file. */ |     /* Get some settings from the configuration file. */ | ||||||
|     thisSystem = querySetting("system", SYSTEM); |     thisSystem = querySetting("system", SYSTEM); | ||||||
|     maxBuildJobs = queryIntSetting("build-max-jobs", 1); |     maxBuildJobs = queryIntSetting("build-max-jobs", 1); | ||||||
|  | @ -320,7 +326,7 @@ int main(int argc, char * * argv) | ||||||
|                 "Try `%2% --help' for more information.") |                 "Try `%2% --help' for more information.") | ||||||
|             % e.what() % programId); |             % e.what() % programId); | ||||||
|         return 1; |         return 1; | ||||||
|     } catch (Error & e) { |     } catch (BaseError & e) { | ||||||
|         printMsg(lvlError, format("error: %1%") % e.msg()); |         printMsg(lvlError, format("error: %1%") % e.msg()); | ||||||
|         return 1; |         return 1; | ||||||
|     } catch (std::exception & e) { |     } catch (std::exception & e) { | ||||||
|  |  | ||||||
|  | @ -164,6 +164,11 @@ private: | ||||||
|     /* Goals waiting for a build slot. */ |     /* Goals waiting for a build slot. */ | ||||||
|     WeakGoals wantingToBuild; |     WeakGoals wantingToBuild; | ||||||
| 
 | 
 | ||||||
|  |     /* Goals waiting for info from substituters (using --query-info),
 | ||||||
|  |        and the info they're (collectively) waiting for. */ | ||||||
|  |     WeakGoals waitingForInfo; | ||||||
|  |     map<Path, PathSet> requestedInfo; | ||||||
|  | 
 | ||||||
|     /* Child processes currently running. */ |     /* Child processes currently running. */ | ||||||
|     Children children; |     Children children; | ||||||
| 
 | 
 | ||||||
|  | @ -213,11 +218,23 @@ public: | ||||||
|        call is made to childTerminate(..., true).  */ |        call is made to childTerminate(..., true).  */ | ||||||
|     void waitForChildTermination(GoalPtr goal); |     void waitForChildTermination(GoalPtr goal); | ||||||
| 
 | 
 | ||||||
|  |     /* Put `goal' to sleep until the top-level loop has run `sub' to
 | ||||||
|  |        get info about `storePath' (with --query-info).  We combine | ||||||
|  |        substituter invocations to reduce overhead. */ | ||||||
|  |     void waitForInfo(GoalPtr goal, Path sub, Path storePath); | ||||||
|  |      | ||||||
|     /* Loop until the specified top-level goals have finished. */ |     /* Loop until the specified top-level goals have finished. */ | ||||||
|     void run(const Goals & topGoals); |     void run(const Goals & topGoals); | ||||||
| 
 | 
 | ||||||
|     /* Wait for input to become available. */ |     /* Wait for input to become available. */ | ||||||
|     void waitForInput(); |     void waitForInput(); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  | 
 | ||||||
|  |     /* Process the pending paths in requestedInfo and wake up the
 | ||||||
|  |        goals in waitingForInfo. */ | ||||||
|  |     void getInfo(); | ||||||
|  |      | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -1829,18 +1846,22 @@ PathSet DerivationGoal::checkPathValidity(bool returnValid) | ||||||
| 
 | 
 | ||||||
| class SubstitutionGoal : public Goal | class SubstitutionGoal : public Goal | ||||||
| { | { | ||||||
|  |     friend class Worker; | ||||||
|  |      | ||||||
| private: | private: | ||||||
|     /* The store path that should be realised through a substitute. */ |     /* The store path that should be realised through a substitute. */ | ||||||
|     Path storePath; |     Path storePath; | ||||||
| 
 | 
 | ||||||
|     /* The remaining substitutes for this path. */ |     /* The remaining substituters. */ | ||||||
|     Substitutes subs; |     Paths subs; | ||||||
| 
 | 
 | ||||||
|     /* The current substitute. */ |     /* The current substituter. */ | ||||||
|     Substitute sub; |     Path sub; | ||||||
| 
 | 
 | ||||||
|     /* Outgoing references for this path. */ |     /* Path info returned by the substituter's --query-info operation. */ | ||||||
|  |     bool infoOkay; | ||||||
|     PathSet references; |     PathSet references; | ||||||
|  |     Path deriver; | ||||||
| 
 | 
 | ||||||
|     /* Pipe for the substitute's standard output/error. */ |     /* Pipe for the substitute's standard output/error. */ | ||||||
|     Pipe logPipe; |     Pipe logPipe; | ||||||
|  | @ -1864,8 +1885,9 @@ public: | ||||||
| 
 | 
 | ||||||
|     /* The states. */ |     /* The states. */ | ||||||
|     void init(); |     void init(); | ||||||
|     void referencesValid(); |  | ||||||
|     void tryNext(); |     void tryNext(); | ||||||
|  |     void gotInfo(); | ||||||
|  |     void referencesValid(); | ||||||
|     void tryToRun(); |     void tryToRun(); | ||||||
|     void finished(); |     void finished(); | ||||||
| 
 | 
 | ||||||
|  | @ -1923,17 +1945,46 @@ void SubstitutionGoal::init() | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /* !!! race condition; should get the substitutes and the
 |     subs = substituters; | ||||||
|        references in a transaction (in case a clearSubstitutes() is |  | ||||||
|        done simultaneously). */ |  | ||||||
| 
 | 
 | ||||||
|     /* Read the substitutes. */ |     tryNext(); | ||||||
|     subs = store->querySubstitutes(storePath); | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | void SubstitutionGoal::tryNext() | ||||||
|  | { | ||||||
|  |     trace("trying next substituter"); | ||||||
|  | 
 | ||||||
|  |     if (subs.size() == 0) { | ||||||
|  |         /* None left.  Terminate this goal and let someone else deal
 | ||||||
|  |            with it. */ | ||||||
|  |         printMsg(lvlError, | ||||||
|  |             format("path `%1%' is required, but there is no substituter that can build it") | ||||||
|  |             % storePath); | ||||||
|  |         amDone(ecFailed); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     sub = subs.front(); | ||||||
|  |     subs.pop_front(); | ||||||
|  | 
 | ||||||
|  |     infoOkay = false; | ||||||
|  |     state = &SubstitutionGoal::gotInfo; | ||||||
|  |     worker.waitForInfo(shared_from_this(), sub, storePath); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | void SubstitutionGoal::gotInfo() | ||||||
|  | { | ||||||
|  |     trace("got info"); | ||||||
|  | 
 | ||||||
|  |     if (!infoOkay) { | ||||||
|  |         tryNext(); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|      |      | ||||||
|     /* To maintain the closure invariant, we first have to realise the
 |     /* To maintain the closure invariant, we first have to realise the
 | ||||||
|        paths referenced by this one. */ |        paths referenced by this one. */ | ||||||
|     store->queryReferences(storePath, references); |  | ||||||
| 
 |  | ||||||
|     for (PathSet::iterator i = references.begin(); |     for (PathSet::iterator i = references.begin(); | ||||||
|          i != references.end(); ++i) |          i != references.end(); ++i) | ||||||
|         if (*i != storePath) /* ignore self-references */ |         if (*i != storePath) /* ignore self-references */ | ||||||
|  | @ -1948,7 +1999,7 @@ void SubstitutionGoal::init() | ||||||
| 
 | 
 | ||||||
| void SubstitutionGoal::referencesValid() | void SubstitutionGoal::referencesValid() | ||||||
| { | { | ||||||
|     trace("all referenced realised"); |     trace("all references realised"); | ||||||
| 
 | 
 | ||||||
|     if (nrFailed > 0) { |     if (nrFailed > 0) { | ||||||
|         printMsg(lvlError, |         printMsg(lvlError, | ||||||
|  | @ -1962,27 +2013,6 @@ void SubstitutionGoal::referencesValid() | ||||||
|         if (*i != storePath) /* ignore self-references */ |         if (*i != storePath) /* ignore self-references */ | ||||||
|             assert(store->isValidPath(*i)); |             assert(store->isValidPath(*i)); | ||||||
| 
 | 
 | ||||||
|     tryNext(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| void SubstitutionGoal::tryNext() |  | ||||||
| { |  | ||||||
|     trace("trying next substitute"); |  | ||||||
| 
 |  | ||||||
|     if (subs.size() == 0) { |  | ||||||
|         /* None left.  Terminate this goal and let someone else deal
 |  | ||||||
|            with it. */ |  | ||||||
|         printMsg(lvlError, |  | ||||||
|             format("path `%1%' is required, but it has no (remaining) substitutes") |  | ||||||
|             % storePath); |  | ||||||
|         amDone(ecFailed); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|     sub = subs.front(); |  | ||||||
|     subs.pop_front(); |  | ||||||
| 
 |  | ||||||
|     /* Wait until we can run the substitute program. */ |  | ||||||
|     state = &SubstitutionGoal::tryToRun; |     state = &SubstitutionGoal::tryToRun; | ||||||
|     worker.waitForBuildSlot(shared_from_this()); |     worker.waitForBuildSlot(shared_from_this()); | ||||||
| } | } | ||||||
|  | @ -2013,7 +2043,7 @@ void SubstitutionGoal::tryToRun() | ||||||
| 
 | 
 | ||||||
|     printMsg(lvlInfo, |     printMsg(lvlInfo, | ||||||
|         format("substituting path `%1%' using substituter `%2%'") |         format("substituting path `%1%' using substituter `%2%'") | ||||||
|         % storePath % sub.program); |         % storePath % sub); | ||||||
|      |      | ||||||
|     logPipe.create(); |     logPipe.create(); | ||||||
| 
 | 
 | ||||||
|  | @ -2038,14 +2068,15 @@ void SubstitutionGoal::tryToRun() | ||||||
|             commonChildInit(logPipe); |             commonChildInit(logPipe); | ||||||
| 
 | 
 | ||||||
|             /* Fill in the arguments. */ |             /* Fill in the arguments. */ | ||||||
|             Strings args(sub.args); |             Strings args; | ||||||
|             args.push_front(storePath); |             args.push_back(baseNameOf(sub)); | ||||||
|             args.push_front(baseNameOf(sub.program)); |             args.push_back("--substitute"); | ||||||
|  |             args.push_back(storePath); | ||||||
|             const char * * argArr = strings2CharPtrs(args); |             const char * * argArr = strings2CharPtrs(args); | ||||||
| 
 | 
 | ||||||
|             execv(sub.program.c_str(), (char * *) argArr); |             execv(sub.c_str(), (char * *) argArr); | ||||||
|              |              | ||||||
|             throw SysError(format("executing `%1%'") % sub.program); |             throw SysError(format("executing `%1%'") % sub); | ||||||
|              |              | ||||||
|         } catch (std::exception & e) { |         } catch (std::exception & e) { | ||||||
|             std::cerr << format("substitute error: %1%\n") % e.what(); |             std::cerr << format("substitute error: %1%\n") % e.what(); | ||||||
|  | @ -2098,7 +2129,7 @@ void SubstitutionGoal::finished() | ||||||
| 
 | 
 | ||||||
|         printMsg(lvlInfo, |         printMsg(lvlInfo, | ||||||
|             format("substitution of path `%1%' using substituter `%2%' failed: %3%") |             format("substitution of path `%1%' using substituter `%2%' failed: %3%") | ||||||
|             % storePath % sub.program % e.msg()); |             % storePath % sub % e.msg()); | ||||||
|          |          | ||||||
|         /* Try the next substitute. */ |         /* Try the next substitute. */ | ||||||
|         state = &SubstitutionGoal::tryNext; |         state = &SubstitutionGoal::tryNext; | ||||||
|  | @ -2113,7 +2144,7 @@ void SubstitutionGoal::finished() | ||||||
|     Transaction txn; |     Transaction txn; | ||||||
|     createStoreTransaction(txn); |     createStoreTransaction(txn); | ||||||
|     registerValidPath(txn, storePath, contentHash, |     registerValidPath(txn, storePath, contentHash, | ||||||
|         references, sub.deriver); |         references, deriver); | ||||||
|     txn.commit(); |     txn.commit(); | ||||||
| 
 | 
 | ||||||
|     outputLock->setDeletion(true); |     outputLock->setDeletion(true); | ||||||
|  | @ -2298,6 +2329,76 @@ void Worker::waitForChildTermination(GoalPtr goal) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | void Worker::waitForInfo(GoalPtr goal, Path sub, Path storePath) | ||||||
|  | { | ||||||
|  |     debug("wait for info"); | ||||||
|  |     requestedInfo[sub].insert(storePath); | ||||||
|  |     waitingForInfo.insert(goal); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | void Worker::getInfo() | ||||||
|  | { | ||||||
|  |     for (map<Path, PathSet>::iterator i = requestedInfo.begin(); | ||||||
|  |          i != requestedInfo.end(); ++i) | ||||||
|  |     { | ||||||
|  |         Path sub = i->first; | ||||||
|  |         PathSet paths = i->second; | ||||||
|  | 
 | ||||||
|  |         while (!paths.empty()) { | ||||||
|  | 
 | ||||||
|  |             /* Run the substituter for at most 100 paths at a time to
 | ||||||
|  |                prevent command line overflows. */ | ||||||
|  |             PathSet paths2; | ||||||
|  |             while (!paths.empty() && paths2.size() < 100) { | ||||||
|  |                 paths2.insert(*paths.begin()); | ||||||
|  |                 paths.erase(paths.begin()); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             /* Ask the substituter for the references and deriver of
 | ||||||
|  |                the paths. */ | ||||||
|  |             debug(format("running `%1%' to get info about `%2%'") % sub % showPaths(paths2)); | ||||||
|  |             Strings args; | ||||||
|  |             args.push_back("--query-info"); | ||||||
|  |             args.insert(args.end(), paths2.begin(), paths2.end()); | ||||||
|  |             string res = runProgram(sub, false, args); | ||||||
|  |             std::istringstream str(res); | ||||||
|  | 
 | ||||||
|  |             while (true) { | ||||||
|  |                 ValidPathInfo info = decodeValidPathInfo(str); | ||||||
|  |                 if (info.path == "") break; | ||||||
|  | 
 | ||||||
|  |                 /* !!! inefficient */ | ||||||
|  |                 for (WeakGoals::iterator k = waitingForInfo.begin(); | ||||||
|  |                      k != waitingForInfo.end(); ++k) | ||||||
|  |                 { | ||||||
|  |                     GoalPtr goal = k->lock(); | ||||||
|  |                     if (goal) { | ||||||
|  |                         SubstitutionGoal * goal2 = dynamic_cast<SubstitutionGoal *>(goal.get()); | ||||||
|  |                         if (goal2->storePath == info.path) { | ||||||
|  |                             goal2->references = info.references; | ||||||
|  |                             goal2->deriver = info.deriver; | ||||||
|  |                             goal2->infoOkay = true; | ||||||
|  |                             wakeUp(goal); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     for (WeakGoals::iterator k = waitingForInfo.begin(); | ||||||
|  |          k != waitingForInfo.end(); ++k) | ||||||
|  |     { | ||||||
|  |         GoalPtr goal = k->lock(); | ||||||
|  |         if (goal) wakeUp(goal); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     requestedInfo.clear(); | ||||||
|  |     waitingForInfo.clear(); // !!! have we done them all?
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| void Worker::run(const Goals & _topGoals) | void Worker::run(const Goals & _topGoals) | ||||||
| { | { | ||||||
|     for (Goals::iterator i = _topGoals.begin(); |     for (Goals::iterator i = _topGoals.begin(); | ||||||
|  | @ -2324,11 +2425,14 @@ void Worker::run(const Goals & _topGoals) | ||||||
| 
 | 
 | ||||||
|         if (topGoals.empty()) break; |         if (topGoals.empty()) break; | ||||||
| 
 | 
 | ||||||
|         /* !!! not when we're polling */ |         getInfo(); | ||||||
|         assert(!children.empty()); |  | ||||||
| 
 | 
 | ||||||
|         /* Wait for input. */ |         /* Wait for input. */ | ||||||
|         waitForInput(); |         if (!children.empty()) | ||||||
|  |             waitForInput(); | ||||||
|  |         else | ||||||
|  |             /* !!! not when we're polling */ | ||||||
|  |             assert(!awake.empty()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /* If --keep-going is not set, it's possible that the main goal
 |     /* If --keep-going is not set, it's possible that the main goal
 | ||||||
|  |  | ||||||
|  | @ -194,7 +194,8 @@ void Database::open2(const string & path, bool removeOldEnv) | ||||||
| 
 | 
 | ||||||
|     env->set_errcall(errorPrinter); |     env->set_errcall(errorPrinter); | ||||||
|     env->set_msgcall(messagePrinter); |     env->set_msgcall(messagePrinter); | ||||||
|     //env->set_verbose(DB_VERB_REGISTER, 1);
 |     if (getEnv("NIX_DEBUG_DB_REGISTER") == "1") | ||||||
|  |         env->set_verbose(DB_VERB_REGISTER, 1); | ||||||
|     env->set_verbose(DB_VERB_RECOVERY, 1); |     env->set_verbose(DB_VERB_RECOVERY, 1); | ||||||
|      |      | ||||||
|     /* Smaller log files. */ |     /* Smaller log files. */ | ||||||
|  | @ -454,4 +455,14 @@ void Database::enumTable(const Transaction & txn, TableId table, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|   |   | ||||||
|  | void Database::clearTable(const Transaction & txn, TableId table) | ||||||
|  | { | ||||||
|  |     try { | ||||||
|  |         Db * db = getDb(table); | ||||||
|  |         u_int32_t count; | ||||||
|  |         db->truncate(txn.txn, &count, 0); | ||||||
|  |     } catch (DbException e) { rethrow(e); } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -89,6 +89,8 @@ public: | ||||||
| 
 | 
 | ||||||
|     void enumTable(const Transaction & txn, TableId table, |     void enumTable(const Transaction & txn, TableId table, | ||||||
|         Strings & keys, const string & keyPrefix = ""); |         Strings & keys, const string & keyPrefix = ""); | ||||||
|  | 
 | ||||||
|  |     void clearTable(const Transaction & txn, TableId table); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -602,6 +602,8 @@ void LocalStore::collectGarbage(GCAction action, const PathSet & pathsToDelete, | ||||||
|             } |             } | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  |             if (!pathExists(*i)) continue; | ||||||
|  |                  | ||||||
|             printMsg(lvlInfo, format("deleting `%1%'") % *i); |             printMsg(lvlInfo, format("deleting `%1%'") % *i); | ||||||
|              |              | ||||||
|             /* Okay, it's safe to delete. */ |             /* Okay, it's safe to delete. */ | ||||||
|  |  | ||||||
|  | @ -25,6 +25,7 @@ unsigned int maxBuildJobs = 1; | ||||||
| bool readOnlyMode = false; | bool readOnlyMode = false; | ||||||
| string thisSystem = "unset"; | string thisSystem = "unset"; | ||||||
| unsigned int maxSilentTime = 0; | unsigned int maxSilentTime = 0; | ||||||
|  | Paths substituters; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| static bool settingsRead = false; | static bool settingsRead = false; | ||||||
|  |  | ||||||
|  | @ -67,6 +67,11 @@ extern string thisSystem; | ||||||
|    infinity. */ |    infinity. */ | ||||||
| extern unsigned int maxSilentTime; | extern unsigned int maxSilentTime; | ||||||
| 
 | 
 | ||||||
|  | /* The substituters.  There are programs that can somehow realise a
 | ||||||
|  |    store path without building, e.g., by downloading it or copying it | ||||||
|  |    from a CD. */ | ||||||
|  | extern Paths substituters; | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| Strings querySetting(const string & name, const Strings & def); | Strings querySetting(const string & name, const Strings & def); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -48,22 +48,6 @@ static TableId dbReferences = 0; | ||||||
|    referrer. */ |    referrer. */ | ||||||
| static TableId dbReferrers = 0; | static TableId dbReferrers = 0; | ||||||
| 
 | 
 | ||||||
| /* dbSubstitutes :: Path -> [[Path]]
 |  | ||||||
| 
 |  | ||||||
|    Each pair $(p, subs)$ tells Nix that it can use any of the |  | ||||||
|    substitutes in $subs$ to build path $p$.  Each substitute defines a |  | ||||||
|    command-line invocation of a program (i.e., the first list element |  | ||||||
|    is the full path to the program, the remaining elements are |  | ||||||
|    arguments). |  | ||||||
| 
 |  | ||||||
|    The main purpose of this is for distributed caching of derivates. |  | ||||||
|    One system can compute a derivate and put it on a website (as a Nix |  | ||||||
|    archive), for instance, and then another system can register a |  | ||||||
|    substitute for that derivate.  The substitute in this case might be |  | ||||||
|    a Nix derivation that fetches the Nix archive. |  | ||||||
| */ |  | ||||||
| static TableId dbSubstitutes = 0; |  | ||||||
| 
 |  | ||||||
| /* dbDerivers :: Path -> [Path]
 | /* dbDerivers :: Path -> [Path]
 | ||||||
| 
 | 
 | ||||||
|    This table lists the derivation used to build a path.  There can |    This table lists the derivation used to build a path.  There can | ||||||
|  | @ -72,13 +56,6 @@ static TableId dbSubstitutes = 0; | ||||||
| static TableId dbDerivers = 0; | static TableId dbDerivers = 0; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| bool Substitute::operator == (const Substitute & sub) const |  | ||||||
| { |  | ||||||
|     return program == sub.program |  | ||||||
|         && args == sub.args; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| static void upgradeStore07(); | static void upgradeStore07(); | ||||||
| static void upgradeStore09(); | static void upgradeStore09(); | ||||||
| 
 | 
 | ||||||
|  | @ -103,6 +80,8 @@ void checkStoreNotSymlink() | ||||||
| 
 | 
 | ||||||
| LocalStore::LocalStore(bool reserveSpace) | LocalStore::LocalStore(bool reserveSpace) | ||||||
| { | { | ||||||
|  |     substitutablePathsLoaded = false; | ||||||
|  |      | ||||||
|     if (readOnlyMode) return; |     if (readOnlyMode) return; | ||||||
| 
 | 
 | ||||||
|     checkStoreNotSymlink(); |     checkStoreNotSymlink(); | ||||||
|  | @ -133,7 +112,6 @@ LocalStore::LocalStore(bool reserveSpace) | ||||||
|     dbValidPaths = nixDB.openTable("validpaths"); |     dbValidPaths = nixDB.openTable("validpaths"); | ||||||
|     dbReferences = nixDB.openTable("references"); |     dbReferences = nixDB.openTable("references"); | ||||||
|     dbReferrers = nixDB.openTable("referrers", true); /* must be sorted */ |     dbReferrers = nixDB.openTable("referrers", true); /* must be sorted */ | ||||||
|     dbSubstitutes = nixDB.openTable("substitutes"); |  | ||||||
|     dbDerivers = nixDB.openTable("derivers"); |     dbDerivers = nixDB.openTable("derivers"); | ||||||
| 
 | 
 | ||||||
|     int curSchema = 0; |     int curSchema = 0; | ||||||
|  | @ -280,17 +258,6 @@ bool LocalStore::isValidPath(const Path & path) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| static Substitutes readSubstitutes(const Transaction & txn, |  | ||||||
|     const Path & srcPath); |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| static bool isRealisablePath(const Transaction & txn, const Path & path) |  | ||||||
| { |  | ||||||
|     return isValidPathTxn(txn, path) |  | ||||||
|         || readSubstitutes(txn, path).size() > 0; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| static string addPrefix(const string & prefix, const string & s) | static string addPrefix(const string & prefix, const string & s) | ||||||
| { | { | ||||||
|     return prefix + string(1, (char) 0) + s; |     return prefix + string(1, (char) 0) + s; | ||||||
|  | @ -322,11 +289,10 @@ static PathSet getReferrers(const Transaction & txn, const Path & storePath) | ||||||
| void setReferences(const Transaction & txn, const Path & storePath, | void setReferences(const Transaction & txn, const Path & storePath, | ||||||
|     const PathSet & references) |     const PathSet & references) | ||||||
| { | { | ||||||
|     /* For unrealisable paths, we can only clear the references. */ |     /* For invalid paths, we can only clear the references. */ | ||||||
|     if (references.size() > 0 && !isRealisablePath(txn, storePath)) |     if (references.size() > 0 && !isValidPathTxn(txn, storePath)) | ||||||
|         throw Error( |         throw Error( | ||||||
|             format("cannot set references for path `%1%' which is invalid and has no substitutes") |             format("cannot set references for invalid path `%1%'") % storePath); | ||||||
|             % storePath); |  | ||||||
| 
 | 
 | ||||||
|     Paths oldReferences; |     Paths oldReferences; | ||||||
|     nixDB.queryStrings(txn, dbReferences, storePath, oldReferences); |     nixDB.queryStrings(txn, dbReferences, storePath, oldReferences); | ||||||
|  | @ -356,7 +322,7 @@ void queryReferences(const Transaction & txn, | ||||||
|     const Path & storePath, PathSet & references) |     const Path & storePath, PathSet & references) | ||||||
| { | { | ||||||
|     Paths references2; |     Paths references2; | ||||||
|     if (!isRealisablePath(txn, storePath)) |     if (!isValidPathTxn(txn, storePath)) | ||||||
|         throw Error(format("path `%1%' is not valid") % storePath); |         throw Error(format("path `%1%' is not valid") % storePath); | ||||||
|     nixDB.queryStrings(txn, dbReferences, storePath, references2); |     nixDB.queryStrings(txn, dbReferences, storePath, references2); | ||||||
|     references.insert(references2.begin(), references2.end()); |     references.insert(references2.begin(), references2.end()); | ||||||
|  | @ -373,7 +339,7 @@ void LocalStore::queryReferences(const Path & storePath, | ||||||
| void queryReferrers(const Transaction & txn, | void queryReferrers(const Transaction & txn, | ||||||
|     const Path & storePath, PathSet & referrers) |     const Path & storePath, PathSet & referrers) | ||||||
| { | { | ||||||
|     if (!isRealisablePath(txn, storePath)) |     if (!isValidPathTxn(txn, storePath)) | ||||||
|         throw Error(format("path `%1%' is not valid") % storePath); |         throw Error(format("path `%1%' is not valid") % storePath); | ||||||
|     PathSet referrers2 = getReferrers(txn, storePath); |     PathSet referrers2 = getReferrers(txn, storePath); | ||||||
|     referrers.insert(referrers2.begin(), referrers2.end()); |     referrers.insert(referrers2.begin(), referrers2.end()); | ||||||
|  | @ -393,7 +359,7 @@ void setDeriver(const Transaction & txn, const Path & storePath, | ||||||
|     assertStorePath(storePath); |     assertStorePath(storePath); | ||||||
|     if (deriver == "") return; |     if (deriver == "") return; | ||||||
|     assertStorePath(deriver); |     assertStorePath(deriver); | ||||||
|     if (!isRealisablePath(txn, storePath)) |     if (!isValidPathTxn(txn, storePath)) | ||||||
|         throw Error(format("path `%1%' is not valid") % storePath); |         throw Error(format("path `%1%' is not valid") % storePath); | ||||||
|     nixDB.setString(txn, dbDerivers, storePath, deriver); |     nixDB.setString(txn, dbDerivers, storePath, deriver); | ||||||
| } | } | ||||||
|  | @ -401,7 +367,7 @@ void setDeriver(const Transaction & txn, const Path & storePath, | ||||||
| 
 | 
 | ||||||
| static Path queryDeriver(const Transaction & txn, const Path & storePath) | static Path queryDeriver(const Transaction & txn, const Path & storePath) | ||||||
| { | { | ||||||
|     if (!isRealisablePath(txn, storePath)) |     if (!isValidPathTxn(txn, storePath)) | ||||||
|         throw Error(format("path `%1%' is not valid") % storePath); |         throw Error(format("path `%1%' is not valid") % storePath); | ||||||
|     Path deriver; |     Path deriver; | ||||||
|     if (nixDB.queryString(txn, dbDerivers, storePath, deriver)) |     if (nixDB.queryString(txn, dbDerivers, storePath, deriver)) | ||||||
|  | @ -417,119 +383,33 @@ Path LocalStore::queryDeriver(const Path & path) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| const int substituteVersion = 2; | PathSet LocalStore::querySubstitutablePaths() | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| static Substitutes readSubstitutes(const Transaction & txn, |  | ||||||
|     const Path & srcPath) |  | ||||||
| { | { | ||||||
|     Strings ss; |     if (!substitutablePathsLoaded) { | ||||||
|     nixDB.queryStrings(txn, dbSubstitutes, srcPath, ss); |         for (Paths::iterator i = substituters.begin(); i != substituters.end(); ++i) { | ||||||
| 
 |             debug(format("running `%1%' to find out substitutable paths") % *i); | ||||||
|     Substitutes subs; |             Strings args; | ||||||
|      |             args.push_back("--query-paths"); | ||||||
|     for (Strings::iterator i = ss.begin(); i != ss.end(); ++i) { |             Strings ss = tokenizeString(runProgram(*i, false, args), "\n"); | ||||||
|         if (i->size() < 4 || (*i)[3] != 0) { |             for (Strings::iterator j = ss.begin(); j != ss.end(); ++j) { | ||||||
|             /* Old-style substitute.  !!! remove this code
 |                 if (!isStorePath(*j)) | ||||||
|                eventually? */ |                     throw Error(format("`%1%' returned a bad substitutable path `%2%'") | ||||||
|             break; |                         % *i % *j); | ||||||
|  |                 substitutablePaths.insert(*j); | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|         Strings ss2 = unpackStrings(*i); |         substitutablePathsLoaded = true; | ||||||
|         if (ss2.size() == 0) continue; |  | ||||||
|         int version; |  | ||||||
|         if (!string2Int(ss2.front(), version)) continue; |  | ||||||
|         if (version != substituteVersion) continue; |  | ||||||
|         if (ss2.size() != 4) throw Error("malformed substitute"); |  | ||||||
|         Strings::iterator j = ss2.begin(); |  | ||||||
|         j++; |  | ||||||
|         Substitute sub; |  | ||||||
|         sub.deriver = *j++; |  | ||||||
|         sub.program = *j++; |  | ||||||
|         sub.args = unpackStrings(*j++); |  | ||||||
|         subs.push_back(sub); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return subs; |     return substitutablePaths; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| static void writeSubstitutes(const Transaction & txn, | bool LocalStore::hasSubstitutes(const Path & path) | ||||||
|     const Path & srcPath, const Substitutes & subs) |  | ||||||
| { | { | ||||||
|     Strings ss; |     if (!substitutablePathsLoaded) | ||||||
| 
 |         querySubstitutablePaths();  | ||||||
|     for (Substitutes::const_iterator i = subs.begin(); |     return substitutablePaths.find(path) != substitutablePaths.end(); | ||||||
|          i != subs.end(); ++i) |  | ||||||
|     { |  | ||||||
|         Strings ss2; |  | ||||||
|         ss2.push_back((format("%1%") % substituteVersion).str()); |  | ||||||
|         ss2.push_back(i->deriver); |  | ||||||
|         ss2.push_back(i->program); |  | ||||||
|         ss2.push_back(packStrings(i->args)); |  | ||||||
|         ss.push_back(packStrings(ss2)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     nixDB.setStrings(txn, dbSubstitutes, srcPath, ss); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| void registerSubstitute(const Transaction & txn, |  | ||||||
|     const Path & srcPath, const Substitute & sub) |  | ||||||
| { |  | ||||||
|     assertStorePath(srcPath); |  | ||||||
|      |  | ||||||
|     Substitutes subs = readSubstitutes(txn, srcPath); |  | ||||||
| 
 |  | ||||||
|     if (find(subs.begin(), subs.end(), sub) != subs.end()) |  | ||||||
|         return; |  | ||||||
| 
 |  | ||||||
|     /* New substitutes take precedence over old ones.  If the
 |  | ||||||
|        substitute is already present, it's moved to the front. */ |  | ||||||
|     remove(subs.begin(), subs.end(), sub); |  | ||||||
|     subs.push_front(sub); |  | ||||||
|          |  | ||||||
|     writeSubstitutes(txn, srcPath, subs); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| Substitutes querySubstitutes(const Transaction & txn, const Path & path) |  | ||||||
| { |  | ||||||
|     return readSubstitutes(txn, path); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| Substitutes LocalStore::querySubstitutes(const Path & path) |  | ||||||
| { |  | ||||||
|     return nix::querySubstitutes(noTxn, path); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| static void invalidatePath(Transaction & txn, const Path & path); |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| void clearSubstitutes() |  | ||||||
| { |  | ||||||
|     Transaction txn(nixDB); |  | ||||||
|      |  | ||||||
|     /* Iterate over all paths for which there are substitutes. */ |  | ||||||
|     Paths subKeys; |  | ||||||
|     nixDB.enumTable(txn, dbSubstitutes, subKeys); |  | ||||||
|     for (Paths::iterator i = subKeys.begin(); i != subKeys.end(); ++i) { |  | ||||||
|          |  | ||||||
|         /* Delete all substitutes for path *i. */ |  | ||||||
|         nixDB.delPair(txn, dbSubstitutes, *i); |  | ||||||
|          |  | ||||||
|         /* Maintain the cleanup invariant. */ |  | ||||||
|         if (!isValidPathTxn(txn, *i)) |  | ||||||
|             invalidatePath(txn, *i); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /* !!! there should be no referrers to any of the invalid
 |  | ||||||
|        substitutable paths.  This should be the case by construction |  | ||||||
|        (the only referrers can be other invalid substitutable paths, |  | ||||||
|        which have all been removed now). */ |  | ||||||
|      |  | ||||||
|     txn.commit(); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -615,17 +495,12 @@ void registerValidPaths(const Transaction & txn, | ||||||
|    there are no referrers. */ |    there are no referrers. */ | ||||||
| static void invalidatePath(Transaction & txn, const Path & path) | static void invalidatePath(Transaction & txn, const Path & path) | ||||||
| { | { | ||||||
|     debug(format("unregistering path `%1%'") % path); |     debug(format("invalidating path `%1%'") % path); | ||||||
| 
 | 
 | ||||||
|     /* Clear the `references' entry for this path, as well as the
 |     /* Clear the `references' entry for this path, as well as the
 | ||||||
|        inverse `referrers' entries, and the `derivers' entry; but only |        inverse `referrers' entries, and the `derivers' entry. */ | ||||||
|        if there are no substitutes for this path.  This maintains the |     setReferences(txn, path, PathSet()); | ||||||
|        cleanup invariant. */ |     nixDB.delPair(txn, dbDerivers, path); | ||||||
|     if (querySubstitutes(txn, path).size() == 0) { |  | ||||||
|         setReferences(txn, path, PathSet()); |  | ||||||
|         nixDB.delPair(txn, dbDerivers, path); |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|     nixDB.delPair(txn, dbValidPaths, path); |     nixDB.delPair(txn, dbValidPaths, path); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -967,30 +842,7 @@ void verifyStore(bool checkContents) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     printMsg(lvlInfo, "checking path realisability"); |     /* Check the cleanup invariant: only valid paths can have
 | ||||||
|      |  | ||||||
|     /* "Realisable" paths are those that are valid or have a
 |  | ||||||
|        substitute. */ |  | ||||||
|     PathSet realisablePaths(validPaths); |  | ||||||
| 
 |  | ||||||
|     /* Check that the values of the substitute mappings are valid
 |  | ||||||
|        paths. */  |  | ||||||
|     Paths subKeys; |  | ||||||
|     nixDB.enumTable(txn, dbSubstitutes, subKeys); |  | ||||||
|     for (Paths::iterator i = subKeys.begin(); i != subKeys.end(); ++i) { |  | ||||||
|         Substitutes subs = readSubstitutes(txn, *i); |  | ||||||
|         if (!isStorePath(*i)) { |  | ||||||
|             printMsg(lvlError, format("removing substitutes for non-store path `%1%'") % *i); |  | ||||||
|             nixDB.delPair(txn, dbSubstitutes, *i); |  | ||||||
|         } |  | ||||||
|         else if (subs.size() == 0) |  | ||||||
|             nixDB.delPair(txn, dbSubstitutes, *i); |  | ||||||
|         else |  | ||||||
| 	    realisablePaths.insert(*i); |  | ||||||
|     } |  | ||||||
|      |  | ||||||
| 
 |  | ||||||
|     /* Check the cleanup invariant: only realisable paths can have
 |  | ||||||
|        `references', `referrers', or `derivers' entries. */ |        `references', `referrers', or `derivers' entries. */ | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -1001,8 +853,8 @@ void verifyStore(bool checkContents) | ||||||
|     for (Paths::iterator i = deriversKeys.begin(); |     for (Paths::iterator i = deriversKeys.begin(); | ||||||
|          i != deriversKeys.end(); ++i) |          i != deriversKeys.end(); ++i) | ||||||
|     { |     { | ||||||
|         if (realisablePaths.find(*i) == realisablePaths.end()) { |         if (validPaths.find(*i) == validPaths.end()) { | ||||||
|             printMsg(lvlError, format("removing deriver entry for unrealisable path `%1%'") |             printMsg(lvlError, format("removing deriver entry for invalid path `%1%'") | ||||||
|                 % *i); |                 % *i); | ||||||
|             nixDB.delPair(txn, dbDerivers, *i); |             nixDB.delPair(txn, dbDerivers, *i); | ||||||
|         } |         } | ||||||
|  | @ -1024,13 +876,12 @@ void verifyStore(bool checkContents) | ||||||
|     for (Paths::iterator i = referencesKeys.begin(); |     for (Paths::iterator i = referencesKeys.begin(); | ||||||
|          i != referencesKeys.end(); ++i) |          i != referencesKeys.end(); ++i) | ||||||
|     { |     { | ||||||
|         if (realisablePaths.find(*i) == realisablePaths.end()) { |         if (validPaths.find(*i) == validPaths.end()) { | ||||||
|             printMsg(lvlError, format("removing references entry for unrealisable path `%1%'") |             printMsg(lvlError, format("removing references entry for invalid path `%1%'") | ||||||
|                 % *i); |                 % *i); | ||||||
|             setReferences(txn, *i, PathSet()); |             setReferences(txn, *i, PathSet()); | ||||||
|         } |         } | ||||||
|         else { |         else { | ||||||
|             bool isValid = validPaths.find(*i) != validPaths.end(); |  | ||||||
|             PathSet references; |             PathSet references; | ||||||
|             queryReferences(txn, *i, references); |             queryReferences(txn, *i, references); | ||||||
|             for (PathSet::iterator j = references.begin(); |             for (PathSet::iterator j = references.begin(); | ||||||
|  | @ -1042,7 +893,7 @@ void verifyStore(bool checkContents) | ||||||
|                         % *j % *i); |                         % *j % *i); | ||||||
|                     nixDB.setString(txn, dbReferrers, addPrefix(*j, *i), ""); |                     nixDB.setString(txn, dbReferrers, addPrefix(*j, *i), ""); | ||||||
|                 } |                 } | ||||||
|                 if (isValid && validPaths.find(*j) == validPaths.end()) { |                 if (validPaths.find(*j) == validPaths.end()) { | ||||||
|                     printMsg(lvlError, format("incomplete closure: `%1%' needs missing `%2%'") |                     printMsg(lvlError, format("incomplete closure: `%1%' needs missing `%2%'") | ||||||
|                         % *i % *j); |                         % *i % *j); | ||||||
|                 } |                 } | ||||||
|  | @ -1066,14 +917,14 @@ void verifyStore(bool checkContents) | ||||||
|         Path to(*i, 0, nul); |         Path to(*i, 0, nul); | ||||||
|         Path from(*i, nul + 1); |         Path from(*i, nul + 1); | ||||||
|          |          | ||||||
|         if (realisablePaths.find(to) == realisablePaths.end()) { |         if (validPaths.find(to) == validPaths.end()) { | ||||||
|             printMsg(lvlError, format("removing referrer entry from `%1%' to unrealisable `%2%'") |             printMsg(lvlError, format("removing referrer entry from `%1%' to invalid `%2%'") | ||||||
|                 % from % to); |                 % from % to); | ||||||
|             nixDB.delPair(txn, dbReferrers, *i); |             nixDB.delPair(txn, dbReferrers, *i); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         else if (realisablePaths.find(from) == realisablePaths.end()) { |         else if (validPaths.find(from) == validPaths.end()) { | ||||||
|             printMsg(lvlError, format("removing referrer entry from unrealisable `%1%' to `%2%'") |             printMsg(lvlError, format("removing referrer entry from invalid `%1%' to `%2%'") | ||||||
|                 % from % to); |                 % from % to); | ||||||
|             nixDB.delPair(txn, dbReferrers, *i); |             nixDB.delPair(txn, dbReferrers, *i); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  | @ -23,6 +23,10 @@ extern string drvsLogDir; | ||||||
| 
 | 
 | ||||||
| class LocalStore : public StoreAPI | class LocalStore : public StoreAPI | ||||||
| { | { | ||||||
|  | private: | ||||||
|  |     bool substitutablePathsLoaded; | ||||||
|  |     PathSet substitutablePaths; | ||||||
|  |      | ||||||
| public: | public: | ||||||
| 
 | 
 | ||||||
|     /* Open the database environment.  If `reserveSpace' is true, make
 |     /* Open the database environment.  If `reserveSpace' is true, make
 | ||||||
|  | @ -41,8 +45,6 @@ public: | ||||||
|      |      | ||||||
|     bool isValidPath(const Path & path); |     bool isValidPath(const Path & path); | ||||||
| 
 | 
 | ||||||
|     Substitutes querySubstitutes(const Path & srcPath); |  | ||||||
| 
 |  | ||||||
|     Hash queryPathHash(const Path & path); |     Hash queryPathHash(const Path & path); | ||||||
| 
 | 
 | ||||||
|     void queryReferences(const Path & path, PathSet & references); |     void queryReferences(const Path & path, PathSet & references); | ||||||
|  | @ -51,6 +53,10 @@ public: | ||||||
| 
 | 
 | ||||||
|     Path queryDeriver(const Path & path); |     Path queryDeriver(const Path & path); | ||||||
|      |      | ||||||
|  |     PathSet querySubstitutablePaths(); | ||||||
|  |      | ||||||
|  |     bool hasSubstitutes(const Path & path); | ||||||
|  |      | ||||||
|     Path addToStore(const Path & srcPath, bool fixed = false, |     Path addToStore(const Path & srcPath, bool fixed = false, | ||||||
|         bool recursive = false, string hashAlgo = "", |         bool recursive = false, string hashAlgo = "", | ||||||
|         PathFilter & filter = defaultPathFilter); |         PathFilter & filter = defaultPathFilter); | ||||||
|  | @ -86,13 +92,6 @@ void createStoreTransaction(Transaction & txn); | ||||||
| /* Copy a path recursively. */ | /* Copy a path recursively. */ | ||||||
| void copyPath(const Path & src, const Path & dst); | void copyPath(const Path & src, const Path & dst); | ||||||
| 
 | 
 | ||||||
| /* Register a substitute. */ |  | ||||||
| void registerSubstitute(const Transaction & txn, |  | ||||||
|     const Path & srcPath, const Substitute & sub); |  | ||||||
| 
 |  | ||||||
| /* Deregister all substitutes. */ |  | ||||||
| void clearSubstitutes(); |  | ||||||
| 
 |  | ||||||
| /* Register the validity of a path, i.e., that `path' exists, that the
 | /* Register the validity of a path, i.e., that `path' exists, that the
 | ||||||
|    paths referenced by it exists, and in the case of an output path of |    paths referenced by it exists, and in the case of an output path of | ||||||
|    a derivation, that it has been produced by a succesful execution of |    a derivation, that it has been produced by a succesful execution of | ||||||
|  | @ -103,14 +102,6 @@ void registerValidPath(const Transaction & txn, | ||||||
|     const Path & path, const Hash & hash, const PathSet & references, |     const Path & path, const Hash & hash, const PathSet & references, | ||||||
|     const Path & deriver); |     const Path & deriver); | ||||||
| 
 | 
 | ||||||
| struct ValidPathInfo  |  | ||||||
| { |  | ||||||
|     Path path; |  | ||||||
|     Path deriver; |  | ||||||
|     Hash hash; |  | ||||||
|     PathSet references; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| typedef list<ValidPathInfo> ValidPathInfos; | typedef list<ValidPathInfo> ValidPathInfos; | ||||||
| 
 | 
 | ||||||
| void registerValidPaths(const Transaction & txn, | void registerValidPaths(const Transaction & txn, | ||||||
|  |  | ||||||
|  | @ -63,8 +63,7 @@ void queryMissing(const PathSet & targets, | ||||||
|             bool mustBuild = false; |             bool mustBuild = false; | ||||||
|             for (DerivationOutputs::iterator i = drv.outputs.begin(); |             for (DerivationOutputs::iterator i = drv.outputs.begin(); | ||||||
|                  i != drv.outputs.end(); ++i) |                  i != drv.outputs.end(); ++i) | ||||||
|                 if (!store->isValidPath(i->second.path) && |                 if (!store->isValidPath(i->second.path) && !store->hasSubstitutes(i->second.path)) | ||||||
|                     !store->hasSubstitutes(i->second.path)) |  | ||||||
|                     mustBuild = true; |                     mustBuild = true; | ||||||
| 
 | 
 | ||||||
|             if (mustBuild) { |             if (mustBuild) { | ||||||
|  | @ -83,8 +82,8 @@ void queryMissing(const PathSet & targets, | ||||||
|             if (store->isValidPath(p)) continue; |             if (store->isValidPath(p)) continue; | ||||||
|             if (store->hasSubstitutes(p)) |             if (store->hasSubstitutes(p)) | ||||||
|                 willSubstitute.insert(p); |                 willSubstitute.insert(p); | ||||||
|             PathSet refs; |             // XXX call the substituters
 | ||||||
|             store->queryReferences(p, todo); |             // store->queryReferences(p, todo);
 | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -164,12 +164,6 @@ bool RemoteStore::isValidPath(const Path & path) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| Substitutes RemoteStore::querySubstitutes(const Path & path) |  | ||||||
| { |  | ||||||
|     throw Error("not implemented 2"); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| bool RemoteStore::hasSubstitutes(const Path & path) | bool RemoteStore::hasSubstitutes(const Path & path) | ||||||
| { | { | ||||||
|     writeInt(wopHasSubstitutes, to); |     writeInt(wopHasSubstitutes, to); | ||||||
|  | @ -221,6 +215,12 @@ Path RemoteStore::queryDeriver(const Path & path) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | PathSet RemoteStore::querySubstitutablePaths() | ||||||
|  | { | ||||||
|  |     throw Error("not implemented"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| Path RemoteStore::addToStore(const Path & _srcPath, bool fixed, | Path RemoteStore::addToStore(const Path & _srcPath, bool fixed, | ||||||
|     bool recursive, string hashAlgo, PathFilter & filter) |     bool recursive, string hashAlgo, PathFilter & filter) | ||||||
| { | { | ||||||
|  |  | ||||||
|  | @ -27,10 +27,6 @@ public: | ||||||
|      |      | ||||||
|     bool isValidPath(const Path & path); |     bool isValidPath(const Path & path); | ||||||
| 
 | 
 | ||||||
|     Substitutes querySubstitutes(const Path & path); |  | ||||||
| 
 |  | ||||||
|     bool hasSubstitutes(const Path & path); |  | ||||||
|      |  | ||||||
|     Hash queryPathHash(const Path & path); |     Hash queryPathHash(const Path & path); | ||||||
| 
 | 
 | ||||||
|     void queryReferences(const Path & path, PathSet & references); |     void queryReferences(const Path & path, PathSet & references); | ||||||
|  | @ -39,6 +35,10 @@ public: | ||||||
| 
 | 
 | ||||||
|     Path queryDeriver(const Path & path); |     Path queryDeriver(const Path & path); | ||||||
|      |      | ||||||
|  |     PathSet querySubstitutablePaths(); | ||||||
|  |      | ||||||
|  |     bool hasSubstitutes(const Path & path); | ||||||
|  |      | ||||||
|     Path addToStore(const Path & srcPath, bool fixed = false, |     Path addToStore(const Path & srcPath, bool fixed = false, | ||||||
|         bool recursive = false, string hashAlgo = "", |         bool recursive = false, string hashAlgo = "", | ||||||
|         PathFilter & filter = defaultPathFilter); |         PathFilter & filter = defaultPathFilter); | ||||||
|  |  | ||||||
|  | @ -8,7 +8,8 @@ namespace nix { | ||||||
| 
 | 
 | ||||||
| bool StoreAPI::hasSubstitutes(const Path & path) | bool StoreAPI::hasSubstitutes(const Path & path) | ||||||
| { | { | ||||||
|     return !querySubstitutes(path).empty(); |     PathSet paths = querySubstitutablePaths(); | ||||||
|  |     return paths.find(path) != paths.end(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -130,6 +131,24 @@ Path computeStorePathForText(const string & suffix, const string & s, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | ValidPathInfo decodeValidPathInfo(std::istream & str) | ||||||
|  | { | ||||||
|  |     ValidPathInfo info; | ||||||
|  |     getline(str, info.path); | ||||||
|  |     if (str.eof()) { info.path = ""; return info; } | ||||||
|  |     getline(str, info.deriver); | ||||||
|  |     string s; int n; | ||||||
|  |     getline(str, s); | ||||||
|  |     if (!string2Int(s, n)) throw Error("number expected"); | ||||||
|  |     while (n--) { | ||||||
|  |         getline(str, s); | ||||||
|  |         info.references.insert(s); | ||||||
|  |     } | ||||||
|  |     if (!str || str.eof()) throw Error("missing input"); | ||||||
|  |     return info; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -13,28 +13,6 @@ | ||||||
| namespace nix { | namespace nix { | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| /* A substitute is a program invocation that constructs some store
 |  | ||||||
|    path (typically by fetching it from somewhere, e.g., from the |  | ||||||
|    network). */ |  | ||||||
| struct Substitute |  | ||||||
| {        |  | ||||||
|     /* The derivation that built this store path (empty if none). */ |  | ||||||
|     Path deriver; |  | ||||||
|      |  | ||||||
|     /* Program to be executed to create the store path.  Must be in
 |  | ||||||
|        the output path of `storeExpr'. */ |  | ||||||
|     Path program; |  | ||||||
| 
 |  | ||||||
|     /* Extra arguments to be passed to the program (the first argument
 |  | ||||||
|        is the store path to be substituted). */ |  | ||||||
|     Strings args; |  | ||||||
| 
 |  | ||||||
|     bool operator == (const Substitute & sub) const; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| typedef list<Substitute> Substitutes; |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| typedef std::map<Path, Path> Roots; | typedef std::map<Path, Path> Roots; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -57,13 +35,6 @@ public: | ||||||
|     /* Checks whether a path is valid. */  |     /* Checks whether a path is valid. */  | ||||||
|     virtual bool isValidPath(const Path & path) = 0; |     virtual bool isValidPath(const Path & path) = 0; | ||||||
| 
 | 
 | ||||||
|     /* Return the substitutes for the given path. */ |  | ||||||
|     virtual Substitutes querySubstitutes(const Path & path) = 0; |  | ||||||
| 
 |  | ||||||
|     /* More efficient variant if we just want to know if a path has
 |  | ||||||
|        substitutes. */ |  | ||||||
|     virtual bool hasSubstitutes(const Path & path); |  | ||||||
| 
 |  | ||||||
|     /* Queries the hash of a valid path. */  |     /* Queries the hash of a valid path. */  | ||||||
|     virtual Hash queryPathHash(const Path & path) = 0; |     virtual Hash queryPathHash(const Path & path) = 0; | ||||||
| 
 | 
 | ||||||
|  | @ -81,6 +52,13 @@ public: | ||||||
|        no deriver has been set. */ |        no deriver has been set. */ | ||||||
|     virtual Path queryDeriver(const Path & path) = 0; |     virtual Path queryDeriver(const Path & path) = 0; | ||||||
| 
 | 
 | ||||||
|  |     /* Query the set of substitutable paths. */ | ||||||
|  |     virtual PathSet querySubstitutablePaths() = 0; | ||||||
|  | 
 | ||||||
|  |     /* More efficient variant if we just want to know if a path has
 | ||||||
|  |        substitutes. */ | ||||||
|  |     virtual bool hasSubstitutes(const Path & path); | ||||||
|  |      | ||||||
|     /* Copy the contents of a path to the store and register the
 |     /* Copy the contents of a path to the store and register the
 | ||||||
|        validity the resulting path.  The resulting path is returned. |        validity the resulting path.  The resulting path is returned. | ||||||
|        If `fixed' is true, then the output of a fixed-output |        If `fixed' is true, then the output of a fixed-output | ||||||
|  | @ -109,10 +87,10 @@ public: | ||||||
| 
 | 
 | ||||||
|     /* Ensure that the output paths of the derivation are valid.  If
 |     /* Ensure that the output paths of the derivation are valid.  If
 | ||||||
|        they are already valid, this is a no-op.  Otherwise, validity |        they are already valid, this is a no-op.  Otherwise, validity | ||||||
|        can be reached in two ways.  First, if the output paths have |        can be reached in two ways.  First, if the output paths is | ||||||
|        substitutes, then those can be used.  Second, the output paths |        substitutable, then build the path that way.  Second, the | ||||||
|        can be created by running the builder, after recursively |        output paths can be created by running the builder, after | ||||||
|        building any sub-derivations. */ |        recursively building any sub-derivations. */ | ||||||
|     virtual void buildDerivations(const PathSet & drvPaths) = 0; |     virtual void buildDerivations(const PathSet & drvPaths) = 0; | ||||||
| 
 | 
 | ||||||
|     /* Ensure that a path is valid.  If it is not currently valid, it
 |     /* Ensure that a path is valid.  If it is not currently valid, it
 | ||||||
|  | @ -261,6 +239,17 @@ extern boost::shared_ptr<StoreAPI> store; | ||||||
| boost::shared_ptr<StoreAPI> openStore(bool reserveSpace = true); | boost::shared_ptr<StoreAPI> openStore(bool reserveSpace = true); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | struct ValidPathInfo  | ||||||
|  | { | ||||||
|  |     Path path; | ||||||
|  |     Path deriver; | ||||||
|  |     Hash hash; | ||||||
|  |     PathSet references; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | ValidPathInfo decodeValidPathInfo(std::istream & str); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -12,7 +12,6 @@ namespace nix { | ||||||
| typedef enum { | typedef enum { | ||||||
|     wopQuit, |     wopQuit, | ||||||
|     wopIsValidPath, |     wopIsValidPath, | ||||||
|     wopQuerySubstitutes, |  | ||||||
|     wopHasSubstitutes, |     wopHasSubstitutes, | ||||||
|     wopQueryPathHash, |     wopQueryPathHash, | ||||||
|     wopQueryReferences, |     wopQueryReferences, | ||||||
|  |  | ||||||
|  | @ -19,23 +19,18 @@ using std::vector; | ||||||
| using boost::format; | using boost::format; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class Error : public std::exception | /* BaseError should generally not be caught, as it has Interrupted as
 | ||||||
|  |    a subclass. Catch Error instead. */ | ||||||
|  | class BaseError : public std::exception  | ||||||
| { | { | ||||||
| protected: | protected: | ||||||
|     string err; |     string err; | ||||||
| public: | public: | ||||||
|     Error(const format & f); |     BaseError(const format & f); | ||||||
|     ~Error() throw () { }; |     ~BaseError() throw () { }; | ||||||
|     const char * what() const throw () { return err.c_str(); } |     const char * what() const throw () { return err.c_str(); } | ||||||
|     const string & msg() const throw () { return err; } |     const string & msg() const throw () { return err; } | ||||||
|     Error & addPrefix(const format & f); |     BaseError & addPrefix(const format & f); | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| class SysError : public Error |  | ||||||
| { |  | ||||||
| public: |  | ||||||
|     int errNo; |  | ||||||
|     SysError(const format & f); |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| #define MakeError(newClass, superClass) \ | #define MakeError(newClass, superClass) \ | ||||||
|  | @ -45,6 +40,15 @@ public: | ||||||
|         newClass(const format & f) : superClass(f) { }; \ |         newClass(const format & f) : superClass(f) { }; \ | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  | MakeError(Error, BaseError) | ||||||
|  | 
 | ||||||
|  | class SysError : public Error | ||||||
|  | { | ||||||
|  | public: | ||||||
|  |     int errNo; | ||||||
|  |     SysError(const format & f); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| typedef list<string> Strings; | typedef list<string> Strings; | ||||||
| typedef set<string> StringSet; | typedef set<string> StringSet; | ||||||
|  |  | ||||||
|  | @ -23,13 +23,13 @@ extern char * * environ; | ||||||
| namespace nix { | namespace nix { | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| Error::Error(const format & f) | BaseError::BaseError(const format & f) | ||||||
| { | { | ||||||
|     err = f.str(); |     err = f.str(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| Error & Error::addPrefix(const format & f) | BaseError & BaseError::addPrefix(const format & f) | ||||||
| { | { | ||||||
|     err = f.str() + err; |     err = f.str() + err; | ||||||
|     return *this; |     return *this; | ||||||
|  | @ -491,6 +491,7 @@ string drainFD(int fd) | ||||||
|     string result; |     string result; | ||||||
|     unsigned char buffer[4096]; |     unsigned char buffer[4096]; | ||||||
|     while (1) { |     while (1) { | ||||||
|  |         checkInterrupt(); | ||||||
|         ssize_t rd = read(fd, buffer, sizeof buffer); |         ssize_t rd = read(fd, buffer, sizeof buffer); | ||||||
|         if (rd == -1) { |         if (rd == -1) { | ||||||
|             if (errno != EINTR) |             if (errno != EINTR) | ||||||
|  | @ -775,6 +776,8 @@ void killUser(uid_t uid) | ||||||
| 
 | 
 | ||||||
| string runProgram(Path program, bool searchPath, const Strings & args) | string runProgram(Path program, bool searchPath, const Strings & args) | ||||||
| { | { | ||||||
|  |     checkInterrupt(); | ||||||
|  |      | ||||||
|     /* Create a pipe. */ |     /* Create a pipe. */ | ||||||
|     Pipe pipe; |     Pipe pipe; | ||||||
|     pipe.create(); |     pipe.create(); | ||||||
|  |  | ||||||
|  | @ -256,7 +256,7 @@ void inline checkInterrupt() | ||||||
|     if (_isInterrupted) _interrupted(); |     if (_isInterrupted) _interrupted(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| MakeError(Interrupted, Error) | MakeError(Interrupted, BaseError) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| /* String packing / unpacking. */ | /* String packing / unpacking. */ | ||||||
|  |  | ||||||
|  | @ -11,8 +11,6 @@ Operations: | ||||||
|   --query / -q: query information |   --query / -q: query information | ||||||
|   --read-log / -l: print build log of given store paths |   --read-log / -l: print build log of given store paths | ||||||
| 
 | 
 | ||||||
|   --register-substitutes: register a substitute expression (dangerous!) |  | ||||||
|   --clear-substitutes: clear all substitutes |  | ||||||
|   --register-validity: register path validity (dangerous!) |   --register-validity: register path validity (dangerous!) | ||||||
|   --check-validity: check path validity |   --check-validity: check path validity | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -413,54 +413,6 @@ static void opReadLog(Strings opFlags, Strings opArgs) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| static void opRegisterSubstitutes(Strings opFlags, Strings opArgs) |  | ||||||
| { |  | ||||||
|     if (!opFlags.empty()) throw UsageError("unknown flag"); |  | ||||||
|     if (!opArgs.empty()) throw UsageError("no arguments expected"); |  | ||||||
| 
 |  | ||||||
|     Transaction txn; |  | ||||||
|     createStoreTransaction(txn); |  | ||||||
| 
 |  | ||||||
|     while (1) { |  | ||||||
|         Path srcPath; |  | ||||||
|         Substitute sub; |  | ||||||
|         PathSet references; |  | ||||||
|         getline(cin, srcPath); |  | ||||||
|         if (cin.eof()) break; |  | ||||||
|         getline(cin, sub.deriver); |  | ||||||
|         getline(cin, sub.program); |  | ||||||
|         string s; int n; |  | ||||||
|         getline(cin, s); |  | ||||||
|         if (!string2Int(s, n)) throw Error("number expected"); |  | ||||||
|         while (n--) { |  | ||||||
|             getline(cin, s); |  | ||||||
|             sub.args.push_back(s); |  | ||||||
|         } |  | ||||||
|         getline(cin, s); |  | ||||||
|         if (!string2Int(s, n)) throw Error("number expected"); |  | ||||||
|         while (n--) { |  | ||||||
|             getline(cin, s); |  | ||||||
|             references.insert(s); |  | ||||||
|         } |  | ||||||
|         if (!cin || cin.eof()) throw Error("missing input"); |  | ||||||
|         registerSubstitute(txn, srcPath, sub); |  | ||||||
|         setReferences(txn, srcPath, references); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     txn.commit(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| static void opClearSubstitutes(Strings opFlags, Strings opArgs) |  | ||||||
| { |  | ||||||
|     if (!opFlags.empty()) throw UsageError("unknown flag"); |  | ||||||
|     if (!opArgs.empty()) |  | ||||||
|         throw UsageError("no arguments expected"); |  | ||||||
| 
 |  | ||||||
|     clearSubstitutes(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| static void opRegisterValidity(Strings opFlags, Strings opArgs) | static void opRegisterValidity(Strings opFlags, Strings opArgs) | ||||||
| { | { | ||||||
|     bool reregister = false; // !!! maybe this should be the default
 |     bool reregister = false; // !!! maybe this should be the default
 | ||||||
|  | @ -475,18 +427,8 @@ static void opRegisterValidity(Strings opFlags, Strings opArgs) | ||||||
|     ValidPathInfos infos; |     ValidPathInfos infos; | ||||||
|      |      | ||||||
|     while (1) { |     while (1) { | ||||||
|         ValidPathInfo info; |         ValidPathInfo info = decodeValidPathInfo(cin); | ||||||
|         getline(cin, info.path); |         if (info.path == "") break; | ||||||
|         if (cin.eof()) break; |  | ||||||
|         getline(cin, info.deriver); |  | ||||||
|         string s; int n; |  | ||||||
|         getline(cin, s); |  | ||||||
|         if (!string2Int(s, n)) throw Error("number expected"); |  | ||||||
|         while (n--) { |  | ||||||
|             getline(cin, s); |  | ||||||
|             info.references.insert(s); |  | ||||||
|         } |  | ||||||
|         if (!cin || cin.eof()) throw Error("missing input"); |  | ||||||
|         if (!store->isValidPath(info.path) || reregister) { |         if (!store->isValidPath(info.path) || reregister) { | ||||||
|             /* !!! races */ |             /* !!! races */ | ||||||
|             canonicalisePathMetaData(info.path); |             canonicalisePathMetaData(info.path); | ||||||
|  | @ -699,10 +641,6 @@ void run(Strings args) | ||||||
|             op = opQuery; |             op = opQuery; | ||||||
|         else if (arg == "--read-log" || arg == "-l") |         else if (arg == "--read-log" || arg == "-l") | ||||||
|             op = opReadLog; |             op = opReadLog; | ||||||
|         else if (arg == "--register-substitutes") |  | ||||||
|             op = opRegisterSubstitutes; |  | ||||||
|         else if (arg == "--clear-substitutes") |  | ||||||
|             op = opClearSubstitutes; |  | ||||||
|         else if (arg == "--register-validity") |         else if (arg == "--register-validity") | ||||||
|             op = opRegisterValidity; |             op = opRegisterValidity; | ||||||
|         else if (arg == "--check-validity") |         else if (arg == "--check-validity") | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue