diff options
-rw-r--r-- | mesonbuild/backend/ninjabackend.py | 5 | ||||
-rw-r--r-- | mesonbuild/compilers/c.py | 28 | ||||
-rw-r--r-- | mesonbuild/mesonlib.py | 18 | ||||
-rwxr-xr-x | run_tests.py | 1 | ||||
-rwxr-xr-x | run_unittests.py | 72 |
5 files changed, 85 insertions, 39 deletions
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 493fc0d..9b215b2 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -2347,15 +2347,14 @@ rule FORTRAN_DEP_HACK%s target_args = self.build_target_link_arguments(linker, target.link_whole_targets) return linker.get_link_whole_for(target_args) if len(target_args) else [] - @staticmethod @lru_cache(maxsize=None) - def guess_library_absolute_path(linker, libname, search_dirs, patterns): + def guess_library_absolute_path(self, linker, libname, search_dirs, patterns): for d in search_dirs: for p in patterns: trial = CCompiler._get_trials_from_pattern(p, d, libname) if not trial: continue - trial = CCompiler._get_file_from_list(trial) + trial = CCompiler._get_file_from_list(self.environment, trial) if not trial: continue # Return the first result diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py index 0a90c8c..a591183 100644 --- a/mesonbuild/compilers/c.py +++ b/mesonbuild/compilers/c.py @@ -27,6 +27,7 @@ from . import compilers from ..mesonlib import ( EnvironmentException, MesonException, version_compare, Popen_safe, listify, for_windows, for_darwin, for_cygwin, for_haiku, for_openbsd, + darwin_get_object_archs ) from .c_function_attributes import C_FUNC_ATTRIBUTES @@ -980,10 +981,28 @@ class CCompiler(Compiler): return [f.as_posix()] @staticmethod - def _get_file_from_list(files: List[str]) -> str: + def _get_file_from_list(env, files: List[str]) -> str: + ''' + We just check whether the library exists. We can't do a link check + because the library might have unresolved symbols that require other + libraries. On macOS we check if the library matches our target + architecture. + ''' + # If not building on macOS for Darwin, do a simple file check + if not env.machines.host.is_darwin() or not env.machines.build.is_darwin(): + for f in files: + if os.path.isfile(f): + return f + # Run `lipo` and check if the library supports the arch we want for f in files: - if os.path.isfile(f): + if not os.path.isfile(f): + continue + archs = darwin_get_object_archs(f) + if archs and env.machines.host.cpu_family in archs: return f + else: + mlog.debug('Rejected {}, supports {} but need {}' + .format(f, archs, env.machines.host.cpu_family)) return None @functools.lru_cache() @@ -1024,10 +1043,7 @@ class CCompiler(Compiler): trial = self._get_trials_from_pattern(p, d, libname) if not trial: continue - # We just check whether the library exists. We can't do a link - # check because the library might have unresolved symbols that - # require other libraries. - trial = self._get_file_from_list(trial) + trial = self._get_file_from_list(env, trial) if not trial: continue return [trial] diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py index 5529ce4..09228dc 100644 --- a/mesonbuild/mesonlib.py +++ b/mesonbuild/mesonlib.py @@ -461,6 +461,24 @@ def exe_exists(arglist): pass return False +lru_cache(maxsize=None) +def darwin_get_object_archs(objpath): + ''' + For a specific object (executable, static library, dylib, etc), run `lipo` + to fetch the list of archs supported by it. Supports both thin objects and + 'fat' objects. + ''' + _, stdo, stderr = Popen_safe(['lipo', '-archs', objpath]) + if not stdo: + mlog.debug('lipo {}: {}'.format(objpath, stderr)) + return None + # Convert from lipo-style archs to meson-style CPUs + stdo = stdo.replace('i386', 'x86') + # Add generic name for armv7 and armv7s + if 'armv7' in stdo: + stdo += ' arm' + return stdo.split() + def detect_vcs(source_dir): vcs_systems = [ dict(name = 'git', cmd = 'git', repo_dir = '.git', get_rev = 'git describe --dirty=+', rev_regex = '(.*)', dep = '.git/logs/HEAD'), diff --git a/run_tests.py b/run_tests.py index 943e16f..20cb4e2 100755 --- a/run_tests.py +++ b/run_tests.py @@ -81,6 +81,7 @@ def get_fake_env(sdir='', bdir=None, prefix='', opts=None): opts = get_fake_options(prefix) env = Environment(sdir, bdir, opts) env.coredata.compiler_options['c_args'] = FakeCompilerOptions() + env.machines.host.cpu_family = 'x86_64' # Used on macOS inside find_library return env diff --git a/run_unittests.py b/run_unittests.py index b8b3192..72ab062 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -45,7 +45,7 @@ from mesonbuild.interpreter import Interpreter, ObjectHolder from mesonbuild.mesonlib import ( is_windows, is_osx, is_cygwin, is_dragonflybsd, is_openbsd, is_haiku, windows_proof_rmtree, python_command, version_compare, - BuildDirLock, Version + BuildDirLock, Version, PerMachine ) from mesonbuild.environment import detect_ninja from mesonbuild.mesonlib import MesonException, EnvironmentException @@ -781,6 +781,17 @@ class InternalTests(unittest.TestCase): https://github.com/mesonbuild/meson/issues/3951 ''' + def create_static_lib(name): + if not is_osx(): + name.open('w').close() + return + src = name.with_suffix('.c') + out = name.with_suffix('.o') + with src.open('w') as f: + f.write('int meson_foobar (void) { return 0; }') + subprocess.check_call(['clang', '-c', str(src), '-o', str(out)]) + subprocess.check_call(['ar', 'csr', str(name), str(out)]) + with tempfile.TemporaryDirectory() as tmpdir: pkgbin = ExternalProgram('pkg-config', command=['pkg-config'], silent=True) env = get_fake_env() @@ -792,16 +803,16 @@ class InternalTests(unittest.TestCase): p1.mkdir() p2.mkdir() # libfoo.a is in one prefix - (p1 / 'libfoo.a').open('w').close() + create_static_lib(p1 / 'libfoo.a') # libbar.a is in both prefixes - (p1 / 'libbar.a').open('w').close() - (p2 / 'libbar.a').open('w').close() + create_static_lib(p1 / 'libbar.a') + create_static_lib(p2 / 'libbar.a') # Ensure that we never statically link to these - (p1 / 'libpthread.a').open('w').close() - (p1 / 'libm.a').open('w').close() - (p1 / 'libc.a').open('w').close() - (p1 / 'libdl.a').open('w').close() - (p1 / 'librt.a').open('w').close() + create_static_lib(p1 / 'libpthread.a') + create_static_lib(p1 / 'libm.a') + create_static_lib(p1 / 'libc.a') + create_static_lib(p1 / 'libdl.a') + create_static_lib(p1 / 'librt.a') def fake_call_pkgbin(self, args, env=None): if '--libs' not in args: @@ -815,30 +826,31 @@ class InternalTests(unittest.TestCase): old_call = PkgConfigDependency._call_pkgbin old_check = PkgConfigDependency.check_pkgconfig - old_pkgbin = PkgConfigDependency.class_pkgbin PkgConfigDependency._call_pkgbin = fake_call_pkgbin PkgConfigDependency.check_pkgconfig = lambda x, _: pkgbin # Test begins - kwargs = {'required': True, 'silent': True} - foo_dep = PkgConfigDependency('foo', env, kwargs) - self.assertEqual(foo_dep.get_link_args(), - [(p1 / 'libfoo.a').as_posix(), (p2 / 'libbar.a').as_posix()]) - bar_dep = PkgConfigDependency('bar', env, kwargs) - self.assertEqual(bar_dep.get_link_args(), [(p2 / 'libbar.a').as_posix()]) - internal_dep = PkgConfigDependency('internal', env, kwargs) - if compiler.get_argument_syntax() == 'msvc': - self.assertEqual(internal_dep.get_link_args(), []) - else: - link_args = internal_dep.get_link_args() - for link_arg in link_args: - for lib in ('pthread', 'm', 'c', 'dl', 'rt'): - self.assertNotIn('lib{}.a'.format(lib), link_arg, msg=link_args) - # Test ends - PkgConfigDependency._call_pkgbin = old_call - PkgConfigDependency.check_pkgconfig = old_check - # Reset dependency class to ensure that in-process configure doesn't mess up - PkgConfigDependency.pkgbin_cache = {} - PkgConfigDependency.class_pkgbin = old_pkgbin + try: + kwargs = {'required': True, 'silent': True} + foo_dep = PkgConfigDependency('foo', env, kwargs) + self.assertEqual(foo_dep.get_link_args(), + [(p1 / 'libfoo.a').as_posix(), (p2 / 'libbar.a').as_posix()]) + bar_dep = PkgConfigDependency('bar', env, kwargs) + self.assertEqual(bar_dep.get_link_args(), [(p2 / 'libbar.a').as_posix()]) + internal_dep = PkgConfigDependency('internal', env, kwargs) + if compiler.get_argument_syntax() == 'msvc': + self.assertEqual(internal_dep.get_link_args(), []) + else: + link_args = internal_dep.get_link_args() + for link_arg in link_args: + for lib in ('pthread', 'm', 'c', 'dl', 'rt'): + self.assertNotIn('lib{}.a'.format(lib), link_arg, msg=link_args) + finally: + # Test ends + PkgConfigDependency._call_pkgbin = old_call + PkgConfigDependency.check_pkgconfig = old_check + # Reset dependency class to ensure that in-process configure doesn't mess up + PkgConfigDependency.pkgbin_cache = {} + PkgConfigDependency.class_pkgbin = PerMachine(None, None, None) def test_version_compare(self): comparefunc = mesonbuild.mesonlib.version_compare_many |