mesonlib: handle meson exe wrappers

There are cases when it is useful to wrap the main meson executable with
a script that sets up environment variables, passes --cross-file, etc.
For example, in a Yocto SDK, we need to point to the right meson.cross
so that everything "just works", and we need to alter CC, CXX, etc. In
such cases, it can happen that the "meson" found in the path is actually
a wrapper script that invokes the real meson, which may be in another
location (e.g. "meson.real" or similar).

Currently, in such a situation, meson gets confused because it tries to
invoke itself using the "meson" executable (which points to the wrapper
script) instead of the actual meson (which may be called "meson.real" or
similar). In fact, the wrapper script is not necessarily even Python, so
the whole thing fails.

Fix this by using Python imports to directly find mesonmain.py instead
of trying to detect it heuristically. In addition to fixing the wrapper
issue, this should make the detection logic much more robust.
pull/3649/head
Martin Kelly 7 years ago committed by Nirbheek Chauhan
parent ab599b5733
commit 0627e9d616
  1. 9
      meson.py
  2. 48
      mesonbuild/mesonlib.py
  3. 8
      mesonbuild/mesonmain.py
  4. 2
      run_cross_test.py
  5. 13
      run_project_tests.py
  6. 22
      run_tests.py
  7. 3
      run_unittests.py

@ -15,12 +15,7 @@
# limitations under the License.
from mesonbuild import mesonmain
import sys, os
def main():
# Always resolve the command path so Ninja can find it for regen, tests, etc.
launcher = os.path.realpath(sys.argv[0])
return mesonmain.run(sys.argv[1:], launcher)
import sys
if __name__ == '__main__':
sys.exit(main())
sys.exit(mesonmain.main())

@ -38,58 +38,12 @@ except Exception:
from glob import glob
def detect_meson_py_location():
c = sys.argv[0]
c_dir, c_fname = os.path.split(c)
# get the absolute path to the <mesontool> folder
m_dir = None
if os.path.isabs(c):
# $ /foo/<mesontool>.py <args>
m_dir = c_dir
elif c_dir == '':
# $ <mesontool> <args> (gets run from /usr/bin/<mesontool>)
in_path_exe = shutil.which(c_fname)
if in_path_exe:
if not os.path.isabs(in_path_exe):
m_dir = os.getcwd()
c_fname = in_path_exe
else:
m_dir, c_fname = os.path.split(in_path_exe)
else:
m_dir = os.path.abspath(c_dir)
# find meson in m_dir
if m_dir is not None:
for fname in ['meson', 'meson.py']:
m_path = os.path.join(m_dir, fname)
if os.path.exists(m_path):
return m_path
# No meson found, which means that either:
# a) meson is not installed
# b) meson is installed to a non-standard location
# c) the script that invoked mesonlib is not the one of meson tools (e.g. run_unittests.py)
fname = os.path.normpath(os.path.join(os.path.dirname(__file__), '..', 'meson.py'))
if os.path.exists(fname):
return fname
# If meson is still not found, we might be imported by out-of-source tests
# https://github.com/mesonbuild/meson/issues/3015
exe = shutil.which('meson')
if exe is None:
exe = shutil.which('meson.py')
if exe is not None:
return exe
# Give up.
raise RuntimeError('Could not determine how to run Meson. Please file a bug with details.')
if os.path.basename(sys.executable) == 'meson.exe':
# In Windows and using the MSI installed executable.
meson_command = [sys.executable]
python_command = [sys.executable, 'runpython']
else:
python_command = [sys.executable]
meson_command = python_command + [detect_meson_py_location()]
meson_command = python_command + ['-m', 'mesonbuild.mesonmain']
def is_ascii_string(astring):
try:

@ -382,3 +382,11 @@ def run(original_args, mainfile=None):
mlog.shutdown()
return 0
def main():
# Always resolve the command path so Ninja can find it for regen, tests, etc.
launcher = os.path.realpath(sys.argv[0])
return run(sys.argv[1:], launcher)
if __name__ == '__main__':
sys.exit(main())

@ -28,6 +28,7 @@ from pathlib import Path
from run_project_tests import gather_tests, run_tests, StopException, setup_commands
from run_project_tests import failing_logs
from run_tests import setup_pythonpath
def runtests(cross_file):
commontests = [('common', gather_tests(Path('test cases', 'common')), False)]
@ -46,5 +47,6 @@ def runtests(cross_file):
if __name__ == '__main__':
setup_commands('ninja')
setup_pythonpath()
cross_file = sys.argv[1]
runtests(cross_file)

@ -41,7 +41,7 @@ import re
from run_unittests import get_fake_options, run_configure
from run_tests import get_backend_commands, get_backend_args_for_dir, Backend
from run_tests import ensure_backend_detects_changes
from run_tests import ensure_backend_detects_changes, setup_pythonpath
class BuildStep(Enum):
@ -88,12 +88,6 @@ no_meson_log_msg = 'No meson-log.txt found.'
system_compiler = None
meson_command = os.path.join(os.getcwd(), 'meson')
if not os.path.exists(meson_command):
meson_command += '.py'
if not os.path.exists(meson_command):
raise RuntimeError('Could not find main Meson script to run.')
class StopException(Exception):
def __init__(self):
super().__init__('Stopped by user')
@ -324,7 +318,7 @@ def _run_test(testdir, test_build_dir, install_dir, extra_args, compiler, backen
if pass_libdir_to_test(testdir):
gen_args += ['--libdir', 'lib']
gen_args += [testdir, test_build_dir] + flags + test_args + extra_args
(returncode, stdo, stde) = run_configure(meson_command, gen_args)
(returncode, stdo, stde) = run_configure(mesonlib.meson_command, gen_args)
try:
logfile = Path(test_build_dir, 'meson-logs', 'meson-log.txt')
mesonlog = logfile.open(errors='ignore', encoding='utf-8').read()
@ -647,7 +641,7 @@ def check_format():
check_file(fullname)
def check_meson_commands_work():
global backend, meson_command, compile_commands, test_commands, install_commands
global backend, compile_commands, test_commands, install_commands
testdir = PurePath('test cases', 'common', '1 trivial').as_posix()
with AutoDeletedDir(tempfile.mkdtemp(prefix='b ', dir='.')) as build_dir:
print('Checking that configuring works...')
@ -692,6 +686,7 @@ if __name__ == '__main__':
setup_commands(options.backend)
detect_system_compiler()
setup_pythonpath()
script_dir = os.path.split(__file__)[0]
if script_dir != '':
os.chdir(script_dir)

@ -42,6 +42,28 @@ if mesonlib.is_windows() or mesonlib.is_cygwin():
else:
exe_suffix = ''
def setup_pythonpath():
# Make sure python can import mesonbuild, even if we change directories as
# some tests do. Since sys.path is the final product of fairly complex code
# in site.py, it's hard to tell where each entry came from just by looking
# at sys.path, so we don't know if a given entry was set from a relative or
# absolute path. If an entry was set from a relative path, it won't
# continue to work if we change directories. Instead of trying to guess
# where a given entry came from, just add the known-good mesonbuild to
# PYTHONPATH so that it will continue to be importable from other
# directories.
import mesonbuild
meson_dir = os.path.dirname(os.path.abspath(mesonbuild.__file__))
meson_root = os.path.realpath(os.path.join(meson_dir, os.pardir))
try:
python_path = os.environ['PYTHONPATH']
except KeyError:
python_path = meson_root
else:
paths = python_path.split(os.pathsep) + [meson_root]
python_path = os.pathsep.join(paths)
os.environ['PYTHONPATH'] = python_path
def get_backend_args_for_dir(backend, builddir):
'''
Visual Studio backend needs to be given the solution to build

@ -47,7 +47,7 @@ import mesonbuild.modules.pkgconfig
from run_tests import exe_suffix, get_fake_options
from run_tests import get_builddir_target_args, get_backend_commands, Backend
from run_tests import ensure_backend_detects_changes, run_configure, meson_exe
from run_tests import should_run_linux_cross_tests
from run_tests import should_run_linux_cross_tests, setup_pythonpath
def get_dynamic_section_entry(fname, entry):
@ -3313,6 +3313,7 @@ def unset_envs():
if __name__ == '__main__':
unset_envs()
setup_pythonpath()
cases = ['InternalTests', 'AllPlatformTests', 'FailureTests', 'PythonTests']
if not is_windows():
cases += ['LinuxlikeTests']

Loading…
Cancel
Save