Switch state.py to use sqlite3 instead of filesystem-based stamps.

It passes all tests when run serialized, but still gives weird errors
(OperationalError: database is locked) when run with -j5.  sqlite3 shouldn't
be barfing just because the database is locked, since the default timeout is
5 seconds, and it's dying *way* faster than that.
This commit is contained in:
Avery Pennarun 2010-12-07 02:17:22 -08:00
commit a62bd50d44
6 changed files with 220 additions and 157 deletions

View file

@ -20,10 +20,10 @@ def _find_do_file(t):
for dofile,basename,ext in _possible_do_files(t):
debug2('%s: %s ?\n' % (t, dofile))
if os.path.exists(dofile):
state.add_dep(t, 'm', dofile)
state.File(name=t).add_dep('m', dofile)
return dofile,basename,ext
else:
state.add_dep(t, 'c', dofile)
state.File(name=t).add_dep('c', dofile)
return None,None,None
@ -53,12 +53,13 @@ class BuildJob:
def start(self):
assert(self.lock.owned)
t = self.t
f = state.File(name=t)
tmpname = self.tmpname
if not self.shouldbuildfunc(t):
# target doesn't need to be built; skip the whole task
return self._after2(0)
if (os.path.exists(t) and not os.path.exists(t + '/.')
and not state.is_generated(t)):
and not f.is_generated):
# 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
@ -67,20 +68,21 @@ class BuildJob:
# FIXME: always refuse to redo any file that was modified outside
# of redo? That would make it easy for someone to override a
# file temporarily, and could be undone by deleting the file.
state.unmark_as_generated(t)
state.stamp_and_maybe_built(t)
debug2("-- static (%r)\n" % t)
f.set_static()
f.save()
return self._after2(0)
state.start(t)
f.zap_deps()
(dofile, basename, ext) = _find_do_file(t)
if not dofile:
if os.path.exists(t):
state.unmark_as_generated(t)
state.stamp_and_maybe_built(t)
f.is_generated = False
f.set_static()
f.save()
return self._after2(0)
else:
err('no rule to make %r\n' % t)
return self._after2(1)
state.stamp_and_maybe_built(dofile)
unlink(tmpname)
ffd = os.open(tmpname, os.O_CREAT|os.O_RDWR|os.O_EXCL, 0666)
close_on_exec(ffd, True)
@ -97,13 +99,19 @@ class BuildJob:
if vars.VERBOSE or vars.XTRACE: log_('\n')
log('%s\n' % _nice(t))
self.argv = argv
f.is_generated = True
f.save()
dof = state.File(name=dofile)
dof.set_static()
dof.save()
jwack.start_job(t, self._do_subproc, self._after)
def _do_subproc(self):
# 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
# than we started it in. So os.getcwd() might be != REDO_PWD right now.
# than we started it in. So os.getcwd() might be != REDO_PWD right
# now.
dn = os.path.dirname(self.t)
newp = os.path.realpath(dn)
os.environ['REDO_PWD'] = state.relpath(newp, vars.STARTDIR)
@ -153,11 +161,17 @@ class BuildJob:
os.rename(tmpname, t)
else:
unlink(tmpname)
state.built(t)
state.stamp(t)
sf = state.File(name=t)
sf.is_generated=True
sf.update_stamp()
sf.set_changed()
sf.save()
else:
unlink(tmpname)
state.unstamp(t)
sf = state.File(name=t)
sf.stamp = None
sf.set_changed()
sf.save()
f.close()
if rv != 0:
err('%s: exit code %d\n' % (_nice(t),rv))
@ -226,7 +240,7 @@ def main(targets, shouldbuildfunc):
assert(lock.owned)
if vars.DEBUG_LOCKS:
log('%s (...unlocked!)\n' % _nice(t))
if state.stamped(t) == None:
if state.File(name=t).stamp == None:
err('%s: failed in another thread\n' % _nice(t))
retcode[0] = 2
lock.unlock()