1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
|
#!/usr/bin/env python3
# SPDX-License-Identifier: Apache-2.0
# Copyright 2018 The Meson development team
import os
import tempfile
import unittest
import subprocess
import zipapp
import sysconfig
from pathlib import Path
from mesonbuild.mesonlib import windows_proof_rmtree, python_command, is_windows
from mesonbuild.coredata import version as meson_version
scheme = None
def needs_debian_path_hack():
try:
import setuptools
return int(setuptools.__version__.split('.')[0]) < 65
except ModuleNotFoundError:
return False
if needs_debian_path_hack():
# Handle the scheme that Debian patches in the as default
# This function was renamed and made public in Python 3.10
if hasattr(sysconfig, 'get_default_scheme'):
scheme = sysconfig.get_default_scheme()
else:
scheme = sysconfig._get_default_scheme()
if scheme == 'posix_local':
scheme = 'posix_prefix'
def get_pypath():
if scheme:
pypath = sysconfig.get_path('purelib', scheme=scheme, vars={'base': ''})
else:
pypath = sysconfig.get_path('purelib', vars={'base': ''})
# Ensure that / is the path separator and not \, then strip /
return Path(pypath).as_posix().strip('/')
def get_pybindir():
# 'Scripts' on Windows and 'bin' on other platforms including MSYS
if scheme:
return sysconfig.get_path('scripts', scheme=scheme, vars={'base': ''}).strip('\\/')
return sysconfig.get_path('scripts', vars={'base': ''}).strip('\\/')
class CommandTests(unittest.TestCase):
'''
Test that running meson in various ways works as expected by checking the
value of mesonlib.meson_command that was set during configuration.
'''
def setUp(self):
super().setUp()
self.orig_env = os.environ.copy()
self.orig_dir = os.getcwd()
os.environ['MESON_COMMAND_TESTS'] = '1'
self.tmpdir = Path(tempfile.mkdtemp()).resolve()
self.src_root = Path(__file__).resolve().parent
self.testdir = str(self.src_root / 'test cases/common/1 trivial')
self.meson_args = ['--backend=ninja']
def tearDown(self):
try:
windows_proof_rmtree(str(self.tmpdir))
except FileNotFoundError:
pass
os.environ.clear()
os.environ.update(self.orig_env)
os.chdir(str(self.orig_dir))
super().tearDown()
def _run(self, command, workdir=None, env=None):
'''
Run a command while printing the stdout, and also return a copy of it
'''
# If this call hangs CI will just abort. It is very hard to distinguish
# between CI issue and test bug in that case. Set timeout and fail loud
# instead.
p = subprocess.run(command, stdout=subprocess.PIPE,
env=env, text=True,
cwd=workdir, timeout=60 * 5)
print(p.stdout)
if p.returncode != 0:
raise subprocess.CalledProcessError(p.returncode, command)
return p.stdout
def assertMesonCommandIs(self, line, cmd):
self.assertTrue(line.startswith('meson_command '), msg=line)
self.assertEqual(line, f'meson_command is {cmd!r}')
def test_meson_uninstalled(self):
# This is what the meson command must be for all these cases
resolved_meson_command = python_command + [str(self.src_root / 'meson.py')]
# Absolute path to meson.py
os.chdir('/')
builddir = str(self.tmpdir / 'build1')
meson_py = str(self.src_root / 'meson.py')
meson_setup = [meson_py, 'setup']
meson_command = python_command + meson_setup + self.meson_args
stdo = self._run(meson_command + [self.testdir, builddir])
self.assertMesonCommandIs(stdo.split('\n')[0], resolved_meson_command)
# ./meson.py
os.chdir(str(self.src_root))
builddir = str(self.tmpdir / 'build2')
meson_py = './meson.py'
meson_setup = [meson_py, 'setup']
meson_command = python_command + meson_setup + self.meson_args
stdo = self._run(meson_command + [self.testdir, builddir])
self.assertMesonCommandIs(stdo.split('\n')[0], resolved_meson_command)
# Symlink to meson.py
if is_windows():
# Symlinks require admin perms
return
os.chdir(str(self.src_root))
builddir = str(self.tmpdir / 'build3')
# Create a symlink to meson.py in bindir, and add it to PATH
bindir = (self.tmpdir / 'bin')
bindir.mkdir()
(bindir / 'meson').symlink_to(self.src_root / 'meson.py')
(bindir / 'python3').symlink_to(python_command[0])
os.environ['PATH'] = str(bindir) + os.pathsep + os.environ['PATH']
# use our overridden PATH-compatible python
path_resolved_meson_command = [str(bindir / 'meson')]
# See if it works!
meson_py = 'meson'
meson_setup = [meson_py, 'setup']
meson_command = meson_setup + self.meson_args
stdo = self._run(meson_command + [self.testdir, builddir])
self.assertMesonCommandIs(stdo.split('\n')[0], path_resolved_meson_command)
def test_meson_installed(self):
# Install meson
prefix = self.tmpdir / 'prefix'
pylibdir = prefix / get_pypath()
bindir = prefix / get_pybindir()
pylibdir.mkdir(parents=True)
# XXX: join with empty name so it always ends with os.sep otherwise
# distutils complains that prefix isn't contained in PYTHONPATH
os.environ['PYTHONPATH'] = os.path.join(str(pylibdir), '')
os.environ['PATH'] = str(bindir) + os.pathsep + os.environ['PATH']
self._run(python_command + ['setup.py', 'install', '--prefix', str(prefix)])
# Fix importlib-metadata by appending all dirs in pylibdir
PYTHONPATHS = [pylibdir] + [x for x in pylibdir.iterdir()]
PYTHONPATHS = [os.path.join(str(x), '') for x in PYTHONPATHS]
os.environ['PYTHONPATH'] = os.pathsep.join(PYTHONPATHS)
# Check that all the files were installed correctly
self.assertTrue(bindir.is_dir())
self.assertTrue(pylibdir.is_dir())
# Run `meson`
os.chdir('/')
resolved_meson_command = [str(bindir / 'meson')]
builddir = str(self.tmpdir / 'build1')
meson_setup = ['meson', 'setup']
meson_command = meson_setup + self.meson_args
stdo = self._run(meson_command + [self.testdir, builddir])
self.assertMesonCommandIs(stdo.split('\n')[0], resolved_meson_command)
# Run `/path/to/meson`
builddir = str(self.tmpdir / 'build2')
meson_setup = [str(bindir / 'meson'), 'setup']
meson_command = meson_setup + self.meson_args
stdo = self._run(meson_command + [self.testdir, builddir])
self.assertMesonCommandIs(stdo.split('\n')[0], resolved_meson_command)
# Run `python3 -m mesonbuild.mesonmain`
resolved_meson_command = python_command + ['-m', 'mesonbuild.mesonmain']
builddir = str(self.tmpdir / 'build3')
meson_setup = ['-m', 'mesonbuild.mesonmain', 'setup']
meson_command = python_command + meson_setup + self.meson_args
stdo = self._run(meson_command + [self.testdir, builddir])
self.assertMesonCommandIs(stdo.split('\n')[0], resolved_meson_command)
if is_windows():
# Next part requires a shell
return
# `meson` is a wrapper to `meson.real`
resolved_meson_command = [str(bindir / 'meson.real')]
builddir = str(self.tmpdir / 'build4')
(bindir / 'meson').rename(bindir / 'meson.real')
wrapper = (bindir / 'meson')
wrapper.write_text('#!/bin/sh\n\nmeson.real "$@"', encoding='utf-8')
wrapper.chmod(0o755)
meson_setup = [str(wrapper), 'setup']
meson_command = meson_setup + self.meson_args
stdo = self._run(meson_command + [self.testdir, builddir])
self.assertMesonCommandIs(stdo.split('\n')[0], resolved_meson_command)
def test_meson_exe_windows(self):
raise unittest.SkipTest('NOT IMPLEMENTED')
def test_meson_zipapp(self):
if is_windows():
raise unittest.SkipTest('NOT IMPLEMENTED')
source = Path(__file__).resolve().parent
target = self.tmpdir / 'meson.pyz'
script = source / 'packaging' / 'create_zipapp.py'
self._run([script.as_posix(), source, '--outfile', target, '--interpreter', python_command[0]])
self._run([target.as_posix(), '--help'])
def test_meson_runpython(self):
meson_command = str(self.src_root / 'meson.py')
script_file = str(self.src_root / 'foo.py')
test_command = 'import sys; print(sys.argv[1])'
env = os.environ.copy()
del env['MESON_COMMAND_TESTS']
with open(script_file, 'w') as f:
f.write('#!/usr/bin/env python3\n\n')
f.write(f'{test_command}\n')
self.addCleanup(os.remove, script_file)
for cmd in [['-c', test_command, 'fake argument'], [script_file, 'fake argument']]:
pyout = self._run(python_command + cmd)
mesonout = self._run(python_command + [meson_command, 'runpython'] + cmd, env=env)
self.assertEqual(pyout, mesonout)
if __name__ == '__main__':
print('Meson build system', meson_version, 'Command Tests')
raise SystemExit(unittest.main(buffer=True))
|