shared_module: Add soname when used as a link target

Emit a detailed deprecation warning that explains what to do instead.
Also add a unittest.

```
DEPRECATION: target prog links against shared module mymod, which is incorrect.
             This will be an error in the future, so please use shared_library() for mymod instead.
             If shared_module() was used for mymod because it has references to undefined symbols,
             use shared_libary() with `override_options: ['b_lundef=false']` instead.
```

Fixes https://github.com/mesonbuild/meson/issues/9492
pull/9667/head
Nirbheek Chauhan 3 years ago
parent dc5b0cf50c
commit 54b2fc3f57
  1. 7
      docs/yaml/functions/shared_library.yaml
  2. 9
      docs/yaml/functions/shared_module.yaml
  3. 2
      mesonbuild/backend/ninjabackend.py
  4. 16
      mesonbuild/build.py
  5. 2
      test cases/failing/75 link with shared module on osx/test.json
  6. 5
      test cases/unit/1 soname/main.c
  7. 13
      test cases/unit/1 soname/meson.build
  8. 6
      unittests/allplatformstests.py
  9. 18
      unittests/linuxliketests.py

@ -2,13 +2,6 @@ name: shared_library
returns: lib returns: lib
description: Builds a shared library with the given sources. description: Builds a shared library with the given sources.
notes:
- |
Linking to a shared module is not supported on some
platforms, notably OSX. Consider using a
[[shared_library]] instead, if you need to both
`dlopen()` and link with a library.
posargs_inherit: _build_target_base posargs_inherit: _build_target_base
varargs_inherit: _build_target_base varargs_inherit: _build_target_base
kwargs_inherit: _build_target_base kwargs_inherit: _build_target_base

@ -13,6 +13,15 @@ description: |
you will need to set the `export_dynamic` argument of the executable to you will need to set the `export_dynamic` argument of the executable to
`true`. `true`.
notes:
- |
*Linking to a shared module is deprecated, and will be an error in the future*.
It used to be allowed because it was the only way to have a shared-library-like target that
contained references to undefined symbols. However, since 0.40.0, the `override_options:`
[[build_target]] keyword argument can be used to create such a [[shared_library]], and shared
modules have other characteristics that make them incompatible with linking, such as a lack of
SONAME. Linking to shared modules also does not work on some platforms, such as on macOS / iOS.
posargs_inherit: _build_target_base posargs_inherit: _build_target_base
varargs_inherit: _build_target_base varargs_inherit: _build_target_base
kwargs_inherit: _build_target_base kwargs_inherit: _build_target_base

@ -2785,7 +2785,7 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
commands += linker.get_std_shared_lib_link_args() commands += linker.get_std_shared_lib_link_args()
# All shared libraries are PIC # All shared libraries are PIC
commands += linker.get_pic_args() commands += linker.get_pic_args()
if not isinstance(target, build.SharedModule): if not isinstance(target, build.SharedModule) or target.backwards_compat_want_soname:
# Add -Wl,-soname arguments on Linux, -install_name on OS X # Add -Wl,-soname arguments on Linux, -install_name on OS X
commands += linker.get_soname_args( commands += linker.get_soname_args(
self.environment, target.prefix, target.name, target.suffix, self.environment, target.prefix, target.name, target.suffix,

@ -1574,11 +1574,16 @@ You probably should put it in link_with instead.''')
if isinstance(link_target, SharedModule): if isinstance(link_target, SharedModule):
if self.environment.machines[self.for_machine].is_darwin(): if self.environment.machines[self.for_machine].is_darwin():
raise MesonException( raise MesonException(
'target links against shared modules. This is not permitted on OSX') f'target {self.name} links against shared module {link_target.name}. This is not permitted on OSX')
else: else:
mlog.warning('target links against shared modules. This ' mlog.deprecation(f'target {self.name} links against shared module {link_target.name}, which is incorrect.'
'is not recommended as it is not supported on some ' '\n '
'platforms') f'This will be an error in the future, so please use shared_library() for {link_target.name} instead.'
'\n '
f'If shared_module() was used for {link_target.name} because it has references to undefined symbols,'
'\n '
'use shared_libary() with `override_options: [\'b_lundef=false\']` instead.')
link_target.backwards_compat_want_soname = True
return return
class Generator(HoldableObject): class Generator(HoldableObject):
@ -2235,6 +2240,9 @@ class SharedModule(SharedLibrary):
raise MesonException('Shared modules must not specify the soversion kwarg.') raise MesonException('Shared modules must not specify the soversion kwarg.')
super().__init__(name, subdir, subproject, for_machine, sources, objects, environment, kwargs) super().__init__(name, subdir, subproject, for_machine, sources, objects, environment, kwargs)
self.typename = 'shared module' self.typename = 'shared module'
# We need to set the soname in cases where build files link the module
# to build targets, see: https://github.com/mesonbuild/meson/issues/9492
self.backwards_compat_want_soname = False
def get_default_install_dir(self, environment) -> T.Tuple[str, str]: def get_default_install_dir(self, environment) -> T.Tuple[str, str]:
return environment.get_shared_module_dir(), '{moduledir_shared}' return environment.get_shared_module_dir(), '{moduledir_shared}'

@ -1,7 +1,7 @@
{ {
"stdout": [ "stdout": [
{ {
"line": "test cases/failing/75 link with shared module on osx/meson.build:8:0: ERROR: target links against shared modules. This is not permitted on OSX" "line": "test cases/failing/75 link with shared module on osx/meson.build:8:0: ERROR: target prog links against shared module mymodule. This is not permitted on OSX"
} }
] ]
} }

@ -0,0 +1,5 @@
int versioned_func (void);
int main (void) {
return versioned_func();
}

@ -20,3 +20,16 @@ shared_library('settosame', 'versioned.c',
install : true, install : true,
soversion : '7.8.9', soversion : '7.8.9',
version : '7.8.9') version : '7.8.9')
shared_module('some_module', 'versioned.c',
install: true)
module1 = shared_module('linked_module1', 'versioned.c',
install: true)
module2 = shared_module('linked_module2', 'versioned.c',
install: true)
module2_dep = declare_dependency(link_with: module2)
executable('main1', 'main.c', link_with: module1)
executable('main2', 'main.c', dependencies: module2_dep)

@ -1950,8 +1950,10 @@ class AllPlatformTests(BasePlatformTests):
""" """
tdir = os.path.join(self.unit_test_dir, '30 shared_mod linking') tdir = os.path.join(self.unit_test_dir, '30 shared_mod linking')
out = self.init(tdir) out = self.init(tdir)
msg = ('WARNING: target links against shared modules. This is not ' msg = ('''DEPRECATION: target prog links against shared module mymod, which is incorrect.
'recommended as it is not supported on some platforms') This will be an error in the future, so please use shared_library() for mymod instead.
If shared_module() was used for mymod because it has references to undefined symbols,
use shared_libary() with `override_options: ['b_lundef=false']` instead.''')
self.assertIn(msg, out) self.assertIn(msg, out)
def test_mixed_language_linker_check(self): def test_mixed_language_linker_check(self):

@ -438,6 +438,24 @@ class LinuxlikeTests(BasePlatformTests):
self.assertEqual(get_soname(bothset), 'libbothset.so.1.2.3') self.assertEqual(get_soname(bothset), 'libbothset.so.1.2.3')
self.assertEqual(len(self.glob_sofiles_without_privdir(bothset[:-3] + '*')), 3) self.assertEqual(len(self.glob_sofiles_without_privdir(bothset[:-3] + '*')), 3)
# A shared_module that is not linked to anything
module = os.path.join(libpath, 'libsome_module.so')
self.assertPathExists(module)
self.assertFalse(os.path.islink(module))
self.assertEqual(get_soname(module), None)
# A shared_module that is not linked to an executable with link_with:
module = os.path.join(libpath, 'liblinked_module1.so')
self.assertPathExists(module)
self.assertFalse(os.path.islink(module))
self.assertEqual(get_soname(module), 'liblinked_module1.so')
# A shared_module that is not linked to an executable with dependencies:
module = os.path.join(libpath, 'liblinked_module2.so')
self.assertPathExists(module)
self.assertFalse(os.path.islink(module))
self.assertEqual(get_soname(module), 'liblinked_module2.so')
def test_soname(self): def test_soname(self):
self._test_soname_impl(self.builddir, False) self._test_soname_impl(self.builddir, False)

Loading…
Cancel
Save