Skip to content

Commit c31a10f

Browse files
committed
Always check if found libraries are linkable
We don't always check if a library is actually linkable because of concerns that a library may not be standalone, so linking against it may still have unresolved references. We can workaround that by building a shared library as a test rather than an executable, and in fact we already do that in a bunch of cases since bb5f2ca. This comes up in particular with Fedora and dependency('atomic') because on x86_64, they provide a libatomic.so linker script (so our file existence check passes), but trying to use it later on will fail if the backing package isn't installed. The _test_all_naming unittest has been tweaked because we were using a blank file rather than an actual shared library, which (as expected), now fails. I've also added a test for dependency('atomic') providing a result that can actually be linked against. I've not made it check generally whether dependency('atomic') finds something when we expect to (at least for now) as it'll involve some CI whack-a-mole. _get_file_from_list has not been deleted because the Ninja backend's guess_library_absolute_path uses it. We might be able to change it to also use a link test but I've left that alone. Bug: https://bugzilla.redhat.com/show_bug.cgi?id=2352531 Bug: #14946 Closes: #10936 Signed-off-by: Sam James <[email protected]>
1 parent 467bd7f commit c31a10f

File tree

4 files changed

+56
-15
lines changed

4 files changed

+56
-15
lines changed

mesonbuild/compilers/mixins/clike.py

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1090,10 +1090,8 @@ def _get_trials_from_pattern(cls, pattern: str, directory: str, libname: str) ->
10901090
@staticmethod
10911091
def _get_file_from_list(env: Environment, paths: T.List[str]) -> T.Optional[Path]:
10921092
'''
1093-
We just check whether the library exists. We can't do a link check
1094-
because the library might have unresolved symbols that require other
1095-
libraries. On macOS we check if the library matches our target
1096-
architecture.
1093+
Check whether the library exists by filename. On macOS, we also
1094+
check if the library matches our target architecture.
10971095
'''
10981096
for p in paths:
10991097
if os.path.isfile(p):
@@ -1147,17 +1145,30 @@ def _find_library_real(self, libname: str, env: 'Environment', extra_dirs: T.Lis
11471145
except (mesonlib.MesonException, KeyError): # TODO evaluate if catching KeyError is wanted here
11481146
elf_class = 0
11491147
# Search in the specified dirs, and then in the system libraries
1148+
largs = self.get_linker_always_args() + self.get_allow_undefined_link_args()
1149+
lcargs = self.linker_to_compiler_args(largs)
11501150
for d in itertools.chain(extra_dirs, [] if ignore_system_dirs else self.get_library_dirs(env, elf_class)):
11511151
for p in patterns:
11521152
trials = self._get_trials_from_pattern(p, d, libname)
11531153
if not trials:
11541154
continue
1155-
trial = self._get_file_from_list(env, trials)
1156-
if not trial:
1155+
1156+
trial_result = ""
1157+
for trial in trials:
1158+
if not os.path.isfile(trial):
1159+
continue
1160+
1161+
extra_args = [trial] + lcargs
1162+
1163+
if self.links(code, env, extra_args=extra_args, disable_cache=True)[0]:
1164+
trial_result = trial
1165+
break
1166+
1167+
if not trial_result:
11571168
continue
1158-
if libname.startswith('lib') and trial.name.startswith(libname) and lib_prefix_warning:
1169+
if libname.startswith('lib') and trial_result.startswith(libname) and lib_prefix_warning:
11591170
mlog.warning(f'find_library({libname!r}) starting in "lib" only works by accident and is not portable')
1160-
return [trial.as_posix()]
1171+
return [Path(trial_result).as_posix()]
11611172
return None
11621173

11631174
def _find_library_impl(self, libname: str, env: 'Environment', extra_dirs: T.List[str],

test cases/common/285 atomic/a.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
int main(void) {
2+
return 0;
3+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
project('meson system dependency', 'c', meson_version: '>=1.7.0')
2+
3+
cc = meson.get_compiler('c')
4+
5+
# We could check if dependency('atomic') actually finds something when
6+
# we 'know' it exists (MESON_SKIP_TEST) but that's likely to be brittle,
7+
# so don't bother (for now, at least).
8+
atomic = dependency('atomic', required : false)
9+
10+
# If the dependency provider says it found something, make sure it can
11+
# be linked against (https://github.com/mesonbuild/meson/issues/14946).
12+
dependencies = [
13+
atomic
14+
]
15+
16+
exe = executable(
17+
'a',
18+
'a.c',
19+
dependencies : dependencies,
20+
install : false,
21+
)
22+
23+
test('basic', exe)

unittests/internaltests.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -551,10 +551,14 @@ def _test_all_naming(self, cc, env, patterns, platform):
551551
for i in ['libfoo.so.6.0', 'libfoo.so.5.0', 'libfoo.so.54.0', 'libfoo.so.66a.0b', 'libfoo.so.70.0.so.1',
552552
'libbar.so.7.10', 'libbar.so.7.9', 'libbar.so.7.9.3']:
553553
libpath = Path(tmpdir) / i
554-
libpath.write_text('', encoding='utf-8')
555-
found = cc._find_library_real('foo', env, [tmpdir], '', LibType.PREFER_SHARED, lib_prefix_warning=True, ignore_system_dirs=False)
554+
src = libpath.with_suffix('.c')
555+
with src.open('w', encoding='utf-8') as f:
556+
f.write('int meson_foobar (void) { return 0; }')
557+
subprocess.check_call(['gcc', str(src), '-o', str(libpath), '-shared'])
558+
559+
found = cc._find_library_real('foo', env, [tmpdir], 'int main(void) { return 0; }', LibType.PREFER_SHARED, lib_prefix_warning=True, ignore_system_dirs=False)
556560
self.assertEqual(os.path.basename(found[0]), 'libfoo.so.54.0')
557-
found = cc._find_library_real('bar', env, [tmpdir], '', LibType.PREFER_SHARED, lib_prefix_warning=True, ignore_system_dirs=False)
561+
found = cc._find_library_real('bar', env, [tmpdir], 'int main(void) { return 0; }', LibType.PREFER_SHARED, lib_prefix_warning=True, ignore_system_dirs=False)
558562
self.assertEqual(os.path.basename(found[0]), 'libbar.so.7.10')
559563

560564
def test_find_library_patterns(self):
@@ -610,15 +614,15 @@ def test_pkgconfig_parse_libs(self):
610614
https://github.com/mesonbuild/meson/issues/3951
611615
'''
612616
def create_static_lib(name):
613-
if not is_osx():
614-
name.open('w', encoding='utf-8').close()
615-
return
616617
src = name.with_suffix('.c')
617618
out = name.with_suffix('.o')
618619
with src.open('w', encoding='utf-8') as f:
619620
f.write('int meson_foobar (void) { return 0; }')
620621
# use of x86_64 is hardcoded in run_tests.py:get_fake_env()
621-
subprocess.check_call(['clang', '-c', str(src), '-o', str(out), '-arch', 'x86_64'])
622+
if is_osx():
623+
subprocess.check_call(['clang', '-c', str(src), '-o', str(out), '-arch', 'x86_64'])
624+
else:
625+
subprocess.check_call(['gcc', '-c', str(src), '-o', str(out)])
622626
subprocess.check_call(['ar', 'csr', str(name), str(out)])
623627

624628
with tempfile.TemporaryDirectory() as tmpdir:

0 commit comments

Comments
 (0)