2012-04-27 16:10:21 +02:00
|
|
|
"""Bring time stamps of generated checked-in files into the right order
|
|
|
|
|
|
|
|
A versioned configuration file .hgtouch specifies generated files, in the
|
|
|
|
syntax of make rules.
|
|
|
|
|
|
|
|
output: input1 input2
|
|
|
|
|
|
|
|
In addition to the dependency syntax, #-comments are supported.
|
|
|
|
"""
|
2012-04-27 17:56:30 +02:00
|
|
|
import errno
|
2012-04-27 16:10:21 +02:00
|
|
|
import os
|
2013-09-30 16:09:44 +02:00
|
|
|
import time
|
2012-04-27 16:10:21 +02:00
|
|
|
|
|
|
|
def parse_config(repo):
|
2012-04-27 17:56:30 +02:00
|
|
|
try:
|
|
|
|
fp = repo.wfile(".hgtouch")
|
|
|
|
except IOError, e:
|
|
|
|
if e.errno != errno.ENOENT:
|
|
|
|
raise
|
2012-04-27 16:10:21 +02:00
|
|
|
return {}
|
|
|
|
result = {}
|
2012-04-27 17:56:30 +02:00
|
|
|
with fp:
|
|
|
|
for line in fp:
|
2012-04-27 16:10:21 +02:00
|
|
|
# strip comments
|
|
|
|
line = line.split('#')[0].strip()
|
|
|
|
if ':' not in line:
|
|
|
|
continue
|
|
|
|
outputs, inputs = line.split(':', 1)
|
|
|
|
outputs = outputs.split()
|
|
|
|
inputs = inputs.split()
|
|
|
|
for o in outputs:
|
|
|
|
try:
|
|
|
|
result[o].extend(inputs)
|
|
|
|
except KeyError:
|
|
|
|
result[o] = inputs
|
|
|
|
return result
|
|
|
|
|
2014-01-27 08:22:49 +01:00
|
|
|
def check_rule(ui, repo, modified, basedir, output, inputs):
|
2013-09-30 16:09:44 +02:00
|
|
|
"""Verify that the output is newer than any of the inputs.
|
|
|
|
Return (status, stamp), where status is True if the update succeeded,
|
|
|
|
and stamp is the newest time stamp assigned to any file (might be in
|
2014-01-27 08:22:49 +01:00
|
|
|
the future).
|
|
|
|
|
|
|
|
If basedir is nonempty, it gives a directory in which the tree is to
|
|
|
|
be checked.
|
|
|
|
"""
|
|
|
|
f_output = repo.wjoin(os.path.join(basedir, output))
|
2012-04-27 16:10:21 +02:00
|
|
|
try:
|
|
|
|
o_time = os.stat(f_output).st_mtime
|
|
|
|
except OSError:
|
|
|
|
ui.warn("Generated file %s does not exist\n" % output)
|
2013-09-30 16:09:44 +02:00
|
|
|
return False, 0
|
|
|
|
youngest = 0 # youngest dependency
|
2012-04-27 16:10:21 +02:00
|
|
|
backdate = None
|
|
|
|
backdate_source = None
|
|
|
|
for i in inputs:
|
2014-01-27 08:22:49 +01:00
|
|
|
f_i = repo.wjoin(os.path.join(basedir, i))
|
2012-04-27 16:10:21 +02:00
|
|
|
try:
|
|
|
|
i_time = os.stat(f_i).st_mtime
|
|
|
|
except OSError:
|
|
|
|
ui.warn(".hgtouch input file %s does not exist\n" % i)
|
2013-09-30 16:09:44 +02:00
|
|
|
return False, 0
|
2012-04-27 16:10:21 +02:00
|
|
|
if i in modified:
|
|
|
|
# input is modified. Need to backdate at least to i_time
|
|
|
|
if backdate is None or backdate > i_time:
|
|
|
|
backdate = i_time
|
|
|
|
backdate_source = i
|
|
|
|
continue
|
2013-09-30 16:09:44 +02:00
|
|
|
youngest = max(i_time, youngest)
|
2012-04-27 16:10:21 +02:00
|
|
|
if backdate is not None:
|
|
|
|
ui.warn("Input %s for file %s locally modified\n" % (backdate_source, output))
|
|
|
|
# set to 1s before oldest modified input
|
|
|
|
backdate -= 1
|
|
|
|
os.utime(f_output, (backdate, backdate))
|
2013-09-30 16:09:44 +02:00
|
|
|
return False, 0
|
|
|
|
if youngest >= o_time:
|
2012-04-27 16:10:21 +02:00
|
|
|
ui.note("Touching %s\n" % output)
|
2013-09-30 16:09:44 +02:00
|
|
|
youngest += 1
|
|
|
|
os.utime(f_output, (youngest, youngest))
|
|
|
|
return True, youngest
|
|
|
|
else:
|
|
|
|
# Nothing to update
|
|
|
|
return True, 0
|
2012-04-27 16:10:21 +02:00
|
|
|
|
2014-01-27 08:22:49 +01:00
|
|
|
def do_touch(ui, repo, basedir):
|
|
|
|
if basedir:
|
|
|
|
if not os.path.isdir(repo.wjoin(basedir)):
|
|
|
|
ui.warn("Abort: basedir %r does not exist\n" % basedir)
|
|
|
|
return
|
|
|
|
modified = []
|
|
|
|
else:
|
|
|
|
modified = repo.status()[0]
|
2012-04-27 16:10:21 +02:00
|
|
|
dependencies = parse_config(repo)
|
|
|
|
success = True
|
2013-09-30 16:09:44 +02:00
|
|
|
tstamp = 0 # newest time stamp assigned
|
2012-04-27 16:10:21 +02:00
|
|
|
# try processing all rules in topological order
|
|
|
|
hold_back = {}
|
|
|
|
while dependencies:
|
|
|
|
output, inputs = dependencies.popitem()
|
|
|
|
# check whether any of the inputs is generated
|
|
|
|
for i in inputs:
|
|
|
|
if i in dependencies:
|
|
|
|
hold_back[output] = inputs
|
|
|
|
continue
|
2014-01-27 08:22:49 +01:00
|
|
|
_success, _tstamp = check_rule(ui, repo, modified, basedir, output, inputs)
|
|
|
|
success = success and _success
|
2013-09-30 16:09:44 +02:00
|
|
|
tstamp = max(tstamp, _tstamp)
|
2012-04-27 16:10:21 +02:00
|
|
|
# put back held back rules
|
|
|
|
dependencies.update(hold_back)
|
|
|
|
hold_back = {}
|
2013-09-30 16:09:44 +02:00
|
|
|
now = time.time()
|
|
|
|
if tstamp > now:
|
|
|
|
# wait until real time has passed the newest time stamp, to
|
|
|
|
# avoid having files dated in the future
|
|
|
|
time.sleep(tstamp-now)
|
2012-04-27 16:10:21 +02:00
|
|
|
if hold_back:
|
|
|
|
ui.warn("Cyclic dependency involving %s\n" % (' '.join(hold_back.keys())))
|
|
|
|
return False
|
|
|
|
return success
|
|
|
|
|
2014-01-27 08:22:49 +01:00
|
|
|
def touch(ui, repo, basedir):
|
2012-04-27 16:10:21 +02:00
|
|
|
"touch generated files that are older than their sources after an update."
|
2014-01-27 08:22:49 +01:00
|
|
|
do_touch(ui, repo, basedir)
|
2012-04-27 16:10:21 +02:00
|
|
|
|
|
|
|
cmdtable = {
|
2014-01-27 08:22:49 +01:00
|
|
|
"touch": (touch,
|
2014-03-09 10:22:10 +01:00
|
|
|
[('b', 'basedir', '', 'base dir of the tree to apply touching')],
|
2014-01-27 08:22:49 +01:00
|
|
|
"hg touch [-b BASEDIR]")
|
2012-04-27 16:10:21 +02:00
|
|
|
}
|