blob: cf139202ea240a1ad55f1c800e62e6cea465eefb [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
39def check_rule(ui, repo, modified, 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
43 the future)."""
Martin v. Loewiscfc1cc22012-04-27 16:10:21 +020044 f_output = repo.wjoin(output)
45 try:
46 o_time = os.stat(f_output).st_mtime
47 except OSError:
48 ui.warn("Generated file %s does not exist\n" % output)
Martin v. Löwisbf526482013-09-30 16:09:44 +020049 return False, 0
50 youngest = 0 # youngest dependency
Martin v. Loewiscfc1cc22012-04-27 16:10:21 +020051 backdate = None
52 backdate_source = None
53 for i in inputs:
54 f_i = repo.wjoin(i)
55 try:
56 i_time = os.stat(f_i).st_mtime
57 except OSError:
58 ui.warn(".hgtouch input file %s does not exist\n" % i)
Martin v. Löwisbf526482013-09-30 16:09:44 +020059 return False, 0
Martin v. Loewiscfc1cc22012-04-27 16:10:21 +020060 if i in modified:
61 # input is modified. Need to backdate at least to i_time
62 if backdate is None or backdate > i_time:
63 backdate = i_time
64 backdate_source = i
65 continue
Martin v. Löwisbf526482013-09-30 16:09:44 +020066 youngest = max(i_time, youngest)
Martin v. Loewiscfc1cc22012-04-27 16:10:21 +020067 if backdate is not None:
68 ui.warn("Input %s for file %s locally modified\n" % (backdate_source, output))
69 # set to 1s before oldest modified input
70 backdate -= 1
71 os.utime(f_output, (backdate, backdate))
Martin v. Löwisbf526482013-09-30 16:09:44 +020072 return False, 0
73 if youngest >= o_time:
Martin v. Loewiscfc1cc22012-04-27 16:10:21 +020074 ui.note("Touching %s\n" % output)
Martin v. Löwisbf526482013-09-30 16:09:44 +020075 youngest += 1
76 os.utime(f_output, (youngest, youngest))
77 return True, youngest
78 else:
79 # Nothing to update
80 return True, 0
Martin v. Loewiscfc1cc22012-04-27 16:10:21 +020081
82def do_touch(ui, repo):
83 modified = repo.status()[0]
84 dependencies = parse_config(repo)
85 success = True
Martin v. Löwisbf526482013-09-30 16:09:44 +020086 tstamp = 0 # newest time stamp assigned
Martin v. Loewiscfc1cc22012-04-27 16:10:21 +020087 # try processing all rules in topological order
88 hold_back = {}
89 while dependencies:
90 output, inputs = dependencies.popitem()
91 # check whether any of the inputs is generated
92 for i in inputs:
93 if i in dependencies:
94 hold_back[output] = inputs
95 continue
Martin v. Löwisbf526482013-09-30 16:09:44 +020096 _success, _tstamp = check_rule(ui, repo, modified, output, inputs)
97 sucess = success and _success
98 tstamp = max(tstamp, _tstamp)
Martin v. Loewiscfc1cc22012-04-27 16:10:21 +020099 # put back held back rules
100 dependencies.update(hold_back)
101 hold_back = {}
Martin v. Löwisbf526482013-09-30 16:09:44 +0200102 now = time.time()
103 if tstamp > now:
104 # wait until real time has passed the newest time stamp, to
105 # avoid having files dated in the future
106 time.sleep(tstamp-now)
Martin v. Loewiscfc1cc22012-04-27 16:10:21 +0200107 if hold_back:
108 ui.warn("Cyclic dependency involving %s\n" % (' '.join(hold_back.keys())))
109 return False
110 return success
111
112def touch(ui, repo):
113 "touch generated files that are older than their sources after an update."
114 do_touch(ui, repo)
115
116cmdtable = {
117 "touch": (touch, [],
118 "touch generated files according to the .hgtouch configuration")
119}