diff --git a/scripts/build-remote.pl.in b/scripts/build-remote.pl.in index a8f73f2e54..0c8081a0bc 100755 --- a/scripts/build-remote.pl.in +++ b/scripts/build-remote.pl.in @@ -36,6 +36,8 @@ sub sendReply { print STDERR "# $reply\n"; } +sub all { $_ || return 0 for @_; 1 } + # Initialisation. my $loadIncreased = 0; @@ -64,13 +66,14 @@ if (defined $conf && -e $conf) { chomp; s/\#.*$//g; next if /^\s*$/; - /^\s*(\S+)\s+(\S+)\s+(\S+)\s+(\d+)(\s+([0-9\.]+))?\s*$/ or die; + my @tokens = split /\s/, $_; push @machines, - { hostName => $1 - , systemTypes => [split(/,/, $2)] - , sshKeys => $3 - , maxJobs => $4 - , speedFactor => 1.0 * ($6 || 1) + { hostName => $tokens[0] + , systemTypes => [ split(/,/, $tokens[1]) ] + , sshKeys => $tokens[2] + , maxJobs => int($tokens[3]) + , speedFactor => 1.0 * (defined $tokens[4] ? int($tokens[4]) : 1) + , features => [ split(/,/, $tokens[5] || "") ] , enabled => 1 }; } @@ -85,7 +88,8 @@ my ($drvPath, $hostName, $slotLock); REQ: while (1) { $_ = || exit 0; my ($amWilling, $neededSystem); - ($amWilling, $neededSystem, $drvPath) = split; + ($amWilling, $neededSystem, $drvPath, $requiredFeatures) = split; + my @requiredFeatures = split /,/, $requiredFeatures; my $canBuildLocally = $amWilling && ($localSystem eq $neededSystem); @@ -103,12 +107,15 @@ REQ: while (1) { while (1) { # Find all machine that can execute this build, i.e., that - # support builds for the given platform and are not at their - # job limit. + # support builds for the given platform and features, and are + # not at their job limit. my $rightType = 0; my @available = (); LOOP: foreach my $cur (@machines) { - if ($cur->{enabled} && grep { $neededSystem eq $_ } @{$cur->{systemTypes}}) { + if ($cur->{enabled} + && (grep { $neededSystem eq $_ } @{$cur->{systemTypes}}) + && all(map { my $f = $_; 0 != grep { $f eq $_ } @{$cur->{features}} } @requiredFeatures)) + { $rightType = 1; # We have a machine of the right type. Determine the load on diff --git a/src/libstore/build.cc b/src/libstore/build.cc index f2781776c6..f9c9a0a1ea 100644 --- a/src/libstore/build.cc +++ b/src/libstore/build.cc @@ -1327,8 +1327,16 @@ HookReply DerivationGoal::tryBuildHook() if (!worker.hook) worker.hook = boost::shared_ptr(new HookInstance); - writeLine(worker.hook->toHook.writeSide, (format("%1% %2% %3%") % - (worker.getNrLocalBuilds() < maxBuildJobs ? "1" : "0") % drv.platform % drvPath).str()); + /* Tell the hook about system features (beyond the system type) + required from the build machine. (The hook could parse the + drv file itself, but this is easier.) */ + Strings features = tokenizeString(drv.env["requiredSystemFeatures"]); + foreach (Strings::iterator, i, features) checkStoreName(*i); /* !!! abuse */ + + /* Send the request to the hook. */ + writeLine(worker.hook->toHook.writeSide, (format("%1% %2% %3% %4%") + % (worker.getNrLocalBuilds() < maxBuildJobs ? "1" : "0") + % drv.platform % drvPath % concatStringsSep(",", features)).str()); /* Read the first line of input, which should be a word indicating whether the hook wishes to perform the build. */ diff --git a/src/libutil/util.cc b/src/libutil/util.cc index 9540720fe2..2d26fc66da 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -966,6 +966,17 @@ Strings tokenizeString(const string & s, const string & separators) } +string concatStringsSep(const string & sep, const Strings & ss) +{ + string s; + foreach (Strings::const_iterator, i, ss) { + if (s.size() != 0) s += sep; + s += *i; + } + return s; +} + + string statusToString(int status) { if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { diff --git a/src/libutil/util.hh b/src/libutil/util.hh index c45ebc062f..27ad469049 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -280,6 +280,11 @@ MakeError(Interrupted, BaseError) Strings tokenizeString(const string & s, const string & separators = " \t\n\r"); +/* Concatenate the given strings with a separator between the + elements. */ +string concatStringsSep(const string & sep, const Strings & ss); + + /* Convert the exit status of a child as returned by wait() into an error string. */ string statusToString(int status);