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.
24 lines
471 B
Python
Executable file
24 lines
471 B
Python
Executable file
#!/usr/bin/python
|
|
import sys, os
|
|
import state
|
|
from helpers import err
|
|
|
|
if len(sys.argv[1:]) < 2:
|
|
err('%s: at least 2 arguments expected.\n' % sys.argv[0])
|
|
sys.exit(1)
|
|
|
|
target = sys.argv[1]
|
|
deps = sys.argv[2:]
|
|
|
|
me = state.File(name=target)
|
|
|
|
argv = ['redo'] + deps
|
|
rv = os.spawnvp(os.P_WAIT, argv[0], argv)
|
|
if rv:
|
|
sys.exit(rv)
|
|
|
|
os.environ['REDO_UNLOCKED'] = '1'
|
|
argv = ['redo-ifchange', target]
|
|
rv = os.spawnvp(os.P_WAIT, argv[0], argv)
|
|
if rv:
|
|
sys.exit(rv)
|