forked from lix-project/lix
* 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
|
@ -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;
|
||||||
|
|
||||||
|
@ -212,12 +217,24 @@ public:
|
||||||
/* Put `goal' to sleep until a child process terminates, i.e., a
|
/* Put `goal' to sleep until a child process terminates, i.e., a
|
||||||
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();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -783,7 +800,7 @@ void DerivationGoal::haveDerivation()
|
||||||
substitutes. */
|
substitutes. */
|
||||||
if (store->hasSubstitutes(*i))
|
if (store->hasSubstitutes(*i))
|
||||||
addWaitee(worker.makeSubstitutionGoal(*i));
|
addWaitee(worker.makeSubstitutionGoal(*i));
|
||||||
|
|
||||||
if (waitees.empty()) /* to prevent hang (no wake-up event) */
|
if (waitees.empty()) /* to prevent hang (no wake-up event) */
|
||||||
outputsSubstituted();
|
outputsSubstituted();
|
||||||
else
|
else
|
||||||
|
@ -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,
|
||||||
|
@ -1961,28 +2012,7 @@ void SubstitutionGoal::referencesValid()
|
||||||
i != references.end(); ++i)
|
i != references.end(); ++i)
|
||||||
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…
Reference in a new issue