1*6777b538SAndroid Build Coastguard Worker#!/usr/bin/env python3 2*6777b538SAndroid Build Coastguard Worker# Copyright 2013 The Chromium Authors 3*6777b538SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be 4*6777b538SAndroid Build Coastguard Worker# found in the LICENSE file. 5*6777b538SAndroid Build Coastguard Worker 6*6777b538SAndroid Build Coastguard Workerdescription = """ 7*6777b538SAndroid Build Coastguard WorkerMake a symlink and optionally touch a file (to handle dependencies). 8*6777b538SAndroid Build Coastguard Worker""" 9*6777b538SAndroid Build Coastguard Workerusage = "%prog [options] source[ source ...] linkname" 10*6777b538SAndroid Build Coastguard Workerepilog = """\ 11*6777b538SAndroid Build Coastguard WorkerA symlink to source is created at linkname. If multiple sources are specified, 12*6777b538SAndroid Build Coastguard Workerthen linkname is assumed to be a directory, and will contain all the links to 13*6777b538SAndroid Build Coastguard Workerthe sources (basenames identical to their source). 14*6777b538SAndroid Build Coastguard Worker 15*6777b538SAndroid Build Coastguard WorkerOn Windows, this will use hard links (mklink /H) to avoid requiring elevation. 16*6777b538SAndroid Build Coastguard WorkerThis means that if the original is deleted and replaced, the link will still 17*6777b538SAndroid Build Coastguard Workerhave the old contents. 18*6777b538SAndroid Build Coastguard Worker""" 19*6777b538SAndroid Build Coastguard Worker 20*6777b538SAndroid Build Coastguard Workerimport errno 21*6777b538SAndroid Build Coastguard Workerimport optparse 22*6777b538SAndroid Build Coastguard Workerimport os.path 23*6777b538SAndroid Build Coastguard Workerimport shutil 24*6777b538SAndroid Build Coastguard Workerimport subprocess 25*6777b538SAndroid Build Coastguard Workerimport sys 26*6777b538SAndroid Build Coastguard Worker 27*6777b538SAndroid Build Coastguard Worker 28*6777b538SAndroid Build Coastguard Workerdef Main(argv): 29*6777b538SAndroid Build Coastguard Worker parser = optparse.OptionParser(usage=usage, description=description, 30*6777b538SAndroid Build Coastguard Worker epilog=epilog) 31*6777b538SAndroid Build Coastguard Worker parser.add_option('-f', '--force', action='store_true') 32*6777b538SAndroid Build Coastguard Worker parser.add_option('--touch') 33*6777b538SAndroid Build Coastguard Worker 34*6777b538SAndroid Build Coastguard Worker options, args = parser.parse_args(argv[1:]) 35*6777b538SAndroid Build Coastguard Worker if len(args) < 2: 36*6777b538SAndroid Build Coastguard Worker parser.error('at least two arguments required.') 37*6777b538SAndroid Build Coastguard Worker 38*6777b538SAndroid Build Coastguard Worker target = args[-1] 39*6777b538SAndroid Build Coastguard Worker sources = args[:-1] 40*6777b538SAndroid Build Coastguard Worker for s in sources: 41*6777b538SAndroid Build Coastguard Worker t = os.path.join(target, os.path.basename(s)) 42*6777b538SAndroid Build Coastguard Worker if len(sources) == 1 and not os.path.isdir(target): 43*6777b538SAndroid Build Coastguard Worker t = target 44*6777b538SAndroid Build Coastguard Worker t = os.path.expanduser(t) 45*6777b538SAndroid Build Coastguard Worker if os.path.realpath(t) == os.path.realpath(s): 46*6777b538SAndroid Build Coastguard Worker continue 47*6777b538SAndroid Build Coastguard Worker try: 48*6777b538SAndroid Build Coastguard Worker # N.B. Python 2.x does not have os.symlink for Windows. 49*6777b538SAndroid Build Coastguard Worker # Python 3 has os.symlink for Windows, but requires either the admin- 50*6777b538SAndroid Build Coastguard Worker # granted privilege SeCreateSymbolicLinkPrivilege or, as of Windows 10 51*6777b538SAndroid Build Coastguard Worker # 1703, that Developer Mode be enabled. Hard links and junctions do not 52*6777b538SAndroid Build Coastguard Worker # require any extra privileges to create. 53*6777b538SAndroid Build Coastguard Worker if os.name == 'nt': 54*6777b538SAndroid Build Coastguard Worker # mklink does not tolerate /-delimited path names. 55*6777b538SAndroid Build Coastguard Worker t = t.replace('/', '\\') 56*6777b538SAndroid Build Coastguard Worker s = s.replace('/', '\\') 57*6777b538SAndroid Build Coastguard Worker # N.B. This tool only handles file hardlinks, not directory junctions. 58*6777b538SAndroid Build Coastguard Worker subprocess.check_output(['cmd.exe', '/c', 'mklink', '/H', t, s], 59*6777b538SAndroid Build Coastguard Worker stderr=subprocess.STDOUT) 60*6777b538SAndroid Build Coastguard Worker else: 61*6777b538SAndroid Build Coastguard Worker os.symlink(s, t) 62*6777b538SAndroid Build Coastguard Worker except OSError as e: 63*6777b538SAndroid Build Coastguard Worker if e.errno == errno.EEXIST and options.force: 64*6777b538SAndroid Build Coastguard Worker if os.path.isdir(t): 65*6777b538SAndroid Build Coastguard Worker shutil.rmtree(t, ignore_errors=True) 66*6777b538SAndroid Build Coastguard Worker else: 67*6777b538SAndroid Build Coastguard Worker os.remove(t) 68*6777b538SAndroid Build Coastguard Worker os.symlink(s, t) 69*6777b538SAndroid Build Coastguard Worker else: 70*6777b538SAndroid Build Coastguard Worker raise 71*6777b538SAndroid Build Coastguard Worker except subprocess.CalledProcessError as e: 72*6777b538SAndroid Build Coastguard Worker # Since subprocess.check_output does not return an easily checked error 73*6777b538SAndroid Build Coastguard Worker # number, in the 'force' case always assume it is 'file already exists' 74*6777b538SAndroid Build Coastguard Worker # and retry. 75*6777b538SAndroid Build Coastguard Worker if options.force: 76*6777b538SAndroid Build Coastguard Worker if os.path.isdir(t): 77*6777b538SAndroid Build Coastguard Worker shutil.rmtree(t, ignore_errors=True) 78*6777b538SAndroid Build Coastguard Worker else: 79*6777b538SAndroid Build Coastguard Worker os.remove(t) 80*6777b538SAndroid Build Coastguard Worker subprocess.check_output(e.cmd, stderr=subprocess.STDOUT) 81*6777b538SAndroid Build Coastguard Worker else: 82*6777b538SAndroid Build Coastguard Worker raise 83*6777b538SAndroid Build Coastguard Worker 84*6777b538SAndroid Build Coastguard Worker 85*6777b538SAndroid Build Coastguard Worker if options.touch: 86*6777b538SAndroid Build Coastguard Worker os.makedirs(os.path.dirname(options.touch), exist_ok=True) 87*6777b538SAndroid Build Coastguard Worker with open(options.touch, 'w'): 88*6777b538SAndroid Build Coastguard Worker pass 89*6777b538SAndroid Build Coastguard Worker 90*6777b538SAndroid Build Coastguard Worker 91*6777b538SAndroid Build Coastguard Workerif __name__ == '__main__': 92*6777b538SAndroid Build Coastguard Worker sys.exit(Main(sys.argv)) 93