summaryrefslogtreecommitdiff
path: root/Tools/hg
diff options
context:
space:
mode:
authorMartin v. Loewis <martin@v.loewis.de>2012-04-27 16:10:21 +0200
committerMartin v. Loewis <martin@v.loewis.de>2012-04-27 16:10:21 +0200
commita4ac4d3b8705113e96acd4eabcedb4c5836abfaf (patch)
treeca5be83029286a6ea859e0da5a04d13bb12b6995 /Tools/hg
parent2555301e0aafefea124fe79f268207bc752a4722 (diff)
downloadcpython-a4ac4d3b8705113e96acd4eabcedb4c5836abfaf.tar.gz
Issue #14642: Add "hg touch" extension, and "make touch" target.
Diffstat (limited to 'Tools/hg')
-rw-r--r--Tools/hg/hgtouch.py99
1 files changed, 99 insertions, 0 deletions
diff --git a/Tools/hg/hgtouch.py b/Tools/hg/hgtouch.py
new file mode 100644
index 0000000000..c7fde1057d
--- /dev/null
+++ b/Tools/hg/hgtouch.py
@@ -0,0 +1,99 @@
+"""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.
+"""
+import os
+
+def parse_config(repo):
+ configfile = repo.wjoin(".hgtouch")
+ if not os.path.exists(configfile):
+ return {}
+ result = {}
+ with open(configfile) as f:
+ for line in f:
+ # 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
+
+def check_rule(ui, repo, modified, output, inputs):
+ f_output = repo.wjoin(output)
+ try:
+ o_time = os.stat(f_output).st_mtime
+ except OSError:
+ ui.warn("Generated file %s does not exist\n" % output)
+ return False
+ need_touch = False
+ backdate = None
+ backdate_source = None
+ for i in inputs:
+ f_i = repo.wjoin(i)
+ try:
+ i_time = os.stat(f_i).st_mtime
+ except OSError:
+ ui.warn(".hgtouch input file %s does not exist\n" % i)
+ return False
+ 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
+ if o_time <= i_time:
+ # generated file is older, touch
+ need_touch = True
+ 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))
+ return False
+ if need_touch:
+ ui.note("Touching %s\n" % output)
+ os.utime(f_output, None)
+ return True
+
+def do_touch(ui, repo):
+ modified = repo.status()[0]
+ dependencies = parse_config(repo)
+ success = True
+ # 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
+ success = check_rule(ui, repo, modified, output, inputs)
+ # put back held back rules
+ dependencies.update(hold_back)
+ hold_back = {}
+ if hold_back:
+ ui.warn("Cyclic dependency involving %s\n" % (' '.join(hold_back.keys())))
+ return False
+ return success
+
+def touch(ui, repo):
+ "touch generated files that are older than their sources after an update."
+ do_touch(ui, repo)
+
+cmdtable = {
+ "touch": (touch, [],
+ "touch generated files according to the .hgtouch configuration")
+}