2018-12-02 23:15:37 -05:00
|
|
|
import os
|
2018-12-05 00:18:07 -05:00
|
|
|
import cycles, env, state
|
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
|
|
|
from logs import debug
|
2010-12-19 02:31:40 -08:00
|
|
|
|
|
|
|
|
CLEAN = 0
|
|
|
|
|
DIRTY = 1
|
|
|
|
|
|
2010-12-19 03:39:37 -08:00
|
|
|
def isdirty(f, depth, max_changed,
|
2018-10-30 23:03:46 -04:00
|
|
|
already_checked,
|
2010-12-19 03:39:37 -08:00
|
|
|
is_checked=state.File.is_checked,
|
2018-12-02 16:53:05 -05:00
|
|
|
set_checked=state.File.set_checked_save,
|
|
|
|
|
log_override=state.warn_override):
|
2018-10-30 23:03:46 -04:00
|
|
|
if f.id in already_checked:
|
2018-12-05 00:18:07 -05:00
|
|
|
raise cycles.CyclicDependencyError()
|
2018-10-30 23:03:46 -04:00
|
|
|
# make a copy of the list, so upon returning, our parent's copy
|
|
|
|
|
# is unaffected
|
|
|
|
|
already_checked = list(already_checked) + [f.id]
|
|
|
|
|
|
2018-12-05 01:07:16 -05:00
|
|
|
if env.v.DEBUG >= 1:
|
2018-12-02 23:15:37 -05:00
|
|
|
debug('%s?%s %r,%r\n'
|
|
|
|
|
% (depth, f.nicename(), f.is_generated, f.is_override))
|
2010-12-19 02:31:40 -08:00
|
|
|
|
|
|
|
|
if f.failed_runid:
|
|
|
|
|
debug('%s-- DIRTY (failed last time)\n' % depth)
|
|
|
|
|
return DIRTY
|
2018-12-02 23:15:37 -05:00
|
|
|
if f.changed_runid is None:
|
2010-12-19 02:31:40 -08:00
|
|
|
debug('%s-- DIRTY (never built)\n' % depth)
|
|
|
|
|
return DIRTY
|
|
|
|
|
if f.changed_runid > max_changed:
|
2018-12-02 23:15:37 -05:00
|
|
|
debug('%s-- DIRTY (built %d > %d; %d)\n'
|
2018-12-05 01:07:16 -05:00
|
|
|
% (depth, f.changed_runid, max_changed, env.v.RUNID))
|
2010-12-19 02:31:40 -08:00
|
|
|
return DIRTY # has been built more recently than parent
|
2010-12-19 03:39:37 -08:00
|
|
|
if is_checked(f):
|
2018-12-05 01:07:16 -05:00
|
|
|
if env.v.DEBUG >= 1:
|
2010-12-19 02:31:40 -08:00
|
|
|
debug('%s-- CLEAN (checked)\n' % depth)
|
|
|
|
|
return CLEAN # has already been checked during this session
|
|
|
|
|
if not f.stamp:
|
|
|
|
|
debug('%s-- DIRTY (no stamp)\n' % depth)
|
|
|
|
|
return DIRTY
|
|
|
|
|
|
|
|
|
|
newstamp = f.read_stamp()
|
|
|
|
|
if f.stamp != newstamp:
|
|
|
|
|
if newstamp == state.STAMP_MISSING:
|
|
|
|
|
debug('%s-- DIRTY (missing)\n' % depth)
|
{ood,sources,targets}: fix relative paths; turn missing targets into sources.
When we check dependencies and a previously-is_generated dependency
existed before, but no longer does, forget that it was is_generated.
This slightly improves the situation where as a project evolves, a file
that used to be a target gets removed, and then later is re-added as a
static source file. (It doesn't fix the other variant, where a file is
changed from target to source in a single atomic change, and is never
missing. That one will be trickier to handle.)
While adding a test for this behaviour, I discovered that redo-sources,
redo-targets, and redo-ood were reporting their output relative to
STARTDIR instead of relative to $PWD, so fix that too.
2018-11-23 18:20:32 -05:00
|
|
|
if f.stamp and f.is_generated:
|
|
|
|
|
# previously was stamped and generated, but suddenly missing.
|
|
|
|
|
# We can safely forget that it is/was a target; if someone
|
|
|
|
|
# does redo-ifchange on it and it doesn't exist, we'll mark
|
|
|
|
|
# it a target again, but if someone creates it by hand,
|
|
|
|
|
# it'll be a source. This should reduce false alarms when
|
|
|
|
|
# files change from targets to sources as a project evolves.
|
2018-12-02 16:53:05 -05:00
|
|
|
debug('%s converted target -> source %r\n' % (depth, f.id))
|
|
|
|
|
f.is_generated = f.failed_runid = 0
|
{ood,sources,targets}: fix relative paths; turn missing targets into sources.
When we check dependencies and a previously-is_generated dependency
existed before, but no longer does, forget that it was is_generated.
This slightly improves the situation where as a project evolves, a file
that used to be a target gets removed, and then later is re-added as a
static source file. (It doesn't fix the other variant, where a file is
changed from target to source in a single atomic change, and is never
missing. That one will be trickier to handle.)
While adding a test for this behaviour, I discovered that redo-sources,
redo-targets, and redo-ood were reporting their output relative to
STARTDIR instead of relative to $PWD, so fix that too.
2018-11-23 18:20:32 -05:00
|
|
|
f.save()
|
2018-12-02 16:53:05 -05:00
|
|
|
f.refresh()
|
|
|
|
|
assert not f.is_generated
|
2010-12-19 02:31:40 -08:00
|
|
|
else:
|
|
|
|
|
debug('%s-- DIRTY (mtime)\n' % depth)
|
|
|
|
|
if f.csum:
|
|
|
|
|
return [f]
|
|
|
|
|
else:
|
|
|
|
|
return DIRTY
|
|
|
|
|
|
|
|
|
|
must_build = []
|
2018-12-02 23:15:37 -05:00
|
|
|
for mode, f2 in f.deps():
|
2010-12-19 02:31:40 -08:00
|
|
|
dirty = CLEAN
|
|
|
|
|
if mode == 'c':
|
2018-12-05 01:07:16 -05:00
|
|
|
if os.path.exists(os.path.join(env.v.BASE, f2.name)):
|
2010-12-19 02:31:40 -08:00
|
|
|
debug('%s-- DIRTY (created)\n' % depth)
|
|
|
|
|
dirty = DIRTY
|
|
|
|
|
elif mode == 'm':
|
2018-12-02 23:15:37 -05:00
|
|
|
sub = isdirty(f2, depth=depth + ' ',
|
|
|
|
|
max_changed=max(f.changed_runid,
|
|
|
|
|
f.checked_runid),
|
2018-10-30 23:03:46 -04:00
|
|
|
already_checked=already_checked,
|
2018-12-02 16:53:05 -05:00
|
|
|
is_checked=is_checked,
|
|
|
|
|
set_checked=set_checked,
|
|
|
|
|
log_override=log_override)
|
2010-12-19 02:31:40 -08:00
|
|
|
if sub:
|
|
|
|
|
debug('%s-- DIRTY (sub)\n' % depth)
|
|
|
|
|
dirty = sub
|
|
|
|
|
else:
|
2018-12-02 23:15:37 -05:00
|
|
|
assert mode in ('c', 'm')
|
2010-12-19 02:31:40 -08:00
|
|
|
if not f.csum:
|
|
|
|
|
# f is a "normal" target: dirty f2 means f is instantly dirty
|
2015-05-06 17:56:14 -04:00
|
|
|
if dirty == DIRTY:
|
|
|
|
|
# f2 is definitely dirty, so f definitely needs to
|
|
|
|
|
# redo.
|
|
|
|
|
return DIRTY
|
2018-12-02 23:15:37 -05:00
|
|
|
elif isinstance(dirty, list):
|
2015-05-06 17:56:14 -04:00
|
|
|
# our child f2 might be dirty, but it's not sure yet. It's
|
|
|
|
|
# given us a list of targets we have to redo in order to
|
|
|
|
|
# be sure.
|
|
|
|
|
must_build += dirty
|
2010-12-19 02:31:40 -08:00
|
|
|
else:
|
|
|
|
|
# f is "checksummable": dirty f2 means f needs to redo,
|
|
|
|
|
# but f might turn out to be clean after that (ie. our parent
|
|
|
|
|
# might not be dirty).
|
|
|
|
|
if dirty == DIRTY:
|
|
|
|
|
# f2 is definitely dirty, so f definitely needs to
|
|
|
|
|
# redo. However, after that, f might turn out to be
|
|
|
|
|
# unchanged.
|
|
|
|
|
return [f]
|
2018-12-02 23:15:37 -05:00
|
|
|
elif isinstance(dirty, list):
|
2010-12-19 02:31:40 -08:00
|
|
|
# our child f2 might be dirty, but it's not sure yet. It's
|
|
|
|
|
# given us a list of targets we have to redo in order to
|
|
|
|
|
# be sure.
|
|
|
|
|
must_build += dirty
|
|
|
|
|
|
|
|
|
|
if must_build:
|
|
|
|
|
# f is *maybe* dirty because at least one of its children is maybe
|
|
|
|
|
# dirty. must_build has accumulated a list of "topmost" uncertain
|
|
|
|
|
# objects in the tree. If we build all those, we can then
|
|
|
|
|
# redo-ifchange f and it won't have any uncertainty next time.
|
|
|
|
|
return must_build
|
2015-05-06 17:56:14 -04:00
|
|
|
debug('%s-- CLEAN\n' % (depth,))
|
2010-12-19 02:31:40 -08:00
|
|
|
|
|
|
|
|
# if we get here, it's because the target is clean
|
|
|
|
|
if f.is_override:
|
2018-12-02 16:53:05 -05:00
|
|
|
log_override(f.name)
|
2010-12-19 03:39:37 -08:00
|
|
|
set_checked(f)
|
2010-12-19 02:31:40 -08:00
|
|
|
return CLEAN
|