Add support for the build-timeout' and
--timeout' options.
This commit is contained in:
parent
9c99aa2620
commit
5c9e9f732d
12 changed files with 115 additions and 5 deletions
|
@ -134,6 +134,23 @@ env-keep-derivations = false
|
||||||
|
|
||||||
</listitem>
|
</listitem>
|
||||||
|
|
||||||
|
<varlistentry xml:id="conf-build-timeout"><term><literal>build-timeout</literal></term>
|
||||||
|
|
||||||
|
<listitem>
|
||||||
|
|
||||||
|
<para>This option defines the maximum number of seconds that a
|
||||||
|
builder can run. This is useful (for instance in a automated
|
||||||
|
build system) to catch builds that are stuck in an infinite loop
|
||||||
|
but keep writing to their standard output or standard error. It
|
||||||
|
can be overriden using the <option
|
||||||
|
linkend="opt-timeout">--timeout</option> command line
|
||||||
|
switch.</para>
|
||||||
|
|
||||||
|
<para>The value <literal>0</literal> means that there is no
|
||||||
|
timeout. This is also the default.</para>
|
||||||
|
|
||||||
|
</listitem>
|
||||||
|
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,10 @@
|
||||||
<arg><option>--max-silent-time</option></arg>
|
<arg><option>--max-silent-time</option></arg>
|
||||||
<replaceable>number</replaceable>
|
<replaceable>number</replaceable>
|
||||||
</arg>
|
</arg>
|
||||||
|
<arg>
|
||||||
|
<arg><option>--timeout</option></arg>
|
||||||
|
<replaceable>number</replaceable>
|
||||||
|
</arg>
|
||||||
<arg><option>--keep-going</option></arg>
|
<arg><option>--keep-going</option></arg>
|
||||||
<arg><option>-k</option></arg>
|
<arg><option>-k</option></arg>
|
||||||
<arg><option>--keep-failed</option></arg>
|
<arg><option>--keep-failed</option></arg>
|
||||||
|
|
|
@ -132,6 +132,16 @@
|
||||||
|
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry xml:id="opt-timeout"><term><option>--timeout</option></term>
|
||||||
|
|
||||||
|
<listitem><para>Sets the maximum number of seconds that a builder
|
||||||
|
can run. The default is specified by the <link
|
||||||
|
linkend='conf-build-timeout'><literal>build-timeout</literal></link>
|
||||||
|
configuration setting. <literal>0</literal> means no
|
||||||
|
timeout.</para></listitem>
|
||||||
|
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry><term><option>--keep-going</option></term>
|
<varlistentry><term><option>--keep-going</option></term>
|
||||||
<term><option>-k</option></term>
|
<term><option>-k</option></term>
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,16 @@
|
||||||
option.</para>
|
option.</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
|
|
||||||
|
<listitem>
|
||||||
|
<para>The option <option>--timeout</option> (corresponding to the
|
||||||
|
configuration setting <literal>build-timeout</literal>) allows you
|
||||||
|
to set an absolute timeout on builds — if a build runs for more than
|
||||||
|
the given number of seconds, it is terminated. This is useful for
|
||||||
|
recovering automatically from builds that are stuck in an infinite
|
||||||
|
loop but keep producing output, and for which
|
||||||
|
<literal>--max-silent-time</literal> is ineffective.</para>
|
||||||
|
</listitem>
|
||||||
|
|
||||||
</itemizedlist>
|
</itemizedlist>
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -142,6 +142,7 @@ static void initAndRun(int argc, char * * argv)
|
||||||
maxBuildJobs = queryIntSetting("build-max-jobs", 1);
|
maxBuildJobs = queryIntSetting("build-max-jobs", 1);
|
||||||
buildCores = queryIntSetting("build-cores", 1);
|
buildCores = queryIntSetting("build-cores", 1);
|
||||||
maxSilentTime = queryIntSetting("build-max-silent-time", 0);
|
maxSilentTime = queryIntSetting("build-max-silent-time", 0);
|
||||||
|
buildTimeout = queryIntSetting("build-timeout", 0);
|
||||||
|
|
||||||
/* Catch SIGINT. */
|
/* Catch SIGINT. */
|
||||||
struct sigaction act;
|
struct sigaction act;
|
||||||
|
@ -237,6 +238,8 @@ static void initAndRun(int argc, char * * argv)
|
||||||
readOnlyMode = true;
|
readOnlyMode = true;
|
||||||
else if (arg == "--max-silent-time")
|
else if (arg == "--max-silent-time")
|
||||||
maxSilentTime = getIntArg<unsigned int>(arg, i, args.end());
|
maxSilentTime = getIntArg<unsigned int>(arg, i, args.end());
|
||||||
|
else if (arg == "--timeout")
|
||||||
|
buildTimeout = getIntArg<unsigned int>(arg, i, args.end());
|
||||||
else if (arg == "--no-build-hook")
|
else if (arg == "--no-build-hook")
|
||||||
useBuildHook = false;
|
useBuildHook = false;
|
||||||
else if (arg == "--show-trace")
|
else if (arg == "--show-trace")
|
||||||
|
|
|
@ -209,7 +209,10 @@ private:
|
||||||
|
|
||||||
/* Last time the goals in `waitingForAWhile' where woken up. */
|
/* Last time the goals in `waitingForAWhile' where woken up. */
|
||||||
time_t lastWokenUp;
|
time_t lastWokenUp;
|
||||||
|
|
||||||
|
/* Last time `waitForInput' was last called. */
|
||||||
|
time_t lastWait;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
bool cacheFailure;
|
bool cacheFailure;
|
||||||
|
@ -681,7 +684,8 @@ HookInstance::HookInstance()
|
||||||
builderOut.readSide.close();
|
builderOut.readSide.close();
|
||||||
if (dup2(builderOut.writeSide, 4) == -1)
|
if (dup2(builderOut.writeSide, 4) == -1)
|
||||||
throw SysError("dupping builder's stdout/stderr");
|
throw SysError("dupping builder's stdout/stderr");
|
||||||
|
|
||||||
|
/* XXX: Pass `buildTimeout' to the hook? */
|
||||||
execl(buildHook.c_str(), buildHook.c_str(), thisSystem.c_str(),
|
execl(buildHook.c_str(), buildHook.c_str(), thisSystem.c_str(),
|
||||||
(format("%1%") % maxSilentTime).str().c_str(),
|
(format("%1%") % maxSilentTime).str().c_str(),
|
||||||
(format("%1%") % printBuildTrace).str().c_str(),
|
(format("%1%") % printBuildTrace).str().c_str(),
|
||||||
|
@ -2666,7 +2670,14 @@ void Worker::waitForInput()
|
||||||
struct timeval timeout;
|
struct timeval timeout;
|
||||||
timeout.tv_usec = 0;
|
timeout.tv_usec = 0;
|
||||||
time_t before = time(0);
|
time_t before = time(0);
|
||||||
|
|
||||||
|
/* If a global timeout has been set, sleep until it's done. */
|
||||||
|
if (buildTimeout != 0) {
|
||||||
|
useTimeout = true;
|
||||||
|
if (lastWait == 0 || lastWait > before) lastWait = before;
|
||||||
|
timeout.tv_sec = std::max((time_t) 0, lastWait + buildTimeout - before);
|
||||||
|
}
|
||||||
|
|
||||||
/* If we're monitoring for silence on stdout/stderr, sleep until
|
/* If we're monitoring for silence on stdout/stderr, sleep until
|
||||||
the first deadline for any child. */
|
the first deadline for any child. */
|
||||||
if (maxSilentTime != 0) {
|
if (maxSilentTime != 0) {
|
||||||
|
@ -2678,8 +2689,11 @@ void Worker::waitForInput()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (oldest) {
|
if (oldest) {
|
||||||
|
time_t silenceTimeout = std::max((time_t) 0, oldest + maxSilentTime - before);
|
||||||
|
timeout.tv_sec = useTimeout
|
||||||
|
? std::min(silenceTimeout, timeout.tv_sec)
|
||||||
|
: silenceTimeout;
|
||||||
useTimeout = true;
|
useTimeout = true;
|
||||||
timeout.tv_sec = std::max((time_t) 0, oldest + maxSilentTime - before);
|
|
||||||
printMsg(lvlVomit, format("sleeping %1% seconds") % timeout.tv_sec);
|
printMsg(lvlVomit, format("sleeping %1% seconds") % timeout.tv_sec);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2717,6 +2731,9 @@ void Worker::waitForInput()
|
||||||
|
|
||||||
time_t after = time(0);
|
time_t after = time(0);
|
||||||
|
|
||||||
|
/* Keep track of when we were last called. */
|
||||||
|
lastWait = after;
|
||||||
|
|
||||||
/* Process all available file descriptors. */
|
/* Process all available file descriptors. */
|
||||||
|
|
||||||
/* Since goals may be canceled from inside the loop below (causing
|
/* Since goals may be canceled from inside the loop below (causing
|
||||||
|
@ -2765,6 +2782,15 @@ void Worker::waitForInput()
|
||||||
% goal->getName() % maxSilentTime);
|
% goal->getName() % maxSilentTime);
|
||||||
goal->cancel();
|
goal->cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (buildTimeout != 0 &&
|
||||||
|
after - before >= (time_t) buildTimeout)
|
||||||
|
{
|
||||||
|
printMsg(lvlError,
|
||||||
|
format("%1% timed out after %2% seconds of activity")
|
||||||
|
% goal->getName() % buildTimeout);
|
||||||
|
goal->cancel();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!waitingForAWhile.empty() && lastWokenUp + wakeUpInterval <= after) {
|
if (!waitingForAWhile.empty() && lastWokenUp + wakeUpInterval <= after) {
|
||||||
|
|
|
@ -26,6 +26,7 @@ unsigned int buildCores = 1;
|
||||||
bool readOnlyMode = false;
|
bool readOnlyMode = false;
|
||||||
string thisSystem = "unset";
|
string thisSystem = "unset";
|
||||||
time_t maxSilentTime = 0;
|
time_t maxSilentTime = 0;
|
||||||
|
time_t buildTimeout = 0;
|
||||||
Paths substituters;
|
Paths substituters;
|
||||||
bool useBuildHook = true;
|
bool useBuildHook = true;
|
||||||
bool printBuildTrace = false;
|
bool printBuildTrace = false;
|
||||||
|
|
|
@ -72,6 +72,10 @@ extern string thisSystem;
|
||||||
infinity. */
|
infinity. */
|
||||||
extern time_t maxSilentTime;
|
extern time_t maxSilentTime;
|
||||||
|
|
||||||
|
/* The maximum duration in seconds that a builder can run. 0 means
|
||||||
|
infinity. */
|
||||||
|
extern time_t buildTimeout;
|
||||||
|
|
||||||
/* The substituters. There are programs that can somehow realise a
|
/* The substituters. There are programs that can somehow realise a
|
||||||
store path without building, e.g., by downloading it or copying it
|
store path without building, e.g., by downloading it or copying it
|
||||||
from a CD. */
|
from a CD. */
|
||||||
|
|
|
@ -8,7 +8,7 @@ TESTS = init.sh hash.sh lang.sh add.sh simple.sh dependencies.sh \
|
||||||
referrers.sh user-envs.sh logging.sh nix-build.sh misc.sh fixed.sh \
|
referrers.sh user-envs.sh logging.sh nix-build.sh misc.sh fixed.sh \
|
||||||
gc-runtime.sh install-package.sh check-refs.sh filter-source.sh \
|
gc-runtime.sh install-package.sh check-refs.sh filter-source.sh \
|
||||||
remote-store.sh export.sh export-graph.sh negative-caching.sh \
|
remote-store.sh export.sh export-graph.sh negative-caching.sh \
|
||||||
binary-patching.sh
|
binary-patching.sh timeout.sh
|
||||||
|
|
||||||
XFAIL_TESTS =
|
XFAIL_TESTS =
|
||||||
|
|
||||||
|
@ -33,5 +33,6 @@ EXTRA_DIST = $(TESTS) \
|
||||||
export-graph.nix \
|
export-graph.nix \
|
||||||
negative-caching.nix \
|
negative-caching.nix \
|
||||||
binary-patching.nix \
|
binary-patching.nix \
|
||||||
|
timeout.nix timeout.builder.sh \
|
||||||
$(wildcard lang/*.nix) $(wildcard lang/*.exp) $(wildcard lang/*.exp.xml) $(wildcard lang/*.flags) \
|
$(wildcard lang/*.nix) $(wildcard lang/*.exp) $(wildcard lang/*.exp.xml) $(wildcard lang/*.flags) \
|
||||||
common.sh.in
|
common.sh.in
|
||||||
|
|
2
tests/timeout.builder.sh
Normal file
2
tests/timeout.builder.sh
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
echo "\`timeout' builder entering an infinite loop"
|
||||||
|
while true ; do : ; done
|
8
tests/timeout.nix
Normal file
8
tests/timeout.nix
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
with import ./config.nix;
|
||||||
|
|
||||||
|
mkDerivation {
|
||||||
|
name = "timeout";
|
||||||
|
builder = ./timeout.builder.sh;
|
||||||
|
PATH = "";
|
||||||
|
goodPath = path;
|
||||||
|
}
|
24
tests/timeout.sh
Normal file
24
tests/timeout.sh
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
# Test the `--timeout' option.
|
||||||
|
|
||||||
|
source common.sh
|
||||||
|
|
||||||
|
drvPath=$($nixinstantiate timeout.nix)
|
||||||
|
|
||||||
|
test "$($nixstore -q --binding system "$drvPath")" = "$system"
|
||||||
|
|
||||||
|
echo "derivation is $drvPath"
|
||||||
|
|
||||||
|
failed=0
|
||||||
|
messages="`$nixstore -r --timeout 2 $drvPath 2>&1 || failed=1`"
|
||||||
|
if test $failed -ne 0; then
|
||||||
|
echo "error: \`nix-store' succeeded; should have timed out" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! echo "$messages" | grep "timed out"; then
|
||||||
|
echo "error: \`nix-store' may have failed for reasons other than timeout" >&2
|
||||||
|
echo >&2
|
||||||
|
echo "output of \`nix-store' follows:" >&2
|
||||||
|
echo "$messages" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
Loading…
Reference in a new issue