Skip to content

Commit

Permalink
fix generate_gir with BothLibraries dependency
Browse files Browse the repository at this point in the history
Co-authored-by: Xavier Claessens <[email protected]>
  • Loading branch information
bruchar1 and xclaesse committed Nov 5, 2024
1 parent db82c2d commit 6fb0b26
Show file tree
Hide file tree
Showing 10 changed files with 100 additions and 36 deletions.
46 changes: 25 additions & 21 deletions mesonbuild/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -768,6 +768,7 @@ def __init__(
}
self.pic = False
self.pie = False
self.both_lib: T.Optional[T.Union[StaticLibrary, SharedLibrary]] = None
# Track build_rpath entries so we can remove them at install time
self.rpath_dirs_to_remove: T.Set[bytes] = set()
self.process_sourcelist(sources)
Expand Down Expand Up @@ -1735,16 +1736,14 @@ def process_vs_module_defs_kw(self, kwargs: T.Dict[str, T.Any]) -> None:
def extract_targets_as_list(self, kwargs: T.Dict[str, T.Union[LibTypes, T.Sequence[LibTypes]]], key: T.Literal['link_with', 'link_whole']) -> T.List[LibTypes]:
bl_type = self.environment.coredata.get_option(OptionKey('default_both_libraries'))
if bl_type == 'auto':
bl_type = 'static' if isinstance(self, StaticLibrary) else 'shared'

def _resolve_both_libs(lib: LibTypes) -> LibTypes:
if isinstance(lib, BothLibraries):
return lib.get(bl_type)
return lib
if isinstance(self, StaticLibrary):
bl_type = 'static'
elif isinstance(self, SharedLibrary):
bl_type = 'shared'

self_libs: T.List[LibTypes] = self.link_targets if key == 'link_with' else self.link_whole_targets
lib_list = listify(kwargs.get(key, [])) + self_libs
return [_resolve_both_libs(t) for t in lib_list]
return [t.get(bl_type) for t in lib_list]

def get(self, lib_type: T.Literal['static', 'shared', 'auto']) -> LibTypes:
"""Base case used by BothLibraries"""
Expand Down Expand Up @@ -2199,6 +2198,14 @@ def is_linkable_target(self):
def is_internal(self) -> bool:
return not self.install

def set_shared(self, shared_library: SharedLibrary) -> None:
self.both_lib = shared_library

def get(self, lib_type: T.Literal['static', 'shared', 'auto']) -> LibTypes:
if lib_type == 'shared':
return self.both_lib or self
return self

class SharedLibrary(BuildTarget):
known_kwargs = known_shlib_kwargs

Expand Down Expand Up @@ -2228,8 +2235,6 @@ def __init__(
self.import_filename = None
# The debugging information file this target will generate
self.debug_filename = None
# Use by the pkgconfig module
self.shared_library_only = False
super().__init__(name, subdir, subproject, for_machine, sources, structured_sources, objects,
environment, compilers, kwargs)

Expand Down Expand Up @@ -2465,6 +2470,14 @@ def type_suffix(self):
def is_linkable_target(self):
return True

def set_static(self, static_library: StaticLibrary) -> None:
self.both_lib = static_library

def get(self, lib_type: T.Literal['static', 'shared']) -> LibTypes:
if lib_type == 'static':
return self.both_lib or self
return self

# A shared library that is meant to be used with dlopen rather than linking
# into something else.
class SharedModule(SharedLibrary):
Expand Down Expand Up @@ -2501,7 +2514,7 @@ def get_default_install_dir(self) -> T.Union[T.Tuple[str, str], T.Tuple[None, No
return self.environment.get_shared_module_dir(), '{moduledir_shared}'

class BothLibraries(SecondLevelHolder):
def __init__(self, shared: SharedLibrary, static: StaticLibrary, preferred_library: Literal['shared', 'static', 'auto']) -> None:
def __init__(self, shared: SharedLibrary, static: StaticLibrary, preferred_library: Literal['shared', 'static']) -> None:
self._preferred_library = preferred_library
self.shared = shared
self.static = static
Expand Down Expand Up @@ -2909,23 +2922,14 @@ class AliasTarget(RunTarget):

typename = 'alias'

def __init__(self, name: str, dependencies: T.Sequence[T.Union[Target, BothLibraries]],
def __init__(self, name: str, dependencies: T.Sequence[Target],
subdir: str, subproject: str, environment: environment.Environment):
super().__init__(name, [], list(self._deps_generator(dependencies)), subdir, subproject, environment)
super().__init__(name, [], dependencies, subdir, subproject, environment)

def __repr__(self):
repr_str = "<{0} {1}>"
return repr_str.format(self.__class__.__name__, self.get_id())

@staticmethod
def _deps_generator(dependencies: T.Sequence[T.Union[Target, BothLibraries]]) -> T.Iterator[Target]:
for dep in dependencies:
if isinstance(dep, BothLibraries):
yield dep.shared
yield dep.static
else:
yield dep

class Jar(BuildTarget):
known_kwargs = known_jar_kwargs

Expand Down
26 changes: 14 additions & 12 deletions mesonbuild/interpreter/interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
from ..interpreterbase import Disabler, disablerIfNotFound
from ..interpreterbase import FeatureNew, FeatureDeprecated, FeatureBroken, FeatureNewKwargs
from ..interpreterbase import ObjectHolder, ContextManagerObject
from ..interpreterbase import stringifyUserArguments, resolve_second_level_holders
from ..interpreterbase import stringifyUserArguments
from ..modules import ExtensionModule, ModuleObject, MutableModuleObject, NewExtensionModule, NotFoundExtensionModule
from ..optinterpreter import optname_regex

Expand Down Expand Up @@ -690,7 +690,6 @@ def func_files(self, node: mparser.FunctionNode, args: T.Tuple[T.List[str]], kwa
KwargInfo('version', (str, NoneType)),
KwargInfo('objects', ContainerTypeInfo(list, build.ExtractedObjects), listify=True, default=[], since='1.1.0'),
)
@noSecondLevelHolderResolving
def func_declare_dependency(self, node: mparser.BaseNode, args: T.List[TYPE_var],
kwargs: kwtypes.FuncDeclareDependency) -> dependencies.Dependency:
deps = kwargs['dependencies']
Expand Down Expand Up @@ -1873,9 +1872,7 @@ def func_static_lib(self, node: mparser.BaseNode,
def func_shared_lib(self, node: mparser.BaseNode,
args: T.Tuple[str, SourcesVarargsType],
kwargs: kwtypes.SharedLibrary) -> build.SharedLibrary:
holder = self.build_target(node, args, kwargs, build.SharedLibrary)
holder.shared_library_only = True
return holder
return self.build_target(node, args, kwargs, build.SharedLibrary)

@permittedKwargs(known_library_kwargs)
@typed_pos_args('both_libraries', str, varargs=SOURCES_VARARGS)
Expand Down Expand Up @@ -1916,15 +1913,12 @@ def func_jar(self, node: mparser.BaseNode,
@permittedKwargs(known_build_target_kwargs)
@typed_pos_args('build_target', str, varargs=SOURCES_VARARGS)
@typed_kwargs('build_target', *BUILD_TARGET_KWS, allow_unknown=True)
@noSecondLevelHolderResolving
def func_build_target(self, node: mparser.BaseNode,
args: T.Tuple[str, SourcesVarargsType],
kwargs: kwtypes.BuildTarget
) -> T.Union[build.Executable, build.StaticLibrary, build.SharedLibrary,
build.SharedModule, build.BothLibraries, build.Jar]:
target_type = kwargs['target_type']
if target_type not in {'both_libraries', 'library'}:
args, kwargs = resolve_second_level_holders(args, kwargs)

if target_type == 'executable':
return self.build_target(node, args, kwargs, build.Executable)
Expand Down Expand Up @@ -2171,13 +2165,19 @@ def func_run_target(self, node: mparser.FunctionNode, args: T.Tuple[str],
@FeatureNew('alias_target', '0.52.0')
@typed_pos_args('alias_target', str, varargs=(build.Target, build.BothLibraries), min_varargs=1)
@noKwargs
@noSecondLevelHolderResolving
def func_alias_target(self, node: mparser.BaseNode, args: T.Tuple[str, T.List[T.Union[build.Target, build.BothLibraries]]],
kwargs: TYPE_kwargs) -> build.AliasTarget:
name, deps = args
if any(isinstance(d, build.RunTarget) for d in deps):
FeatureNew.single_use('alias_target that depends on run_targets', '0.60.0', self.subproject)
tg = build.AliasTarget(name, deps, self.subdir, self.subproject, self.environment)
real_deps: T.List[build.Target] = []
for d in deps:
if isinstance(d, build.BothLibraries):
real_deps.append(d.shared)
real_deps.append(d.static)
else:
real_deps.append(d)
tg = build.AliasTarget(name, real_deps, self.subdir, self.subproject, self.environment)
self.add_target(name, tg)
return tg

Expand Down Expand Up @@ -3283,16 +3283,18 @@ def build_both_libraries(self, node: mparser.BaseNode, args: T.Tuple[str, Source
# Keep only compilers used for linking
static_lib.compilers = {k: v for k, v in static_lib.compilers.items() if k in compilers.clink_langs}

# Cross reference them to implement as_shared() and as_static() methods.
shared_lib.set_static(static_lib)
static_lib.set_shared(shared_lib)

return build.BothLibraries(shared_lib, static_lib, preferred_library)

def build_library(self, node: mparser.BaseNode, args: T.Tuple[str, SourcesVarargsType], kwargs: kwtypes.Library):
default_library = self.coredata.get_option(OptionKey('default_library', subproject=self.subproject))
assert isinstance(default_library, str), 'for mypy'
if default_library == 'shared':
args, kwargs = resolve_second_level_holders(args, kwargs)
return self.build_target(node, args, T.cast('kwtypes.StaticLibrary', kwargs), build.SharedLibrary)
elif default_library == 'static':
args, kwargs = resolve_second_level_holders(args, kwargs)
return self.build_target(node, args, T.cast('kwtypes.SharedLibrary', kwargs), build.StaticLibrary)
elif default_library == 'both':
return self.build_both_libraries(node, args, kwargs)
Expand Down
2 changes: 0 additions & 2 deletions mesonbuild/interpreter/interpreterobjects.py
Original file line number Diff line number Diff line change
Expand Up @@ -1001,8 +1001,6 @@ class SharedLibraryHolder(BuildTargetHolder[build.SharedLibrary]):

class BothLibrariesHolder(BuildTargetHolder[build.BothLibraries]):
def __init__(self, libs: build.BothLibraries, interp: 'Interpreter'):
# FIXME: This build target always represents the shared library, but
# that should be configurable.
super().__init__(libs, interp)
self.methods.update({'get_shared_lib': self.get_shared_lib_method,
'get_static_lib': self.get_static_lib_method,
Expand Down
2 changes: 1 addition & 1 deletion mesonbuild/modules/pkgconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ def _process_libs(
if obj.found():
processed_libs += obj.get_link_args()
processed_cflags += obj.get_compile_args()
elif isinstance(obj, build.SharedLibrary) and obj.shared_library_only:
elif isinstance(obj, build.SharedLibrary) and obj.both_lib is None:
# Do not pull dependencies for shared libraries because they are
# only required for static linking. Adding private requires has
# the side effect of exposing their cflags, which is the
Expand Down
7 changes: 7 additions & 0 deletions test cases/frameworks/38 gir both_libraries/bar.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#include "bar.h"
#include "foo.h"

int bar_func(void)
{
return foo_func() + 42;
}
1 change: 1 addition & 0 deletions test cases/frameworks/38 gir both_libraries/bar.h
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
int bar_func(void);
6 changes: 6 additions & 0 deletions test cases/frameworks/38 gir both_libraries/foo.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#include "foo.h"

int foo_func(void)
{
return 42;
}
1 change: 1 addition & 0 deletions test cases/frameworks/38 gir both_libraries/foo.h
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
int foo_func(void);
42 changes: 42 additions & 0 deletions test cases/frameworks/38 gir both_libraries/meson.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
project('gir both libraries', 'c')

gir = dependency('gobject-introspection-1.0', required: false)
if not gir.found()
error('MESON_SKIP_TEST gobject-introspection not found.')
endif

if host_machine.system() == 'cygwin'
# FIXME: g-ir-scanner seems broken on cygwin:
# ERROR: can't resolve libraries to shared libraries: foo++
error('MESON_SKIP_TEST g-ir-scanner is broken on cygwin.')
endif

gnome = import('gnome')

# Regression test simulating how GStreamer generate its GIRs.
# Generated gobject-introspection binaries for every GStreamer libraries must
# first call gst_init() defined in the main libgstreamer, which means they need
# to link on that lib.
# A regression caused by https://github.com/mesonbuild/meson/pull/12632 made
# Meson not link the binary generated for bar with libfoo in the case it uses
# both_libraries().

libfoo = both_libraries('foo', 'foo.c')
foo_gir = gnome.generate_gir(libfoo,
namespace: 'foo',
nsversion: '1.0',
sources: ['foo.c', 'foo.h'],
)
foo_dep = declare_dependency(
link_with: libfoo,
sources: foo_gir,
)

libbar = both_libraries('bar', 'bar.c', dependencies: foo_dep)
gnome.generate_gir(libbar,
namespace: 'bar',
nsversion: '1.0',
sources: ['bar.c', 'bar.h'],
extra_args: '--add-init-section=extern void foo_func(void);foo_func();',
dependencies: foo_dep,
)
3 changes: 3 additions & 0 deletions test cases/frameworks/38 gir both_libraries/test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"expect_skip_on_jobname": ["azure", "macos", "msys2", "cygwin"]
}

0 comments on commit 6fb0b26

Please sign in to comment.