Skip to content

Commit

Permalink
Support building a derivation if some outputs are already valid
Browse files Browse the repository at this point in the history
This handles the chroot and build hook cases, which are easy.
Supporting the non-chroot-build case will require more work (hash
rewriting!).

Issue #21.
  • Loading branch information
edolstra committed Sep 11, 2012
1 parent 295027f commit a2785b7
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 23 deletions.
23 changes: 12 additions & 11 deletions scripts/build-remote.pl.in
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use IO::Handle;
use Nix::Config;
use Nix::SSH qw/sshOpts openSSHConnection/;
use Nix::CopyClosure;
use Nix::Store;
no warnings('once');


Expand All @@ -18,7 +19,7 @@ no warnings('once');
# less than the maximum load for that machine, we try to get an
# exclusive lock on $currentLoad/$machine-$slot (without blocking).
# If we get such a lock, we send "accept" to the caller. Otherwise,
# we send "postpone" and exit.
# we send "postpone" and exit.
# - We release the exclusive lock on $currentLoad/main-lock.
# - We perform the build on $neededSystem.
# - We release the exclusive lock on $currentLoad/$machine-$slot.
Expand Down Expand Up @@ -102,14 +103,14 @@ REQ: while (1) {
sendReply "decline";
next;
}

# Acquire the exclusive lock on $currentLoad/main-lock.
mkdir $currentLoad, 0777 or die unless -d $currentLoad;
my $mainLock = "$currentLoad/main-lock";
sysopen MAINLOCK, "$mainLock", O_RDWR|O_CREAT, 0600 or die;
flock(MAINLOCK, LOCK_EX) or die;


while (1) {
# Find all machine that can execute this build, i.e., that
# support builds for the given platform and features, and are
Expand Down Expand Up @@ -141,7 +142,7 @@ REQ: while (1) {
close $slotLock;
$slot++;
}

push @available, { machine => $cur, load => $load, free => $free }
if $load < $cur->{maxJobs};
}
Expand All @@ -160,7 +161,7 @@ REQ: while (1) {
if ($rightType && !$canBuildLocally) {
sendReply "postpone";
} else {
sendReply "decline";
sendReply "decline";
}
close MAINLOCK;
next REQ;
Expand All @@ -182,9 +183,9 @@ REQ: while (1) {


# Select the best available machine and lock a free slot.
my $selected = $available[0];
my $selected = $available[0];
my $machine = $selected->{machine};

$slotLock = openSlotLock($machine, $selected->{free});
flock($slotLock, LOCK_EX | LOCK_NB) or die;
utime undef, undef, $slotLock;
Expand All @@ -196,7 +197,7 @@ REQ: while (1) {
@sshOpts = ("-i", $machine->{sshKeys}, "-x");
$hostName = $machine->{hostName};
last REQ if openSSHConnection $hostName;

warn "unable to open SSH connection to $hostName, trying other available machines...\n";
$machine->{enabled} = 0;
}
Expand Down Expand Up @@ -286,10 +287,10 @@ if (system("exec ssh $hostName @sshOpts '(read; kill -INT -\$\$) <&0 & nix-store
foreach my $output (@outputs) {
my $maybeSignRemote = "";
$maybeSignRemote = "--sign" if $UID != 0;

next if isValidPath($output);
system("exec ssh $hostName @sshOpts 'nix-store --export $maybeSignRemote $output'" .
"| NIX_HELD_LOCKS=$output @bindir@/nix-store --import > /dev/null") == 0
or die "cannot copy $output from $hostName: $?";
or die "cannot copy $output from $hostName: $?";
}


Expand Down
28 changes: 16 additions & 12 deletions src/libstore/build.cc
Original file line number Diff line number Diff line change
Expand Up @@ -756,6 +756,9 @@ class DerivationGoal : public Goal
/* Referenceable paths (i.e., input and output paths). */
PathSet allPaths;

/* Outputs that are already valid. */
PathSet validPaths;

/* User selected for running the builder. */
UserLock buildUser;

Expand Down Expand Up @@ -1141,7 +1144,7 @@ void DerivationGoal::tryToBuild()
omitted, but that would be less efficient.) Note that since we
now hold the locks on the output paths, no other process can
build this derivation, so no further checks are necessary. */
PathSet validPaths = checkPathValidity(true);
validPaths = checkPathValidity(true);
if (validPaths.size() == drv.outputs.size()) {
debug(format("skipping build of derivation `%1%', someone beat us to it")
% drvPath);
Expand All @@ -1150,22 +1153,16 @@ void DerivationGoal::tryToBuild()
return;
}

if (validPaths.size() > 0)
/* !!! fix this; try to delete valid paths */
throw Error(
format("derivation `%1%' is blocked by its output paths")
% drvPath);
printMsg(lvlError, format("BLOCKERS: %1%") % showPaths(validPaths));

/* If any of the outputs already exist but are not valid, delete
them. */
foreach (DerivationOutputs::iterator, i, drv.outputs) {
Path path = i->second.path;
if (worker.store.isValidPath(path))
throw Error(format("obstructed build: path `%1%' exists") % path);
if (pathExists(path)) {
debug(format("removing unregistered path `%1%'") % path);
deletePathWrapped(path);
}
if (worker.store.isValidPath(path)) continue;
if (!pathExists(path)) continue;
debug(format("removing unregistered path `%1%'") % path);
deletePathWrapped(path);
}

/* Check again whether any output previously failed to build,
Expand Down Expand Up @@ -1283,6 +1280,10 @@ void DerivationGoal::buildDone()
Path path = i->second.path;

if (useChroot && pathExists(chrootRootDir + path)) {
/* Move output paths from the chroot to the Nix store.
If the output was already valid, just skip
(discard) it. */
if (validPaths.find(path) != validPaths.end()) continue;
if (rename((chrootRootDir + path).c_str(), path.c_str()) == -1)
throw SysError(format("moving build output `%1%' from the chroot to the Nix store") % path);
}
Expand Down Expand Up @@ -1747,6 +1748,9 @@ void DerivationGoal::startBuilder()
#else
throw Error("chroot builds are not supported on this platform");
#endif
} else { // !useChroot
if (validPaths.size() > 0)
throw Error(format("derivation `%1%' is blocked by its output paths %2%") % drvPath % showPaths(validPaths));
}


Expand Down

0 comments on commit a2785b7

Please sign in to comment.