aboutsummaryrefslogtreecommitdiff
path: root/run_unittests.py
diff options
context:
space:
mode:
authorJohn Ericson <git@JohnEricson.me>2020-08-03 11:48:27 -0400
committerJohn Ericson <git@JohnEricson.me>2020-08-03 11:48:27 -0400
commiteaf6343c065842b9719793066e765b2e5f1c2f3b (patch)
tree1bfeac5297ba489721e704e63c28f33d0fb98990 /run_unittests.py
parent87aa98c1787d800145853a8e84654e4c54ee1078 (diff)
parent70edf82c6c77902cd64f44848302bbac92d611d8 (diff)
downloadmeson-lang-enum.zip
meson-lang-enum.tar.gz
meson-lang-enum.tar.bz2
Merge remote-tracking branch 'upstream/master' into lang-enumlang-enum
Diffstat (limited to 'run_unittests.py')
-rwxr-xr-xrun_unittests.py1321
1 files changed, 1160 insertions, 161 deletions
diff --git a/run_unittests.py b/run_unittests.py
index e427ee9..4b4fe34 100755
--- a/run_unittests.py
+++ b/run_unittests.py
@@ -13,6 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import typing as T
import stat
import subprocess
import re
@@ -40,6 +41,7 @@ from contextlib import contextmanager
from glob import glob
from pathlib import (PurePath, Path)
from distutils.dir_util import copy_tree
+import typing as T
import mesonbuild.mlog
import mesonbuild.depfile
@@ -56,7 +58,7 @@ from mesonbuild.mesonlib import (
BuildDirLock, Language, LibType, MachineChoice, PerMachine, Version, is_windows,
is_osx, is_cygwin, is_dragonflybsd, is_openbsd, is_haiku, is_sunos,
windows_proof_rmtree, python_command, version_compare, split_args,
- quote_arg, relpath
+ quote_arg, relpath, is_linux
)
from mesonbuild.environment import detect_ninja
from mesonbuild.mesonlib import MesonException, EnvironmentException
@@ -311,8 +313,14 @@ class InternalTests(unittest.TestCase):
self.assertEqual(searchfunc('1.2.3'), '1.2.3')
self.assertEqual(searchfunc('foobar 2016.10.28 1.2.3'), '1.2.3')
self.assertEqual(searchfunc('2016.10.28 1.2.3'), '1.2.3')
- self.assertEqual(searchfunc('foobar 2016.10.128'), 'unknown version')
- self.assertEqual(searchfunc('2016.10.128'), 'unknown version')
+ self.assertEqual(searchfunc('foobar 2016.10.128'), '2016.10.128')
+ self.assertEqual(searchfunc('2016.10.128'), '2016.10.128')
+ self.assertEqual(searchfunc('2016.10'), '2016.10')
+ self.assertEqual(searchfunc('2016.10 1.2.3'), '1.2.3')
+ self.assertEqual(searchfunc('oops v1.2.3'), '1.2.3')
+ self.assertEqual(searchfunc('2016.oops 1.2.3'), '1.2.3')
+ self.assertEqual(searchfunc('2016.x'), 'unknown version')
+
def test_mode_symbolic_to_bits(self):
modefunc = mesonbuild.mesonlib.FileMode.perms_s_to_bits
@@ -349,17 +357,34 @@ class InternalTests(unittest.TestCase):
stat.S_IRWXU | stat.S_ISUID |
stat.S_IRGRP | stat.S_IXGRP)
+ def test_compiler_args_class_none_flush(self):
+ cc = mesonbuild.compilers.CCompiler([], 'fake', False, MachineChoice.HOST, mock.Mock())
+ a = cc.compiler_args(['-I.'])
+ #first we are checking if the tree construction deduplicates the correct -I argument
+ a += ['-I..']
+ a += ['-I./tests/']
+ a += ['-I./tests2/']
+ #think this here as assertion, we cannot apply it, otherwise the CompilerArgs would already flush the changes:
+ # assertEqual(a, ['-I.', '-I./tests2/', '-I./tests/', '-I..', '-I.'])
+ a += ['-I.']
+ a += ['-I.', '-I./tests/']
+ self.assertEqual(a, ['-I.', '-I./tests/', '-I./tests2/', '-I..'])
+
+ #then we are checking that when CompilerArgs already have a build container list, that the deduplication is taking the correct one
+ a += ['-I.', '-I./tests2/']
+ self.assertEqual(a, ['-I.', '-I./tests2/', '-I./tests/', '-I..'])
+
+
def test_compiler_args_class(self):
- cargsfunc = mesonbuild.compilers.CompilerArgs
cc = mesonbuild.compilers.CCompiler([], 'fake', False, MachineChoice.HOST, mock.Mock())
# Test that empty initialization works
- a = cargsfunc(cc)
+ a = cc.compiler_args()
self.assertEqual(a, [])
# Test that list initialization works
- a = cargsfunc(cc, ['-I.', '-I..'])
+ a = cc.compiler_args(['-I.', '-I..'])
self.assertEqual(a, ['-I.', '-I..'])
# Test that there is no de-dup on initialization
- self.assertEqual(cargsfunc(cc, ['-I.', '-I.']), ['-I.', '-I.'])
+ self.assertEqual(cc.compiler_args(['-I.', '-I.']), ['-I.', '-I.'])
## Test that appending works
a.append('-I..')
@@ -405,7 +430,7 @@ class InternalTests(unittest.TestCase):
self.assertEqual(a, ['-Ibar', '-Ifoo', '-Ibaz', '-I..', '-I.', '-Ldir', '-Lbah', '-Werror', '-O3', '-O2', '-Wall'])
## Test that adding libraries works
- l = cargsfunc(cc, ['-Lfoodir', '-lfoo'])
+ l = cc.compiler_args(['-Lfoodir', '-lfoo'])
self.assertEqual(l, ['-Lfoodir', '-lfoo'])
# Adding a library and a libpath appends both correctly
l += ['-Lbardir', '-lbar']
@@ -415,7 +440,7 @@ class InternalTests(unittest.TestCase):
self.assertEqual(l, ['-Lbardir', '-Lfoodir', '-lfoo', '-lbar'])
## Test that 'direct' append and extend works
- l = cargsfunc(cc, ['-Lfoodir', '-lfoo'])
+ l = cc.compiler_args(['-Lfoodir', '-lfoo'])
self.assertEqual(l, ['-Lfoodir', '-lfoo'])
# Direct-adding a library and a libpath appends both correctly
l.extend_direct(['-Lbardir', '-lbar'])
@@ -431,14 +456,13 @@ class InternalTests(unittest.TestCase):
self.assertEqual(l, ['-Lfoodir', '-lfoo', '-Lbardir', '-lbar', '-lbar', '/libbaz.a'])
def test_compiler_args_class_gnuld(self):
- cargsfunc = mesonbuild.compilers.CompilerArgs
## Test --start/end-group
linker = mesonbuild.linkers.GnuDynamicLinker([], MachineChoice.HOST, 'fake', '-Wl,', [])
gcc = mesonbuild.compilers.GnuCCompiler([], 'fake', False, MachineChoice.HOST, mock.Mock(), linker=linker)
## Ensure that the fake compiler is never called by overriding the relevant function
gcc.get_default_include_dirs = lambda: ['/usr/include', '/usr/share/include', '/usr/local/include']
## Test that 'direct' append and extend works
- l = cargsfunc(gcc, ['-Lfoodir', '-lfoo'])
+ l = gcc.compiler_args(['-Lfoodir', '-lfoo'])
self.assertEqual(l.to_native(copy=True), ['-Lfoodir', '-Wl,--start-group', '-lfoo', '-Wl,--end-group'])
# Direct-adding a library and a libpath appends both correctly
l.extend_direct(['-Lbardir', '-lbar'])
@@ -460,14 +484,13 @@ class InternalTests(unittest.TestCase):
self.assertEqual(l.to_native(copy=True), ['-Lfoo', '-Lfoodir', '-Wl,--start-group', '-lfoo', '-Lbardir', '-lbar', '-lbar', '/libbaz.a', '-Wl,--export-dynamic', '-Wl,-ldl', '-Wl,--end-group'])
def test_compiler_args_remove_system(self):
- cargsfunc = mesonbuild.compilers.CompilerArgs
## Test --start/end-group
linker = mesonbuild.linkers.GnuDynamicLinker([], MachineChoice.HOST, 'fake', '-Wl,', [])
gcc = mesonbuild.compilers.GnuCCompiler([], 'fake', False, MachineChoice.HOST, mock.Mock(), linker=linker)
## Ensure that the fake compiler is never called by overriding the relevant function
gcc.get_default_include_dirs = lambda: ['/usr/include', '/usr/share/include', '/usr/local/include']
## Test that 'direct' append and extend works
- l = cargsfunc(gcc, ['-Lfoodir', '-lfoo'])
+ l = gcc.compiler_args(['-Lfoodir', '-lfoo'])
self.assertEqual(l.to_native(copy=True), ['-Lfoodir', '-Wl,--start-group', '-lfoo', '-Wl,--end-group'])
## Test that to_native removes all system includes
l += ['-isystem/usr/include', '-isystem=/usr/share/include', '-DSOMETHING_IMPORTANT=1', '-isystem', '/usr/local/include']
@@ -681,7 +704,6 @@ class InternalTests(unittest.TestCase):
self.assertEqual([1, [2, [3]]], listify([1, [2, [3]]], flatten=False))
# Test flattening and unholdering
holder1 = ObjectHolder(1)
- holder3 = ObjectHolder(3)
self.assertEqual([holder1], listify(holder1))
self.assertEqual([holder1], listify([holder1]))
self.assertEqual([holder1, 2], listify([holder1, 2]))
@@ -717,25 +739,22 @@ class InternalTests(unittest.TestCase):
self.assertEqual([1, 2, 3], extract(kwargs, 'sources'))
def test_pkgconfig_module(self):
-
- class Mock:
- pass
-
- dummystate = Mock()
+ dummystate = mock.Mock()
dummystate.subproject = 'dummy'
- mock = Mock()
- mock.pcdep = Mock()
- mock.pcdep.name = "some_name"
- mock.version_reqs = []
+ _mock = mock.Mock(spec=mesonbuild.dependencies.ExternalDependency)
+ _mock.pcdep = mock.Mock()
+ _mock.pcdep.name = "some_name"
+ _mock.version_reqs = []
+ _mock = mock.Mock(held_object=_mock)
# pkgconfig dependency as lib
deps = mesonbuild.modules.pkgconfig.DependenciesHelper(dummystate, "thislib")
- deps.add_pub_libs([mock])
+ deps.add_pub_libs([_mock])
self.assertEqual(deps.format_reqs(deps.pub_reqs), "some_name")
# pkgconfig dependency as requires
deps = mesonbuild.modules.pkgconfig.DependenciesHelper(dummystate, "thislib")
- deps.add_pub_reqs([mock])
+ deps.add_pub_reqs([_mock])
self.assertEqual(deps.format_reqs(deps.pub_reqs), "some_name")
def _test_all_naming(self, cc, env, patterns, platform):
@@ -1253,7 +1272,6 @@ class InternalTests(unittest.TestCase):
self.assertFalse(errors)
-
@unittest.skipIf(is_tarball(), 'Skipping because this is a tarball release')
class DataTests(unittest.TestCase):
@@ -1466,16 +1484,49 @@ class DataTests(unittest.TestCase):
astint = AstInterpreter('.', '', '')
self.assertEqual(set(interp.funcs.keys()), set(astint.funcs.keys()))
+ def test_mesondata_is_up_to_date(self):
+ from mesonbuild.mesondata import mesondata
+ err_msg = textwrap.dedent('''
+
+ ###########################################################
+ ### mesonbuild.mesondata is not up-to-date ###
+ ### Please regenerate it by running tools/gen_data.py ###
+ ###########################################################
+
+ ''')
+
+ root_dir = Path(__file__).resolve().parent
+ mesonbuild_dir = root_dir / 'mesonbuild'
+
+ data_dirs = mesonbuild_dir.glob('**/data')
+ data_files = [] # type: T.List[T.Tuple(str, str)]
+
+ for i in data_dirs:
+ for p in i.iterdir():
+ data_files += [(p.relative_to(mesonbuild_dir).as_posix(), hashlib.sha256(p.read_bytes()).hexdigest())]
+
+ from pprint import pprint
+ current_files = set(mesondata.keys())
+ scanned_files = set([x[0] for x in data_files])
+
+ self.assertSetEqual(current_files, scanned_files, err_msg + 'Data files were added or removed\n')
+ errors = []
+ for i in data_files:
+ if mesondata[i[0]].sha256sum != i[1]:
+ errors += [i[0]]
+
+ self.assertListEqual(errors, [], err_msg + 'Files were changed')
class BasePlatformTests(unittest.TestCase):
+ prefix = '/usr'
+ libdir = 'lib'
+
def setUp(self):
super().setUp()
self.maxDiff = None
src_root = os.path.dirname(__file__)
src_root = os.path.join(os.getcwd(), src_root)
self.src_root = src_root
- self.prefix = '/usr'
- self.libdir = 'lib'
# Get the backend
# FIXME: Extract this from argv?
self.backend = getattr(Backend, os.environ.get('MESON_UNIT_TEST_BACKEND', 'ninja'))
@@ -1588,8 +1639,9 @@ class BasePlatformTests(unittest.TestCase):
extra_args = [extra_args]
args = [srcdir, self.builddir]
if default_args:
- args += ['--prefix', self.prefix,
- '--libdir', self.libdir]
+ args += ['--prefix', self.prefix]
+ if self.libdir:
+ args += ['--libdir', self.libdir]
if self.meson_native_file:
args += ['--native-file', self.meson_native_file]
if self.meson_cross_file:
@@ -1901,48 +1953,48 @@ class AllPlatformTests(BasePlatformTests):
(result, missing_variables, confdata_useless) = mesonbuild.mesonlib.do_conf_str(in_data, confdata, variable_format = vformat)
return '\n'.join(result)
- def check_formats (confdata, result):
- self.assertEqual(conf_str(['#mesondefine VAR'], confdata, 'meson'),result)
- self.assertEqual(conf_str(['#cmakedefine VAR ${VAR}'], confdata, 'cmake'),result)
- self.assertEqual(conf_str(['#cmakedefine VAR @VAR@'], confdata, 'cmake@'),result)
+ def check_formats(confdata, result):
+ self.assertEqual(conf_str(['#mesondefine VAR'], confdata, 'meson'), result)
+ self.assertEqual(conf_str(['#cmakedefine VAR ${VAR}'], confdata, 'cmake'), result)
+ self.assertEqual(conf_str(['#cmakedefine VAR @VAR@'], confdata, 'cmake@'), result)
confdata = ConfigurationData()
# Key error as they do not exists
check_formats(confdata, '/* #undef VAR */\n')
# Check boolean
- confdata.values = {'VAR': (False,'description')}
+ confdata.values = {'VAR': (False, 'description')}
check_formats(confdata, '#undef VAR\n')
- confdata.values = {'VAR': (True,'description')}
+ confdata.values = {'VAR': (True, 'description')}
check_formats(confdata, '#define VAR\n')
# Check string
- confdata.values = {'VAR': ('value','description')}
+ confdata.values = {'VAR': ('value', 'description')}
check_formats(confdata, '#define VAR value\n')
# Check integer
- confdata.values = {'VAR': (10,'description')}
+ confdata.values = {'VAR': (10, 'description')}
check_formats(confdata, '#define VAR 10\n')
# Check multiple string with cmake formats
- confdata.values = {'VAR': ('value','description')}
- self.assertEqual(conf_str(['#cmakedefine VAR xxx @VAR@ yyy @VAR@'], confdata, 'cmake@'),'#define VAR xxx value yyy value\n')
- self.assertEqual(conf_str(['#define VAR xxx @VAR@ yyy @VAR@'], confdata, 'cmake@'),'#define VAR xxx value yyy value')
- self.assertEqual(conf_str(['#cmakedefine VAR xxx ${VAR} yyy ${VAR}'], confdata, 'cmake'),'#define VAR xxx value yyy value\n')
- self.assertEqual(conf_str(['#define VAR xxx ${VAR} yyy ${VAR}'], confdata, 'cmake'),'#define VAR xxx value yyy value')
+ confdata.values = {'VAR': ('value', 'description')}
+ self.assertEqual(conf_str(['#cmakedefine VAR xxx @VAR@ yyy @VAR@'], confdata, 'cmake@'), '#define VAR xxx value yyy value\n')
+ self.assertEqual(conf_str(['#define VAR xxx @VAR@ yyy @VAR@'], confdata, 'cmake@'), '#define VAR xxx value yyy value')
+ self.assertEqual(conf_str(['#cmakedefine VAR xxx ${VAR} yyy ${VAR}'], confdata, 'cmake'), '#define VAR xxx value yyy value\n')
+ self.assertEqual(conf_str(['#define VAR xxx ${VAR} yyy ${VAR}'], confdata, 'cmake'), '#define VAR xxx value yyy value')
# Handles meson format exceptions
# Unknown format
- self.assertRaises(mesonbuild.mesonlib.MesonException, conf_str,['#mesondefine VAR xxx'], confdata, 'unknown_format')
+ self.assertRaises(mesonbuild.mesonlib.MesonException, conf_str, ['#mesondefine VAR xxx'], confdata, 'unknown_format')
# More than 2 params in mesondefine
- self.assertRaises(mesonbuild.mesonlib.MesonException, conf_str,['#mesondefine VAR xxx'], confdata, 'meson')
+ self.assertRaises(mesonbuild.mesonlib.MesonException, conf_str, ['#mesondefine VAR xxx'], confdata, 'meson')
# Mismatched line with format
- self.assertRaises(mesonbuild.mesonlib.MesonException, conf_str,['#cmakedefine VAR'], confdata, 'meson')
- self.assertRaises(mesonbuild.mesonlib.MesonException, conf_str,['#mesondefine VAR'], confdata, 'cmake')
- self.assertRaises(mesonbuild.mesonlib.MesonException, conf_str,['#mesondefine VAR'], confdata, 'cmake@')
+ self.assertRaises(mesonbuild.mesonlib.MesonException, conf_str, ['#cmakedefine VAR'], confdata, 'meson')
+ self.assertRaises(mesonbuild.mesonlib.MesonException, conf_str, ['#mesondefine VAR'], confdata, 'cmake')
+ self.assertRaises(mesonbuild.mesonlib.MesonException, conf_str, ['#mesondefine VAR'], confdata, 'cmake@')
# Dict value in confdata
- confdata.values = {'VAR': (['value'],'description')}
- self.assertRaises(mesonbuild.mesonlib.MesonException, conf_str,['#mesondefine VAR'], confdata, 'meson')
+ confdata.values = {'VAR': (['value'], 'description')}
+ self.assertRaises(mesonbuild.mesonlib.MesonException, conf_str, ['#mesondefine VAR'], confdata, 'meson')
def test_absolute_prefix_libdir(self):
'''
@@ -2262,6 +2314,12 @@ class AllPlatformTests(BasePlatformTests):
self.build()
self.run_tests()
+ def test_force_fallback_for(self):
+ testdir = os.path.join(self.unit_test_dir, '31 forcefallback')
+ self.init(testdir, extra_args=['--force-fallback-for=zlib,foo'])
+ self.build()
+ self.run_tests()
+
def test_env_ops_dont_stack(self):
'''
Test that env ops prepend/append do not stack, and that this usage issues a warning
@@ -2433,6 +2491,9 @@ class AllPlatformTests(BasePlatformTests):
self.assertPathExists(exe2)
def test_internal_include_order(self):
+ if mesonbuild.environment.detect_msys2_arch() and ('MESON_RSP_THRESHOLD' in os.environ):
+ raise unittest.SkipTest('Test does not yet support gcc rsp files on msys2')
+
testdir = os.path.join(self.common_test_dir, '134 include order')
self.init(testdir)
execmd = fxecmd = None
@@ -2448,9 +2509,12 @@ class AllPlatformTests(BasePlatformTests):
# Check include order for 'someexe'
incs = [a for a in split_args(execmd) if a.startswith("-I")]
self.assertEqual(len(incs), 9)
- # target private dir
- someexe_id = Target.construct_id_from_path("sub4", "someexe", "@exe")
- self.assertPathEqual(incs[0], "-I" + os.path.join("sub4", someexe_id))
+ # Need to run the build so the private dir is created.
+ self.build()
+ pdirs = glob(os.path.join(self.builddir, 'sub4/someexe*.p'))
+ self.assertEqual(len(pdirs), 1)
+ privdir = pdirs[0][len(self.builddir)+1:]
+ self.assertPathEqual(incs[0], "-I" + privdir)
# target build subdir
self.assertPathEqual(incs[1], "-Isub4")
# target source subdir
@@ -2471,7 +2535,10 @@ class AllPlatformTests(BasePlatformTests):
incs = [a for a in split_args(fxecmd) if a.startswith('-I')]
self.assertEqual(len(incs), 9)
# target private dir
- self.assertPathEqual(incs[0], '-Isomefxe@exe')
+ pdirs = glob(os.path.join(self.builddir, 'somefxe*.p'))
+ self.assertEqual(len(pdirs), 1)
+ privdir = pdirs[0][len(self.builddir)+1:]
+ self.assertPathEqual(incs[0], '-I' + privdir)
# target build dir
self.assertPathEqual(incs[1], '-I.')
# target source dir
@@ -2546,6 +2613,8 @@ class AllPlatformTests(BasePlatformTests):
self.assertIsInstance(linker, ar)
if is_osx():
self.assertIsInstance(cc.linker, mesonbuild.linkers.AppleDynamicLinker)
+ elif is_sunos():
+ self.assertIsInstance(cc.linker, (mesonbuild.linkers.SolarisDynamicLinker, mesonbuild.linkers.GnuLikeDynamicLinkerMixin))
else:
self.assertIsInstance(cc.linker, mesonbuild.linkers.GnuLikeDynamicLinkerMixin)
if isinstance(cc, clangcl):
@@ -2796,9 +2865,25 @@ class AllPlatformTests(BasePlatformTests):
# fails sometimes.
pass
- def test_dist_hg(self):
+ def has_working_hg(self):
if not shutil.which('hg'):
- raise unittest.SkipTest('Mercurial not found')
+ return False
+ try:
+ # This check should not be necessary, but
+ # CI under macOS passes the above test even
+ # though Mercurial is not installed.
+ if subprocess.call(['hg', '--version'],
+ stdout=subprocess.DEVNULL,
+ stderr=subprocess.DEVNULL) != 0:
+ return False
+ return True
+ except FileNotFoundError:
+ return False
+
+
+ def test_dist_hg(self):
+ if not self.has_working_hg():
+ raise unittest.SkipTest('Mercurial not found or broken.')
if self.backend is not Backend.ninja:
raise unittest.SkipTest('Dist is only supported with Ninja')
@@ -3139,8 +3224,9 @@ int main(int argc, char **argv) {
self.assertEqual(foo_dep.get_link_args(), link_args)
# Ensure include args are properly quoted
incdir = PurePath(prefix) / PurePath('include')
- cargs = ['-I' + incdir.as_posix()]
- self.assertEqual(foo_dep.get_compile_args(), cargs)
+ cargs = ['-I' + incdir.as_posix(), '-DLIBFOO']
+ # pkg-config and pkgconf does not respect the same order
+ self.assertEqual(sorted(foo_dep.get_compile_args()), sorted(cargs))
def test_array_option_change(self):
def get_opt():
@@ -3419,67 +3505,6 @@ int main(int argc, char **argv) {
f.write('public class Foo { public static void main() {} }')
self._run(self.meson_command + ['init', '-b'], workdir=tmpdir)
- # The test uses mocking and thus requires that
- # the current process is the one to run the Meson steps.
- # If we are using an external test executable (most commonly
- # in Debian autopkgtests) then the mocking won't work.
- @unittest.skipIf('MESON_EXE' in os.environ, 'MESON_EXE is defined, can not use mocking.')
- def test_cross_file_system_paths(self):
- if is_windows():
- raise unittest.SkipTest('system crossfile paths not defined for Windows (yet)')
- if is_sunos():
- cc = 'gcc'
- else:
- cc = 'cc'
-
- testdir = os.path.join(self.common_test_dir, '1 trivial')
- cross_content = textwrap.dedent("""\
- [binaries]
- c = '/usr/bin/{}'
- ar = '/usr/bin/ar'
- strip = '/usr/bin/ar'
-
- [properties]
-
- [host_machine]
- system = 'linux'
- cpu_family = 'x86'
- cpu = 'i686'
- endian = 'little'
- """.format(cc))
-
- with tempfile.TemporaryDirectory() as d:
- dir_ = os.path.join(d, 'meson', 'cross')
- os.makedirs(dir_)
- with tempfile.NamedTemporaryFile('w', dir=dir_, delete=False) as f:
- f.write(cross_content)
- name = os.path.basename(f.name)
-
- with mock.patch.dict(os.environ, {'XDG_DATA_HOME': d}):
- self.init(testdir, extra_args=['--cross-file=' + name], inprocess=True)
- self.wipe()
-
- with mock.patch.dict(os.environ, {'XDG_DATA_DIRS': d}):
- os.environ.pop('XDG_DATA_HOME', None)
- self.init(testdir, extra_args=['--cross-file=' + name], inprocess=True)
- self.wipe()
-
- with tempfile.TemporaryDirectory() as d:
- dir_ = os.path.join(d, '.local', 'share', 'meson', 'cross')
- os.makedirs(dir_)
- with tempfile.NamedTemporaryFile('w', dir=dir_, delete=False) as f:
- f.write(cross_content)
- name = os.path.basename(f.name)
-
- # If XDG_DATA_HOME is set in the environment running the
- # tests this test will fail, os mock the environment, pop
- # it, then test
- with mock.patch.dict(os.environ):
- os.environ.pop('XDG_DATA_HOME', None)
- with mock.patch('mesonbuild.coredata.os.path.expanduser', lambda x: x.replace('~', d)):
- self.init(testdir, extra_args=['--cross-file=' + name], inprocess=True)
- self.wipe()
-
def test_compiler_run_command(self):
'''
The test checks that the compiler object can be passed to
@@ -3937,7 +3962,7 @@ recommended as it is not supported on some platforms''')
with tempfile.NamedTemporaryFile(mode='w', delete=False) as crossfile:
crossfile.write(textwrap.dedent(
'''[binaries]
- pkgconfig = r'{0}'
+ pkgconfig = '{0}'
[properties]
@@ -3967,7 +3992,7 @@ recommended as it is not supported on some platforms''')
pkgconfig = 'pkg-config'
[properties]
- pkg_config_libdir = [r'{0}']
+ pkg_config_libdir = ['{0}']
[host_machine]
system = 'linux'
@@ -4103,6 +4128,11 @@ recommended as it is not supported on some platforms''')
'version': '1.0'
},
{
+ 'descriptive_name': 'sub_implicit',
+ 'name': 'sub_implicit',
+ 'version': '1.0',
+ },
+ {
'descriptive_name': 'sub-novar',
'name': 'sub_novar',
'version': '1.0',
@@ -4456,6 +4486,83 @@ recommended as it is not supported on some platforms''')
self.maxDiff = None
self.assertListEqual(res_nb, res_wb)
+ def test_introspect_ast_source(self):
+ testdir = os.path.join(self.unit_test_dir, '57 introspection')
+ testfile = os.path.join(testdir, 'meson.build')
+ res_nb = self.introspect_directory(testfile, ['--ast'] + self.meson_args)
+
+ node_counter = {}
+
+ def accept_node(json_node):
+ self.assertIsInstance(json_node, dict)
+ for i in ['lineno', 'colno', 'end_lineno', 'end_colno']:
+ self.assertIn(i, json_node)
+ self.assertIsInstance(json_node[i], int)
+ self.assertIn('node', json_node)
+ n = json_node['node']
+ self.assertIsInstance(n, str)
+ self.assertIn(n, nodes)
+ if n not in node_counter:
+ node_counter[n] = 0
+ node_counter[n] = node_counter[n] + 1
+ for nodeDesc in nodes[n]:
+ key = nodeDesc[0]
+ func = nodeDesc[1]
+ self.assertIn(key, json_node)
+ if func is None:
+ tp = nodeDesc[2]
+ self.assertIsInstance(json_node[key], tp)
+ continue
+ func(json_node[key])
+
+ def accept_node_list(node_list):
+ self.assertIsInstance(node_list, list)
+ for i in node_list:
+ accept_node(i)
+
+ def accept_kwargs(kwargs):
+ self.assertIsInstance(kwargs, list)
+ for i in kwargs:
+ self.assertIn('key', i)
+ self.assertIn('val', i)
+ accept_node(i['key'])
+ accept_node(i['val'])
+
+ nodes = {
+ 'BooleanNode': [('value', None, bool)],
+ 'IdNode': [('value', None, str)],
+ 'NumberNode': [('value', None, int)],
+ 'StringNode': [('value', None, str)],
+ 'ContinueNode': [],
+ 'BreakNode': [],
+ 'ArgumentNode': [('positional', accept_node_list), ('kwargs', accept_kwargs)],
+ 'ArrayNode': [('args', accept_node)],
+ 'DictNode': [('args', accept_node)],
+ 'EmptyNode': [],
+ 'OrNode': [('left', accept_node), ('right', accept_node)],
+ 'AndNode': [('left', accept_node), ('right', accept_node)],
+ 'ComparisonNode': [('left', accept_node), ('right', accept_node), ('ctype', None, str)],
+ 'ArithmeticNode': [('left', accept_node), ('right', accept_node), ('op', None, str)],
+ 'NotNode': [('right', accept_node)],
+ 'CodeBlockNode': [('lines', accept_node_list)],
+ 'IndexNode': [('object', accept_node), ('index', accept_node)],
+ 'MethodNode': [('object', accept_node), ('args', accept_node), ('name', None, str)],
+ 'FunctionNode': [('args', accept_node), ('name', None, str)],
+ 'AssignmentNode': [('value', accept_node), ('var_name', None, str)],
+ 'PlusAssignmentNode': [('value', accept_node), ('var_name', None, str)],
+ 'ForeachClauseNode': [('items', accept_node), ('block', accept_node), ('varnames', None, list)],
+ 'IfClauseNode': [('ifs', accept_node_list), ('else', accept_node)],
+ 'IfNode': [('condition', accept_node), ('block', accept_node)],
+ 'UMinusNode': [('right', accept_node)],
+ 'TernaryNode': [('condition', accept_node), ('true', accept_node), ('false', accept_node)],
+ }
+
+ accept_node(res_nb)
+
+ for n, c in [('ContinueNode', 2), ('BreakNode', 1), ('NotNode', 3)]:
+ self.assertIn(n, node_counter)
+ self.assertEqual(node_counter[n], c)
+
def test_introspect_dependencies_from_source(self):
testdir = os.path.join(self.unit_test_dir, '57 introspection')
testfile = os.path.join(testdir, 'meson.build')
@@ -4535,7 +4642,7 @@ recommended as it is not supported on some platforms''')
self._run(self.mconf_command + [self.builddir])
def test_summary(self):
- testdir = os.path.join(self.unit_test_dir, '72 summary')
+ testdir = os.path.join(self.unit_test_dir, '73 summary')
out = self.init(testdir)
expected = textwrap.dedent(r'''
Some Subproject 2.0
@@ -4559,6 +4666,10 @@ recommended as it is not supported on some platforms''')
no: NO
coma list: a, b, c
+ Plugins
+ long coma list: alpha, alphacolor, apetag, audiofx, audioparsers, auparse,
+ autodetect, avi
+
Subprojects
sub: YES
sub2: NO Problem encountered: This subproject failed
@@ -4575,21 +4686,86 @@ recommended as it is not supported on some platforms''')
def test_meson_compile(self):
"""Test the meson compile command."""
- prog = 'trivialprog'
- if is_windows():
- prog = '{}.exe'.format(prog)
+
+ def get_exe_name(basename: str) -> str:
+ if is_windows():
+ return '{}.exe'.format(basename)
+ else:
+ return basename
+
+ def get_shared_lib_name(basename: str) -> str:
+ if mesonbuild.environment.detect_msys2_arch():
+ return 'lib{}.dll'.format(basename)
+ elif is_windows():
+ return '{}.dll'.format(basename)
+ elif is_cygwin():
+ return 'cyg{}.dll'.format(basename)
+ elif is_osx():
+ return 'lib{}.dylib'.format(basename)
+ else:
+ return 'lib{}.so'.format(basename)
+
+ def get_static_lib_name(basename: str) -> str:
+ return 'lib{}.a'.format(basename)
+
+ # Base case (no targets or additional arguments)
testdir = os.path.join(self.common_test_dir, '1 trivial')
self.init(testdir)
+
self._run([*self.meson_command, 'compile', '-C', self.builddir])
- # If compile worked then we should get a program
- self.assertPathExists(os.path.join(self.builddir, prog))
+ self.assertPathExists(os.path.join(self.builddir, get_exe_name('trivialprog')))
+
+ # `--clean`
self._run([*self.meson_command, 'compile', '-C', self.builddir, '--clean'])
- self.assertPathDoesNotExist(os.path.join(self.builddir, prog))
+ self.assertPathDoesNotExist(os.path.join(self.builddir, get_exe_name('trivialprog')))
+
+ # Target specified in a project with unique names
+
+ testdir = os.path.join(self.common_test_dir, '6 linkshared')
+ self.init(testdir, extra_args=['--wipe'])
+ # Multiple targets and target type specified
+ self._run([*self.meson_command, 'compile', '-C', self.builddir, 'mylib', 'mycpplib:shared_library'])
+ # Check that we have a shared lib, but not an executable, i.e. check that target actually worked
+ self.assertPathExists(os.path.join(self.builddir, get_shared_lib_name('mylib')))
+ self.assertPathDoesNotExist(os.path.join(self.builddir, get_exe_name('prog')))
+ self.assertPathExists(os.path.join(self.builddir, get_shared_lib_name('mycpplib')))
+ self.assertPathDoesNotExist(os.path.join(self.builddir, get_exe_name('cppprog')))
+
+ # Target specified in a project with non unique names
+
+ testdir = os.path.join(self.common_test_dir, '190 same target name')
+ self.init(testdir, extra_args=['--wipe'])
+ self._run([*self.meson_command, 'compile', '-C', self.builddir, './foo'])
+ self.assertPathExists(os.path.join(self.builddir, get_static_lib_name('foo')))
+ self._run([*self.meson_command, 'compile', '-C', self.builddir, 'sub/foo'])
+ self.assertPathExists(os.path.join(self.builddir, 'sub', get_static_lib_name('foo')))
+
+ # run_target
+
+ testdir = os.path.join(self.common_test_dir, '54 run target')
+ self.init(testdir, extra_args=['--wipe'])
+ out = self._run([*self.meson_command, 'compile', '-C', self.builddir, 'py3hi'])
+ self.assertIn('I am Python3.', out)
+
+ # `--$BACKEND-args`
+
+ testdir = os.path.join(self.common_test_dir, '1 trivial')
+ if self.backend is Backend.ninja:
+ self.init(testdir, extra_args=['--wipe'])
+ # Dry run - should not create a program
+ self._run([*self.meson_command, 'compile', '-C', self.builddir, '--ninja-args=-n'])
+ self.assertPathDoesNotExist(os.path.join(self.builddir, get_exe_name('trivialprog')))
+ elif self.backend is Backend.vs:
+ self.init(testdir, extra_args=['--wipe'])
+ self._run([*self.meson_command, 'compile', '-C', self.builddir])
+ # Explicitly clean the target through msbuild interface
+ self._run([*self.meson_command, 'compile', '-C', self.builddir, '--vs-args=-t:{}:Clean'.format(re.sub(r'[\%\$\@\;\.\(\)\']', '_', get_exe_name('trivialprog')))])
+ self.assertPathDoesNotExist(os.path.join(self.builddir, get_exe_name('trivialprog')))
def test_spurious_reconfigure_built_dep_file(self):
- testdir = os.path.join(self.unit_test_dir, '74 dep files')
+ testdir = os.path.join(self.unit_test_dir, '75 dep files')
# Regression test: Spurious reconfigure was happening when build
# directory is inside source directory.
@@ -4617,6 +4793,242 @@ recommended as it is not supported on some platforms''')
out = self.build()
self.assertNotIn('Project configured', out)
+ def _test_junit(self, case: str) -> None:
+ try:
+ import lxml.etree as et
+ except ImportError:
+ raise unittest.SkipTest('lxml required, but not found.')
+
+ schema = et.XMLSchema(et.parse(str(Path(__file__).parent / 'data' / 'schema.xsd')))
+
+ self.init(case)
+ self.run_tests()
+
+ junit = et.parse(str(Path(self.builddir) / 'meson-logs' / 'testlog.junit.xml'))
+ try:
+ schema.assertValid(junit)
+ except et.DocumentInvalid as e:
+ self.fail(e.error_log)
+
+ def test_junit_valid_tap(self):
+ self._test_junit(os.path.join(self.common_test_dir, '213 tap tests'))
+
+ def test_junit_valid_exitcode(self):
+ self._test_junit(os.path.join(self.common_test_dir, '44 test args'))
+
+ def test_junit_valid_gtest(self):
+ self._test_junit(os.path.join(self.framework_test_dir, '2 gtest'))
+
+ def test_link_language_linker(self):
+ # TODO: there should be some way to query how we're linking things
+ # without resorting to reading the ninja.build file
+ if self.backend is not Backend.ninja:
+ raise unittest.SkipTest('This test reads the ninja file')
+
+ testdir = os.path.join(self.common_test_dir, '232 link language')
+ self.init(testdir)
+
+ build_ninja = os.path.join(self.builddir, 'build.ninja')
+ with open(build_ninja, 'r', encoding='utf-8') as f:
+ contents = f.read()
+
+ self.assertRegex(contents, r'build main(\.exe)?.*: c_LINKER')
+ self.assertRegex(contents, r'build (lib|cyg)?mylib.*: c_LINKER')
+
+ def test_commands_documented(self):
+ '''
+ Test that all listed meson commands are documented in Commands.md.
+ '''
+
+ # The docs directory is not in release tarballs.
+ if not os.path.isdir('docs'):
+ raise unittest.SkipTest('Doc directory does not exist.')
+ doc_path = 'docs/markdown_dynamic/Commands.md'
+
+ md = None
+ with open(doc_path, encoding='utf-8') as f:
+ md = f.read()
+ self.assertIsNotNone(md)
+
+ ## Get command sections
+
+ section_pattern = re.compile(r'^### (.+)$', re.MULTILINE)
+ md_command_section_matches = [i for i in section_pattern.finditer(md)]
+ md_command_sections = dict()
+ for i, s in enumerate(md_command_section_matches):
+ section_end = len(md) if i == len(md_command_section_matches) - 1 else md_command_section_matches[i + 1].start()
+ md_command_sections[s.group(1)] = (s.start(), section_end)
+
+ ## Validate commands
+
+ md_commands = set(k for k,v in md_command_sections.items())
+
+ help_output = self._run(self.meson_command + ['--help'])
+ help_commands = set(c.strip() for c in re.findall(r'usage:(?:.+)?{((?:[a-z]+,*)+?)}', help_output, re.MULTILINE|re.DOTALL)[0].split(','))
+
+ self.assertEqual(md_commands | {'help'}, help_commands, 'Doc file: `{}`'.format(doc_path))
+
+ ## Validate that each section has proper placeholders
+
+ def get_data_pattern(command):
+ return re.compile(
+ r'^```[\r\n]'
+ r'{{ cmd_help\[\'' + command + r'\'\]\[\'usage\'\] }}[\r\n]'
+ r'^```[\r\n]'
+ r'.*?'
+ r'^```[\r\n]'
+ r'{{ cmd_help\[\'' + command + r'\'\]\[\'arguments\'\] }}[\r\n]'
+ r'^```',
+ flags = re.MULTILINE|re.DOTALL)
+
+ for command in md_commands:
+ m = get_data_pattern(command).search(md, pos=md_command_sections[command][0], endpos=md_command_sections[command][1])
+ self.assertIsNotNone(m, 'Command `{}` is missing placeholders for dynamic data. Doc file: `{}`'.format(command, doc_path))
+
+ def test_coverage(self):
+ if mesonbuild.environment.detect_msys2_arch():
+ raise unittest.SkipTest('Skipped due to problems with coverage on MSYS2')
+ gcovr_exe, gcovr_new_rootdir = mesonbuild.environment.detect_gcovr()
+ if not gcovr_exe:
+ raise unittest.SkipTest('gcovr not found, or too old')
+ testdir = os.path.join(self.common_test_dir, '1 trivial')
+ env = get_fake_env(testdir, self.builddir, self.prefix)
+ cc = env.detect_c_compiler(MachineChoice.HOST)
+ if cc.get_id() == 'clang':
+ if not mesonbuild.environment.detect_llvm_cov():
+ raise unittest.SkipTest('llvm-cov not found')
+ if cc.get_id() == 'msvc':
+ raise unittest.SkipTest('Test only applies to non-MSVC compilers')
+ self.init(testdir, extra_args=['-Db_coverage=true'])
+ self.build()
+ self.run_tests()
+ self.run_target('coverage')
+
+ def test_coverage_complex(self):
+ if mesonbuild.environment.detect_msys2_arch():
+ raise unittest.SkipTest('Skipped due to problems with coverage on MSYS2')
+ gcovr_exe, gcovr_new_rootdir = mesonbuild.environment.detect_gcovr()
+ if not gcovr_exe:
+ raise unittest.SkipTest('gcovr not found, or too old')
+ testdir = os.path.join(self.common_test_dir, '109 generatorcustom')
+ env = get_fake_env(testdir, self.builddir, self.prefix)
+ cc = env.detect_c_compiler(MachineChoice.HOST)
+ if cc.get_id() == 'clang':
+ if not mesonbuild.environment.detect_llvm_cov():
+ raise unittest.SkipTest('llvm-cov not found')
+ if cc.get_id() == 'msvc':
+ raise unittest.SkipTest('Test only applies to non-MSVC compilers')
+ self.init(testdir, extra_args=['-Db_coverage=true'])
+ self.build()
+ self.run_tests()
+ self.run_target('coverage')
+
+ def test_coverage_html(self):
+ if mesonbuild.environment.detect_msys2_arch():
+ raise unittest.SkipTest('Skipped due to problems with coverage on MSYS2')
+ gcovr_exe, gcovr_new_rootdir = mesonbuild.environment.detect_gcovr()
+ if not gcovr_exe:
+ raise unittest.SkipTest('gcovr not found, or too old')
+ testdir = os.path.join(self.common_test_dir, '1 trivial')
+ env = get_fake_env(testdir, self.builddir, self.prefix)
+ cc = env.detect_c_compiler(MachineChoice.HOST)
+ if cc.get_id() == 'clang':
+ if not mesonbuild.environment.detect_llvm_cov():
+ raise unittest.SkipTest('llvm-cov not found')
+ if cc.get_id() == 'msvc':
+ raise unittest.SkipTest('Test only applies to non-MSVC compilers')
+ self.init(testdir, extra_args=['-Db_coverage=true'])
+ self.build()
+ self.run_tests()
+ self.run_target('coverage-html')
+
+ def test_coverage_text(self):
+ if mesonbuild.environment.detect_msys2_arch():
+ raise unittest.SkipTest('Skipped due to problems with coverage on MSYS2')
+ gcovr_exe, gcovr_new_rootdir = mesonbuild.environment.detect_gcovr()
+ if not gcovr_exe:
+ raise unittest.SkipTest('gcovr not found, or too old')
+ testdir = os.path.join(self.common_test_dir, '1 trivial')
+ env = get_fake_env(testdir, self.builddir, self.prefix)
+ cc = env.detect_c_compiler(MachineChoice.HOST)
+ if cc.get_id() == 'clang':
+ if not mesonbuild.environment.detect_llvm_cov():
+ raise unittest.SkipTest('llvm-cov not found')
+ if cc.get_id() == 'msvc':
+ raise unittest.SkipTest('Test only applies to non-MSVC compilers')
+ self.init(testdir, extra_args=['-Db_coverage=true'])
+ self.build()
+ self.run_tests()
+ self.run_target('coverage-text')
+
+ def test_coverage_xml(self):
+ if mesonbuild.environment.detect_msys2_arch():
+ raise unittest.SkipTest('Skipped due to problems with coverage on MSYS2')
+ gcovr_exe, gcovr_new_rootdir = mesonbuild.environment.detect_gcovr()
+ if not gcovr_exe:
+ raise unittest.SkipTest('gcovr not found, or too old')
+ testdir = os.path.join(self.common_test_dir, '1 trivial')
+ env = get_fake_env(testdir, self.builddir, self.prefix)
+ cc = env.detect_c_compiler(MachineChoice.HOST)
+ if cc.get_id() == 'clang':
+ if not mesonbuild.environment.detect_llvm_cov():
+ raise unittest.SkipTest('llvm-cov not found')
+ if cc.get_id() == 'msvc':
+ raise unittest.SkipTest('Test only applies to non-MSVC compilers')
+ self.init(testdir, extra_args=['-Db_coverage=true'])
+ self.build()
+ self.run_tests()
+ self.run_target('coverage-xml')
+
+ def test_cross_file_constants(self):
+ with temp_filename() as crossfile1, temp_filename() as crossfile2:
+ with open(crossfile1, 'w') as f:
+ f.write(textwrap.dedent(
+ '''
+ [constants]
+ compiler = 'gcc'
+ '''))
+ with open(crossfile2, 'w') as f:
+ f.write(textwrap.dedent(
+ '''
+ [constants]
+ toolchain = '/toolchain/'
+ common_flags = ['--sysroot=' + toolchain / 'sysroot']
+
+ [properties]
+ c_args = common_flags + ['-DSOMETHING']
+ cpp_args = c_args + ['-DSOMETHING_ELSE']
+
+ [binaries]
+ c = toolchain / compiler
+ '''))
+
+ values = mesonbuild.coredata.parse_machine_files([crossfile1, crossfile2])
+ self.assertEqual(values['binaries']['c'], '/toolchain/gcc')
+ self.assertEqual(values['properties']['c_args'],
+ ['--sysroot=/toolchain/sysroot', '-DSOMETHING'])
+ self.assertEqual(values['properties']['cpp_args'],
+ ['--sysroot=/toolchain/sysroot', '-DSOMETHING', '-DSOMETHING_ELSE'])
+
+ @unittest.skipIf(is_windows(), 'Directory cleanup fails for some reason')
+ def test_wrap_git(self):
+ with tempfile.TemporaryDirectory() as tmpdir:
+ srcdir = os.path.join(tmpdir, 'src')
+ shutil.copytree(os.path.join(self.unit_test_dir, '81 wrap-git'), srcdir)
+ upstream = os.path.join(srcdir, 'subprojects', 'wrap_git_upstream')
+ upstream_uri = Path(upstream).as_uri()
+ _git_init(upstream)
+ with open(os.path.join(srcdir, 'subprojects', 'wrap_git.wrap'), 'w') as f:
+ f.write(textwrap.dedent('''
+ [wrap-git]
+ url = {}
+ patch_directory = wrap_git_builddef
+ revision = master
+ '''.format(upstream_uri)))
+ self.init(srcdir)
+ self.build()
+ self.run_tests()
+
class FailureTests(BasePlatformTests):
'''
Tests that test failure conditions. Build files here should be dynamically
@@ -5134,7 +5546,7 @@ class WindowsTests(BasePlatformTests):
raise
raise unittest.SkipTest('pefile module not found')
testdir = os.path.join(self.common_test_dir, '6 linkshared')
- self.init(testdir)
+ self.init(testdir, extra_args=['--buildtype=release'])
self.build()
# Test that binaries have a non-zero checksum
env = get_fake_env()
@@ -5282,7 +5694,7 @@ class DarwinTests(BasePlatformTests):
def test_removing_unused_linker_args(self):
testdir = os.path.join(self.common_test_dir, '108 has arg')
- env = {'CFLAGS': '-L/tmp -L /var/tmp -headerpad_max_install_names -Wl,-export_dynamic'}
+ env = {'CFLAGS': '-L/tmp -L /var/tmp -headerpad_max_install_names -Wl,-export_dynamic -framework Foundation'}
self.init(testdir, override_envvars=env)
@@ -5424,6 +5836,19 @@ class LinuxlikeTests(BasePlatformTests):
out = self._run(cmd + ['--libs'], override_envvars=env).strip().split()
self.assertEqual(out, ['-llibmain2', '-llibinternal'])
+ # See common/47 pkgconfig-gen/meson.build for description of the case this test
+ with open(os.path.join(privatedir1, 'simple2.pc')) as f:
+ content = f.read()
+ self.assertIn('Libs: -L${libdir} -lsimple2 -lz -lsimple1', content)
+
+ with open(os.path.join(privatedir1, 'simple3.pc')) as f:
+ content = f.read()
+ self.assertEqual(1, content.count('-lsimple3'))
+
+ with open(os.path.join(privatedir1, 'simple5.pc')) as f:
+ content = f.read()
+ self.assertNotIn('-lstat2', content)
+
def test_pkgconfig_uninstalled(self):
testdir = os.path.join(self.common_test_dir, '47 pkgconfig-gen')
self.init(testdir)
@@ -5533,6 +5958,10 @@ class LinuxlikeTests(BasePlatformTests):
self.assertRegex('\n'.join(mesonlog),
r'Run-time dependency qt5 \(modules: Core\) found: YES .* \((qmake|qmake-qt5)\)\n')
+ def glob_sofiles_without_privdir(self, g):
+ files = glob(g)
+ return [f for f in files if not f.endswith('.p')]
+
def _test_soname_impl(self, libpath, install):
if is_cygwin() or is_osx():
raise unittest.SkipTest('Test only applicable to ELF and linuxlike sonames')
@@ -5548,28 +5977,28 @@ class LinuxlikeTests(BasePlatformTests):
self.assertPathExists(nover)
self.assertFalse(os.path.islink(nover))
self.assertEqual(get_soname(nover), 'libnover.so')
- self.assertEqual(len(glob(nover[:-3] + '*')), 1)
+ self.assertEqual(len(self.glob_sofiles_without_privdir(nover[:-3] + '*')), 1)
# File with version set
verset = os.path.join(libpath, 'libverset.so')
self.assertPathExists(verset + '.4.5.6')
self.assertEqual(os.readlink(verset), 'libverset.so.4')
self.assertEqual(get_soname(verset), 'libverset.so.4')
- self.assertEqual(len(glob(verset[:-3] + '*')), 3)
+ self.assertEqual(len(self.glob_sofiles_without_privdir(verset[:-3] + '*')), 3)
# File with soversion set
soverset = os.path.join(libpath, 'libsoverset.so')
self.assertPathExists(soverset + '.1.2.3')
self.assertEqual(os.readlink(soverset), 'libsoverset.so.1.2.3')
self.assertEqual(get_soname(soverset), 'libsoverset.so.1.2.3')
- self.assertEqual(len(glob(soverset[:-3] + '*')), 2)
+ self.assertEqual(len(self.glob_sofiles_without_privdir(soverset[:-3] + '*')), 2)
# File with version and soversion set to same values
settosame = os.path.join(libpath, 'libsettosame.so')
self.assertPathExists(settosame + '.7.8.9')
self.assertEqual(os.readlink(settosame), 'libsettosame.so.7.8.9')
self.assertEqual(get_soname(settosame), 'libsettosame.so.7.8.9')
- self.assertEqual(len(glob(settosame[:-3] + '*')), 2)
+ self.assertEqual(len(self.glob_sofiles_without_privdir(settosame[:-3] + '*')), 2)
# File with version and soversion set to different values
bothset = os.path.join(libpath, 'libbothset.so')
@@ -5577,7 +6006,7 @@ class LinuxlikeTests(BasePlatformTests):
self.assertEqual(os.readlink(bothset), 'libbothset.so.1.2.3')
self.assertEqual(os.readlink(bothset + '.1.2.3'), 'libbothset.so.4.5.6')
self.assertEqual(get_soname(bothset), 'libbothset.so.1.2.3')
- self.assertEqual(len(glob(bothset[:-3] + '*')), 3)
+ self.assertEqual(len(self.glob_sofiles_without_privdir(bothset[:-3] + '*')), 3)
def test_soname(self):
self._test_soname_impl(self.builddir, False)
@@ -5697,10 +6126,12 @@ class LinuxlikeTests(BasePlatformTests):
def test_unity_subproj(self):
testdir = os.path.join(self.common_test_dir, '45 subproject')
self.init(testdir, extra_args='--unity=subprojects')
- simpletest_id = Target.construct_id_from_path('subprojects/sublib', 'simpletest', '@exe')
- self.assertPathExists(os.path.join(self.builddir, 'subprojects/sublib', simpletest_id, 'simpletest-unity0.c'))
- sublib_id = Target.construct_id_from_path('subprojects/sublib', 'sublib', '@sha')
- self.assertPathExists(os.path.join(self.builddir, 'subprojects/sublib', sublib_id, 'sublib-unity0.c'))
+ pdirs = glob(os.path.join(self.builddir, 'subprojects/sublib/simpletest*.p'))
+ self.assertEqual(len(pdirs), 1)
+ self.assertPathExists(os.path.join(pdirs[0], 'simpletest-unity0.c'))
+ sdirs = glob(os.path.join(self.builddir, 'subprojects/sublib/*sublib*.p'))
+ self.assertEqual(len(sdirs), 1)
+ self.assertPathExists(os.path.join(sdirs[0], 'sublib-unity0.c'))
self.assertPathDoesNotExist(os.path.join(self.builddir, 'user@exe/user-unity.c'))
self.build()
@@ -6036,6 +6467,54 @@ class LinuxlikeTests(BasePlatformTests):
install_rpath = get_rpath(os.path.join(self.installdir, 'usr/bin/progcxx'))
self.assertEqual(install_rpath, 'baz')
+ def test_global_rpath(self):
+ if is_cygwin():
+ raise unittest.SkipTest('Windows PE/COFF binaries do not use RPATH')
+ if is_osx():
+ raise unittest.SkipTest('Global RPATHs via LDFLAGS not yet supported on MacOS (does anybody need it?)')
+
+ testdir = os.path.join(self.unit_test_dir, '80 global-rpath')
+ oldinstalldir = self.installdir
+
+ # Build and install an external library without DESTDIR.
+ # The external library generates a .pc file without an rpath.
+ yonder_dir = os.path.join(testdir, 'yonder')
+ yonder_prefix = os.path.join(oldinstalldir, 'yonder')
+ yonder_libdir = os.path.join(yonder_prefix, self.libdir)
+ self.prefix = yonder_prefix
+ self.installdir = yonder_prefix
+ self.init(yonder_dir)
+ self.build()
+ self.install(use_destdir=False)
+
+ # Since rpath has multiple valid formats we need to
+ # test that they are all properly used.
+ rpath_formats = [
+ ('-Wl,-rpath=', False),
+ ('-Wl,-rpath,', False),
+ ('-Wl,--just-symbols=', True),
+ ('-Wl,--just-symbols,', True),
+ ('-Wl,-R', False),
+ ('-Wl,-R,', False)
+ ]
+ for rpath_format, exception in rpath_formats:
+ # Build an app that uses that installed library.
+ # Supply the rpath to the installed library via LDFLAGS
+ # (as systems like buildroot and guix are wont to do)
+ # and verify install preserves that rpath.
+ self.new_builddir()
+ env = {'LDFLAGS': rpath_format + yonder_libdir,
+ 'PKG_CONFIG_PATH': os.path.join(yonder_libdir, 'pkgconfig')}
+ if exception:
+ with self.assertRaises(subprocess.CalledProcessError):
+ self.init(testdir, override_envvars=env)
+ break
+ self.init(testdir, override_envvars=env)
+ self.build()
+ self.install(use_destdir=False)
+ got_rpath = get_rpath(os.path.join(yonder_prefix, 'bin/rpathified'))
+ self.assertEqual(got_rpath, yonder_libdir, rpath_format)
+
@skip_if_not_base_option('b_sanitize')
def test_pch_with_address_sanitizer(self):
if is_cygwin():
@@ -6050,21 +6529,6 @@ class LinuxlikeTests(BasePlatformTests):
for i in compdb:
self.assertIn("-fsanitize=address", i["command"])
- def test_coverage(self):
- gcovr_exe, gcovr_new_rootdir = mesonbuild.environment.detect_gcovr()
- if not gcovr_exe:
- raise unittest.SkipTest('gcovr not found')
- if not shutil.which('genhtml') and not gcovr_new_rootdir:
- raise unittest.SkipTest('genhtml not found and gcovr is too old')
- if 'clang' in os.environ.get('CC', ''):
- # We need to use llvm-cov instead of gcovr with clang
- raise unittest.SkipTest('Coverage does not work with clang right now, help wanted!')
- testdir = os.path.join(self.common_test_dir, '1 trivial')
- self.init(testdir, extra_args=['-Db_coverage=true'])
- self.build()
- self.run_tests()
- self.run_target('coverage-html')
-
def test_cross_find_program(self):
testdir = os.path.join(self.unit_test_dir, '11 cross prog')
crossfile = tempfile.NamedTemporaryFile(mode='w')
@@ -6347,13 +6811,15 @@ class LinuxlikeTests(BasePlatformTests):
self.build(override_envvars=env)
# test uninstalled
self.run_tests(override_envvars=env)
- if not is_osx():
- # Rest of the workflow only works on macOS
+ if not (is_osx() or is_linux()):
return
# test running after installation
self.install(use_destdir=False)
prog = os.path.join(self.installdir, 'bin', 'prog')
self._run([prog])
+ if not is_osx():
+ # Rest of the workflow only works on macOS
+ return
out = self._run(['otool', '-L', prog])
self.assertNotIn('@rpath', out)
## New builddir for testing that DESTDIR is not added to install_name
@@ -6370,6 +6836,57 @@ class LinuxlikeTests(BasePlatformTests):
# Ensure that the otool output does not contain self.installdir
self.assertNotRegex(out, self.installdir + '.*dylib ')
+ @skipIfNoPkgconfig
+ def test_usage_pkgconfig_prefixes(self):
+ '''
+ Build and install two external libraries, to different prefixes,
+ then build and install a client program that finds them via pkgconfig,
+ and verify the installed client program runs.
+ '''
+ oldinstalldir = self.installdir
+
+ # Build and install both external libraries without DESTDIR
+ val1dir = os.path.join(self.unit_test_dir, '77 pkgconfig prefixes', 'val1')
+ val1prefix = os.path.join(oldinstalldir, 'val1')
+ self.prefix = val1prefix
+ self.installdir = val1prefix
+ self.init(val1dir)
+ self.build()
+ self.install(use_destdir=False)
+ self.new_builddir()
+
+ env1 = {}
+ env1['PKG_CONFIG_PATH'] = os.path.join(val1prefix, self.libdir, 'pkgconfig')
+ val2dir = os.path.join(self.unit_test_dir, '77 pkgconfig prefixes', 'val2')
+ val2prefix = os.path.join(oldinstalldir, 'val2')
+ self.prefix = val2prefix
+ self.installdir = val2prefix
+ self.init(val2dir, override_envvars=env1)
+ self.build()
+ self.install(use_destdir=False)
+ self.new_builddir()
+
+ # Build, install, and run the client program
+ env2 = {}
+ env2['PKG_CONFIG_PATH'] = os.path.join(val2prefix, self.libdir, 'pkgconfig')
+ testdir = os.path.join(self.unit_test_dir, '77 pkgconfig prefixes', 'client')
+ testprefix = os.path.join(oldinstalldir, 'client')
+ self.prefix = testprefix
+ self.installdir = testprefix
+ self.init(testdir, override_envvars=env2)
+ self.build()
+ self.install(use_destdir=False)
+ prog = os.path.join(self.installdir, 'bin', 'client')
+ env3 = {}
+ if is_cygwin():
+ env3['PATH'] = os.path.join(val1prefix, 'bin') + \
+ os.pathsep + \
+ os.path.join(val2prefix, 'bin') + \
+ os.pathsep + os.environ['PATH']
+ out = self._run([prog], override_envvars=env3).strip()
+ # Expected output is val1 + val2 = 3
+ self.assertEqual(out, '3')
+
def install_subdir_invalid_symlinks(self, testdir, subdir_path):
'''
Test that installation of broken symlinks works fine.
@@ -6409,6 +6926,11 @@ class LinuxlikeTests(BasePlatformTests):
testdir = os.path.join(self.unit_test_dir, '52 ldflagdedup')
if is_cygwin() or is_osx():
raise unittest.SkipTest('Not applicable on Cygwin or OSX.')
+ env = get_fake_env()
+ cc = env.detect_c_compiler(MachineChoice.HOST)
+ linker = cc.linker
+ if not linker.export_dynamic_args(env):
+ raise unittest.SkipTest('Not applicable for linkers without --export-dynamic')
self.init(testdir)
build_ninja = os.path.join(self.builddir, 'build.ninja')
max_count = 0
@@ -6560,7 +7082,7 @@ c = ['{0}']
return hashlib.sha256(f.read()).hexdigest()
def test_wrap_with_file_url(self):
- testdir = os.path.join(self.unit_test_dir, '73 wrap file url')
+ testdir = os.path.join(self.unit_test_dir, '74 wrap file url')
source_filename = os.path.join(testdir, 'subprojects', 'foo.tar.xz')
patch_filename = os.path.join(testdir, 'subprojects', 'foo-patch.tar.xz')
wrap_filename = os.path.join(testdir, 'subprojects', 'foo.wrap')
@@ -6590,12 +7112,25 @@ c = ['{0}']
windows_proof_rmtree(os.path.join(testdir, 'subprojects', 'foo'))
os.unlink(wrap_filename)
+ def test_no_rpath_for_static(self):
+ testdir = os.path.join(self.common_test_dir, '5 linkstatic')
+ self.init(testdir)
+ self.build()
+ build_rpath = get_rpath(os.path.join(self.builddir, 'prog'))
+ self.assertIsNone(build_rpath)
+
+
+class BaseLinuxCrossTests(BasePlatformTests):
+ # Don't pass --libdir when cross-compiling. We have tests that
+ # check whether meson auto-detects it correctly.
+ libdir = None
+
def should_run_cross_arm_tests():
return shutil.which('arm-linux-gnueabihf-gcc') and not platform.machine().lower().startswith('arm')
@unittest.skipUnless(not is_windows() and should_run_cross_arm_tests(), "requires ability to cross compile to ARM")
-class LinuxCrossArmTests(BasePlatformTests):
+class LinuxCrossArmTests(BaseLinuxCrossTests):
'''
Tests that cross-compilation to Linux/ARM works
'''
@@ -6642,6 +7177,17 @@ class LinuxCrossArmTests(BasePlatformTests):
return
self.assertTrue(False, 'Option libdir not in introspect data.')
+ def test_cross_libdir_subproject(self):
+ # Guard against a regression where calling "subproject"
+ # would reset the value of libdir to its default value.
+ testdir = os.path.join(self.unit_test_dir, '78 subdir libdir')
+ self.init(testdir, extra_args=['--libdir=fuf'])
+ for i in self.introspect('--buildoptions'):
+ if i['name'] == 'libdir':
+ self.assertEqual(i['value'], 'fuf')
+ return
+ self.assertTrue(False, 'Libdir specified on command line gets reset.')
+
def test_std_remains(self):
# C_std defined in project options must be in effect also when cross compiling.
testdir = os.path.join(self.unit_test_dir, '51 noncross options')
@@ -6665,7 +7211,7 @@ def should_run_cross_mingw_tests():
return shutil.which('x86_64-w64-mingw32-gcc') and not (is_windows() or is_cygwin())
@unittest.skipUnless(not is_windows() and should_run_cross_mingw_tests(), "requires ability to cross compile with MinGW")
-class LinuxCrossMingwTests(BasePlatformTests):
+class LinuxCrossMingwTests(BaseLinuxCrossTests):
'''
Tests that cross-compilation to Windows/MinGW works
'''
@@ -7126,7 +7672,12 @@ class NativeFileTests(BasePlatformTests):
for section, entries in values.items():
f.write('[{}]\n'.format(section))
for k, v in entries.items():
- f.write("{}='{}'\n".format(k, v))
+ if isinstance(v, (bool, int, float)):
+ f.write("{}={}\n".format(k, v))
+ elif isinstance(v, list):
+ f.write("{}=[{}]\n".format(k, ', '.join(["'{}'".format(w) for w in v])))
+ else:
+ f.write("{}='{}'\n".format(k, v))
return filename
def helper_create_binary_wrapper(self, binary, dir_=None, extra_args=None, **kwargs):
@@ -7450,6 +8001,219 @@ class NativeFileTests(BasePlatformTests):
self.init(testcase, extra_args=['--native-file', config])
self.build()
+ def test_user_options(self):
+ testcase = os.path.join(self.common_test_dir, '43 options')
+ for opt, value in [('testoption', 'some other val'), ('other_one', True),
+ ('combo_opt', 'one'), ('array_opt', ['two']),
+ ('integer_opt', 0)]:
+ config = self.helper_create_native_file({'project options': {opt: value}})
+ with self.assertRaises(subprocess.CalledProcessError) as cm:
+ self.init(testcase, extra_args=['--native-file', config])
+ self.assertRegex(cm.exception.stdout, r'Incorrect value to [a-z]+ option')
+
+ def test_user_options_command_line_overrides(self):
+ testcase = os.path.join(self.common_test_dir, '43 options')
+ config = self.helper_create_native_file({'project options': {'other_one': True}})
+ self.init(testcase, extra_args=['--native-file', config, '-Dother_one=false'])
+
+ def test_user_options_subproject(self):
+ testcase = os.path.join(self.unit_test_dir, '79 user options for subproject')
+
+ s = os.path.join(testcase, 'subprojects')
+ if not os.path.exists(s):
+ os.mkdir(s)
+ s = os.path.join(s, 'sub')
+ if not os.path.exists(s):
+ sub = os.path.join(self.common_test_dir, '43 options')
+ shutil.copytree(sub, s)
+
+ for opt, value in [('testoption', 'some other val'), ('other_one', True),
+ ('combo_opt', 'one'), ('array_opt', ['two']),
+ ('integer_opt', 0)]:
+ config = self.helper_create_native_file({'sub:project options': {opt: value}})
+ with self.assertRaises(subprocess.CalledProcessError) as cm:
+ self.init(testcase, extra_args=['--native-file', config])
+ self.assertRegex(cm.exception.stdout, r'Incorrect value to [a-z]+ option')
+
+ def test_option_bool(self):
+ # Bools are allowed to be unquoted
+ testcase = os.path.join(self.common_test_dir, '1 trivial')
+ config = self.helper_create_native_file({'built-in options': {'werror': True}})
+ self.init(testcase, extra_args=['--native-file', config])
+ configuration = self.introspect('--buildoptions')
+ for each in configuration:
+ # Test that no-per subproject options are inherited from the parent
+ if 'werror' in each['name']:
+ self.assertEqual(each['value'], True)
+ break
+ else:
+ self.fail('Did not find werror in build options?')
+
+ def test_option_integer(self):
+ # Bools are allowed to be unquoted
+ testcase = os.path.join(self.common_test_dir, '1 trivial')
+ config = self.helper_create_native_file({'built-in options': {'unity_size': 100}})
+ self.init(testcase, extra_args=['--native-file', config])
+ configuration = self.introspect('--buildoptions')
+ for each in configuration:
+ # Test that no-per subproject options are inherited from the parent
+ if 'unity_size' in each['name']:
+ self.assertEqual(each['value'], 100)
+ break
+ else:
+ self.fail('Did not find unity_size in build options?')
+
+ def test_builtin_options(self):
+ testcase = os.path.join(self.common_test_dir, '2 cpp')
+ config = self.helper_create_native_file({'built-in options': {'cpp_std': 'c++14'}})
+
+ self.init(testcase, extra_args=['--native-file', config])
+ configuration = self.introspect('--buildoptions')
+ for each in configuration:
+ if each['name'] == 'cpp_std':
+ self.assertEqual(each['value'], 'c++14')
+ break
+ else:
+ self.fail('Did not find werror in build options?')
+
+ def test_builtin_options_env_overrides_conf(self):
+ testcase = os.path.join(self.common_test_dir, '2 cpp')
+ config = self.helper_create_native_file({'built-in options': {'pkg_config_path': '/foo'}})
+
+ self.init(testcase, extra_args=['--native-file', config], override_envvars={'PKG_CONFIG_PATH': '/bar'})
+ configuration = self.introspect('--buildoptions')
+ for each in configuration:
+ if each['name'] == 'pkg_config_path':
+ self.assertEqual(each['value'], ['/bar'])
+ break
+ else:
+ self.fail('Did not find pkg_config_path in build options?')
+
+ def test_builtin_options_subprojects(self):
+ testcase = os.path.join(self.common_test_dir, '102 subproject subdir')
+ config = self.helper_create_native_file({'built-in options': {'default_library': 'both', 'c_args': ['-Dfoo']}, 'sub:built-in options': {'default_library': 'static'}})
+
+ self.init(testcase, extra_args=['--native-file', config])
+ configuration = self.introspect('--buildoptions')
+ found = 0
+ for each in configuration:
+ # Test that no-per subproject options are inherited from the parent
+ if 'c_args' in each['name']:
+ # This path will be hit twice, once for build and once for host,
+ self.assertEqual(each['value'], ['-Dfoo'])
+ found += 1
+ elif each['name'] == 'default_library':
+ self.assertEqual(each['value'], 'both')
+ found += 1
+ elif each['name'] == 'sub:default_library':
+ self.assertEqual(each['value'], 'static')
+ found += 1
+ self.assertEqual(found, 4, 'Did not find all three sections')
+
+ def test_builtin_options_subprojects_overrides_buildfiles(self):
+ # If the buildfile says subproject(... default_library: shared), ensure that's overwritten
+ testcase = os.path.join(self.common_test_dir, '230 persubproject options')
+ config = self.helper_create_native_file({'sub2:built-in options': {'default_library': 'shared'}})
+
+ with self.assertRaises(subprocess.CalledProcessError) as cm:
+ self.init(testcase, extra_args=['--native-file', config])
+ self.assertIn(cm.exception.stdout, 'Parent should override default_library')
+
+ def test_builtin_options_subprojects_inherits_parent_override(self):
+ # If the buildfile says subproject(... default_library: shared), ensure that's overwritten
+ testcase = os.path.join(self.common_test_dir, '230 persubproject options')
+ config = self.helper_create_native_file({'built-in options': {'default_library': 'both'}})
+
+ with self.assertRaises(subprocess.CalledProcessError) as cm:
+ self.init(testcase, extra_args=['--native-file', config])
+ self.assertIn(cm.exception.stdout, 'Parent should override default_library')
+
+ def test_builtin_options_compiler_properties(self):
+ # the properties section can have lang_args, and those need to be
+ # overwritten by the built-in options
+ testcase = os.path.join(self.common_test_dir, '1 trivial')
+ config = self.helper_create_native_file({
+ 'built-in options': {'c_args': ['-DFOO']},
+ 'properties': {'c_args': ['-DBAR']},
+ })
+
+ self.init(testcase, extra_args=['--native-file', config])
+ configuration = self.introspect('--buildoptions')
+ for each in configuration:
+ if each['name'] == 'c_args':
+ self.assertEqual(each['value'], ['-DFOO'])
+ break
+ else:
+ self.fail('Did not find c_args in build options?')
+
+ def test_builtin_options_compiler_properties_legacy(self):
+ # The legacy placement in properties is still valid if a 'built-in
+ # options' setting is present, but doesn't have the lang_args
+ testcase = os.path.join(self.common_test_dir, '1 trivial')
+ config = self.helper_create_native_file({
+ 'built-in options': {'default_library': 'static'},
+ 'properties': {'c_args': ['-DBAR']},
+ })
+
+ self.init(testcase, extra_args=['--native-file', config])
+ configuration = self.introspect('--buildoptions')
+ for each in configuration:
+ if each['name'] == 'c_args':
+ self.assertEqual(each['value'], ['-DBAR'])
+ break
+ else:
+ self.fail('Did not find c_args in build options?')
+
+ def test_builtin_options_paths(self):
+ # the properties section can have lang_args, and those need to be
+ # overwritten by the built-in options
+ testcase = os.path.join(self.common_test_dir, '1 trivial')
+ config = self.helper_create_native_file({
+ 'built-in options': {'bindir': 'foo'},
+ 'paths': {'bindir': 'bar'},
+ })
+
+ self.init(testcase, extra_args=['--native-file', config])
+ configuration = self.introspect('--buildoptions')
+ for each in configuration:
+ if each['name'] == 'bindir':
+ self.assertEqual(each['value'], 'foo')
+ break
+ else:
+ self.fail('Did not find bindir in build options?')
+
+ def test_builtin_options_paths_legacy(self):
+ testcase = os.path.join(self.common_test_dir, '1 trivial')
+ config = self.helper_create_native_file({
+ 'built-in options': {'default_library': 'static'},
+ 'paths': {'bindir': 'bar'},
+ })
+
+ self.init(testcase, extra_args=['--native-file', config])
+ configuration = self.introspect('--buildoptions')
+ for each in configuration:
+ if each['name'] == 'bindir':
+ self.assertEqual(each['value'], 'bar')
+ break
+ else:
+ self.fail('Did not find bindir in build options?')
+
+ def test_builtin_options_paths_legacy(self):
+ testcase = os.path.join(self.common_test_dir, '1 trivial')
+ config = self.helper_create_native_file({
+ 'built-in options': {'default_library': 'static'},
+ 'paths': {'bindir': 'bar'},
+ })
+
+ self.init(testcase, extra_args=['--native-file', config])
+ configuration = self.introspect('--buildoptions')
+ for each in configuration:
+ if each['name'] == 'bindir':
+ self.assertEqual(each['value'], 'bar')
+ break
+ else:
+ self.fail('Did not find bindir in build options?')
+
class CrossFileTests(BasePlatformTests):
@@ -7459,6 +8223,154 @@ class CrossFileTests(BasePlatformTests):
This is mainly aimed to testing overrides from cross files.
"""
+ def setUp(self):
+ super().setUp()
+ self.current_config = 0
+ self.current_wrapper = 0
+
+ def _cross_file_generator(self, *, needs_exe_wrapper: bool = False,
+ exe_wrapper: T.Optional[T.List[str]] = None) -> str:
+ if is_windows():
+ raise unittest.SkipTest('Cannot run this test on non-mingw/non-cygwin windows')
+ if is_sunos():
+ cc = 'gcc'
+ else:
+ cc = 'cc'
+
+ return textwrap.dedent("""\
+ [binaries]
+ c = '/usr/bin/{}'
+ ar = '/usr/bin/ar'
+ strip = '/usr/bin/ar'
+ {}
+
+ [properties]
+ needs_exe_wrapper = {}
+
+ [host_machine]
+ system = 'linux'
+ cpu_family = 'x86'
+ cpu = 'i686'
+ endian = 'little'
+ """.format(cc,
+ 'exe_wrapper = {}'.format(str(exe_wrapper)) if exe_wrapper is not None else '',
+ needs_exe_wrapper))
+
+ def _stub_exe_wrapper(self) -> str:
+ return textwrap.dedent('''\
+ #!/usr/bin/env python3
+ import subprocess
+ import sys
+
+ sys.exit(subprocess.run(sys.argv[1:]).returncode)
+ ''')
+
+ def test_needs_exe_wrapper_true(self):
+ testdir = os.path.join(self.unit_test_dir, '72 cross test passed')
+ with tempfile.TemporaryDirectory() as d:
+ p = Path(d) / 'crossfile'
+ with p.open('wt') as f:
+ f.write(self._cross_file_generator(needs_exe_wrapper=True))
+ self.init(testdir, extra_args=['--cross-file=' + str(p)])
+ out = self.run_target('test')
+ self.assertRegex(out, r'Skipped:\s*1\s*\n')
+
+ def test_needs_exe_wrapper_false(self):
+ testdir = os.path.join(self.unit_test_dir, '72 cross test passed')
+ with tempfile.TemporaryDirectory() as d:
+ p = Path(d) / 'crossfile'
+ with p.open('wt') as f:
+ f.write(self._cross_file_generator(needs_exe_wrapper=False))
+ self.init(testdir, extra_args=['--cross-file=' + str(p)])
+ out = self.run_target('test')
+ self.assertNotRegex(out, r'Skipped:\s*1\n')
+
+ def test_needs_exe_wrapper_true_wrapper(self):
+ testdir = os.path.join(self.unit_test_dir, '72 cross test passed')
+ with tempfile.TemporaryDirectory() as d:
+ s = Path(d) / 'wrapper.py'
+ with s.open('wt') as f:
+ f.write(self._stub_exe_wrapper())
+ s.chmod(0o774)
+ p = Path(d) / 'crossfile'
+ with p.open('wt') as f:
+ f.write(self._cross_file_generator(
+ needs_exe_wrapper=True,
+ exe_wrapper=[str(s)]))
+
+ self.init(testdir, extra_args=['--cross-file=' + str(p), '-Dexpect=true'])
+ out = self.run_target('test')
+ self.assertRegex(out, r'Ok:\s*3\s*\n')
+
+ def test_cross_exe_passed_no_wrapper(self):
+ testdir = os.path.join(self.unit_test_dir, '72 cross test passed')
+ with tempfile.TemporaryDirectory() as d:
+ p = Path(d) / 'crossfile'
+ with p.open('wt') as f:
+ f.write(self._cross_file_generator(needs_exe_wrapper=True))
+
+ self.init(testdir, extra_args=['--cross-file=' + str(p)])
+ self.build()
+ out = self.run_target('test')
+ self.assertRegex(out, r'Skipped:\s*1\s*\n')
+
+ # The test uses mocking and thus requires that the current process is the
+ # one to run the Meson steps. If we are using an external test executable
+ # (most commonly in Debian autopkgtests) then the mocking won't work.
+ @unittest.skipIf('MESON_EXE' in os.environ, 'MESON_EXE is defined, can not use mocking.')
+ def test_cross_file_system_paths(self):
+ if is_windows():
+ raise unittest.SkipTest('system crossfile paths not defined for Windows (yet)')
+
+ testdir = os.path.join(self.common_test_dir, '1 trivial')
+ cross_content = self._cross_file_generator()
+ with tempfile.TemporaryDirectory() as d:
+ dir_ = os.path.join(d, 'meson', 'cross')
+ os.makedirs(dir_)
+ with tempfile.NamedTemporaryFile('w', dir=dir_, delete=False) as f:
+ f.write(cross_content)
+ name = os.path.basename(f.name)
+
+ with mock.patch.dict(os.environ, {'XDG_DATA_HOME': d}):
+ self.init(testdir, extra_args=['--cross-file=' + name], inprocess=True)
+ self.wipe()
+
+ with mock.patch.dict(os.environ, {'XDG_DATA_DIRS': d}):
+ os.environ.pop('XDG_DATA_HOME', None)
+ self.init(testdir, extra_args=['--cross-file=' + name], inprocess=True)
+ self.wipe()
+
+ with tempfile.TemporaryDirectory() as d:
+ dir_ = os.path.join(d, '.local', 'share', 'meson', 'cross')
+ os.makedirs(dir_)
+ with tempfile.NamedTemporaryFile('w', dir=dir_, delete=False) as f:
+ f.write(cross_content)
+ name = os.path.basename(f.name)
+
+ # If XDG_DATA_HOME is set in the environment running the
+ # tests this test will fail, os mock the environment, pop
+ # it, then test
+ with mock.patch.dict(os.environ):
+ os.environ.pop('XDG_DATA_HOME', None)
+ with mock.patch('mesonbuild.coredata.os.path.expanduser', lambda x: x.replace('~', d)):
+ self.init(testdir, extra_args=['--cross-file=' + name], inprocess=True)
+ self.wipe()
+
+ def helper_create_cross_file(self, values):
+ """Create a config file as a temporary file.
+
+ values should be a nested dictionary structure of {section: {key:
+ value}}
+ """
+ filename = os.path.join(self.builddir, 'generated{}.config'.format(self.current_config))
+ self.current_config += 1
+ with open(filename, 'wt') as f:
+ for section, entries in values.items():
+ f.write('[{}]\n'.format(section))
+ for k, v in entries.items():
+ f.write("{}='{}'\n".format(k, v))
+ return filename
+
def test_cross_file_dirs(self):
testcase = os.path.join(self.unit_test_dir, '60 native file override')
self.init(testcase, default_args=False,
@@ -7515,6 +8427,89 @@ class CrossFileTests(BasePlatformTests):
'-Ddef_sharedstatedir=sharedstatebar',
'-Ddef_sysconfdir=sysconfbar'])
+ def test_user_options(self):
+ # This is just a touch test for cross file, since the implementation
+ # shares code after loading from the files
+ testcase = os.path.join(self.common_test_dir, '43 options')
+ config = self.helper_create_cross_file({'project options': {'testoption': 'some other value'}})
+ with self.assertRaises(subprocess.CalledProcessError) as cm:
+ self.init(testcase, extra_args=['--cross-file', config])
+ self.assertRegex(cm.exception.stdout, r'Incorrect value to [a-z]+ option')
+
+ def test_builtin_options(self):
+ testcase = os.path.join(self.common_test_dir, '2 cpp')
+ config = self.helper_create_cross_file({'built-in options': {'cpp_std': 'c++14'}})
+
+ self.init(testcase, extra_args=['--cross-file', config])
+ configuration = self.introspect('--buildoptions')
+ for each in configuration:
+ if each['name'] == 'cpp_std':
+ self.assertEqual(each['value'], 'c++14')
+ break
+ else:
+ self.fail('No c++ standard set?')
+
+ def test_builtin_options_per_machine(self):
+ """Test options that are allowed to be set on a per-machine basis.
+
+ Such options could be passed twice, once for the build machine, and
+ once for the host machine. I've picked pkg-config path, but any would
+ do that can be set for both.
+ """
+ testcase = os.path.join(self.common_test_dir, '2 cpp')
+ cross = self.helper_create_cross_file({'built-in options': {'pkg_config_path': '/cross/path', 'cpp_std': 'c++17'}})
+ native = self.helper_create_cross_file({'built-in options': {'pkg_config_path': '/native/path', 'cpp_std': 'c++14'}})
+
+ # Ensure that PKG_CONFIG_PATH is not set in the environment
+ with mock.patch.dict('os.environ'):
+ for k in ['PKG_CONFIG_PATH', 'PKG_CONFIG_PATH_FOR_BUILD']:
+ try:
+ del os.environ[k]
+ except KeyError:
+ pass
+ self.init(testcase, extra_args=['--cross-file', cross, '--native-file', native])
+
+ configuration = self.introspect('--buildoptions')
+ found = 0
+ for each in configuration:
+ if each['name'] == 'pkg_config_path':
+ self.assertEqual(each['value'], ['/cross/path'])
+ found += 1
+ elif each['name'] == 'cpp_std':
+ self.assertEqual(each['value'], 'c++17')
+ found += 1
+ elif each['name'] == 'build.pkg_config_path':
+ self.assertEqual(each['value'], ['/native/path'])
+ found += 1
+ elif each['name'] == 'build.cpp_std':
+ self.assertEqual(each['value'], 'c++14')
+ found += 1
+
+ if found == 4:
+ break
+ self.assertEqual(found, 4, 'Did not find all sections.')
+
+ def test_builtin_options_env_overrides_conf(self):
+ testcase = os.path.join(self.common_test_dir, '2 cpp')
+ config = self.helper_create_cross_file({'built-in options': {'pkg_config_path': '/foo'}})
+ cross = self.helper_create_cross_file({'built-in options': {'pkg_config_path': '/foo'}})
+
+ self.init(testcase, extra_args=['--native-file', config, '--cross-file', cross],
+ override_envvars={'PKG_CONFIG_PATH': '/bar', 'PKG_CONFIG_PATH_FOR_BUILD': '/dir'})
+ configuration = self.introspect('--buildoptions')
+ found = 0
+ for each in configuration:
+ if each['name'] == 'pkg_config_path':
+ self.assertEqual(each['value'], ['/bar'])
+ found += 1
+ elif each['name'] == 'build.pkg_config_path':
+ self.assertEqual(each['value'], ['/dir'])
+ found += 1
+ if found == 2:
+ break
+ self.assertEqual(found, 2, 'Did not find all sections.')
+
+
class TAPParserTests(unittest.TestCase):
def assert_test(self, events, **kwargs):
if 'explanation' not in kwargs:
@@ -7827,6 +8822,9 @@ def convert_args(argv):
test_list = []
for arg in argv:
if arg.startswith('-'):
+ if arg in ('-f', '--failfast'):
+ arg = '--exitfirst'
+ pytest_args.append(arg)
continue
# ClassName.test_name => 'ClassName and test_name'
if '.' in arg:
@@ -7858,4 +8856,5 @@ def main():
return unittest.main(defaultTest=cases, buffer=True)
if __name__ == '__main__':
+ print('Meson build system', mesonbuild.coredata.version, 'Unit Tests')
raise SystemExit(main())