Move into the 21st century by fixing some pylint warnings.
This commit is contained in:
parent
1966a0fac7
commit
e1327540fb
22 changed files with 797 additions and 388 deletions
318
.pylintrc
Normal file
318
.pylintrc
Normal file
|
|
@ -0,0 +1,318 @@
|
|||
[MASTER]
|
||||
|
||||
# Pickle collected data for later comparisons.
|
||||
persistent=no
|
||||
|
||||
# Use multiple processes to speed up Pylint.
|
||||
jobs=1
|
||||
|
||||
|
||||
[MESSAGES CONTROL]
|
||||
|
||||
# We probably want to fix these eventually, but in the meantime, these
|
||||
# ones are relatively harmless.
|
||||
disable=redefined-builtin,multiple-imports,missing-docstring,wrong-import-position,locally-disabled,invalid-name,unused-argument,fixme,global-statement,redefined-variable-type,using-constant-test,unused-variable,file-ignored,simplifiable-if-statement
|
||||
|
||||
|
||||
[REPORTS]
|
||||
|
||||
# Set the output format. Available formats are text, parseable, colorized, msvs
|
||||
# (visual studio) and html. You can also give a reporter class, eg
|
||||
# mypackage.mymodule.MyReporterClass.
|
||||
output-format=text
|
||||
|
||||
# Put messages in a separate file for each module / package specified on the
|
||||
# command line instead of printing them on stdout. Reports (if any) will be
|
||||
# written in a file name "pylint_global.[txt|html]". This option is deprecated
|
||||
# and it will be removed in Pylint 2.0.
|
||||
files-output=no
|
||||
|
||||
# Tells whether to display a full report or only the messages
|
||||
reports=no
|
||||
|
||||
|
||||
[SIMILARITIES]
|
||||
|
||||
# Minimum lines number of a similarity.
|
||||
min-similarity-lines=20
|
||||
|
||||
# Ignore comments when computing similarities.
|
||||
ignore-comments=yes
|
||||
|
||||
# Ignore docstrings when computing similarities.
|
||||
ignore-docstrings=yes
|
||||
|
||||
# Ignore imports when computing similarities.
|
||||
ignore-imports=no
|
||||
|
||||
|
||||
[FORMAT]
|
||||
|
||||
# Maximum number of characters on a single line.
|
||||
max-line-length=80
|
||||
|
||||
# Regexp for a line that is allowed to be longer than the limit.
|
||||
ignore-long-lines=^\s*(# )?<?https?://\S+>?$
|
||||
|
||||
# Allow the body of an if to be on the same line as the test if there is no
|
||||
# else.
|
||||
single-line-if-stmt=no
|
||||
|
||||
# List of optional constructs for which whitespace checking is disabled. `dict-
|
||||
# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}.
|
||||
# `trailing-comma` allows a space between comma and closing bracket: (a, ).
|
||||
# `empty-line` allows space-only lines.
|
||||
no-space-check=trailing-comma,dict-separator
|
||||
|
||||
# Maximum number of lines in a module
|
||||
max-module-lines=1000
|
||||
|
||||
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
|
||||
# tab).
|
||||
indent-string=' '
|
||||
|
||||
# Number of spaces of indent required inside a hanging or continued line.
|
||||
indent-after-paren=4
|
||||
|
||||
# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
|
||||
expected-line-ending-format=LF
|
||||
|
||||
|
||||
[BASIC]
|
||||
|
||||
# Good variable names which should always be accepted, separated by a comma
|
||||
good-names=i,j,k,ex,Run,_
|
||||
|
||||
# Bad variable names which should always be refused, separated by a comma
|
||||
bad-names=foo,bar,baz,toto,tutu,tata
|
||||
|
||||
# Colon-delimited sets of names that determine each other's naming style when
|
||||
# the name regexes allow several styles.
|
||||
name-group=
|
||||
|
||||
# Include a hint for the correct naming format with invalid-name
|
||||
include-naming-hint=yes
|
||||
|
||||
# List of decorators that produce properties, such as abc.abstractproperty. Add
|
||||
# to this list to register other decorators that produce valid properties.
|
||||
property-classes=abc.abstractproperty
|
||||
|
||||
# Regular expression matching correct function names
|
||||
function-rgx=[a-z_][a-z0-9_]{2,30}$
|
||||
|
||||
# Naming hint for function names
|
||||
function-name-hint=[a-z_][a-z0-9_]{2,30}$
|
||||
|
||||
# Regular expression matching correct variable names
|
||||
variable-rgx=[a-z_][a-z0-9_]{0,30}$
|
||||
|
||||
# Naming hint for variable names
|
||||
variable-name-hint=[a-z_][a-z0-9_]{0,30}$
|
||||
|
||||
# Regular expression matching correct constant names
|
||||
const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
|
||||
|
||||
# Naming hint for constant names
|
||||
const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$
|
||||
|
||||
# Regular expression matching correct attribute names
|
||||
attr-rgx=[a-z_][a-z0-9_]{2,30}$
|
||||
|
||||
# Naming hint for attribute names
|
||||
attr-name-hint=[a-z_][a-z0-9_]{2,30}$
|
||||
|
||||
# Regular expression matching correct argument names
|
||||
argument-rgx=[a-z_][a-z0-9_]{2,30}$
|
||||
|
||||
# Naming hint for argument names
|
||||
argument-name-hint=[a-z_][a-z0-9_]{2,30}$
|
||||
|
||||
# Regular expression matching correct class attribute names
|
||||
class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
|
||||
|
||||
# Naming hint for class attribute names
|
||||
class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
|
||||
|
||||
# Regular expression matching correct inline iteration names
|
||||
inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
|
||||
|
||||
# Naming hint for inline iteration names
|
||||
inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$
|
||||
|
||||
# Regular expression matching correct class names
|
||||
class-rgx=[A-Z_][a-zA-Z0-9]+$
|
||||
|
||||
# Naming hint for class names
|
||||
class-name-hint=[A-Z_][a-zA-Z0-9]+$
|
||||
|
||||
# Regular expression matching correct module names
|
||||
module-rgx=(([a-z_][-a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
|
||||
|
||||
# Naming hint for module names
|
||||
module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
|
||||
|
||||
# Regular expression matching correct method names
|
||||
method-rgx=[a-z_][a-z0-9_]{2,30}$
|
||||
|
||||
# Naming hint for method names
|
||||
method-name-hint=[a-z_][a-z0-9_]{2,30}$
|
||||
|
||||
# Regular expression which should only match function or class names that do
|
||||
# not require a docstring.
|
||||
no-docstring-rgx=^_
|
||||
|
||||
# Minimum line length for functions/classes that require docstrings, shorter
|
||||
# ones are exempt.
|
||||
docstring-min-length=10
|
||||
|
||||
|
||||
[ELIF]
|
||||
|
||||
# Maximum number of nested blocks for function / method body
|
||||
max-nested-blocks=100
|
||||
|
||||
|
||||
[MISCELLANEOUS]
|
||||
|
||||
# List of note tags to take in consideration, separated by a comma.
|
||||
notes=FIXME,XXX,TODO
|
||||
|
||||
|
||||
[TYPECHECK]
|
||||
|
||||
# Tells whether missing members accessed in mixin class should be ignored. A
|
||||
# mixin class is detected if its name ends with "mixin" (case insensitive).
|
||||
ignore-mixin-members=yes
|
||||
|
||||
# List of module names for which member attributes should not be checked
|
||||
# (useful for modules/projects where namespaces are manipulated during runtime
|
||||
# and thus existing member attributes cannot be deduced by static analysis. It
|
||||
# supports qualified module names, as well as Unix pattern matching.
|
||||
ignored-modules=
|
||||
|
||||
# List of class names for which member attributes should not be checked (useful
|
||||
# for classes with dynamically set attributes). This supports the use of
|
||||
# qualified names.
|
||||
ignored-classes=optparse.Values,thread._local,_thread._local
|
||||
|
||||
# List of members which are set dynamically and missed by pylint inference
|
||||
# system, and so shouldn't trigger E1101 when accessed. Python regular
|
||||
# expressions are accepted.
|
||||
generated-members=
|
||||
|
||||
# List of decorators that produce context managers, such as
|
||||
# contextlib.contextmanager. Add to this list to register other decorators that
|
||||
# produce valid context managers.
|
||||
contextmanager-decorators=contextlib.contextmanager
|
||||
|
||||
|
||||
[VARIABLES]
|
||||
|
||||
# Tells whether we should check for unused import in __init__ files.
|
||||
init-import=no
|
||||
|
||||
# A regular expression matching the name of dummy variables (i.e. expectedly
|
||||
# not used).
|
||||
dummy-variables-rgx=(_+[a-zA-Z0-9]*?$)|dummy
|
||||
|
||||
# List of additional names supposed to be defined in builtins. Remember that
|
||||
# you should avoid to define new builtins when possible.
|
||||
additional-builtins=
|
||||
|
||||
# List of strings which can identify a callback function by name. A callback
|
||||
# name must start or end with one of those strings.
|
||||
callbacks=cb_,_cb
|
||||
|
||||
# List of qualified module names which can have objects that can redefine
|
||||
# builtins.
|
||||
redefining-builtins-modules=six.moves,future.builtins
|
||||
|
||||
|
||||
[CLASSES]
|
||||
|
||||
# List of method names used to declare (i.e. assign) instance attributes.
|
||||
defining-attr-methods=__init__,__new__,setUp
|
||||
|
||||
# List of valid names for the first argument in a class method.
|
||||
valid-classmethod-first-arg=cls
|
||||
|
||||
# List of valid names for the first argument in a metaclass class method.
|
||||
valid-metaclass-classmethod-first-arg=mcs
|
||||
|
||||
# List of member names, which should be excluded from the protected access
|
||||
# warning.
|
||||
exclude-protected=_asdict,_fields,_replace,_source,_make,_exit
|
||||
|
||||
|
||||
[IMPORTS]
|
||||
|
||||
# Deprecated modules which should not be used, separated by a comma
|
||||
deprecated-modules=regsub,TERMIOS,Bastion,rexec
|
||||
|
||||
# Create a graph of every (i.e. internal and external) dependencies in the
|
||||
# given file (report RP0402 must not be disabled)
|
||||
import-graph=
|
||||
|
||||
# Create a graph of external dependencies in the given file (report RP0402 must
|
||||
# not be disabled)
|
||||
ext-import-graph=
|
||||
|
||||
# Create a graph of internal dependencies in the given file (report RP0402 must
|
||||
# not be disabled)
|
||||
int-import-graph=
|
||||
|
||||
# Force import order to recognize a module as part of the standard
|
||||
# compatibility libraries.
|
||||
known-standard-library=
|
||||
|
||||
# Force import order to recognize a module as part of a third party library.
|
||||
known-third-party=enchant
|
||||
|
||||
# Analyse import fallback blocks. This can be used to support both Python 2 and
|
||||
# 3 compatible code, which means that the block might have code that exists
|
||||
# only in one or another interpreter, leading to false positives when analysed.
|
||||
analyse-fallback-blocks=no
|
||||
|
||||
|
||||
[DESIGN]
|
||||
|
||||
# Maximum number of arguments for function / method
|
||||
max-args=100
|
||||
|
||||
# Argument names that match this expression will be ignored. Default to name
|
||||
# with leading underscore
|
||||
ignored-argument-names=_.*
|
||||
|
||||
# Maximum number of locals for function / method body
|
||||
max-locals=100
|
||||
|
||||
# Maximum number of return / yield for function / method body
|
||||
max-returns=100
|
||||
|
||||
# Maximum number of branch for function / method body
|
||||
max-branches=100
|
||||
|
||||
# Maximum number of statements in function / method body
|
||||
max-statements=500
|
||||
|
||||
# Maximum number of parents for a class (see R0901).
|
||||
max-parents=7
|
||||
|
||||
# Maximum number of attributes for a class (see R0902).
|
||||
max-attributes=100
|
||||
|
||||
# Minimum number of public methods for a class (see R0903).
|
||||
min-public-methods=0
|
||||
|
||||
# Maximum number of public methods for a class (see R0904).
|
||||
max-public-methods=100
|
||||
|
||||
# Maximum number of boolean expressions in a if statement
|
||||
max-bool-expr=5
|
||||
|
||||
|
||||
[EXCEPTIONS]
|
||||
|
||||
# Exceptions that will emit a warning when being caught. Defaults to
|
||||
# "Exception"
|
||||
overgeneral-exceptions=Exception
|
||||
93
builder.py
93
builder.py
|
|
@ -1,8 +1,8 @@
|
|||
import sys, os, errno, random, stat, signal, time
|
||||
import sys, os, errno, stat, signal, time
|
||||
import vars, jwack, state, paths
|
||||
from helpers import unlink, close_on_exec, join
|
||||
from helpers import unlink, close_on_exec
|
||||
import logs
|
||||
from logs import debug, debug2, err, warn, meta, check_tty
|
||||
from logs import debug2, err, warn, meta, check_tty
|
||||
|
||||
|
||||
def _nice(t):
|
||||
|
|
@ -81,15 +81,15 @@ def start_stdin_log_reader(status, details, pretty, color,
|
|||
argv.append('--color' if color >= 2 else '--no-color')
|
||||
argv.append('-')
|
||||
os.execvp(argv[0], argv)
|
||||
except Exception, e:
|
||||
except Exception, e: # pylint: disable=broad-except
|
||||
sys.stderr.write('redo-log: exec: %s\n' % e)
|
||||
finally:
|
||||
os._exit(99)
|
||||
|
||||
|
||||
def await_log_reader():
|
||||
if not vars.LOG: return
|
||||
global log_reader_pid
|
||||
if not vars.LOG:
|
||||
return
|
||||
if log_reader_pid > 0:
|
||||
# never actually close fd#1 or fd#2; insanity awaits.
|
||||
# replace it with something else instead.
|
||||
|
|
@ -108,14 +108,14 @@ class ImmediateReturn(Exception):
|
|||
self.rv = rv
|
||||
|
||||
|
||||
class BuildJob:
|
||||
class BuildJob(object):
|
||||
def __init__(self, t, sf, lock, shouldbuildfunc, donefunc):
|
||||
self.t = t # original target name, not relative to vars.BASE
|
||||
self.sf = sf
|
||||
tmpbase = t
|
||||
while not os.path.isdir(os.path.dirname(tmpbase) or '.'):
|
||||
ofs = tmpbase.rfind('/')
|
||||
assert(ofs >= 0)
|
||||
assert ofs >= 0
|
||||
tmpbase = tmpbase[:ofs] + '__' + tmpbase[ofs+1:]
|
||||
self.tmpname1 = '%s.redo1.tmp' % tmpbase
|
||||
self.tmpname2 = '%s.redo2.tmp' % tmpbase
|
||||
|
|
@ -125,7 +125,7 @@ class BuildJob:
|
|||
self.before_t = _try_stat(self.t)
|
||||
|
||||
def start(self):
|
||||
assert(self.lock.owned)
|
||||
assert self.lock.owned
|
||||
try:
|
||||
try:
|
||||
is_target, dirty = self.shouldbuildfunc(self.t)
|
||||
|
|
@ -140,29 +140,29 @@ class BuildJob:
|
|||
except ImmediateReturn, e:
|
||||
return self._after2(e.rv)
|
||||
|
||||
if vars.NO_OOB or dirty == True:
|
||||
if vars.NO_OOB or dirty == True: # pylint: disable=singleton-comparison
|
||||
self._start_do()
|
||||
else:
|
||||
self._start_unlocked(dirty)
|
||||
|
||||
def _start_do(self):
|
||||
assert(self.lock.owned)
|
||||
assert self.lock.owned
|
||||
t = self.t
|
||||
sf = self.sf
|
||||
newstamp = sf.read_stamp()
|
||||
if (sf.is_generated and
|
||||
newstamp != state.STAMP_MISSING and
|
||||
(sf.is_override or state.detect_override(sf.stamp, newstamp))):
|
||||
state.warn_override(_nice(t))
|
||||
if not sf.is_override:
|
||||
warn('%s - old: %r\n' % (_nice(t), sf.stamp))
|
||||
warn('%s - new: %r\n' % (_nice(t), newstamp))
|
||||
sf.set_override()
|
||||
sf.set_checked()
|
||||
sf.save()
|
||||
return self._after2(0)
|
||||
newstamp != state.STAMP_MISSING and
|
||||
(sf.is_override or state.detect_override(sf.stamp, newstamp))):
|
||||
state.warn_override(_nice(t))
|
||||
if not sf.is_override:
|
||||
warn('%s - old: %r\n' % (_nice(t), sf.stamp))
|
||||
warn('%s - new: %r\n' % (_nice(t), newstamp))
|
||||
sf.set_override()
|
||||
sf.set_checked()
|
||||
sf.save()
|
||||
return self._after2(0)
|
||||
if (os.path.exists(t) and not os.path.isdir(t + '/.')
|
||||
and not sf.is_generated):
|
||||
and not sf.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
|
||||
|
|
@ -173,7 +173,7 @@ class BuildJob:
|
|||
sf.save()
|
||||
return self._after2(0)
|
||||
sf.zap_deps1()
|
||||
(dodir, dofile, basedir, basename, ext) = paths.find_do_file(sf)
|
||||
(dodir, dofile, _, basename, ext) = paths.find_do_file(sf)
|
||||
if not dofile:
|
||||
if os.path.exists(t):
|
||||
sf.set_static()
|
||||
|
|
@ -186,6 +186,7 @@ class BuildJob:
|
|||
unlink(self.tmpname2)
|
||||
ffd = os.open(self.tmpname1, os.O_CREAT|os.O_RDWR|os.O_EXCL, 0666)
|
||||
close_on_exec(ffd, True)
|
||||
# pylint: disable=attribute-defined-outside-init
|
||||
self.f = os.fdopen(ffd, 'w+')
|
||||
# this will run in the dofile's directory, so use only basenames here
|
||||
arg1 = basename + ext # target name (including extension)
|
||||
|
|
@ -196,15 +197,21 @@ class BuildJob:
|
|||
arg2,
|
||||
# temp output file name
|
||||
state.relpath(os.path.abspath(self.tmpname2), dodir),
|
||||
]
|
||||
if vars.VERBOSE: argv[1] += 'v'
|
||||
if vars.XTRACE: argv[1] += 'x'
|
||||
]
|
||||
if vars.VERBOSE:
|
||||
argv[1] += 'v'
|
||||
if vars.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 vars.LOG: open(state.logname(self.sf.id), 'w')
|
||||
if vars.LOG:
|
||||
open(state.logname(self.sf.id), 'w')
|
||||
# FIXME: put these variables somewhere else, instead of on-the-fly
|
||||
# extending this class!
|
||||
# pylint: disable=attribute-defined-outside-init
|
||||
self.dodir = dodir
|
||||
self.basename = basename
|
||||
self.ext = ext
|
||||
|
|
@ -236,9 +243,10 @@ class BuildJob:
|
|||
state.commit()
|
||||
def run():
|
||||
os.environ['REDO_DEPTH'] = vars.DEPTH + ' '
|
||||
signal.signal(signal.SIGPIPE, signal.SIG_DFL) # python ignores SIGPIPE
|
||||
# python ignores SIGPIPE
|
||||
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
|
||||
os.execvp(argv[0], argv)
|
||||
assert(0)
|
||||
assert 0
|
||||
# returns only if there's an exception
|
||||
def after(t, rv):
|
||||
return self._after2(rv)
|
||||
|
|
@ -250,7 +258,7 @@ class BuildJob:
|
|||
# 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.
|
||||
assert(state.is_flushed())
|
||||
assert state.is_flushed()
|
||||
dn = self.dodir
|
||||
newp = os.path.realpath(dn)
|
||||
os.environ['REDO_PWD'] = state.relpath(newp, vars.STARTDIR)
|
||||
|
|
@ -288,7 +296,7 @@ class BuildJob:
|
|||
# FIXME: it would be nice to log the exit code to logf.
|
||||
# But that would have to happen in the parent process, which doesn't
|
||||
# have logf open.
|
||||
assert(0)
|
||||
assert 0
|
||||
# returns only if there's an exception
|
||||
|
||||
def _after(self, t, rv):
|
||||
|
|
@ -306,8 +314,8 @@ class BuildJob:
|
|||
st1 = os.fstat(f.fileno())
|
||||
st2 = _try_stat(self.tmpname2)
|
||||
if (after_t and
|
||||
(not before_t or before_t.st_mtime != after_t.st_mtime) and
|
||||
not stat.S_ISDIR(after_t.st_mode)):
|
||||
(not before_t or before_t.st_mtime != after_t.st_mtime) and
|
||||
not stat.S_ISDIR(after_t.st_mode)):
|
||||
err('%s modified %s directly!\n' % (self.argv[2], t))
|
||||
err('...you should update $3 (a temp file) or stdout, not $1.\n')
|
||||
rv = 206
|
||||
|
|
@ -315,7 +323,7 @@ class BuildJob:
|
|||
err('%s wrote to stdout *and* created $3.\n' % self.argv[2])
|
||||
err('...you should write status messages to stderr, not stdout.\n')
|
||||
rv = 207
|
||||
if rv==0:
|
||||
if rv == 0:
|
||||
# FIXME: race condition here between updating stamp/is_generated
|
||||
# and actually renaming the files into place. There needs to
|
||||
# be some kind of two-stage commit, I guess.
|
||||
|
|
@ -372,7 +380,7 @@ class BuildJob:
|
|||
def _after2(self, rv):
|
||||
try:
|
||||
self.donefunc(self.t, rv)
|
||||
assert(self.lock.owned)
|
||||
assert self.lock.owned
|
||||
finally:
|
||||
self.lock.unlock()
|
||||
|
||||
|
|
@ -398,7 +406,8 @@ def main(targets, shouldbuildfunc):
|
|||
selflock = myfile = me = None
|
||||
|
||||
def cheat():
|
||||
if not selflock: return 0
|
||||
if not selflock:
|
||||
return 0
|
||||
selflock.trylock()
|
||||
if not selflock.owned:
|
||||
# redo-log already owns it: let's cheat.
|
||||
|
|
@ -419,7 +428,7 @@ def main(targets, shouldbuildfunc):
|
|||
err('cannot build the empty target ("").\n')
|
||||
retcode[0] = 204
|
||||
break
|
||||
assert(state.is_flushed())
|
||||
assert state.is_flushed()
|
||||
if t in seen:
|
||||
continue
|
||||
seen[t] = 1
|
||||
|
|
@ -440,7 +449,7 @@ def main(targets, shouldbuildfunc):
|
|||
lock.trylock()
|
||||
if not lock.owned:
|
||||
meta('locked', state.target_relpath(t))
|
||||
locked.append((f.id,t,f.name))
|
||||
locked.append((f.id, t, f.name))
|
||||
else:
|
||||
# We had to create f before we had a lock, because we need f.id
|
||||
# to make the lock. But someone may have updated the state
|
||||
|
|
@ -450,7 +459,7 @@ def main(targets, shouldbuildfunc):
|
|||
f.refresh()
|
||||
BuildJob(t, f, lock, shouldbuildfunc, done).start()
|
||||
state.commit()
|
||||
assert(state.is_flushed())
|
||||
assert state.is_flushed()
|
||||
lock = None
|
||||
|
||||
del lock
|
||||
|
|
@ -465,7 +474,7 @@ def main(targets, shouldbuildfunc):
|
|||
while locked or jwack.running():
|
||||
state.commit()
|
||||
jwack.wait_all()
|
||||
assert jwack._mytokens == 0
|
||||
assert jwack._mytokens == 0 # pylint: disable=protected-access
|
||||
jwack.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.
|
||||
|
|
@ -476,7 +485,7 @@ def main(targets, shouldbuildfunc):
|
|||
err('.redo directory disappeared; cannot continue.\n')
|
||||
retcode[0] = 205
|
||||
break
|
||||
fid,t,fname = locked.pop(0)
|
||||
fid, t, _ = locked.pop(0)
|
||||
lock = state.Lock(fid)
|
||||
backoff = 0.01
|
||||
lock.trylock()
|
||||
|
|
@ -505,7 +514,7 @@ def main(targets, shouldbuildfunc):
|
|||
lock.unlock()
|
||||
jwack.ensure_token_or_cheat(t, cheat)
|
||||
lock.trylock()
|
||||
assert(lock.owned)
|
||||
assert lock.owned
|
||||
meta('unlocked', state.target_relpath(t))
|
||||
if state.File(name=t).is_failed():
|
||||
err('%s: failed in another thread\n' % _nice(t))
|
||||
|
|
|
|||
26
deps.py
26
deps.py
|
|
@ -1,5 +1,5 @@
|
|||
import sys, os
|
||||
import vars, state, builder
|
||||
import os
|
||||
import vars, state
|
||||
from logs import debug
|
||||
|
||||
CLEAN = 0
|
||||
|
|
@ -17,16 +17,18 @@ def isdirty(f, depth, max_changed,
|
|||
already_checked = list(already_checked) + [f.id]
|
||||
|
||||
if vars.DEBUG >= 1:
|
||||
debug('%s?%s %r,%r\n' % (depth, f.nicename(), f.is_generated, f.is_override))
|
||||
debug('%s?%s %r,%r\n'
|
||||
% (depth, f.nicename(), f.is_generated, f.is_override))
|
||||
|
||||
if f.failed_runid:
|
||||
debug('%s-- DIRTY (failed last time)\n' % depth)
|
||||
return DIRTY
|
||||
if f.changed_runid == None:
|
||||
if f.changed_runid is None:
|
||||
debug('%s-- DIRTY (never built)\n' % depth)
|
||||
return DIRTY
|
||||
if f.changed_runid > max_changed:
|
||||
debug('%s-- DIRTY (built %d > %d; %d)\n' % (depth, f.changed_runid, max_changed, vars.RUNID))
|
||||
debug('%s-- DIRTY (built %d > %d; %d)\n'
|
||||
% (depth, f.changed_runid, max_changed, vars.RUNID))
|
||||
return DIRTY # has been built more recently than parent
|
||||
if is_checked(f):
|
||||
if vars.DEBUG >= 1:
|
||||
|
|
@ -60,16 +62,16 @@ def isdirty(f, depth, max_changed,
|
|||
return DIRTY
|
||||
|
||||
must_build = []
|
||||
for mode,f2 in f.deps():
|
||||
for mode, f2 in f.deps():
|
||||
dirty = CLEAN
|
||||
if mode == 'c':
|
||||
if os.path.exists(os.path.join(vars.BASE, f2.name)):
|
||||
debug('%s-- DIRTY (created)\n' % depth)
|
||||
dirty = DIRTY
|
||||
elif mode == 'm':
|
||||
sub = isdirty(f2, depth = depth + ' ',
|
||||
max_changed = max(f.changed_runid,
|
||||
f.checked_runid),
|
||||
sub = isdirty(f2, depth=depth + ' ',
|
||||
max_changed=max(f.changed_runid,
|
||||
f.checked_runid),
|
||||
already_checked=already_checked,
|
||||
is_checked=is_checked,
|
||||
set_checked=set_checked,
|
||||
|
|
@ -78,14 +80,14 @@ def isdirty(f, depth, max_changed,
|
|||
debug('%s-- DIRTY (sub)\n' % depth)
|
||||
dirty = sub
|
||||
else:
|
||||
assert(mode in ('c','m'))
|
||||
assert mode in ('c', 'm')
|
||||
if not f.csum:
|
||||
# f is a "normal" target: dirty f2 means f is instantly dirty
|
||||
if dirty == DIRTY:
|
||||
# f2 is definitely dirty, so f definitely needs to
|
||||
# redo.
|
||||
return DIRTY
|
||||
elif isinstance(dirty,list):
|
||||
elif isinstance(dirty, list):
|
||||
# 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.
|
||||
|
|
@ -99,7 +101,7 @@ def isdirty(f, depth, max_changed,
|
|||
# redo. However, after that, f might turn out to be
|
||||
# unchanged.
|
||||
return [f]
|
||||
elif isinstance(dirty,list):
|
||||
elif isinstance(dirty, list):
|
||||
# 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.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import os, errno, fcntl
|
||||
from atoi import atoi
|
||||
|
||||
|
||||
def join(between, l):
|
||||
|
|
@ -25,5 +24,3 @@ def close_on_exec(fd, yes):
|
|||
if yes:
|
||||
fl |= fcntl.FD_CLOEXEC
|
||||
fcntl.fcntl(fd, fcntl.F_SETFD, fl)
|
||||
|
||||
|
||||
|
|
|
|||
44
jwack.py
44
jwack.py
|
|
@ -74,7 +74,8 @@
|
|||
# simpler :)
|
||||
#
|
||||
import sys, os, errno, select, fcntl, signal
|
||||
from helpers import atoi, close_on_exec
|
||||
from atoi import atoi
|
||||
from helpers import close_on_exec
|
||||
import state, vars
|
||||
|
||||
_toplevel = 0
|
||||
|
|
@ -87,7 +88,7 @@ _waitfds = {}
|
|||
|
||||
def _debug(s):
|
||||
if 0:
|
||||
sys.stderr.write('jwack#%d: %s' % (os.getpid(),s))
|
||||
sys.stderr.write('jwack#%d: %s' % (os.getpid(), s))
|
||||
|
||||
|
||||
def _create_tokens(n):
|
||||
|
|
@ -132,7 +133,6 @@ def _release_except_mine():
|
|||
|
||||
|
||||
def release_mine():
|
||||
global _mytokens
|
||||
assert _mytokens >= 1
|
||||
_debug('%d,%d -> release_mine()\n' % (_mytokens, _cheats))
|
||||
_release(1)
|
||||
|
|
@ -146,9 +146,9 @@ def _timeout(sig, frame):
|
|||
# This makes it easier to differentiate different kinds of pipes when using
|
||||
# strace.
|
||||
def _make_pipe(startfd):
|
||||
(a,b) = os.pipe()
|
||||
(a, b) = os.pipe()
|
||||
fds = (fcntl.fcntl(a, fcntl.F_DUPFD, startfd),
|
||||
fcntl.fcntl(b, fcntl.F_DUPFD, startfd+1))
|
||||
fcntl.fcntl(b, fcntl.F_DUPFD, startfd + 1))
|
||||
os.close(a)
|
||||
os.close(b)
|
||||
return fds
|
||||
|
|
@ -162,7 +162,7 @@ def _try_read(fd, n):
|
|||
# socket: http://cr.yp.to/unix/nonblock.html
|
||||
# We can't just make the socket non-blocking, because we want to be
|
||||
# compatible with GNU Make, and they can't handle it.
|
||||
r,w,x = select.select([fd], [], [], 0)
|
||||
r, w, x = select.select([fd], [], [], 0)
|
||||
if not r:
|
||||
return None # try again
|
||||
# ok, the socket is readable - but some other process might get there
|
||||
|
|
@ -206,12 +206,12 @@ def setup(maxjobs):
|
|||
FIND = FIND1
|
||||
ofs = flags.find(FIND1)
|
||||
if ofs < 0:
|
||||
FIND = FIND2
|
||||
ofs = flags.find(FIND2)
|
||||
FIND = FIND2
|
||||
ofs = flags.find(FIND2)
|
||||
if ofs >= 0:
|
||||
s = flags[ofs+len(FIND):]
|
||||
(arg,junk) = s.split(' ', 1)
|
||||
(a,b) = arg.split(',', 1)
|
||||
(arg, junk) = s.split(' ', 1)
|
||||
(a, b) = arg.split(',', 1)
|
||||
a = atoi(a)
|
||||
b = atoi(b)
|
||||
if a <= 0 or b <= 0:
|
||||
|
|
@ -221,19 +221,20 @@ def setup(maxjobs):
|
|||
fcntl.fcntl(b, fcntl.F_GETFL)
|
||||
except IOError, e:
|
||||
if e.errno == errno.EBADF:
|
||||
raise ValueError('broken --jobserver-auth from make; prefix your Makefile rule with a "+"')
|
||||
raise ValueError('broken --jobserver-auth from make; ' +
|
||||
'prefix your Makefile rule with a "+"')
|
||||
else:
|
||||
raise
|
||||
_tokenfds = (a,b)
|
||||
_tokenfds = (a, b)
|
||||
|
||||
cheats = os.getenv('REDO_CHEATFDS', '')
|
||||
if cheats:
|
||||
(a,b) = cheats.split(',', 1)
|
||||
(a, b) = cheats.split(',', 1)
|
||||
a = atoi(a)
|
||||
b = atoi(b)
|
||||
if a <= 0 or b <= 0:
|
||||
raise ValueError('invalid REDO_CHEATFDS: %r' % cheats)
|
||||
_cheatfds = (a,b)
|
||||
_cheatfds = (a, b)
|
||||
|
||||
if not _tokenfds:
|
||||
# need to start a new server
|
||||
|
|
@ -256,7 +257,7 @@ def _wait(want_token, max_delay):
|
|||
rfds.append(_tokenfds[0])
|
||||
assert rfds
|
||||
assert state.is_flushed()
|
||||
r,w,x = select.select(rfds, [], [], max_delay)
|
||||
r, w, x = select.select(rfds, [], [], max_delay)
|
||||
_debug('_tokenfds=%r; wfds=%r; readable: %r\n' % (_tokenfds, _waitfds, r))
|
||||
for fd in r:
|
||||
if fd == _tokenfds[0]:
|
||||
|
|
@ -270,7 +271,7 @@ def _wait(want_token, max_delay):
|
|||
# now need to recreate it.
|
||||
b = _try_read(_cheatfds[0], 1)
|
||||
_debug('GOT cheatfd\n')
|
||||
if b == None:
|
||||
if b is None:
|
||||
_create_tokens(1)
|
||||
if has_token():
|
||||
_release_except_mine()
|
||||
|
|
@ -383,7 +384,7 @@ def force_return_tokens():
|
|||
n = len(_waitfds)
|
||||
_debug('%d,%d -> %d jobs left in force_return_tokens\n'
|
||||
% (_mytokens, _cheats, n))
|
||||
for k in _waitfds.keys():
|
||||
for k in list(_waitfds):
|
||||
del _waitfds[k]
|
||||
_create_tokens(n)
|
||||
if has_token():
|
||||
|
|
@ -405,7 +406,7 @@ def _pre_job(r, w, pfn):
|
|||
pfn()
|
||||
|
||||
|
||||
class Job:
|
||||
class Job(object):
|
||||
def __init__(self, name, pid, donefunc):
|
||||
self.name = name
|
||||
self.pid = pid
|
||||
|
|
@ -418,13 +419,12 @@ class Job:
|
|||
|
||||
def start_job(reason, jobfunc, donefunc):
|
||||
assert state.is_flushed()
|
||||
global _mytokens
|
||||
assert _mytokens <= 1
|
||||
assert _mytokens == 1
|
||||
# Subprocesses always start with 1 token, so we have to destroy ours
|
||||
# in order for the universe to stay in balance.
|
||||
_destroy_tokens(1)
|
||||
r,w = _make_pipe(50)
|
||||
r, w = _make_pipe(50)
|
||||
pid = os.fork()
|
||||
if pid == 0:
|
||||
# child
|
||||
|
|
@ -433,8 +433,8 @@ def start_job(reason, jobfunc, donefunc):
|
|||
try:
|
||||
try:
|
||||
rv = jobfunc() or 0
|
||||
_debug('jobfunc completed (%r, %r)\n' % (jobfunc,rv))
|
||||
except Exception:
|
||||
_debug('jobfunc completed (%r, %r)\n' % (jobfunc, rv))
|
||||
except Exception: # pylint: disable=broad-except
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
finally:
|
||||
|
|
|
|||
27
logs.py
27
logs.py
|
|
@ -1,12 +1,15 @@
|
|||
import os, re, sys, time
|
||||
import vars
|
||||
|
||||
RED = GREEN = YELLOW = BOLD = PLAIN = None
|
||||
|
||||
|
||||
def check_tty(file, color):
|
||||
global RED, GREEN, YELLOW, BOLD, PLAIN
|
||||
color_ok = file.isatty() and (os.environ.get('TERM') or 'dumb') != 'dumb'
|
||||
if (color and color_ok) or color >= 2:
|
||||
# ...use ANSI formatting codes.
|
||||
# pylint: disable=bad-whitespace
|
||||
RED = "\x1b[31m"
|
||||
GREEN = "\x1b[32m"
|
||||
YELLOW = "\x1b[33m"
|
||||
|
|
@ -25,7 +28,7 @@ class RawLog(object):
|
|||
self.file = file
|
||||
|
||||
def write(self, s):
|
||||
assert('\n' not in s)
|
||||
assert '\n' not in s
|
||||
sys.stdout.flush()
|
||||
sys.stderr.flush()
|
||||
self.file.write(s + '\n')
|
||||
|
|
@ -45,11 +48,12 @@ class PrettyLog(object):
|
|||
redo = '%-6d redo ' % pid
|
||||
else:
|
||||
redo = 'redo '
|
||||
self.file.write(''.join([color, redo, vars.DEPTH,
|
||||
BOLD if color else '', s, PLAIN, '\n']))
|
||||
self.file.write(
|
||||
''.join([color, redo, vars.DEPTH,
|
||||
BOLD if color else '', s, PLAIN, '\n']))
|
||||
|
||||
def write(self, s):
|
||||
assert('\n' not in s)
|
||||
assert '\n' not in s
|
||||
sys.stdout.flush()
|
||||
sys.stderr.flush()
|
||||
g = REDO_RE.match(s)
|
||||
|
|
@ -58,7 +62,7 @@ class PrettyLog(object):
|
|||
self.file.write(s[:-len(all)])
|
||||
words = g.group(1).split(':')
|
||||
text = g.group(2)
|
||||
kind, pid, when = words[0:3]
|
||||
kind, pid, _ = words[0:3]
|
||||
pid = int(pid)
|
||||
if kind == 'unchanged':
|
||||
self._pretty(pid, '', '%s (unchanged)' % text)
|
||||
|
|
@ -85,10 +89,10 @@ class PrettyLog(object):
|
|||
self._pretty(pid, GREEN, '%s (...unlocked!)' % text)
|
||||
elif kind == 'error':
|
||||
self.file.write(''.join([RED, 'redo: ',
|
||||
BOLD, text, PLAIN, '\n']))
|
||||
BOLD, text, PLAIN, '\n']))
|
||||
elif kind == 'warning':
|
||||
self.file.write(''.join([YELLOW, 'redo: ',
|
||||
BOLD, text, PLAIN, '\n']))
|
||||
BOLD, text, PLAIN, '\n']))
|
||||
elif kind == 'debug':
|
||||
self._pretty(pid, '', text)
|
||||
else:
|
||||
|
|
@ -118,10 +122,11 @@ def write(s):
|
|||
|
||||
|
||||
def meta(kind, s, pid=None):
|
||||
assert(':' not in kind)
|
||||
assert('@' not in kind)
|
||||
assert('\n' not in s)
|
||||
if pid == None: pid = os.getpid()
|
||||
assert ':' not in kind
|
||||
assert '@' not in kind
|
||||
assert '\n' not in s
|
||||
if pid is None:
|
||||
pid = os.getpid()
|
||||
write('@@REDO:%s:%d:%.4f@@ %s'
|
||||
% (kind, pid, time.time(), s))
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
# pylint: skip-file
|
||||
#
|
||||
# Copyright 2011 Avery Pennarun and options.py contributors.
|
||||
# All rights reserved.
|
||||
#
|
||||
|
|
|
|||
19
paths.py
19
paths.py
|
|
@ -1,19 +1,20 @@
|
|||
import os
|
||||
import vars
|
||||
from logs import err, debug2
|
||||
from logs import debug2
|
||||
|
||||
|
||||
def _default_do_files(filename):
|
||||
l = filename.split('.')
|
||||
for i in range(1,len(l)+1):
|
||||
for i in range(1, len(l)+1):
|
||||
basename = '.'.join(l[:i])
|
||||
ext = '.'.join(l[i:])
|
||||
if ext: ext = '.' + ext
|
||||
if ext:
|
||||
ext = '.' + ext
|
||||
yield ("default%s.do" % ext), basename, ext
|
||||
|
||||
|
||||
def possible_do_files(t):
|
||||
dirname,filename = os.path.split(t)
|
||||
dirname, filename = os.path.split(t)
|
||||
yield (os.path.join(vars.BASE, dirname), "%s.do" % filename,
|
||||
'', filename, '')
|
||||
|
||||
|
|
@ -24,25 +25,25 @@ def possible_do_files(t):
|
|||
# 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(vars.BASE, t))
|
||||
dirname,filename = os.path.split(t)
|
||||
dirname, filename = os.path.split(t)
|
||||
dirbits = dirname.split('/')
|
||||
# since t is an absolute path, dirbits[0] is always '', so we don't
|
||||
# need to count all the way down to i=0.
|
||||
for i in range(len(dirbits), 0, -1):
|
||||
basedir = '/'.join(dirbits[:i])
|
||||
subdir = '/'.join(dirbits[i:])
|
||||
for dofile,basename,ext in _default_do_files(filename):
|
||||
for dofile, basename, ext in _default_do_files(filename):
|
||||
yield (basedir, dofile,
|
||||
subdir, os.path.join(subdir, basename), ext)
|
||||
|
||||
|
||||
def find_do_file(f):
|
||||
for dodir,dofile,basedir,basename,ext in possible_do_files(f.name):
|
||||
for dodir, dofile, basedir, basename, ext in possible_do_files(f.name):
|
||||
dopath = os.path.join(dodir, dofile)
|
||||
debug2('%s: %s:%s ?\n' % (f.name, dodir, dofile))
|
||||
if os.path.exists(dopath):
|
||||
f.add_dep('m', dopath)
|
||||
return dodir,dofile,basedir,basename,ext
|
||||
return dodir, dofile, basedir, basename, ext
|
||||
else:
|
||||
f.add_dep('c', dopath)
|
||||
return None,None,None,None,None
|
||||
return None, None, None, None, None
|
||||
|
|
|
|||
|
|
@ -1,18 +1,22 @@
|
|||
#!/usr/bin/env python2
|
||||
import sys, os
|
||||
import vars, state
|
||||
from logs import err
|
||||
|
||||
|
||||
try:
|
||||
me = os.path.join(vars.STARTDIR,
|
||||
os.path.join(vars.PWD, vars.TARGET))
|
||||
f = state.File(name=me)
|
||||
f.add_dep('m', state.ALWAYS)
|
||||
always = state.File(name=state.ALWAYS)
|
||||
always.stamp = state.STAMP_MISSING
|
||||
always.set_changed()
|
||||
always.save()
|
||||
state.commit()
|
||||
except KeyboardInterrupt:
|
||||
sys.exit(200)
|
||||
def main():
|
||||
try:
|
||||
me = os.path.join(vars.STARTDIR,
|
||||
os.path.join(vars.PWD, vars.TARGET))
|
||||
f = state.File(name=me)
|
||||
f.add_dep('m', state.ALWAYS)
|
||||
always = state.File(name=state.ALWAYS)
|
||||
always.stamp = state.STAMP_MISSING
|
||||
always.set_changed()
|
||||
always.save()
|
||||
state.commit()
|
||||
except KeyboardInterrupt:
|
||||
sys.exit(200)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -5,56 +5,62 @@ import vars_init
|
|||
vars_init.init(sys.argv[1:])
|
||||
|
||||
import vars, state, builder, jwack, deps
|
||||
from helpers import unlink
|
||||
from logs import debug, debug2, err
|
||||
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 = vars.RUNID,
|
||||
dirty = deps.isdirty(f, depth='', max_changed=vars.RUNID,
|
||||
already_checked=[])
|
||||
return f.is_generated, dirty==[f] and deps.DIRTY or dirty
|
||||
return f.is_generated, dirty == [f] and deps.DIRTY or dirty
|
||||
|
||||
|
||||
rv = 202
|
||||
try:
|
||||
if vars_init.is_toplevel and vars.LOG:
|
||||
builder.close_stdin()
|
||||
builder.start_stdin_log_reader(status=True, details=True,
|
||||
pretty=True, color=True, debug_locks=False, debug_pids=False)
|
||||
if vars.TARGET and not vars.UNLOCKED:
|
||||
me = os.path.join(vars.STARTDIR,
|
||||
os.path.join(vars.PWD, vars.TARGET))
|
||||
f = state.File(name=me)
|
||||
debug2('TARGET: %r %r %r\n' % (vars.STARTDIR, vars.PWD, vars.TARGET))
|
||||
else:
|
||||
f = me = None
|
||||
debug2('redo-ifchange: not adding depends.\n')
|
||||
jwack.setup(1)
|
||||
def main():
|
||||
rv = 202
|
||||
try:
|
||||
targets = sys.argv[1:]
|
||||
if f:
|
||||
for t in targets:
|
||||
f.add_dep('m', t)
|
||||
f.save()
|
||||
state.commit()
|
||||
rv = builder.main(targets, should_build)
|
||||
finally:
|
||||
if vars_init.is_toplevel and vars.LOG:
|
||||
builder.close_stdin()
|
||||
builder.start_stdin_log_reader(
|
||||
status=True, details=True,
|
||||
pretty=True, color=True, debug_locks=False, debug_pids=False)
|
||||
if vars.TARGET and not vars.UNLOCKED:
|
||||
me = os.path.join(vars.STARTDIR,
|
||||
os.path.join(vars.PWD, vars.TARGET))
|
||||
f = state.File(name=me)
|
||||
debug2('TARGET: %r %r %r\n'
|
||||
% (vars.STARTDIR, vars.PWD, vars.TARGET))
|
||||
else:
|
||||
f = me = None
|
||||
debug2('redo-ifchange: not adding depends.\n')
|
||||
jwack.setup(1)
|
||||
try:
|
||||
state.rollback()
|
||||
targets = sys.argv[1:]
|
||||
if f:
|
||||
for t in targets:
|
||||
f.add_dep('m', t)
|
||||
f.save()
|
||||
state.commit()
|
||||
rv = builder.main(targets, should_build)
|
||||
finally:
|
||||
try:
|
||||
jwack.force_return_tokens()
|
||||
except Exception, e:
|
||||
traceback.print_exc(100, sys.stderr)
|
||||
err('unexpected error: %r\n' % e)
|
||||
rv = 1
|
||||
except KeyboardInterrupt:
|
||||
state.rollback()
|
||||
finally:
|
||||
try:
|
||||
jwack.force_return_tokens()
|
||||
except Exception, e: # pylint: disable=broad-except
|
||||
traceback.print_exc(100, sys.stderr)
|
||||
err('unexpected error: %r\n' % e)
|
||||
rv = 1
|
||||
except KeyboardInterrupt:
|
||||
if vars_init.is_toplevel:
|
||||
builder.await_log_reader()
|
||||
sys.exit(200)
|
||||
state.commit()
|
||||
if vars_init.is_toplevel:
|
||||
builder.await_log_reader()
|
||||
sys.exit(200)
|
||||
state.commit()
|
||||
if vars_init.is_toplevel:
|
||||
builder.await_log_reader()
|
||||
sys.exit(rv)
|
||||
sys.exit(rv)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -4,19 +4,24 @@ import vars, state
|
|||
from logs import err
|
||||
|
||||
|
||||
try:
|
||||
me = os.path.join(vars.STARTDIR,
|
||||
os.path.join(vars.PWD, vars.TARGET))
|
||||
f = state.File(name=me)
|
||||
for t in sys.argv[1:]:
|
||||
if not t:
|
||||
err('cannot build the empty target ("").\n')
|
||||
sys.exit(204)
|
||||
if os.path.exists(t):
|
||||
err('redo-ifcreate: error: %r already exists\n' % t)
|
||||
sys.exit(1)
|
||||
else:
|
||||
f.add_dep('c', t)
|
||||
state.commit()
|
||||
except KeyboardInterrupt:
|
||||
sys.exit(200)
|
||||
def main():
|
||||
try:
|
||||
me = os.path.join(vars.STARTDIR,
|
||||
os.path.join(vars.PWD, vars.TARGET))
|
||||
f = state.File(name=me)
|
||||
for t in sys.argv[1:]:
|
||||
if not t:
|
||||
err('cannot build the empty target ("").\n')
|
||||
sys.exit(204)
|
||||
if os.path.exists(t):
|
||||
err('redo-ifcreate: error: %r already exists\n' % t)
|
||||
sys.exit(1)
|
||||
else:
|
||||
f.add_dep('c', t)
|
||||
state.commit()
|
||||
except KeyboardInterrupt:
|
||||
sys.exit(200)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
|
|||
109
redo-log.py
109
redo-log.py
|
|
@ -1,5 +1,6 @@
|
|||
#!/usr/bin/env python2
|
||||
import errno, fcntl, os, re, struct, sys, termios, time
|
||||
import errno, fcntl, os, re, struct, sys, time
|
||||
import termios
|
||||
from atoi import atoi
|
||||
import options
|
||||
|
||||
|
|
@ -28,7 +29,6 @@ import vars, logs, state
|
|||
|
||||
topdir = os.getcwd()
|
||||
already = set()
|
||||
queue = []
|
||||
depth = []
|
||||
total_lines = 0
|
||||
status = None
|
||||
|
|
@ -53,11 +53,10 @@ def _atoi(s):
|
|||
def _tty_width():
|
||||
s = struct.pack("HHHH", 0, 0, 0, 0)
|
||||
try:
|
||||
import fcntl, termios
|
||||
s = fcntl.ioctl(sys.stderr.fileno(), termios.TIOCGWINSZ, s)
|
||||
except (IOError, ImportError):
|
||||
return _atoi(os.environ.get('WIDTH')) or 70
|
||||
(ysize,xsize,ypix,xpix) = struct.unpack('HHHH', s)
|
||||
(ysize, xsize, ypix, xpix) = struct.unpack('HHHH', s)
|
||||
return xsize or 70
|
||||
|
||||
|
||||
|
|
@ -69,8 +68,8 @@ def _fix_depth():
|
|||
vars.DEPTH = len(depth) * ' '
|
||||
|
||||
|
||||
def _rel(topdir, mydir, path):
|
||||
return os.path.relpath(os.path.join(topdir, mydir, path), topdir)
|
||||
def _rel(top, mydir, path):
|
||||
return os.path.relpath(os.path.join(top, mydir, path), topdir)
|
||||
|
||||
|
||||
def catlog(t):
|
||||
|
|
@ -92,7 +91,7 @@ def catlog(t):
|
|||
sf = state.File(name=t, allow_add=False)
|
||||
except KeyError:
|
||||
sys.stderr.write('redo-log: [%s] %r: not known to redo.\n'
|
||||
% (os.getcwd(), t,))
|
||||
% (os.getcwd(), t,))
|
||||
sys.exit(24)
|
||||
fid = sf.id
|
||||
del sf
|
||||
|
|
@ -148,9 +147,10 @@ def catlog(t):
|
|||
tail = n + ' ' + tail
|
||||
status = head + tail
|
||||
if len(status) > width:
|
||||
sys.stderr.write('\nOVERSIZE STATUS (%d):\n%r\n' %
|
||||
(len(status), status))
|
||||
assert(len(status) <= width)
|
||||
sys.stderr.write(
|
||||
'\nOVERSIZE STATUS (%d):\n%r\n'
|
||||
% (len(status), status))
|
||||
assert len(status) <= width
|
||||
sys.stdout.flush()
|
||||
sys.stderr.write('\r%-*.*s\r' % (width, width, status))
|
||||
time.sleep(min(delay, 1.0))
|
||||
|
|
@ -184,9 +184,11 @@ def catlog(t):
|
|||
elif fixname not in already:
|
||||
logs.meta('do', relname, pid=pid)
|
||||
if opt.recursive:
|
||||
if loglock: loglock.unlock()
|
||||
if loglock:
|
||||
loglock.unlock()
|
||||
catlog(os.path.join(mydir, text))
|
||||
if loglock: loglock.waitlock(shared=True)
|
||||
if loglock:
|
||||
loglock.waitlock(shared=True)
|
||||
already.add(fixname)
|
||||
elif kind in ('do', 'waiting', 'locked', 'unlocked'):
|
||||
if opt.debug_locks:
|
||||
|
|
@ -196,9 +198,11 @@ def catlog(t):
|
|||
logs.meta('do', relname, pid=pid)
|
||||
if opt.recursive:
|
||||
assert text
|
||||
if loglock: loglock.unlock()
|
||||
if loglock:
|
||||
loglock.unlock()
|
||||
catlog(os.path.join(mydir, text))
|
||||
if loglock: loglock.waitlock(shared=True)
|
||||
if loglock:
|
||||
loglock.waitlock(shared=True)
|
||||
already.add(fixname)
|
||||
elif kind == 'done':
|
||||
rv, name = text.split(' ', 1)
|
||||
|
|
@ -218,40 +222,49 @@ def catlog(t):
|
|||
# partial line never got terminated
|
||||
print line_head
|
||||
if t != '-':
|
||||
assert(depth[-1] == t)
|
||||
assert depth[-1] == t
|
||||
depth.pop(-1)
|
||||
_fix_depth()
|
||||
|
||||
try:
|
||||
if not targets:
|
||||
sys.stderr.write('redo-log: give at least one target; maybe "all"?\n')
|
||||
sys.exit(1)
|
||||
if opt.status < 2 and not os.isatty(2):
|
||||
opt.status = False
|
||||
logs.setup(file=sys.stdout, pretty=opt.pretty, color=opt.color)
|
||||
if opt.debug_locks:
|
||||
vars.DEBUG_LOCKS = 1
|
||||
if opt.debug_pids:
|
||||
vars.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
|
||||
# their old stderr.
|
||||
ack_fd = int(opt.ack_fd)
|
||||
assert(ack_fd > 2)
|
||||
if os.write(ack_fd, 'REDO-OK\n') != 8:
|
||||
raise Exception('write to ack_fd returned wrong length')
|
||||
os.close(ack_fd)
|
||||
queue += targets
|
||||
while queue:
|
||||
t = queue.pop(0)
|
||||
if t != '-':
|
||||
logs.meta('do', _rel(topdir, '.', t), pid=0)
|
||||
catlog(t)
|
||||
except KeyboardInterrupt:
|
||||
sys.exit(200)
|
||||
except IOError, e:
|
||||
if e.errno == errno.EPIPE:
|
||||
pass
|
||||
else:
|
||||
raise
|
||||
|
||||
def main():
|
||||
queue = []
|
||||
try:
|
||||
if not targets:
|
||||
sys.stderr.write(
|
||||
'redo-log: give at least one target; ' +
|
||||
'maybe "all"?\n')
|
||||
sys.exit(1)
|
||||
if opt.status < 2 and not os.isatty(2):
|
||||
opt.status = False
|
||||
logs.setup(file=sys.stdout, pretty=opt.pretty, color=opt.color)
|
||||
if opt.debug_locks:
|
||||
vars.DEBUG_LOCKS = 1
|
||||
if opt.debug_pids:
|
||||
vars.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
|
||||
# their old stderr.
|
||||
ack_fd = int(opt.ack_fd)
|
||||
assert ack_fd > 2
|
||||
if os.write(ack_fd, 'REDO-OK\n') != 8:
|
||||
raise Exception('write to ack_fd returned wrong length')
|
||||
os.close(ack_fd)
|
||||
queue += targets
|
||||
while queue:
|
||||
t = queue.pop(0)
|
||||
if t != '-':
|
||||
logs.meta('do', _rel(topdir, '.', t), pid=0)
|
||||
catlog(t)
|
||||
except KeyboardInterrupt:
|
||||
sys.exit(200)
|
||||
except IOError, e:
|
||||
if e.errno == errno.EPIPE:
|
||||
pass
|
||||
else:
|
||||
raise
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
|
|||
27
redo-ood.py
27
redo-ood.py
|
|
@ -27,14 +27,19 @@ def log_override(name):
|
|||
pass
|
||||
|
||||
|
||||
cwd = os.getcwd()
|
||||
for f in state.files():
|
||||
if f.is_target():
|
||||
if deps.isdirty(f,
|
||||
depth='',
|
||||
max_changed=vars.RUNID,
|
||||
already_checked=[],
|
||||
is_checked=is_checked,
|
||||
set_checked=set_checked,
|
||||
log_override=log_override):
|
||||
print state.relpath(os.path.join(vars.BASE, f.name), cwd)
|
||||
def main():
|
||||
cwd = os.getcwd()
|
||||
for f in state.files():
|
||||
if f.is_target():
|
||||
if deps.isdirty(f,
|
||||
depth='',
|
||||
max_changed=vars.RUNID,
|
||||
already_checked=[],
|
||||
is_checked=is_checked,
|
||||
set_checked=set_checked,
|
||||
log_override=log_override):
|
||||
print state.relpath(os.path.join(vars.BASE, f.name), cwd)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -7,11 +7,17 @@ vars_init.init([])
|
|||
import state, vars
|
||||
from logs import err
|
||||
|
||||
if len(sys.argv[1:]) != 0:
|
||||
err('%s: no arguments expected.\n' % sys.argv[0])
|
||||
sys.exit(1)
|
||||
|
||||
cwd = os.getcwd()
|
||||
for f in state.files():
|
||||
if f.is_source():
|
||||
print state.relpath(os.path.join(vars.BASE, f.name), cwd)
|
||||
def main():
|
||||
if len(sys.argv[1:]) != 0:
|
||||
err('%s: no arguments expected.\n' % sys.argv[0])
|
||||
sys.exit(1)
|
||||
|
||||
cwd = os.getcwd()
|
||||
for f in state.files():
|
||||
if f.is_source():
|
||||
print state.relpath(os.path.join(vars.BASE, f.name), cwd)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -3,51 +3,58 @@ import sys, os
|
|||
import vars, state
|
||||
from logs import err, debug2
|
||||
|
||||
if len(sys.argv) > 1:
|
||||
err('%s: no arguments expected.\n' % sys.argv[0])
|
||||
sys.exit(1)
|
||||
|
||||
if os.isatty(0):
|
||||
err('%s: you must provide the data to stamp on stdin\n' % sys.argv[0])
|
||||
sys.exit(1)
|
||||
def main():
|
||||
if len(sys.argv) > 1:
|
||||
err('%s: no arguments expected.\n' % sys.argv[0])
|
||||
sys.exit(1)
|
||||
|
||||
# hashlib is only available in python 2.5 or higher, but the 'sha' module
|
||||
# produces a DeprecationWarning in python 2.6 or higher. We want to support
|
||||
# python 2.4 and above without any stupid warnings, so let's try using hashlib
|
||||
# first, and downgrade if it fails.
|
||||
try:
|
||||
import hashlib
|
||||
except ImportError:
|
||||
import sha
|
||||
sh = sha.sha()
|
||||
else:
|
||||
sh = hashlib.sha1()
|
||||
if os.isatty(0):
|
||||
err('%s: you must provide the data to stamp on stdin\n' % sys.argv[0])
|
||||
sys.exit(1)
|
||||
|
||||
while 1:
|
||||
b = os.read(0, 4096)
|
||||
sh.update(b)
|
||||
if not b: break
|
||||
# hashlib is only available in python 2.5 or higher, but the 'sha'
|
||||
# module produces a DeprecationWarning in python 2.6 or higher. We want
|
||||
# to support python 2.4 and above without any stupid warnings, so let's
|
||||
# try using hashlib first, and downgrade if it fails.
|
||||
try:
|
||||
import hashlib
|
||||
except ImportError:
|
||||
import sha
|
||||
sh = sha.sha()
|
||||
else:
|
||||
sh = hashlib.sha1()
|
||||
|
||||
csum = sh.hexdigest()
|
||||
while 1:
|
||||
b = os.read(0, 4096)
|
||||
sh.update(b)
|
||||
if not b:
|
||||
break
|
||||
|
||||
if not vars.TARGET:
|
||||
sys.exit(0)
|
||||
csum = sh.hexdigest()
|
||||
|
||||
me = os.path.join(vars.STARTDIR,
|
||||
os.path.join(vars.PWD, vars.TARGET))
|
||||
f = state.File(name=me)
|
||||
changed = (csum != f.csum)
|
||||
debug2('%s: old = %s\n' % (f.name, f.csum))
|
||||
debug2('%s: sum = %s (%s)\n' % (f.name, csum,
|
||||
changed and 'changed' or 'unchanged'))
|
||||
f.is_generated = True
|
||||
f.is_override = False
|
||||
f.failed_runid = None
|
||||
if changed:
|
||||
f.set_changed() # update_stamp might not do this if the mtime is identical
|
||||
f.csum = csum
|
||||
else:
|
||||
# unchanged
|
||||
f.set_checked()
|
||||
f.save()
|
||||
state.commit()
|
||||
if not vars.TARGET:
|
||||
sys.exit(0)
|
||||
|
||||
me = os.path.join(vars.STARTDIR,
|
||||
os.path.join(vars.PWD, vars.TARGET))
|
||||
f = state.File(name=me)
|
||||
changed = (csum != f.csum)
|
||||
debug2('%s: old = %s\n' % (f.name, f.csum))
|
||||
debug2('%s: sum = %s (%s)\n' % (f.name, csum,
|
||||
changed and 'changed' or 'unchanged'))
|
||||
f.is_generated = True
|
||||
f.is_override = False
|
||||
f.failed_runid = None
|
||||
if changed:
|
||||
f.set_changed() # update_stamp might skip this if mtime is identical
|
||||
f.csum = csum
|
||||
else:
|
||||
# unchanged
|
||||
f.set_checked()
|
||||
f.save()
|
||||
state.commit()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -7,11 +7,17 @@ vars_init.init([])
|
|||
import state, vars
|
||||
from logs import err
|
||||
|
||||
if len(sys.argv[1:]) != 0:
|
||||
err('%s: no arguments expected.\n' % sys.argv[0])
|
||||
sys.exit(1)
|
||||
|
||||
cwd = os.getcwd()
|
||||
for f in state.files():
|
||||
if f.is_target():
|
||||
print state.relpath(os.path.join(vars.BASE, f.name), cwd)
|
||||
def main():
|
||||
if len(sys.argv[1:]) != 0:
|
||||
err('%s: no arguments expected.\n' % sys.argv[0])
|
||||
sys.exit(1)
|
||||
|
||||
cwd = os.getcwd()
|
||||
for f in state.files():
|
||||
if f.is_target():
|
||||
print state.relpath(os.path.join(vars.BASE, f.name), cwd)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -3,33 +3,39 @@ import sys, os
|
|||
import state
|
||||
from logs 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:]
|
||||
def main():
|
||||
if len(sys.argv[1:]) < 2:
|
||||
err('%s: at least 2 arguments expected.\n' % sys.argv[0])
|
||||
sys.exit(1)
|
||||
|
||||
for d in deps:
|
||||
assert(d != target)
|
||||
target = sys.argv[1]
|
||||
deps = sys.argv[2:]
|
||||
|
||||
me = state.File(name=target)
|
||||
for d in deps:
|
||||
assert d != target
|
||||
|
||||
# Build the known dependencies of our primary target. This *does* require
|
||||
# grabbing locks.
|
||||
os.environ['REDO_NO_OOB'] = '1'
|
||||
argv = ['redo-ifchange'] + deps
|
||||
rv = os.spawnvp(os.P_WAIT, argv[0], argv)
|
||||
if rv:
|
||||
sys.exit(rv)
|
||||
me = state.File(name=target)
|
||||
|
||||
# We know our caller already owns the lock on target, so we don't have to
|
||||
# acquire another one; tell redo-ifchange about that. Also, REDO_NO_OOB
|
||||
# persists from up above, because we don't want to do OOB now either.
|
||||
# (Actually it's most important for the primary target, since it's the one
|
||||
# who initiated the OOB in the first place.)
|
||||
os.environ['REDO_UNLOCKED'] = '1'
|
||||
argv = ['redo-ifchange', target]
|
||||
rv = os.spawnvp(os.P_WAIT, argv[0], argv)
|
||||
if rv:
|
||||
sys.exit(rv)
|
||||
# Build the known dependencies of our primary target. This *does* require
|
||||
# grabbing locks.
|
||||
os.environ['REDO_NO_OOB'] = '1'
|
||||
argv = ['redo-ifchange'] + deps
|
||||
rv = os.spawnvp(os.P_WAIT, argv[0], argv)
|
||||
if rv:
|
||||
sys.exit(rv)
|
||||
|
||||
# We know our caller already owns the lock on target, so we don't have to
|
||||
# acquire another one; tell redo-ifchange about that. Also, REDO_NO_OOB
|
||||
# persists from up above, because we don't want to do OOB now either.
|
||||
# (Actually it's most important for the primary target, since it's the one
|
||||
# who initiated the OOB in the first place.)
|
||||
os.environ['REDO_UNLOCKED'] = '1'
|
||||
argv = ['redo-ifchange', target]
|
||||
rv = os.spawnvp(os.P_WAIT, argv[0], argv)
|
||||
if rv:
|
||||
sys.exit(rv)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -7,22 +7,29 @@ vars_init.init_no_state()
|
|||
import paths
|
||||
from logs import err
|
||||
|
||||
if len(sys.argv[1:]) != 1:
|
||||
err('%s: exactly one argument expected.\n' % sys.argv[0])
|
||||
sys.exit(1)
|
||||
|
||||
want = sys.argv[1]
|
||||
if not want:
|
||||
err('cannot build the empty target ("").\n')
|
||||
sys.exit(204)
|
||||
def main():
|
||||
if len(sys.argv[1:]) != 1:
|
||||
err('%s: exactly one argument expected.\n' % sys.argv[0])
|
||||
sys.exit(1)
|
||||
|
||||
abswant = os.path.abspath(want)
|
||||
for dodir,dofile,basedir,basename,ext in paths.possible_do_files(abswant):
|
||||
dopath = os.path.join('/', dodir, dofile)
|
||||
relpath = os.path.relpath(dopath, '.')
|
||||
exists = os.path.exists(dopath)
|
||||
assert('\n' not in relpath)
|
||||
print relpath
|
||||
if exists:
|
||||
sys.exit(0)
|
||||
sys.exit(1) # no appropriate dofile found
|
||||
want = sys.argv[1]
|
||||
if not want:
|
||||
err('cannot build the empty target ("").\n')
|
||||
sys.exit(204)
|
||||
|
||||
abswant = os.path.abspath(want)
|
||||
pdf = paths.possible_do_files(abswant)
|
||||
for dodir, dofile, basedir, basename, ext in pdf:
|
||||
dopath = os.path.join('/', dodir, dofile)
|
||||
relpath = os.path.relpath(dopath, '.')
|
||||
exists = os.path.exists(dopath)
|
||||
assert '\n' not in relpath
|
||||
print relpath
|
||||
if exists:
|
||||
sys.exit(0)
|
||||
sys.exit(1) # no appropriate dofile found
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
|
|||
11
redo.py
11
redo.py
|
|
@ -16,7 +16,7 @@
|
|||
#
|
||||
import sys, os, traceback
|
||||
import options
|
||||
from helpers import atoi
|
||||
from atoi import atoi
|
||||
|
||||
optspec = """
|
||||
redo [targets...]
|
||||
|
|
@ -83,7 +83,8 @@ try:
|
|||
if vars_init.is_toplevel and (vars.LOG or j > 1):
|
||||
builder.close_stdin()
|
||||
if vars_init.is_toplevel and vars.LOG:
|
||||
builder.start_stdin_log_reader(status=opt.status, details=opt.details,
|
||||
builder.start_stdin_log_reader(
|
||||
status=opt.status, details=opt.details,
|
||||
pretty=opt.pretty, color=opt.color,
|
||||
debug_locks=opt.debug_locks, debug_pids=opt.debug_pids)
|
||||
for t in targets:
|
||||
|
|
@ -98,16 +99,16 @@ try:
|
|||
err('invalid --jobs value: %r\n' % opt.jobs)
|
||||
jwack.setup(j)
|
||||
try:
|
||||
assert(state.is_flushed())
|
||||
assert state.is_flushed()
|
||||
retcode = builder.main(targets, lambda t: (True, True))
|
||||
assert(state.is_flushed())
|
||||
assert state.is_flushed()
|
||||
finally:
|
||||
try:
|
||||
state.rollback()
|
||||
finally:
|
||||
try:
|
||||
jwack.force_return_tokens()
|
||||
except Exception, e:
|
||||
except Exception, e: # pylint: disable=broad-except
|
||||
traceback.print_exc(100, sys.stderr)
|
||||
err('unexpected error: %r\n' % e)
|
||||
retcode = 1
|
||||
|
|
|
|||
70
state.py
70
state.py
|
|
@ -1,7 +1,7 @@
|
|||
import sys, os, errno, glob, stat, fcntl, sqlite3
|
||||
import sys, os, errno, stat, fcntl, sqlite3
|
||||
import vars
|
||||
from helpers import unlink, close_on_exec, join
|
||||
from logs import warn, err, debug2, debug3
|
||||
from logs import warn, debug2, debug3
|
||||
|
||||
# When the module is imported, change the process title.
|
||||
# We do it here because this module is imported by all the scripts.
|
||||
|
|
@ -14,17 +14,18 @@ else:
|
|||
cmdline[0] = os.path.splitext(os.path.basename(cmdline[0]))[0]
|
||||
setproctitle(" ".join(cmdline))
|
||||
|
||||
SCHEMA_VER=2
|
||||
TIMEOUT=60
|
||||
SCHEMA_VER = 2
|
||||
TIMEOUT = 60
|
||||
|
||||
ALWAYS='//ALWAYS' # an invalid filename that is always marked as dirty
|
||||
STAMP_DIR='dir' # the stamp of a directory; mtime is unhelpful
|
||||
STAMP_MISSING='0' # the stamp of a nonexistent file
|
||||
ALWAYS = '//ALWAYS' # an invalid filename that is always marked as dirty
|
||||
STAMP_DIR = 'dir' # the stamp of a directory; mtime is unhelpful
|
||||
STAMP_MISSING = '0' # the stamp of a nonexistent file
|
||||
|
||||
LOG_LOCK_MAGIC=0x10000000 # fid offset for "log locks"
|
||||
LOG_LOCK_MAGIC = 0x10000000 # fid offset for "log locks"
|
||||
|
||||
|
||||
class CyclicDependencyError(Exception): pass
|
||||
class CyclicDependencyError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def _connect(dbfile):
|
||||
|
|
@ -74,7 +75,8 @@ def db():
|
|||
if ver != SCHEMA_VER:
|
||||
# Don't use err() here because this might happen before
|
||||
# redo-log spawns.
|
||||
sys.stderr.write('redo: %s: found v%s (expected v%s)\n'
|
||||
sys.stderr.write(
|
||||
'redo: %s: found v%s (expected v%s)\n'
|
||||
% (dbfile, ver, SCHEMA_VER))
|
||||
sys.stderr.write('redo: manually delete .redo dir to start over.\n')
|
||||
sys.exit(1)
|
||||
|
|
@ -155,7 +157,7 @@ def is_flushed():
|
|||
|
||||
_insane = None
|
||||
def check_sane():
|
||||
global _insane, _writable
|
||||
global _insane
|
||||
if not _insane:
|
||||
_insane = not os.path.exists('%s/.redo' % vars.BASE)
|
||||
return not _insane
|
||||
|
|
@ -170,7 +172,7 @@ def relpath(t, base):
|
|||
base = os.path.normpath(base)
|
||||
tparts = t.split('/')
|
||||
bparts = base.split('/')
|
||||
for tp,bp in zip(tparts,bparts):
|
||||
for tp, bp in zip(tparts, bparts):
|
||||
if tp != bp:
|
||||
break
|
||||
tparts.pop(0)
|
||||
|
|
@ -225,13 +227,16 @@ class File(object):
|
|||
# use this mostly to avoid accidentally assigning to typos
|
||||
__slots__ = ['id'] + _file_cols[1:]
|
||||
|
||||
# These warnings are a result of the weird way this class is
|
||||
# initialized, which we should fix, and then re-enable warning.
|
||||
# pylint: disable=attribute-defined-outside-init
|
||||
def _init_from_idname(self, id, name, allow_add):
|
||||
q = ('select %s from Files ' % join(', ', _file_cols))
|
||||
if id != None:
|
||||
q += 'where rowid=?'
|
||||
l = [id]
|
||||
elif name != None:
|
||||
name = (name==ALWAYS) and ALWAYS or relpath(name, vars.BASE)
|
||||
name = (name == ALWAYS) and ALWAYS or relpath(name, vars.BASE)
|
||||
q += 'where name=?'
|
||||
l = [name]
|
||||
else:
|
||||
|
|
@ -250,7 +255,7 @@ class File(object):
|
|||
# big deal.
|
||||
pass
|
||||
row = d.execute(q, l).fetchone()
|
||||
assert(row)
|
||||
assert row
|
||||
return self._init_from_cols(row)
|
||||
|
||||
def _init_from_cols(self, cols):
|
||||
|
|
@ -262,9 +267,9 @@ class File(object):
|
|||
|
||||
def __init__(self, id=None, name=None, cols=None, allow_add=True):
|
||||
if cols:
|
||||
return self._init_from_cols(cols)
|
||||
self._init_from_cols(cols)
|
||||
else:
|
||||
return self._init_from_idname(id, name, allow_add=allow_add)
|
||||
self._init_from_idname(id, name, allow_add=allow_add)
|
||||
|
||||
def __repr__(self):
|
||||
return "File(%r)" % (self.nicename(),)
|
||||
|
|
@ -337,13 +342,13 @@ class File(object):
|
|||
return False # special name, ignore
|
||||
newstamp = self.read_stamp()
|
||||
if (self.is_generated and
|
||||
(not self.is_failed() or newstamp != STAMP_MISSING) and
|
||||
not self.is_override and
|
||||
self.stamp == newstamp):
|
||||
(not self.is_failed() or newstamp != STAMP_MISSING) and
|
||||
not self.is_override and
|
||||
self.stamp == newstamp):
|
||||
# target is as we left it
|
||||
return False
|
||||
if ((not self.is_generated or self.stamp != newstamp) and
|
||||
newstamp == STAMP_MISSING):
|
||||
newstamp == STAMP_MISSING):
|
||||
# target has gone missing after the last build.
|
||||
# It's not usefully a source *or* a target.
|
||||
return False
|
||||
|
|
@ -375,8 +380,8 @@ class File(object):
|
|||
for row in db().execute(q, [self.id]).fetchall():
|
||||
mode = row[0]
|
||||
cols = row[1:]
|
||||
assert(mode in ('c', 'm'))
|
||||
yield mode,File(cols=cols)
|
||||
assert mode in ('c', 'm')
|
||||
yield mode, File(cols=cols)
|
||||
|
||||
def zap_deps1(self):
|
||||
debug2('zap-deps1: %r\n' % self.name)
|
||||
|
|
@ -389,7 +394,7 @@ class File(object):
|
|||
def add_dep(self, mode, dep):
|
||||
src = File(name=dep)
|
||||
debug3('add-dep: "%s" < %s "%s"\n' % (self.name, mode, src.name))
|
||||
assert(self.id != src.id)
|
||||
assert self.id != src.id
|
||||
_write("insert or replace into Deps "
|
||||
" (target, mode, source, delete_me) values (?,?,?,?)",
|
||||
[self.id, mode, src.id, False])
|
||||
|
|
@ -404,10 +409,12 @@ class File(object):
|
|||
return False, STAMP_DIR
|
||||
else:
|
||||
# a "unique identifier" stamp for a regular file
|
||||
return (stat.S_ISLNK(st.st_mode),
|
||||
return (
|
||||
stat.S_ISLNK(st.st_mode),
|
||||
'-'.join(str(s) for s in
|
||||
('%.6f' % st.st_mtime, st.st_size, st.st_ino,
|
||||
st.st_mode, st.st_uid, st.st_gid)))
|
||||
st.st_mode, st.st_uid, st.st_gid))
|
||||
)
|
||||
|
||||
def read_stamp(self):
|
||||
is_link, pre = self._read_stamp_st(os.lstat)
|
||||
|
|
@ -444,12 +451,12 @@ def logname(fid):
|
|||
# ok, but it doesn't have F_GETLK, so we can't report which pid owns the lock.
|
||||
# The makes debugging a bit harder. When we someday port to C, we can do that.
|
||||
_locks = {}
|
||||
class Lock:
|
||||
class Lock(object):
|
||||
def __init__(self, fid):
|
||||
self.owned = False
|
||||
self.fid = fid
|
||||
assert(_lockfile >= 0)
|
||||
assert(_locks.get(fid,0) == 0)
|
||||
assert _lockfile >= 0
|
||||
assert _locks.get(fid, 0) == 0
|
||||
_locks[fid] = 1
|
||||
|
||||
def __del__(self):
|
||||
|
|
@ -458,7 +465,7 @@ class Lock:
|
|||
self.unlock()
|
||||
|
||||
def check(self):
|
||||
assert(not self.owned)
|
||||
assert not self.owned
|
||||
if str(self.fid) in vars.get_locks():
|
||||
# Lock already held by parent: cyclic dependence
|
||||
raise CyclicDependencyError()
|
||||
|
|
@ -480,7 +487,8 @@ class Lock:
|
|||
def waitlock(self, shared=False):
|
||||
self.check()
|
||||
assert not self.owned
|
||||
fcntl.lockf(_lockfile,
|
||||
fcntl.lockf(
|
||||
_lockfile,
|
||||
fcntl.LOCK_SH if shared else fcntl.LOCK_EX,
|
||||
1, self.fid)
|
||||
self.owned = True
|
||||
|
|
@ -488,6 +496,6 @@ class Lock:
|
|||
def unlock(self):
|
||||
if not self.owned:
|
||||
raise Exception("can't unlock %r - we don't own it"
|
||||
% self.lockname)
|
||||
% self.fid)
|
||||
fcntl.lockf(_lockfile, fcntl.LOCK_UN, 1, self.fid)
|
||||
self.owned = False
|
||||
|
|
|
|||
12
vars.py
12
vars.py
|
|
@ -36,11 +36,11 @@ os.environ['REDO_NO_OOB'] = '' # not inheritable by subprocesses
|
|||
|
||||
|
||||
def get_locks():
|
||||
"""Get the list of held locks."""
|
||||
return os.environ.get('REDO_LOCKS', '').split(':')
|
||||
"""Get the list of held locks."""
|
||||
return os.environ.get('REDO_LOCKS', '').split(':')
|
||||
|
||||
def add_lock(name):
|
||||
"""Add a lock to the list of held locks."""
|
||||
locks = set(get_locks())
|
||||
locks.add(name)
|
||||
os.environ['REDO_LOCKS'] = ':'.join(list(locks))
|
||||
"""Add a lock to the list of held locks."""
|
||||
locks = set(get_locks())
|
||||
locks.add(name)
|
||||
os.environ['REDO_LOCKS'] = ':'.join(list(locks))
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ is_toplevel = False
|
|||
|
||||
|
||||
def init_no_state():
|
||||
global is_toplevel
|
||||
if not os.environ.get('REDO'):
|
||||
os.environ['REDO'] = 'NOT_DEFINED'
|
||||
is_toplevel = True
|
||||
|
|
@ -13,9 +14,9 @@ def init_no_state():
|
|||
|
||||
|
||||
def init(targets):
|
||||
global is_toplevel
|
||||
if not os.environ.get('REDO'):
|
||||
# toplevel call to redo
|
||||
global is_toplevel
|
||||
is_toplevel = True
|
||||
if len(targets) == 0:
|
||||
targets.append('all')
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue