Don't wipe the timestamp when a target fails to redo.
It's really a separate condition. And since we're not removing the target *file* in case of error - we update it atomically, and keeping it is better than losing it - there's no reason to wipe the timestamp in that case either. However, we do need to know that the build failed, so that anybody else (especially in a parallel build) who looks at that target knows that it died. So add a separate flag just for that.
This commit is contained in:
parent
16bebd21b5
commit
0126f6be1e
4 changed files with 39 additions and 17 deletions
20
builder.py
20
builder.py
|
|
@ -42,6 +42,12 @@ def _try_stat(filename):
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
class ImmediateReturn(Exception):
|
||||||
|
def __init__(self, rv):
|
||||||
|
Exception.__init__(self, "immediate return with exit code %d" % rv)
|
||||||
|
self.rv = rv
|
||||||
|
|
||||||
|
|
||||||
class BuildJob:
|
class BuildJob:
|
||||||
def __init__(self, t, sf, lock, shouldbuildfunc, donefunc):
|
def __init__(self, t, sf, lock, shouldbuildfunc, donefunc):
|
||||||
self.t = t # original target name, not relative to vars.BASE
|
self.t = t # original target name, not relative to vars.BASE
|
||||||
|
|
@ -57,9 +63,12 @@ class BuildJob:
|
||||||
t = self.t
|
t = self.t
|
||||||
sf = self.sf
|
sf = self.sf
|
||||||
tmpname = self.tmpname
|
tmpname = self.tmpname
|
||||||
if not self.shouldbuildfunc(t):
|
try:
|
||||||
# target doesn't need to be built; skip the whole task
|
if not self.shouldbuildfunc(t):
|
||||||
return self._after2(0)
|
# target doesn't need to be built; skip the whole task
|
||||||
|
return self._after2(0)
|
||||||
|
except ImmediateReturn, e:
|
||||||
|
return self._after2(e.rv)
|
||||||
if (os.path.exists(t) and not os.path.exists(t + '/.')
|
if (os.path.exists(t) and not os.path.exists(t + '/.')
|
||||||
and not sf.is_generated):
|
and not sf.is_generated):
|
||||||
# an existing source file that was not generated by us.
|
# an existing source file that was not generated by us.
|
||||||
|
|
@ -174,8 +183,7 @@ class BuildJob:
|
||||||
else:
|
else:
|
||||||
unlink(tmpname)
|
unlink(tmpname)
|
||||||
sf = self.sf
|
sf = self.sf
|
||||||
sf.stamp = None
|
sf.set_failed()
|
||||||
sf.set_changed()
|
|
||||||
sf.save()
|
sf.save()
|
||||||
f.close()
|
f.close()
|
||||||
if rv != 0:
|
if rv != 0:
|
||||||
|
|
@ -259,7 +267,7 @@ def main(targets, shouldbuildfunc):
|
||||||
assert(lock.owned)
|
assert(lock.owned)
|
||||||
if vars.DEBUG_LOCKS:
|
if vars.DEBUG_LOCKS:
|
||||||
log('%s (...unlocked!)\n' % _nice(t))
|
log('%s (...unlocked!)\n' % _nice(t))
|
||||||
if state.File(name=t).stamp == None:
|
if state.File(name=t).is_failed():
|
||||||
err('%s: failed in another thread\n' % _nice(t))
|
err('%s: failed in another thread\n' % _nice(t))
|
||||||
retcode[0] = 2
|
retcode[0] = 2
|
||||||
lock.unlock()
|
lock.unlock()
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,8 @@ def dirty_deps(f, depth, max_changed):
|
||||||
|
|
||||||
def should_build(t):
|
def should_build(t):
|
||||||
f = state.File(name=t)
|
f = state.File(name=t)
|
||||||
|
if f.is_failed():
|
||||||
|
raise builder.ImmediateReturn(32)
|
||||||
return dirty_deps(f, depth = '', max_changed = vars.RUNID)
|
return dirty_deps(f, depth = '', max_changed = vars.RUNID)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
29
state.py
29
state.py
|
|
@ -59,6 +59,7 @@ def db():
|
||||||
" is_generated int, "
|
" is_generated int, "
|
||||||
" checked_runid int, "
|
" checked_runid int, "
|
||||||
" changed_runid int, "
|
" changed_runid int, "
|
||||||
|
" failed_runid int, "
|
||||||
" stamp, "
|
" stamp, "
|
||||||
" csum)")
|
" csum)")
|
||||||
_db.execute("create table Deps "
|
_db.execute("create table Deps "
|
||||||
|
|
@ -134,18 +135,19 @@ def relpath(t, base):
|
||||||
class File(object):
|
class File(object):
|
||||||
# use this mostly to avoid accidentally assigning to typos
|
# use this mostly to avoid accidentally assigning to typos
|
||||||
__slots__ = ['id', 'name', 'is_generated',
|
__slots__ = ['id', 'name', 'is_generated',
|
||||||
'checked_runid', 'changed_runid',
|
'checked_runid', 'changed_runid', 'failed_runid',
|
||||||
'stamp', 'csum']
|
'stamp', 'csum']
|
||||||
|
|
||||||
def _init_from_cols(self, cols):
|
def _init_from_cols(self, cols):
|
||||||
(self.id, self.name, self.is_generated,
|
(self.id, self.name, self.is_generated,
|
||||||
self.checked_runid, self.changed_runid,
|
self.checked_runid, self.changed_runid, self.failed_runid,
|
||||||
self.stamp, self.csum) = cols
|
self.stamp, self.csum) = cols
|
||||||
|
|
||||||
def __init__(self, id=None, name=None, cols=None):
|
def __init__(self, id=None, name=None, cols=None):
|
||||||
if cols:
|
if cols:
|
||||||
return self._init_from_cols(cols)
|
return self._init_from_cols(cols)
|
||||||
q = ('select rowid, name, is_generated, checked_runid, changed_runid, '
|
q = ('select rowid, name, is_generated, '
|
||||||
|
' checked_runid, changed_runid, failed_runid, '
|
||||||
' stamp, csum '
|
' stamp, csum '
|
||||||
' from Files ')
|
' from Files ')
|
||||||
if id != None:
|
if id != None:
|
||||||
|
|
@ -175,11 +177,12 @@ class File(object):
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
_write('update Files set '
|
_write('update Files set '
|
||||||
' is_generated=?, checked_runid=?, changed_runid=?, '
|
' is_generated=?, '
|
||||||
|
' checked_runid=?, changed_runid=?, failed_runid=?, '
|
||||||
' stamp=?, csum=? '
|
' stamp=?, csum=? '
|
||||||
' where rowid=?',
|
' where rowid=?',
|
||||||
[self.is_generated,
|
[self.is_generated,
|
||||||
self.checked_runid, self.changed_runid,
|
self.checked_runid, self.changed_runid, self.failed_runid,
|
||||||
self.stamp, self.csum,
|
self.stamp, self.csum,
|
||||||
self.id])
|
self.id])
|
||||||
|
|
||||||
|
|
@ -190,6 +193,10 @@ class File(object):
|
||||||
debug2('BUILT: %r (%r)\n' % (self.name, self.stamp))
|
debug2('BUILT: %r (%r)\n' % (self.name, self.stamp))
|
||||||
self.changed_runid = vars.RUNID
|
self.changed_runid = vars.RUNID
|
||||||
|
|
||||||
|
def set_failed(self):
|
||||||
|
debug2('FAILED: %r\n' % self.name)
|
||||||
|
self.failed_runid = vars.RUNID
|
||||||
|
|
||||||
def set_static(self):
|
def set_static(self):
|
||||||
self.update_stamp()
|
self.update_stamp()
|
||||||
|
|
||||||
|
|
@ -200,15 +207,19 @@ class File(object):
|
||||||
self.stamp = newstamp
|
self.stamp = newstamp
|
||||||
self.set_changed()
|
self.set_changed()
|
||||||
|
|
||||||
def is_changed(self):
|
|
||||||
return self.changed_runid and self.changed_runid >= vars.RUNID
|
|
||||||
|
|
||||||
def is_checked(self):
|
def is_checked(self):
|
||||||
return self.checked_runid and self.checked_runid >= vars.RUNID
|
return self.checked_runid and self.checked_runid >= vars.RUNID
|
||||||
|
|
||||||
|
def is_changed(self):
|
||||||
|
return self.changed_runid and self.changed_runid >= vars.RUNID
|
||||||
|
|
||||||
|
def is_failed(self):
|
||||||
|
return self.failed_runid and self.failed_runid >= vars.RUNID
|
||||||
|
|
||||||
def deps(self):
|
def deps(self):
|
||||||
q = ('select Deps.mode, Deps.source, '
|
q = ('select Deps.mode, Deps.source, '
|
||||||
' name, is_generated, checked_runid, changed_runid, '
|
' name, is_generated, '
|
||||||
|
' checked_runid, changed_runid, failed_runid, '
|
||||||
' stamp, csum '
|
' stamp, csum '
|
||||||
' from Files '
|
' from Files '
|
||||||
' join Deps on Files.rowid = Deps.source '
|
' join Deps on Files.rowid = Deps.source '
|
||||||
|
|
|
||||||
|
|
@ -4,5 +4,6 @@
|
||||||
echo ".timeout 5000"
|
echo ".timeout 5000"
|
||||||
echo "pragma synchronous = off;"
|
echo "pragma synchronous = off;"
|
||||||
echo "update Files set checked_runid=null, " \
|
echo "update Files set checked_runid=null, " \
|
||||||
" changed_runid=changed_runid-1;"
|
" changed_runid=changed_runid-1, " \
|
||||||
|
" failed_runid=null;"
|
||||||
) | sqlite3 "$REDO_BASE/.redo/db.sqlite3"
|
) | sqlite3 "$REDO_BASE/.redo/db.sqlite3"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue