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