aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mesonbuild/dependencies.py50
-rwxr-xr-xrun_tests.py10
-rwxr-xr-xrun_unittests.py49
-rw-r--r--test cases/windows/9 find program/meson.build8
-rw-r--r--test cases/windows/9 find program/test-script-ext.py3
5 files changed, 102 insertions, 18 deletions
diff --git a/mesonbuild/dependencies.py b/mesonbuild/dependencies.py
index 9525ffa..920a279 100644
--- a/mesonbuild/dependencies.py
+++ b/mesonbuild/dependencies.py
@@ -402,7 +402,7 @@ class WxDependency(Dependency):
return self.is_found
class ExternalProgram:
- windows_exts = ('exe', 'com', 'bat')
+ windows_exts = ('exe', 'msc', 'com', 'bat')
def __init__(self, name, fullpath=None, silent=False, search_dir=None):
self.name = name
@@ -420,6 +420,10 @@ class ExternalProgram:
else:
mlog.log('Program', mlog.bold(name), 'found:', mlog.red('NO'))
+ def __repr__(self):
+ r = '<{} {!r} -> {!r}>'
+ return r.format(self.__class__.__name__, self.name, self.fullpath)
+
@staticmethod
def _shebang_to_cmd(script):
"""
@@ -473,27 +477,49 @@ class ExternalProgram:
return self._shebang_to_cmd(trial)
def _search(self, name, search_dir):
+ '''
+ Search in the specified dir for the specified executable by name
+ and if not found search in PATH
+ '''
commands = self._search_dir(name, search_dir)
if commands:
return commands
# Do a standard search in PATH
fullpath = shutil.which(name)
- if fullpath or not mesonlib.is_windows():
+ if not mesonlib.is_windows():
# On UNIX-like platforms, the standard PATH search is enough
return [fullpath]
- # On Windows, if name is an absolute path, we need the extension too
- for ext in self.windows_exts:
- fullpath = '{}.{}'.format(name, ext)
- if os.path.exists(fullpath):
+ # HERE BEGINS THE TERROR OF WINDOWS
+ if fullpath:
+ # On Windows, even if the PATH search returned a full path, we can't be
+ # sure that it can be run directly if it's not a native executable.
+ # For instance, interpreted scripts sometimes need to be run explicitly
+ # with an interpreter if the file association is not done properly.
+ name_ext = os.path.splitext(fullpath)[1]
+ if name_ext[1:].lower() in self.windows_exts:
+ # Good, it can be directly executed
return [fullpath]
- # On Windows, interpreted scripts must have an extension otherwise they
- # cannot be found by a standard PATH search. So we do a custom search
- # where we manually search for a script with a shebang in PATH.
- search_dirs = os.environ.get('PATH', '').split(';')
- for search_dir in search_dirs:
- commands = self._search_dir(name, search_dir)
+ # Try to extract the interpreter from the shebang
+ commands = self._shebang_to_cmd(fullpath)
if commands:
return commands
+ else:
+ # Maybe the name is an absolute path to a native Windows
+ # executable, but without the extension. This is technically wrong,
+ # but many people do it because it works in the MinGW shell.
+ if os.path.isabs(name):
+ for ext in self.windows_exts:
+ fullpath = '{}.{}'.format(name, ext)
+ if os.path.exists(fullpath):
+ return [fullpath]
+ # On Windows, interpreted scripts must have an extension otherwise they
+ # cannot be found by a standard PATH search. So we do a custom search
+ # where we manually search for a script with a shebang in PATH.
+ search_dirs = os.environ.get('PATH', '').split(';')
+ for search_dir in search_dirs:
+ commands = self._search_dir(name, search_dir)
+ if commands:
+ return commands
return [None]
def found(self):
diff --git a/run_tests.py b/run_tests.py
index 6282440..f2038e4 100755
--- a/run_tests.py
+++ b/run_tests.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
-# Copyright 2012-2016 The Meson development team
+# Copyright 2012-2017 The Meson development team
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -21,10 +21,12 @@ from mesonbuild import mesonlib
if __name__ == '__main__':
returncode = 0
print('Running unittests.\n')
+ units = ['InternalTests', 'AllPlatformTests']
if mesonlib.is_linux():
- returncode += subprocess.call([sys.executable, 'run_unittests.py', '-v'])
- else:
- returncode += subprocess.call([sys.executable, 'run_unittests.py', '-v', 'InternalTests', 'AllPlatformTests'])
+ units += ['LinuxlikeTests']
+ elif mesonlib.is_windows():
+ units += ['WindowsTests']
+ returncode += subprocess.call([sys.executable, 'run_unittests.py', '-v'] + units)
# Ubuntu packages do not have a binary without -6 suffix.
if shutil.which('arm-linux-gnueabihf-gcc-6') and not platform.machine().startswith('arm'):
print('Running cross compilation tests.\n')
diff --git a/run_unittests.py b/run_unittests.py
index 3facf49..f72313a 100755
--- a/run_unittests.py
+++ b/run_unittests.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright 2016 The Meson development team
+# Copyright 2016-2017 The Meson development team
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -26,7 +26,7 @@ import mesonbuild.environment
import mesonbuild.mesonlib
from mesonbuild.mesonlib import is_windows
from mesonbuild.environment import detect_ninja, Environment
-from mesonbuild.dependencies import PkgConfigDependency
+from mesonbuild.dependencies import PkgConfigDependency, ExternalProgram
if is_windows():
exe_suffix = '.exe'
@@ -186,6 +186,7 @@ class BasePlatformTests(unittest.TestCase):
super().setUp()
src_root = os.path.dirname(__file__)
src_root = os.path.join(os.getcwd(), src_root)
+ self.src_root = src_root
# In case the directory is inside a symlinked directory, find the real
# path otherwise we might not find the srcdir from inside the builddir.
self.builddir = os.path.realpath(tempfile.mkdtemp())
@@ -564,6 +565,50 @@ class AllPlatformTests(BasePlatformTests):
self.assertPathBasenameEqual(incs[7], 'sub1')
+class WindowsTests(BasePlatformTests):
+ '''
+ Tests that should run on Cygwin, MinGW, and MSVC
+ '''
+ def setUp(self):
+ super().setUp()
+ self.platform_test_dir = os.path.join(self.src_root, 'test cases/windows')
+
+ def test_find_program(self):
+ '''
+ Test that Windows-specific edge-cases in find_program are functioning
+ correctly. Cannot be an ordinary test because it involves manipulating
+ PATH to point to a directory with Python scripts.
+ '''
+ testdir = os.path.join(self.platform_test_dir, '9 find program')
+ # Find `cmd` and `cmd.exe`
+ prog1 = ExternalProgram('cmd')
+ self.assertTrue(prog1.found(), msg='cmd not found')
+ prog2 = ExternalProgram('cmd.exe')
+ self.assertTrue(prog2.found(), msg='cmd.exe not found')
+ self.assertPathEqual(prog1.fullpath[0], prog2.fullpath[0])
+ # Find cmd with an absolute path that's missing the extension
+ cmd_path = prog2.fullpath[0][:-4]
+ prog = ExternalProgram(cmd_path)
+ self.assertTrue(prog.found(), msg='{!r} not found'.format(cmd_path))
+ # Finding a script with no extension inside a directory works
+ prog = ExternalProgram(os.path.join(testdir, 'test-script'))
+ self.assertTrue(prog.found(), msg='test-script not found')
+ # Finding a script with an extension inside a directory works
+ prog = ExternalProgram(os.path.join(testdir, 'test-script-ext.py'))
+ self.assertTrue(prog.found(), msg='test-script-ext.py not found')
+ # Finding a script in PATH w/o extension works and adds the interpreter
+ os.environ['PATH'] += os.pathsep + testdir
+ prog = ExternalProgram('test-script-ext')
+ self.assertTrue(prog.found(), msg='test-script-ext not found in PATH')
+ self.assertPathEqual(prog.fullpath[0], sys.executable)
+ self.assertPathBasenameEqual(prog.fullpath[1], 'test-script-ext.py')
+ # Finding a script in PATH with extension works and adds the interpreter
+ prog = ExternalProgram('test-script-ext.py')
+ self.assertTrue(prog.found(), msg='test-script-ext.py not found in PATH')
+ self.assertPathEqual(prog.fullpath[0], sys.executable)
+ self.assertPathBasenameEqual(prog.fullpath[1], 'test-script-ext.py')
+
+
class LinuxlikeTests(BasePlatformTests):
'''
Tests that should run on Linux and *BSD
diff --git a/test cases/windows/9 find program/meson.build b/test cases/windows/9 find program/meson.build
index ef34586..565fb62 100644
--- a/test cases/windows/9 find program/meson.build
+++ b/test cases/windows/9 find program/meson.build
@@ -1,4 +1,12 @@
project('find program', 'c')
+# Test that we can find native windows executables
+find_program('cmd')
+find_program('cmd.exe')
+
+# Test that a script file with an extension can be found
+ext = find_program('test-script-ext.py')
+test('ext', ext)
+# Test that a script file without an extension can be found
prog = find_program('test-script')
test('script', prog)
diff --git a/test cases/windows/9 find program/test-script-ext.py b/test cases/windows/9 find program/test-script-ext.py
new file mode 100644
index 0000000..ae9adfb
--- /dev/null
+++ b/test cases/windows/9 find program/test-script-ext.py
@@ -0,0 +1,3 @@
+#!/usr/bin/env python3
+
+print('ext/noext')