[M120 Migration][VD] Fix url crash in RequestCertificateConfirm
[platform/framework/web/chromium-efl.git] / build / symlink.py
index 5a261dc..ad93807 100755 (executable)
@@ -1,27 +1,33 @@
-#!/usr/bin/env python
-# Copyright (c) 2013 The Chromium Authors. All rights reserved.
+#!/usr/bin/env python3
+# Copyright 2013 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""Make a symlink and optionally touch a file (to handle dependencies).
-
-Usage:
-  symlink.py [options] sources... target
-
-A sym link to source is created at target. If multiple sources are specfied,
-then target is assumed to be a directory, and will contain all the links to
+description = """
+Make a symlink and optionally touch a file (to handle dependencies).
+"""
+usage = "%prog [options] source[ source ...] linkname"
+epilog = """\
+A symlink to source is created at linkname. If multiple sources are specified,
+then linkname is assumed to be a directory, and will contain all the links to
 the sources (basenames identical to their source).
+
+On Windows, this will use hard links (mklink /H) to avoid requiring elevation.
+This means that if the original is deleted and replaced, the link will still
+have the old contents.
 """
 
 import errno
 import optparse
 import os.path
 import shutil
+import subprocess
 import sys
 
 
 def Main(argv):
-  parser = optparse.OptionParser()
+  parser = optparse.OptionParser(usage=usage, description=description,
+                                 epilog=epilog)
   parser.add_option('-f', '--force', action='store_true')
   parser.add_option('--touch')
 
@@ -36,11 +42,24 @@ def Main(argv):
     if len(sources) == 1 and not os.path.isdir(target):
       t = target
     t = os.path.expanduser(t)
-    if os.path.realpath(t) == s:
+    if os.path.realpath(t) == os.path.realpath(s):
       continue
     try:
-      os.symlink(s, t)
-    except OSError, e:
+      # N.B. Python 2.x does not have os.symlink for Windows.
+      #   Python 3 has os.symlink for Windows, but requires either the admin-
+      #   granted privilege SeCreateSymbolicLinkPrivilege or, as of Windows 10
+      #   1703, that Developer Mode be enabled. Hard links and junctions do not
+      #   require any extra privileges to create.
+      if os.name == 'nt':
+        # mklink does not tolerate /-delimited path names.
+        t = t.replace('/', '\\')
+        s = s.replace('/', '\\')
+        # N.B. This tool only handles file hardlinks, not directory junctions.
+        subprocess.check_output(['cmd.exe', '/c', 'mklink', '/H', t, s],
+                                stderr=subprocess.STDOUT)
+      else:
+        os.symlink(s, t)
+    except OSError as e:
       if e.errno == errno.EEXIST and options.force:
         if os.path.isdir(t):
           shutil.rmtree(t, ignore_errors=True)
@@ -49,10 +68,23 @@ def Main(argv):
         os.symlink(s, t)
       else:
         raise
+    except subprocess.CalledProcessError as e:
+      # Since subprocess.check_output does not return an easily checked error
+      # number, in the 'force' case always assume it is 'file already exists'
+      # and retry.
+      if options.force:
+        if os.path.isdir(t):
+          shutil.rmtree(t, ignore_errors=True)
+        else:
+          os.remove(t)
+        subprocess.check_output(e.cmd, stderr=subprocess.STDOUT)
+      else:
+        raise
 
 
   if options.touch:
-    with open(options.touch, 'w') as f:
+    os.makedirs(os.path.dirname(options.touch), exist_ok=True)
+    with open(options.touch, 'w'):
       pass