"""Bring time stamps of generated checked-in files into the right order A versioned configuration file .hgtouch specifies generated files, in the syntax of make rules. output: input1 input2 In addition to the dependency syntax, #-comments are supported. """ import errno import os def parse_config(repo): try: fp = repo.wfile(".hgtouch") except IOError, e: if e.errno != errno.ENOENT: raise return {} result = {} with fp: for line in fp: # strip comments line = line.split('#')[0].strip() if ':' not in line: continue outputs, inputs = line.split(':', 1) outputs = outputs.split() inputs = inputs.split() for o in outputs: try: result[o].extend(inputs) except KeyError: result[o] = inputs return result def check_rule(ui, repo, modified, output, inputs): f_output = repo.wjoin(output) try: o_time = os.stat(f_output).st_mtime except OSError: ui.warn("Generated file %s does not exist\n" % output) return False need_touch = False backdate = None backdate_source = None for i in inputs: f_i = repo.wjoin(i) try: i_time = os.stat(f_i).st_mtime except OSError: ui.warn(".hgtouch input file %s does not exist\n" % i) return False if i in modified: # input is modified. Need to backdate at least to i_time if backdate is None or backdate > i_time: backdate = i_time backdate_source = i continue if o_time <= i_time: # generated file is older, touch need_touch = True if backdate is not None: ui.warn("Input %s for file %s locally modified\n" % (backdate_source, output)) # set to 1s before oldest modified input backdate -= 1 os.utime(f_output, (backdate, backdate)) return False if need_touch: ui.note("Touching %s\n" % output) os.utime(f_output, None) return True def do_touch(ui, repo): modified = repo.status()[0] dependencies = parse_config(repo) success = True # try processing all rules in topological order hold_back = {} while dependencies: output, inputs = dependencies.popitem() # check whether any of the inputs is generated for i in inputs: if i in dependencies: hold_back[output] = inputs continue success = check_rule(ui, repo, modified, output, inputs) # put back held back rules dependencies.update(hold_back) hold_back = {} if hold_back: ui.warn("Cyclic dependency involving %s\n" % (' '.join(hold_back.keys()))) return False return success def touch(ui, repo): "touch generated files that are older than their sources after an update." do_touch(ui, repo) cmdtable = { "touch": (touch, [], "touch generated files according to the .hgtouch configuration") }