* Make nix-env --dry-run print the paths to be substituted correctly
again. (After the previous substituter mechanism refactoring I didn't update the code that obtains the references of substitutable paths.) This required some refactoring: the substituter programs are now kept running and receive/respond to info requests via stdin/stdout.
This commit is contained in:
parent
fc691e1cbd
commit
3c92ea399d
14 changed files with 338 additions and 272 deletions
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
use strict;
|
use strict;
|
||||||
use File::Basename;
|
use File::Basename;
|
||||||
|
use IO::Handle;
|
||||||
|
|
||||||
|
STDOUT->autoflush(1);
|
||||||
|
|
||||||
my @remoteStoresAll = split ':', ($ENV{"NIX_OTHER_STORES"} or "");
|
my @remoteStoresAll = split ':', ($ENV{"NIX_OTHER_STORES"} or "");
|
||||||
|
|
||||||
|
@ -33,22 +36,25 @@ sub findStorePath {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if ($ARGV[0] eq "--query-paths") {
|
if ($ARGV[0] eq "--query") {
|
||||||
foreach my $store (@remoteStores) {
|
|
||||||
opendir DIR, "$store/var/nix/db/info" or next;
|
|
||||||
print "@storedir@/$_\n" foreach readdir DIR;
|
|
||||||
closedir DIR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
while (<STDIN>) {
|
||||||
|
my $cmd = $_; chomp $cmd;
|
||||||
|
|
||||||
elsif ($ARGV[0] eq "--query-info") {
|
if ($cmd eq "have") {
|
||||||
shift @ARGV;
|
my $storePath = <STDIN>; chomp $storePath;
|
||||||
|
|
||||||
foreach my $storePath (@ARGV) {
|
|
||||||
|
|
||||||
(my $infoFile) = findStorePath $storePath;
|
(my $infoFile) = findStorePath $storePath;
|
||||||
next unless $infoFile;
|
print STDOUT ($infoFile ? "1\n" : "0\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
elsif ($cmd eq "info") {
|
||||||
|
my $storePath = <STDIN>; chomp $storePath;
|
||||||
|
(my $infoFile) = findStorePath $storePath;
|
||||||
|
if (!$infoFile) {
|
||||||
|
print "0\n";
|
||||||
|
next; # not an error
|
||||||
|
}
|
||||||
|
print "1\n";
|
||||||
|
|
||||||
my $deriver = "";
|
my $deriver = "";
|
||||||
my @references = ();
|
my @references = ();
|
||||||
|
@ -56,7 +62,6 @@ elsif ($ARGV[0] eq "--query-info") {
|
||||||
open INFO, "<$infoFile" or die "cannot read info file $infoFile\n";
|
open INFO, "<$infoFile" or die "cannot read info file $infoFile\n";
|
||||||
while (<INFO>) {
|
while (<INFO>) {
|
||||||
chomp;
|
chomp;
|
||||||
#print STDERR "GOT $_\n";
|
|
||||||
/^([\w-]+): (.*)$/ or die "bad info file";
|
/^([\w-]+): (.*)$/ or die "bad info file";
|
||||||
my $key = $1;
|
my $key = $1;
|
||||||
my $value = $2;
|
my $value = $2;
|
||||||
|
@ -65,11 +70,13 @@ elsif ($ARGV[0] eq "--query-info") {
|
||||||
}
|
}
|
||||||
close INFO;
|
close INFO;
|
||||||
|
|
||||||
print "$storePath\n";
|
|
||||||
print "$deriver\n";
|
print "$deriver\n";
|
||||||
print scalar @references, "\n";
|
print scalar @references, "\n";
|
||||||
print "$_\n" foreach @references;
|
print "$_\n" foreach @references;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
else { die "unknown command `$cmd'"; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,27 +5,18 @@ use readmanifest;
|
||||||
use POSIX qw(strftime);
|
use POSIX qw(strftime);
|
||||||
use File::Temp qw(tempdir);
|
use File::Temp qw(tempdir);
|
||||||
|
|
||||||
|
STDOUT->autoflush(1);
|
||||||
|
|
||||||
my $manifestDir = "@localstatedir@/nix/manifests";
|
my $manifestDir = "@localstatedir@/nix/manifests";
|
||||||
my $logFile = "@localstatedir@/log/nix/downloads";
|
my $logFile = "@localstatedir@/log/nix/downloads";
|
||||||
|
|
||||||
|
|
||||||
# Create a temporary directory.
|
|
||||||
my $tmpDir = tempdir("nix-download.XXXXXX", CLEANUP => 1, TMPDIR => 1)
|
|
||||||
or die "cannot create a temporary directory";
|
|
||||||
|
|
||||||
chdir $tmpDir or die "cannot change to `$tmpDir': $!";
|
|
||||||
|
|
||||||
my $tmpNar = "$tmpDir/nar";
|
|
||||||
my $tmpNar2 = "$tmpDir/nar2";
|
|
||||||
|
|
||||||
|
|
||||||
# Load all manifests.
|
# Load all manifests.
|
||||||
my %narFiles;
|
my %narFiles;
|
||||||
my %localPaths;
|
my %localPaths;
|
||||||
my %patches;
|
my %patches;
|
||||||
|
|
||||||
for my $manifest (glob "$manifestDir/*.nixmanifest") {
|
for my $manifest (glob "$manifestDir/*.nixmanifest") {
|
||||||
# print STDERR "reading $manifest\n";
|
|
||||||
if (readManifest($manifest, \%narFiles, \%localPaths, \%patches) < 3) {
|
if (readManifest($manifest, \%narFiles, \%localPaths, \%patches) < 3) {
|
||||||
print STDERR "you have an old-style manifest `$manifest'; please delete it\n";
|
print STDERR "you have an old-style manifest `$manifest'; please delete it\n";
|
||||||
exit 1;
|
exit 1;
|
||||||
|
@ -35,15 +26,19 @@ for my $manifest (glob "$manifestDir/*.nixmanifest") {
|
||||||
|
|
||||||
# Parse the arguments.
|
# Parse the arguments.
|
||||||
|
|
||||||
if ($ARGV[0] eq "--query-paths") {
|
if ($ARGV[0] eq "--query") {
|
||||||
foreach my $storePath (keys %narFiles) { print "$storePath\n"; }
|
|
||||||
foreach my $storePath (keys %localPaths) { print "$storePath\n"; }
|
while (<STDIN>) {
|
||||||
exit 0;
|
my $cmd = $_; chomp $cmd;
|
||||||
|
|
||||||
|
if ($cmd eq "have") {
|
||||||
|
my $storePath = <STDIN>; chomp $storePath;
|
||||||
|
print STDOUT ((defined $narFiles{$storePath} or defined $localPaths{$storePath})
|
||||||
|
? "1\n" : "0\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
elsif ($ARGV[0] eq "--query-info") {
|
elsif ($cmd eq "info") {
|
||||||
shift @ARGV;
|
my $storePath = <STDIN>; chomp $storePath;
|
||||||
foreach my $storePath (@ARGV) {
|
|
||||||
my $info;
|
my $info;
|
||||||
if (defined $narFiles{$storePath}) {
|
if (defined $narFiles{$storePath}) {
|
||||||
$info = @{$narFiles{$storePath}}[0];
|
$info = @{$narFiles{$storePath}}[0];
|
||||||
|
@ -52,17 +47,19 @@ elsif ($ARGV[0] eq "--query-info") {
|
||||||
$info = @{$localPaths{$storePath}}[0];
|
$info = @{$localPaths{$storePath}}[0];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
print "0\n";
|
||||||
next; # not an error
|
next; # not an error
|
||||||
}
|
}
|
||||||
print "$storePath\n";
|
print "1\n";
|
||||||
print "$info->{deriver}\n";
|
print "$info->{deriver}\n";
|
||||||
my @references = split " ", $info->{references};
|
my @references = split " ", $info->{references};
|
||||||
my $count = scalar @references;
|
print scalar @references, "\n";
|
||||||
print "$count\n";
|
print "$_\n" foreach @references;
|
||||||
foreach my $reference (@references) {
|
|
||||||
print "$reference\n";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
else { die "unknown command `$cmd'"; }
|
||||||
}
|
}
|
||||||
|
|
||||||
exit 0;
|
exit 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,6 +72,16 @@ die unless scalar @ARGV == 2;
|
||||||
my $targetPath = $ARGV[1];
|
my $targetPath = $ARGV[1];
|
||||||
|
|
||||||
|
|
||||||
|
# Create a temporary directory.
|
||||||
|
my $tmpDir = tempdir("nix-download.XXXXXX", CLEANUP => 1, TMPDIR => 1)
|
||||||
|
or die "cannot create a temporary directory";
|
||||||
|
|
||||||
|
chdir $tmpDir or die "cannot change to `$tmpDir': $!";
|
||||||
|
|
||||||
|
my $tmpNar = "$tmpDir/nar";
|
||||||
|
my $tmpNar2 = "$tmpDir/nar2";
|
||||||
|
|
||||||
|
|
||||||
open LOGFILE, ">>$logFile" or die "cannot open log file $logFile";
|
open LOGFILE, ">>$logFile" or die "cannot open log file $logFile";
|
||||||
|
|
||||||
my $date = strftime ("%F %H:%M:%S UTC", gmtime (time));
|
my $date = strftime ("%F %H:%M:%S UTC", gmtime (time));
|
||||||
|
|
|
@ -184,11 +184,6 @@ 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;
|
||||||
|
|
||||||
|
@ -243,11 +238,6 @@ 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);
|
|
||||||
|
|
||||||
/* Wait for any goal to finish. Pretty indiscriminate way to
|
/* Wait for any goal to finish. Pretty indiscriminate way to
|
||||||
wait for some resource that some other goal is holding. */
|
wait for some resource that some other goal is holding. */
|
||||||
void waitForAnyGoal(GoalPtr goal);
|
void waitForAnyGoal(GoalPtr goal);
|
||||||
|
@ -258,12 +248,6 @@ public:
|
||||||
/* 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();
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -2042,12 +2026,12 @@ void DerivationGoal::initChild()
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Close all other file descriptors. */
|
/* Close all other file descriptors. */
|
||||||
int maxFD = 0;
|
set<int> exceptions;
|
||||||
maxFD = sysconf(_SC_OPEN_MAX);
|
if (inHook) {
|
||||||
for (int fd = 0; fd < maxFD; ++fd)
|
exceptions.insert(3);
|
||||||
if (fd != STDIN_FILENO && fd != STDOUT_FILENO && fd != STDERR_FILENO
|
exceptions.insert(4);
|
||||||
&& (!inHook || (fd != 3 && fd != 4)))
|
}
|
||||||
close(fd); /* ignore result */
|
closeMostFDs(exceptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2118,10 +2102,8 @@ private:
|
||||||
/* The current substituter. */
|
/* The current substituter. */
|
||||||
Path sub;
|
Path sub;
|
||||||
|
|
||||||
/* Path info returned by the substituter's --query-info operation. */
|
/* Path info returned by the substituter's query info operation. */
|
||||||
bool infoOkay;
|
SubstitutablePathInfo info;
|
||||||
PathSet references;
|
|
||||||
Path deriver;
|
|
||||||
|
|
||||||
/* Pipe for the substitute's standard output/error. */
|
/* Pipe for the substitute's standard output/error. */
|
||||||
Pipe logPipe;
|
Pipe logPipe;
|
||||||
|
@ -2205,6 +2187,42 @@ void SubstitutionGoal::init()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!worker.store.querySubstitutablePathInfo(storePath, info)) {
|
||||||
|
printMsg(lvlError,
|
||||||
|
format("path `%1%' is required, but there is no substituter that knows anything about it")
|
||||||
|
% storePath);
|
||||||
|
amDone(ecFailed);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* To maintain the closure invariant, we first have to realise the
|
||||||
|
paths referenced by this one. */
|
||||||
|
foreach (PathSet::iterator, i, info.references)
|
||||||
|
if (*i != storePath) /* ignore self-references */
|
||||||
|
addWaitee(worker.makeSubstitutionGoal(*i));
|
||||||
|
|
||||||
|
if (waitees.empty()) /* to prevent hang (no wake-up event) */
|
||||||
|
referencesValid();
|
||||||
|
else
|
||||||
|
state = &SubstitutionGoal::referencesValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SubstitutionGoal::referencesValid()
|
||||||
|
{
|
||||||
|
trace("all references realised");
|
||||||
|
|
||||||
|
if (nrFailed > 0) {
|
||||||
|
printMsg(lvlError,
|
||||||
|
format("some references of path `%1%' could not be realised") % storePath);
|
||||||
|
amDone(ecFailed);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (PathSet::iterator, i, info.references)
|
||||||
|
if (*i != storePath) /* ignore self-references */
|
||||||
|
assert(worker.store.isValidPath(*i));
|
||||||
|
|
||||||
subs = substituters;
|
subs = substituters;
|
||||||
|
|
||||||
tryNext();
|
tryNext();
|
||||||
|
@ -2228,51 +2246,6 @@ void SubstitutionGoal::tryNext()
|
||||||
sub = subs.front();
|
sub = subs.front();
|
||||||
subs.pop_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
|
|
||||||
paths referenced by this one. */
|
|
||||||
for (PathSet::iterator i = references.begin();
|
|
||||||
i != references.end(); ++i)
|
|
||||||
if (*i != storePath) /* ignore self-references */
|
|
||||||
addWaitee(worker.makeSubstitutionGoal(*i));
|
|
||||||
|
|
||||||
if (waitees.empty()) /* to prevent hang (no wake-up event) */
|
|
||||||
referencesValid();
|
|
||||||
else
|
|
||||||
state = &SubstitutionGoal::referencesValid;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void SubstitutionGoal::referencesValid()
|
|
||||||
{
|
|
||||||
trace("all references realised");
|
|
||||||
|
|
||||||
if (nrFailed > 0) {
|
|
||||||
printMsg(lvlError,
|
|
||||||
format("some references of path `%1%' could not be realised") % storePath);
|
|
||||||
amDone(ecFailed);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (PathSet::iterator i = references.begin();
|
|
||||||
i != references.end(); ++i)
|
|
||||||
if (*i != storePath) /* ignore self-references */
|
|
||||||
assert(worker.store.isValidPath(*i));
|
|
||||||
|
|
||||||
state = &SubstitutionGoal::tryToRun;
|
state = &SubstitutionGoal::tryToRun;
|
||||||
worker.waitForBuildSlot(shared_from_this());
|
worker.waitForBuildSlot(shared_from_this());
|
||||||
}
|
}
|
||||||
|
@ -2413,7 +2386,7 @@ void SubstitutionGoal::finished()
|
||||||
Hash contentHash = hashPath(htSHA256, storePath);
|
Hash contentHash = hashPath(htSHA256, storePath);
|
||||||
|
|
||||||
worker.store.registerValidPath(storePath, contentHash,
|
worker.store.registerValidPath(storePath, contentHash,
|
||||||
references, deriver);
|
info.references, info.deriver);
|
||||||
|
|
||||||
outputLock->setDeletion(true);
|
outputLock->setDeletion(true);
|
||||||
|
|
||||||
|
@ -2608,14 +2581,6 @@ 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::waitForAnyGoal(GoalPtr goal)
|
void Worker::waitForAnyGoal(GoalPtr goal)
|
||||||
{
|
{
|
||||||
debug("wait for any goal");
|
debug("wait for any goal");
|
||||||
|
@ -2623,68 +2588,6 @@ void Worker::waitForAnyGoal(GoalPtr 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();
|
||||||
|
@ -2711,8 +2614,6 @@ void Worker::run(const Goals & _topGoals)
|
||||||
|
|
||||||
if (topGoals.empty()) break;
|
if (topGoals.empty()) break;
|
||||||
|
|
||||||
getInfo();
|
|
||||||
|
|
||||||
/* Wait for input. */
|
/* Wait for input. */
|
||||||
if (!children.empty())
|
if (!children.empty())
|
||||||
waitForInput();
|
waitForInput();
|
||||||
|
|
|
@ -83,6 +83,13 @@ LocalStore::~LocalStore()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
flushDelayedUpdates();
|
flushDelayedUpdates();
|
||||||
|
|
||||||
|
foreach (RunningSubstituters::iterator, i, runningSubstituters) {
|
||||||
|
i->second.toBuf.reset();
|
||||||
|
i->second.to.reset();
|
||||||
|
i->second.pid.wait(true);
|
||||||
|
}
|
||||||
|
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
ignoreException();
|
ignoreException();
|
||||||
}
|
}
|
||||||
|
@ -367,8 +374,6 @@ ValidPathInfo LocalStore::queryPathInfo(const Path & path)
|
||||||
std::map<Path, ValidPathInfo>::iterator lookup = pathInfoCache.find(path);
|
std::map<Path, ValidPathInfo>::iterator lookup = pathInfoCache.find(path);
|
||||||
if (lookup != pathInfoCache.end()) return lookup->second;
|
if (lookup != pathInfoCache.end()) return lookup->second;
|
||||||
|
|
||||||
//printMsg(lvlError, "queryPathInfo: " + path);
|
|
||||||
|
|
||||||
/* Read the info file. */
|
/* Read the info file. */
|
||||||
Path infoFile = infoFileFor(path);
|
Path infoFile = infoFileFor(path);
|
||||||
if (!pathExists(infoFile))
|
if (!pathExists(infoFile))
|
||||||
|
@ -467,33 +472,105 @@ Path LocalStore::queryDeriver(const Path & path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
PathSet LocalStore::querySubstitutablePaths()
|
void LocalStore::startSubstituter(const Path & substituter, RunningSubstituter & run)
|
||||||
{
|
{
|
||||||
if (!substitutablePathsLoaded) {
|
if (run.pid != -1) return;
|
||||||
for (Paths::iterator i = substituters.begin(); i != substituters.end(); ++i) {
|
|
||||||
debug(format("running `%1%' to find out substitutable paths") % *i);
|
debug(format("starting substituter program `%1%'") % substituter);
|
||||||
Strings args;
|
|
||||||
args.push_back("--query-paths");
|
Pipe toPipe, fromPipe;
|
||||||
Strings ss = tokenizeString(runProgram(*i, false, args), "\n");
|
|
||||||
for (Strings::iterator j = ss.begin(); j != ss.end(); ++j) {
|
toPipe.create();
|
||||||
if (!isStorePath(*j))
|
fromPipe.create();
|
||||||
throw Error(format("`%1%' returned a bad substitutable path `%2%'")
|
|
||||||
% *i % *j);
|
run.pid = fork();
|
||||||
substitutablePaths.insert(*j);
|
|
||||||
|
switch (run.pid) {
|
||||||
|
|
||||||
|
case -1:
|
||||||
|
throw SysError("unable to fork");
|
||||||
|
|
||||||
|
case 0: /* child */
|
||||||
|
try {
|
||||||
|
fromPipe.readSide.close();
|
||||||
|
toPipe.writeSide.close();
|
||||||
|
if (dup2(toPipe.readSide, STDIN_FILENO) == -1)
|
||||||
|
throw SysError("dupping stdin");
|
||||||
|
if (dup2(fromPipe.writeSide, STDOUT_FILENO) == -1)
|
||||||
|
throw SysError("dupping stdout");
|
||||||
|
closeMostFDs(set<int>());
|
||||||
|
execl(substituter.c_str(), substituter.c_str(), "--query", NULL);
|
||||||
|
throw SysError(format("executing `%1%'") % substituter);
|
||||||
|
} catch (std::exception & e) {
|
||||||
|
std::cerr << "error: " << e.what() << std::endl;
|
||||||
}
|
}
|
||||||
}
|
quickExit(1);
|
||||||
substitutablePathsLoaded = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return substitutablePaths;
|
/* Parent. */
|
||||||
|
|
||||||
|
toPipe.readSide.close();
|
||||||
|
fromPipe.writeSide.close();
|
||||||
|
|
||||||
|
run.toBuf = boost::shared_ptr<stdio_filebuf>(new stdio_filebuf(toPipe.writeSide.borrow(), std::ios_base::out));
|
||||||
|
run.to = boost::shared_ptr<std::ostream>(new std::ostream(&*run.toBuf));
|
||||||
|
|
||||||
|
run.fromBuf = boost::shared_ptr<stdio_filebuf>(new stdio_filebuf(fromPipe.readSide.borrow(), std::ios_base::in));
|
||||||
|
run.from = boost::shared_ptr<std::istream>(new std::istream(&*run.fromBuf));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool LocalStore::hasSubstitutes(const Path & path)
|
bool LocalStore::hasSubstitutes(const Path & path)
|
||||||
{
|
{
|
||||||
if (!substitutablePathsLoaded)
|
foreach (Paths::iterator, i, substituters) {
|
||||||
querySubstitutablePaths();
|
RunningSubstituter & run(runningSubstituters[*i]);
|
||||||
return substitutablePaths.find(path) != substitutablePaths.end();
|
startSubstituter(*i, run);
|
||||||
|
|
||||||
|
*run.to << "have\n" << path << "\n" << std::flush;
|
||||||
|
|
||||||
|
string s;
|
||||||
|
|
||||||
|
int res;
|
||||||
|
getline(*run.from, s);
|
||||||
|
if (!string2Int(s, res)) abort();
|
||||||
|
|
||||||
|
if (res) return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool LocalStore::querySubstitutablePathInfo(const Path & path,
|
||||||
|
SubstitutablePathInfo & info)
|
||||||
|
{
|
||||||
|
foreach (Paths::iterator, i, substituters) {
|
||||||
|
RunningSubstituter & run(runningSubstituters[*i]);
|
||||||
|
startSubstituter(*i, run);
|
||||||
|
|
||||||
|
*run.to << "info\n" << path << "\n" << std::flush;
|
||||||
|
|
||||||
|
string s;
|
||||||
|
|
||||||
|
int res;
|
||||||
|
getline(*run.from, s);
|
||||||
|
if (!string2Int(s, res)) abort();
|
||||||
|
|
||||||
|
if (res) {
|
||||||
|
getline(*run.from, info.deriver);
|
||||||
|
int nrRefs;
|
||||||
|
getline(*run.from, s);
|
||||||
|
if (!string2Int(s, nrRefs)) abort();
|
||||||
|
while (nrRefs--) {
|
||||||
|
Path p; getline(*run.from, p);
|
||||||
|
info.references.insert(p);
|
||||||
|
}
|
||||||
|
info.downloadSize = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include <ext/stdio_filebuf.h>
|
||||||
|
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
|
|
||||||
|
@ -34,12 +36,27 @@ struct OptimiseStats
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
typedef __gnu_cxx::stdio_filebuf<char> stdio_filebuf;
|
||||||
|
|
||||||
|
|
||||||
|
struct RunningSubstituter
|
||||||
|
{
|
||||||
|
Pid pid;
|
||||||
|
boost::shared_ptr<stdio_filebuf> toBuf, fromBuf;
|
||||||
|
boost::shared_ptr<std::ostream> to;
|
||||||
|
boost::shared_ptr<std::istream> from;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class LocalStore : public StoreAPI
|
class LocalStore : public StoreAPI
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
bool substitutablePathsLoaded;
|
bool substitutablePathsLoaded;
|
||||||
PathSet substitutablePaths;
|
PathSet substitutablePaths;
|
||||||
|
|
||||||
|
typedef std::map<Path, RunningSubstituter> RunningSubstituters;
|
||||||
|
RunningSubstituters runningSubstituters;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/* Initialise the local store, upgrading the schema if
|
/* Initialise the local store, upgrading the schema if
|
||||||
|
@ -66,6 +83,9 @@ public:
|
||||||
|
|
||||||
bool hasSubstitutes(const Path & path);
|
bool hasSubstitutes(const Path & path);
|
||||||
|
|
||||||
|
bool querySubstitutablePathInfo(const Path & path,
|
||||||
|
SubstitutablePathInfo & info);
|
||||||
|
|
||||||
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);
|
||||||
|
@ -147,6 +167,9 @@ private:
|
||||||
void tryToDelete(const GCOptions & options, GCResults & results,
|
void tryToDelete(const GCOptions & options, GCResults & results,
|
||||||
const PathSet & livePaths, const PathSet & tempRootsClosed, PathSet & done,
|
const PathSet & livePaths, const PathSet & tempRootsClosed, PathSet & done,
|
||||||
const Path & path);
|
const Path & path);
|
||||||
|
|
||||||
|
void startSubstituter(const Path & substituter,
|
||||||
|
RunningSubstituter & runningSubstituter);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "misc.hh"
|
#include "misc.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
|
#include "local-store.hh"
|
||||||
|
|
||||||
#include <aterm2.h>
|
#include <aterm2.h>
|
||||||
|
|
||||||
|
@ -79,10 +80,13 @@ void queryMissing(const PathSet & targets,
|
||||||
|
|
||||||
else {
|
else {
|
||||||
if (store->isValidPath(p)) continue;
|
if (store->isValidPath(p)) continue;
|
||||||
if (store->hasSubstitutes(p))
|
SubstitutablePathInfo info;
|
||||||
|
if (dynamic_cast<LocalStore *>(store.get())->querySubstitutablePathInfo(p, info)) {
|
||||||
willSubstitute.insert(p);
|
willSubstitute.insert(p);
|
||||||
// XXX call the substituters
|
todo.insert(info.references.begin(), info.references.end());
|
||||||
// store->queryReferences(p, todo);
|
}
|
||||||
|
/* Not substitutable and not buildable; should we flag
|
||||||
|
this? */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -213,6 +213,13 @@ bool RemoteStore::hasSubstitutes(const Path & path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool RemoteStore::querySubstitutablePathInfo(const Path & path,
|
||||||
|
SubstitutablePathInfo & info)
|
||||||
|
{
|
||||||
|
throw Error("not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Hash RemoteStore::queryPathHash(const Path & path)
|
Hash RemoteStore::queryPathHash(const Path & path)
|
||||||
{
|
{
|
||||||
writeInt(wopQueryPathHash, to);
|
writeInt(wopQueryPathHash, to);
|
||||||
|
@ -256,12 +263,6 @@ 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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -37,10 +37,11 @@ public:
|
||||||
|
|
||||||
Path queryDeriver(const Path & path);
|
Path queryDeriver(const Path & path);
|
||||||
|
|
||||||
PathSet querySubstitutablePaths();
|
|
||||||
|
|
||||||
bool hasSubstitutes(const Path & path);
|
bool hasSubstitutes(const Path & path);
|
||||||
|
|
||||||
|
bool querySubstitutablePathInfo(const Path & path,
|
||||||
|
SubstitutablePathInfo & info);
|
||||||
|
|
||||||
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);
|
||||||
|
|
|
@ -17,13 +17,6 @@ GCOptions::GCOptions()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool StoreAPI::hasSubstitutes(const Path & path)
|
|
||||||
{
|
|
||||||
PathSet paths = querySubstitutablePaths();
|
|
||||||
return paths.find(path) != paths.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool isInStore(const Path & path)
|
bool isInStore(const Path & path)
|
||||||
{
|
{
|
||||||
return path[0] == '/'
|
return path[0] == '/'
|
||||||
|
|
|
@ -88,6 +88,14 @@ struct GCResults
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct SubstitutablePathInfo
|
||||||
|
{
|
||||||
|
Path deriver;
|
||||||
|
PathSet references;
|
||||||
|
unsigned long long downloadSize; /* 0 = unknown or inapplicable */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class StoreAPI
|
class StoreAPI
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -117,12 +125,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. */
|
/* Query whether a path has substitutes. */
|
||||||
virtual PathSet querySubstitutablePaths() = 0;
|
virtual bool hasSubstitutes(const Path & path) = 0;
|
||||||
|
|
||||||
/* More efficient variant if we just want to know if a path has
|
/* Query the references, deriver and download size of a
|
||||||
substitutes. */
|
substitutable path. */
|
||||||
virtual bool hasSubstitutes(const Path & path);
|
virtual bool querySubstitutablePathInfo(const Path & path,
|
||||||
|
SubstitutablePathInfo & info) = 0;
|
||||||
|
|
||||||
/* 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.
|
||||||
|
|
|
@ -579,7 +579,14 @@ AutoCloseFD::AutoCloseFD(int fd)
|
||||||
|
|
||||||
AutoCloseFD::AutoCloseFD(const AutoCloseFD & fd)
|
AutoCloseFD::AutoCloseFD(const AutoCloseFD & fd)
|
||||||
{
|
{
|
||||||
abort();
|
/* Copying a AutoCloseFD isn't allowed (who should get to close
|
||||||
|
it?). But as a edge case, allow copying of closed
|
||||||
|
AutoCloseFDs. This is necessary due to tiresome reasons
|
||||||
|
involving copy constructor use on default object values in STL
|
||||||
|
containers (like when you do `map[value]' where value isn't in
|
||||||
|
the map yet). */
|
||||||
|
this->fd = fd.fd;
|
||||||
|
if (this->fd != -1) abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -832,7 +839,7 @@ string runProgram(Path program, bool searchPath, const Strings & args)
|
||||||
pipe.readSide.close();
|
pipe.readSide.close();
|
||||||
|
|
||||||
if (dup2(pipe.writeSide, STDOUT_FILENO) == -1)
|
if (dup2(pipe.writeSide, STDOUT_FILENO) == -1)
|
||||||
throw SysError("dupping from-hook write side");
|
throw SysError("dupping stdout");
|
||||||
|
|
||||||
std::vector<const char *> cargs; /* careful with c_str()! */
|
std::vector<const char *> cargs; /* careful with c_str()! */
|
||||||
cargs.push_back(program.c_str());
|
cargs.push_back(program.c_str());
|
||||||
|
@ -868,6 +875,17 @@ string runProgram(Path program, bool searchPath, const Strings & args)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void closeMostFDs(const set<int> & exceptions)
|
||||||
|
{
|
||||||
|
int maxFD = 0;
|
||||||
|
maxFD = sysconf(_SC_OPEN_MAX);
|
||||||
|
for (int fd = 0; fd < maxFD; ++fd)
|
||||||
|
if (fd != STDIN_FILENO && fd != STDOUT_FILENO && fd != STDERR_FILENO
|
||||||
|
&& exceptions.find(fd) == exceptions.end())
|
||||||
|
close(fd); /* ignore result */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void quickExit(int status)
|
void quickExit(int status)
|
||||||
{
|
{
|
||||||
#ifdef __CYGWIN__
|
#ifdef __CYGWIN__
|
||||||
|
|
|
@ -245,6 +245,10 @@ void killUser(uid_t uid);
|
||||||
string runProgram(Path program, bool searchPath = false,
|
string runProgram(Path program, bool searchPath = false,
|
||||||
const Strings & args = Strings());
|
const Strings & args = Strings());
|
||||||
|
|
||||||
|
/* Close all file descriptors except stdin, stdout, stderr, and those
|
||||||
|
listed in the given set. Good practice in child processes. */
|
||||||
|
void closeMostFDs(const set<int> & exceptions);
|
||||||
|
|
||||||
/* Wrapper around _exit() on Unix and ExitProcess() on Windows. (On
|
/* Wrapper around _exit() on Unix and ExitProcess() on Windows. (On
|
||||||
Cygwin, _exit() doesn't seem to do the right thing.) */
|
Cygwin, _exit() doesn't seem to do the right thing.) */
|
||||||
void quickExit(int status);
|
void quickExit(int status);
|
||||||
|
|
|
@ -1,14 +1,25 @@
|
||||||
#! /bin/sh -e
|
#! /bin/sh -e
|
||||||
echo substituter args: $* >&2
|
echo substituter args: $* >&2
|
||||||
|
|
||||||
if test $1 = "--query-paths"; then
|
if test $1 = "--query"; then
|
||||||
cat $TEST_ROOT/sub-paths
|
while read cmd; do
|
||||||
elif test $1 = "--query-info"; then
|
echo FOO $cmd >&2
|
||||||
shift
|
if test "$cmd" = "have"; then
|
||||||
for i in in $@; do
|
read path
|
||||||
echo $i
|
if grep -q "$path" $TEST_ROOT/sub-paths; then
|
||||||
|
echo 1
|
||||||
|
else
|
||||||
|
echo 0
|
||||||
|
fi
|
||||||
|
elif test "$cmd" = "info"; then
|
||||||
|
read path
|
||||||
|
echo 1
|
||||||
echo "" # deriver
|
echo "" # deriver
|
||||||
echo 0 # nr of refs
|
echo 0 # nr of refs
|
||||||
|
else
|
||||||
|
echo "bad command $cmd"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
done
|
done
|
||||||
elif test $1 = "--substitute"; then
|
elif test $1 = "--substitute"; then
|
||||||
mkdir $2
|
mkdir $2
|
||||||
|
|
|
@ -1,14 +1,24 @@
|
||||||
#! /bin/sh -e
|
#! /bin/sh -e
|
||||||
echo substituter2 args: $* >&2
|
echo substituter2 args: $* >&2
|
||||||
|
|
||||||
if test $1 = "--query-paths"; then
|
if test $1 = "--query"; then
|
||||||
cat $TEST_ROOT/sub-paths
|
while read cmd; do
|
||||||
elif test $1 = "--query-info"; then
|
if test "$cmd" = "have"; then
|
||||||
shift
|
read path
|
||||||
for i in in $@; do
|
if grep -q "$path" $TEST_ROOT/sub-paths; then
|
||||||
echo $i
|
echo 1
|
||||||
|
else
|
||||||
|
echo 0
|
||||||
|
fi
|
||||||
|
elif test "$cmd" = "info"; then
|
||||||
|
read path
|
||||||
|
echo 1
|
||||||
echo "" # deriver
|
echo "" # deriver
|
||||||
echo 0 # nr of refs
|
echo 0 # nr of refs
|
||||||
|
else
|
||||||
|
echo "bad command $cmd"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
done
|
done
|
||||||
elif test $1 = "--substitute"; then
|
elif test $1 = "--substitute"; then
|
||||||
exit 1
|
exit 1
|
||||||
|
|
Loading…
Reference in a new issue