xref: /aosp_15_r20/external/angle/build/symlink.py (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
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