2018-12-11 02:57:29 +00:00
|
|
|
"""Code for parallel-building a set of targets, if needed."""
|
Use mkstemp() to create the stdout temp file, and simplify $3 path.
Previously, we'd try to put the stdout temp file in the same dir as the
target, if that dir exists. Otherwise we'd walk up the directory tree
looking for a good place. But this would go wrong if the directory we
chose got *deleted* during the run of the .do file.
Instead, we switch to an entirely new design: we use mkstemp() to
generate a temp file in the standard temp file location (probably
/tmp), then open it and immediately delete it, so the .do file can't
cause any unexpected behaviour. After the .do file exits, we use our
still-open fd to the stdout file to read the content back out.
In the old implementation, we also put the $3 in the "adjusted"
location that depended whether the target dir already existed, just for
consistency. But that was never necessary: we didn't create the $3
file, and if the .do script wants to write to $3, it should create the
target dir first anyway. So change it to *always* use a $3 temp
filename in the target dir, which is much simpler and so has fewer edge
cases.
Add t/202-del/deltest4 with some tests for all these edge cases.
Reported-by: Jeff Stearns <jeff.stearns@gmail.com>
2018-12-12 03:37:44 +00:00
|
|
|
import errno, os, stat, signal, sys, tempfile, time
|
2019-01-18 00:06:18 +00:00
|
|
|
from . import cycles, env, helpers, jobserver, logs, paths, state
|
2018-12-11 00:55:05 +00:00
|
|
|
from .logs import debug2, err, warn, meta
|
2010-11-19 07:21:09 -08:00
|
|
|
|
|
|
|
|
|
2010-11-21 03:34:32 -08:00
|
|
|
def _nice(t):
|
2018-12-05 01:07:16 -05:00
|
|
|
return state.relpath(t, env.v.STARTDIR)
|
2010-11-21 03:34:32 -08:00
|
|
|
|
|
|
|
|
|
2010-11-22 04:40:54 -08:00
|
|
|
def _try_stat(filename):
|
|
|
|
|
try:
|
2018-10-06 00:14:02 -04:00
|
|
|
return os.lstat(filename)
|
2019-10-27 14:19:50 +01:00
|
|
|
except OSError as e:
|
2010-11-22 04:40:54 -08:00
|
|
|
if e.errno == errno.ENOENT:
|
|
|
|
|
return None
|
|
|
|
|
else:
|
|
|
|
|
raise
|
|
|
|
|
|
|
|
|
|
|
redo-log: capture and linearize the output of redo builds.
redo now saves the stderr from every .do script, for every target, into
a file in the .redo directory. That means you can look up the logs
from the most recent build of any target using the new redo-log
command, for example:
redo-log -r all
The default is to show logs non-recursively, that is, it'll show when a
target does redo-ifchange on another target, but it won't recurse into
the logs for the latter target. With -r (recursive), it does. With -u
(unchanged), it does even if redo-ifchange discovered that the target
was already up-to-date; in that case, it prints the logs of the *most
recent* time the target was generated.
With --no-details, redo-log will show only the 'redo' lines, not the
other log messages. For very noisy build systems (like recursing into
a 'make' instance) this can be helpful to get an overview of what
happened, without all the cruft.
You can use the -f (follow) option like tail -f, to follow a build
that's currently in progress until it finishes. redo itself spins up a
copy of redo-log -r -f while it runs, so you can see what's going on.
Still broken in this version:
- No man page or new tests yet.
- ANSI colors don't yet work (unless you use --raw-logs, which gives
the old-style behaviour).
- You can't redirect the output of a sub-redo to a file or a
pipe right now, because redo-log is eating it.
- The regex for matching 'redo' lines in the log is very gross.
Instead, we should put the raw log files in a more machine-parseable
format, and redo-log should turn that into human-readable format.
- redo-log tries to "linearize" the logs, which makes them
comprehensible even for a large parallel build. It recursively shows
log messages for each target in depth-first tree order (by tracing
into a new target every time it sees a 'redo' line). This works
really well, but in some specific cases, the "topmost" redo instance
can get stuck waiting for a jwack token, which makes it look like the
whole build has stalled, when really redo-log is just waiting a long
time for a particular subprocess to be able to continue. We'll need to
add a specific workaround for that.
2018-11-03 22:09:18 -04:00
|
|
|
log_reader_pid = None
|
2019-05-13 23:08:41 +00:00
|
|
|
stderr_fd = None
|
redo-log: capture and linearize the output of redo builds.
redo now saves the stderr from every .do script, for every target, into
a file in the .redo directory. That means you can look up the logs
from the most recent build of any target using the new redo-log
command, for example:
redo-log -r all
The default is to show logs non-recursively, that is, it'll show when a
target does redo-ifchange on another target, but it won't recurse into
the logs for the latter target. With -r (recursive), it does. With -u
(unchanged), it does even if redo-ifchange discovered that the target
was already up-to-date; in that case, it prints the logs of the *most
recent* time the target was generated.
With --no-details, redo-log will show only the 'redo' lines, not the
other log messages. For very noisy build systems (like recursing into
a 'make' instance) this can be helpful to get an overview of what
happened, without all the cruft.
You can use the -f (follow) option like tail -f, to follow a build
that's currently in progress until it finishes. redo itself spins up a
copy of redo-log -r -f while it runs, so you can see what's going on.
Still broken in this version:
- No man page or new tests yet.
- ANSI colors don't yet work (unless you use --raw-logs, which gives
the old-style behaviour).
- You can't redirect the output of a sub-redo to a file or a
pipe right now, because redo-log is eating it.
- The regex for matching 'redo' lines in the log is very gross.
Instead, we should put the raw log files in a more machine-parseable
format, and redo-log should turn that into human-readable format.
- redo-log tries to "linearize" the logs, which makes them
comprehensible even for a large parallel build. It recursively shows
log messages for each target in depth-first tree order (by tracing
into a new target every time it sees a 'redo' line). This works
really well, but in some specific cases, the "topmost" redo instance
can get stuck waiting for a jwack token, which makes it look like the
whole build has stalled, when really redo-log is just waiting a long
time for a particular subprocess to be able to continue. We'll need to
add a specific workaround for that.
2018-11-03 22:09:18 -04:00
|
|
|
|
|
|
|
|
|
2018-12-02 22:53:00 -05:00
|
|
|
def close_stdin():
|
|
|
|
|
f = open('/dev/null')
|
|
|
|
|
os.dup2(f.fileno(), 0)
|
|
|
|
|
f.close()
|
|
|
|
|
|
|
|
|
|
|
2018-11-19 11:22:53 -05:00
|
|
|
def start_stdin_log_reader(status, details, pretty, color,
|
|
|
|
|
debug_locks, debug_pids):
|
2018-12-11 02:57:29 +00:00
|
|
|
"""Redirect stderr to a redo-log instance with the given options.
|
|
|
|
|
|
|
|
|
|
Then we automatically run logs.setup() to send the right data format
|
|
|
|
|
to that redo-log instance.
|
|
|
|
|
|
|
|
|
|
After this, be sure to run await_log_reader() before exiting.
|
|
|
|
|
"""
|
redo-log: capture and linearize the output of redo builds.
redo now saves the stderr from every .do script, for every target, into
a file in the .redo directory. That means you can look up the logs
from the most recent build of any target using the new redo-log
command, for example:
redo-log -r all
The default is to show logs non-recursively, that is, it'll show when a
target does redo-ifchange on another target, but it won't recurse into
the logs for the latter target. With -r (recursive), it does. With -u
(unchanged), it does even if redo-ifchange discovered that the target
was already up-to-date; in that case, it prints the logs of the *most
recent* time the target was generated.
With --no-details, redo-log will show only the 'redo' lines, not the
other log messages. For very noisy build systems (like recursing into
a 'make' instance) this can be helpful to get an overview of what
happened, without all the cruft.
You can use the -f (follow) option like tail -f, to follow a build
that's currently in progress until it finishes. redo itself spins up a
copy of redo-log -r -f while it runs, so you can see what's going on.
Still broken in this version:
- No man page or new tests yet.
- ANSI colors don't yet work (unless you use --raw-logs, which gives
the old-style behaviour).
- You can't redirect the output of a sub-redo to a file or a
pipe right now, because redo-log is eating it.
- The regex for matching 'redo' lines in the log is very gross.
Instead, we should put the raw log files in a more machine-parseable
format, and redo-log should turn that into human-readable format.
- redo-log tries to "linearize" the logs, which makes them
comprehensible even for a large parallel build. It recursively shows
log messages for each target in depth-first tree order (by tracing
into a new target every time it sees a 'redo' line). This works
really well, but in some specific cases, the "topmost" redo instance
can get stuck waiting for a jwack token, which makes it look like the
whole build has stalled, when really redo-log is just waiting a long
time for a particular subprocess to be able to continue. We'll need to
add a specific workaround for that.
2018-11-03 22:09:18 -04:00
|
|
|
global log_reader_pid
|
2019-02-09 19:17:34 +01:00
|
|
|
global stderr_fd
|
redo-log: capture and linearize the output of redo builds.
redo now saves the stderr from every .do script, for every target, into
a file in the .redo directory. That means you can look up the logs
from the most recent build of any target using the new redo-log
command, for example:
redo-log -r all
The default is to show logs non-recursively, that is, it'll show when a
target does redo-ifchange on another target, but it won't recurse into
the logs for the latter target. With -r (recursive), it does. With -u
(unchanged), it does even if redo-ifchange discovered that the target
was already up-to-date; in that case, it prints the logs of the *most
recent* time the target was generated.
With --no-details, redo-log will show only the 'redo' lines, not the
other log messages. For very noisy build systems (like recursing into
a 'make' instance) this can be helpful to get an overview of what
happened, without all the cruft.
You can use the -f (follow) option like tail -f, to follow a build
that's currently in progress until it finishes. redo itself spins up a
copy of redo-log -r -f while it runs, so you can see what's going on.
Still broken in this version:
- No man page or new tests yet.
- ANSI colors don't yet work (unless you use --raw-logs, which gives
the old-style behaviour).
- You can't redirect the output of a sub-redo to a file or a
pipe right now, because redo-log is eating it.
- The regex for matching 'redo' lines in the log is very gross.
Instead, we should put the raw log files in a more machine-parseable
format, and redo-log should turn that into human-readable format.
- redo-log tries to "linearize" the logs, which makes them
comprehensible even for a large parallel build. It recursively shows
log messages for each target in depth-first tree order (by tracing
into a new target every time it sees a 'redo' line). This works
really well, but in some specific cases, the "topmost" redo instance
can get stuck waiting for a jwack token, which makes it look like the
whole build has stalled, when really redo-log is just waiting a long
time for a particular subprocess to be able to continue. We'll need to
add a specific workaround for that.
2018-11-03 22:09:18 -04:00
|
|
|
r, w = os.pipe() # main pipe to redo-log
|
|
|
|
|
ar, aw = os.pipe() # ack pipe from redo-log --ack-fd
|
|
|
|
|
sys.stdout.flush()
|
|
|
|
|
sys.stderr.flush()
|
|
|
|
|
pid = os.fork()
|
|
|
|
|
if pid:
|
|
|
|
|
# parent
|
|
|
|
|
log_reader_pid = pid
|
2019-05-13 23:08:41 +00:00
|
|
|
stderr_fd = os.dup(2) # save for after the log pipe gets closed
|
redo-log: capture and linearize the output of redo builds.
redo now saves the stderr from every .do script, for every target, into
a file in the .redo directory. That means you can look up the logs
from the most recent build of any target using the new redo-log
command, for example:
redo-log -r all
The default is to show logs non-recursively, that is, it'll show when a
target does redo-ifchange on another target, but it won't recurse into
the logs for the latter target. With -r (recursive), it does. With -u
(unchanged), it does even if redo-ifchange discovered that the target
was already up-to-date; in that case, it prints the logs of the *most
recent* time the target was generated.
With --no-details, redo-log will show only the 'redo' lines, not the
other log messages. For very noisy build systems (like recursing into
a 'make' instance) this can be helpful to get an overview of what
happened, without all the cruft.
You can use the -f (follow) option like tail -f, to follow a build
that's currently in progress until it finishes. redo itself spins up a
copy of redo-log -r -f while it runs, so you can see what's going on.
Still broken in this version:
- No man page or new tests yet.
- ANSI colors don't yet work (unless you use --raw-logs, which gives
the old-style behaviour).
- You can't redirect the output of a sub-redo to a file or a
pipe right now, because redo-log is eating it.
- The regex for matching 'redo' lines in the log is very gross.
Instead, we should put the raw log files in a more machine-parseable
format, and redo-log should turn that into human-readable format.
- redo-log tries to "linearize" the logs, which makes them
comprehensible even for a large parallel build. It recursively shows
log messages for each target in depth-first tree order (by tracing
into a new target every time it sees a 'redo' line). This works
really well, but in some specific cases, the "topmost" redo instance
can get stuck waiting for a jwack token, which makes it look like the
whole build has stalled, when really redo-log is just waiting a long
time for a particular subprocess to be able to continue. We'll need to
add a specific workaround for that.
2018-11-03 22:09:18 -04:00
|
|
|
os.close(r)
|
|
|
|
|
os.close(aw)
|
|
|
|
|
b = os.read(ar, 8)
|
|
|
|
|
if not b:
|
|
|
|
|
# subprocess died without sending us anything: that's bad.
|
|
|
|
|
err('failed to start redo-log subprocess; cannot continue.\n')
|
|
|
|
|
os._exit(99)
|
|
|
|
|
assert b == 'REDO-OK\n'
|
|
|
|
|
# now we know the subproc is running and will report our errors
|
|
|
|
|
# to stderr, so it's okay to lose our own stderr.
|
|
|
|
|
os.close(ar)
|
|
|
|
|
os.dup2(w, 1)
|
|
|
|
|
os.dup2(w, 2)
|
|
|
|
|
os.close(w)
|
Workaround for completely broken file locking on Windows 10 WSL.
WSL (Windows Services for Linux) provides a Linux-kernel-compatible ABI
for userspace processes, but the current version doesn't not implement
fcntl() locks at all; it just always returns success. See
https://github.com/Microsoft/WSL/issues/1927.
This causes us three kinds of problem:
1. sqlite3 in WAL mode gives "OperationalError: locking protocol".
1b. Other sqlite3 journal modes also don't work when used by
multiple processes.
2. redo parallelism doesn't work, because we can't prevent the same
target from being build several times simultaneously.
3. "redo-log -f" doesn't work, since it can't tell whether the log
file it's tailing is "done" or not.
To fix #1, we switch the sqlite3 journal back to PERSIST instead of
WAL. We originally changed to WAL in commit 5156feae9d to reduce
deadlocks on MacOS. That was never adequately explained, but PERSIST
still acts weird on MacOS, so we'll only switch to PERSIST when we
detect that locking is definitely broken. Sigh.
To (mostly) fix #2, we disable any -j value > 1 when locking is broken.
This prevents basic forms of parallelism, but doesn't stop you from
re-entrantly starting other instances of redo. To fix that properly,
we need to switch to a different locking mechanism entirely, which is
tough in python. flock() locks probably work, for example, but
python's locks lie and just use fcntl locks for those.
To fix #3, we always force --no-log mode when we find that locking is
broken.
2019-01-02 14:18:51 -05:00
|
|
|
logs.setup(tty=sys.stderr, parent_logs=True, pretty=False, color=False)
|
redo-log: capture and linearize the output of redo builds.
redo now saves the stderr from every .do script, for every target, into
a file in the .redo directory. That means you can look up the logs
from the most recent build of any target using the new redo-log
command, for example:
redo-log -r all
The default is to show logs non-recursively, that is, it'll show when a
target does redo-ifchange on another target, but it won't recurse into
the logs for the latter target. With -r (recursive), it does. With -u
(unchanged), it does even if redo-ifchange discovered that the target
was already up-to-date; in that case, it prints the logs of the *most
recent* time the target was generated.
With --no-details, redo-log will show only the 'redo' lines, not the
other log messages. For very noisy build systems (like recursing into
a 'make' instance) this can be helpful to get an overview of what
happened, without all the cruft.
You can use the -f (follow) option like tail -f, to follow a build
that's currently in progress until it finishes. redo itself spins up a
copy of redo-log -r -f while it runs, so you can see what's going on.
Still broken in this version:
- No man page or new tests yet.
- ANSI colors don't yet work (unless you use --raw-logs, which gives
the old-style behaviour).
- You can't redirect the output of a sub-redo to a file or a
pipe right now, because redo-log is eating it.
- The regex for matching 'redo' lines in the log is very gross.
Instead, we should put the raw log files in a more machine-parseable
format, and redo-log should turn that into human-readable format.
- redo-log tries to "linearize" the logs, which makes them
comprehensible even for a large parallel build. It recursively shows
log messages for each target in depth-first tree order (by tracing
into a new target every time it sees a 'redo' line). This works
really well, but in some specific cases, the "topmost" redo instance
can get stuck waiting for a jwack token, which makes it look like the
whole build has stalled, when really redo-log is just waiting a long
time for a particular subprocess to be able to continue. We'll need to
add a specific workaround for that.
2018-11-03 22:09:18 -04:00
|
|
|
else:
|
|
|
|
|
# child
|
|
|
|
|
try:
|
|
|
|
|
os.close(ar)
|
|
|
|
|
os.close(w)
|
|
|
|
|
os.dup2(r, 0)
|
|
|
|
|
os.close(r)
|
redo-log: fix stdout vs stderr; don't recapture if .do script redirects stderr.
redo-log should log to stdout, because when you ask for the specific
logs from a run, the logs are the output you requested. redo-log's
stderr should be about any errors retrieving that output.
On the other hand, when you run redo, the logs are literally the stderr
of the build steps, which are incidental to the main job (building
things). So that should be send to stderr. Previously, we were
sending to stderr when --no-log, but stdout when --log, which is
totally wrong.
Also, adding redo-log had the unexpected result that if a .do script
redirected the stderr of a sub-redo or redo-ifchange to a file or pipe,
the output would be eaten by redo-log instead of the intended output.
So a test runner like this:
self.test:
redo self.runtest 2>&1 | grep ERROR
would not work; the self.runtest output would be sent to redo's log
buffer (and from there, probably printed to the toplevel redo's stderr)
rather than passed along to grep.
2018-11-19 16:27:41 -05:00
|
|
|
# redo-log sends to stdout (because if you ask for logs, that's
|
|
|
|
|
# the output you wanted!). But redo itself sends logs to stderr
|
|
|
|
|
# (because they're incidental to the thing you asked for).
|
|
|
|
|
# To make these semantics work, we point redo-log's stdout at
|
|
|
|
|
# our stderr when we launch it.
|
|
|
|
|
os.dup2(2, 1)
|
redo-log: capture and linearize the output of redo builds.
redo now saves the stderr from every .do script, for every target, into
a file in the .redo directory. That means you can look up the logs
from the most recent build of any target using the new redo-log
command, for example:
redo-log -r all
The default is to show logs non-recursively, that is, it'll show when a
target does redo-ifchange on another target, but it won't recurse into
the logs for the latter target. With -r (recursive), it does. With -u
(unchanged), it does even if redo-ifchange discovered that the target
was already up-to-date; in that case, it prints the logs of the *most
recent* time the target was generated.
With --no-details, redo-log will show only the 'redo' lines, not the
other log messages. For very noisy build systems (like recursing into
a 'make' instance) this can be helpful to get an overview of what
happened, without all the cruft.
You can use the -f (follow) option like tail -f, to follow a build
that's currently in progress until it finishes. redo itself spins up a
copy of redo-log -r -f while it runs, so you can see what's going on.
Still broken in this version:
- No man page or new tests yet.
- ANSI colors don't yet work (unless you use --raw-logs, which gives
the old-style behaviour).
- You can't redirect the output of a sub-redo to a file or a
pipe right now, because redo-log is eating it.
- The regex for matching 'redo' lines in the log is very gross.
Instead, we should put the raw log files in a more machine-parseable
format, and redo-log should turn that into human-readable format.
- redo-log tries to "linearize" the logs, which makes them
comprehensible even for a large parallel build. It recursively shows
log messages for each target in depth-first tree order (by tracing
into a new target every time it sees a 'redo' line). This works
really well, but in some specific cases, the "topmost" redo instance
can get stuck waiting for a jwack token, which makes it look like the
whole build has stalled, when really redo-log is just waiting a long
time for a particular subprocess to be able to continue. We'll need to
add a specific workaround for that.
2018-11-03 22:09:18 -04:00
|
|
|
argv = [
|
|
|
|
|
'redo-log',
|
|
|
|
|
'--recursive', '--follow',
|
|
|
|
|
'--ack-fd', str(aw),
|
Raw logs contain @@REDO lines instead of formatted data.
This makes them more reliable to parse. redo-log can parse each line,
format and print it, then recurse if necessary. This got a little ugly
because I wanted 'redo --raw-logs' to work, which we want to format the
output nicely, but not call redo-log.
(As a result, --raw-logs has a different meaning to redo and
redo-log, which is kinda dumb. I should fix that.)
As an added bonus, redo-log now handles indenting of recursive logs, so
if the build was a -> a/b -> a/b/c, and you look at the log for a/b, it
can still start at the top level indentation.
2018-11-13 04:05:42 -05:00
|
|
|
('--status' if status and os.isatty(2) else '--no-status'),
|
redo-log: capture and linearize the output of redo builds.
redo now saves the stderr from every .do script, for every target, into
a file in the .redo directory. That means you can look up the logs
from the most recent build of any target using the new redo-log
command, for example:
redo-log -r all
The default is to show logs non-recursively, that is, it'll show when a
target does redo-ifchange on another target, but it won't recurse into
the logs for the latter target. With -r (recursive), it does. With -u
(unchanged), it does even if redo-ifchange discovered that the target
was already up-to-date; in that case, it prints the logs of the *most
recent* time the target was generated.
With --no-details, redo-log will show only the 'redo' lines, not the
other log messages. For very noisy build systems (like recursing into
a 'make' instance) this can be helpful to get an overview of what
happened, without all the cruft.
You can use the -f (follow) option like tail -f, to follow a build
that's currently in progress until it finishes. redo itself spins up a
copy of redo-log -r -f while it runs, so you can see what's going on.
Still broken in this version:
- No man page or new tests yet.
- ANSI colors don't yet work (unless you use --raw-logs, which gives
the old-style behaviour).
- You can't redirect the output of a sub-redo to a file or a
pipe right now, because redo-log is eating it.
- The regex for matching 'redo' lines in the log is very gross.
Instead, we should put the raw log files in a more machine-parseable
format, and redo-log should turn that into human-readable format.
- redo-log tries to "linearize" the logs, which makes them
comprehensible even for a large parallel build. It recursively shows
log messages for each target in depth-first tree order (by tracing
into a new target every time it sees a 'redo' line). This works
really well, but in some specific cases, the "topmost" redo instance
can get stuck waiting for a jwack token, which makes it look like the
whole build has stalled, when really redo-log is just waiting a long
time for a particular subprocess to be able to continue. We'll need to
add a specific workaround for that.
2018-11-03 22:09:18 -04:00
|
|
|
('--details' if details else '--no-details'),
|
2018-11-19 10:55:56 -05:00
|
|
|
('--pretty' if pretty else '--no-pretty'),
|
redo-log: prioritize the "foreground" process.
When running a parallel build, redo-log -f (which is auto-started by
redo) tries to traverse through the logs depth first, in the order
parent processes started subprocesses. This works pretty well, but if
its dependencies are locked, a process might have to give up its
jobserver token while other stuff builds its dependencies. After the
dependency finishes, the parent might not be able to get a token for
quite some time, and the logs will appear to stop.
To prevent this from happening, we can instantiate up to one "cheater"
token, only in the foreground process (the one locked by redo-log -f),
which will allow it to continue running, albeit a bit slowly (since it
only has one token out of possibly many). When the process finishes,
we then destroy the fake token. It gets a little complicated; see
explanation at the top of jwack.py.
2018-11-17 04:32:09 -05:00
|
|
|
('--debug-locks' if debug_locks else '--no-debug-locks'),
|
|
|
|
|
('--debug-pids' if debug_pids else '--no-debug-pids'),
|
redo-log: capture and linearize the output of redo builds.
redo now saves the stderr from every .do script, for every target, into
a file in the .redo directory. That means you can look up the logs
from the most recent build of any target using the new redo-log
command, for example:
redo-log -r all
The default is to show logs non-recursively, that is, it'll show when a
target does redo-ifchange on another target, but it won't recurse into
the logs for the latter target. With -r (recursive), it does. With -u
(unchanged), it does even if redo-ifchange discovered that the target
was already up-to-date; in that case, it prints the logs of the *most
recent* time the target was generated.
With --no-details, redo-log will show only the 'redo' lines, not the
other log messages. For very noisy build systems (like recursing into
a 'make' instance) this can be helpful to get an overview of what
happened, without all the cruft.
You can use the -f (follow) option like tail -f, to follow a build
that's currently in progress until it finishes. redo itself spins up a
copy of redo-log -r -f while it runs, so you can see what's going on.
Still broken in this version:
- No man page or new tests yet.
- ANSI colors don't yet work (unless you use --raw-logs, which gives
the old-style behaviour).
- You can't redirect the output of a sub-redo to a file or a
pipe right now, because redo-log is eating it.
- The regex for matching 'redo' lines in the log is very gross.
Instead, we should put the raw log files in a more machine-parseable
format, and redo-log should turn that into human-readable format.
- redo-log tries to "linearize" the logs, which makes them
comprehensible even for a large parallel build. It recursively shows
log messages for each target in depth-first tree order (by tracing
into a new target every time it sees a 'redo' line). This works
really well, but in some specific cases, the "topmost" redo instance
can get stuck waiting for a jwack token, which makes it look like the
whole build has stalled, when really redo-log is just waiting a long
time for a particular subprocess to be able to continue. We'll need to
add a specific workaround for that.
2018-11-03 22:09:18 -04:00
|
|
|
]
|
2018-11-19 11:22:53 -05:00
|
|
|
if color != 1:
|
|
|
|
|
argv.append('--color' if color >= 2 else '--no-color')
|
|
|
|
|
argv.append('-')
|
redo-log: capture and linearize the output of redo builds.
redo now saves the stderr from every .do script, for every target, into
a file in the .redo directory. That means you can look up the logs
from the most recent build of any target using the new redo-log
command, for example:
redo-log -r all
The default is to show logs non-recursively, that is, it'll show when a
target does redo-ifchange on another target, but it won't recurse into
the logs for the latter target. With -r (recursive), it does. With -u
(unchanged), it does even if redo-ifchange discovered that the target
was already up-to-date; in that case, it prints the logs of the *most
recent* time the target was generated.
With --no-details, redo-log will show only the 'redo' lines, not the
other log messages. For very noisy build systems (like recursing into
a 'make' instance) this can be helpful to get an overview of what
happened, without all the cruft.
You can use the -f (follow) option like tail -f, to follow a build
that's currently in progress until it finishes. redo itself spins up a
copy of redo-log -r -f while it runs, so you can see what's going on.
Still broken in this version:
- No man page or new tests yet.
- ANSI colors don't yet work (unless you use --raw-logs, which gives
the old-style behaviour).
- You can't redirect the output of a sub-redo to a file or a
pipe right now, because redo-log is eating it.
- The regex for matching 'redo' lines in the log is very gross.
Instead, we should put the raw log files in a more machine-parseable
format, and redo-log should turn that into human-readable format.
- redo-log tries to "linearize" the logs, which makes them
comprehensible even for a large parallel build. It recursively shows
log messages for each target in depth-first tree order (by tracing
into a new target every time it sees a 'redo' line). This works
really well, but in some specific cases, the "topmost" redo instance
can get stuck waiting for a jwack token, which makes it look like the
whole build has stalled, when really redo-log is just waiting a long
time for a particular subprocess to be able to continue. We'll need to
add a specific workaround for that.
2018-11-03 22:09:18 -04:00
|
|
|
os.execvp(argv[0], argv)
|
2019-10-27 14:19:50 +01:00
|
|
|
except Exception as e: # pylint: disable=broad-except
|
redo-log: capture and linearize the output of redo builds.
redo now saves the stderr from every .do script, for every target, into
a file in the .redo directory. That means you can look up the logs
from the most recent build of any target using the new redo-log
command, for example:
redo-log -r all
The default is to show logs non-recursively, that is, it'll show when a
target does redo-ifchange on another target, but it won't recurse into
the logs for the latter target. With -r (recursive), it does. With -u
(unchanged), it does even if redo-ifchange discovered that the target
was already up-to-date; in that case, it prints the logs of the *most
recent* time the target was generated.
With --no-details, redo-log will show only the 'redo' lines, not the
other log messages. For very noisy build systems (like recursing into
a 'make' instance) this can be helpful to get an overview of what
happened, without all the cruft.
You can use the -f (follow) option like tail -f, to follow a build
that's currently in progress until it finishes. redo itself spins up a
copy of redo-log -r -f while it runs, so you can see what's going on.
Still broken in this version:
- No man page or new tests yet.
- ANSI colors don't yet work (unless you use --raw-logs, which gives
the old-style behaviour).
- You can't redirect the output of a sub-redo to a file or a
pipe right now, because redo-log is eating it.
- The regex for matching 'redo' lines in the log is very gross.
Instead, we should put the raw log files in a more machine-parseable
format, and redo-log should turn that into human-readable format.
- redo-log tries to "linearize" the logs, which makes them
comprehensible even for a large parallel build. It recursively shows
log messages for each target in depth-first tree order (by tracing
into a new target every time it sees a 'redo' line). This works
really well, but in some specific cases, the "topmost" redo instance
can get stuck waiting for a jwack token, which makes it look like the
whole build has stalled, when really redo-log is just waiting a long
time for a particular subprocess to be able to continue. We'll need to
add a specific workaround for that.
2018-11-03 22:09:18 -04:00
|
|
|
sys.stderr.write('redo-log: exec: %s\n' % e)
|
|
|
|
|
finally:
|
|
|
|
|
os._exit(99)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def await_log_reader():
|
2018-12-11 02:57:29 +00:00
|
|
|
"""Await the redo-log instance we redirected stderr to, if any."""
|
2018-12-05 01:07:16 -05:00
|
|
|
if not env.v.LOG:
|
2018-12-02 23:15:37 -05:00
|
|
|
return
|
redo-log: capture and linearize the output of redo builds.
redo now saves the stderr from every .do script, for every target, into
a file in the .redo directory. That means you can look up the logs
from the most recent build of any target using the new redo-log
command, for example:
redo-log -r all
The default is to show logs non-recursively, that is, it'll show when a
target does redo-ifchange on another target, but it won't recurse into
the logs for the latter target. With -r (recursive), it does. With -u
(unchanged), it does even if redo-ifchange discovered that the target
was already up-to-date; in that case, it prints the logs of the *most
recent* time the target was generated.
With --no-details, redo-log will show only the 'redo' lines, not the
other log messages. For very noisy build systems (like recursing into
a 'make' instance) this can be helpful to get an overview of what
happened, without all the cruft.
You can use the -f (follow) option like tail -f, to follow a build
that's currently in progress until it finishes. redo itself spins up a
copy of redo-log -r -f while it runs, so you can see what's going on.
Still broken in this version:
- No man page or new tests yet.
- ANSI colors don't yet work (unless you use --raw-logs, which gives
the old-style behaviour).
- You can't redirect the output of a sub-redo to a file or a
pipe right now, because redo-log is eating it.
- The regex for matching 'redo' lines in the log is very gross.
Instead, we should put the raw log files in a more machine-parseable
format, and redo-log should turn that into human-readable format.
- redo-log tries to "linearize" the logs, which makes them
comprehensible even for a large parallel build. It recursively shows
log messages for each target in depth-first tree order (by tracing
into a new target every time it sees a 'redo' line). This works
really well, but in some specific cases, the "topmost" redo instance
can get stuck waiting for a jwack token, which makes it look like the
whole build has stalled, when really redo-log is just waiting a long
time for a particular subprocess to be able to continue. We'll need to
add a specific workaround for that.
2018-11-03 22:09:18 -04:00
|
|
|
if log_reader_pid > 0:
|
|
|
|
|
# never actually close fd#1 or fd#2; insanity awaits.
|
|
|
|
|
# replace it with something else instead.
|
|
|
|
|
# Since our stdout/stderr are attached to redo-log's stdin,
|
|
|
|
|
# this will notify redo-log that it's time to die (after it finishes
|
|
|
|
|
# reading the logs)
|
2019-02-09 19:17:34 +01:00
|
|
|
os.dup2(stderr_fd, 1)
|
|
|
|
|
os.dup2(stderr_fd, 2)
|
redo-log: capture and linearize the output of redo builds.
redo now saves the stderr from every .do script, for every target, into
a file in the .redo directory. That means you can look up the logs
from the most recent build of any target using the new redo-log
command, for example:
redo-log -r all
The default is to show logs non-recursively, that is, it'll show when a
target does redo-ifchange on another target, but it won't recurse into
the logs for the latter target. With -r (recursive), it does. With -u
(unchanged), it does even if redo-ifchange discovered that the target
was already up-to-date; in that case, it prints the logs of the *most
recent* time the target was generated.
With --no-details, redo-log will show only the 'redo' lines, not the
other log messages. For very noisy build systems (like recursing into
a 'make' instance) this can be helpful to get an overview of what
happened, without all the cruft.
You can use the -f (follow) option like tail -f, to follow a build
that's currently in progress until it finishes. redo itself spins up a
copy of redo-log -r -f while it runs, so you can see what's going on.
Still broken in this version:
- No man page or new tests yet.
- ANSI colors don't yet work (unless you use --raw-logs, which gives
the old-style behaviour).
- You can't redirect the output of a sub-redo to a file or a
pipe right now, because redo-log is eating it.
- The regex for matching 'redo' lines in the log is very gross.
Instead, we should put the raw log files in a more machine-parseable
format, and redo-log should turn that into human-readable format.
- redo-log tries to "linearize" the logs, which makes them
comprehensible even for a large parallel build. It recursively shows
log messages for each target in depth-first tree order (by tracing
into a new target every time it sees a 'redo' line). This works
really well, but in some specific cases, the "topmost" redo instance
can get stuck waiting for a jwack token, which makes it look like the
whole build has stalled, when really redo-log is just waiting a long
time for a particular subprocess to be able to continue. We'll need to
add a specific workaround for that.
2018-11-03 22:09:18 -04:00
|
|
|
os.waitpid(log_reader_pid, 0)
|
|
|
|
|
|
|
|
|
|
|
2018-12-11 02:57:29 +00:00
|
|
|
class _BuildJob(object):
|
2010-12-10 02:58:13 -08:00
|
|
|
def __init__(self, t, sf, lock, shouldbuildfunc, donefunc):
|
2018-12-05 01:07:16 -05:00
|
|
|
self.t = t # original target name, not relative to env.v.BASE
|
2010-12-10 02:58:13 -08:00
|
|
|
self.sf = sf
|
Use mkstemp() to create the stdout temp file, and simplify $3 path.
Previously, we'd try to put the stdout temp file in the same dir as the
target, if that dir exists. Otherwise we'd walk up the directory tree
looking for a good place. But this would go wrong if the directory we
chose got *deleted* during the run of the .do file.
Instead, we switch to an entirely new design: we use mkstemp() to
generate a temp file in the standard temp file location (probably
/tmp), then open it and immediately delete it, so the .do file can't
cause any unexpected behaviour. After the .do file exits, we use our
still-open fd to the stdout file to read the content back out.
In the old implementation, we also put the $3 in the "adjusted"
location that depended whether the target dir already existed, just for
consistency. But that was never necessary: we didn't create the $3
file, and if the .do script wants to write to $3, it should create the
target dir first anyway. So change it to *always* use a $3 temp
filename in the target dir, which is much simpler and so has fewer edge
cases.
Add t/202-del/deltest4 with some tests for all these edge cases.
Reported-by: Jeff Stearns <jeff.stearns@gmail.com>
2018-12-12 03:37:44 +00:00
|
|
|
self.tmpname = None
|
2010-11-21 23:33:11 -08:00
|
|
|
self.lock = lock
|
|
|
|
|
self.shouldbuildfunc = shouldbuildfunc
|
|
|
|
|
self.donefunc = donefunc
|
2010-11-22 04:40:54 -08:00
|
|
|
self.before_t = _try_stat(self.t)
|
2010-11-21 23:33:11 -08:00
|
|
|
|
2018-12-11 02:57:29 +00:00
|
|
|
# attributes of the running process
|
Use mkstemp() to create the stdout temp file, and simplify $3 path.
Previously, we'd try to put the stdout temp file in the same dir as the
target, if that dir exists. Otherwise we'd walk up the directory tree
looking for a good place. But this would go wrong if the directory we
chose got *deleted* during the run of the .do file.
Instead, we switch to an entirely new design: we use mkstemp() to
generate a temp file in the standard temp file location (probably
/tmp), then open it and immediately delete it, so the .do file can't
cause any unexpected behaviour. After the .do file exits, we use our
still-open fd to the stdout file to read the content back out.
In the old implementation, we also put the $3 in the "adjusted"
location that depended whether the target dir already existed, just for
consistency. But that was never necessary: we didn't create the $3
file, and if the .do script wants to write to $3, it should create the
target dir first anyway. So change it to *always* use a $3 temp
filename in the target dir, which is much simpler and so has fewer edge
cases.
Add t/202-del/deltest4 with some tests for all these edge cases.
Reported-by: Jeff Stearns <jeff.stearns@gmail.com>
2018-12-12 03:37:44 +00:00
|
|
|
self.outfile = None
|
2018-12-11 02:57:29 +00:00
|
|
|
|
2010-11-21 23:33:11 -08:00
|
|
|
def start(self):
|
2018-12-11 02:57:29 +00:00
|
|
|
"""Actually start running this job in a subproc, if needed."""
|
2018-12-02 23:15:37 -05:00
|
|
|
assert self.lock.owned
|
2010-12-10 20:53:31 -08:00
|
|
|
try:
|
2018-10-30 23:03:46 -04:00
|
|
|
try:
|
redo-log: capture and linearize the output of redo builds.
redo now saves the stderr from every .do script, for every target, into
a file in the .redo directory. That means you can look up the logs
from the most recent build of any target using the new redo-log
command, for example:
redo-log -r all
The default is to show logs non-recursively, that is, it'll show when a
target does redo-ifchange on another target, but it won't recurse into
the logs for the latter target. With -r (recursive), it does. With -u
(unchanged), it does even if redo-ifchange discovered that the target
was already up-to-date; in that case, it prints the logs of the *most
recent* time the target was generated.
With --no-details, redo-log will show only the 'redo' lines, not the
other log messages. For very noisy build systems (like recursing into
a 'make' instance) this can be helpful to get an overview of what
happened, without all the cruft.
You can use the -f (follow) option like tail -f, to follow a build
that's currently in progress until it finishes. redo itself spins up a
copy of redo-log -r -f while it runs, so you can see what's going on.
Still broken in this version:
- No man page or new tests yet.
- ANSI colors don't yet work (unless you use --raw-logs, which gives
the old-style behaviour).
- You can't redirect the output of a sub-redo to a file or a
pipe right now, because redo-log is eating it.
- The regex for matching 'redo' lines in the log is very gross.
Instead, we should put the raw log files in a more machine-parseable
format, and redo-log should turn that into human-readable format.
- redo-log tries to "linearize" the logs, which makes them
comprehensible even for a large parallel build. It recursively shows
log messages for each target in depth-first tree order (by tracing
into a new target every time it sees a 'redo' line). This works
really well, but in some specific cases, the "topmost" redo instance
can get stuck waiting for a jwack token, which makes it look like the
whole build has stalled, when really redo-log is just waiting a long
time for a particular subprocess to be able to continue. We'll need to
add a specific workaround for that.
2018-11-03 22:09:18 -04:00
|
|
|
is_target, dirty = self.shouldbuildfunc(self.t)
|
2018-12-05 00:18:07 -05:00
|
|
|
except cycles.CyclicDependencyError:
|
2018-10-30 23:03:46 -04:00
|
|
|
err('cyclic dependency while checking %s\n' % _nice(self.t))
|
2019-01-18 00:06:18 +00:00
|
|
|
raise helpers.ImmediateReturn(208)
|
The second half of redo-stamp: out-of-order building.
If a depends on b depends on c, and c is dirty but b uses redo-stamp
checksums, then 'redo-ifchange a' is indeterminate: we won't know if we need
to run a.do unless we first build b, but the script that *normally* runs
'redo-ifchange b' is a.do, and we don't want to run that yet, because we
don't know for sure if b is dirty, and we shouldn't build a unless one of
its dependencies is dirty. Eek!
Luckily, there's a safe solution. If we *know* a is dirty - eg. because
a.do or one of its children has definitely changed - then we can just run
a.do immediately and there's no problem, even if b is indeterminate, because
we were going to run a.do anyhow.
If a's dependencies are *not* definitely dirty, and all we have is
indeterminate ones like b, then that means a's build process *hasn't
changed*, which means its tree of dependencies still includes b, which means
we can deduce that if we *did* run a.do, it would end up running b.do.
Since we know that anyhow, we can safely just run b.do, which will either
b.set_checked() or b.set_changed(). Once that's done, we can re-parse a's
dependencies and this time conclusively tell if it needs to be redone or
not. Even if it does, b is already up-to-date, so the 'redo-ifchange b'
line in a.do will be fast.
...now take all the above and do it recursively to handle nested
dependencies, etc, and you're done.
2010-12-11 04:40:05 -08:00
|
|
|
if not dirty:
|
2010-12-10 20:53:31 -08:00
|
|
|
# target doesn't need to be built; skip the whole task
|
Raw logs contain @@REDO lines instead of formatted data.
This makes them more reliable to parse. redo-log can parse each line,
format and print it, then recurse if necessary. This got a little ugly
because I wanted 'redo --raw-logs' to work, which we want to format the
output nicely, but not call redo-log.
(As a result, --raw-logs has a different meaning to redo and
redo-log, which is kinda dumb. I should fix that.)
As an added bonus, redo-log now handles indenting of recursive logs, so
if the build was a -> a/b -> a/b/c, and you look at the log for a/b, it
can still start at the top level indentation.
2018-11-13 04:05:42 -05:00
|
|
|
if is_target:
|
redo-log: add automated tests, and fix some path bugs revealed by them.
When a log for X was saying it wanted to refer to Y, we used a relative
path, but it was sometimes relative to the wrong starting location, so
redo-log couldn't find it later.
Two examples:
- if default.o.do is handling builds for a/b/x.o, and default.o.do
does 'redo a/b/x.h', the log for x.o should refer to ./x.h, not
a/b/x.h.
- if foo.do is handling builds for foo, and it does
"cd a/b && redo x", the log for foo should refer to a/b/x, not just
x.
2018-11-19 17:09:40 -05:00
|
|
|
meta('unchanged', state.target_relpath(self.t))
|
2018-12-11 02:57:29 +00:00
|
|
|
return self._finalize(0)
|
2019-10-27 14:19:50 +01:00
|
|
|
except helpers.ImmediateReturn as e:
|
2018-12-11 02:57:29 +00:00
|
|
|
return self._finalize(e.rv)
|
The second half of redo-stamp: out-of-order building.
If a depends on b depends on c, and c is dirty but b uses redo-stamp
checksums, then 'redo-ifchange a' is indeterminate: we won't know if we need
to run a.do unless we first build b, but the script that *normally* runs
'redo-ifchange b' is a.do, and we don't want to run that yet, because we
don't know for sure if b is dirty, and we shouldn't build a unless one of
its dependencies is dirty. Eek!
Luckily, there's a safe solution. If we *know* a is dirty - eg. because
a.do or one of its children has definitely changed - then we can just run
a.do immediately and there's no problem, even if b is indeterminate, because
we were going to run a.do anyhow.
If a's dependencies are *not* definitely dirty, and all we have is
indeterminate ones like b, then that means a's build process *hasn't
changed*, which means its tree of dependencies still includes b, which means
we can deduce that if we *did* run a.do, it would end up running b.do.
Since we know that anyhow, we can safely just run b.do, which will either
b.set_checked() or b.set_changed(). Once that's done, we can re-parse a's
dependencies and this time conclusively tell if it needs to be redone or
not. Even if it does, b is already up-to-date, so the 'redo-ifchange b'
line in a.do will be fast.
...now take all the above and do it recursively to handle nested
dependencies, etc, and you're done.
2010-12-11 04:40:05 -08:00
|
|
|
|
2018-12-05 01:07:16 -05:00
|
|
|
if env.v.NO_OOB or dirty == True: # pylint: disable=singleton-comparison
|
2018-12-11 02:57:29 +00:00
|
|
|
self._start_self()
|
The second half of redo-stamp: out-of-order building.
If a depends on b depends on c, and c is dirty but b uses redo-stamp
checksums, then 'redo-ifchange a' is indeterminate: we won't know if we need
to run a.do unless we first build b, but the script that *normally* runs
'redo-ifchange b' is a.do, and we don't want to run that yet, because we
don't know for sure if b is dirty, and we shouldn't build a unless one of
its dependencies is dirty. Eek!
Luckily, there's a safe solution. If we *know* a is dirty - eg. because
a.do or one of its children has definitely changed - then we can just run
a.do immediately and there's no problem, even if b is indeterminate, because
we were going to run a.do anyhow.
If a's dependencies are *not* definitely dirty, and all we have is
indeterminate ones like b, then that means a's build process *hasn't
changed*, which means its tree of dependencies still includes b, which means
we can deduce that if we *did* run a.do, it would end up running b.do.
Since we know that anyhow, we can safely just run b.do, which will either
b.set_checked() or b.set_changed(). Once that's done, we can re-parse a's
dependencies and this time conclusively tell if it needs to be redone or
not. Even if it does, b is already up-to-date, so the 'redo-ifchange b'
line in a.do will be fast.
...now take all the above and do it recursively to handle nested
dependencies, etc, and you're done.
2010-12-11 04:40:05 -08:00
|
|
|
else:
|
2018-12-11 02:57:29 +00:00
|
|
|
self._start_deps_unlocked(dirty)
|
The second half of redo-stamp: out-of-order building.
If a depends on b depends on c, and c is dirty but b uses redo-stamp
checksums, then 'redo-ifchange a' is indeterminate: we won't know if we need
to run a.do unless we first build b, but the script that *normally* runs
'redo-ifchange b' is a.do, and we don't want to run that yet, because we
don't know for sure if b is dirty, and we shouldn't build a unless one of
its dependencies is dirty. Eek!
Luckily, there's a safe solution. If we *know* a is dirty - eg. because
a.do or one of its children has definitely changed - then we can just run
a.do immediately and there's no problem, even if b is indeterminate, because
we were going to run a.do anyhow.
If a's dependencies are *not* definitely dirty, and all we have is
indeterminate ones like b, then that means a's build process *hasn't
changed*, which means its tree of dependencies still includes b, which means
we can deduce that if we *did* run a.do, it would end up running b.do.
Since we know that anyhow, we can safely just run b.do, which will either
b.set_checked() or b.set_changed(). Once that's done, we can re-parse a's
dependencies and this time conclusively tell if it needs to be redone or
not. Even if it does, b is already up-to-date, so the 'redo-ifchange b'
line in a.do will be fast.
...now take all the above and do it recursively to handle nested
dependencies, etc, and you're done.
2010-12-11 04:40:05 -08:00
|
|
|
|
2018-12-11 02:57:29 +00:00
|
|
|
def _start_self(self):
|
|
|
|
|
"""Run jobserver.start() to build this object's target file."""
|
2018-12-02 23:15:37 -05:00
|
|
|
assert self.lock.owned
|
The second half of redo-stamp: out-of-order building.
If a depends on b depends on c, and c is dirty but b uses redo-stamp
checksums, then 'redo-ifchange a' is indeterminate: we won't know if we need
to run a.do unless we first build b, but the script that *normally* runs
'redo-ifchange b' is a.do, and we don't want to run that yet, because we
don't know for sure if b is dirty, and we shouldn't build a unless one of
its dependencies is dirty. Eek!
Luckily, there's a safe solution. If we *know* a is dirty - eg. because
a.do or one of its children has definitely changed - then we can just run
a.do immediately and there's no problem, even if b is indeterminate, because
we were going to run a.do anyhow.
If a's dependencies are *not* definitely dirty, and all we have is
indeterminate ones like b, then that means a's build process *hasn't
changed*, which means its tree of dependencies still includes b, which means
we can deduce that if we *did* run a.do, it would end up running b.do.
Since we know that anyhow, we can safely just run b.do, which will either
b.set_checked() or b.set_changed(). Once that's done, we can re-parse a's
dependencies and this time conclusively tell if it needs to be redone or
not. Even if it does, b is already up-to-date, so the 'redo-ifchange b'
line in a.do will be fast.
...now take all the above and do it recursively to handle nested
dependencies, etc, and you're done.
2010-12-11 04:40:05 -08:00
|
|
|
t = self.t
|
|
|
|
|
sf = self.sf
|
2010-12-10 22:42:33 -08:00
|
|
|
newstamp = sf.read_stamp()
|
|
|
|
|
if (sf.is_generated and
|
2018-12-02 23:15:37 -05:00
|
|
|
newstamp != state.STAMP_MISSING and
|
|
|
|
|
(sf.is_override or state.detect_override(sf.stamp, newstamp))):
|
|
|
|
|
state.warn_override(_nice(t))
|
|
|
|
|
if not sf.is_override:
|
|
|
|
|
warn('%s - old: %r\n' % (_nice(t), sf.stamp))
|
|
|
|
|
warn('%s - new: %r\n' % (_nice(t), newstamp))
|
|
|
|
|
sf.set_override()
|
|
|
|
|
sf.save()
|
2018-12-18 12:59:51 +00:00
|
|
|
# fall through and treat it the same as a static file
|
2011-03-05 18:11:20 -08:00
|
|
|
if (os.path.exists(t) and not os.path.isdir(t + '/.')
|
2018-12-18 12:59:51 +00:00
|
|
|
and (sf.is_override or not sf.is_generated)):
|
2010-12-06 03:12:53 -08:00
|
|
|
# an existing source file that was not generated by us.
|
|
|
|
|
# This step is mentioned by djb in his notes.
|
|
|
|
|
# For example, a rule called default.c.do could be used to try
|
|
|
|
|
# to produce hello.c, but we don't want that to happen if
|
|
|
|
|
# hello.c was created by the end user.
|
2010-12-07 02:17:22 -08:00
|
|
|
debug2("-- static (%r)\n" % t)
|
2019-03-02 02:38:02 -05:00
|
|
|
if not sf.is_override:
|
|
|
|
|
sf.set_static()
|
2010-12-10 02:58:13 -08:00
|
|
|
sf.save()
|
2018-12-11 02:57:29 +00:00
|
|
|
return self._finalize(0)
|
2010-12-11 22:59:55 -08:00
|
|
|
sf.zap_deps1()
|
2018-12-02 23:15:37 -05:00
|
|
|
(dodir, dofile, _, basename, ext) = paths.find_do_file(sf)
|
2010-11-22 00:03:43 -08:00
|
|
|
if not dofile:
|
2010-11-24 02:18:19 -08:00
|
|
|
if os.path.exists(t):
|
2010-12-10 02:58:13 -08:00
|
|
|
sf.set_static()
|
|
|
|
|
sf.save()
|
2018-12-11 02:57:29 +00:00
|
|
|
return self._finalize(0)
|
2010-11-24 02:18:19 -08:00
|
|
|
else:
|
Raw logs contain @@REDO lines instead of formatted data.
This makes them more reliable to parse. redo-log can parse each line,
format and print it, then recurse if necessary. This got a little ugly
because I wanted 'redo --raw-logs' to work, which we want to format the
output nicely, but not call redo-log.
(As a result, --raw-logs has a different meaning to redo and
redo-log, which is kinda dumb. I should fix that.)
As an added bonus, redo-log now handles indenting of recursive logs, so
if the build was a -> a/b -> a/b/c, and you look at the log for a/b, it
can still start at the top level indentation.
2018-11-13 04:05:42 -05:00
|
|
|
err('no rule to redo %r\n' % t)
|
2019-03-02 02:53:02 -05:00
|
|
|
sf.set_failed()
|
|
|
|
|
sf.save()
|
2018-12-11 02:57:29 +00:00
|
|
|
return self._finalize(1)
|
Use mkstemp() to create the stdout temp file, and simplify $3 path.
Previously, we'd try to put the stdout temp file in the same dir as the
target, if that dir exists. Otherwise we'd walk up the directory tree
looking for a good place. But this would go wrong if the directory we
chose got *deleted* during the run of the .do file.
Instead, we switch to an entirely new design: we use mkstemp() to
generate a temp file in the standard temp file location (probably
/tmp), then open it and immediately delete it, so the .do file can't
cause any unexpected behaviour. After the .do file exits, we use our
still-open fd to the stdout file to read the content back out.
In the old implementation, we also put the $3 in the "adjusted"
location that depended whether the target dir already existed, just for
consistency. But that was never necessary: we didn't create the $3
file, and if the .do script wants to write to $3, it should create the
target dir first anyway. So change it to *always* use a $3 temp
filename in the target dir, which is much simpler and so has fewer edge
cases.
Add t/202-del/deltest4 with some tests for all these edge cases.
Reported-by: Jeff Stearns <jeff.stearns@gmail.com>
2018-12-12 03:37:44 +00:00
|
|
|
# There is no good place for us to pre-create a temp file for
|
|
|
|
|
# stdout. The target dir might not exist yet, or it might currently
|
|
|
|
|
# exist but get wiped by the .do script. Other dirs, like the one
|
|
|
|
|
# containing the .do file, might be mounted readonly. We can put it
|
|
|
|
|
# in the system temp dir, but then we can't necessarily rename it to
|
|
|
|
|
# the target filename because it might cross filesystem boundaries.
|
|
|
|
|
# Also, if redo is interrupted, it would leave a temp file lying
|
|
|
|
|
# around. To avoid all this, use mkstemp() to create a temp file
|
|
|
|
|
# wherever it wants to, and immediately unlink it, but keep a file
|
|
|
|
|
# handle open. When the .do script finishes, we can copy the
|
|
|
|
|
# content out of that nameless file handle into a file in the same
|
|
|
|
|
# dir as the target (which by definition must now exist, if you
|
|
|
|
|
# wanted the target to exist).
|
|
|
|
|
#
|
|
|
|
|
# On the other hand, the $3 temp filename can be hardcoded to be in
|
|
|
|
|
# the target directory, even if that directory does not exist.
|
|
|
|
|
# It's not *redo*'s job to create that file. The .do file will
|
|
|
|
|
# create it, if it wants, and it's the .do file's job to first ensure
|
|
|
|
|
# that the directory exists.
|
|
|
|
|
tmpbase = os.path.join(dodir, basename + ext)
|
|
|
|
|
self.tmpname = tmpbase + '.redo.tmp'
|
2019-01-18 00:06:18 +00:00
|
|
|
helpers.unlink(self.tmpname)
|
Use mkstemp() to create the stdout temp file, and simplify $3 path.
Previously, we'd try to put the stdout temp file in the same dir as the
target, if that dir exists. Otherwise we'd walk up the directory tree
looking for a good place. But this would go wrong if the directory we
chose got *deleted* during the run of the .do file.
Instead, we switch to an entirely new design: we use mkstemp() to
generate a temp file in the standard temp file location (probably
/tmp), then open it and immediately delete it, so the .do file can't
cause any unexpected behaviour. After the .do file exits, we use our
still-open fd to the stdout file to read the content back out.
In the old implementation, we also put the $3 in the "adjusted"
location that depended whether the target dir already existed, just for
consistency. But that was never necessary: we didn't create the $3
file, and if the .do script wants to write to $3, it should create the
target dir first anyway. So change it to *always* use a $3 temp
filename in the target dir, which is much simpler and so has fewer edge
cases.
Add t/202-del/deltest4 with some tests for all these edge cases.
Reported-by: Jeff Stearns <jeff.stearns@gmail.com>
2018-12-12 03:37:44 +00:00
|
|
|
ffd, fname = tempfile.mkstemp(prefix='redo.', suffix='.tmp')
|
2019-01-18 00:06:18 +00:00
|
|
|
helpers.close_on_exec(ffd, True)
|
Use mkstemp() to create the stdout temp file, and simplify $3 path.
Previously, we'd try to put the stdout temp file in the same dir as the
target, if that dir exists. Otherwise we'd walk up the directory tree
looking for a good place. But this would go wrong if the directory we
chose got *deleted* during the run of the .do file.
Instead, we switch to an entirely new design: we use mkstemp() to
generate a temp file in the standard temp file location (probably
/tmp), then open it and immediately delete it, so the .do file can't
cause any unexpected behaviour. After the .do file exits, we use our
still-open fd to the stdout file to read the content back out.
In the old implementation, we also put the $3 in the "adjusted"
location that depended whether the target dir already existed, just for
consistency. But that was never necessary: we didn't create the $3
file, and if the .do script wants to write to $3, it should create the
target dir first anyway. So change it to *always* use a $3 temp
filename in the target dir, which is much simpler and so has fewer edge
cases.
Add t/202-del/deltest4 with some tests for all these edge cases.
Reported-by: Jeff Stearns <jeff.stearns@gmail.com>
2018-12-12 03:37:44 +00:00
|
|
|
os.unlink(fname)
|
|
|
|
|
self.outfile = os.fdopen(ffd, 'w+')
|
2010-11-22 00:03:43 -08:00
|
|
|
# this will run in the dofile's directory, so use only basenames here
|
2016-11-27 12:29:43 -08:00
|
|
|
arg1 = basename + ext # target name (including extension)
|
|
|
|
|
arg2 = basename # target name (without extension)
|
2010-11-22 00:03:43 -08:00
|
|
|
argv = ['sh', '-e',
|
2010-12-19 05:47:38 -08:00
|
|
|
dofile,
|
2011-12-31 02:45:38 -05:00
|
|
|
arg1,
|
|
|
|
|
arg2,
|
Use mkstemp() to create the stdout temp file, and simplify $3 path.
Previously, we'd try to put the stdout temp file in the same dir as the
target, if that dir exists. Otherwise we'd walk up the directory tree
looking for a good place. But this would go wrong if the directory we
chose got *deleted* during the run of the .do file.
Instead, we switch to an entirely new design: we use mkstemp() to
generate a temp file in the standard temp file location (probably
/tmp), then open it and immediately delete it, so the .do file can't
cause any unexpected behaviour. After the .do file exits, we use our
still-open fd to the stdout file to read the content back out.
In the old implementation, we also put the $3 in the "adjusted"
location that depended whether the target dir already existed, just for
consistency. But that was never necessary: we didn't create the $3
file, and if the .do script wants to write to $3, it should create the
target dir first anyway. So change it to *always* use a $3 temp
filename in the target dir, which is much simpler and so has fewer edge
cases.
Add t/202-del/deltest4 with some tests for all these edge cases.
Reported-by: Jeff Stearns <jeff.stearns@gmail.com>
2018-12-12 03:37:44 +00:00
|
|
|
# $3 temp output file name
|
|
|
|
|
state.relpath(os.path.abspath(self.tmpname), dodir),
|
2018-12-02 23:15:37 -05:00
|
|
|
]
|
2018-12-05 01:07:16 -05:00
|
|
|
if env.v.VERBOSE:
|
2018-12-02 23:15:37 -05:00
|
|
|
argv[1] += 'v'
|
2018-12-05 01:07:16 -05:00
|
|
|
if env.v.XTRACE:
|
2018-12-02 23:15:37 -05:00
|
|
|
argv[1] += 'x'
|
2011-01-15 15:56:43 -08:00
|
|
|
firstline = open(os.path.join(dodir, dofile)).readline().strip()
|
2011-01-01 22:00:14 -08:00
|
|
|
if firstline.startswith('#!/'):
|
|
|
|
|
argv[0:2] = firstline[2:].split(' ')
|
2019-03-02 03:45:35 -05:00
|
|
|
# make sure to create the logfile *before* writing the meta() about
|
|
|
|
|
# it. that way redo-log won't trace into an obsolete logfile.
|
|
|
|
|
#
|
|
|
|
|
# We open a temp file and atomically rename it into place here.
|
|
|
|
|
# This guarantees that redo-log will never experience a file that
|
|
|
|
|
# gets truncated halfway through reading (eg. if we build the same
|
|
|
|
|
# target more than once in a run). Similarly, we don't want to
|
|
|
|
|
# actually unlink() the file in case redo-log is about to start
|
|
|
|
|
# reading a previous instance created during this session. It
|
|
|
|
|
# should always see either the old or new instance.
|
2018-12-05 01:07:16 -05:00
|
|
|
if env.v.LOG:
|
2019-03-12 00:03:34 -04:00
|
|
|
lfend = state.logname(self.sf.id)
|
|
|
|
|
# Make sure the temp file is in the same directory as lfend,
|
|
|
|
|
# so we can be sure of our ability to rename it atomically later.
|
|
|
|
|
lfd, lfname = tempfile.mkstemp(
|
|
|
|
|
prefix='redo.',
|
|
|
|
|
suffix='.log.tmp',
|
|
|
|
|
dir=os.path.dirname(lfend))
|
2019-03-02 03:45:35 -05:00
|
|
|
os.fdopen(lfd, 'w')
|
2019-03-12 00:03:34 -04:00
|
|
|
os.rename(lfname, lfend)
|
2011-01-17 23:53:35 -08:00
|
|
|
dof = state.File(name=os.path.join(dodir, dofile))
|
2010-12-07 02:17:22 -08:00
|
|
|
dof.set_static()
|
|
|
|
|
dof.save()
|
2010-12-09 02:44:33 -08:00
|
|
|
state.commit()
|
2018-11-21 19:21:02 -05:00
|
|
|
meta('do', state.target_relpath(t))
|
2018-12-11 02:57:29 +00:00
|
|
|
def call_subproc():
|
|
|
|
|
self._subproc(dodir, basename, ext, argv)
|
|
|
|
|
def call_exited(t, rv):
|
|
|
|
|
self._subproc_exited(t, rv, argv)
|
|
|
|
|
jobserver.start(t, call_subproc, call_exited)
|
|
|
|
|
|
|
|
|
|
def _start_deps_unlocked(self, dirty):
|
|
|
|
|
"""Run jobserver.start to build objects needed to check deps.
|
|
|
|
|
|
|
|
|
|
Out-of-band redo of some sub-objects. This happens when we're not
|
|
|
|
|
quite sure if t needs to be built or not (because some children
|
|
|
|
|
look dirty, but might turn out to be clean thanks to redo-stamp
|
|
|
|
|
checksums). We have to call redo-unlocked to figure it all out.
|
|
|
|
|
|
|
|
|
|
Note: redo-unlocked will handle all the updating of sf, so we don't
|
|
|
|
|
have to do it here, nor call _record_new_state. However, we have to
|
|
|
|
|
hold onto the lock because otherwise we would introduce a race
|
|
|
|
|
condition; that's why it's called redo-unlocked, because it doesn't
|
|
|
|
|
grab a lock.
|
|
|
|
|
"""
|
|
|
|
|
# FIXME: redo-unlocked is kind of a weird hack.
|
|
|
|
|
# Maybe we should just start jobs to build the necessary deps
|
|
|
|
|
# directly from this process, and when done, reconsider building
|
|
|
|
|
# the target we started with. But that makes this one process's
|
|
|
|
|
# build recursive, where currently it's flat.
|
redo-log: add automated tests, and fix some path bugs revealed by them.
When a log for X was saying it wanted to refer to Y, we used a relative
path, but it was sometimes relative to the wrong starting location, so
redo-log couldn't find it later.
Two examples:
- if default.o.do is handling builds for a/b/x.o, and default.o.do
does 'redo a/b/x.h', the log for x.o should refer to ./x.h, not
a/b/x.h.
- if foo.do is handling builds for foo, and it does
"cd a/b && redo x", the log for foo should refer to a/b/x, not just
x.
2018-11-19 17:09:40 -05:00
|
|
|
here = os.getcwd()
|
|
|
|
|
def _fix(p):
|
2018-12-05 01:07:16 -05:00
|
|
|
return state.relpath(os.path.join(env.v.BASE, p), here)
|
redo-log: add automated tests, and fix some path bugs revealed by them.
When a log for X was saying it wanted to refer to Y, we used a relative
path, but it was sometimes relative to the wrong starting location, so
redo-log couldn't find it later.
Two examples:
- if default.o.do is handling builds for a/b/x.o, and default.o.do
does 'redo a/b/x.h', the log for x.o should refer to ./x.h, not
a/b/x.h.
- if foo.do is handling builds for foo, and it does
"cd a/b && redo x", the log for foo should refer to a/b/x, not just
x.
2018-11-19 17:09:40 -05:00
|
|
|
argv = (['redo-unlocked', _fix(self.sf.name)] +
|
2018-11-20 10:02:32 -05:00
|
|
|
list(set(_fix(d.name) for d in dirty)))
|
redo-log: add automated tests, and fix some path bugs revealed by them.
When a log for X was saying it wanted to refer to Y, we used a relative
path, but it was sometimes relative to the wrong starting location, so
redo-log couldn't find it later.
Two examples:
- if default.o.do is handling builds for a/b/x.o, and default.o.do
does 'redo a/b/x.h', the log for x.o should refer to ./x.h, not
a/b/x.h.
- if foo.do is handling builds for foo, and it does
"cd a/b && redo x", the log for foo should refer to a/b/x, not just
x.
2018-11-19 17:09:40 -05:00
|
|
|
meta('check', state.target_relpath(self.t))
|
The second half of redo-stamp: out-of-order building.
If a depends on b depends on c, and c is dirty but b uses redo-stamp
checksums, then 'redo-ifchange a' is indeterminate: we won't know if we need
to run a.do unless we first build b, but the script that *normally* runs
'redo-ifchange b' is a.do, and we don't want to run that yet, because we
don't know for sure if b is dirty, and we shouldn't build a unless one of
its dependencies is dirty. Eek!
Luckily, there's a safe solution. If we *know* a is dirty - eg. because
a.do or one of its children has definitely changed - then we can just run
a.do immediately and there's no problem, even if b is indeterminate, because
we were going to run a.do anyhow.
If a's dependencies are *not* definitely dirty, and all we have is
indeterminate ones like b, then that means a's build process *hasn't
changed*, which means its tree of dependencies still includes b, which means
we can deduce that if we *did* run a.do, it would end up running b.do.
Since we know that anyhow, we can safely just run b.do, which will either
b.set_checked() or b.set_changed(). Once that's done, we can re-parse a's
dependencies and this time conclusively tell if it needs to be redone or
not. Even if it does, b is already up-to-date, so the 'redo-ifchange b'
line in a.do will be fast.
...now take all the above and do it recursively to handle nested
dependencies, etc, and you're done.
2010-12-11 04:40:05 -08:00
|
|
|
state.commit()
|
2018-12-11 02:57:29 +00:00
|
|
|
def subtask():
|
2018-12-05 01:07:16 -05:00
|
|
|
os.environ['REDO_DEPTH'] = env.v.DEPTH + ' '
|
2018-12-02 23:15:37 -05:00
|
|
|
# python ignores SIGPIPE
|
|
|
|
|
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
|
The second half of redo-stamp: out-of-order building.
If a depends on b depends on c, and c is dirty but b uses redo-stamp
checksums, then 'redo-ifchange a' is indeterminate: we won't know if we need
to run a.do unless we first build b, but the script that *normally* runs
'redo-ifchange b' is a.do, and we don't want to run that yet, because we
don't know for sure if b is dirty, and we shouldn't build a unless one of
its dependencies is dirty. Eek!
Luckily, there's a safe solution. If we *know* a is dirty - eg. because
a.do or one of its children has definitely changed - then we can just run
a.do immediately and there's no problem, even if b is indeterminate, because
we were going to run a.do anyhow.
If a's dependencies are *not* definitely dirty, and all we have is
indeterminate ones like b, then that means a's build process *hasn't
changed*, which means its tree of dependencies still includes b, which means
we can deduce that if we *did* run a.do, it would end up running b.do.
Since we know that anyhow, we can safely just run b.do, which will either
b.set_checked() or b.set_changed(). Once that's done, we can re-parse a's
dependencies and this time conclusively tell if it needs to be redone or
not. Even if it does, b is already up-to-date, so the 'redo-ifchange b'
line in a.do will be fast.
...now take all the above and do it recursively to handle nested
dependencies, etc, and you're done.
2010-12-11 04:40:05 -08:00
|
|
|
os.execvp(argv[0], argv)
|
2018-12-02 23:15:37 -05:00
|
|
|
assert 0
|
The second half of redo-stamp: out-of-order building.
If a depends on b depends on c, and c is dirty but b uses redo-stamp
checksums, then 'redo-ifchange a' is indeterminate: we won't know if we need
to run a.do unless we first build b, but the script that *normally* runs
'redo-ifchange b' is a.do, and we don't want to run that yet, because we
don't know for sure if b is dirty, and we shouldn't build a unless one of
its dependencies is dirty. Eek!
Luckily, there's a safe solution. If we *know* a is dirty - eg. because
a.do or one of its children has definitely changed - then we can just run
a.do immediately and there's no problem, even if b is indeterminate, because
we were going to run a.do anyhow.
If a's dependencies are *not* definitely dirty, and all we have is
indeterminate ones like b, then that means a's build process *hasn't
changed*, which means its tree of dependencies still includes b, which means
we can deduce that if we *did* run a.do, it would end up running b.do.
Since we know that anyhow, we can safely just run b.do, which will either
b.set_checked() or b.set_changed(). Once that's done, we can re-parse a's
dependencies and this time conclusively tell if it needs to be redone or
not. Even if it does, b is already up-to-date, so the 'redo-ifchange b'
line in a.do will be fast.
...now take all the above and do it recursively to handle nested
dependencies, etc, and you're done.
2010-12-11 04:40:05 -08:00
|
|
|
# returns only if there's an exception
|
2018-12-11 02:57:29 +00:00
|
|
|
def job_exited(t, rv):
|
|
|
|
|
return self._finalize(rv)
|
|
|
|
|
jobserver.start(self.t, jobfunc=subtask, donefunc=job_exited)
|
The second half of redo-stamp: out-of-order building.
If a depends on b depends on c, and c is dirty but b uses redo-stamp
checksums, then 'redo-ifchange a' is indeterminate: we won't know if we need
to run a.do unless we first build b, but the script that *normally* runs
'redo-ifchange b' is a.do, and we don't want to run that yet, because we
don't know for sure if b is dirty, and we shouldn't build a unless one of
its dependencies is dirty. Eek!
Luckily, there's a safe solution. If we *know* a is dirty - eg. because
a.do or one of its children has definitely changed - then we can just run
a.do immediately and there's no problem, even if b is indeterminate, because
we were going to run a.do anyhow.
If a's dependencies are *not* definitely dirty, and all we have is
indeterminate ones like b, then that means a's build process *hasn't
changed*, which means its tree of dependencies still includes b, which means
we can deduce that if we *did* run a.do, it would end up running b.do.
Since we know that anyhow, we can safely just run b.do, which will either
b.set_checked() or b.set_changed(). Once that's done, we can re-parse a's
dependencies and this time conclusively tell if it needs to be redone or
not. Even if it does, b is already up-to-date, so the 'redo-ifchange b'
line in a.do will be fast.
...now take all the above and do it recursively to handle nested
dependencies, etc, and you're done.
2010-12-11 04:40:05 -08:00
|
|
|
|
2018-12-11 02:57:29 +00:00
|
|
|
def _subproc(self, dodir, basename, ext, argv):
|
2019-03-02 18:46:00 -05:00
|
|
|
"""The function called by jobserver.start to exec the build script.
|
2018-12-11 02:57:29 +00:00
|
|
|
|
|
|
|
|
This is run in the *child* process.
|
|
|
|
|
"""
|
2010-11-25 06:35:22 -08:00
|
|
|
# careful: REDO_PWD was the PWD relative to the STARTPATH at the time
|
|
|
|
|
# we *started* building the current target; but that target ran
|
|
|
|
|
# redo-ifchange, and it might have done it from a different directory
|
2010-12-07 02:17:22 -08:00
|
|
|
# than we started it in. So os.getcwd() might be != REDO_PWD right
|
|
|
|
|
# now.
|
2018-12-02 23:15:37 -05:00
|
|
|
assert state.is_flushed()
|
2018-12-11 02:57:29 +00:00
|
|
|
newp = os.path.realpath(dodir)
|
2019-05-01 13:17:35 -04:00
|
|
|
# CDPATH apparently caused unexpected 'cd' output on some platforms.
|
|
|
|
|
os.unsetenv('CDPATH')
|
2018-12-05 01:07:16 -05:00
|
|
|
os.environ['REDO_PWD'] = state.relpath(newp, env.v.STARTDIR)
|
2018-12-11 02:57:29 +00:00
|
|
|
os.environ['REDO_TARGET'] = basename + ext
|
2018-12-05 01:07:16 -05:00
|
|
|
os.environ['REDO_DEPTH'] = env.v.DEPTH + ' '
|
2019-03-02 18:46:00 -05:00
|
|
|
if env.v.XTRACE == 1:
|
|
|
|
|
os.environ['REDO_XTRACE'] = '0'
|
|
|
|
|
if env.v.VERBOSE == 1:
|
|
|
|
|
os.environ['REDO_VERBOSE'] = '0'
|
2018-12-05 00:18:07 -05:00
|
|
|
cycles.add(self.lock.fid)
|
2018-12-11 02:57:29 +00:00
|
|
|
if dodir:
|
|
|
|
|
os.chdir(dodir)
|
Use mkstemp() to create the stdout temp file, and simplify $3 path.
Previously, we'd try to put the stdout temp file in the same dir as the
target, if that dir exists. Otherwise we'd walk up the directory tree
looking for a good place. But this would go wrong if the directory we
chose got *deleted* during the run of the .do file.
Instead, we switch to an entirely new design: we use mkstemp() to
generate a temp file in the standard temp file location (probably
/tmp), then open it and immediately delete it, so the .do file can't
cause any unexpected behaviour. After the .do file exits, we use our
still-open fd to the stdout file to read the content back out.
In the old implementation, we also put the $3 in the "adjusted"
location that depended whether the target dir already existed, just for
consistency. But that was never necessary: we didn't create the $3
file, and if the .do script wants to write to $3, it should create the
target dir first anyway. So change it to *always* use a $3 temp
filename in the target dir, which is much simpler and so has fewer edge
cases.
Add t/202-del/deltest4 with some tests for all these edge cases.
Reported-by: Jeff Stearns <jeff.stearns@gmail.com>
2018-12-12 03:37:44 +00:00
|
|
|
os.dup2(self.outfile.fileno(), 1)
|
|
|
|
|
os.close(self.outfile.fileno())
|
2019-01-18 00:06:18 +00:00
|
|
|
helpers.close_on_exec(1, False)
|
2018-12-05 01:07:16 -05:00
|
|
|
if env.v.LOG:
|
redo-log: fix stdout vs stderr; don't recapture if .do script redirects stderr.
redo-log should log to stdout, because when you ask for the specific
logs from a run, the logs are the output you requested. redo-log's
stderr should be about any errors retrieving that output.
On the other hand, when you run redo, the logs are literally the stderr
of the build steps, which are incidental to the main job (building
things). So that should be send to stderr. Previously, we were
sending to stderr when --no-log, but stdout when --log, which is
totally wrong.
Also, adding redo-log had the unexpected result that if a .do script
redirected the stderr of a sub-redo or redo-ifchange to a file or pipe,
the output would be eaten by redo-log instead of the intended output.
So a test runner like this:
self.test:
redo self.runtest 2>&1 | grep ERROR
would not work; the self.runtest output would be sent to redo's log
buffer (and from there, probably printed to the toplevel redo's stderr)
rather than passed along to grep.
2018-11-19 16:27:41 -05:00
|
|
|
cur_inode = str(os.fstat(2).st_ino)
|
2018-12-05 01:07:16 -05:00
|
|
|
if not env.v.LOG_INODE or cur_inode == env.v.LOG_INODE:
|
redo-log: fix stdout vs stderr; don't recapture if .do script redirects stderr.
redo-log should log to stdout, because when you ask for the specific
logs from a run, the logs are the output you requested. redo-log's
stderr should be about any errors retrieving that output.
On the other hand, when you run redo, the logs are literally the stderr
of the build steps, which are incidental to the main job (building
things). So that should be send to stderr. Previously, we were
sending to stderr when --no-log, but stdout when --log, which is
totally wrong.
Also, adding redo-log had the unexpected result that if a .do script
redirected the stderr of a sub-redo or redo-ifchange to a file or pipe,
the output would be eaten by redo-log instead of the intended output.
So a test runner like this:
self.test:
redo self.runtest 2>&1 | grep ERROR
would not work; the self.runtest output would be sent to redo's log
buffer (and from there, probably printed to the toplevel redo's stderr)
rather than passed along to grep.
2018-11-19 16:27:41 -05:00
|
|
|
# .do script has *not* redirected stderr, which means we're
|
|
|
|
|
# using redo-log's log saving mode. That means subprocs
|
|
|
|
|
# should be logged to their own file. If the .do script
|
|
|
|
|
# *does* redirect stderr, that redirection should be inherited
|
|
|
|
|
# by subprocs, so we'd do nothing.
|
|
|
|
|
logf = open(state.logname(self.sf.id), 'w')
|
Workaround for completely broken file locking on Windows 10 WSL.
WSL (Windows Services for Linux) provides a Linux-kernel-compatible ABI
for userspace processes, but the current version doesn't not implement
fcntl() locks at all; it just always returns success. See
https://github.com/Microsoft/WSL/issues/1927.
This causes us three kinds of problem:
1. sqlite3 in WAL mode gives "OperationalError: locking protocol".
1b. Other sqlite3 journal modes also don't work when used by
multiple processes.
2. redo parallelism doesn't work, because we can't prevent the same
target from being build several times simultaneously.
3. "redo-log -f" doesn't work, since it can't tell whether the log
file it's tailing is "done" or not.
To fix #1, we switch the sqlite3 journal back to PERSIST instead of
WAL. We originally changed to WAL in commit 5156feae9d to reduce
deadlocks on MacOS. That was never adequately explained, but PERSIST
still acts weird on MacOS, so we'll only switch to PERSIST when we
detect that locking is definitely broken. Sigh.
To (mostly) fix #2, we disable any -j value > 1 when locking is broken.
This prevents basic forms of parallelism, but doesn't stop you from
re-entrantly starting other instances of redo. To fix that properly,
we need to switch to a different locking mechanism entirely, which is
tough in python. flock() locks probably work, for example, but
python's locks lie and just use fcntl locks for those.
To fix #3, we always force --no-log mode when we find that locking is
broken.
2019-01-02 14:18:51 -05:00
|
|
|
new_inode = os.fstat(logf.fileno()).st_ino
|
redo-log: add automated tests, and fix some path bugs revealed by them.
When a log for X was saying it wanted to refer to Y, we used a relative
path, but it was sometimes relative to the wrong starting location, so
redo-log couldn't find it later.
Two examples:
- if default.o.do is handling builds for a/b/x.o, and default.o.do
does 'redo a/b/x.h', the log for x.o should refer to ./x.h, not
a/b/x.h.
- if foo.do is handling builds for foo, and it does
"cd a/b && redo x", the log for foo should refer to a/b/x, not just
x.
2018-11-19 17:09:40 -05:00
|
|
|
os.environ['REDO_LOG'] = '1' # .do files can check this
|
Workaround for completely broken file locking on Windows 10 WSL.
WSL (Windows Services for Linux) provides a Linux-kernel-compatible ABI
for userspace processes, but the current version doesn't not implement
fcntl() locks at all; it just always returns success. See
https://github.com/Microsoft/WSL/issues/1927.
This causes us three kinds of problem:
1. sqlite3 in WAL mode gives "OperationalError: locking protocol".
1b. Other sqlite3 journal modes also don't work when used by
multiple processes.
2. redo parallelism doesn't work, because we can't prevent the same
target from being build several times simultaneously.
3. "redo-log -f" doesn't work, since it can't tell whether the log
file it's tailing is "done" or not.
To fix #1, we switch the sqlite3 journal back to PERSIST instead of
WAL. We originally changed to WAL in commit 5156feae9d to reduce
deadlocks on MacOS. That was never adequately explained, but PERSIST
still acts weird on MacOS, so we'll only switch to PERSIST when we
detect that locking is definitely broken. Sigh.
To (mostly) fix #2, we disable any -j value > 1 when locking is broken.
This prevents basic forms of parallelism, but doesn't stop you from
re-entrantly starting other instances of redo. To fix that properly,
we need to switch to a different locking mechanism entirely, which is
tough in python. flock() locks probably work, for example, but
python's locks lie and just use fcntl locks for those.
To fix #3, we always force --no-log mode when we find that locking is
broken.
2019-01-02 14:18:51 -05:00
|
|
|
os.environ['REDO_LOG_INODE'] = str(new_inode)
|
redo-log: fix stdout vs stderr; don't recapture if .do script redirects stderr.
redo-log should log to stdout, because when you ask for the specific
logs from a run, the logs are the output you requested. redo-log's
stderr should be about any errors retrieving that output.
On the other hand, when you run redo, the logs are literally the stderr
of the build steps, which are incidental to the main job (building
things). So that should be send to stderr. Previously, we were
sending to stderr when --no-log, but stdout when --log, which is
totally wrong.
Also, adding redo-log had the unexpected result that if a .do script
redirected the stderr of a sub-redo or redo-ifchange to a file or pipe,
the output would be eaten by redo-log instead of the intended output.
So a test runner like this:
self.test:
redo self.runtest 2>&1 | grep ERROR
would not work; the self.runtest output would be sent to redo's log
buffer (and from there, probably printed to the toplevel redo's stderr)
rather than passed along to grep.
2018-11-19 16:27:41 -05:00
|
|
|
os.dup2(logf.fileno(), 2)
|
2019-01-18 00:06:18 +00:00
|
|
|
helpers.close_on_exec(2, False)
|
redo-log: fix stdout vs stderr; don't recapture if .do script redirects stderr.
redo-log should log to stdout, because when you ask for the specific
logs from a run, the logs are the output you requested. redo-log's
stderr should be about any errors retrieving that output.
On the other hand, when you run redo, the logs are literally the stderr
of the build steps, which are incidental to the main job (building
things). So that should be send to stderr. Previously, we were
sending to stderr when --no-log, but stdout when --log, which is
totally wrong.
Also, adding redo-log had the unexpected result that if a .do script
redirected the stderr of a sub-redo or redo-ifchange to a file or pipe,
the output would be eaten by redo-log instead of the intended output.
So a test runner like this:
self.test:
redo self.runtest 2>&1 | grep ERROR
would not work; the self.runtest output would be sent to redo's log
buffer (and from there, probably printed to the toplevel redo's stderr)
rather than passed along to grep.
2018-11-19 16:27:41 -05:00
|
|
|
logf.close()
|
|
|
|
|
else:
|
|
|
|
|
if 'REDO_LOG_INODE' in os.environ:
|
|
|
|
|
del os.environ['REDO_LOG_INODE']
|
redo-log: add automated tests, and fix some path bugs revealed by them.
When a log for X was saying it wanted to refer to Y, we used a relative
path, but it was sometimes relative to the wrong starting location, so
redo-log couldn't find it later.
Two examples:
- if default.o.do is handling builds for a/b/x.o, and default.o.do
does 'redo a/b/x.h', the log for x.o should refer to ./x.h, not
a/b/x.h.
- if foo.do is handling builds for foo, and it does
"cd a/b && redo x", the log for foo should refer to a/b/x, not just
x.
2018-11-19 17:09:40 -05:00
|
|
|
os.environ['REDO_LOG'] = ''
|
2013-06-28 01:48:41 +00:00
|
|
|
signal.signal(signal.SIGPIPE, signal.SIG_DFL) # python ignores SIGPIPE
|
2018-12-05 01:07:16 -05:00
|
|
|
if env.v.VERBOSE or env.v.XTRACE:
|
2018-12-11 02:57:29 +00:00
|
|
|
logs.write('* %s' % ' '.join(argv).replace('\n', ' '))
|
|
|
|
|
os.execvp(argv[0], argv)
|
Raw logs contain @@REDO lines instead of formatted data.
This makes them more reliable to parse. redo-log can parse each line,
format and print it, then recurse if necessary. This got a little ugly
because I wanted 'redo --raw-logs' to work, which we want to format the
output nicely, but not call redo-log.
(As a result, --raw-logs has a different meaning to redo and
redo-log, which is kinda dumb. I should fix that.)
As an added bonus, redo-log now handles indenting of recursive logs, so
if the build was a -> a/b -> a/b/c, and you look at the log for a/b, it
can still start at the top level indentation.
2018-11-13 04:05:42 -05:00
|
|
|
# FIXME: it would be nice to log the exit code to logf.
|
|
|
|
|
# But that would have to happen in the parent process, which doesn't
|
|
|
|
|
# have logf open.
|
2018-12-02 23:15:37 -05:00
|
|
|
assert 0
|
2010-11-22 00:12:56 -08:00
|
|
|
# returns only if there's an exception
|
2010-11-22 00:03:43 -08:00
|
|
|
|
2018-12-11 02:57:29 +00:00
|
|
|
def _subproc_exited(self, t, rv, argv):
|
|
|
|
|
"""Called by the jobserver when our subtask exits.
|
|
|
|
|
|
|
|
|
|
This is run in the *parent* process.
|
|
|
|
|
"""
|
2010-11-22 03:21:17 -08:00
|
|
|
try:
|
2010-12-09 03:01:26 -08:00
|
|
|
state.check_sane()
|
2018-12-11 02:57:29 +00:00
|
|
|
rv = self._record_new_state(t, rv, argv)
|
2010-12-09 05:53:30 -08:00
|
|
|
state.commit()
|
2010-11-22 03:21:17 -08:00
|
|
|
finally:
|
2018-12-11 02:57:29 +00:00
|
|
|
self._finalize(rv)
|
|
|
|
|
|
|
|
|
|
def _record_new_state(self, t, rv, argv):
|
|
|
|
|
"""After a subtask finishes, handle its changes to the output file.
|
2010-11-22 03:21:17 -08:00
|
|
|
|
2018-12-11 02:57:29 +00:00
|
|
|
This is run in the *parent* process.
|
|
|
|
|
|
|
|
|
|
This includes renaming temp files into place and detecting mistakes
|
|
|
|
|
(like writing directly to $1 instead of $3). We also have to record
|
|
|
|
|
the new file stamp data for the completed target.
|
|
|
|
|
"""
|
Use mkstemp() to create the stdout temp file, and simplify $3 path.
Previously, we'd try to put the stdout temp file in the same dir as the
target, if that dir exists. Otherwise we'd walk up the directory tree
looking for a good place. But this would go wrong if the directory we
chose got *deleted* during the run of the .do file.
Instead, we switch to an entirely new design: we use mkstemp() to
generate a temp file in the standard temp file location (probably
/tmp), then open it and immediately delete it, so the .do file can't
cause any unexpected behaviour. After the .do file exits, we use our
still-open fd to the stdout file to read the content back out.
In the old implementation, we also put the $3 in the "adjusted"
location that depended whether the target dir already existed, just for
consistency. But that was never necessary: we didn't create the $3
file, and if the .do script wants to write to $3, it should create the
target dir first anyway. So change it to *always* use a $3 temp
filename in the target dir, which is much simpler and so has fewer edge
cases.
Add t/202-del/deltest4 with some tests for all these edge cases.
Reported-by: Jeff Stearns <jeff.stearns@gmail.com>
2018-12-12 03:37:44 +00:00
|
|
|
outfile = self.outfile
|
2010-11-22 04:40:54 -08:00
|
|
|
before_t = self.before_t
|
|
|
|
|
after_t = _try_stat(t)
|
Use mkstemp() to create the stdout temp file, and simplify $3 path.
Previously, we'd try to put the stdout temp file in the same dir as the
target, if that dir exists. Otherwise we'd walk up the directory tree
looking for a good place. But this would go wrong if the directory we
chose got *deleted* during the run of the .do file.
Instead, we switch to an entirely new design: we use mkstemp() to
generate a temp file in the standard temp file location (probably
/tmp), then open it and immediately delete it, so the .do file can't
cause any unexpected behaviour. After the .do file exits, we use our
still-open fd to the stdout file to read the content back out.
In the old implementation, we also put the $3 in the "adjusted"
location that depended whether the target dir already existed, just for
consistency. But that was never necessary: we didn't create the $3
file, and if the .do script wants to write to $3, it should create the
target dir first anyway. So change it to *always* use a $3 temp
filename in the target dir, which is much simpler and so has fewer edge
cases.
Add t/202-del/deltest4 with some tests for all these edge cases.
Reported-by: Jeff Stearns <jeff.stearns@gmail.com>
2018-12-12 03:37:44 +00:00
|
|
|
st1 = os.fstat(outfile.fileno())
|
|
|
|
|
st2 = _try_stat(self.tmpname)
|
|
|
|
|
if (after_t and
|
2018-12-02 23:15:37 -05:00
|
|
|
(not before_t or before_t.st_mtime != after_t.st_mtime) and
|
|
|
|
|
not stat.S_ISDIR(after_t.st_mode)):
|
2018-12-11 02:57:29 +00:00
|
|
|
err('%s modified %s directly!\n' % (argv[2], t))
|
2010-12-11 00:29:04 -08:00
|
|
|
err('...you should update $3 (a temp file) or stdout, not $1.\n')
|
2010-11-22 04:40:54 -08:00
|
|
|
rv = 206
|
2010-12-11 00:29:04 -08:00
|
|
|
elif st2 and st1.st_size > 0:
|
2018-12-11 02:57:29 +00:00
|
|
|
err('%s wrote to stdout *and* created $3.\n' % argv[2])
|
2010-11-22 04:40:54 -08:00
|
|
|
err('...you should write status messages to stderr, not stdout.\n')
|
|
|
|
|
rv = 207
|
2018-12-02 23:15:37 -05:00
|
|
|
if rv == 0:
|
2018-12-02 16:53:05 -05:00
|
|
|
# FIXME: race condition here between updating stamp/is_generated
|
|
|
|
|
# and actually renaming the files into place. There needs to
|
|
|
|
|
# be some kind of two-stage commit, I guess.
|
2018-12-13 12:58:56 +00:00
|
|
|
if st1.st_size > 0 and not st2:
|
Use mkstemp() to create the stdout temp file, and simplify $3 path.
Previously, we'd try to put the stdout temp file in the same dir as the
target, if that dir exists. Otherwise we'd walk up the directory tree
looking for a good place. But this would go wrong if the directory we
chose got *deleted* during the run of the .do file.
Instead, we switch to an entirely new design: we use mkstemp() to
generate a temp file in the standard temp file location (probably
/tmp), then open it and immediately delete it, so the .do file can't
cause any unexpected behaviour. After the .do file exits, we use our
still-open fd to the stdout file to read the content back out.
In the old implementation, we also put the $3 in the "adjusted"
location that depended whether the target dir already existed, just for
consistency. But that was never necessary: we didn't create the $3
file, and if the .do script wants to write to $3, it should create the
target dir first anyway. So change it to *always* use a $3 temp
filename in the target dir, which is much simpler and so has fewer edge
cases.
Add t/202-del/deltest4 with some tests for all these edge cases.
Reported-by: Jeff Stearns <jeff.stearns@gmail.com>
2018-12-12 03:37:44 +00:00
|
|
|
# script wrote to stdout. Copy its contents to the tmpfile.
|
2019-01-18 00:06:18 +00:00
|
|
|
helpers.unlink(self.tmpname)
|
2018-10-06 02:38:32 -04:00
|
|
|
try:
|
Use mkstemp() to create the stdout temp file, and simplify $3 path.
Previously, we'd try to put the stdout temp file in the same dir as the
target, if that dir exists. Otherwise we'd walk up the directory tree
looking for a good place. But this would go wrong if the directory we
chose got *deleted* during the run of the .do file.
Instead, we switch to an entirely new design: we use mkstemp() to
generate a temp file in the standard temp file location (probably
/tmp), then open it and immediately delete it, so the .do file can't
cause any unexpected behaviour. After the .do file exits, we use our
still-open fd to the stdout file to read the content back out.
In the old implementation, we also put the $3 in the "adjusted"
location that depended whether the target dir already existed, just for
consistency. But that was never necessary: we didn't create the $3
file, and if the .do script wants to write to $3, it should create the
target dir first anyway. So change it to *always* use a $3 temp
filename in the target dir, which is much simpler and so has fewer edge
cases.
Add t/202-del/deltest4 with some tests for all these edge cases.
Reported-by: Jeff Stearns <jeff.stearns@gmail.com>
2018-12-12 03:37:44 +00:00
|
|
|
newf = open(self.tmpname, 'w')
|
2019-10-27 14:19:50 +01:00
|
|
|
except IOError as e:
|
2018-12-13 12:58:56 +00:00
|
|
|
dnt = os.path.dirname(os.path.abspath(t))
|
2018-10-06 02:38:32 -04:00
|
|
|
if not os.path.exists(dnt):
|
2018-12-12 03:36:09 +00:00
|
|
|
# This could happen, so report a simple error message
|
|
|
|
|
# that gives a hint for how to fix your .do script.
|
2018-10-06 02:38:32 -04:00
|
|
|
err('%s: target dir %r does not exist!\n' % (t, dnt))
|
|
|
|
|
else:
|
2018-12-13 12:58:56 +00:00
|
|
|
# This could happen for, eg. a permissions error on
|
|
|
|
|
# the target directory.
|
|
|
|
|
err('%s: copy stdout: %s\n' % (t, e))
|
|
|
|
|
rv = 209
|
Use mkstemp() to create the stdout temp file, and simplify $3 path.
Previously, we'd try to put the stdout temp file in the same dir as the
target, if that dir exists. Otherwise we'd walk up the directory tree
looking for a good place. But this would go wrong if the directory we
chose got *deleted* during the run of the .do file.
Instead, we switch to an entirely new design: we use mkstemp() to
generate a temp file in the standard temp file location (probably
/tmp), then open it and immediately delete it, so the .do file can't
cause any unexpected behaviour. After the .do file exits, we use our
still-open fd to the stdout file to read the content back out.
In the old implementation, we also put the $3 in the "adjusted"
location that depended whether the target dir already existed, just for
consistency. But that was never necessary: we didn't create the $3
file, and if the .do script wants to write to $3, it should create the
target dir first anyway. So change it to *always* use a $3 temp
filename in the target dir, which is much simpler and so has fewer edge
cases.
Add t/202-del/deltest4 with some tests for all these edge cases.
Reported-by: Jeff Stearns <jeff.stearns@gmail.com>
2018-12-12 03:37:44 +00:00
|
|
|
else:
|
|
|
|
|
self.outfile.seek(0)
|
|
|
|
|
while 1:
|
|
|
|
|
b = self.outfile.read(1024*1024)
|
|
|
|
|
if not b:
|
|
|
|
|
break
|
|
|
|
|
newf.write(b)
|
|
|
|
|
newf.close()
|
|
|
|
|
st2 = _try_stat(self.tmpname)
|
|
|
|
|
if st2:
|
|
|
|
|
# either $3 file was created *or* stdout was written to.
|
|
|
|
|
# therefore tmpfile now exists.
|
2010-12-11 21:10:57 -08:00
|
|
|
try:
|
Use mkstemp() to create the stdout temp file, and simplify $3 path.
Previously, we'd try to put the stdout temp file in the same dir as the
target, if that dir exists. Otherwise we'd walk up the directory tree
looking for a good place. But this would go wrong if the directory we
chose got *deleted* during the run of the .do file.
Instead, we switch to an entirely new design: we use mkstemp() to
generate a temp file in the standard temp file location (probably
/tmp), then open it and immediately delete it, so the .do file can't
cause any unexpected behaviour. After the .do file exits, we use our
still-open fd to the stdout file to read the content back out.
In the old implementation, we also put the $3 in the "adjusted"
location that depended whether the target dir already existed, just for
consistency. But that was never necessary: we didn't create the $3
file, and if the .do script wants to write to $3, it should create the
target dir first anyway. So change it to *always* use a $3 temp
filename in the target dir, which is much simpler and so has fewer edge
cases.
Add t/202-del/deltest4 with some tests for all these edge cases.
Reported-by: Jeff Stearns <jeff.stearns@gmail.com>
2018-12-12 03:37:44 +00:00
|
|
|
# Atomically replace the target file
|
|
|
|
|
os.rename(self.tmpname, t)
|
2019-10-27 14:19:50 +01:00
|
|
|
except OSError as e:
|
2018-12-13 12:58:56 +00:00
|
|
|
# This could happen for, eg. a permissions error on
|
|
|
|
|
# the target directory.
|
Use mkstemp() to create the stdout temp file, and simplify $3 path.
Previously, we'd try to put the stdout temp file in the same dir as the
target, if that dir exists. Otherwise we'd walk up the directory tree
looking for a good place. But this would go wrong if the directory we
chose got *deleted* during the run of the .do file.
Instead, we switch to an entirely new design: we use mkstemp() to
generate a temp file in the standard temp file location (probably
/tmp), then open it and immediately delete it, so the .do file can't
cause any unexpected behaviour. After the .do file exits, we use our
still-open fd to the stdout file to read the content back out.
In the old implementation, we also put the $3 in the "adjusted"
location that depended whether the target dir already existed, just for
consistency. But that was never necessary: we didn't create the $3
file, and if the .do script wants to write to $3, it should create the
target dir first anyway. So change it to *always* use a $3 temp
filename in the target dir, which is much simpler and so has fewer edge
cases.
Add t/202-del/deltest4 with some tests for all these edge cases.
Reported-by: Jeff Stearns <jeff.stearns@gmail.com>
2018-12-12 03:37:44 +00:00
|
|
|
err('%s: rename %s: %s\n' % (t, self.tmpname, e))
|
2018-12-13 12:58:56 +00:00
|
|
|
rv = 209
|
2010-12-11 00:29:04 -08:00
|
|
|
else: # no output generated at all; that's ok
|
2019-01-18 00:06:18 +00:00
|
|
|
helpers.unlink(t)
|
2010-12-10 02:58:13 -08:00
|
|
|
sf = self.sf
|
2010-12-11 02:17:51 -08:00
|
|
|
sf.refresh()
|
2010-12-10 22:42:33 -08:00
|
|
|
sf.is_generated = True
|
2010-12-11 02:17:51 -08:00
|
|
|
sf.is_override = False
|
|
|
|
|
if sf.is_checked() or sf.is_changed():
|
|
|
|
|
# it got checked during the run; someone ran redo-stamp.
|
Further improve handling of symlink targets/deps.
In commit redo-0.11-4-g34669fb, we changed os.stat into os.lstat to
avoid false positives in the "manual override" detector: a .do file
that generates $3 as a symlink would trigger manual override if the
*target* of that symlink ever changed, which is incorrect.
Unfortunately using os.lstat() leads to a different problem: if X
depends on Y and Y is a symlink to Z, then X would not be rebuilt when
Z changes, which is clearly wrong.
The fix is twofold:
1. read_stamp() should change on changes to both the link itself,
*and* the target of the link.
2. We shouldn't mark a target as overridden under so many situations.
We'll use *only* the primary mtime of the os.lstat(), not all the
other bits in the stamp.
Step 2 fixes a few other false positives also. For example, if you
'cp -a' a whole tree to another location, the st_ino of all the targets
will change, which would trigger a mass of "manual override" warnings.
Although a change in inode is sufficient to count an input as having
changed (just to be extra safe), it should *not* be considered a manual
override. Now we can distinguish between the two.
Because the stamp format has changed, update the SCHEMA_VER field. I
should have done this every other time I changed the stamp format, but
I forgot. Sorry. That leads to spurious "manually modified" warnings
after upgrading redo.
2018-11-21 07:19:20 -05:00
|
|
|
# update_stamp would call set_changed(); we don't want that,
|
|
|
|
|
# so only use read_stamp.
|
2010-12-11 02:17:51 -08:00
|
|
|
sf.stamp = sf.read_stamp()
|
|
|
|
|
else:
|
|
|
|
|
sf.csum = None
|
|
|
|
|
sf.update_stamp()
|
|
|
|
|
sf.set_changed()
|
2019-03-02 02:13:18 -05:00
|
|
|
# rv might have changed up above
|
|
|
|
|
if rv:
|
2019-01-18 00:06:18 +00:00
|
|
|
helpers.unlink(self.tmpname)
|
2010-12-10 02:58:13 -08:00
|
|
|
sf = self.sf
|
2010-12-10 20:53:31 -08:00
|
|
|
sf.set_failed()
|
2010-12-11 22:59:55 -08:00
|
|
|
sf.zap_deps2()
|
|
|
|
|
sf.save()
|
Use mkstemp() to create the stdout temp file, and simplify $3 path.
Previously, we'd try to put the stdout temp file in the same dir as the
target, if that dir exists. Otherwise we'd walk up the directory tree
looking for a good place. But this would go wrong if the directory we
chose got *deleted* during the run of the .do file.
Instead, we switch to an entirely new design: we use mkstemp() to
generate a temp file in the standard temp file location (probably
/tmp), then open it and immediately delete it, so the .do file can't
cause any unexpected behaviour. After the .do file exits, we use our
still-open fd to the stdout file to read the content back out.
In the old implementation, we also put the $3 in the "adjusted"
location that depended whether the target dir already existed, just for
consistency. But that was never necessary: we didn't create the $3
file, and if the .do script wants to write to $3, it should create the
target dir first anyway. So change it to *always* use a $3 temp
filename in the target dir, which is much simpler and so has fewer edge
cases.
Add t/202-del/deltest4 with some tests for all these edge cases.
Reported-by: Jeff Stearns <jeff.stearns@gmail.com>
2018-12-12 03:37:44 +00:00
|
|
|
outfile.close()
|
redo-log: add automated tests, and fix some path bugs revealed by them.
When a log for X was saying it wanted to refer to Y, we used a relative
path, but it was sometimes relative to the wrong starting location, so
redo-log couldn't find it later.
Two examples:
- if default.o.do is handling builds for a/b/x.o, and default.o.do
does 'redo a/b/x.h', the log for x.o should refer to ./x.h, not
a/b/x.h.
- if foo.do is handling builds for foo, and it does
"cd a/b && redo x", the log for foo should refer to a/b/x, not just
x.
2018-11-19 17:09:40 -05:00
|
|
|
meta('done', '%d %s' % (rv, state.target_relpath(self.t)))
|
2010-11-22 04:40:54 -08:00
|
|
|
return rv
|
2010-11-21 23:33:11 -08:00
|
|
|
|
2018-12-11 02:57:29 +00:00
|
|
|
def _finalize(self, rv):
|
|
|
|
|
"""After a target is built, report completion and unlock.
|
|
|
|
|
|
|
|
|
|
This is run in the *parent* process.
|
|
|
|
|
Note: you usually need to call _record_new_state() first.
|
|
|
|
|
"""
|
2010-11-22 03:21:17 -08:00
|
|
|
try:
|
|
|
|
|
self.donefunc(self.t, rv)
|
2018-12-02 23:15:37 -05:00
|
|
|
assert self.lock.owned
|
2010-11-22 03:21:17 -08:00
|
|
|
finally:
|
|
|
|
|
self.lock.unlock()
|
2010-11-21 23:33:11 -08:00
|
|
|
|
|
|
|
|
|
2018-12-11 02:57:29 +00:00
|
|
|
def run(targets, shouldbuildfunc):
|
|
|
|
|
"""Build the given list of targets, if necessary.
|
|
|
|
|
|
|
|
|
|
Builds in parallel using whatever jobserver tokens can be obtained.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
targets: a list of target filenames to consider building.
|
|
|
|
|
shouldbuildfunc: a function(target) which determines whether the given
|
|
|
|
|
target needs to be built, as of the time it is called.
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
0 if all necessary targets returned exit code zero; nonzero otherwise.
|
|
|
|
|
"""
|
2010-11-19 07:21:09 -08:00
|
|
|
retcode = [0] # a list so that it can be reassigned from done()
|
2018-12-05 01:07:16 -05:00
|
|
|
if env.v.SHUFFLE:
|
2010-12-09 04:54:40 -08:00
|
|
|
import random
|
2010-11-19 07:21:09 -08:00
|
|
|
random.shuffle(targets)
|
|
|
|
|
|
|
|
|
|
locked = []
|
|
|
|
|
|
2018-12-11 02:57:29 +00:00
|
|
|
def job_exited(t, rv):
|
2010-11-19 07:21:09 -08:00
|
|
|
if rv:
|
|
|
|
|
retcode[0] = 1
|
2018-12-02 23:15:37 -05:00
|
|
|
|
2018-12-05 01:07:16 -05:00
|
|
|
if env.v.TARGET and not env.v.UNLOCKED:
|
|
|
|
|
me = os.path.join(env.v.STARTDIR,
|
|
|
|
|
os.path.join(env.v.PWD, env.v.TARGET))
|
redo-log: prioritize the "foreground" process.
When running a parallel build, redo-log -f (which is auto-started by
redo) tries to traverse through the logs depth first, in the order
parent processes started subprocesses. This works pretty well, but if
its dependencies are locked, a process might have to give up its
jobserver token while other stuff builds its dependencies. After the
dependency finishes, the parent might not be able to get a token for
quite some time, and the logs will appear to stop.
To prevent this from happening, we can instantiate up to one "cheater"
token, only in the foreground process (the one locked by redo-log -f),
which will allow it to continue running, albeit a bit slowly (since it
only has one token out of possibly many). When the process finishes,
we then destroy the fake token. It gets a little complicated; see
explanation at the top of jwack.py.
2018-11-17 04:32:09 -05:00
|
|
|
myfile = state.File(name=me)
|
|
|
|
|
selflock = state.Lock(state.LOG_LOCK_MAGIC + myfile.id)
|
|
|
|
|
else:
|
|
|
|
|
selflock = myfile = me = None
|
2018-12-02 23:15:37 -05:00
|
|
|
|
2019-03-02 03:09:42 -05:00
|
|
|
for t in targets:
|
|
|
|
|
if '\n' in t:
|
|
|
|
|
err('%r: filenames containing newlines are not allowed.\n' % t)
|
|
|
|
|
return 204
|
|
|
|
|
|
redo-log: prioritize the "foreground" process.
When running a parallel build, redo-log -f (which is auto-started by
redo) tries to traverse through the logs depth first, in the order
parent processes started subprocesses. This works pretty well, but if
its dependencies are locked, a process might have to give up its
jobserver token while other stuff builds its dependencies. After the
dependency finishes, the parent might not be able to get a token for
quite some time, and the logs will appear to stop.
To prevent this from happening, we can instantiate up to one "cheater"
token, only in the foreground process (the one locked by redo-log -f),
which will allow it to continue running, albeit a bit slowly (since it
only has one token out of possibly many). When the process finishes,
we then destroy the fake token. It gets a little complicated; see
explanation at the top of jwack.py.
2018-11-17 04:32:09 -05:00
|
|
|
def cheat():
|
2018-12-02 23:15:37 -05:00
|
|
|
if not selflock:
|
|
|
|
|
return 0
|
redo-log: prioritize the "foreground" process.
When running a parallel build, redo-log -f (which is auto-started by
redo) tries to traverse through the logs depth first, in the order
parent processes started subprocesses. This works pretty well, but if
its dependencies are locked, a process might have to give up its
jobserver token while other stuff builds its dependencies. After the
dependency finishes, the parent might not be able to get a token for
quite some time, and the logs will appear to stop.
To prevent this from happening, we can instantiate up to one "cheater"
token, only in the foreground process (the one locked by redo-log -f),
which will allow it to continue running, albeit a bit slowly (since it
only has one token out of possibly many). When the process finishes,
we then destroy the fake token. It gets a little complicated; see
explanation at the top of jwack.py.
2018-11-17 04:32:09 -05:00
|
|
|
selflock.trylock()
|
|
|
|
|
if not selflock.owned:
|
|
|
|
|
# redo-log already owns it: let's cheat.
|
|
|
|
|
# Give ourselves one extra token so that the "foreground" log
|
|
|
|
|
# can always make progress.
|
|
|
|
|
return 1
|
|
|
|
|
else:
|
|
|
|
|
# redo-log isn't watching us (yet)
|
|
|
|
|
selflock.unlock()
|
|
|
|
|
return 0
|
2010-11-19 07:21:09 -08:00
|
|
|
|
2010-11-21 22:46:20 -08:00
|
|
|
# In the first cycle, we just build as much as we can without worrying
|
|
|
|
|
# about any lock contention. If someone else has it locked, we move on.
|
2010-12-14 02:19:08 -08:00
|
|
|
seen = {}
|
2011-02-21 03:55:18 -08:00
|
|
|
lock = None
|
2010-11-19 07:21:09 -08:00
|
|
|
for t in targets:
|
2018-11-03 03:36:13 -04:00
|
|
|
if not t:
|
|
|
|
|
err('cannot build the empty target ("").\n')
|
|
|
|
|
retcode[0] = 204
|
|
|
|
|
break
|
2018-12-02 23:15:37 -05:00
|
|
|
assert state.is_flushed()
|
2010-12-14 02:19:08 -08:00
|
|
|
if t in seen:
|
|
|
|
|
continue
|
|
|
|
|
seen[t] = 1
|
2018-12-04 23:20:14 -05:00
|
|
|
if not jobserver.has_token():
|
2010-12-09 05:53:30 -08:00
|
|
|
state.commit()
|
2018-12-04 23:20:14 -05:00
|
|
|
jobserver.ensure_token_or_cheat(t, cheat)
|
2018-12-05 01:07:16 -05:00
|
|
|
if retcode[0] and not env.v.KEEP_GOING:
|
2010-11-21 07:10:48 -08:00
|
|
|
break
|
2010-12-09 03:01:26 -08:00
|
|
|
if not state.check_sane():
|
|
|
|
|
err('.redo directory disappeared; cannot continue.\n')
|
2010-11-22 03:34:37 -08:00
|
|
|
retcode[0] = 205
|
|
|
|
|
break
|
2010-12-10 02:58:13 -08:00
|
|
|
f = state.File(name=t)
|
|
|
|
|
lock = state.Lock(f.id)
|
2018-12-05 01:07:16 -05:00
|
|
|
if env.v.UNLOCKED:
|
The second half of redo-stamp: out-of-order building.
If a depends on b depends on c, and c is dirty but b uses redo-stamp
checksums, then 'redo-ifchange a' is indeterminate: we won't know if we need
to run a.do unless we first build b, but the script that *normally* runs
'redo-ifchange b' is a.do, and we don't want to run that yet, because we
don't know for sure if b is dirty, and we shouldn't build a unless one of
its dependencies is dirty. Eek!
Luckily, there's a safe solution. If we *know* a is dirty - eg. because
a.do or one of its children has definitely changed - then we can just run
a.do immediately and there's no problem, even if b is indeterminate, because
we were going to run a.do anyhow.
If a's dependencies are *not* definitely dirty, and all we have is
indeterminate ones like b, then that means a's build process *hasn't
changed*, which means its tree of dependencies still includes b, which means
we can deduce that if we *did* run a.do, it would end up running b.do.
Since we know that anyhow, we can safely just run b.do, which will either
b.set_checked() or b.set_changed(). Once that's done, we can re-parse a's
dependencies and this time conclusively tell if it needs to be redone or
not. Even if it does, b is already up-to-date, so the 'redo-ifchange b'
line in a.do will be fast.
...now take all the above and do it recursively to handle nested
dependencies, etc, and you're done.
2010-12-11 04:40:05 -08:00
|
|
|
lock.owned = True
|
|
|
|
|
else:
|
|
|
|
|
lock.trylock()
|
2010-11-19 07:21:09 -08:00
|
|
|
if not lock.owned:
|
redo-log: add automated tests, and fix some path bugs revealed by them.
When a log for X was saying it wanted to refer to Y, we used a relative
path, but it was sometimes relative to the wrong starting location, so
redo-log couldn't find it later.
Two examples:
- if default.o.do is handling builds for a/b/x.o, and default.o.do
does 'redo a/b/x.h', the log for x.o should refer to ./x.h, not
a/b/x.h.
- if foo.do is handling builds for foo, and it does
"cd a/b && redo x", the log for foo should refer to a/b/x, not just
x.
2018-11-19 17:09:40 -05:00
|
|
|
meta('locked', state.target_relpath(t))
|
2018-12-02 23:15:37 -05:00
|
|
|
locked.append((f.id, t, f.name))
|
2010-11-19 07:21:09 -08:00
|
|
|
else:
|
2018-10-13 01:30:42 -04:00
|
|
|
# We had to create f before we had a lock, because we need f.id
|
|
|
|
|
# to make the lock. But someone may have updated the state
|
|
|
|
|
# between then and now.
|
|
|
|
|
# FIXME: separate obtaining the fid from creating the File.
|
|
|
|
|
# FIXME: maybe integrate locking into the File object?
|
|
|
|
|
f.refresh()
|
2018-12-11 02:57:29 +00:00
|
|
|
_BuildJob(t, f, lock,
|
|
|
|
|
shouldbuildfunc=shouldbuildfunc,
|
|
|
|
|
donefunc=job_exited).start()
|
2018-10-06 04:36:24 -04:00
|
|
|
state.commit()
|
2018-12-02 23:15:37 -05:00
|
|
|
assert state.is_flushed()
|
2015-01-15 16:13:35 -05:00
|
|
|
lock = None
|
2010-11-21 22:46:20 -08:00
|
|
|
|
2010-12-14 02:19:08 -08:00
|
|
|
del lock
|
|
|
|
|
|
2010-11-21 22:46:20 -08:00
|
|
|
# Now we've built all the "easy" ones. Go back and just wait on the
|
2010-12-10 04:31:22 -08:00
|
|
|
# remaining ones one by one. There's no reason to do it any more
|
|
|
|
|
# efficiently, because if these targets were previously locked, that
|
|
|
|
|
# means someone else was building them; thus, we probably won't need to
|
|
|
|
|
# do anything. The only exception is if we're invoked as redo instead
|
|
|
|
|
# of redo-ifchange; then we have to redo it even if someone else already
|
|
|
|
|
# did. But that should be rare.
|
2018-12-04 23:20:14 -05:00
|
|
|
while locked or jobserver.running():
|
2010-12-09 05:53:30 -08:00
|
|
|
state.commit()
|
2018-12-04 23:20:14 -05:00
|
|
|
jobserver.wait_all()
|
jobserver: don't release the very last token in wait_all().
After waiting for children to exit, we would release our own token, and
then the caller would immediately try to obtain a token again. This
accounted for tokens correctly, but would pass tokens around the call
tree in unexpected ways.
For example, imagine we had only one token. We call 'redo a1 a2', and
a1 calls 'redo b1 b2', and b1 calls 'redo c1'. When c1 exits, it
releases its token, then tries to re-acquire it before exiting. This
also includes 'redo b1 b2' and 'redo a1 a2' in the race for the token,
which means b1 might get suspended while *either* a2 or b2 starts
running.
This never caused a deadlock, even if a2 or b2 depends on b1, because
if they tried to build b1, they would notice it is locked, give up
their token, and wait for the lock. c1 (and then b1) could then obtain
the token and immediately terminate, allowing progress to continue.
But this is not really the way we expect things to happen. "Obviously"
what we want here is a straightforward stack unwinding: c1 should finish,
then b1, then b2, then a1, then b2.
The not-very-obvious symptom of this bug is that redo's unit tests
seemed to run in the wrong order when using -j1 --no-log. (--log would
hide the problem by rearranging logs back into the right order!)
2018-12-31 16:53:13 -05:00
|
|
|
assert jobserver._mytokens <= 1 # pylint: disable=protected-access
|
2018-12-04 23:20:14 -05:00
|
|
|
jobserver.ensure_token_or_cheat('self', cheat)
|
2010-11-21 23:33:11 -08:00
|
|
|
# at this point, we don't have any children holding any tokens, so
|
|
|
|
|
# it's okay to block below.
|
2018-12-05 01:07:16 -05:00
|
|
|
if retcode[0] and not env.v.KEEP_GOING:
|
2010-11-21 07:10:48 -08:00
|
|
|
break
|
2010-11-19 07:21:09 -08:00
|
|
|
if locked:
|
2010-12-09 03:01:26 -08:00
|
|
|
if not state.check_sane():
|
|
|
|
|
err('.redo directory disappeared; cannot continue.\n')
|
2010-11-22 03:34:37 -08:00
|
|
|
retcode[0] = 205
|
|
|
|
|
break
|
2018-12-02 23:15:37 -05:00
|
|
|
fid, t, _ = locked.pop(0)
|
2010-12-10 02:58:13 -08:00
|
|
|
lock = state.Lock(fid)
|
2018-10-29 07:21:12 +00:00
|
|
|
backoff = 0.01
|
2010-12-10 04:31:22 -08:00
|
|
|
lock.trylock()
|
2010-12-10 23:04:46 -08:00
|
|
|
while not lock.owned:
|
2018-10-29 07:21:12 +00:00
|
|
|
# Don't spin with 100% CPU while we fight for the lock.
|
|
|
|
|
import random
|
|
|
|
|
time.sleep(random.random() * min(backoff, 1.0))
|
|
|
|
|
backoff *= 2
|
redo-log: prioritize the "foreground" process.
When running a parallel build, redo-log -f (which is auto-started by
redo) tries to traverse through the logs depth first, in the order
parent processes started subprocesses. This works pretty well, but if
its dependencies are locked, a process might have to give up its
jobserver token while other stuff builds its dependencies. After the
dependency finishes, the parent might not be able to get a token for
quite some time, and the logs will appear to stop.
To prevent this from happening, we can instantiate up to one "cheater"
token, only in the foreground process (the one locked by redo-log -f),
which will allow it to continue running, albeit a bit slowly (since it
only has one token out of possibly many). When the process finishes,
we then destroy the fake token. It gets a little complicated; see
explanation at the top of jwack.py.
2018-11-17 04:32:09 -05:00
|
|
|
# after printing this line, redo-log will recurse into t,
|
|
|
|
|
# whether it's us building it, or someone else.
|
redo-log: add automated tests, and fix some path bugs revealed by them.
When a log for X was saying it wanted to refer to Y, we used a relative
path, but it was sometimes relative to the wrong starting location, so
redo-log couldn't find it later.
Two examples:
- if default.o.do is handling builds for a/b/x.o, and default.o.do
does 'redo a/b/x.h', the log for x.o should refer to ./x.h, not
a/b/x.h.
- if foo.do is handling builds for foo, and it does
"cd a/b && redo x", the log for foo should refer to a/b/x, not just
x.
2018-11-19 17:09:40 -05:00
|
|
|
meta('waiting', state.target_relpath(t))
|
2016-11-27 23:35:28 -08:00
|
|
|
try:
|
Cyclic dependency checker: don't give up token in common case.
The way the code was written, we'd give up our token, detect a cyclic
dependency, and then try to get our token back before exiting. Even
with -j1, the temporary token release allowed any parent up the tree to
continue running jobs, so it would take an arbitrary amount of time
before we could exit (and report an error code to the parent).
There was no visible symptom of this except that, with -j1, t/355-deps-cyclic
would not finish until some of the later tests finished, which was
surprising.
To fix it, let's just check for a cyclic dependency first, then release
the token only once we're sure things are sane.
2018-11-13 06:54:31 -05:00
|
|
|
lock.check()
|
2018-12-05 00:18:07 -05:00
|
|
|
except cycles.CyclicDependencyError:
|
2016-11-27 23:35:28 -08:00
|
|
|
err('cyclic dependency while building %s\n' % _nice(t))
|
|
|
|
|
retcode[0] = 208
|
|
|
|
|
return retcode[0]
|
Cyclic dependency checker: don't give up token in common case.
The way the code was written, we'd give up our token, detect a cyclic
dependency, and then try to get our token back before exiting. Even
with -j1, the temporary token release allowed any parent up the tree to
continue running jobs, so it would take an arbitrary amount of time
before we could exit (and report an error code to the parent).
There was no visible symptom of this except that, with -j1, t/355-deps-cyclic
would not finish until some of the later tests finished, which was
surprising.
To fix it, let's just check for a cyclic dependency first, then release
the token only once we're sure things are sane.
2018-11-13 06:54:31 -05:00
|
|
|
# this sequence looks a little silly, but the idea is to
|
|
|
|
|
# give up our personal token while we wait for the lock to
|
redo-log: prioritize the "foreground" process.
When running a parallel build, redo-log -f (which is auto-started by
redo) tries to traverse through the logs depth first, in the order
parent processes started subprocesses. This works pretty well, but if
its dependencies are locked, a process might have to give up its
jobserver token while other stuff builds its dependencies. After the
dependency finishes, the parent might not be able to get a token for
quite some time, and the logs will appear to stop.
To prevent this from happening, we can instantiate up to one "cheater"
token, only in the foreground process (the one locked by redo-log -f),
which will allow it to continue running, albeit a bit slowly (since it
only has one token out of possibly many). When the process finishes,
we then destroy the fake token. It gets a little complicated; see
explanation at the top of jwack.py.
2018-11-17 04:32:09 -05:00
|
|
|
# be released; but we should never run ensure_token() while
|
Cyclic dependency checker: don't give up token in common case.
The way the code was written, we'd give up our token, detect a cyclic
dependency, and then try to get our token back before exiting. Even
with -j1, the temporary token release allowed any parent up the tree to
continue running jobs, so it would take an arbitrary amount of time
before we could exit (and report an error code to the parent).
There was no visible symptom of this except that, with -j1, t/355-deps-cyclic
would not finish until some of the later tests finished, which was
surprising.
To fix it, let's just check for a cyclic dependency first, then release
the token only once we're sure things are sane.
2018-11-13 06:54:31 -05:00
|
|
|
# holding a lock, or we could cause deadlocks.
|
2018-12-04 23:20:14 -05:00
|
|
|
jobserver.release_mine()
|
Cyclic dependency checker: don't give up token in common case.
The way the code was written, we'd give up our token, detect a cyclic
dependency, and then try to get our token back before exiting. Even
with -j1, the temporary token release allowed any parent up the tree to
continue running jobs, so it would take an arbitrary amount of time
before we could exit (and report an error code to the parent).
There was no visible symptom of this except that, with -j1, t/355-deps-cyclic
would not finish until some of the later tests finished, which was
surprising.
To fix it, let's just check for a cyclic dependency first, then release
the token only once we're sure things are sane.
2018-11-13 06:54:31 -05:00
|
|
|
lock.waitlock()
|
redo-log: prioritize the "foreground" process.
When running a parallel build, redo-log -f (which is auto-started by
redo) tries to traverse through the logs depth first, in the order
parent processes started subprocesses. This works pretty well, but if
its dependencies are locked, a process might have to give up its
jobserver token while other stuff builds its dependencies. After the
dependency finishes, the parent might not be able to get a token for
quite some time, and the logs will appear to stop.
To prevent this from happening, we can instantiate up to one "cheater"
token, only in the foreground process (the one locked by redo-log -f),
which will allow it to continue running, albeit a bit slowly (since it
only has one token out of possibly many). When the process finishes,
we then destroy the fake token. It gets a little complicated; see
explanation at the top of jwack.py.
2018-11-17 04:32:09 -05:00
|
|
|
# now t is definitely free, so we get to decide whether
|
|
|
|
|
# to build it.
|
2010-12-10 23:04:46 -08:00
|
|
|
lock.unlock()
|
2018-12-04 23:20:14 -05:00
|
|
|
jobserver.ensure_token_or_cheat(t, cheat)
|
2010-12-10 23:04:46 -08:00
|
|
|
lock.trylock()
|
2018-12-02 23:15:37 -05:00
|
|
|
assert lock.owned
|
redo-log: add automated tests, and fix some path bugs revealed by them.
When a log for X was saying it wanted to refer to Y, we used a relative
path, but it was sometimes relative to the wrong starting location, so
redo-log couldn't find it later.
Two examples:
- if default.o.do is handling builds for a/b/x.o, and default.o.do
does 'redo a/b/x.h', the log for x.o should refer to ./x.h, not
a/b/x.h.
- if foo.do is handling builds for foo, and it does
"cd a/b && redo x", the log for foo should refer to a/b/x, not just
x.
2018-11-19 17:09:40 -05:00
|
|
|
meta('unlocked', state.target_relpath(t))
|
2010-12-10 20:53:31 -08:00
|
|
|
if state.File(name=t).is_failed():
|
2010-11-21 03:34:32 -08:00
|
|
|
err('%s: failed in another thread\n' % _nice(t))
|
2010-11-19 07:21:09 -08:00
|
|
|
retcode[0] = 2
|
|
|
|
|
lock.unlock()
|
|
|
|
|
else:
|
2018-12-11 02:57:29 +00:00
|
|
|
_BuildJob(t, state.File(fid=fid), lock,
|
|
|
|
|
shouldbuildfunc=shouldbuildfunc,
|
|
|
|
|
donefunc=job_exited).start()
|
2015-01-15 16:13:35 -05:00
|
|
|
lock = None
|
2010-12-09 02:44:33 -08:00
|
|
|
state.commit()
|
2010-11-19 07:21:09 -08:00
|
|
|
return retcode[0]
|