blob: 2328107caac8de5ff9a569ac60cfe74bbcb3b993 [file] [log] [blame]
Dylan Bakere991c4d2023-12-13 11:38:41 -08001# SPDX-License-Identifier: Apache-2.0
Fini Jastrowc16fdae2022-03-08 18:41:08 +01002# Copyright 2016-2022 The Meson development team
Xavier Claessens7eb4c232021-07-24 18:31:45 -04003
Xavier Claessens7eb4c232021-07-24 18:31:45 -04004import stat
5import subprocess
6import re
7import tempfile
8import textwrap
9import os
10import shutil
11import hashlib
12from unittest import mock, skipUnless, SkipTest
13from glob import glob
14from pathlib import Path
15import typing as T
16
17import mesonbuild.mlog
18import mesonbuild.depfile
19import mesonbuild.dependencies.base
20import mesonbuild.dependencies.factory
21import mesonbuild.envconfig
22import mesonbuild.environment
23import mesonbuild.coredata
24import mesonbuild.modules.gnome
25from mesonbuild.mesonlib import (
26 MachineChoice, is_windows, is_osx, is_cygwin, is_openbsd, is_haiku,
27 is_sunos, windows_proof_rmtree, version_compare, is_linux,
28 OptionKey, EnvironmentException
29)
30from mesonbuild.compilers import (
31 detect_c_compiler, detect_cpp_compiler, compiler_from_language,
Xavier Claessens7eb4c232021-07-24 18:31:45 -040032)
Eli Schwartzd3dac3c2022-09-11 20:01:56 -040033from mesonbuild.compilers.c import AppleClangCCompiler
34from mesonbuild.compilers.cpp import AppleClangCPPCompiler
35from mesonbuild.compilers.objc import AppleClangObjCCompiler
36from mesonbuild.compilers.objcpp import AppleClangObjCPPCompiler
Xavier Claessensf1c35b52023-08-18 09:56:13 -040037from mesonbuild.dependencies.pkgconfig import PkgConfigDependency, PkgConfigCLI, PkgConfigInterface
Xavier Claessens7eb4c232021-07-24 18:31:45 -040038import mesonbuild.modules.pkgconfig
39
Dylan Bakerb43cf092022-03-09 13:05:01 -080040PKG_CONFIG = os.environ.get('PKG_CONFIG', 'pkg-config')
41
Xavier Claessens7eb4c232021-07-24 18:31:45 -040042
43from run_tests import (
44 get_fake_env
45)
46
47from .baseplatformtests import BasePlatformTests
48from .helpers import *
49
Dylan Bakerf0b27962022-03-09 13:13:30 -080050def _prepend_pkg_config_path(path: str) -> str:
51 """Prepend a string value to pkg_config_path
52
53 :param path: The path to prepend
54 :return: The path, followed by any PKG_CONFIG_PATH already in the environment
55 """
56 pkgconf = os.environ.get('PKG_CONFIG_PATH')
57 if pkgconf:
58 return f'{path}{os.path.pathsep}{pkgconf}'
59 return path
60
61
Xavier Claessens7eb4c232021-07-24 18:31:45 -040062def _clang_at_least(compiler: 'Compiler', minver: str, apple_minver: T.Optional[str]) -> bool:
63 """
64 check that Clang compiler is at least a specified version, whether AppleClang or regular Clang
65
66 Parameters
67 ----------
68 compiler:
69 Meson compiler object
70 minver: str
71 Clang minimum version
72 apple_minver: str
73 AppleCLang minimum version
74
75 Returns
76 -------
77 at_least: bool
78 Clang is at least the specified version
79 """
80 if isinstance(compiler, (AppleClangCCompiler, AppleClangCPPCompiler)):
81 if apple_minver is None:
82 return False
83 return version_compare(compiler.version, apple_minver)
84 return version_compare(compiler.version, minver)
85
86@skipUnless(not is_windows(), "requires something Unix-like")
87class LinuxlikeTests(BasePlatformTests):
88 '''
89 Tests that should run on Linux, macOS, and *BSD
90 '''
91
92 def test_basic_soname(self):
93 '''
94 Test that the soname is set correctly for shared libraries. This can't
95 be an ordinary test case because we need to run `readelf` and actually
96 check the soname.
97 https://github.com/mesonbuild/meson/issues/785
98 '''
99 testdir = os.path.join(self.common_test_dir, '4 shared')
100 self.init(testdir)
101 self.build()
102 lib1 = os.path.join(self.builddir, 'libmylib.so')
103 soname = get_soname(lib1)
104 self.assertEqual(soname, 'libmylib.so')
105
106 def test_custom_soname(self):
107 '''
108 Test that the soname is set correctly for shared libraries when
109 a custom prefix and/or suffix is used. This can't be an ordinary test
110 case because we need to run `readelf` and actually check the soname.
111 https://github.com/mesonbuild/meson/issues/785
112 '''
113 testdir = os.path.join(self.common_test_dir, '24 library versions')
114 self.init(testdir)
115 self.build()
116 lib1 = os.path.join(self.builddir, 'prefixsomelib.suffix')
117 soname = get_soname(lib1)
118 self.assertEqual(soname, 'prefixsomelib.suffix')
119
120 def test_pic(self):
121 '''
122 Test that -fPIC is correctly added to static libraries when b_staticpic
123 is true and not when it is false. This can't be an ordinary test case
124 because we need to inspect the compiler database.
125 '''
126 if is_windows() or is_cygwin() or is_osx():
127 raise SkipTest('PIC not relevant')
128
129 testdir = os.path.join(self.common_test_dir, '3 static')
130 self.init(testdir)
131 compdb = self.get_compdb()
132 self.assertIn('-fPIC', compdb[0]['command'])
133 self.setconf('-Db_staticpic=false')
134 # Regenerate build
135 self.build()
136 compdb = self.get_compdb()
137 self.assertNotIn('-fPIC', compdb[0]['command'])
138
139 @mock.patch.dict(os.environ)
140 def test_pkgconfig_gen(self):
141 '''
142 Test that generated pkg-config files can be found and have the correct
143 version and link args. This can't be an ordinary test case because we
144 need to run pkg-config outside of a Meson build file.
145 https://github.com/mesonbuild/meson/issues/889
146 '''
147 testdir = os.path.join(self.common_test_dir, '44 pkgconfig-gen')
148 self.init(testdir)
149 env = get_fake_env(testdir, self.builddir, self.prefix)
150 kwargs = {'required': True, 'silent': True}
151 os.environ['PKG_CONFIG_LIBDIR'] = self.privatedir
152 foo_dep = PkgConfigDependency('libfoo', env, kwargs)
153 self.assertTrue(foo_dep.found())
154 self.assertEqual(foo_dep.get_version(), '1.0')
155 self.assertIn('-lfoo', foo_dep.get_link_args())
Xavier Claessens30d7f502023-08-15 16:56:23 -0400156 self.assertEqual(foo_dep.get_variable(pkgconfig='foo'), 'bar')
157 self.assertPathEqual(foo_dep.get_variable(pkgconfig='datadir'), '/usr/data')
Xavier Claessens7eb4c232021-07-24 18:31:45 -0400158
159 libhello_nolib = PkgConfigDependency('libhello_nolib', env, kwargs)
160 self.assertTrue(libhello_nolib.found())
161 self.assertEqual(libhello_nolib.get_link_args(), [])
162 self.assertEqual(libhello_nolib.get_compile_args(), [])
Xavier Claessens30d7f502023-08-15 16:56:23 -0400163 self.assertEqual(libhello_nolib.get_variable(pkgconfig='foo'), 'bar')
164 self.assertEqual(libhello_nolib.get_variable(pkgconfig='prefix'), self.prefix)
Xavier Claessens183e4b82023-06-06 12:10:35 -0400165 impl = libhello_nolib.pkgconfig
Xavier Claessens0eae4e02023-08-16 08:50:34 -0400166 if not isinstance(impl, PkgConfigCLI) or version_compare(impl.pkgbin_version, ">=0.29.1"):
Xavier Claessens30d7f502023-08-15 16:56:23 -0400167 self.assertEqual(libhello_nolib.get_variable(pkgconfig='escaped_var'), r'hello\ world')
168 self.assertEqual(libhello_nolib.get_variable(pkgconfig='unescaped_var'), 'hello world')
Xavier Claessens7eb4c232021-07-24 18:31:45 -0400169
170 cc = detect_c_compiler(env, MachineChoice.HOST)
171 if cc.get_id() in {'gcc', 'clang'}:
172 for name in {'ct', 'ct0'}:
173 ct_dep = PkgConfigDependency(name, env, kwargs)
174 self.assertTrue(ct_dep.found())
Eli Schwartzb40e4de2022-11-17 02:08:11 -0500175 self.assertIn('-lct', ct_dep.get_link_args(raw=True))
Xavier Claessens7eb4c232021-07-24 18:31:45 -0400176
177 def test_pkgconfig_gen_deps(self):
178 '''
179 Test that generated pkg-config files correctly handle dependencies
180 '''
181 testdir = os.path.join(self.common_test_dir, '44 pkgconfig-gen')
182 self.init(testdir)
183 privatedir1 = self.privatedir
184
185 self.new_builddir()
186 testdir = os.path.join(self.common_test_dir, '44 pkgconfig-gen', 'dependencies')
187 self.init(testdir, override_envvars={'PKG_CONFIG_LIBDIR': privatedir1})
188 privatedir2 = self.privatedir
189
190 env = {
191 'PKG_CONFIG_LIBDIR': os.pathsep.join([privatedir1, privatedir2]),
192 'PKG_CONFIG_SYSTEM_LIBRARY_PATH': '/usr/lib',
193 }
Dylan Bakerb43cf092022-03-09 13:05:01 -0800194 self._run([PKG_CONFIG, 'dependency-test', '--validate'], override_envvars=env)
Xavier Claessens7eb4c232021-07-24 18:31:45 -0400195
196 # pkg-config strips some duplicated flags so we have to parse the
197 # generated file ourself.
198 expected = {
199 'Requires': 'libexposed',
200 'Requires.private': 'libfoo >= 1.0',
201 'Libs': '-L${libdir} -llibmain -pthread -lcustom',
202 'Libs.private': '-lcustom2 -L${libdir} -llibinternal',
203 'Cflags': '-I${includedir} -pthread -DCUSTOM',
204 }
205 if is_osx() or is_haiku():
206 expected['Cflags'] = expected['Cflags'].replace('-pthread ', '')
207 with open(os.path.join(privatedir2, 'dependency-test.pc'), encoding='utf-8') as f:
208 matched_lines = 0
209 for line in f:
210 parts = line.split(':', 1)
211 if parts[0] in expected:
212 key = parts[0]
213 val = parts[1].strip()
214 expected_val = expected[key]
215 self.assertEqual(expected_val, val)
216 matched_lines += 1
217 self.assertEqual(len(expected), matched_lines)
218
Dylan Bakerb43cf092022-03-09 13:05:01 -0800219 cmd = [PKG_CONFIG, 'requires-test']
Xavier Claessens7eb4c232021-07-24 18:31:45 -0400220 out = self._run(cmd + ['--print-requires'], override_envvars=env).strip().split('\n')
221 if not is_openbsd():
222 self.assertEqual(sorted(out), sorted(['libexposed', 'libfoo >= 1.0', 'libhello']))
223 else:
224 self.assertEqual(sorted(out), sorted(['libexposed', 'libfoo>=1.0', 'libhello']))
225
Dylan Bakerb43cf092022-03-09 13:05:01 -0800226 cmd = [PKG_CONFIG, 'requires-private-test']
Xavier Claessens7eb4c232021-07-24 18:31:45 -0400227 out = self._run(cmd + ['--print-requires-private'], override_envvars=env).strip().split('\n')
228 if not is_openbsd():
229 self.assertEqual(sorted(out), sorted(['libexposed', 'libfoo >= 1.0', 'libhello']))
230 else:
231 self.assertEqual(sorted(out), sorted(['libexposed', 'libfoo>=1.0', 'libhello']))
232
Dylan Bakerb43cf092022-03-09 13:05:01 -0800233 cmd = [PKG_CONFIG, 'pub-lib-order']
Xavier Claessens7eb4c232021-07-24 18:31:45 -0400234 out = self._run(cmd + ['--libs'], override_envvars=env).strip().split()
235 self.assertEqual(out, ['-llibmain2', '-llibinternal'])
236
237 # See common/44 pkgconfig-gen/meson.build for description of the case this test
238 with open(os.path.join(privatedir1, 'simple2.pc'), encoding='utf-8') as f:
239 content = f.read()
240 self.assertIn('Libs: -L${libdir} -lsimple2 -lsimple1', content)
241 self.assertIn('Libs.private: -lz', content)
242
243 with open(os.path.join(privatedir1, 'simple3.pc'), encoding='utf-8') as f:
244 content = f.read()
245 self.assertEqual(1, content.count('-lsimple3'))
246
247 with open(os.path.join(privatedir1, 'simple5.pc'), encoding='utf-8') as f:
248 content = f.read()
249 self.assertNotIn('-lstat2', content)
250
251 @mock.patch.dict(os.environ)
252 def test_pkgconfig_uninstalled(self):
253 testdir = os.path.join(self.common_test_dir, '44 pkgconfig-gen')
254 self.init(testdir)
255 self.build()
256
257 os.environ['PKG_CONFIG_LIBDIR'] = os.path.join(self.builddir, 'meson-uninstalled')
258 if is_cygwin():
259 os.environ['PATH'] += os.pathsep + self.builddir
260
261 self.new_builddir()
262 testdir = os.path.join(self.common_test_dir, '44 pkgconfig-gen', 'dependencies')
263 self.init(testdir)
264 self.build()
265 self.run_tests()
266
267 def test_pkg_unfound(self):
268 testdir = os.path.join(self.unit_test_dir, '23 unfound pkgconfig')
269 self.init(testdir)
270 with open(os.path.join(self.privatedir, 'somename.pc'), encoding='utf-8') as f:
271 pcfile = f.read()
Eli Schwartz739de7b2021-10-19 19:51:44 -0400272 self.assertNotIn('blub_blob_blib', pcfile)
Xavier Claessens7eb4c232021-07-24 18:31:45 -0400273
Dylan Bakere0ab7292021-08-19 09:17:57 -0700274 def test_symlink_builddir(self) -> None:
Xavier Claessens7eb4c232021-07-24 18:31:45 -0400275 '''
276 Test using a symlink as either the builddir for "setup" or
277 the argument for "-C".
278 '''
279 testdir = os.path.join(self.common_test_dir, '1 trivial')
Dylan Bakere0ab7292021-08-19 09:17:57 -0700280
281 symdir = f'{self.builddir}-symlink'
282 os.symlink(self.builddir, symdir)
283 self.addCleanup(os.unlink, symdir)
284 self.change_builddir(symdir)
285
Xavier Claessens7eb4c232021-07-24 18:31:45 -0400286 self.init(testdir)
287 self.build()
288 self._run(self.mtest_command)
289
Xavier Claessens7eb4c232021-07-24 18:31:45 -0400290 @skipIfNoPkgconfig
291 def test_qtdependency_pkgconfig_detection(self):
292 '''
293 Test that qt4 and qt5 detection with pkgconfig works.
294 '''
295 # Verify Qt4 or Qt5 can be found with pkg-config
Dylan Bakerb43cf092022-03-09 13:05:01 -0800296 qt4 = subprocess.call([PKG_CONFIG, '--exists', 'QtCore'])
297 qt5 = subprocess.call([PKG_CONFIG, '--exists', 'Qt5Core'])
Xavier Claessens7eb4c232021-07-24 18:31:45 -0400298 testdir = os.path.join(self.framework_test_dir, '4 qt')
299 self.init(testdir, extra_args=['-Dmethod=pkg-config'])
300 # Confirm that the dependency was found with pkg-config
Marvin Scholz7cbc15b2022-03-30 18:45:02 +0200301 mesonlog = self.get_meson_log_raw()
Xavier Claessens7eb4c232021-07-24 18:31:45 -0400302 if qt4 == 0:
Marvin Scholz7cbc15b2022-03-30 18:45:02 +0200303 self.assertRegex(mesonlog,
Xavier Claessens7eb4c232021-07-24 18:31:45 -0400304 r'Run-time dependency qt4 \(modules: Core\) found: YES 4.* \(pkg-config\)')
305 if qt5 == 0:
Marvin Scholz7cbc15b2022-03-30 18:45:02 +0200306 self.assertRegex(mesonlog,
Xavier Claessens7eb4c232021-07-24 18:31:45 -0400307 r'Run-time dependency qt5 \(modules: Core\) found: YES 5.* \(pkg-config\)')
308
309 @skip_if_not_base_option('b_sanitize')
310 def test_generate_gir_with_address_sanitizer(self):
311 if is_cygwin():
312 raise SkipTest('asan not available on Cygwin')
313 if is_openbsd():
314 raise SkipTest('-fsanitize=address is not supported on OpenBSD')
315
316 testdir = os.path.join(self.framework_test_dir, '7 gnome')
317 self.init(testdir, extra_args=['-Db_sanitize=address', '-Db_lundef=false'])
318 self.build()
319
320 def test_qt5dependency_qmake_detection(self):
321 '''
322 Test that qt5 detection with qmake works. This can't be an ordinary
323 test case because it involves setting the environment.
324 '''
325 # Verify that qmake is for Qt5
326 if not shutil.which('qmake-qt5'):
327 if not shutil.which('qmake'):
328 raise SkipTest('QMake not found')
329 output = subprocess.getoutput('qmake --version')
330 if 'Qt version 5' not in output:
331 raise SkipTest('Qmake found, but it is not for Qt 5.')
332 # Disable pkg-config codepath and force searching with qmake/qmake-qt5
333 testdir = os.path.join(self.framework_test_dir, '4 qt')
334 self.init(testdir, extra_args=['-Dmethod=qmake'])
335 # Confirm that the dependency was found with qmake
Marvin Scholz7cbc15b2022-03-30 18:45:02 +0200336 mesonlog = self.get_meson_log_raw()
337 self.assertRegex(mesonlog,
Xavier Claessens7eb4c232021-07-24 18:31:45 -0400338 r'Run-time dependency qt5 \(modules: Core\) found: YES .* \(qmake\)\n')
339
340 def test_qt6dependency_qmake_detection(self):
341 '''
342 Test that qt6 detection with qmake works. This can't be an ordinary
343 test case because it involves setting the environment.
344 '''
Eli Schwartzb5a81ff2022-02-09 21:13:26 -0500345 # Verify that qmake is for Qt6
346 if not shutil.which('qmake6'):
Xavier Claessens7eb4c232021-07-24 18:31:45 -0400347 if not shutil.which('qmake'):
348 raise SkipTest('QMake not found')
349 output = subprocess.getoutput('qmake --version')
350 if 'Qt version 6' not in output:
351 raise SkipTest('Qmake found, but it is not for Qt 6.')
352 # Disable pkg-config codepath and force searching with qmake/qmake-qt6
353 testdir = os.path.join(self.framework_test_dir, '4 qt')
354 self.init(testdir, extra_args=['-Dmethod=qmake'])
355 # Confirm that the dependency was found with qmake
Marvin Scholz7cbc15b2022-03-30 18:45:02 +0200356 mesonlog = self.get_meson_log_raw()
357 self.assertRegex(mesonlog,
Xavier Claessens7eb4c232021-07-24 18:31:45 -0400358 r'Run-time dependency qt6 \(modules: Core\) found: YES .* \(qmake\)\n')
359
360 def glob_sofiles_without_privdir(self, g):
361 files = glob(g)
362 return [f for f in files if not f.endswith('.p')]
363
364 def _test_soname_impl(self, libpath, install):
365 if is_cygwin() or is_osx():
366 raise SkipTest('Test only applicable to ELF and linuxlike sonames')
367
368 testdir = os.path.join(self.unit_test_dir, '1 soname')
369 self.init(testdir)
370 self.build()
371 if install:
372 self.install()
373
374 # File without aliases set.
375 nover = os.path.join(libpath, 'libnover.so')
376 self.assertPathExists(nover)
377 self.assertFalse(os.path.islink(nover))
378 self.assertEqual(get_soname(nover), 'libnover.so')
379 self.assertEqual(len(self.glob_sofiles_without_privdir(nover[:-3] + '*')), 1)
380
381 # File with version set
382 verset = os.path.join(libpath, 'libverset.so')
383 self.assertPathExists(verset + '.4.5.6')
384 self.assertEqual(os.readlink(verset), 'libverset.so.4')
385 self.assertEqual(get_soname(verset), 'libverset.so.4')
386 self.assertEqual(len(self.glob_sofiles_without_privdir(verset[:-3] + '*')), 3)
387
388 # File with soversion set
389 soverset = os.path.join(libpath, 'libsoverset.so')
390 self.assertPathExists(soverset + '.1.2.3')
391 self.assertEqual(os.readlink(soverset), 'libsoverset.so.1.2.3')
392 self.assertEqual(get_soname(soverset), 'libsoverset.so.1.2.3')
393 self.assertEqual(len(self.glob_sofiles_without_privdir(soverset[:-3] + '*')), 2)
394
395 # File with version and soversion set to same values
396 settosame = os.path.join(libpath, 'libsettosame.so')
397 self.assertPathExists(settosame + '.7.8.9')
398 self.assertEqual(os.readlink(settosame), 'libsettosame.so.7.8.9')
399 self.assertEqual(get_soname(settosame), 'libsettosame.so.7.8.9')
400 self.assertEqual(len(self.glob_sofiles_without_privdir(settosame[:-3] + '*')), 2)
401
402 # File with version and soversion set to different values
403 bothset = os.path.join(libpath, 'libbothset.so')
404 self.assertPathExists(bothset + '.1.2.3')
405 self.assertEqual(os.readlink(bothset), 'libbothset.so.1.2.3')
406 self.assertEqual(os.readlink(bothset + '.1.2.3'), 'libbothset.so.4.5.6')
407 self.assertEqual(get_soname(bothset), 'libbothset.so.1.2.3')
408 self.assertEqual(len(self.glob_sofiles_without_privdir(bothset[:-3] + '*')), 3)
409
Nirbheek Chauhanaf5993f2021-11-24 17:29:06 +0530410 # A shared_module that is not linked to anything
411 module = os.path.join(libpath, 'libsome_module.so')
412 self.assertPathExists(module)
413 self.assertFalse(os.path.islink(module))
414 self.assertEqual(get_soname(module), None)
415
416 # A shared_module that is not linked to an executable with link_with:
417 module = os.path.join(libpath, 'liblinked_module1.so')
418 self.assertPathExists(module)
419 self.assertFalse(os.path.islink(module))
420 self.assertEqual(get_soname(module), 'liblinked_module1.so')
421
422 # A shared_module that is not linked to an executable with dependencies:
423 module = os.path.join(libpath, 'liblinked_module2.so')
424 self.assertPathExists(module)
425 self.assertFalse(os.path.islink(module))
426 self.assertEqual(get_soname(module), 'liblinked_module2.so')
427
Xavier Claessens7eb4c232021-07-24 18:31:45 -0400428 def test_soname(self):
429 self._test_soname_impl(self.builddir, False)
430
431 def test_installed_soname(self):
432 libdir = self.installdir + os.path.join(self.prefix, self.libdir)
433 self._test_soname_impl(libdir, True)
434
435 def test_compiler_check_flags_order(self):
436 '''
437 Test that compiler check flags override all other flags. This can't be
438 an ordinary test case because it needs the environment to be set.
439 '''
440 testdir = os.path.join(self.common_test_dir, '36 has function')
441 env = get_fake_env(testdir, self.builddir, self.prefix)
442 cpp = detect_cpp_compiler(env, MachineChoice.HOST)
443 Oflag = '-O3'
444 OflagCPP = Oflag
445 if cpp.get_id() in ('clang', 'gcc'):
446 # prevent developers from adding "int main(int argc, char **argv)"
447 # to small Meson checks unless these parameters are actually used
448 OflagCPP += ' -Werror=unused-parameter'
449 env = {'CFLAGS': Oflag,
450 'CXXFLAGS': OflagCPP}
451 self.init(testdir, override_envvars=env)
452 cmds = self.get_meson_log_compiler_checks()
453 for cmd in cmds:
454 if cmd[0] == 'ccache':
455 cmd = cmd[1:]
456 # Verify that -I flags from the `args` kwarg are first
457 # This is set in the '36 has function' test case
458 self.assertEqual(cmd[1], '-I/tmp')
459 # Verify that -O3 set via the environment is overridden by -O0
460 Oargs = [arg for arg in cmd if arg.startswith('-O')]
461 self.assertEqual(Oargs, [Oflag, '-O0'])
462
463 def _test_stds_impl(self, testdir: str, compiler: 'Compiler') -> None:
464 has_cpp17 = (compiler.get_id() not in {'clang', 'gcc'} or
465 compiler.get_id() == 'clang' and _clang_at_least(compiler, '>=5.0.0', '>=9.1') or
466 compiler.get_id() == 'gcc' and version_compare(compiler.version, '>=5.0.0'))
467 has_cpp2a_c17 = (compiler.get_id() not in {'clang', 'gcc'} or
468 compiler.get_id() == 'clang' and _clang_at_least(compiler, '>=6.0.0', '>=10.0') or
469 compiler.get_id() == 'gcc' and version_compare(compiler.version, '>=8.0.0'))
470 has_cpp20 = (compiler.get_id() not in {'clang', 'gcc'} or
471 compiler.get_id() == 'clang' and _clang_at_least(compiler, '>=10.0.0', None) or
472 compiler.get_id() == 'gcc' and version_compare(compiler.version, '>=10.0.0'))
Steven Noonan51d04772023-06-20 15:45:18 -0700473 has_cpp2b = (compiler.get_id() not in {'clang', 'gcc'} or
474 compiler.get_id() == 'clang' and _clang_at_least(compiler, '>=12.0.0', None) or
Benjamin Redelings84b8d252023-10-07 10:37:49 -0400475 compiler.get_id() == 'gcc' and version_compare(compiler.version, '>=11.0.0'))
Steven Noonan51d04772023-06-20 15:45:18 -0700476 has_cpp23 = (compiler.get_id() not in {'clang', 'gcc'} or
477 compiler.get_id() == 'clang' and _clang_at_least(compiler, '>=17.0.0', None) or
Benjamin Redelings84b8d252023-10-07 10:37:49 -0400478 compiler.get_id() == 'gcc' and version_compare(compiler.version, '>=11.0.0'))
Steven Noonan51d04772023-06-20 15:45:18 -0700479 has_cpp26 = (compiler.get_id() not in {'clang', 'gcc'} or
480 compiler.get_id() == 'clang' and _clang_at_least(compiler, '>=17.0.0', None) or
481 compiler.get_id() == 'gcc' and version_compare(compiler.version, '>=14.0.0'))
Xavier Claessens7eb4c232021-07-24 18:31:45 -0400482 has_c18 = (compiler.get_id() not in {'clang', 'gcc'} or
483 compiler.get_id() == 'clang' and _clang_at_least(compiler, '>=8.0.0', '>=11.0') or
484 compiler.get_id() == 'gcc' and version_compare(compiler.version, '>=8.0.0'))
485 # Check that all the listed -std=xxx options for this compiler work just fine when used
486 # https://en.wikipedia.org/wiki/Xcode#Latest_versions
487 # https://www.gnu.org/software/gcc/projects/cxx-status.html
488 key = OptionKey('std', lang=compiler.language)
489 for v in compiler.get_options()[key].choices:
490 # we do it like this to handle gnu++17,c++17 and gnu17,c17 cleanly
491 # thus, C++ first
492 if '++17' in v and not has_cpp17:
493 continue
494 elif '++2a' in v and not has_cpp2a_c17: # https://en.cppreference.com/w/cpp/compiler_support
495 continue
496 elif '++20' in v and not has_cpp20:
497 continue
Steven Noonan51d04772023-06-20 15:45:18 -0700498 elif '++2b' in v and not has_cpp2b:
499 continue
500 elif '++23' in v and not has_cpp23:
501 continue
502 elif ('++26' in v or '++2c' in v) and not has_cpp26:
503 continue
Xavier Claessens7eb4c232021-07-24 18:31:45 -0400504 # now C
505 elif '17' in v and not has_cpp2a_c17:
506 continue
507 elif '18' in v and not has_c18:
508 continue
509 self.init(testdir, extra_args=[f'-D{key!s}={v}'])
510 cmd = self.get_compdb()[0]['command']
511 # c++03 and gnu++03 are not understood by ICC, don't try to look for them
512 skiplist = frozenset([
513 ('intel', 'c++03'),
514 ('intel', 'gnu++03')])
515 if v != 'none' and not (compiler.get_id(), v) in skiplist:
516 cmd_std = f" -std={v} "
517 self.assertIn(cmd_std, cmd)
518 try:
519 self.build()
520 except Exception:
521 print(f'{key!s} was {v!r}')
522 raise
523 self.wipe()
524 # Check that an invalid std option in CFLAGS/CPPFLAGS fails
525 # Needed because by default ICC ignores invalid options
526 cmd_std = '-std=FAIL'
527 if compiler.language == 'c':
528 env_flag_name = 'CFLAGS'
529 elif compiler.language == 'cpp':
530 env_flag_name = 'CXXFLAGS'
531 else:
532 raise NotImplementedError(f'Language {compiler.language} not defined.')
533 env = {}
534 env[env_flag_name] = cmd_std
535 with self.assertRaises((subprocess.CalledProcessError, EnvironmentException),
536 msg='C compiler should have failed with -std=FAIL'):
537 self.init(testdir, override_envvars = env)
538 # ICC won't fail in the above because additional flags are needed to
539 # make unknown -std=... options errors.
540 self.build()
541
542 def test_compiler_c_stds(self):
543 '''
544 Test that C stds specified for this compiler can all be used. Can't be
545 an ordinary test because it requires passing options to meson.
546 '''
547 testdir = os.path.join(self.common_test_dir, '1 trivial')
548 env = get_fake_env(testdir, self.builddir, self.prefix)
549 cc = detect_c_compiler(env, MachineChoice.HOST)
550 self._test_stds_impl(testdir, cc)
551
552 def test_compiler_cpp_stds(self):
553 '''
554 Test that C++ stds specified for this compiler can all be used. Can't
555 be an ordinary test because it requires passing options to meson.
556 '''
557 testdir = os.path.join(self.common_test_dir, '2 cpp')
558 env = get_fake_env(testdir, self.builddir, self.prefix)
559 cpp = detect_cpp_compiler(env, MachineChoice.HOST)
560 self._test_stds_impl(testdir, cpp)
561
562 def test_unity_subproj(self):
563 testdir = os.path.join(self.common_test_dir, '42 subproject')
564 self.init(testdir, extra_args='--unity=subprojects')
565 pdirs = glob(os.path.join(self.builddir, 'subprojects/sublib/simpletest*.p'))
566 self.assertEqual(len(pdirs), 1)
567 self.assertPathExists(os.path.join(pdirs[0], 'simpletest-unity0.c'))
568 sdirs = glob(os.path.join(self.builddir, 'subprojects/sublib/*sublib*.p'))
569 self.assertEqual(len(sdirs), 1)
570 self.assertPathExists(os.path.join(sdirs[0], 'sublib-unity0.c'))
571 self.assertPathDoesNotExist(os.path.join(self.builddir, 'user@exe/user-unity.c'))
572 self.build()
573
574 def test_installed_modes(self):
575 '''
576 Test that files installed by these tests have the correct permissions.
577 Can't be an ordinary test because our installed_files.txt is very basic.
578 '''
Nirbheek Chauhan04ae1cf2021-10-30 18:54:04 +0530579 if is_cygwin():
580 self.new_builddir_in_tempdir()
Xavier Claessens7eb4c232021-07-24 18:31:45 -0400581 # Test file modes
582 testdir = os.path.join(self.common_test_dir, '12 data')
583 self.init(testdir)
584 self.install()
585
586 f = os.path.join(self.installdir, 'etc', 'etcfile.dat')
587 found_mode = stat.filemode(os.stat(f).st_mode)
Eli Schwartzf8ebfdf2021-11-18 17:52:12 -0500588 want_mode = 'rw-------'
Xavier Claessens7eb4c232021-07-24 18:31:45 -0400589 self.assertEqual(want_mode, found_mode[1:])
590
591 f = os.path.join(self.installdir, 'usr', 'bin', 'runscript.sh')
592 statf = os.stat(f)
593 found_mode = stat.filemode(statf.st_mode)
594 want_mode = 'rwxr-sr-x'
595 self.assertEqual(want_mode, found_mode[1:])
596 if os.getuid() == 0:
597 # The chown failed nonfatally if we're not root
598 self.assertEqual(0, statf.st_uid)
599 self.assertEqual(0, statf.st_gid)
600
601 f = os.path.join(self.installdir, 'usr', 'share', 'progname',
602 'fileobject_datafile.dat')
603 orig = os.path.join(testdir, 'fileobject_datafile.dat')
604 statf = os.stat(f)
605 statorig = os.stat(orig)
606 found_mode = stat.filemode(statf.st_mode)
607 orig_mode = stat.filemode(statorig.st_mode)
608 self.assertEqual(orig_mode[1:], found_mode[1:])
609 self.assertEqual(os.getuid(), statf.st_uid)
610 if os.getuid() == 0:
611 # The chown failed nonfatally if we're not root
612 self.assertEqual(0, statf.st_gid)
613
614 self.wipe()
615 # Test directory modes
616 testdir = os.path.join(self.common_test_dir, '59 install subdir')
617 self.init(testdir)
618 self.install()
619
620 f = os.path.join(self.installdir, 'usr', 'share', 'sub1', 'second.dat')
621 statf = os.stat(f)
622 found_mode = stat.filemode(statf.st_mode)
Eli Schwartzf8ebfdf2021-11-18 17:52:12 -0500623 want_mode = 'rwxr-x--x'
Xavier Claessens7eb4c232021-07-24 18:31:45 -0400624 self.assertEqual(want_mode, found_mode[1:])
625 if os.getuid() == 0:
626 # The chown failed nonfatally if we're not root
627 self.assertEqual(0, statf.st_uid)
628
629 def test_installed_modes_extended(self):
630 '''
631 Test that files are installed with correct permissions using install_mode.
632 '''
Nirbheek Chauhan04ae1cf2021-10-30 18:54:04 +0530633 if is_cygwin():
634 self.new_builddir_in_tempdir()
Xavier Claessens7eb4c232021-07-24 18:31:45 -0400635 testdir = os.path.join(self.common_test_dir, '190 install_mode')
636 self.init(testdir)
637 self.build()
638 self.install()
639
640 for fsobj, want_mode in [
641 ('bin', 'drwxr-x---'),
642 ('bin/runscript.sh', '-rwxr-sr-x'),
643 ('bin/trivialprog', '-rwxr-sr-x'),
644 ('include', 'drwxr-x---'),
645 ('include/config.h', '-rw-rwSr--'),
Eli Schwartzf8ebfdf2021-11-18 17:52:12 -0500646 ('include/rootdir.h', '-r--r--r--'),
Xavier Claessens7eb4c232021-07-24 18:31:45 -0400647 ('lib', 'drwxr-x---'),
648 ('lib/libstat.a', '-rw---Sr--'),
649 ('share', 'drwxr-x---'),
650 ('share/man', 'drwxr-x---'),
651 ('share/man/man1', 'drwxr-x---'),
Eli Schwartzf8ebfdf2021-11-18 17:52:12 -0500652 ('share/man/man1/foo.1', '-r--r--r--'),
Xavier Claessens7eb4c232021-07-24 18:31:45 -0400653 ('share/sub1', 'drwxr-x---'),
Eli Schwartzf8ebfdf2021-11-18 17:52:12 -0500654 ('share/sub1/second.dat', '-rwxr-x--x'),
Xavier Claessens7eb4c232021-07-24 18:31:45 -0400655 ('subdir', 'drwxr-x---'),
656 ('subdir/data.dat', '-rw-rwSr--'),
657 ]:
658 f = os.path.join(self.installdir, 'usr', *fsobj.split('/'))
659 found_mode = stat.filemode(os.stat(f).st_mode)
660 self.assertEqual(want_mode, found_mode,
661 msg=('Expected file %s to have mode %s but found %s instead.' %
662 (fsobj, want_mode, found_mode)))
663 # Ensure that introspect --installed works on all types of files
664 # FIXME: also verify the files list
665 self.introspect('--installed')
666
667 def test_install_umask(self):
668 '''
669 Test that files are installed with correct permissions using default
670 install umask of 022, regardless of the umask at time the worktree
671 was checked out or the build was executed.
672 '''
Nirbheek Chauhan04ae1cf2021-10-30 18:54:04 +0530673 if is_cygwin():
674 self.new_builddir_in_tempdir()
Xavier Claessens7eb4c232021-07-24 18:31:45 -0400675 # Copy source tree to a temporary directory and change permissions
676 # there to simulate a checkout with umask 002.
677 orig_testdir = os.path.join(self.unit_test_dir, '26 install umask')
678 # Create a new testdir under tmpdir.
679 tmpdir = os.path.realpath(tempfile.mkdtemp())
680 self.addCleanup(windows_proof_rmtree, tmpdir)
681 testdir = os.path.join(tmpdir, '26 install umask')
682 # Copy the tree using shutil.copyfile, which will use the current umask
683 # instead of preserving permissions of the old tree.
684 save_umask = os.umask(0o002)
685 self.addCleanup(os.umask, save_umask)
686 shutil.copytree(orig_testdir, testdir, copy_function=shutil.copyfile)
687 # Preserve the executable status of subdir/sayhello though.
688 os.chmod(os.path.join(testdir, 'subdir', 'sayhello'), 0o775)
689 self.init(testdir)
690 # Run the build under a 027 umask now.
691 os.umask(0o027)
692 self.build()
693 # And keep umask 027 for the install step too.
694 self.install()
695
696 for executable in [
697 'bin/prog',
698 'share/subdir/sayhello',
699 ]:
700 f = os.path.join(self.installdir, 'usr', *executable.split('/'))
701 found_mode = stat.filemode(os.stat(f).st_mode)
702 want_mode = '-rwxr-xr-x'
703 self.assertEqual(want_mode, found_mode,
704 msg=('Expected file %s to have mode %s but found %s instead.' %
705 (executable, want_mode, found_mode)))
706
707 for directory in [
708 'usr',
709 'usr/bin',
710 'usr/include',
711 'usr/share',
712 'usr/share/man',
713 'usr/share/man/man1',
714 'usr/share/subdir',
715 ]:
716 f = os.path.join(self.installdir, *directory.split('/'))
717 found_mode = stat.filemode(os.stat(f).st_mode)
718 want_mode = 'drwxr-xr-x'
719 self.assertEqual(want_mode, found_mode,
720 msg=('Expected directory %s to have mode %s but found %s instead.' %
721 (directory, want_mode, found_mode)))
722
723 for datafile in [
724 'include/sample.h',
725 'share/datafile.cat',
726 'share/file.dat',
727 'share/man/man1/prog.1',
728 'share/subdir/datafile.dog',
729 ]:
730 f = os.path.join(self.installdir, 'usr', *datafile.split('/'))
731 found_mode = stat.filemode(os.stat(f).st_mode)
732 want_mode = '-rw-r--r--'
733 self.assertEqual(want_mode, found_mode,
734 msg=('Expected file %s to have mode %s but found %s instead.' %
735 (datafile, want_mode, found_mode)))
736
737 def test_cpp_std_override(self):
738 testdir = os.path.join(self.unit_test_dir, '6 std override')
739 self.init(testdir)
740 compdb = self.get_compdb()
741 # Don't try to use -std=c++03 as a check for the
742 # presence of a compiler flag, as ICC does not
743 # support it.
744 for i in compdb:
745 if 'prog98' in i['file']:
746 c98_comp = i['command']
747 if 'prog11' in i['file']:
748 c11_comp = i['command']
749 if 'progp' in i['file']:
750 plain_comp = i['command']
751 self.assertNotEqual(len(plain_comp), 0)
752 self.assertIn('-std=c++98', c98_comp)
753 self.assertNotIn('-std=c++11', c98_comp)
754 self.assertIn('-std=c++11', c11_comp)
755 self.assertNotIn('-std=c++98', c11_comp)
756 self.assertNotIn('-std=c++98', plain_comp)
757 self.assertNotIn('-std=c++11', plain_comp)
758 # Now werror
759 self.assertIn('-Werror', plain_comp)
760 self.assertNotIn('-Werror', c98_comp)
761
762 def test_run_installed(self):
763 if is_cygwin() or is_osx():
764 raise SkipTest('LD_LIBRARY_PATH and RPATH not applicable')
765
766 testdir = os.path.join(self.unit_test_dir, '7 run installed')
767 self.init(testdir)
768 self.build()
769 self.install()
770 installed_exe = os.path.join(self.installdir, 'usr/bin/prog')
771 installed_libdir = os.path.join(self.installdir, 'usr/foo')
772 installed_lib = os.path.join(installed_libdir, 'libfoo.so')
773 self.assertTrue(os.path.isfile(installed_exe))
774 self.assertTrue(os.path.isdir(installed_libdir))
775 self.assertTrue(os.path.isfile(installed_lib))
776 # Must fail when run without LD_LIBRARY_PATH to ensure that
777 # rpath has been properly stripped rather than pointing to the builddir.
778 self.assertNotEqual(subprocess.call(installed_exe, stderr=subprocess.DEVNULL), 0)
779 # When LD_LIBRARY_PATH is set it should start working.
780 # For some reason setting LD_LIBRARY_PATH in os.environ fails
781 # when all tests are run (but works when only this test is run),
782 # but doing this explicitly works.
783 env = os.environ.copy()
784 env['LD_LIBRARY_PATH'] = ':'.join([installed_libdir, env.get('LD_LIBRARY_PATH', '')])
785 self.assertEqual(subprocess.call(installed_exe, env=env), 0)
786 # Ensure that introspect --installed works
787 installed = self.introspect('--installed')
788 for v in installed.values():
789 self.assertTrue('prog' in v or 'foo' in v)
790
791 @skipIfNoPkgconfig
792 def test_order_of_l_arguments(self):
793 testdir = os.path.join(self.unit_test_dir, '8 -L -l order')
794 self.init(testdir, override_envvars={'PKG_CONFIG_PATH': testdir})
795 # NOTE: .pc file has -Lfoo -lfoo -Lbar -lbar but pkg-config reorders
796 # the flags before returning them to -Lfoo -Lbar -lfoo -lbar
797 # but pkgconf seems to not do that. Sigh. Support both.
798 expected_order = [('-L/me/first', '-lfoo1'),
799 ('-L/me/second', '-lfoo2'),
800 ('-L/me/first', '-L/me/second'),
801 ('-lfoo1', '-lfoo2'),
802 ('-L/me/second', '-L/me/third'),
803 ('-L/me/third', '-L/me/fourth',),
804 ('-L/me/third', '-lfoo3'),
805 ('-L/me/fourth', '-lfoo4'),
806 ('-lfoo3', '-lfoo4'),
807 ]
808 with open(os.path.join(self.builddir, 'build.ninja'), encoding='utf-8') as ifile:
809 for line in ifile:
810 if expected_order[0][0] in line:
811 for first, second in expected_order:
812 self.assertLess(line.index(first), line.index(second))
813 return
814 raise RuntimeError('Linker entries not found in the Ninja file.')
815
816 def test_introspect_dependencies(self):
817 '''
818 Tests that mesonintrospect --dependencies returns expected output.
819 '''
820 testdir = os.path.join(self.framework_test_dir, '7 gnome')
821 self.init(testdir)
822 glib_found = False
823 gobject_found = False
824 deps = self.introspect('--dependencies')
825 self.assertIsInstance(deps, list)
826 for dep in deps:
827 self.assertIsInstance(dep, dict)
828 self.assertIn('name', dep)
829 self.assertIn('compile_args', dep)
830 self.assertIn('link_args', dep)
831 if dep['name'] == 'glib-2.0':
832 glib_found = True
833 elif dep['name'] == 'gobject-2.0':
834 gobject_found = True
835 self.assertTrue(glib_found)
836 self.assertTrue(gobject_found)
Dylan Bakerb43cf092022-03-09 13:05:01 -0800837 if subprocess.call([PKG_CONFIG, '--exists', 'glib-2.0 >= 2.56.2']) != 0:
Xavier Claessens7eb4c232021-07-24 18:31:45 -0400838 raise SkipTest('glib >= 2.56.2 needed for the rest')
839 targets = self.introspect('--targets')
840 docbook_target = None
841 for t in targets:
842 if t['name'] == 'generated-gdbus-docbook':
843 docbook_target = t
844 break
845 self.assertIsInstance(docbook_target, dict)
846 self.assertEqual(os.path.basename(t['filename'][0]), 'generated-gdbus-doc-' + os.path.basename(t['target_sources'][0]['sources'][0]))
847
848 def test_introspect_installed(self):
849 testdir = os.path.join(self.linuxlike_test_dir, '7 library versions')
850 self.init(testdir)
851
852 install = self.introspect('--installed')
853 install = {os.path.basename(k): v for k, v in install.items()}
854 print(install)
855 if is_osx():
856 the_truth = {
857 'libmodule.dylib': '/usr/lib/libmodule.dylib',
858 'libnoversion.dylib': '/usr/lib/libnoversion.dylib',
859 'libonlysoversion.5.dylib': '/usr/lib/libonlysoversion.5.dylib',
860 'libonlysoversion.dylib': '/usr/lib/libonlysoversion.dylib',
861 'libonlyversion.1.dylib': '/usr/lib/libonlyversion.1.dylib',
862 'libonlyversion.dylib': '/usr/lib/libonlyversion.dylib',
863 'libsome.0.dylib': '/usr/lib/libsome.0.dylib',
864 'libsome.dylib': '/usr/lib/libsome.dylib',
865 }
866 the_truth_2 = {'/usr/lib/libsome.dylib',
867 '/usr/lib/libsome.0.dylib',
868 }
869 else:
870 the_truth = {
871 'libmodule.so': '/usr/lib/libmodule.so',
872 'libnoversion.so': '/usr/lib/libnoversion.so',
873 'libonlysoversion.so': '/usr/lib/libonlysoversion.so',
874 'libonlysoversion.so.5': '/usr/lib/libonlysoversion.so.5',
875 'libonlyversion.so': '/usr/lib/libonlyversion.so',
876 'libonlyversion.so.1': '/usr/lib/libonlyversion.so.1',
877 'libonlyversion.so.1.4.5': '/usr/lib/libonlyversion.so.1.4.5',
878 'libsome.so': '/usr/lib/libsome.so',
879 'libsome.so.0': '/usr/lib/libsome.so.0',
880 'libsome.so.1.2.3': '/usr/lib/libsome.so.1.2.3',
881 }
882 the_truth_2 = {'/usr/lib/libsome.so',
883 '/usr/lib/libsome.so.0',
884 '/usr/lib/libsome.so.1.2.3'}
885 self.assertDictEqual(install, the_truth)
886
887 targets = self.introspect('--targets')
888 for t in targets:
889 if t['name'] != 'some':
890 continue
891 self.assertSetEqual(the_truth_2, set(t['install_filename']))
892
893 def test_build_rpath(self):
894 if is_cygwin():
895 raise SkipTest('Windows PE/COFF binaries do not use RPATH')
896 testdir = os.path.join(self.unit_test_dir, '10 build_rpath')
897 self.init(testdir)
898 self.build()
899 build_rpath = get_rpath(os.path.join(self.builddir, 'prog'))
900 self.assertEqual(build_rpath, '$ORIGIN/sub:/foo/bar')
901 build_rpath = get_rpath(os.path.join(self.builddir, 'progcxx'))
902 self.assertEqual(build_rpath, '$ORIGIN/sub:/foo/bar')
903 self.install()
904 install_rpath = get_rpath(os.path.join(self.installdir, 'usr/bin/prog'))
905 self.assertEqual(install_rpath, '/baz')
906 install_rpath = get_rpath(os.path.join(self.installdir, 'usr/bin/progcxx'))
907 self.assertEqual(install_rpath, 'baz')
908
909 @skipIfNoPkgconfig
910 def test_build_rpath_pkgconfig(self):
911 '''
912 Test that current build artefacts (libs) are found first on the rpath,
913 manually specified rpath comes second and additional rpath elements (from
914 pkg-config files) come last
915 '''
916 if is_cygwin():
917 raise SkipTest('Windows PE/COFF binaries do not use RPATH')
Jussi Pakkanen1c3191b2022-03-28 19:33:52 +0300918 testdir = os.path.join(self.unit_test_dir, '89 pkgconfig build rpath order')
Xavier Claessens7eb4c232021-07-24 18:31:45 -0400919 self.init(testdir, override_envvars={'PKG_CONFIG_PATH': testdir})
920 self.build()
921 build_rpath = get_rpath(os.path.join(self.builddir, 'prog'))
922 self.assertEqual(build_rpath, '$ORIGIN/sub:/foo/bar:/foo/dummy')
923 build_rpath = get_rpath(os.path.join(self.builddir, 'progcxx'))
924 self.assertEqual(build_rpath, '$ORIGIN/sub:/foo/bar:/foo/dummy')
925 self.install()
926 install_rpath = get_rpath(os.path.join(self.installdir, 'usr/bin/prog'))
927 self.assertEqual(install_rpath, '/baz:/foo/dummy')
928 install_rpath = get_rpath(os.path.join(self.installdir, 'usr/bin/progcxx'))
929 self.assertEqual(install_rpath, 'baz:/foo/dummy')
930
Alyssa Ross9322a292023-12-15 18:28:00 +0100931 @skipIfNoPkgconfig
Xavier Claessens7eb4c232021-07-24 18:31:45 -0400932 def test_global_rpath(self):
933 if is_cygwin():
934 raise SkipTest('Windows PE/COFF binaries do not use RPATH')
935 if is_osx():
936 raise SkipTest('Global RPATHs via LDFLAGS not yet supported on MacOS (does anybody need it?)')
937
Jussi Pakkanen1c3191b2022-03-28 19:33:52 +0300938 testdir = os.path.join(self.unit_test_dir, '79 global-rpath')
Xavier Claessens7eb4c232021-07-24 18:31:45 -0400939 oldinstalldir = self.installdir
940
941 # Build and install an external library without DESTDIR.
942 # The external library generates a .pc file without an rpath.
943 yonder_dir = os.path.join(testdir, 'yonder')
944 yonder_prefix = os.path.join(oldinstalldir, 'yonder')
945 yonder_libdir = os.path.join(yonder_prefix, self.libdir)
946 self.prefix = yonder_prefix
947 self.installdir = yonder_prefix
948 self.init(yonder_dir)
949 self.build()
950 self.install(use_destdir=False)
951
952 # Since rpath has multiple valid formats we need to
953 # test that they are all properly used.
954 rpath_formats = [
955 ('-Wl,-rpath=', False),
956 ('-Wl,-rpath,', False),
957 ('-Wl,--just-symbols=', True),
958 ('-Wl,--just-symbols,', True),
959 ('-Wl,-R', False),
960 ('-Wl,-R,', False)
961 ]
962 for rpath_format, exception in rpath_formats:
963 # Build an app that uses that installed library.
964 # Supply the rpath to the installed library via LDFLAGS
965 # (as systems like buildroot and guix are wont to do)
966 # and verify install preserves that rpath.
967 self.new_builddir()
968 env = {'LDFLAGS': rpath_format + yonder_libdir,
969 'PKG_CONFIG_PATH': os.path.join(yonder_libdir, 'pkgconfig')}
970 if exception:
971 with self.assertRaises(subprocess.CalledProcessError):
972 self.init(testdir, override_envvars=env)
973 continue
974 self.init(testdir, override_envvars=env)
975 self.build()
976 self.install(use_destdir=False)
977 got_rpath = get_rpath(os.path.join(yonder_prefix, 'bin/rpathified'))
978 self.assertEqual(got_rpath, yonder_libdir, rpath_format)
979
980 @skip_if_not_base_option('b_sanitize')
981 def test_pch_with_address_sanitizer(self):
982 if is_cygwin():
983 raise SkipTest('asan not available on Cygwin')
984 if is_openbsd():
985 raise SkipTest('-fsanitize=address is not supported on OpenBSD')
986
987 testdir = os.path.join(self.common_test_dir, '13 pch')
988 self.init(testdir, extra_args=['-Db_sanitize=address', '-Db_lundef=false'])
989 self.build()
990 compdb = self.get_compdb()
991 for i in compdb:
992 self.assertIn("-fsanitize=address", i["command"])
993
994 def test_cross_find_program(self):
995 testdir = os.path.join(self.unit_test_dir, '11 cross prog')
Tristan Partin3784f722023-07-12 14:10:08 -0500996 crossfile = tempfile.NamedTemporaryFile(mode='w', encoding='utf-8')
Xavier Claessens7eb4c232021-07-24 18:31:45 -0400997 print(os.path.join(testdir, 'some_cross_tool.py'))
998
999 tool_path = os.path.join(testdir, 'some_cross_tool.py')
1000
1001 crossfile.write(textwrap.dedent(f'''\
1002 [binaries]
1003 c = '{shutil.which('gcc' if is_sunos() else 'cc')}'
1004 ar = '{shutil.which('ar')}'
1005 strip = '{shutil.which('strip')}'
1006 sometool.py = ['{tool_path}']
1007 someothertool.py = '{tool_path}'
1008
1009 [properties]
1010
1011 [host_machine]
1012 system = 'linux'
1013 cpu_family = 'arm'
1014 cpu = 'armv7' # Not sure if correct.
1015 endian = 'little'
1016 '''))
1017 crossfile.flush()
Nirbheek Chauhan2512c252022-01-28 14:59:33 +05301018 self.meson_cross_files = [crossfile.name]
Xavier Claessens7eb4c232021-07-24 18:31:45 -04001019 self.init(testdir)
1020
1021 def test_reconfigure(self):
1022 testdir = os.path.join(self.unit_test_dir, '13 reconfigure')
1023 self.init(testdir, extra_args=['-Db_coverage=true'], default_args=False)
1024 self.build('reconfigure')
1025
1026 def test_vala_generated_source_buildir_inside_source_tree(self):
1027 '''
1028 Test that valac outputs generated C files in the expected location when
1029 the builddir is a subdir of the source tree.
1030 '''
1031 if not shutil.which('valac'):
1032 raise SkipTest('valac not installed.')
1033
1034 testdir = os.path.join(self.vala_test_dir, '8 generated sources')
1035 newdir = os.path.join(self.builddir, 'srctree')
1036 shutil.copytree(testdir, newdir)
1037 testdir = newdir
1038 # New builddir
1039 builddir = os.path.join(testdir, 'subdir/_build')
1040 os.makedirs(builddir, exist_ok=True)
1041 self.change_builddir(builddir)
1042 self.init(testdir)
1043 self.build()
1044
1045 def test_old_gnome_module_codepaths(self):
1046 '''
1047 A lot of code in the GNOME module is conditional on the version of the
1048 glib tools that are installed, and breakages in the old code can slip
1049 by once the CI has a newer glib version. So we force the GNOME module
1050 to pretend that it's running on an ancient glib so the fallback code is
1051 also tested.
1052 '''
1053 testdir = os.path.join(self.framework_test_dir, '7 gnome')
Dylan Baker1917b922022-09-02 14:37:51 -07001054 with mock.patch('mesonbuild.modules.gnome.GnomeModule._get_native_glib_version', mock.Mock(return_value='2.20')):
1055 env = {'MESON_UNIT_TEST_PRETEND_GLIB_OLD': "1"}
Xavier Claessens7eb4c232021-07-24 18:31:45 -04001056 self.init(testdir,
1057 inprocess=True,
1058 override_envvars=env)
1059 self.build(override_envvars=env)
Xavier Claessens7eb4c232021-07-24 18:31:45 -04001060
1061 @skipIfNoPkgconfig
1062 def test_pkgconfig_usage(self):
1063 testdir1 = os.path.join(self.unit_test_dir, '27 pkgconfig usage/dependency')
1064 testdir2 = os.path.join(self.unit_test_dir, '27 pkgconfig usage/dependee')
Dylan Bakerb43cf092022-03-09 13:05:01 -08001065 if subprocess.call([PKG_CONFIG, '--cflags', 'glib-2.0'],
Xavier Claessens7eb4c232021-07-24 18:31:45 -04001066 stdout=subprocess.DEVNULL,
1067 stderr=subprocess.DEVNULL) != 0:
1068 raise SkipTest('Glib 2.0 dependency not available.')
1069 with tempfile.TemporaryDirectory() as tempdirname:
1070 self.init(testdir1, extra_args=['--prefix=' + tempdirname, '--libdir=lib'], default_args=False)
1071 self.install(use_destdir=False)
1072 shutil.rmtree(self.builddir)
1073 os.mkdir(self.builddir)
1074 pkg_dir = os.path.join(tempdirname, 'lib/pkgconfig')
1075 self.assertTrue(os.path.exists(os.path.join(pkg_dir, 'libpkgdep.pc')))
1076 lib_dir = os.path.join(tempdirname, 'lib')
1077 myenv = os.environ.copy()
1078 myenv['PKG_CONFIG_PATH'] = pkg_dir
1079 # Private internal libraries must not leak out.
Dylan Bakerb43cf092022-03-09 13:05:01 -08001080 pkg_out = subprocess.check_output([PKG_CONFIG, '--static', '--libs', 'libpkgdep'], env=myenv)
Eli Schwartz739de7b2021-10-19 19:51:44 -04001081 self.assertNotIn(b'libpkgdep-int', pkg_out, 'Internal library leaked out.')
Xavier Claessens7eb4c232021-07-24 18:31:45 -04001082 # Dependencies must not leak to cflags when building only a shared library.
Dylan Bakerb43cf092022-03-09 13:05:01 -08001083 pkg_out = subprocess.check_output([PKG_CONFIG, '--cflags', 'libpkgdep'], env=myenv)
Eli Schwartz739de7b2021-10-19 19:51:44 -04001084 self.assertNotIn(b'glib', pkg_out, 'Internal dependency leaked to headers.')
Xavier Claessens7eb4c232021-07-24 18:31:45 -04001085 # Test that the result is usable.
1086 self.init(testdir2, override_envvars=myenv)
1087 self.build(override_envvars=myenv)
1088 myenv = os.environ.copy()
1089 myenv['LD_LIBRARY_PATH'] = ':'.join([lib_dir, myenv.get('LD_LIBRARY_PATH', '')])
1090 if is_cygwin():
1091 bin_dir = os.path.join(tempdirname, 'bin')
1092 myenv['PATH'] = bin_dir + os.pathsep + myenv['PATH']
1093 self.assertTrue(os.path.isdir(lib_dir))
1094 test_exe = os.path.join(self.builddir, 'pkguser')
1095 self.assertTrue(os.path.isfile(test_exe))
1096 subprocess.check_call(test_exe, env=myenv)
1097
1098 @skipIfNoPkgconfig
1099 def test_pkgconfig_relative_paths(self):
Jussi Pakkanen1c3191b2022-03-28 19:33:52 +03001100 testdir = os.path.join(self.unit_test_dir, '61 pkgconfig relative paths')
Xavier Claessens7eb4c232021-07-24 18:31:45 -04001101 pkg_dir = os.path.join(testdir, 'pkgconfig')
Eli Schwartz739de7b2021-10-19 19:51:44 -04001102 self.assertPathExists(os.path.join(pkg_dir, 'librelativepath.pc'))
Xavier Claessens7eb4c232021-07-24 18:31:45 -04001103
1104 env = get_fake_env(testdir, self.builddir, self.prefix)
1105 env.coredata.set_options({OptionKey('pkg_config_path'): pkg_dir}, subproject='')
1106 kwargs = {'required': True, 'silent': True}
1107 relative_path_dep = PkgConfigDependency('librelativepath', env, kwargs)
1108 self.assertTrue(relative_path_dep.found())
1109
1110 # Ensure link_args are properly quoted
1111 libpath = Path(self.builddir) / '../relativepath/lib'
1112 link_args = ['-L' + libpath.as_posix(), '-lrelativepath']
1113 self.assertEqual(relative_path_dep.get_link_args(), link_args)
1114
1115 @skipIfNoPkgconfig
1116 def test_pkgconfig_duplicate_path_entries(self):
1117 testdir = os.path.join(self.unit_test_dir, '111 pkgconfig duplicate path entries')
1118 pkg_dir = os.path.join(testdir, 'pkgconfig')
1119
1120 env = get_fake_env(testdir, self.builddir, self.prefix)
1121 env.coredata.set_options({OptionKey('pkg_config_path'): pkg_dir}, subproject='')
1122
Xavier Claessens18bec0d2022-04-13 09:35:52 -04001123 # Regression test: This used to modify the value of `pkg_config_path`
1124 # option, adding the meson-uninstalled directory to it.
Xavier Claessensf1c35b52023-08-18 09:56:13 -04001125 PkgConfigInterface.setup_env({}, env, MachineChoice.HOST, uninstalled=True)
Xavier Claessens18bec0d2022-04-13 09:35:52 -04001126
Xavier Claessens7eb4c232021-07-24 18:31:45 -04001127 pkg_config_path = env.coredata.options[OptionKey('pkg_config_path')].value
Xavier Claessens18bec0d2022-04-13 09:35:52 -04001128 self.assertEqual(pkg_config_path, [pkg_dir])
Xavier Claessens7eb4c232021-07-24 18:31:45 -04001129
1130 @skipIfNoPkgconfig
1131 def test_pkgconfig_internal_libraries(self):
1132 '''
1133 '''
1134 with tempfile.TemporaryDirectory() as tempdirname:
1135 # build library
1136 testdirbase = os.path.join(self.unit_test_dir, '32 pkgconfig use libraries')
1137 testdirlib = os.path.join(testdirbase, 'lib')
1138 self.init(testdirlib, extra_args=['--prefix=' + tempdirname,
1139 '--libdir=lib',
1140 '--default-library=static'], default_args=False)
1141 self.build()
1142 self.install(use_destdir=False)
1143
1144 # build user of library
1145 pkg_dir = os.path.join(tempdirname, 'lib/pkgconfig')
1146 self.new_builddir()
1147 self.init(os.path.join(testdirbase, 'app'),
1148 override_envvars={'PKG_CONFIG_PATH': pkg_dir})
1149 self.build()
1150
1151 @skipIfNoPkgconfig
1152 def test_static_archive_stripping(self):
1153 '''
1154 Check that Meson produces valid static archives with --strip enabled
1155 '''
1156 with tempfile.TemporaryDirectory() as tempdirname:
Jussi Pakkanen1c3191b2022-03-28 19:33:52 +03001157 testdirbase = os.path.join(self.unit_test_dir, '65 static archive stripping')
Xavier Claessens7eb4c232021-07-24 18:31:45 -04001158
1159 # build lib
1160 self.new_builddir()
1161 testdirlib = os.path.join(testdirbase, 'lib')
1162 testlibprefix = os.path.join(tempdirname, 'libprefix')
1163 self.init(testdirlib, extra_args=['--prefix=' + testlibprefix,
1164 '--libdir=lib',
1165 '--default-library=static',
1166 '--buildtype=debug',
1167 '--strip'], default_args=False)
1168 self.build()
1169 self.install(use_destdir=False)
1170
1171 # build executable (uses lib, fails if static archive has been stripped incorrectly)
1172 pkg_dir = os.path.join(testlibprefix, 'lib/pkgconfig')
1173 self.new_builddir()
1174 self.init(os.path.join(testdirbase, 'app'),
1175 override_envvars={'PKG_CONFIG_PATH': pkg_dir})
1176 self.build()
1177
1178 @skipIfNoPkgconfig
1179 def test_pkgconfig_formatting(self):
1180 testdir = os.path.join(self.unit_test_dir, '38 pkgconfig format')
1181 self.init(testdir)
1182 myenv = os.environ.copy()
Dylan Bakerf0b27962022-03-09 13:13:30 -08001183 myenv['PKG_CONFIG_PATH'] = _prepend_pkg_config_path(self.privatedir)
Dylan Bakerb43cf092022-03-09 13:05:01 -08001184 stdo = subprocess.check_output([PKG_CONFIG, '--libs-only-l', 'libsomething'], env=myenv)
Xavier Claessens7eb4c232021-07-24 18:31:45 -04001185 deps = [b'-lgobject-2.0', b'-lgio-2.0', b'-lglib-2.0', b'-lsomething']
1186 if is_windows() or is_cygwin() or is_osx() or is_openbsd():
1187 # On Windows, libintl is a separate library
1188 deps.append(b'-lintl')
1189 self.assertEqual(set(deps), set(stdo.split()))
1190
1191 @skipIfNoPkgconfig
1192 @skip_if_not_language('cs')
1193 def test_pkgconfig_csharp_library(self):
Jussi Pakkanen1c3191b2022-03-28 19:33:52 +03001194 testdir = os.path.join(self.unit_test_dir, '49 pkgconfig csharp library')
Xavier Claessens7eb4c232021-07-24 18:31:45 -04001195 self.init(testdir)
1196 myenv = os.environ.copy()
Dylan Bakerf0b27962022-03-09 13:13:30 -08001197 myenv['PKG_CONFIG_PATH'] = _prepend_pkg_config_path(self.privatedir)
Dylan Bakerb43cf092022-03-09 13:05:01 -08001198 stdo = subprocess.check_output([PKG_CONFIG, '--libs', 'libsomething'], env=myenv)
Xavier Claessens7eb4c232021-07-24 18:31:45 -04001199
1200 self.assertEqual("-r/usr/lib/libsomething.dll", str(stdo.decode('ascii')).strip())
1201
1202 @skipIfNoPkgconfig
1203 def test_pkgconfig_link_order(self):
1204 '''
1205 Test that libraries are listed before their dependencies.
1206 '''
Jussi Pakkanen1c3191b2022-03-28 19:33:52 +03001207 testdir = os.path.join(self.unit_test_dir, '52 pkgconfig static link order')
Xavier Claessens7eb4c232021-07-24 18:31:45 -04001208 self.init(testdir)
1209 myenv = os.environ.copy()
Dylan Bakerf0b27962022-03-09 13:13:30 -08001210 myenv['PKG_CONFIG_PATH'] = _prepend_pkg_config_path(self.privatedir)
Dylan Bakerb43cf092022-03-09 13:05:01 -08001211 stdo = subprocess.check_output([PKG_CONFIG, '--libs', 'libsomething'], env=myenv)
Xavier Claessens7eb4c232021-07-24 18:31:45 -04001212 deps = stdo.split()
Eli Schwartz739de7b2021-10-19 19:51:44 -04001213 self.assertLess(deps.index(b'-lsomething'), deps.index(b'-ldependency'))
Xavier Claessens7eb4c232021-07-24 18:31:45 -04001214
1215 def test_deterministic_dep_order(self):
1216 '''
1217 Test that the dependencies are always listed in a deterministic order.
1218 '''
Jussi Pakkanen1c3191b2022-03-28 19:33:52 +03001219 testdir = os.path.join(self.unit_test_dir, '42 dep order')
Xavier Claessens7eb4c232021-07-24 18:31:45 -04001220 self.init(testdir)
1221 with open(os.path.join(self.builddir, 'build.ninja'), encoding='utf-8') as bfile:
1222 for line in bfile:
1223 if 'build myexe:' in line or 'build myexe.exe:' in line:
1224 self.assertIn('liblib1.a liblib2.a', line)
1225 return
1226 raise RuntimeError('Could not find the build rule')
1227
1228 def test_deterministic_rpath_order(self):
1229 '''
1230 Test that the rpaths are always listed in a deterministic order.
1231 '''
1232 if is_cygwin():
1233 raise SkipTest('rpath are not used on Cygwin')
Jussi Pakkanen1c3191b2022-03-28 19:33:52 +03001234 testdir = os.path.join(self.unit_test_dir, '41 rpath order')
Xavier Claessens7eb4c232021-07-24 18:31:45 -04001235 self.init(testdir)
1236 if is_osx():
1237 rpathre = re.compile(r'-rpath,.*/subprojects/sub1.*-rpath,.*/subprojects/sub2')
1238 else:
1239 rpathre = re.compile(r'-rpath,\$\$ORIGIN/subprojects/sub1:\$\$ORIGIN/subprojects/sub2')
1240 with open(os.path.join(self.builddir, 'build.ninja'), encoding='utf-8') as bfile:
1241 for line in bfile:
1242 if '-rpath' in line:
1243 self.assertRegex(line, rpathre)
1244 return
1245 raise RuntimeError('Could not find the rpath')
1246
1247 def test_override_with_exe_dep(self):
1248 '''
1249 Test that we produce the correct dependencies when a program is overridden with an executable.
1250 '''
1251 testdir = os.path.join(self.src_root, 'test cases', 'native', '9 override with exe')
1252 self.init(testdir)
1253 with open(os.path.join(self.builddir, 'build.ninja'), encoding='utf-8') as bfile:
1254 for line in bfile:
1255 if 'main1.c:' in line or 'main2.c:' in line:
1256 self.assertIn('| subprojects/sub/foobar', line)
1257
1258 @skipIfNoPkgconfig
1259 def test_usage_external_library(self):
1260 '''
1261 Test that uninstalled usage of an external library (from the system or
1262 PkgConfigDependency) works. On macOS, this workflow works out of the
1263 box. On Linux, BSDs, Windows, etc, you need to set extra arguments such
1264 as LD_LIBRARY_PATH, etc, so this test is skipped.
1265
1266 The system library is found with cc.find_library() and pkg-config deps.
1267 '''
1268 oldprefix = self.prefix
1269 # Install external library so we can find it
Jussi Pakkanen1c3191b2022-03-28 19:33:52 +03001270 testdir = os.path.join(self.unit_test_dir, '39 external, internal library rpath', 'external library')
Xavier Claessens7eb4c232021-07-24 18:31:45 -04001271 # install into installdir without using DESTDIR
1272 installdir = self.installdir
1273 self.prefix = installdir
1274 self.init(testdir)
1275 self.prefix = oldprefix
1276 self.build()
1277 self.install(use_destdir=False)
1278 ## New builddir for the consumer
1279 self.new_builddir()
1280 env = {'LIBRARY_PATH': os.path.join(installdir, self.libdir),
Dylan Bakerf0b27962022-03-09 13:13:30 -08001281 'PKG_CONFIG_PATH': _prepend_pkg_config_path(os.path.join(installdir, self.libdir, 'pkgconfig'))}
Jussi Pakkanen1c3191b2022-03-28 19:33:52 +03001282 testdir = os.path.join(self.unit_test_dir, '39 external, internal library rpath', 'built library')
Xavier Claessens7eb4c232021-07-24 18:31:45 -04001283 # install into installdir without using DESTDIR
1284 self.prefix = self.installdir
1285 self.init(testdir, override_envvars=env)
1286 self.prefix = oldprefix
1287 self.build(override_envvars=env)
1288 # test uninstalled
1289 self.run_tests(override_envvars=env)
1290 if not (is_osx() or is_linux()):
1291 return
1292 # test running after installation
1293 self.install(use_destdir=False)
1294 prog = os.path.join(self.installdir, 'bin', 'prog')
1295 self._run([prog])
1296 if not is_osx():
1297 # Rest of the workflow only works on macOS
1298 return
1299 out = self._run(['otool', '-L', prog])
1300 self.assertNotIn('@rpath', out)
1301 ## New builddir for testing that DESTDIR is not added to install_name
1302 self.new_builddir()
1303 # install into installdir with DESTDIR
1304 self.init(testdir, override_envvars=env)
1305 self.build(override_envvars=env)
1306 # test running after installation
1307 self.install(override_envvars=env)
1308 prog = self.installdir + os.path.join(self.prefix, 'bin', 'prog')
1309 lib = self.installdir + os.path.join(self.prefix, 'lib', 'libbar_built.dylib')
1310 for f in prog, lib:
1311 out = self._run(['otool', '-L', f])
1312 # Ensure that the otool output does not contain self.installdir
1313 self.assertNotRegex(out, self.installdir + '.*dylib ')
Eli Schwartz59d4f772021-06-09 14:57:15 -04001314
lilinzhedd2e3bf2021-07-19 15:24:44 +08001315 @skipIfNoPkgconfig
1316 def test_link_arg_fullname(self):
1317 '''
1318 Test for support of -l:libfullname.a
1319 see: https://github.com/mesonbuild/meson/issues/9000
1320 https://stackoverflow.com/questions/48532868/gcc-library-option-with-a-colon-llibevent-a
1321 '''
Jussi Pakkanen97dc8802023-11-12 17:23:28 +02001322 testdir = os.path.join(self.unit_test_dir, '98 link full name','libtestprovider')
lilinzhedd2e3bf2021-07-19 15:24:44 +08001323 oldprefix = self.prefix
1324 # install into installdir without using DESTDIR
1325 installdir = self.installdir
1326 self.prefix = installdir
1327 self.init(testdir)
1328 self.prefix=oldprefix
1329 self.build()
1330 self.install(use_destdir=False)
1331
1332 self.new_builddir()
1333 env = {'LIBRARY_PATH': os.path.join(installdir, self.libdir),
Dylan Bakerf0b27962022-03-09 13:13:30 -08001334 'PKG_CONFIG_PATH': _prepend_pkg_config_path(os.path.join(installdir, self.libdir, 'pkgconfig'))}
Jussi Pakkanen97dc8802023-11-12 17:23:28 +02001335 testdir = os.path.join(self.unit_test_dir, '98 link full name','proguser')
lilinzhedd2e3bf2021-07-19 15:24:44 +08001336 self.init(testdir,override_envvars=env)
Eli Schwartz59d4f772021-06-09 14:57:15 -04001337
lilinzhedd2e3bf2021-07-19 15:24:44 +08001338 # test for link with full path
1339 with open(os.path.join(self.builddir, 'build.ninja'), encoding='utf-8') as bfile:
1340 for line in bfile:
1341 if 'build dprovidertest:' in line:
1342 self.assertIn('/libtestprovider.a', line)
Eli Schwartz59d4f772021-06-09 14:57:15 -04001343
lilinzhedd2e3bf2021-07-19 15:24:44 +08001344 if is_osx():
1345 # macOS's ld do not supports `--whole-archive`, skip build & run
1346 return
Eli Schwartz59d4f772021-06-09 14:57:15 -04001347
lilinzhedd2e3bf2021-07-19 15:24:44 +08001348 self.build(override_envvars=env)
Eli Schwartz59d4f772021-06-09 14:57:15 -04001349
lilinzhedd2e3bf2021-07-19 15:24:44 +08001350 # skip test if pkg-config is too old.
1351 # before v0.28, Libs flags like -Wl will not kept in context order with -l flags.
1352 # see https://gitlab.freedesktop.org/pkg-config/pkg-config/-/blob/master/NEWS
Dylan Bakerb43cf092022-03-09 13:05:01 -08001353 pkgconfigver = subprocess.check_output([PKG_CONFIG, '--version'])
lilinzhedd2e3bf2021-07-19 15:24:44 +08001354 if b'0.28' > pkgconfigver:
1355 raise SkipTest('pkg-config is too old to be correctly done this.')
1356 self.run_tests()
Xavier Claessens7eb4c232021-07-24 18:31:45 -04001357
1358 @skipIfNoPkgconfig
1359 def test_usage_pkgconfig_prefixes(self):
1360 '''
1361 Build and install two external libraries, to different prefixes,
1362 then build and install a client program that finds them via pkgconfig,
1363 and verify the installed client program runs.
1364 '''
1365 oldinstalldir = self.installdir
1366
1367 # Build and install both external libraries without DESTDIR
Jussi Pakkanen1c3191b2022-03-28 19:33:52 +03001368 val1dir = os.path.join(self.unit_test_dir, '74 pkgconfig prefixes', 'val1')
Xavier Claessens7eb4c232021-07-24 18:31:45 -04001369 val1prefix = os.path.join(oldinstalldir, 'val1')
1370 self.prefix = val1prefix
1371 self.installdir = val1prefix
1372 self.init(val1dir)
1373 self.build()
1374 self.install(use_destdir=False)
1375 self.new_builddir()
1376
1377 env1 = {}
1378 env1['PKG_CONFIG_PATH'] = os.path.join(val1prefix, self.libdir, 'pkgconfig')
Jussi Pakkanen1c3191b2022-03-28 19:33:52 +03001379 val2dir = os.path.join(self.unit_test_dir, '74 pkgconfig prefixes', 'val2')
Xavier Claessens7eb4c232021-07-24 18:31:45 -04001380 val2prefix = os.path.join(oldinstalldir, 'val2')
1381 self.prefix = val2prefix
1382 self.installdir = val2prefix
1383 self.init(val2dir, override_envvars=env1)
1384 self.build()
1385 self.install(use_destdir=False)
1386 self.new_builddir()
1387
1388 # Build, install, and run the client program
1389 env2 = {}
1390 env2['PKG_CONFIG_PATH'] = os.path.join(val2prefix, self.libdir, 'pkgconfig')
Jussi Pakkanen1c3191b2022-03-28 19:33:52 +03001391 testdir = os.path.join(self.unit_test_dir, '74 pkgconfig prefixes', 'client')
Xavier Claessens7eb4c232021-07-24 18:31:45 -04001392 testprefix = os.path.join(oldinstalldir, 'client')
1393 self.prefix = testprefix
1394 self.installdir = testprefix
1395 self.init(testdir, override_envvars=env2)
1396 self.build()
1397 self.install(use_destdir=False)
1398 prog = os.path.join(self.installdir, 'bin', 'client')
1399 env3 = {}
1400 if is_cygwin():
1401 env3['PATH'] = os.path.join(val1prefix, 'bin') + \
1402 os.pathsep + \
1403 os.path.join(val2prefix, 'bin') + \
1404 os.pathsep + os.environ['PATH']
1405 out = self._run([prog], override_envvars=env3).strip()
1406 # Expected output is val1 + val2 = 3
1407 self.assertEqual(out, '3')
1408
1409 def install_subdir_invalid_symlinks(self, testdir, subdir_path):
1410 '''
1411 Test that installation of broken symlinks works fine.
1412 https://github.com/mesonbuild/meson/issues/3914
1413 '''
1414 testdir = os.path.join(self.common_test_dir, testdir)
1415 subdir = os.path.join(testdir, subdir_path)
1416 with chdir(subdir):
1417 # Can't distribute broken symlinks in the source tree because it breaks
1418 # the creation of zipapps. Create it dynamically and run the test by
1419 # hand.
1420 src = '../../nonexistent.txt'
1421 os.symlink(src, 'invalid-symlink.txt')
1422 try:
1423 self.init(testdir)
1424 self.build()
1425 self.install()
1426 install_path = subdir_path.split(os.path.sep)[-1]
1427 link = os.path.join(self.installdir, 'usr', 'share', install_path, 'invalid-symlink.txt')
1428 self.assertTrue(os.path.islink(link), msg=link)
1429 self.assertEqual(src, os.readlink(link))
1430 self.assertFalse(os.path.isfile(link), msg=link)
1431 finally:
1432 os.remove(os.path.join(subdir, 'invalid-symlink.txt'))
1433
1434 def test_install_subdir_symlinks(self):
1435 self.install_subdir_invalid_symlinks('59 install subdir', os.path.join('sub', 'sub1'))
1436
1437 def test_install_subdir_symlinks_with_default_umask(self):
1438 self.install_subdir_invalid_symlinks('190 install_mode', 'sub2')
1439
1440 def test_install_subdir_symlinks_with_default_umask_and_mode(self):
1441 self.install_subdir_invalid_symlinks('190 install_mode', 'sub1')
1442
1443 @skipIfNoPkgconfigDep('gmodule-2.0')
1444 def test_ldflag_dedup(self):
Jussi Pakkanen1c3191b2022-03-28 19:33:52 +03001445 testdir = os.path.join(self.unit_test_dir, '51 ldflagdedup')
Xavier Claessens7eb4c232021-07-24 18:31:45 -04001446 if is_cygwin() or is_osx():
1447 raise SkipTest('Not applicable on Cygwin or OSX.')
1448 env = get_fake_env()
1449 cc = detect_c_compiler(env, MachineChoice.HOST)
1450 linker = cc.linker
1451 if not linker.export_dynamic_args(env):
1452 raise SkipTest('Not applicable for linkers without --export-dynamic')
1453 self.init(testdir)
1454 build_ninja = os.path.join(self.builddir, 'build.ninja')
1455 max_count = 0
1456 search_term = '-Wl,--export-dynamic'
1457 with open(build_ninja, encoding='utf-8') as f:
1458 for line in f:
1459 max_count = max(max_count, line.count(search_term))
1460 self.assertEqual(max_count, 1, 'Export dynamic incorrectly deduplicated.')
1461
1462 def test_compiler_libs_static_dedup(self):
Jussi Pakkanen1c3191b2022-03-28 19:33:52 +03001463 testdir = os.path.join(self.unit_test_dir, '55 dedup compiler libs')
Xavier Claessens7eb4c232021-07-24 18:31:45 -04001464 self.init(testdir)
1465 build_ninja = os.path.join(self.builddir, 'build.ninja')
1466 with open(build_ninja, encoding='utf-8') as f:
1467 lines = f.readlines()
1468 for lib in ('-ldl', '-lm', '-lc', '-lrt'):
1469 for line in lines:
1470 if lib not in line:
1471 continue
1472 # Assert that
1473 self.assertEqual(len(line.split(lib)), 2, msg=(lib, line))
1474
1475 @skipIfNoPkgconfig
1476 def test_noncross_options(self):
1477 # C_std defined in project options must be in effect also when native compiling.
Jussi Pakkanen1c3191b2022-03-28 19:33:52 +03001478 testdir = os.path.join(self.unit_test_dir, '50 noncross options')
Xavier Claessens7eb4c232021-07-24 18:31:45 -04001479 self.init(testdir, extra_args=['-Dpkg_config_path=' + testdir])
1480 compdb = self.get_compdb()
1481 self.assertEqual(len(compdb), 2)
1482 self.assertRegex(compdb[0]['command'], '-std=c99')
1483 self.assertRegex(compdb[1]['command'], '-std=c99')
1484 self.build()
1485
1486 def test_identity_cross(self):
Jussi Pakkanen1c3191b2022-03-28 19:33:52 +03001487 testdir = os.path.join(self.unit_test_dir, '60 identity cross')
Xavier Claessens7eb4c232021-07-24 18:31:45 -04001488
Tristan Partin3784f722023-07-12 14:10:08 -05001489 constantsfile = tempfile.NamedTemporaryFile(mode='w', encoding='utf-8')
Nirbheek Chauhan2512c252022-01-28 14:59:33 +05301490 constantsfile.write(textwrap.dedent('''\
1491 [constants]
1492 py_ext = '.py'
1493 '''))
1494 constantsfile.flush()
1495
Tristan Partin3784f722023-07-12 14:10:08 -05001496 nativefile = tempfile.NamedTemporaryFile(mode='w', encoding='utf-8')
Xavier Claessens7eb4c232021-07-24 18:31:45 -04001497 nativefile.write(textwrap.dedent('''\
1498 [binaries]
Nirbheek Chauhan2512c252022-01-28 14:59:33 +05301499 c = ['{}' + py_ext]
1500 '''.format(os.path.join(testdir, 'build_wrapper'))))
Xavier Claessens7eb4c232021-07-24 18:31:45 -04001501 nativefile.flush()
Nirbheek Chauhan2512c252022-01-28 14:59:33 +05301502 self.meson_native_files = [constantsfile.name, nativefile.name]
Xavier Claessens7eb4c232021-07-24 18:31:45 -04001503
Tristan Partin3784f722023-07-12 14:10:08 -05001504 crossfile = tempfile.NamedTemporaryFile(mode='w', encoding='utf-8')
Xavier Claessens7eb4c232021-07-24 18:31:45 -04001505 crossfile.write(textwrap.dedent('''\
1506 [binaries]
Nirbheek Chauhan2512c252022-01-28 14:59:33 +05301507 c = ['{}' + py_ext]
1508 '''.format(os.path.join(testdir, 'host_wrapper'))))
Xavier Claessens7eb4c232021-07-24 18:31:45 -04001509 crossfile.flush()
Nirbheek Chauhan2512c252022-01-28 14:59:33 +05301510 self.meson_cross_files = [constantsfile.name, crossfile.name]
Xavier Claessens7eb4c232021-07-24 18:31:45 -04001511
1512 # TODO should someday be explicit about build platform only here
1513 self.init(testdir)
1514
1515 def test_identity_cross_env(self):
Jussi Pakkanen1c3191b2022-03-28 19:33:52 +03001516 testdir = os.path.join(self.unit_test_dir, '60 identity cross')
Xavier Claessens7eb4c232021-07-24 18:31:45 -04001517 env = {
1518 'CC_FOR_BUILD': '"' + os.path.join(testdir, 'build_wrapper.py') + '"',
John Ericson01a7aa02020-03-19 18:05:31 -04001519 'CC': '"' + os.path.join(testdir, 'host_wrapper.py') + '"',
Xavier Claessens7eb4c232021-07-24 18:31:45 -04001520 }
Tristan Partin3784f722023-07-12 14:10:08 -05001521 crossfile = tempfile.NamedTemporaryFile(mode='w', encoding='utf-8')
John Ericson01a7aa02020-03-19 18:05:31 -04001522 crossfile.write('')
Xavier Claessens7eb4c232021-07-24 18:31:45 -04001523 crossfile.flush()
Nirbheek Chauhan2512c252022-01-28 14:59:33 +05301524 self.meson_cross_files = [crossfile.name]
Xavier Claessens7eb4c232021-07-24 18:31:45 -04001525 # TODO should someday be explicit about build platform only here
1526 self.init(testdir, override_envvars=env)
1527
1528 @skipIfNoPkgconfig
1529 def test_static_link(self):
1530 if is_cygwin():
1531 raise SkipTest("Cygwin doesn't support LD_LIBRARY_PATH.")
1532
1533 # Build some libraries and install them
Jussi Pakkanen1c3191b2022-03-28 19:33:52 +03001534 testdir = os.path.join(self.unit_test_dir, '66 static link/lib')
Xavier Claessens7eb4c232021-07-24 18:31:45 -04001535 libdir = os.path.join(self.installdir, self.libdir)
1536 oldprefix = self.prefix
1537 self.prefix = self.installdir
1538 self.init(testdir)
1539 self.install(use_destdir=False)
1540
1541 # Test that installed libraries works
1542 self.new_builddir()
1543 self.prefix = oldprefix
1544 meson_args = [f'-Dc_link_args=-L{libdir}',
1545 '--fatal-meson-warnings']
Jussi Pakkanen1c3191b2022-03-28 19:33:52 +03001546 testdir = os.path.join(self.unit_test_dir, '66 static link')
Xavier Claessens7eb4c232021-07-24 18:31:45 -04001547 env = {'PKG_CONFIG_LIBDIR': os.path.join(libdir, 'pkgconfig')}
1548 self.init(testdir, extra_args=meson_args, override_envvars=env)
1549 self.build()
1550 self.run_tests()
1551
1552 def _check_ld(self, check: str, name: str, lang: str, expected: str) -> None:
1553 if is_sunos():
1554 raise SkipTest('Solaris currently cannot override the linker.')
1555 if not shutil.which(check):
1556 raise SkipTest(f'Could not find {check}.')
1557 envvars = [mesonbuild.envconfig.ENV_VAR_PROG_MAP[f'{lang}_ld']]
1558
1559 # Also test a deprecated variable if there is one.
1560 if f'{lang}_ld' in mesonbuild.envconfig.DEPRECATED_ENV_PROG_MAP:
1561 envvars.append(
1562 mesonbuild.envconfig.DEPRECATED_ENV_PROG_MAP[f'{lang}_ld'])
1563
1564 for envvar in envvars:
1565 with mock.patch.dict(os.environ, {envvar: name}):
1566 env = get_fake_env()
1567 comp = compiler_from_language(env, lang, MachineChoice.HOST)
1568 if isinstance(comp, (AppleClangCCompiler, AppleClangCPPCompiler,
1569 AppleClangObjCCompiler, AppleClangObjCPPCompiler)):
1570 raise SkipTest('AppleClang is currently only supported with ld64')
Fini Jastrowc16fdae2022-03-08 18:41:08 +01001571 if lang != 'rust' and comp.use_linker_args('bfd', '') == []:
Xavier Claessens7eb4c232021-07-24 18:31:45 -04001572 raise SkipTest(
1573 f'Compiler {comp.id} does not support using alternative linkers')
1574 self.assertEqual(comp.linker.id, expected)
1575
1576 def test_ld_environment_variable_bfd(self):
1577 self._check_ld('ld.bfd', 'bfd', 'c', 'ld.bfd')
1578
1579 def test_ld_environment_variable_gold(self):
1580 self._check_ld('ld.gold', 'gold', 'c', 'ld.gold')
1581
1582 def test_ld_environment_variable_lld(self):
1583 self._check_ld('ld.lld', 'lld', 'c', 'ld.lld')
1584
1585 @skip_if_not_language('rust')
1586 @skipIfNoExecutable('ld.gold') # need an additional check here because _check_ld checks for gcc
1587 def test_ld_environment_variable_rust(self):
1588 self._check_ld('gcc', 'gcc -fuse-ld=gold', 'rust', 'ld.gold')
1589
1590 def test_ld_environment_variable_cpp(self):
1591 self._check_ld('ld.gold', 'gold', 'cpp', 'ld.gold')
1592
1593 @skip_if_not_language('objc')
1594 def test_ld_environment_variable_objc(self):
1595 self._check_ld('ld.gold', 'gold', 'objc', 'ld.gold')
1596
1597 @skip_if_not_language('objcpp')
1598 def test_ld_environment_variable_objcpp(self):
1599 self._check_ld('ld.gold', 'gold', 'objcpp', 'ld.gold')
1600
1601 @skip_if_not_language('fortran')
1602 def test_ld_environment_variable_fortran(self):
1603 self._check_ld('ld.gold', 'gold', 'fortran', 'ld.gold')
1604
1605 @skip_if_not_language('d')
1606 def test_ld_environment_variable_d(self):
1607 # At least for me, ldc defaults to gold, and gdc defaults to bfd, so
1608 # let's pick lld, which isn't the default for either (currently)
1609 if is_osx():
1610 expected = 'ld64'
1611 else:
1612 expected = 'ld.lld'
1613 self._check_ld('ld.lld', 'lld', 'd', expected)
1614
1615 def compute_sha256(self, filename):
1616 with open(filename, 'rb') as f:
1617 return hashlib.sha256(f.read()).hexdigest()
1618
1619 def test_wrap_with_file_url(self):
Jussi Pakkanen1c3191b2022-03-28 19:33:52 +03001620 testdir = os.path.join(self.unit_test_dir, '72 wrap file url')
Xavier Claessens7eb4c232021-07-24 18:31:45 -04001621 source_filename = os.path.join(testdir, 'subprojects', 'foo.tar.xz')
1622 patch_filename = os.path.join(testdir, 'subprojects', 'foo-patch.tar.xz')
1623 wrap_filename = os.path.join(testdir, 'subprojects', 'foo.wrap')
1624 source_hash = self.compute_sha256(source_filename)
1625 patch_hash = self.compute_sha256(patch_filename)
1626 wrap = textwrap.dedent("""\
1627 [wrap-file]
1628 directory = foo
1629
1630 source_url = http://server.invalid/foo
1631 source_fallback_url = file://{}
1632 source_filename = foo.tar.xz
1633 source_hash = {}
1634
1635 patch_url = http://server.invalid/foo
1636 patch_fallback_url = file://{}
1637 patch_filename = foo-patch.tar.xz
1638 patch_hash = {}
1639 """.format(source_filename, source_hash, patch_filename, patch_hash))
1640 with open(wrap_filename, 'w', encoding='utf-8') as f:
1641 f.write(wrap)
1642 self.init(testdir)
1643 self.build()
1644 self.run_tests()
1645
1646 windows_proof_rmtree(os.path.join(testdir, 'subprojects', 'packagecache'))
1647 windows_proof_rmtree(os.path.join(testdir, 'subprojects', 'foo'))
1648 os.unlink(wrap_filename)
1649
1650 def test_no_rpath_for_static(self):
1651 testdir = os.path.join(self.common_test_dir, '5 linkstatic')
1652 self.init(testdir)
1653 self.build()
1654 build_rpath = get_rpath(os.path.join(self.builddir, 'prog'))
1655 self.assertIsNone(build_rpath)
1656
1657 def test_lookup_system_after_broken_fallback(self):
1658 # Just to generate libfoo.pc so we can test system dependency lookup.
1659 testdir = os.path.join(self.common_test_dir, '44 pkgconfig-gen')
1660 self.init(testdir)
1661 privatedir = self.privatedir
1662
1663 # Write test project where the first dependency() returns not-found
1664 # because 'broken' subproject does not exit, but that should not prevent
1665 # the 2nd dependency() to lookup on system.
1666 self.new_builddir()
1667 with tempfile.TemporaryDirectory() as d:
1668 with open(os.path.join(d, 'meson.build'), 'w', encoding='utf-8') as f:
1669 f.write(textwrap.dedent('''\
1670 project('test')
1671 dependency('notfound', fallback: 'broken', required: false)
1672 dependency('libfoo', fallback: 'broken', required: true)
1673 '''))
1674 self.init(d, override_envvars={'PKG_CONFIG_LIBDIR': privatedir})
1675
1676 def test_as_link_whole(self):
Jussi Pakkanen1c3191b2022-03-28 19:33:52 +03001677 testdir = os.path.join(self.unit_test_dir, '76 as link whole')
Xavier Claessens7eb4c232021-07-24 18:31:45 -04001678 self.init(testdir)
1679 with open(os.path.join(self.privatedir, 'bar1.pc'), encoding='utf-8') as f:
1680 content = f.read()
1681 self.assertIn('-lfoo', content)
1682 with open(os.path.join(self.privatedir, 'bar2.pc'), encoding='utf-8') as f:
1683 content = f.read()
1684 self.assertNotIn('-lfoo', content)
1685
1686 def test_prelinking(self):
1687 # Prelinking currently only works on recently new GNU toolchains.
1688 # Skip everything else. When support for other toolchains is added,
1689 # remove limitations as necessary.
1690 if is_osx():
1691 raise SkipTest('Prelinking not supported on Darwin.')
1692 if 'clang' in os.environ.get('CC', 'dummy'):
1693 raise SkipTest('Prelinking not supported with Clang.')
Jussi Pakkanen1c3191b2022-03-28 19:33:52 +03001694 testdir = os.path.join(self.unit_test_dir, '86 prelinking')
Alan Coopersmith7f0193e2021-09-25 11:21:35 -07001695 env = get_fake_env(testdir, self.builddir, self.prefix)
1696 cc = detect_c_compiler(env, MachineChoice.HOST)
Simon McVittie42327ea2022-05-05 11:38:41 +01001697 if cc.id == "gcc" and not version_compare(cc.version, '>=9'):
1698 raise SkipTest('Prelinking not supported with gcc 8 or older.')
Xavier Claessens7eb4c232021-07-24 18:31:45 -04001699 self.init(testdir)
1700 self.build()
1701 outlib = os.path.join(self.builddir, 'libprelinked.a')
1702 ar = shutil.which('ar')
Eli Schwartz739de7b2021-10-19 19:51:44 -04001703 self.assertPathExists(outlib)
1704 self.assertIsNotNone(ar)
Xavier Claessens7eb4c232021-07-24 18:31:45 -04001705 p = subprocess.run([ar, 't', outlib],
1706 stdout=subprocess.PIPE,
1707 stderr=subprocess.DEVNULL,
Eli Schwartz751f8442021-08-15 11:34:11 -04001708 text=True, timeout=1)
Xavier Claessens7eb4c232021-07-24 18:31:45 -04001709 obj_files = p.stdout.strip().split('\n')
1710 self.assertEqual(len(obj_files), 1)
1711 self.assertTrue(obj_files[0].endswith('-prelink.o'))
Paolo Bonzini7639b702021-10-08 12:20:35 +02001712
1713 def do_one_test_with_nativefile(self, testdir, args):
1714 testdir = os.path.join(self.common_test_dir, testdir)
1715 with tempfile.TemporaryDirectory() as d:
1716 p = Path(d) / 'nativefile'
1717 with p.open('wt', encoding='utf-8') as f:
1718 f.write(f'''[binaries]
1719 c = {args}
1720 ''')
1721 self.init(testdir, extra_args=['--native-file=' + str(p)])
1722 self.build()
1723
1724 def test_cmake_multilib(self):
1725 '''
1726 Test that the cmake module handles multilib paths correctly.
1727 '''
1728 # Verify that "gcc -m32" works
1729 try:
1730 self.do_one_test_with_nativefile('1 trivial', "['gcc', '-m32']")
1731 except subprocess.CalledProcessError as e:
1732 raise SkipTest('Not GCC, or GCC does not have the -m32 option')
1733 self.wipe()
1734
1735 # Verify that cmake works
1736 try:
1737 self.do_one_test_with_nativefile('../cmake/1 basic', "['gcc']")
1738 except subprocess.CalledProcessError as e:
1739 raise SkipTest('Could not build basic cmake project')
1740 self.wipe()
1741
1742 # If so, we can test that cmake works with "gcc -m32"
1743 self.do_one_test_with_nativefile('../cmake/1 basic', "['gcc', '-m32']")
Xavier Claessenseafb8a82022-03-02 20:04:15 -05001744
L. E. Segovia7e5b0762022-10-30 13:05:40 +00001745 @skipUnless(is_linux() or is_osx(), 'Test only applicable to Linux and macOS')
Xavier Claessenseafb8a82022-03-02 20:04:15 -05001746 def test_install_strip(self):
Jussi Pakkanen97dc8802023-11-12 17:23:28 +02001747 testdir = os.path.join(self.unit_test_dir, '104 strip')
Xavier Claessenseafb8a82022-03-02 20:04:15 -05001748 self.init(testdir)
1749 self.build()
1750
1751 destdir = self.installdir + self.prefix
L. E. Segovia7e5b0762022-10-30 13:05:40 +00001752 if is_linux():
1753 lib = os.path.join(destdir, self.libdir, 'liba.so')
1754 else:
1755 lib = os.path.join(destdir, self.libdir, 'liba.dylib')
Xavier Claessenseafb8a82022-03-02 20:04:15 -05001756 install_cmd = self.meson_command + ['install', '--destdir', self.installdir]
1757
1758 # Check we have debug symbols by default
1759 self._run(install_cmd, workdir=self.builddir)
L. E. Segovia7e5b0762022-10-30 13:05:40 +00001760 if is_linux():
1761 # file can detect stripped libraries on linux
1762 stdout = self._run(['file', '-b', lib])
1763 self.assertIn('not stripped', stdout)
1764 else:
1765 # on macOS we need to query dsymutil instead.
1766 # Alternatively, check if __dyld_private is defined
1767 # in the output of nm liba.dylib, but that is not
1768 # 100% reliable, it needs linking to an external library
1769 stdout = self._run(['dsymutil', '--dump-debug-map', lib])
1770 self.assertIn('symbols:', stdout)
Xavier Claessenseafb8a82022-03-02 20:04:15 -05001771
1772 # Check debug symbols got removed with --strip
1773 self._run(install_cmd + ['--strip'], workdir=self.builddir)
L. E. Segovia7e5b0762022-10-30 13:05:40 +00001774 if is_linux():
1775 stdout = self._run(['file', '-b', lib])
1776 self.assertNotIn('not stripped', stdout)
1777 else:
1778 stdout = self._run(['dsymutil', '--dump-debug-map', lib])
1779 self.assertNotIn('symbols:', stdout)
Yang Bo83d18d12022-09-08 11:29:30 +00001780
1781 def test_isystem_default_removal_with_symlink(self):
1782 env = get_fake_env()
1783 cpp = detect_cpp_compiler(env, MachineChoice.HOST)
1784 default_dirs = cpp.get_default_include_dirs()
1785 default_symlinks = []
1786 with tempfile.TemporaryDirectory() as tmpdir:
1787 for i in range(len(default_dirs)):
1788 symlink = f'{tmpdir}/default_dir{i}'
1789 default_symlinks.append(symlink)
1790 os.symlink(default_dirs[i], symlink)
1791 self.assertFalse(cpp.compiler_args([f'-isystem{symlink}' for symlink in default_symlinks]).to_native())
Jussi Pakkanen3ae89a72022-11-19 21:22:55 +02001792
1793 def test_freezing(self):
Jussi Pakkanen78d89242023-03-28 14:09:58 +03001794 testdir = os.path.join(self.unit_test_dir, '110 freeze')
Jussi Pakkanen3ae89a72022-11-19 21:22:55 +02001795 self.init(testdir)
1796 self.build()
1797 with self.assertRaises(subprocess.CalledProcessError) as e:
1798 self.run_tests()
1799 self.assertNotIn('Traceback', e.exception.output)
Xavier Claessensa78af232023-04-26 10:05:19 -04001800
1801 @skipUnless(is_linux(), "Ninja file differs on different platforms")
1802 def test_complex_link_cases(self):
Jussi Pakkanen3bbe66e2023-10-26 19:23:20 +03001803 testdir = os.path.join(self.unit_test_dir, '114 complex link cases')
Xavier Claessensa78af232023-04-26 10:05:19 -04001804 self.init(testdir)
1805 self.build()
1806 with open(os.path.join(self.builddir, 'build.ninja'), encoding='utf-8') as f:
1807 content = f.read()
1808 # Verify link dependencies, see comments in meson.build.
1809 self.assertIn('build libt1-s3.a: STATIC_LINKER libt1-s2.a.p/s2.c.o libt1-s3.a.p/s3.c.o\n', content)
1810 self.assertIn('build t1-e1: c_LINKER t1-e1.p/main.c.o | libt1-s1.a libt1-s3.a\n', content)
1811 self.assertIn('build libt2-s3.a: STATIC_LINKER libt2-s2.a.p/s2.c.o libt2-s1.a.p/s1.c.o libt2-s3.a.p/s3.c.o\n', content)
1812 self.assertIn('build t2-e1: c_LINKER t2-e1.p/main.c.o | libt2-s3.a\n', content)
1813 self.assertIn('build t3-e1: c_LINKER t3-e1.p/main.c.o | libt3-s3.so.p/libt3-s3.so.symbols\n', content)
1814 self.assertIn('build t4-e1: c_LINKER t4-e1.p/main.c.o | libt4-s2.so.p/libt4-s2.so.symbols libt4-s3.a\n', content)
1815 self.assertIn('build t5-e1: c_LINKER t5-e1.p/main.c.o | libt5-s1.so.p/libt5-s1.so.symbols libt5-s3.a\n', content)
1816 self.assertIn('build t6-e1: c_LINKER t6-e1.p/main.c.o | libt6-s2.a libt6-s3.a\n', content)
Xavier Claessenscd638532023-07-08 15:56:08 -04001817 self.assertIn('build t7-e1: c_LINKER t7-e1.p/main.c.o | libt7-s3.a\n', content)
Xavier Claessens1fef03c2023-07-10 13:21:41 -04001818 self.assertIn('build t8-e1: c_LINKER t8-e1.p/main.c.o | libt8-s1.a libt8-s2.a libt8-s3.a\n', content)
Xavier Claessens926c3a62023-07-18 21:20:32 -04001819 self.assertIn('build t9-e1: c_LINKER t9-e1.p/main.c.o | libt9-s1.a libt9-s2.a libt9-s3.a\n', content)
1820 self.assertIn('build t12-e1: c_LINKER t12-e1.p/main.c.o | libt12-s1.a libt12-s2.a libt12-s3.a\n', content)
1821 self.assertIn('build t13-e1: c_LINKER t13-e1.p/main.c.o | libt12-s1.a libt13-s3.a\n', content)