blob: c7fde1057d443332be0d57581d79b05e43466aa2 [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"""
10import os
11
12def parse_config(repo):
13 configfile = repo.wjoin(".hgtouch")
14 if not os.path.exists(configfile):
15 return {}
16 result = {}
17 with open(configfile) as f:
18 for line in f:
19 # strip comments
20 line = line.split('#')[0].strip()
21 if ':' not in line:
22 continue
23 outputs, inputs = line.split(':', 1)
24 outputs = outputs.split()
25 inputs = inputs.split()
26 for o in outputs:
27 try:
28 result[o].extend(inputs)
29 except KeyError:
30 result[o] = inputs
31 return result
32
33def check_rule(ui, repo, modified, output, inputs):
34 f_output = repo.wjoin(output)
35 try:
36 o_time = os.stat(f_output).st_mtime
37 except OSError:
38 ui.warn("Generated file %s does not exist\n" % output)
39 return False
40 need_touch = False
41 backdate = None
42 backdate_source = None
43 for i in inputs:
44 f_i = repo.wjoin(i)
45 try:
46 i_time = os.stat(f_i).st_mtime
47 except OSError:
48 ui.warn(".hgtouch input file %s does not exist\n" % i)
49 return False
50 if i in modified:
51 # input is modified. Need to backdate at least to i_time
52 if backdate is None or backdate > i_time:
53 backdate = i_time
54 backdate_source = i
55 continue
56 if o_time <= i_time:
57 # generated file is older, touch
58 need_touch = True
59 if backdate is not None:
60 ui.warn("Input %s for file %s locally modified\n" % (backdate_source, output))
61 # set to 1s before oldest modified input
62 backdate -= 1
63 os.utime(f_output, (backdate, backdate))
64 return False
65 if need_touch:
66 ui.note("Touching %s\n" % output)
67 os.utime(f_output, None)
68 return True
69
70def do_touch(ui, repo):
71 modified = repo.status()[0]
72 dependencies = parse_config(repo)
73 success = True
74 # try processing all rules in topological order
75 hold_back = {}
76 while dependencies:
77 output, inputs = dependencies.popitem()
78 # check whether any of the inputs is generated
79 for i in inputs:
80 if i in dependencies:
81 hold_back[output] = inputs
82 continue
83 success = check_rule(ui, repo, modified, output, inputs)
84 # put back held back rules
85 dependencies.update(hold_back)
86 hold_back = {}
87 if hold_back:
88 ui.warn("Cyclic dependency involving %s\n" % (' '.join(hold_back.keys())))
89 return False
90 return success
91
92def touch(ui, repo):
93 "touch generated files that are older than their sources after an update."
94 do_touch(ui, repo)
95
96cmdtable = {
97 "touch": (touch, [],
98 "touch generated files according to the .hgtouch configuration")
99}