| """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") |
| } |