Bug 908052 - Use GetShortPathName/GetLongPathName to canonicalize paths for dependency files generated by cl.py. r=gps, a=NPOTB
authorMike Hommey <mh+mozilla@glandium.org>
Fri, 23 Aug 2013 08:08:05 +0900
changeset 148647 51517c1a190a77ea97bafc34e71f8bd5c5200843
parent 148646 bfb57753afb763da8c75bdb541c1d3374aae1d02
child 148648 58fb08e34722c5d1f99fbe25808e94d38c5b4911
push id54
push userryanvm@gmail.com
push dateFri, 08 Nov 2013 20:44:01 +0000
reviewersgps, NPOTB
bugs908052
milestone24.1.0
Bug 908052 - Use GetShortPathName/GetLongPathName to canonicalize paths for dependency files generated by cl.py. r=gps, a=NPOTB
build/cl.py
js/src/build/cl.py
--- a/build/cl.py
+++ b/build/cl.py
@@ -1,18 +1,52 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+import ctypes
 import os, os.path
 import subprocess
 import sys
 
 CL_INCLUDES_PREFIX = os.environ.get("CL_INCLUDES_PREFIX", "Note: including file:")
 
+GetShortPathName = ctypes.windll.kernel32.GetShortPathNameW
+GetLongPathName = ctypes.windll.kernel32.GetLongPathNameW
+
+
+# cl.exe likes to print inconsistent paths in the showIncludes output
+# (some lowercased, some not, with different directions of slashes),
+# and we need the original file case for make/pymake to be happy.
+# As this is slow and needs to be called a lot of times, use a cache
+# to speed things up.
+_normcase_cache = {}
+
+def normcase(path):
+    # Get*PathName want paths with backslashes
+    path = path.replace('/', os.sep)
+    dir = os.path.dirname(path)
+    # name is fortunately always going to have the right case,
+    # so we can use a cache for the directory part only.
+    name = os.path.basename(path)
+    if dir in _normcase_cache:
+        result = _normcase_cache[dir]
+    else:
+        path = ctypes.create_unicode_buffer(dir)
+        length = GetShortPathName(path, None, 0)
+        shortpath = ctypes.create_unicode_buffer(length)
+        GetShortPathName(path, shortpath, length)
+        length = GetLongPathName(shortpath, None, 0)
+        if length > len(path):
+            path = ctypes.create_unicode_buffer(length)
+        GetLongPathName(shortpath, path, length)
+        result = _normcase_cache[dir] = path.value
+    return os.path.join(result, name)
+
+
 def InvokeClWithDependencyGeneration(cmdline):
     target = ""
     # Figure out what the target is
     for arg in cmdline:
         if arg.startswith("-Fo"):
             target = arg[3:]
             break
 
@@ -24,27 +58,27 @@ def InvokeClWithDependencyGeneration(cmd
     assert not source.startswith('-')
 
     # The deps target lives here
     depstarget = os.path.basename(target) + ".pp"
 
     cmdline += ['-showIncludes']
     cl = subprocess.Popen(cmdline, stdout=subprocess.PIPE)
 
-    deps = set([os.path.normcase(source).replace(os.sep, '/')])
+    deps = set([normcase(source).replace(os.sep, '/')])
     for line in cl.stdout:
         # cl -showIncludes prefixes every header with "Note: including file:"
         # and an indentation corresponding to the depth (which we don't need)
         if line.startswith(CL_INCLUDES_PREFIX):
             dep = line[len(CL_INCLUDES_PREFIX):].strip()
             # We can't handle pathes with spaces properly in mddepend.pl, but
             # we can assume that anything in a path with spaces is a system
             # header and throw it away.
             if ' ' not in dep:
-                deps.add(os.path.normcase(dep).replace(os.sep, '/'))
+                deps.add(normcase(dep).replace(os.sep, '/'))
         else:
             sys.stdout.write(line) # Make sure we preserve the relevant output
                                    # from cl
 
     ret = cl.wait()
     if ret != 0 or target == "":
         sys.exit(ret)
 
--- a/js/src/build/cl.py
+++ b/js/src/build/cl.py
@@ -1,18 +1,52 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+import ctypes
 import os, os.path
 import subprocess
 import sys
 
 CL_INCLUDES_PREFIX = os.environ.get("CL_INCLUDES_PREFIX", "Note: including file:")
 
+GetShortPathName = ctypes.windll.kernel32.GetShortPathNameW
+GetLongPathName = ctypes.windll.kernel32.GetLongPathNameW
+
+
+# cl.exe likes to print inconsistent paths in the showIncludes output
+# (some lowercased, some not, with different directions of slashes),
+# and we need the original file case for make/pymake to be happy.
+# As this is slow and needs to be called a lot of times, use a cache
+# to speed things up.
+_normcase_cache = {}
+
+def normcase(path):
+    # Get*PathName want paths with backslashes
+    path = path.replace('/', os.sep)
+    dir = os.path.dirname(path)
+    # name is fortunately always going to have the right case,
+    # so we can use a cache for the directory part only.
+    name = os.path.basename(path)
+    if dir in _normcase_cache:
+        result = _normcase_cache[dir]
+    else:
+        path = ctypes.create_unicode_buffer(dir)
+        length = GetShortPathName(path, None, 0)
+        shortpath = ctypes.create_unicode_buffer(length)
+        GetShortPathName(path, shortpath, length)
+        length = GetLongPathName(shortpath, None, 0)
+        if length > len(path):
+            path = ctypes.create_unicode_buffer(length)
+        GetLongPathName(shortpath, path, length)
+        result = _normcase_cache[dir] = path.value
+    return os.path.join(result, name)
+
+
 def InvokeClWithDependencyGeneration(cmdline):
     target = ""
     # Figure out what the target is
     for arg in cmdline:
         if arg.startswith("-Fo"):
             target = arg[3:]
             break
 
@@ -24,27 +58,27 @@ def InvokeClWithDependencyGeneration(cmd
     assert not source.startswith('-')
 
     # The deps target lives here
     depstarget = os.path.basename(target) + ".pp"
 
     cmdline += ['-showIncludes']
     cl = subprocess.Popen(cmdline, stdout=subprocess.PIPE)
 
-    deps = set([os.path.normcase(source).replace(os.sep, '/')])
+    deps = set([normcase(source).replace(os.sep, '/')])
     for line in cl.stdout:
         # cl -showIncludes prefixes every header with "Note: including file:"
         # and an indentation corresponding to the depth (which we don't need)
         if line.startswith(CL_INCLUDES_PREFIX):
             dep = line[len(CL_INCLUDES_PREFIX):].strip()
             # We can't handle pathes with spaces properly in mddepend.pl, but
             # we can assume that anything in a path with spaces is a system
             # header and throw it away.
             if ' ' not in dep:
-                deps.add(os.path.normcase(dep).replace(os.sep, '/'))
+                deps.add(normcase(dep).replace(os.sep, '/'))
         else:
             sys.stdout.write(line) # Make sure we preserve the relevant output
                                    # from cl
 
     ret = cl.wait()
     if ret != 0 or target == "":
         sys.exit(ret)