env and env_init: Eliminate weird auto-initialization of globals.

Merge the two files into env, and make each command explicitly call the
function that sets it up in the way that's needed for that command.

This means we can finally just import all the modules at the top of
each file, without worrying about import order.  Phew.

While we're here, remove the weird auto-appending-'all'-to-targets
feature in env.init().  Instead, do it explicitly, and only from redo and
redo-ifchange, only if is_toplevel and no other targets are given.
This commit is contained in:
Avery Pennarun 2018-12-05 01:07:16 -05:00
commit 9b6d1eeb6e
19 changed files with 267 additions and 256 deletions

View file

@ -6,7 +6,7 @@ from logs import debug2, err, warn, meta, check_tty
def _nice(t):
return state.relpath(t, env.STARTDIR)
return state.relpath(t, env.v.STARTDIR)
def _try_stat(filename):
@ -53,7 +53,7 @@ def start_stdin_log_reader(status, details, pretty, color,
os.dup2(w, 1)
os.dup2(w, 2)
os.close(w)
check_tty(sys.stderr, env.COLOR)
check_tty(sys.stderr, env.v.COLOR)
else:
# child
try:
@ -88,7 +88,7 @@ def start_stdin_log_reader(status, details, pretty, color,
def await_log_reader():
if not env.LOG:
if not env.v.LOG:
return
if log_reader_pid > 0:
# never actually close fd#1 or fd#2; insanity awaits.
@ -110,7 +110,7 @@ class ImmediateReturn(Exception):
class BuildJob(object):
def __init__(self, t, sf, lock, shouldbuildfunc, donefunc):
self.t = t # original target name, not relative to env.BASE
self.t = t # original target name, not relative to env.v.BASE
self.sf = sf
tmpbase = t
while not os.path.isdir(os.path.dirname(tmpbase) or '.'):
@ -140,7 +140,7 @@ class BuildJob(object):
except ImmediateReturn, e:
return self._after2(e.rv)
if env.NO_OOB or dirty == True: # pylint: disable=singleton-comparison
if env.v.NO_OOB or dirty == True: # pylint: disable=singleton-comparison
self._start_do()
else:
self._start_unlocked(dirty)
@ -198,16 +198,16 @@ class BuildJob(object):
# temp output file name
state.relpath(os.path.abspath(self.tmpname2), dodir),
]
if env.VERBOSE:
if env.v.VERBOSE:
argv[1] += 'v'
if env.XTRACE:
if env.v.XTRACE:
argv[1] += 'x'
firstline = open(os.path.join(dodir, dofile)).readline().strip()
if firstline.startswith('#!/'):
argv[0:2] = firstline[2:].split(' ')
# make sure to create the logfile *before* writing the meta() about it.
# that way redo-log won't trace into an obsolete logfile.
if env.LOG:
if env.v.LOG:
open(state.logname(self.sf.id), 'w')
# FIXME: put these variables somewhere else, instead of on-the-fly
# extending this class!
@ -236,13 +236,13 @@ class BuildJob(object):
# grab a lock.
here = os.getcwd()
def _fix(p):
return state.relpath(os.path.join(env.BASE, p), here)
return state.relpath(os.path.join(env.v.BASE, p), here)
argv = (['redo-unlocked', _fix(self.sf.name)] +
list(set(_fix(d.name) for d in dirty)))
meta('check', state.target_relpath(self.t))
state.commit()
def run():
os.environ['REDO_DEPTH'] = env.DEPTH + ' '
os.environ['REDO_DEPTH'] = env.v.DEPTH + ' '
# python ignores SIGPIPE
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
os.execvp(argv[0], argv)
@ -261,18 +261,18 @@ class BuildJob(object):
assert state.is_flushed()
dn = self.dodir
newp = os.path.realpath(dn)
os.environ['REDO_PWD'] = state.relpath(newp, env.STARTDIR)
os.environ['REDO_PWD'] = state.relpath(newp, env.v.STARTDIR)
os.environ['REDO_TARGET'] = self.basename + self.ext
os.environ['REDO_DEPTH'] = env.DEPTH + ' '
os.environ['REDO_DEPTH'] = env.v.DEPTH + ' '
cycles.add(self.lock.fid)
if dn:
os.chdir(dn)
os.dup2(self.f.fileno(), 1)
os.close(self.f.fileno())
close_on_exec(1, False)
if env.LOG:
if env.v.LOG:
cur_inode = str(os.fstat(2).st_ino)
if not env.LOG_INODE or cur_inode == env.LOG_INODE:
if not env.v.LOG_INODE or cur_inode == env.v.LOG_INODE:
# .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
@ -290,7 +290,7 @@ class BuildJob(object):
del os.environ['REDO_LOG_INODE']
os.environ['REDO_LOG'] = ''
signal.signal(signal.SIGPIPE, signal.SIG_DFL) # python ignores SIGPIPE
if env.VERBOSE or env.XTRACE:
if env.v.VERBOSE or env.v.XTRACE:
logs.write('* %s' % ' '.join(self.argv).replace('\n', ' '))
os.execvp(self.argv[0], self.argv)
# FIXME: it would be nice to log the exit code to logf.
@ -387,7 +387,7 @@ class BuildJob(object):
def main(targets, shouldbuildfunc):
retcode = [0] # a list so that it can be reassigned from done()
if env.SHUFFLE:
if env.v.SHUFFLE:
import random
random.shuffle(targets)
@ -397,9 +397,9 @@ def main(targets, shouldbuildfunc):
if rv:
retcode[0] = 1
if env.TARGET and not env.UNLOCKED:
me = os.path.join(env.STARTDIR,
os.path.join(env.PWD, env.TARGET))
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))
myfile = state.File(name=me)
selflock = state.Lock(state.LOG_LOCK_MAGIC + myfile.id)
else:
@ -435,7 +435,7 @@ def main(targets, shouldbuildfunc):
if not jobserver.has_token():
state.commit()
jobserver.ensure_token_or_cheat(t, cheat)
if retcode[0] and not env.KEEP_GOING:
if retcode[0] and not env.v.KEEP_GOING:
break
if not state.check_sane():
err('.redo directory disappeared; cannot continue.\n')
@ -443,7 +443,7 @@ def main(targets, shouldbuildfunc):
break
f = state.File(name=t)
lock = state.Lock(f.id)
if env.UNLOCKED:
if env.v.UNLOCKED:
lock.owned = True
else:
lock.trylock()
@ -478,7 +478,7 @@ def main(targets, shouldbuildfunc):
jobserver.ensure_token_or_cheat('self', cheat)
# at this point, we don't have any children holding any tokens, so
# it's okay to block below.
if retcode[0] and not env.KEEP_GOING:
if retcode[0] and not env.v.KEEP_GOING:
break
if locked:
if not state.check_sane():

View file

@ -4,8 +4,9 @@ import env, state
def main():
try:
me = os.path.join(env.STARTDIR,
os.path.join(env.PWD, env.TARGET))
env.inherit()
me = os.path.join(env.v.STARTDIR,
os.path.join(env.v.PWD, env.v.TARGET))
f = state.File(name=me)
f.add_dep('m', state.ALWAYS)
always = state.File(name=state.ALWAYS)

View file

@ -1,16 +1,13 @@
import os, sys, traceback
import env_init
env_init.init(sys.argv[1:])
import env, state, builder, jobserver, deps
from logs import debug2, err
def should_build(t):
f = state.File(name=t)
if f.is_failed():
raise builder.ImmediateReturn(32)
dirty = deps.isdirty(f, depth='', max_changed=env.RUNID,
dirty = deps.isdirty(f, depth='', max_changed=env.v.RUNID,
already_checked=[])
return f.is_generated, dirty == [f] and deps.DIRTY or dirty
@ -18,23 +15,26 @@ def should_build(t):
def main():
rv = 202
try:
if env_init.is_toplevel and env.LOG:
targets = sys.argv[1:]
state.init(targets)
if env.is_toplevel and not targets:
targets = ['all']
if env.is_toplevel and env.v.LOG:
builder.close_stdin()
builder.start_stdin_log_reader(
status=True, details=True,
pretty=True, color=True, debug_locks=False, debug_pids=False)
if env.TARGET and not env.UNLOCKED:
me = os.path.join(env.STARTDIR,
os.path.join(env.PWD, env.TARGET))
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))
f = state.File(name=me)
debug2('TARGET: %r %r %r\n'
% (env.STARTDIR, env.PWD, env.TARGET))
% (env.v.STARTDIR, env.v.PWD, env.v.TARGET))
else:
f = me = None
debug2('redo-ifchange: not adding depends.\n')
jobserver.setup(1)
try:
targets = sys.argv[1:]
if f:
for t in targets:
f.add_dep('m', t)
@ -52,11 +52,11 @@ def main():
err('unexpected error: %r\n' % e)
rv = 1
except KeyboardInterrupt:
if env_init.is_toplevel:
if env.is_toplevel:
builder.await_log_reader()
sys.exit(200)
state.commit()
if env_init.is_toplevel:
if env.is_toplevel:
builder.await_log_reader()
sys.exit(rv)

View file

@ -5,8 +5,9 @@ from logs import err
def main():
try:
me = os.path.join(env.STARTDIR,
os.path.join(env.PWD, env.TARGET))
env.inherit()
me = os.path.join(env.v.STARTDIR,
os.path.join(env.v.PWD, env.v.TARGET))
f = state.File(name=me)
for t in sys.argv[1:]:
if not t:

View file

@ -1,7 +1,7 @@
import errno, fcntl, os, re, struct, sys, time
import termios
from atoi import atoi
import options
import env, logs, options, state
optspec = """
redo-log [options...] [targets...]
@ -21,11 +21,6 @@ o = options.Options(optspec)
(opt, flags, extra) = o.parse(sys.argv[1:])
targets = extra
import env_init
env_init.init(list(targets))
import env, logs, state
topdir = os.getcwd()
already = set()
depth = []
@ -42,19 +37,12 @@ start_time = time.time()
REDO_LINE_RE = re.compile(r'^@@REDO:([^@]+)@@ (.*)\n$')
def _atoi(s):
try:
return int(s)
except TypeError:
return 0
def _tty_width():
s = struct.pack("HHHH", 0, 0, 0, 0)
try:
s = fcntl.ioctl(sys.stderr.fileno(), termios.TIOCGWINSZ, s)
except (IOError, ImportError):
return _atoi(os.environ.get('WIDTH')) or 70
return atoi(os.environ.get('WIDTH')) or 70
(ysize, xsize, ypix, xpix) = struct.unpack('HHHH', s)
return xsize or 70
@ -64,7 +52,7 @@ def is_locked(fid):
def _fix_depth():
env.DEPTH = len(depth) * ' '
env.v.DEPTH = len(depth) * ' '
def _rel(top, mydir, path):
@ -234,13 +222,14 @@ def main():
'redo-log: give at least one target; ' +
'maybe "all"?\n')
sys.exit(1)
state.init(targets)
if opt.status < 2 and not os.isatty(2):
opt.status = False
logs.setup(tty=sys.stdout, pretty=opt.pretty, color=opt.color)
if opt.debug_locks:
env.DEBUG_LOCKS = 1
env.v.DEBUG_LOCKS = 1
if opt.debug_pids:
env.DEBUG_PIDS = 1
env.v.DEBUG_PIDS = 1
if opt.ack_fd:
# Write back to owner, to let them know we started up okay and
# will be able to see their error output, so it's okay to close

View file

@ -1,16 +1,7 @@
import sys, os
import env_init
env_init.init([])
import env, state, deps
from logs import err
if len(sys.argv[1:]) != 0:
err('%s: no arguments expected.\n' % sys.argv[0])
sys.exit(1)
cache = {}
@ -27,17 +18,22 @@ def log_override(name):
def main():
if len(sys.argv[1:]) != 0:
err('%s: no arguments expected.\n' % sys.argv[0])
sys.exit(1)
state.init([])
cwd = os.getcwd()
for f in state.files():
if f.is_target():
if deps.isdirty(f,
depth='',
max_changed=env.RUNID,
max_changed=env.v.RUNID,
already_checked=[],
is_checked=is_checked,
set_checked=set_checked,
log_override=log_override):
print state.relpath(os.path.join(env.BASE, f.name), cwd)
print state.relpath(os.path.join(env.v.BASE, f.name), cwd)
if __name__ == '__main__':

View file

@ -14,8 +14,9 @@
# limitations under the License.
#
import sys, os, traceback
import options
import env, options, state, builder, jobserver
from atoi import atoi
from logs import warn, err
optspec = """
redo [targets...]
@ -37,52 +38,50 @@ no-color disable ANSI color; --color to force enable (default: auto)
debug-locks print messages about file locking (useful for debugging)
debug-pids print process ids as part of log messages (useful for debugging)
"""
o = options.Options(optspec)
(opt, flags, extra) = o.parse(sys.argv[1:])
targets = extra
if opt.version:
import version
print version.TAG
sys.exit(0)
if opt.debug:
os.environ['REDO_DEBUG'] = str(opt.debug or 0)
if opt.verbose:
os.environ['REDO_VERBOSE'] = '1'
if opt.xtrace:
os.environ['REDO_XTRACE'] = '1'
if opt.keep_going:
os.environ['REDO_KEEP_GOING'] = '1'
if opt.shuffle:
os.environ['REDO_SHUFFLE'] = '1'
if opt.debug_locks:
os.environ['REDO_DEBUG_LOCKS'] = '1'
if opt.debug_pids:
os.environ['REDO_DEBUG_PIDS'] = '1'
# This is slightly tricky: the log and pretty options default to true. We
# want to inherit that 'true' value from parent processes *unless* someone
# explicitly specifies the reverse.
if opt.no_log:
os.environ['REDO_LOG'] = '0'
if opt.no_pretty:
os.environ['REDO_PRETTY'] = '0'
if opt.no_color:
os.environ['REDO_COLOR'] = '0'
import env_init
env_init.init(targets)
import env, state, builder, jobserver
from logs import warn, err
def main():
o = options.Options(optspec)
(opt, flags, extra) = o.parse(sys.argv[1:])
targets = extra
if opt.version:
import version
print version.TAG
sys.exit(0)
if opt.debug:
os.environ['REDO_DEBUG'] = str(opt.debug or 0)
if opt.verbose:
os.environ['REDO_VERBOSE'] = '1'
if opt.xtrace:
os.environ['REDO_XTRACE'] = '1'
if opt.keep_going:
os.environ['REDO_KEEP_GOING'] = '1'
if opt.shuffle:
os.environ['REDO_SHUFFLE'] = '1'
if opt.debug_locks:
os.environ['REDO_DEBUG_LOCKS'] = '1'
if opt.debug_pids:
os.environ['REDO_DEBUG_PIDS'] = '1'
# This is slightly tricky: the log and pretty options default to true. We
# want to inherit that 'true' value from parent processes *unless* someone
# explicitly specifies the reverse.
if opt.no_log:
os.environ['REDO_LOG'] = '0'
if opt.no_pretty:
os.environ['REDO_PRETTY'] = '0'
if opt.no_color:
os.environ['REDO_COLOR'] = '0'
try:
state.init(targets)
if env.is_toplevel and not targets:
targets = ['all']
j = atoi(opt.jobs or 1)
if env_init.is_toplevel and (env.LOG or j > 1):
if env.is_toplevel and (env.v.LOG or j > 1):
builder.close_stdin()
if env_init.is_toplevel and env.LOG:
if env.is_toplevel and env.v.LOG:
builder.start_stdin_log_reader(
status=opt.status, details=opt.details,
pretty=opt.pretty, color=opt.color,
@ -112,11 +111,11 @@ def main():
traceback.print_exc(100, sys.stderr)
err('unexpected error: %r\n' % e)
retcode = 1
if env_init.is_toplevel:
if env.is_toplevel:
builder.await_log_reader()
sys.exit(retcode)
except KeyboardInterrupt:
if env_init.is_toplevel:
if env.is_toplevel:
builder.await_log_reader()
sys.exit(200)

View file

@ -1,13 +1,10 @@
import sys, os
import env_init
env_init.init([])
import state, env
from logs import err
def main():
state.init([])
if len(sys.argv[1:]) != 0:
err('%s: no arguments expected.\n' % sys.argv[0])
sys.exit(1)
@ -15,7 +12,7 @@ def main():
cwd = os.getcwd()
for f in state.files():
if f.is_source():
print state.relpath(os.path.join(env.BASE, f.name), cwd)
print state.relpath(os.path.join(env.v.BASE, f.name), cwd)
if __name__ == '__main__':

View file

@ -4,6 +4,7 @@ from logs import err, debug2
def main():
env.inherit()
if len(sys.argv) > 1:
err('%s: no arguments expected.\n' % sys.argv[0])
sys.exit(1)
@ -32,11 +33,11 @@ def main():
csum = sh.hexdigest()
if not env.TARGET:
if not env.v.TARGET:
sys.exit(0)
me = os.path.join(env.STARTDIR,
os.path.join(env.PWD, env.TARGET))
me = os.path.join(env.v.STARTDIR,
os.path.join(env.v.PWD, env.v.TARGET))
f = state.File(name=me)
changed = (csum != f.csum)
debug2('%s: old = %s\n' % (f.name, f.csum))

View file

@ -1,13 +1,10 @@
import sys, os
import env_init
env_init.init([])
import state, env
import env, state
from logs import err
def main():
state.init([])
if len(sys.argv[1:]) != 0:
err('%s: no arguments expected.\n' % sys.argv[0])
sys.exit(1)
@ -15,7 +12,7 @@ def main():
cwd = os.getcwd()
for f in state.files():
if f.is_target():
print state.relpath(os.path.join(env.BASE, f.name), cwd)
print state.relpath(os.path.join(env.v.BASE, f.name), cwd)
if __name__ == '__main__':

View file

@ -1,9 +1,10 @@
import sys, os
import state
import env, state
from logs import err
def main():
env.inherit()
if len(sys.argv[1:]) < 2:
err('%s: at least 2 arguments expected.\n' % sys.argv[0])
sys.exit(1)

View file

@ -1,13 +1,10 @@
import sys, os
import env_init
env_init.init_no_state()
import paths
import env, paths
from logs import err
def main():
env.init_no_state()
if len(sys.argv[1:]) != 1:
err('%s: exactly one argument expected.\n' % sys.argv[0])
sys.exit(1)

View file

@ -16,7 +16,7 @@ def isdirty(f, depth, max_changed,
# is unaffected
already_checked = list(already_checked) + [f.id]
if env.DEBUG >= 1:
if env.v.DEBUG >= 1:
debug('%s?%s %r,%r\n'
% (depth, f.nicename(), f.is_generated, f.is_override))
@ -28,10 +28,10 @@ def isdirty(f, depth, max_changed,
return DIRTY
if f.changed_runid > max_changed:
debug('%s-- DIRTY (built %d > %d; %d)\n'
% (depth, f.changed_runid, max_changed, env.RUNID))
% (depth, f.changed_runid, max_changed, env.v.RUNID))
return DIRTY # has been built more recently than parent
if is_checked(f):
if env.DEBUG >= 1:
if env.v.DEBUG >= 1:
debug('%s-- CLEAN (checked)\n' % depth)
return CLEAN # has already been checked during this session
if not f.stamp:
@ -65,7 +65,7 @@ def isdirty(f, depth, max_changed,
for mode, f2 in f.deps():
dirty = CLEAN
if mode == 'c':
if os.path.exists(os.path.join(env.BASE, f2.name)):
if os.path.exists(os.path.join(env.v.BASE, f2.name)):
debug('%s-- DIRTY (created)\n' % depth)
dirty = DIRTY
elif mode == 'm':

View file

@ -1,35 +1,116 @@
import os
import os, sys
from atoi import atoi
if not os.environ.get('REDO'):
import sys
sys.stderr.write('%s: error: must be run from inside a .do\n'
% sys.argv[0])
sys.exit(100)
is_toplevel = False
PWD = os.environ.get('REDO_PWD', '')
TARGET = os.environ.get('REDO_TARGET', '')
DEPTH = os.environ.get('REDO_DEPTH', '')
DEBUG = atoi(os.environ.get('REDO_DEBUG', ''))
DEBUG_LOCKS = os.environ.get('REDO_DEBUG_LOCKS', '') and 1 or 0
DEBUG_PIDS = os.environ.get('REDO_DEBUG_PIDS', '') and 1 or 0
VERBOSE = os.environ.get('REDO_VERBOSE', '') and 1 or 0
XTRACE = os.environ.get('REDO_XTRACE', '') and 1 or 0
KEEP_GOING = os.environ.get('REDO_KEEP_GOING', '') and 1 or 0
LOG = atoi(os.environ.get('REDO_LOG', '1')) # defaults on
LOG_INODE = os.environ.get('REDO_LOG_INODE', '')
COLOR = atoi(os.environ.get('REDO_COLOR', '1')) # defaults on
# subprocesses mustn't pretty-print if a parent is running redo-log
PRETTY = (not LOG) and atoi(os.environ.get('REDO_PRETTY', '1'))
SHUFFLE = os.environ.get('REDO_SHUFFLE', '') and 1 or 0
STARTDIR = os.environ.get('REDO_STARTDIR', '')
RUNID = atoi(os.environ.get('REDO_RUNID')) or None
BASE = os.environ['REDO_BASE']
while BASE and BASE.endswith('/'):
BASE = BASE[:-1]
v = None
UNLOCKED = os.environ.get('REDO_UNLOCKED', '') and 1 or 0
os.environ['REDO_UNLOCKED'] = '' # not inheritable by subprocesses
NO_OOB = os.environ.get('REDO_NO_OOB', '') and 1 or 0
os.environ['REDO_NO_OOB'] = '' # not inheritable by subprocesses
def _get(name, default):
return os.environ.get(name, default)
def _get_int(name, default):
return atoi(_get(name, str(default)))
def _get_bool(name, default):
return 1 if _get(name, default) else 0
class Env(object):
def __init__(self):
# Mandatory. If this is missing, you forgot to call init().
self.BASE = os.environ['REDO_BASE'].rstrip('/')
# Everything else, we can recover from defaults if unset.
self.PWD = _get('REDO_PWD', '')
self.TARGET = _get('REDO_TARGET', '')
self.DEPTH = _get('REDO_DEPTH', '')
self.DEBUG = atoi(_get('REDO_DEBUG', ''))
self.DEBUG_LOCKS = _get_bool('REDO_DEBUG_LOCKS', '')
self.DEBUG_PIDS = _get_bool('REDO_DEBUG_PIDS', '')
self.VERBOSE = _get_bool('REDO_VERBOSE', '')
self.XTRACE = _get_bool('REDO_XTRACE', '')
self.KEEP_GOING = _get_bool('REDO_KEEP_GOING', '')
self.LOG = _get_int('REDO_LOG', 1) # defaults on
self.LOG_INODE = _get('REDO_LOG_INODE', '')
self.COLOR = _get_int('REDO_COLOR', 1) # defaults on
# subprocesses mustn't pretty-print if a parent is running redo-log
self.PRETTY = (not self.LOG) and _get_int('REDO_PRETTY', 1)
self.SHUFFLE = _get_bool('REDO_SHUFFLE', '')
self.STARTDIR = _get('REDO_STARTDIR', '')
self.RUNID = _get_int('REDO_RUNID', '') or None
self.UNLOCKED = _get_bool('REDO_UNLOCKED', '')
self.NO_OOB = _get_bool('REDO_NO_OOB', '')
def inherit():
"""Read environment (which must already be set) to get runtime settings."""
if not os.environ.get('REDO'):
sys.stderr.write('%s: error: must be run from inside a .do\n'
% sys.argv[0])
sys.exit(100)
global v
v = Env()
# not inheritable by subprocesses
os.environ['REDO_UNLOCKED'] = ''
os.environ['REDO_NO_OOB'] = ''
def init_no_state():
"""Start a session (if needed) for a command that needs no state db."""
global is_toplevel
if not os.environ.get('REDO'):
os.environ['REDO'] = 'NOT_DEFINED'
is_toplevel = True
if not os.environ.get('REDO_BASE'):
os.environ['REDO_BASE'] = 'NOT_DEFINED'
inherit()
def init(targets):
"""Start a session (if needed) for a command that does need the state db.
Args:
targets: a list of targets we're trying to build. We use this to
help in guessing where the .redo database is located.
"""
global is_toplevel
if not os.environ.get('REDO'):
# toplevel call to redo
is_toplevel = True
exenames = [os.path.abspath(sys.argv[0]),
os.path.realpath(sys.argv[0])]
dirnames = [os.path.dirname(p) for p in exenames]
trynames = ([os.path.abspath(p+'/../lib/redo') for p in dirnames] +
[p+'/../redo' for p in dirnames] +
dirnames)
seen = {}
dirs = []
for k in trynames:
if not seen.get(k):
seen[k] = 1
dirs.append(k)
os.environ['PATH'] = ':'.join(dirs) + ':' + os.environ['PATH']
os.environ['REDO'] = os.path.abspath(sys.argv[0])
if not os.environ.get('REDO_BASE'):
if not targets:
# if no other targets given, assume the current directory
targets = ['all']
base = os.path.commonprefix([os.path.abspath(os.path.dirname(t))
for t in targets] + [os.getcwd()])
bsplit = base.split('/')
for i in range(len(bsplit)-1, 0, -1):
newbase = '/'.join(bsplit[:i])
if os.path.exists(newbase + '/.redo'):
base = newbase
break
os.environ['REDO_BASE'] = base
os.environ['REDO_STARTDIR'] = os.getcwd()
inherit()

View file

@ -1,53 +0,0 @@
import sys, os
is_toplevel = False
def init_no_state():
global is_toplevel
if not os.environ.get('REDO'):
os.environ['REDO'] = 'NOT_DEFINED'
is_toplevel = True
if not os.environ.get('REDO_BASE'):
os.environ['REDO_BASE'] = 'NOT_DEFINED'
def init(targets):
global is_toplevel
if not os.environ.get('REDO'):
# toplevel call to redo
is_toplevel = True
if len(targets) == 0:
targets.append('all')
exenames = [os.path.abspath(sys.argv[0]),
os.path.realpath(sys.argv[0])]
dirnames = [os.path.dirname(p) for p in exenames]
trynames = ([os.path.abspath(p+'/../lib/redo') for p in dirnames] +
[p+'/../redo' for p in dirnames] +
dirnames)
seen = {}
dirs = []
for k in trynames:
if not seen.get(k):
seen[k] = 1
dirs.append(k)
os.environ['PATH'] = ':'.join(dirs) + ':' + os.environ['PATH']
os.environ['REDO'] = os.path.abspath(sys.argv[0])
if not os.environ.get('REDO_BASE'):
base = os.path.commonprefix([os.path.abspath(os.path.dirname(t))
for t in targets] + [os.getcwd()])
bsplit = base.split('/')
for i in range(len(bsplit)-1, 0, -1):
newbase = '/'.join(bsplit[:i])
if os.path.exists(newbase + '/.redo'):
base = newbase
break
os.environ['REDO_BASE'] = base
os.environ['REDO_STARTDIR'] = os.getcwd()
import state
state.init()
os.environ['REDO_LOCKS'] = os.environ.get('REDO_LOCKS', '')

View file

@ -343,7 +343,7 @@ def ensure_token_or_cheat(reason, cheatfunc):
if not has_token():
assert _mytokens == 0
n = cheatfunc()
_debug('%s: %s: cheat = %d\n' % (env.TARGET, reason, n))
_debug('%s: %s: cheat = %d\n' % (env.v.TARGET, reason, n))
if n > 0:
_mytokens += n
_cheats += n

View file

@ -44,12 +44,12 @@ class PrettyLog(object):
self.file = tty
def _pretty(self, pid, color, s):
if env.DEBUG_PIDS:
if env.v.DEBUG_PIDS:
redo = '%-6d redo ' % pid
else:
redo = 'redo '
self.file.write(
''.join([color, redo, env.DEPTH,
''.join([color, redo, env.v.DEPTH,
BOLD if color else '', s, PLAIN, '\n']))
def write(self, s):
@ -75,17 +75,17 @@ class PrettyLog(object):
rv = int(rv)
if rv:
self._pretty(pid, RED, '%s (exit %d)' % (name, rv))
elif env.VERBOSE or env.XTRACE or env.DEBUG:
elif env.v.VERBOSE or env.v.XTRACE or env.v.DEBUG:
self._pretty(pid, GREEN, '%s (done)' % name)
self.file.write('\n')
elif kind == 'locked':
if env.DEBUG_LOCKS:
if env.v.DEBUG_LOCKS:
self._pretty(pid, GREEN, '%s (locked...)' % text)
elif kind == 'waiting':
if env.DEBUG_LOCKS:
if env.v.DEBUG_LOCKS:
self._pretty(pid, GREEN, '%s (WAITING)' % text)
elif kind == 'unlocked':
if env.DEBUG_LOCKS:
if env.v.DEBUG_LOCKS:
self._pretty(pid, GREEN, '%s (...unlocked!)' % text)
elif kind == 'error':
self.file.write(''.join([RED, 'redo: ',
@ -106,18 +106,21 @@ _log = None
def setup(tty, pretty, color):
global _log
if pretty or env.PRETTY:
if pretty or env.v.PRETTY:
check_tty(tty, color=color)
_log = PrettyLog(tty=tty)
else:
_log = RawLog(tty=tty)
# FIXME: explicitly initialize in each program, for clarity
setup(tty=sys.stderr, pretty=env.PRETTY, color=env.COLOR)
def _maybe_setup():
# FIXME: explicitly initialize in each program, for clarity
if not _log:
setup(tty=sys.stderr, pretty=env.v.PRETTY, color=env.v.COLOR)
def write(s):
_maybe_setup()
_log.write(s)
@ -139,16 +142,16 @@ def warn(s):
meta('warning', s)
def debug(s):
if env.DEBUG >= 1:
if env.v.DEBUG >= 1:
s = s.rstrip()
meta('debug', s)
def debug2(s):
if env.DEBUG >= 2:
if env.v.DEBUG >= 2:
s = s.rstrip()
meta('debug', s)
def debug3(s):
if env.DEBUG >= 3:
if env.v.DEBUG >= 3:
s = s.rstrip()
meta('debug', s)

View file

@ -15,7 +15,7 @@ def _default_do_files(filename):
def possible_do_files(t):
dirname, filename = os.path.split(t)
yield (os.path.join(env.BASE, dirname), "%s.do" % filename,
yield (os.path.join(env.v.BASE, dirname), "%s.do" % filename,
'', filename, '')
# It's important to try every possibility in a directory before resorting
@ -24,7 +24,7 @@ def possible_do_files(t):
# the former one might just be an artifact of someone embedding my project
# into theirs as a subdir. When they do, my rules should still be used
# for building my project in *all* cases.
t = os.path.normpath(os.path.join(env.BASE, t))
t = os.path.normpath(os.path.join(env.v.BASE, t))
dirname, filename = os.path.split(t)
dirbits = dirname.split('/')
# since t is an absolute path, dirbits[0] is always '', so we don't

View file

@ -46,7 +46,7 @@ def db():
if _db:
return _db
dbdir = '%s/.redo' % env.BASE
dbdir = '%s/.redo' % env.v.BASE
dbfile = '%s/db.sqlite3' % dbdir
try:
os.mkdir(dbdir)
@ -56,7 +56,7 @@ def db():
else:
raise
_lockfile = os.open(os.path.join(env.BASE, '.redo/locks'),
_lockfile = os.open(os.path.join(env.v.BASE, '.redo/locks'),
os.O_RDWR | os.O_CREAT, 0666)
close_on_exec(_lockfile, True)
@ -106,17 +106,18 @@ def db():
_db.execute("insert into Runid values (1000000000)")
_db.execute("insert into Files (name) values (?)", [ALWAYS])
if not env.RUNID:
if not env.v.RUNID:
_db.execute("insert into Runid values "
" ((select max(id)+1 from Runid))")
env.RUNID = _db.execute("select last_insert_rowid()").fetchone()[0]
os.environ['REDO_RUNID'] = str(env.RUNID)
env.v.RUNID = _db.execute("select last_insert_rowid()").fetchone()[0]
os.environ['REDO_RUNID'] = str(env.v.RUNID)
_db.commit()
return _db
def init():
def init(targets):
env.init(targets)
db()
@ -155,7 +156,7 @@ _insane = None
def check_sane():
global _insane
if not _insane:
_insane = not os.path.exists('%s/.redo' % env.BASE)
_insane = not os.path.exists('%s/.redo' % env.v.BASE)
return not _insane
@ -179,18 +180,18 @@ def relpath(t, base):
return join('/', tparts)
# Return a path for t, if cwd were the dirname of env.TARGET.
# Return a path for t, if cwd were the dirname of env.v.TARGET.
# This is tricky! STARTDIR+PWD is the directory for the *dofile*, when
# the dofile was started. However, inside the dofile, someone may have done
# a chdir to anywhere else. env.TARGET is relative to the dofile path, so
# a chdir to anywhere else. env.v.TARGET is relative to the dofile path, so
# we have to first figure out where the dofile was, then find TARGET relative
# to that, then find t relative to that.
#
# FIXME: find some cleaner terminology for all these different paths.
def target_relpath(t):
dofile_dir = os.path.abspath(os.path.join(env.STARTDIR, env.PWD))
dofile_dir = os.path.abspath(os.path.join(env.v.STARTDIR, env.v.PWD))
target_dir = os.path.abspath(
os.path.dirname(os.path.join(dofile_dir, env.TARGET)))
os.path.dirname(os.path.join(dofile_dir, env.v.TARGET)))
return relpath(t, target_dir)
@ -232,7 +233,7 @@ class File(object):
q += 'where rowid=?'
l = [fid]
elif name != None:
name = (name == ALWAYS) and ALWAYS or relpath(name, env.BASE)
name = (name == ALWAYS) and ALWAYS or relpath(name, env.v.BASE)
q += 'where name=?'
l = [name]
else:
@ -258,8 +259,8 @@ class File(object):
(self.id, self.name, self.is_generated, self.is_override,
self.checked_runid, self.changed_runid, self.failed_runid,
self.stamp, self.csum) = cols
if self.name == ALWAYS and self.changed_runid < env.RUNID:
self.changed_runid = env.RUNID
if self.name == ALWAYS and self.changed_runid < env.v.RUNID:
self.changed_runid = env.v.RUNID
def __init__(self, fid=None, name=None, cols=None, allow_add=True):
if cols:
@ -284,7 +285,7 @@ class File(object):
self.id])
def set_checked(self):
self.checked_runid = env.RUNID
self.checked_runid = env.v.RUNID
def set_checked_save(self):
self.set_checked()
@ -292,14 +293,14 @@ class File(object):
def set_changed(self):
debug2('BUILT: %r (%r)\n' % (self.name, self.stamp))
self.changed_runid = env.RUNID
self.changed_runid = env.v.RUNID
self.failed_runid = None
self.is_override = False
def set_failed(self):
debug2('FAILED: %r\n' % self.name)
self.update_stamp()
self.failed_runid = env.RUNID
self.failed_runid = env.v.RUNID
if self.stamp != STAMP_MISSING:
# if we failed and the target file still exists,
# then we're generated.
@ -358,13 +359,13 @@ class File(object):
return True
def is_checked(self):
return self.checked_runid and self.checked_runid >= env.RUNID
return self.checked_runid and self.checked_runid >= env.v.RUNID
def is_changed(self):
return self.changed_runid and self.changed_runid >= env.RUNID
return self.changed_runid and self.changed_runid >= env.v.RUNID
def is_failed(self):
return self.failed_runid and self.failed_runid >= env.RUNID
return self.failed_runid and self.failed_runid >= env.v.RUNID
def deps(self):
if self.is_override or not self.is_generated:
@ -397,7 +398,7 @@ class File(object):
def _read_stamp_st(self, statfunc):
try:
st = statfunc(os.path.join(env.BASE, self.name))
st = statfunc(os.path.join(env.v.BASE, self.name))
except OSError:
return False, STAMP_MISSING
if stat.S_ISDIR(st.st_mode):
@ -427,7 +428,7 @@ class File(object):
return pre
def nicename(self):
return relpath(os.path.join(env.BASE, self.name), env.STARTDIR)
return relpath(os.path.join(env.v.BASE, self.name), env.v.STARTDIR)
def files():
@ -438,7 +439,7 @@ def files():
def logname(fid):
"""Given the id of a File, return the filename of its build log."""
return os.path.join(env.BASE, '.redo', 'log.%d' % fid)
return os.path.join(env.v.BASE, '.redo', 'log.%d' % fid)
# FIXME: I really want to use fcntl F_SETLK, F_SETLKW, etc here. But python