Bug 1290040 - Make find_program return short paths automatically when paths contain spaces on Windows. r?gps
Also fake enough of ctypes to keep the configure unit tests passing
after these changes.
--- a/build/moz.configure/util.configure
+++ b/build/moz.configure/util.configure
@@ -56,36 +56,79 @@ def is_absolute_or_relative(path):
return True
return os.sep in path
@imports(_import='mozpack.path', _as='mozpath')
def normsep(path):
return mozpath.normsep(path)
+
+@imports('ctypes')
+@imports(_from='ctypes', _import='wintypes')
+def get_GetShortPathNameW():
+ GetShortPathNameW = ctypes.windll.kernel32.GetShortPathNameW
+ GetShortPathNameW.argtypes = [wintypes.LPCWSTR, wintypes.LPWSTR,
+ wintypes.DWORD]
+ GetShortPathNameW.restype = wintypes.DWORD
+ return GetShortPathNameW
+
+
+@template
+@imports('ctypes')
+@imports('platform')
+@imports(_from='mozbuild.shellutil', _import='quote')
+def normalize_path():
+ # Until the build system can properly handle programs that need quoting,
+ # transform those paths into their short version on Windows (e.g.
+ # c:\PROGRA~1...).
+ if platform.system() == 'Windows':
+ GetShortPathNameW = get_GetShortPathNameW()
+
+ def normalize_path(path):
+ path = normsep(path)
+ if quote(path) == path:
+ return path
+ size = 0
+ while True:
+ out = ctypes.create_unicode_buffer(size)
+ needed = GetShortPathNameW(path, out, size)
+ if size >= needed:
+ return normsep(out.value)
+ size = needed
+
+ else:
+ def normalize_path(path):
+ return normsep(path)
+
+ return normalize_path
+
+normalize_path = normalize_path()
+
+
# Locates the given program using which, or returns the given path if it
# exists.
# The `paths` parameter may be passed to search the given paths instead of
# $PATH.
@imports(_from='which', _import='which')
@imports(_from='which', _import='WhichError')
@imports('itertools')
@imports(_from='os', _import='pathsep')
def find_program(file, paths=None):
try:
if is_absolute_or_relative(file):
- return normsep(which(os.path.basename(file),
- [os.path.dirname(file)]))
+ return normalize_path(which(os.path.basename(file),
+ [os.path.dirname(file)]))
if paths:
if not isinstance(paths, (list, tuple)):
die("Paths provided to find_program must be a list of strings, "
"not %r", paths)
paths = list(itertools.chain(
*(p.split(pathsep) for p in paths if p)))
- return normsep(which(file, path=paths))
+ return normalize_path(which(file, path=paths))
except WhichError:
return None
@imports('os')
@imports('subprocess')
@imports(_from='mozbuild.configure.util', _import='LineIO')
@imports(_from='tempfile', _import='mkstemp')
--- a/python/mozbuild/mozbuild/test/configure/common.py
+++ b/python/mozbuild/mozbuild/test/configure/common.py
@@ -115,18 +115,54 @@ class ConfigureTestSandbox(ConfigureSand
PIPE=subprocess.PIPE,
STDOUT=subprocess.STDOUT,
Popen=self.Popen,
)
if what == 'os.environ':
return self._environ
+ if what == 'ctypes.wintypes':
+ return ReadOnlyNamespace(
+ LPCWSTR=0,
+ LPWSTR=1,
+ DWORD=2,
+ )
+
+ if what == 'ctypes':
+ class CTypesFunc(object):
+ def __init__(self, func):
+ self._func = func
+
+ def __call__(self, *args, **kwargs):
+ return self._func(*args, **kwargs)
+
+
+ return ReadOnlyNamespace(
+ create_unicode_buffer=self.create_unicode_buffer,
+ windll=ReadOnlyNamespace(
+ kernel32=ReadOnlyNamespace(
+ GetShortPathNameW=CTypesFunc(self.GetShortPathNameW),
+ )
+ ),
+ )
+
return super(ConfigureTestSandbox, self)._get_one_import(what)
+ def create_unicode_buffer(self, *args, **kwargs):
+ class Buffer(object):
+ def __init__(self):
+ self.value = ''
+
+ return Buffer()
+
+ def GetShortPathNameW(self, path_in, path_out, length):
+ path_out.value = path_in
+ return length
+
def which(self, command, path=None):
for parent in (path or self._search_path):
c = mozpath.abspath(mozpath.join(parent, command))
for candidate in (c, ensure_exe_extension(c)):
if self.OS.path.exists(candidate):
return candidate
raise WhichError()