find_program: Fix implementation of .path()

And actually test that prog.path() works. The earlier test was just
running the command without checking if it succeeded.

Also make everything use prog.get_command() or get_path() instead of
accessing the internal member prog.fullpath directly.
pull/1356/head
Nirbheek Chauhan 8 years ago
parent 280346da3a
commit 7e805a019a
  1. 14
      mesonbuild/backend/backends.py
  2. 2
      mesonbuild/backend/vs2010backend.py
  3. 49
      mesonbuild/dependencies.py
  4. 6
      mesonbuild/interpreter.py
  5. 6
      mesonbuild/modules/qt4.py
  6. 6
      mesonbuild/modules/qt5.py
  7. 2
      mesonbuild/modules/rpm.py
  8. 12
      run_unittests.py
  9. 19
      test cases/common/105 find program path/meson.build

@ -215,13 +215,13 @@ class Backend:
exe_data = os.path.join(self.environment.get_scratch_dir(), scratch_file)
with open(exe_data, 'wb') as f:
if isinstance(exe, dependencies.ExternalProgram):
exe_fullpath = exe.fullpath
exe_cmd = exe.get_command()
exe_needs_wrapper = False
elif isinstance(exe, (build.BuildTarget, build.CustomTarget)):
exe_fullpath = [self.get_target_filename_abs(exe)]
exe_cmd = [self.get_target_filename_abs(exe)]
exe_needs_wrapper = exe.is_cross
else:
exe_fullpath = [exe]
exe_cmd = [exe]
exe_needs_wrapper = False
is_cross = exe_needs_wrapper and \
self.environment.is_cross_build() and \
@ -235,7 +235,7 @@ class Backend:
extra_paths = self.determine_windows_extra_paths(exe)
else:
extra_paths = []
es = ExecutableSerialisation(basename, exe_fullpath, cmd_args, env,
es = ExecutableSerialisation(basename, exe_cmd, cmd_args, env,
is_cross, exe_wrapper, workdir,
extra_paths, capture)
pickle.dump(es, f)
@ -444,9 +444,9 @@ class Backend:
for t in tests:
exe = t.get_exe()
if isinstance(exe, dependencies.ExternalProgram):
fname = exe.fullpath
cmd = exe.get_command()
else:
fname = [os.path.join(self.environment.get_build_dir(), self.get_target_filename(t.get_exe()))]
cmd = [os.path.join(self.environment.get_build_dir(), self.get_target_filename(t.get_exe()))]
is_cross = self.environment.is_cross_build() and \
self.environment.cross_info.need_cross_compiler() and \
self.environment.cross_info.need_exe_wrapper()
@ -471,7 +471,7 @@ class Backend:
cmd_args.append(self.get_target_filename(a))
else:
raise MesonException('Bad object in test command.')
ts = TestSerialisation(t.get_name(), t.suite, fname, is_cross, exe_wrapper,
ts = TestSerialisation(t.get_name(), t.suite, cmd, is_cross, exe_wrapper,
t.is_parallel, cmd_args, t.env, t.should_fail,
t.timeout, t.workdir, extra_paths)
arr.append(ts)

@ -395,7 +395,7 @@ class Vs2010Backend(backends.Backend):
if isinstance(i, build.BuildTarget):
cmd.append(os.path.join(self.environment.get_build_dir(), self.get_target_filename(i)))
elif isinstance(i, dependencies.ExternalProgram):
cmd += i.fullpath
cmd += i.get_command()
else:
cmd.append(i)
cmd_templ = '''"%s" ''' * len(cmd)

@ -404,25 +404,25 @@ class WxDependency(Dependency):
class ExternalProgram:
windows_exts = ('exe', 'msc', 'com', 'bat')
def __init__(self, name, fullpath=None, silent=False, search_dir=None):
def __init__(self, name, command=None, silent=False, search_dir=None):
self.name = name
if fullpath is not None:
if not isinstance(fullpath, list):
self.fullpath = [fullpath]
if command is not None:
if not isinstance(command, list):
self.command = [command]
else:
self.fullpath = fullpath
self.command = command
else:
self.fullpath = self._search(name, search_dir)
self.command = self._search(name, search_dir)
if not silent:
if self.found():
mlog.log('Program', mlog.bold(name), 'found:', mlog.green('YES'),
'(%s)' % ' '.join(self.fullpath))
'(%s)' % ' '.join(self.command))
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)
return r.format(self.__class__.__name__, self.name, self.command)
@staticmethod
def _shebang_to_cmd(script):
@ -485,22 +485,22 @@ class ExternalProgram:
if commands:
return commands
# Do a standard search in PATH
fullpath = shutil.which(name)
command = shutil.which(name)
if not mesonlib.is_windows():
# On UNIX-like platforms, the standard PATH search is enough
return [fullpath]
return [command]
# HERE BEGINS THE TERROR OF WINDOWS
if fullpath:
if command:
# 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]
name_ext = os.path.splitext(command)[1]
if name_ext[1:].lower() in self.windows_exts:
# Good, it can be directly executed
return [fullpath]
return [command]
# Try to extract the interpreter from the shebang
commands = self._shebang_to_cmd(fullpath)
commands = self._shebang_to_cmd(command)
if commands:
return commands
else:
@ -509,9 +509,9 @@ class ExternalProgram:
# 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]
command = '{}.{}'.format(name, ext)
if os.path.exists(command):
return [command]
# 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.
@ -523,10 +523,17 @@ class ExternalProgram:
return [None]
def found(self):
return self.fullpath[0] is not None
return self.command[0] is not None
def get_command(self):
return self.fullpath[:]
return self.command[:]
def get_path(self):
# Assume that the last element is the full path to the script
# If it's not a script, this will be an array of length 1
if self.found():
return self.command[-1]
return None
def get_name(self):
return self.name
@ -1020,7 +1027,7 @@ class QtBaseDependency(Dependency):
if not self.qmake.found():
continue
# Check that the qmake is for qt5
pc, stdo = Popen_safe(self.qmake.fullpath + ['-v'])[0:2]
pc, stdo = Popen_safe(self.qmake.get_command() + ['-v'])[0:2]
if pc.returncode != 0:
continue
if not 'Qt version ' + self.qtver in stdo:
@ -1033,7 +1040,7 @@ class QtBaseDependency(Dependency):
return
self.version = re.search(self.qtver + '(\.\d+)+', stdo).group(0)
# Query library path, header path, and binary path
stdo = Popen_safe(self.qmake.fullpath + ['-query'])[1]
stdo = Popen_safe(self.qmake.get_command() + ['-query'])[1]
qvars = {}
for line in stdo.split('\n'):
line = line.strip()

@ -285,16 +285,16 @@ class ExternalProgramHolder(InterpreterObject):
return self.found()
def path_method(self, args, kwargs):
return self.get_command()
return self.held_object.get_path()
def found(self):
return self.held_object.found()
def get_command(self):
return self.held_object.fullpath
return self.held_object.get_command()
def get_name(self):
return self.held_object.name
return self.held_object.get_name()
class ExternalLibraryHolder(InterpreterObject):
def __init__(self, el):

@ -48,7 +48,7 @@ class Qt4Module(ExtensionModule):
raise MesonException('Moc preprocessor is not for Qt 4. Output:\n%s\n%s' %
(stdout, stderr))
mlog.log(' moc:', mlog.green('YES'), '(%s, %s)' %
(' '.join(self.moc.fullpath), moc_ver.split()[-1]))
(self.moc.get_path(), moc_ver.split()[-1]))
else:
mlog.log(' moc:', mlog.red('NO'))
if self.uic.found():
@ -61,7 +61,7 @@ class Qt4Module(ExtensionModule):
raise MesonException('Uic compiler is not for Qt4. Output:\n%s\n%s' %
(stdout, stderr))
mlog.log(' uic:', mlog.green('YES'), '(%s, %s)' %
(' '.join(self.uic.fullpath), uic_ver.split()[-1]))
(self.uic.get_path(), uic_ver.split()[-1]))
else:
mlog.log(' uic:', mlog.red('NO'))
if self.rcc.found():
@ -74,7 +74,7 @@ class Qt4Module(ExtensionModule):
raise MesonException('Rcc compiler is not for Qt 4. Output:\n%s\n%s' %
(stdout, stderr))
mlog.log(' rcc:', mlog.green('YES'), '(%s, %s)'
% (' '.join(self.rcc.fullpath), rcc_ver.split()[-1]))
% (self.rcc.get_path(), rcc_ver.split()[-1]))
else:
mlog.log(' rcc:', mlog.red('NO'))
self.tools_detected = True

@ -50,7 +50,7 @@ class Qt5Module(ExtensionModule):
raise MesonException('Moc preprocessor is not for Qt 5. Output:\n%s\n%s' %
(stdout, stderr))
mlog.log(' moc:', mlog.green('YES'), '(%s, %s)' %
(' '.join(self.moc.fullpath), moc_ver.split()[-1]))
(self.moc.get_path(), moc_ver.split()[-1]))
else:
mlog.log(' moc:', mlog.red('NO'))
if self.uic.found():
@ -65,7 +65,7 @@ class Qt5Module(ExtensionModule):
raise MesonException('Uic compiler is not for Qt 5. Output:\n%s\n%s' %
(stdout, stderr))
mlog.log(' uic:', mlog.green('YES'), '(%s, %s)' %
(' '.join(self.uic.fullpath), uic_ver.split()[-1]))
(self.uic.get_path(), uic_ver.split()[-1]))
else:
mlog.log(' uic:', mlog.red('NO'))
if self.rcc.found():
@ -80,7 +80,7 @@ class Qt5Module(ExtensionModule):
raise MesonException('Rcc compiler is not for Qt 5. Output:\n%s\n%s' %
(stdout, stderr))
mlog.log(' rcc:', mlog.green('YES'), '(%s, %s)'
% (' '.join(self.rcc.fullpath), rcc_ver.split()[-1]))
% (self.rcc.get_path(), rcc_ver.split()[-1]))
else:
mlog.log(' rcc:', mlog.red('NO'))
self.tools_detected = True

@ -108,7 +108,7 @@ class RPMModule(ExtensionModule):
fn.write('BuildRequires: %%{_bindir}/%s # FIXME\n' %
prog.get_name())
else:
fn.write('BuildRequires: %s\n' % ' '.join(prog.fullpath))
fn.write('BuildRequires: {}\n'.format(prog.get_path()))
fn.write('BuildRequires: meson\n')
fn.write('\n')
fn.write('%description\n')

@ -585,9 +585,9 @@ class WindowsTests(BasePlatformTests):
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])
self.assertPathEqual(prog1.get_path(), prog2.get_path())
# Find cmd with an absolute path that's missing the extension
cmd_path = prog2.fullpath[0][:-4]
cmd_path = prog2.get_path()[:-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
@ -600,13 +600,13 @@ class WindowsTests(BasePlatformTests):
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')
self.assertPathEqual(prog.get_command()[0], sys.executable)
self.assertPathBasenameEqual(prog.get_path(), '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')
self.assertPathEqual(prog.get_command()[0], sys.executable)
self.assertPathBasenameEqual(prog.get_path(), 'test-script-ext.py')
class LinuxlikeTests(BasePlatformTests):

@ -1,18 +1,25 @@
project('find program', 'c')
prog = find_program('program.py')
python = find_program('python3', required : false)
if not python.found()
python = find_program('python')
endif
run_command(python, prog.path())
# Source file via string
prog = find_program('program.py')
# Source file via files()
progf = files('program.py')
# Built file
py = configure_file(input : 'program.py',
output : 'builtprogram.py',
configuration : configuration_data())
find_program(py)
find_program(progf)
foreach f : [prog, find_program(py), find_program(progf)]
ret = run_command(python, f.path())
assert(ret.returncode() == 0, 'can\'t manually run @0@'.format(prog.path()))
assert(ret.stdout().strip() == 'Found', 'wrong output from manually-run @0@'.format(prog.path()))
ret = run_command(f)
assert(ret.returncode() == 0, 'can\'t run @0@'.format(prog.path()))
assert(ret.stdout().strip() == 'Found', 'wrong output from @0@'.format(prog.path()))
endforeach

Loading…
Cancel
Save