blob: fbca469ba94c25ec02b9c50581667fab720e2578 [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
Martin v. Löwisbf526482013-09-30 16:09:44 +020012import time
Martin v. Loewiscfc1cc22012-04-27 16:10:21 +020013
14def parse_config(repo):
Benjamin Peterson0e1a5b42012-04-27 11:56:30 -040015 try:
16 fp = repo.wfile(".hgtouch")
17 except IOError, e:
18 if e.errno != errno.ENOENT:
19 raise
Martin v. Loewiscfc1cc22012-04-27 16:10:21 +020020 return {}
21 result = {}
Benjamin Peterson0e1a5b42012-04-27 11:56:30 -040022 with fp:
23 for line in fp:
Martin v. Loewiscfc1cc22012-04-27 16:10:21 +020024 # strip comments
25 line = line.split('#')[0].strip()
26 if ':' not in line:
27 continue
28 outputs, inputs = line.split(':', 1)
29 outputs = outputs.split()
30 inputs = inputs.split()
31 for o in outputs:
32 try:
33 result[o].extend(inputs)
34 except KeyError:
35 result[o] = inputs
36 return result
37
Georg Brandla8145332014-01-27 08:22:49 +010038def check_rule(ui, repo, modified, basedir, output, inputs):
Martin v. Löwisbf526482013-09-30 16:09:44 +020039 """Verify that the output is newer than any of the inputs.
40 Return (status, stamp), where status is True if the update succeeded,
41 and stamp is the newest time stamp assigned to any file (might be in
Georg Brandla8145332014-01-27 08:22:49 +010042 the future).
43
44 If basedir is nonempty, it gives a directory in which the tree is to
45 be checked.
46 """
47 f_output = repo.wjoin(os.path.join(basedir, output))
Martin v. Loewiscfc1cc22012-04-27 16:10:21 +020048 try:
49 o_time = os.stat(f_output).st_mtime
50 except OSError:
51 ui.warn("Generated file %s does not exist\n" % output)
Martin v. Löwisbf526482013-09-30 16:09:44 +020052 return False, 0
53 youngest = 0 # youngest dependency
Martin v. Loewiscfc1cc22012-04-27 16:10:21 +020054 backdate = None
55 backdate_source = None
56 for i in inputs:
Georg Brandla8145332014-01-27 08:22:49 +010057 f_i = repo.wjoin(os.path.join(basedir, i))
Martin v. Loewiscfc1cc22012-04-27 16:10:21 +020058 try:
59 i_time = os.stat(f_i).st_mtime
60 except OSError:
61 ui.warn(".hgtouch input file %s does not exist\n" % i)
Martin v. Löwisbf526482013-09-30 16:09:44 +020062 return False, 0
Martin v. Loewiscfc1cc22012-04-27 16:10:21 +020063 if i in modified:
64 # input is modified. Need to backdate at least to i_time
65 if backdate is None or backdate > i_time:
66 backdate = i_time
67 backdate_source = i
68 continue
Martin v. Löwisbf526482013-09-30 16:09:44 +020069 youngest = max(i_time, youngest)
Martin v. Loewiscfc1cc22012-04-27 16:10:21 +020070 if backdate is not None:
71 ui.warn("Input %s for file %s locally modified\n" % (backdate_source, output))
72 # set to 1s before oldest modified input
73 backdate -= 1
74 os.utime(f_output, (backdate, backdate))
Martin v. Löwisbf526482013-09-30 16:09:44 +020075 return False, 0
76 if youngest >= o_time:
Martin v. Loewiscfc1cc22012-04-27 16:10:21 +020077 ui.note("Touching %s\n" % output)
Martin v. Löwisbf526482013-09-30 16:09:44 +020078 youngest += 1
79 os.utime(f_output, (youngest, youngest))
80 return True, youngest
81 else:
82 # Nothing to update
83 return True, 0
Martin v. Loewiscfc1cc22012-04-27 16:10:21 +020084
Georg Brandla8145332014-01-27 08:22:49 +010085def do_touch(ui, repo, basedir):
86 if basedir:
87 if not os.path.isdir(repo.wjoin(basedir)):
88 ui.warn("Abort: basedir %r does not exist\n" % basedir)
89 return
90 modified = []
91 else:
92 modified = repo.status()[0]
Martin v. Loewiscfc1cc22012-04-27 16:10:21 +020093 dependencies = parse_config(repo)
94 success = True
Martin v. Löwisbf526482013-09-30 16:09:44 +020095 tstamp = 0 # newest time stamp assigned
Martin v. Loewiscfc1cc22012-04-27 16:10:21 +020096 # try processing all rules in topological order
97 hold_back = {}
98 while dependencies:
99 output, inputs = dependencies.popitem()
100 # check whether any of the inputs is generated
101 for i in inputs:
102 if i in dependencies:
103 hold_back[output] = inputs
104 continue
Georg Brandla8145332014-01-27 08:22:49 +0100105 _success, _tstamp = check_rule(ui, repo, modified, basedir, output, inputs)
106 success = success and _success
Martin v. Löwisbf526482013-09-30 16:09:44 +0200107 tstamp = max(tstamp, _tstamp)
Martin v. Loewiscfc1cc22012-04-27 16:10:21 +0200108 # put back held back rules
109 dependencies.update(hold_back)
110 hold_back = {}
Martin v. Löwisbf526482013-09-30 16:09:44 +0200111 now = time.time()
112 if tstamp > now:
113 # wait until real time has passed the newest time stamp, to
114 # avoid having files dated in the future
115 time.sleep(tstamp-now)
Martin v. Loewiscfc1cc22012-04-27 16:10:21 +0200116 if hold_back:
117 ui.warn("Cyclic dependency involving %s\n" % (' '.join(hold_back.keys())))
118 return False
119 return success
120
Georg Brandla8145332014-01-27 08:22:49 +0100121def touch(ui, repo, basedir):
Martin v. Loewiscfc1cc22012-04-27 16:10:21 +0200122 "touch generated files that are older than their sources after an update."
Georg Brandla8145332014-01-27 08:22:49 +0100123 do_touch(ui, repo, basedir)
Martin v. Loewiscfc1cc22012-04-27 16:10:21 +0200124
125cmdtable = {
Georg Brandla8145332014-01-27 08:22:49 +0100126 "touch": (touch,
Georg Brandle46abb42014-03-09 10:22:10 +0100127 [('b', 'basedir', '', 'base dir of the tree to apply touching')],
Georg Brandla8145332014-01-27 08:22:49 +0100128 "hg touch [-b BASEDIR]")
Martin v. Loewiscfc1cc22012-04-27 16:10:21 +0200129}