blob: 119d81214826f62d812a8b923432da783d321859 [file] [log] [blame]
Martin v. Loewiscfc1cc22012-04-27 16:10:21 +02001"""Bring time stamps of generated checked-in files into the right order
2
3A versioned configuration file .hgtouch specifies generated files, in the
4syntax of make rules.
5
6 output: input1 input2
7
8In addition to the dependency syntax, #-comments are supported.
9"""
Benjamin Peterson1f09c662013-09-27 09:11:21 -040010from __future__ import with_statement
Benjamin Peterson0e1a5b42012-04-27 11:56:30 -040011import errno
Martin v. Loewiscfc1cc22012-04-27 16:10:21 +020012import os
Martin v. Löwisbf526482013-09-30 16:09:44 +020013import time
Martin v. Loewiscfc1cc22012-04-27 16:10:21 +020014
15def parse_config(repo):
Benjamin Peterson0e1a5b42012-04-27 11:56:30 -040016 try:
17 fp = repo.wfile(".hgtouch")
18 except IOError, e:
19 if e.errno != errno.ENOENT:
20 raise
Martin v. Loewiscfc1cc22012-04-27 16:10:21 +020021 return {}
22 result = {}
Benjamin Peterson0e1a5b42012-04-27 11:56:30 -040023 with fp:
24 for line in fp:
Martin v. Loewiscfc1cc22012-04-27 16:10:21 +020025 # strip comments
26 line = line.split('#')[0].strip()
27 if ':' not in line:
28 continue
29 outputs, inputs = line.split(':', 1)
30 outputs = outputs.split()
31 inputs = inputs.split()
32 for o in outputs:
33 try:
34 result[o].extend(inputs)
35 except KeyError:
36 result[o] = inputs
37 return result
38
Georg Brandla8145332014-01-27 08:22:49 +010039def check_rule(ui, repo, modified, basedir, output, inputs):
Martin v. Löwisbf526482013-09-30 16:09:44 +020040 """Verify that the output is newer than any of the inputs.
41 Return (status, stamp), where status is True if the update succeeded,
42 and stamp is the newest time stamp assigned to any file (might be in
Georg Brandla8145332014-01-27 08:22:49 +010043 the future).
44
45 If basedir is nonempty, it gives a directory in which the tree is to
46 be checked.
47 """
48 f_output = repo.wjoin(os.path.join(basedir, output))
Martin v. Loewiscfc1cc22012-04-27 16:10:21 +020049 try:
50 o_time = os.stat(f_output).st_mtime
51 except OSError:
52 ui.warn("Generated file %s does not exist\n" % output)
Martin v. Löwisbf526482013-09-30 16:09:44 +020053 return False, 0
54 youngest = 0 # youngest dependency
Martin v. Loewiscfc1cc22012-04-27 16:10:21 +020055 backdate = None
56 backdate_source = None
57 for i in inputs:
Georg Brandla8145332014-01-27 08:22:49 +010058 f_i = repo.wjoin(os.path.join(basedir, i))
Martin v. Loewiscfc1cc22012-04-27 16:10:21 +020059 try:
60 i_time = os.stat(f_i).st_mtime
61 except OSError:
62 ui.warn(".hgtouch input file %s does not exist\n" % i)
Martin v. Löwisbf526482013-09-30 16:09:44 +020063 return False, 0
Martin v. Loewiscfc1cc22012-04-27 16:10:21 +020064 if i in modified:
65 # input is modified. Need to backdate at least to i_time
66 if backdate is None or backdate > i_time:
67 backdate = i_time
68 backdate_source = i
69 continue
Martin v. Löwisbf526482013-09-30 16:09:44 +020070 youngest = max(i_time, youngest)
Martin v. Loewiscfc1cc22012-04-27 16:10:21 +020071 if backdate is not None:
72 ui.warn("Input %s for file %s locally modified\n" % (backdate_source, output))
73 # set to 1s before oldest modified input
74 backdate -= 1
75 os.utime(f_output, (backdate, backdate))
Martin v. Löwisbf526482013-09-30 16:09:44 +020076 return False, 0
77 if youngest >= o_time:
Martin v. Loewiscfc1cc22012-04-27 16:10:21 +020078 ui.note("Touching %s\n" % output)
Martin v. Löwisbf526482013-09-30 16:09:44 +020079 youngest += 1
80 os.utime(f_output, (youngest, youngest))
81 return True, youngest
82 else:
83 # Nothing to update
84 return True, 0
Martin v. Loewiscfc1cc22012-04-27 16:10:21 +020085
Georg Brandla8145332014-01-27 08:22:49 +010086def do_touch(ui, repo, basedir):
87 if basedir:
88 if not os.path.isdir(repo.wjoin(basedir)):
89 ui.warn("Abort: basedir %r does not exist\n" % basedir)
90 return
91 modified = []
92 else:
93 modified = repo.status()[0]
Martin v. Loewiscfc1cc22012-04-27 16:10:21 +020094 dependencies = parse_config(repo)
95 success = True
Martin v. Löwisbf526482013-09-30 16:09:44 +020096 tstamp = 0 # newest time stamp assigned
Martin v. Loewiscfc1cc22012-04-27 16:10:21 +020097 # try processing all rules in topological order
98 hold_back = {}
99 while dependencies:
100 output, inputs = dependencies.popitem()
101 # check whether any of the inputs is generated
102 for i in inputs:
103 if i in dependencies:
104 hold_back[output] = inputs
105 continue
Georg Brandla8145332014-01-27 08:22:49 +0100106 _success, _tstamp = check_rule(ui, repo, modified, basedir, output, inputs)
107 success = success and _success
Martin v. Löwisbf526482013-09-30 16:09:44 +0200108 tstamp = max(tstamp, _tstamp)
Martin v. Loewiscfc1cc22012-04-27 16:10:21 +0200109 # put back held back rules
110 dependencies.update(hold_back)
111 hold_back = {}
Martin v. Löwisbf526482013-09-30 16:09:44 +0200112 now = time.time()
113 if tstamp > now:
114 # wait until real time has passed the newest time stamp, to
115 # avoid having files dated in the future
116 time.sleep(tstamp-now)
Martin v. Loewiscfc1cc22012-04-27 16:10:21 +0200117 if hold_back:
118 ui.warn("Cyclic dependency involving %s\n" % (' '.join(hold_back.keys())))
119 return False
120 return success
121
Georg Brandla8145332014-01-27 08:22:49 +0100122def touch(ui, repo, basedir):
Martin v. Loewiscfc1cc22012-04-27 16:10:21 +0200123 "touch generated files that are older than their sources after an update."
Georg Brandla8145332014-01-27 08:22:49 +0100124 do_touch(ui, repo, basedir)
Martin v. Loewiscfc1cc22012-04-27 16:10:21 +0200125
126cmdtable = {
Georg Brandla8145332014-01-27 08:22:49 +0100127 "touch": (touch,
Georg Brandle46abb42014-03-09 10:22:10 +0100128 [('b', 'basedir', '', 'base dir of the tree to apply touching')],
Georg Brandla8145332014-01-27 08:22:49 +0100129 "hg touch [-b BASEDIR]")
Martin v. Loewiscfc1cc22012-04-27 16:10:21 +0200130}