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