aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/libexpr/lexer.l16
-rw-r--r--src/libstore/build.cc125
-rw-r--r--src/libstore/derivations.cc2
-rw-r--r--src/libstore/gc.cc5
-rw-r--r--src/libstore/globals.hh5
-rw-r--r--src/libstore/nar-accessor.cc3
-rw-r--r--src/libstore/pathlocks.cc87
-rw-r--r--src/libstore/s3-binary-cache-store.cc29
-rw-r--r--src/libstore/store-api.hh15
-rw-r--r--src/libutil/hash.cc10
-rw-r--r--src/libutil/logging.hh1
-rw-r--r--src/libutil/util.hh51
-rwxr-xr-xsrc/nix-build/nix-build.cc30
-rw-r--r--src/nix-store/nix-store.cc9
-rw-r--r--src/nix-store/serve-protocol.hh2
15 files changed, 234 insertions, 156 deletions
diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l
index 3ac7ce723..5b1ff0350 100644
--- a/src/libexpr/lexer.l
+++ b/src/libexpr/lexer.l
@@ -87,8 +87,8 @@ static Expr * unescapeStr(SymbolTable & symbols, const char * s)
ID [a-zA-Z\_][a-zA-Z0-9\_\'\-]*
INT [0-9]+
FLOAT (([1-9][0-9]*\.[0-9]*)|(0?\.[0-9]+))([Ee][+-]?[0-9]+)?
-PATH [a-zA-Z0-9\.\_\-\+]*(\/[a-zA-Z0-9\.\_\-\+]+)+
-HPATH \~(\/[a-zA-Z0-9\.\_\-\+]+)+
+PATH [a-zA-Z0-9\.\_\-\+]*(\/[a-zA-Z0-9\.\_\-\+]+)+\/?
+HPATH \~(\/[a-zA-Z0-9\.\_\-\+]+)+\/?
SPATH \<[a-zA-Z0-9\.\_\-\+]+(\/[a-zA-Z0-9\.\_\-\+]+)*\>
URI [a-zA-Z][a-zA-Z0-9\+\-\.]*\:[a-zA-Z0-9\%\/\?\:\@\&\=\+\$\,\-\_\.\!\~\*\']+
@@ -182,8 +182,16 @@ or { return OR_KW; }
<INITIAL,INSIDE_DOLLAR_CURLY>{
-{PATH} { yylval->path = strdup(yytext); return PATH; }
-{HPATH} { yylval->path = strdup(yytext); return HPATH; }
+{PATH} { if (yytext[yyleng-1] == '/')
+ throw ParseError("path ‘%s’ has a trailing slash", yytext);
+ yylval->path = strdup(yytext);
+ return PATH;
+ }
+{HPATH} { if (yytext[yyleng-1] == '/')
+ throw ParseError("path ‘%s’ has a trailing slash", yytext);
+ yylval->path = strdup(yytext);
+ return HPATH;
+ }
{SPATH} { yylval->path = strdup(yytext); return SPATH; }
{URI} { yylval->uri = strdup(yytext); return URI; }
diff --git a/src/libstore/build.cc b/src/libstore/build.cc
index ee266b7d3..ca56d3ad9 100644
--- a/src/libstore/build.cc
+++ b/src/libstore/build.cc
@@ -17,9 +17,9 @@
#include <sstream>
#include <thread>
#include <future>
+#include <chrono>
#include <limits.h>
-#include <time.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/types.h>
@@ -187,6 +187,9 @@ bool CompareGoalPtrs::operator() (const GoalPtr & a, const GoalPtr & b) {
}
+typedef std::chrono::time_point<std::chrono::steady_clock> steady_time_point;
+
+
/* A mapping used to remember for each child process to what goal it
belongs, and file descriptors for receiving log data and output
path creation commands. */
@@ -197,8 +200,8 @@ struct Child
set<int> fds;
bool respectTimeouts;
bool inBuildSlot;
- time_t lastOutput; /* time we last got output on stdout/stderr */
- time_t timeStarted;
+ steady_time_point lastOutput; /* time we last got output on stdout/stderr */
+ steady_time_point timeStarted;
};
@@ -238,7 +241,7 @@ private:
WeakGoals waitingForAWhile;
/* Last time the goals in `waitingForAWhile' where woken up. */
- time_t lastWokenUp;
+ steady_time_point lastWokenUp;
/* Cache for pathContentsGood(). */
std::map<Path, bool> pathContentsGoodCache;
@@ -1269,6 +1272,8 @@ void DerivationGoal::inputsRealised()
build hook. */
state = &DerivationGoal::tryToBuild;
worker.wakeUp(shared_from_this());
+
+ result = BuildResult();
}
@@ -1342,6 +1347,7 @@ void DerivationGoal::tryToBuild()
case rpAccept:
/* Yes, it has started doing so. Wait until we get
EOF from the hook. */
+ result.startTime = time(0); // inexact
state = &DerivationGoal::buildDone;
return;
case rpPostpone:
@@ -1418,6 +1424,9 @@ void DerivationGoal::buildDone()
debug(format("builder process for ‘%1%’ finished") % drvPath);
+ result.timesBuilt++;
+ result.stopTime = time(0);
+
/* So the child is gone now. */
worker.childTerminated(this);
@@ -2101,6 +2110,8 @@ void DerivationGoal::startBuilder()
/* Create a pipe to get the output of the builder. */
builderOut.create();
+ result.startTime = time(0);
+
/* Fork a child to build the package. */
#if __linux__
if (useChroot) {
@@ -2154,7 +2165,8 @@ void DerivationGoal::startBuilder()
namespace, we can't drop additional groups; they will
be mapped to nogroup in the child namespace. There does
not seem to be a workaround for this. (But who can tell
- from reading user_namespaces(7)?)*/
+ from reading user_namespaces(7)?)
+ See also https://lwn.net/Articles/621612/. */
if (getuid() == 0 && setgroups(0, 0) == -1)
throw SysError("setgroups failed");
@@ -2324,6 +2336,7 @@ void DerivationGoal::runChild()
ss.push_back("/etc/nsswitch.conf");
ss.push_back("/etc/services");
ss.push_back("/etc/hosts");
+ ss.push_back("/var/run/nscd/socket");
}
for (auto & i : ss) dirsInChroot[i] = i;
@@ -2672,7 +2685,9 @@ void DerivationGoal::registerOutputs()
outputs to allow hard links between outputs. */
InodesSeen inodesSeen;
- Path checkSuffix = "-check";
+ Path checkSuffix = ".check";
+ bool runDiffHook = settings.get("run-diff-hook", false);
+ bool keepPreviousRound = settings.keepFailed || runDiffHook;
/* Check whether the output paths were created, and grep each
output path to determine what other paths it references. Also make all
@@ -2902,30 +2917,42 @@ void DerivationGoal::registerOutputs()
assert(prevInfos.size() == infos.size());
for (auto i = prevInfos.begin(), j = infos.begin(); i != prevInfos.end(); ++i, ++j)
if (!(*i == *j)) {
+ result.isNonDeterministic = true;
Path prev = i->path + checkSuffix;
- if (pathExists(prev))
- throw NotDeterministic(
- format("output ‘%1%’ of ‘%2%’ differs from ‘%3%’ from previous round")
- % i->path % drvPath % prev);
- else
- throw NotDeterministic(
- format("output ‘%1%’ of ‘%2%’ differs from previous round")
- % i->path % drvPath);
+ bool prevExists = keepPreviousRound && pathExists(prev);
+ auto msg = prevExists
+ ? fmt("output ‘%1%’ of ‘%2%’ differs from ‘%3%’ from previous round", i->path, drvPath, prev)
+ : fmt("output ‘%1%’ of ‘%2%’ differs from previous round", i->path, drvPath);
+
+ auto diffHook = settings.get("diff-hook", std::string(""));
+ if (prevExists && diffHook != "" && runDiffHook) {
+ try {
+ auto diff = runProgram(diffHook, true, {prev, i->path});
+ if (diff != "")
+ printError(chomp(diff));
+ } catch (Error & error) {
+ printError("diff hook execution failed: %s", error.what());
+ }
+ }
+
+ if (settings.get("enforce-determinism", true))
+ throw NotDeterministic(msg);
+
+ printError(msg);
+ curRound = nrRounds; // we know enough, bail out early
}
- abort(); // shouldn't happen
}
- if (settings.keepFailed) {
+ /* If this is the first round of several, then move the output out
+ of the way. */
+ if (nrRounds > 1 && curRound == 1 && curRound < nrRounds && keepPreviousRound) {
for (auto & i : drv->outputs) {
Path prev = i.second.path + checkSuffix;
deletePath(prev);
- if (curRound < nrRounds) {
- Path dst = i.second.path + checkSuffix;
- if (rename(i.second.path.c_str(), dst.c_str()))
- throw SysError(format("renaming ‘%1%’ to ‘%2%’") % i.second.path % dst);
- }
+ Path dst = i.second.path + checkSuffix;
+ if (rename(i.second.path.c_str(), dst.c_str()))
+ throw SysError(format("renaming ‘%1%’ to ‘%2%’") % i.second.path % dst);
}
-
}
if (curRound < nrRounds) {
@@ -2933,6 +2960,15 @@ void DerivationGoal::registerOutputs()
return;
}
+ /* Remove the .check directories if we're done. FIXME: keep them
+ if the result was not determistic? */
+ if (curRound == nrRounds) {
+ for (auto & i : drv->outputs) {
+ Path prev = i.second.path + checkSuffix;
+ deletePath(prev);
+ }
+ }
+
/* Register each output path as valid, and register the sets of
paths referenced by each of them. If there are cycles in the
outputs, this will fail. */
@@ -3043,7 +3079,8 @@ void DerivationGoal::handleEOF(int fd)
void DerivationGoal::flushLine()
{
- if (settings.verboseBuild)
+ if (settings.verboseBuild &&
+ (settings.printRepeatedBuilds || curRound == 1))
printError(filterANSIEscapes(currentLogLine, true));
else {
logTail.push_back(currentLogLine);
@@ -3385,7 +3422,7 @@ Worker::Worker(LocalStore & store)
if (working) abort();
working = true;
nrLocalBuilds = 0;
- lastWokenUp = 0;
+ lastWokenUp = steady_time_point::min();
permanentFailure = false;
timedOut = false;
}
@@ -3494,7 +3531,7 @@ void Worker::childStarted(GoalPtr goal, const set<int> & fds,
child.goal = goal;
child.goal2 = goal.get();
child.fds = fds;
- child.timeStarted = child.lastOutput = time(0);
+ child.timeStarted = child.lastOutput = steady_time_point::clock::now();
child.inBuildSlot = inBuildSlot;
child.respectTimeouts = respectTimeouts;
children.emplace_back(child);
@@ -3613,35 +3650,38 @@ void Worker::waitForInput()
bool useTimeout = false;
struct timeval timeout;
timeout.tv_usec = 0;
- time_t before = time(0);
+ auto before = steady_time_point::clock::now();
/* If we're monitoring for silence on stdout/stderr, or if there
is a build timeout, then wait for input until the first
deadline for any child. */
- assert(sizeof(time_t) >= sizeof(long));
- time_t nearest = LONG_MAX; // nearest deadline
+ auto nearest = steady_time_point::max(); // nearest deadline
for (auto & i : children) {
if (!i.respectTimeouts) continue;
if (settings.maxSilentTime != 0)
- nearest = std::min(nearest, i.lastOutput + settings.maxSilentTime);
+ nearest = std::min(nearest, i.lastOutput + std::chrono::seconds(settings.maxSilentTime));
if (settings.buildTimeout != 0)
- nearest = std::min(nearest, i.timeStarted + settings.buildTimeout);
+ nearest = std::min(nearest, i.timeStarted + std::chrono::seconds(settings.buildTimeout));
}
- if (nearest != LONG_MAX) {
- timeout.tv_sec = std::max((time_t) 1, nearest - before);
+ if (nearest != steady_time_point::max()) {
+ timeout.tv_sec = std::max(1L, (long) std::chrono::duration_cast<std::chrono::seconds>(nearest - before).count());
useTimeout = true;
- printMsg(lvlVomit, format("sleeping %1% seconds") % timeout.tv_sec);
}
/* If we are polling goals that are waiting for a lock, then wake
up after a few seconds at most. */
if (!waitingForAWhile.empty()) {
useTimeout = true;
- if (lastWokenUp == 0)
+ if (lastWokenUp == steady_time_point::min())
printError("waiting for locks or build slots...");
- if (lastWokenUp == 0 || lastWokenUp > before) lastWokenUp = before;
- timeout.tv_sec = std::max((time_t) 1, (time_t) (lastWokenUp + settings.pollInterval - before));
- } else lastWokenUp = 0;
+ if (lastWokenUp == steady_time_point::min() || lastWokenUp > before) lastWokenUp = before;
+ timeout.tv_sec = std::max(1L,
+ (long) std::chrono::duration_cast<std::chrono::seconds>(
+ lastWokenUp + std::chrono::seconds(settings.pollInterval) - before).count());
+ } else lastWokenUp = steady_time_point::min();
+
+ if (useTimeout)
+ vomit("sleeping %d seconds", timeout.tv_sec);
/* Use select() to wait for the input side of any logger pipe to
become `available'. Note that `available' (i.e., non-blocking)
@@ -3661,7 +3701,7 @@ void Worker::waitForInput()
throw SysError("waiting for input");
}
- time_t after = time(0);
+ auto after = steady_time_point::clock::now();
/* Process all available file descriptors. */
decltype(children)::iterator i;
@@ -3699,7 +3739,7 @@ void Worker::waitForInput()
if (goal->getExitCode() == Goal::ecBusy &&
settings.maxSilentTime != 0 &&
j->respectTimeouts &&
- after - j->lastOutput >= (time_t) settings.maxSilentTime)
+ after - j->lastOutput >= std::chrono::seconds(settings.maxSilentTime))
{
printError(
format("%1% timed out after %2% seconds of silence")
@@ -3710,7 +3750,7 @@ void Worker::waitForInput()
else if (goal->getExitCode() == Goal::ecBusy &&
settings.buildTimeout != 0 &&
j->respectTimeouts &&
- after - j->timeStarted >= (time_t) settings.buildTimeout)
+ after - j->timeStarted >= std::chrono::seconds(settings.buildTimeout))
{
printError(
format("%1% timed out after %2% seconds")
@@ -3719,7 +3759,7 @@ void Worker::waitForInput()
}
}
- if (!waitingForAWhile.empty() && lastWokenUp + (time_t) settings.pollInterval <= after) {
+ if (!waitingForAWhile.empty() && lastWokenUp + std::chrono::seconds(settings.pollInterval) <= after) {
lastWokenUp = after;
for (auto & i : waitingForAWhile) {
GoalPtr goal = i.lock();
@@ -3781,12 +3821,13 @@ void LocalStore::buildPaths(const PathSet & drvPaths, BuildMode buildMode)
worker.run(goals);
PathSet failed;
- for (auto & i : goals)
+ for (auto & i : goals) {
if (i->getExitCode() != Goal::ecSuccess) {
DerivationGoal * i2 = dynamic_cast<DerivationGoal *>(i.get());
if (i2) failed.insert(i2->getDrvPath());
else failed.insert(dynamic_cast<SubstitutionGoal *>(i.get())->getStorePath());
}
+ }
if (!failed.empty())
throw Error(worker.exitStatus(), "build of %s failed", showPaths(failed));
diff --git a/src/libstore/derivations.cc b/src/libstore/derivations.cc
index 5562d4689..d934bda38 100644
--- a/src/libstore/derivations.cc
+++ b/src/libstore/derivations.cc
@@ -155,7 +155,7 @@ static StringSet parseStrings(std::istream & str, bool arePaths)
static Derivation parseDerivation(const string & s)
{
Derivation drv;
- istringstream_nocopy str(s);
+ std::istringstream str(s);
expect(str, "Derive([");
/* Parse the list of outputs. */
diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc
index ae03604fa..f8c4a0723 100644
--- a/src/libstore/gc.cc
+++ b/src/libstore/gc.cc
@@ -621,6 +621,11 @@ void LocalStore::tryToDelete(GCState & state, const Path & path)
/* Don't delete .chroot directories for derivations that are
currently being built. */
if (isActiveTempFile(state, path, ".chroot")) return;
+
+ /* Don't delete .check directories for derivations that are
+ currently being built, because we may need to run
+ diff-hook. */
+ if (isActiveTempFile(state, path, ".check")) return;
}
PathSet visited;
diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh
index 3194193bc..a423b4e5c 100644
--- a/src/libstore/globals.hh
+++ b/src/libstore/globals.hh
@@ -149,6 +149,11 @@ struct Settings {
before being killed (0 means no limit). */
unsigned long maxLogSize;
+ /* When build-repeat > 0 and verboseBuild == true, whether to
+ print repeated builds (i.e. builds other than the first one) to
+ stderr. Hack to prevent Hydra logs from being polluted. */
+ bool printRepeatedBuilds = true;
+
/* How often (in seconds) to poll for locks. */
unsigned int pollInterval;
diff --git a/src/libstore/nar-accessor.cc b/src/libstore/nar-accessor.cc
index ded19c05d..4cb5de744 100644
--- a/src/libstore/nar-accessor.cc
+++ b/src/libstore/nar-accessor.cc
@@ -52,8 +52,9 @@ struct NarIndexer : ParseSink, StringSource
void preallocateContents(unsigned long long size) override
{
currentStart = string(s, pos, 16);
+ assert(size <= std::numeric_limits<size_t>::max());
members.emplace(currentPath,
- NarMember{FSAccessor::Type::tRegular, isExec, pos, size});
+ NarMember{FSAccessor::Type::tRegular, isExec, pos, (size_t) size});
}
void receiveContents(unsigned char * data, unsigned int len) override
diff --git a/src/libstore/pathlocks.cc b/src/libstore/pathlocks.cc
index 8788ee164..fecd63687 100644
--- a/src/libstore/pathlocks.cc
+++ b/src/libstore/pathlocks.cc
@@ -1,5 +1,6 @@
#include "pathlocks.hh"
#include "util.hh"
+#include "sync.hh"
#include <cerrno>
#include <cstdlib>
@@ -72,7 +73,7 @@ bool lockFile(int fd, LockType lockType, bool wait)
close a descriptor, the previous lock will be closed as well. And
there is no way to query whether we already have a lock (F_GETLK
only works on locks held by other processes). */
-static StringSet lockedPaths; /* !!! not thread-safe */
+static Sync<StringSet> lockedPaths_;
PathLocks::PathLocks()
@@ -108,49 +109,60 @@ bool PathLocks::lockPaths(const PathSet & _paths,
debug(format("locking path ‘%1%’") % path);
- if (lockedPaths.find(lockPath) != lockedPaths.end())
- throw Error("deadlock: trying to re-acquire self-held lock");
+ {
+ auto lockedPaths(lockedPaths_.lock());
+ if (lockedPaths->count(lockPath))
+ throw Error("deadlock: trying to re-acquire self-held lock ‘%s’", lockPath);
+ lockedPaths->insert(lockPath);
+ }
+
+ try {
- AutoCloseFD fd;
+ AutoCloseFD fd;
- while (1) {
+ while (1) {
- /* Open/create the lock file. */
- fd = openLockFile(lockPath, true);
+ /* Open/create the lock file. */
+ fd = openLockFile(lockPath, true);
- /* Acquire an exclusive lock. */
- if (!lockFile(fd.get(), ltWrite, false)) {
- if (wait) {
- if (waitMsg != "") printError(waitMsg);
- lockFile(fd.get(), ltWrite, true);
- } else {
- /* Failed to lock this path; release all other
- locks. */
- unlock();
- return false;
+ /* Acquire an exclusive lock. */
+ if (!lockFile(fd.get(), ltWrite, false)) {
+ if (wait) {
+ if (waitMsg != "") printError(waitMsg);
+ lockFile(fd.get(), ltWrite, true);
+ } else {
+ /* Failed to lock this path; release all other
+ locks. */
+ unlock();
+ return false;
+ }
}
+
+ debug(format("lock acquired on ‘%1%’") % lockPath);
+
+ /* Check that the lock file hasn't become stale (i.e.,
+ hasn't been unlinked). */
+ struct stat st;
+ if (fstat(fd.get(), &st) == -1)
+ throw SysError(format("statting lock file ‘%1%’") % lockPath);
+ if (st.st_size != 0)
+ /* This lock file has been unlinked, so we're holding
+ a lock on a deleted file. This means that other
+ processes may create and acquire a lock on
+ `lockPath', and proceed. So we must retry. */
+ debug(format("open lock file ‘%1%’ has become stale") % lockPath);
+ else
+ break;
}
- debug(format("lock acquired on ‘%1%’") % lockPath);
-
- /* Check that the lock file hasn't become stale (i.e.,
- hasn't been unlinked). */
- struct stat st;
- if (fstat(fd.get(), &st) == -1)
- throw SysError(format("statting lock file ‘%1%’") % lockPath);
- if (st.st_size != 0)
- /* This lock file has been unlinked, so we're holding
- a lock on a deleted file. This means that other
- processes may create and acquire a lock on
- `lockPath', and proceed. So we must retry. */
- debug(format("open lock file ‘%1%’ has become stale") % lockPath);
- else
- break;
+ /* Use borrow so that the descriptor isn't closed. */
+ fds.push_back(FDPair(fd.release(), lockPath));
+
+ } catch (...) {
+ lockedPaths_.lock()->erase(lockPath);
+ throw;
}
- /* Use borrow so that the descriptor isn't closed. */
- fds.push_back(FDPair(fd.release(), lockPath));
- lockedPaths.insert(lockPath);
}
return true;
@@ -172,7 +184,8 @@ void PathLocks::unlock()
for (auto & i : fds) {
if (deletePaths) deleteLockFile(i.second, i.first);
- lockedPaths.erase(i.second);
+ lockedPaths_.lock()->erase(i.second);
+
if (close(i.first) == -1)
printError(
format("error (ignored): cannot close lock file on ‘%1%’") % i.second);
@@ -193,7 +206,7 @@ void PathLocks::setDeletion(bool deletePaths)
bool pathIsLockedByMe(const Path & path)
{
Path lockPath = path + ".lock";
- return lockedPaths.find(lockPath) != lockedPaths.end();
+ return lockedPaths_.lock()->count(lockPath);
}
diff --git a/src/libstore/s3-binary-cache-store.cc b/src/libstore/s3-binary-cache-store.cc
index 1bc8576a8..ccb71f1ee 100644
--- a/src/libstore/s3-binary-cache-store.cc
+++ b/src/libstore/s3-binary-cache-store.cc
@@ -1,23 +1,34 @@
#include "config.h"
#if ENABLE_S3
+#if __linux__
#include "s3-binary-cache-store.hh"
#include "nar-info.hh"
#include "nar-info-disk-cache.hh"
#include "globals.hh"
+#include <aws/core/Aws.h>
#include <aws/core/client/ClientConfiguration.h>
#include <aws/s3/S3Client.h>
#include <aws/s3/model/CreateBucketRequest.h>
#include <aws/s3/model/GetBucketLocationRequest.h>
#include <aws/s3/model/GetObjectRequest.h>
#include <aws/s3/model/HeadObjectRequest.h>
-#include <aws/s3/model/PutObjectRequest.h>
#include <aws/s3/model/ListObjectsRequest.h>
+#include <aws/s3/model/PutObjectRequest.h>
namespace nix {
+struct istringstream_nocopy : public std::stringstream
+{
+ istringstream_nocopy(const std::string & s)
+ {
+ rdbuf()->pubsetbuf(
+ (char *) s.data(), s.size());
+ }
+};
+
struct S3Error : public Error
{
Aws::S3::S3Errors err;
@@ -37,6 +48,20 @@ R && checkAws(const FormatOrString & fs, Aws::Utils::Outcome<R, E> && outcome)
return outcome.GetResultWithOwnership();
}
+static void initAWS()
+{
+ static std::once_flag flag;
+ std::call_once(flag, []() {
+ Aws::SDKOptions options;
+
+ /* We install our own OpenSSL locking function (see
+ shared.cc), so don't let aws-sdk-cpp override it. */
+ options.cryptoOptions.initAndCleanupOpenSSL = false;
+
+ Aws::InitAPI(options);
+ });
+}
+
struct S3BinaryCacheStoreImpl : public S3BinaryCacheStore
{
std::string bucketName;
@@ -63,6 +88,7 @@ struct S3BinaryCacheStoreImpl : public S3BinaryCacheStore
ref<Aws::Client::ClientConfiguration> makeConfig()
{
+ initAWS();
auto res = make_ref<Aws::Client::ClientConfiguration>();
res->region = Aws::Region::US_EAST_1; // FIXME: make configurable
res->requestTimeoutMs = 600 * 1000;
@@ -260,3 +286,4 @@ static RegisterStoreImplementation regStore([](
}
#endif
+#endif
diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh
index 2ea74d90e..789526cc2 100644
--- a/src/libstore/store-api.hh
+++ b/src/libstore/store-api.hh
@@ -208,7 +208,20 @@ struct BuildResult
NotDeterministic,
} status = MiscFailure;
std::string errorMsg;
- //time_t startTime = 0, stopTime = 0;
+
+ /* How many times this build was performed. */
+ unsigned int timesBuilt = 0;
+
+ /* If timesBuilt > 1, whether some builds did not produce the same
+ result. (Note that 'isNonDeterministic = false' does not mean
+ the build is deterministic, just that we don't have evidence of
+ non-determinism.) */
+ bool isNonDeterministic = false;
+
+ /* The start/stop times of the build (or one of the rounds, if it
+ was repeated). */
+ time_t startTime = 0, stopTime = 0;
+
bool success() {
return status == Built || status == Substituted || status == AlreadyValid;
}
diff --git a/src/libutil/hash.cc b/src/libutil/hash.cc
index 49e781980..aa50fceb9 100644
--- a/src/libutil/hash.cc
+++ b/src/libutil/hash.cc
@@ -106,7 +106,7 @@ Hash parseHash(HashType ht, const string & s)
string s2(s, i * 2, 2);
if (!isxdigit(s2[0]) || !isxdigit(s2[1]))
throw BadHash(format("invalid hash ‘%1%’") % s);
- istringstream_nocopy str(s2);
+ std::istringstream str(s2);
int n;
str >> std::hex >> n;
hash.hash[i] = n;
@@ -165,7 +165,13 @@ Hash parseHash32(HashType ht, const string & s)
unsigned int i = b / 8;
unsigned int j = b % 8;
hash.hash[i] |= digit << j;
- if (i < hash.hashSize - 1) hash.hash[i + 1] |= digit >> (8 - j);
+
+ if (i < hash.hashSize - 1) {
+ hash.hash[i + 1] |= digit >> (8 - j);
+ } else {
+ if (digit >> (8 - j))
+ throw BadHash(format("invalid base-32 hash ‘%1%’") % s);
+ }
}
return hash;
diff --git a/src/libutil/logging.hh b/src/libutil/logging.hh
index ba99a81c3..3e6c4b548 100644
--- a/src/libutil/logging.hh
+++ b/src/libutil/logging.hh
@@ -79,6 +79,7 @@ extern Verbosity verbosity; /* suppress msgs > this */
#define printError(args...) printMsg(lvlError, args)
#define printInfo(args...) printMsg(lvlInfo, args)
#define debug(args...) printMsg(lvlDebug, args)
+#define vomit(args...) printMsg(lvlVomit, args)
void warnOnce(bool & haveWarned, const FormatOrString & fs);
diff --git a/src/libutil/util.hh b/src/libutil/util.hh
index 1ede48a65..50b96f7ed 100644
--- a/src/libutil/util.hh
+++ b/src/libutil/util.hh
@@ -431,55 +431,4 @@ void callSuccess(
}
-/* A variant of std::istringstream that doesn't copy its string
- argument. This is useful for large strings. The caller must ensure
- that the string object is not destroyed while it's referenced by
- this object. */
-class istringbuf_nocopy : public std::streambuf
-{
- const std::string & s;
- decltype(s.size()) off;
- decltype(s.size()) size;
-public:
- istringbuf_nocopy(const std::string & s) : s{s}, off{0}, size{s.size()}
- {
- }
-
-private:
- int_type underflow()
- {
- if (off == size)
- return traits_type::eof();
- return traits_type::to_int_type(s[off]);
- }
-
- int_type uflow()
- {
- if (off == size)
- return traits_type::eof();
- return traits_type::to_int_type(s[off++]);
- }
-
- int_type pbackfail(int_type ch)
- {
- if (off == 0 || (ch != traits_type::eof() && ch != s[off - 1]))
- return traits_type::eof();
-
- return traits_type::to_int_type(s[--off]);
- }
-
- std::streamsize showmanyc()
- {
- return size - off;
- }
-};
-
-
-struct istringstream_nocopy : public std::iostream
-{
- istringbuf_nocopy buf;
- istringstream_nocopy(const std::string & s) : std::iostream(&buf), buf(s) {};
-};
-
-
}
diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc
index 08c679357..71ef5af86 100755
--- a/src/nix-build/nix-build.cc
+++ b/src/nix-build/nix-build.cc
@@ -81,7 +81,8 @@ int main(int argc, char ** argv)
auto pure = false;
auto fromArgs = false;
auto packages = false;
- auto interactive = true;
+ // Same condition as bash uses for interactive shells
+ auto interactive = isatty(STDIN_FILENO) && isatty(STDERR_FILENO);
Strings instArgs;
Strings buildArgs;
@@ -105,6 +106,7 @@ int main(int argc, char ** argv)
std::vector<string> args;
for (int i = 1; i < argc; ++i)
args.push_back(argv[i]);
+
// Heuristic to see if we're invoked as a shebang script, namely, if we
// have a single argument, it's the name of an executable file, and it
// starts with "#!".
@@ -115,9 +117,9 @@ int main(int argc, char ** argv)
if (std::regex_search(lines.front(), std::regex("^#!"))) {
lines.pop_front();
inShebang = true;
- for (int i = 2; i < argc - 1; ++i)
+ for (int i = 2; i < argc; ++i)
savedArgs.push_back(argv[i]);
- std::vector<string> args;
+ args.clear();
for (auto line : lines) {
line = chomp(line);
std::smatch match;
@@ -276,6 +278,7 @@ int main(int argc, char ** argv)
if (n >= args.size()) {
throw UsageError(format("%1% requires an argument") % arg);
}
+ interactive = false;
auto interpreter = args[n];
auto execArgs = "";
@@ -287,9 +290,8 @@ int main(int argc, char ** argv)
// executes it unless it contains the string "perl" or "indir",
// or (undocumented) argv[0] does not contain "perl". Exploit
// the latter by doing "exec -a".
- if (std::regex_search(interpreter, std::regex("perl"))) {
- execArgs = "-a PERL";
- }
+ if (std::regex_search(interpreter, std::regex("perl")))
+ execArgs = "-a PERL";
std::ostringstream joined;
for (const auto & i : savedArgs)
@@ -300,7 +302,6 @@ int main(int argc, char ** argv)
// read the shebang to understand which packages to read from. Since
// this is handled via nix-shell -p, we wrap our ruby script execution
// in ruby -e 'load' which ignores the shebangs.
-
envCommand = (format("exec %1% %2% -e 'load(\"%3%\") -- %4%") % execArgs % interpreter % script % joined.str()).str();
} else {
envCommand = (format("exec %1% %2% %3% %4%") % execArgs % interpreter % script % joined.str()).str();
@@ -420,7 +421,7 @@ int main(int argc, char ** argv)
// environment variables and shell functions. Also don't lose
// the current $PATH directories.
auto rcfile = (Path) tmpDir + "/rc";
- writeFile(rcfile, (format(
+ writeFile(rcfile, fmt(
"rm -rf '%1%'; "
"[ -n \"$PS1\" ] && [ -e ~/.bashrc ] && source ~/.bashrc; "
"%2%"
@@ -434,13 +435,12 @@ int main(int argc, char ** argv)
"unset NIX_INDENT_MAKE; "
"shopt -u nullglob; "
"unset TZ; %4%"
- "%5%"
- )
- % (Path) tmpDir
- % (pure ? "" : "p=$PATH; ")
- % (pure ? "" : "PATH=$PATH:$p; unset p; ")
- % (getenv("TZ") ? (string("export TZ='") + getenv("TZ") + "'; ") : "")
- % envCommand).str());
+ "%5%",
+ (Path) tmpDir,
+ (pure ? "" : "p=$PATH; "),
+ (pure ? "" : "PATH=$PATH:$p; unset p; "),
+ (getenv("TZ") ? (string("export TZ='") + getenv("TZ") + "'; ") : ""),
+ envCommand));
Strings envStrs;
for (auto & i : env)
diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc
index 63e20a8c7..c1e6afef0 100644
--- a/src/nix-store/nix-store.cc
+++ b/src/nix-store/nix-store.cc
@@ -840,6 +840,12 @@ static void opServe(Strings opFlags, Strings opArgs)
settings.buildTimeout = readInt(in);
if (GET_PROTOCOL_MINOR(clientVersion) >= 2)
settings.maxLogSize = readInt(in);
+ if (GET_PROTOCOL_MINOR(clientVersion) >= 3) {
+ settings.set("build-repeat", std::to_string(readInt(in)));
+ settings.set("enforce-determinism", readInt(in) != 0 ? "true" : "false");
+ settings.set("run-diff-hook", "true");
+ }
+ settings.printRepeatedBuilds = false;
};
while (true) {
@@ -955,6 +961,9 @@ static void opServe(Strings opFlags, Strings opArgs)
out << status.status << status.errorMsg;
+ if (GET_PROTOCOL_MINOR(clientVersion) >= 3)
+ out << status.timesBuilt << status.isNonDeterministic << status.startTime << status.stopTime;
+
break;
}
diff --git a/src/nix-store/serve-protocol.hh b/src/nix-store/serve-protocol.hh
index c4e2a3703..f8cc9a4b6 100644
--- a/src/nix-store/serve-protocol.hh
+++ b/src/nix-store/serve-protocol.hh
@@ -5,7 +5,7 @@ namespace nix {
#define SERVE_MAGIC_1 0x390c9deb
#define SERVE_MAGIC_2 0x5452eecb
-#define SERVE_PROTOCOL_VERSION 0x202
+#define SERVE_PROTOCOL_VERSION 0x203
#define GET_PROTOCOL_MAJOR(x) ((x) & 0xff00)
#define GET_PROTOCOL_MINOR(x) ((x) & 0x00ff)