Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 88 additions & 72 deletions mesonbuild/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
GeneratedTypes: TypeAlias = T.Union['CustomTarget', 'CustomTargetIndex', 'GeneratedList']
LibTypes: TypeAlias = T.Union['SharedLibrary', 'StaticLibrary', 'CustomTarget', 'CustomTargetIndex']
BuildTargetTypes: TypeAlias = T.Union['BuildTarget', 'CustomTarget', 'CustomTargetIndex']
StaticTargetTypes: TypeAlias = T.Union['StaticLibrary', 'CustomTarget', 'CustomTargetIndex']
ObjectTypes: TypeAlias = T.Union[str, 'File', 'ExtractedObjects', 'GeneratedTypes']
AnyTargetType: TypeAlias = T.Union['Target', 'CustomTargetIndex']
RustCrateType: TypeAlias = Literal['bin', 'lib', 'rlib', 'dylib', 'cdylib', 'staticlib', 'proc-macro']
Expand Down Expand Up @@ -90,7 +91,7 @@ class BuildTargetKeywordArguments(TypedDict, total=False):
link_args: T.List[str]
link_depends: T.List[T.Union[str, File, CustomTarget, CustomTargetIndex]]
link_language: str
link_whole: T.List[T.Union[StaticLibrary, CustomTarget, CustomTargetIndex]]
link_whole: T.List[StaticTargetTypes]
link_with: T.List[BuildTargetTypes]
name_prefix: T.Optional[str]
name_suffix: T.Optional[str]
Expand Down Expand Up @@ -795,8 +796,8 @@ def __init__(
self.external_deps: T.List[dependencies.Dependency] = []
self.include_dirs: T.List['IncludeDirs'] = []
self.link_language = kwargs.get('link_language')
self.link_targets: T.List[LibTypes] = []
self.link_whole_targets: T.List[T.Union[StaticLibrary, CustomTarget, CustomTargetIndex]] = []
self.link_targets: T.List[BuildTargetTypes] = []
self.link_whole_targets: T.List[StaticTargetTypes] = []
self.depend_files: T.List[File] = []
self.link_depends: T.List[T.Union[File, BuildTargetTypes]] = []
self.added_deps = set()
Expand Down Expand Up @@ -1492,62 +1493,24 @@ def is_internal(self) -> bool:

def link(self, targets: T.List[BuildTargetTypes]) -> None:
for t in targets:
if not isinstance(t, (Target, CustomTargetIndex)):
if isinstance(t, dependencies.ExternalLibrary):
raise MesonException(textwrap.dedent('''\
An external library was used in link_with keyword argument, which
is reserved for libraries built as part of this project. External
libraries must be passed using the dependencies keyword argument
instead, because they are conceptually "external dependencies",
just like those detected with the dependency() function.
'''))
raise InvalidArguments(f'{t!r} is not a target.')
if not t.is_linkable_target():
raise InvalidArguments(f"Link target '{t!s}' is not linkable.")
if isinstance(self, StaticLibrary) and self.install and t.is_internal():
# When we're a static library and we link_with to an
# internal/convenience library, promote to link_whole.
self.link_whole([t], promoted=True)
continue
if isinstance(self, SharedLibrary) and isinstance(t, StaticLibrary) and not t.pic:
msg = f"Can't link non-PIC static library {t.name!r} into shared library {self.name!r}. "
msg += "Use the 'pic' option to static_library to build with PIC."
raise InvalidArguments(msg)
self.check_can_link_together(t)
self.link_targets.append(t)

def link_whole(self, targets: T.List[BuildTargetTypes], promoted: bool = False) -> None:
def link_whole(
self,
targets: T.List[StaticTargetTypes],
promoted: bool = False) -> None:
for t in targets:
if isinstance(t, (CustomTarget, CustomTargetIndex)):
if not t.is_linkable_target():
raise InvalidArguments(f'Custom target {t!r} is not linkable.')
if t.links_dynamically():
raise InvalidArguments('Can only link_whole custom targets that are static archives.')
elif not isinstance(t, StaticLibrary):
raise InvalidArguments(f'{t!r} is not a static library.')
elif isinstance(self, SharedLibrary) and not t.pic:
msg = f"Can't link non-PIC static library {t.name!r} into shared library {self.name!r}. "
msg += "Use the 'pic' option to static_library to build with PIC."
raise InvalidArguments(msg)
self.check_can_link_together(t)
if isinstance(self, StaticLibrary):
# When we're a static library and we link_whole: to another static
# library, we need to add that target's objects to ourselves.
self._bundle_static_library(t, promoted)
# If we install this static library we also need to include objects
# from all uninstalled static libraries it depends on.
if self.install:
for lib in t.get_internal_static_libraries():
self._bundle_static_library(lib, True)
self.link_whole_targets.append(t)

@lru_cache(maxsize=None)
def get_internal_static_libraries(self) -> OrderedSet[BuildTargetTypes]:
result: OrderedSet[BuildTargetTypes] = OrderedSet()
def get_internal_static_libraries(self) -> OrderedSet[StaticTargetTypes]:
result: OrderedSet[StaticTargetTypes] = OrderedSet()
self.get_internal_static_libraries_recurse(result)
return result

def get_internal_static_libraries_recurse(self, result: OrderedSet[BuildTargetTypes]) -> None:
def get_internal_static_libraries_recurse(self, result: OrderedSet[StaticTargetTypes]) -> None:
for t in self.link_targets:
if t.is_internal() and t not in result:
result.add(t)
Expand All @@ -1556,28 +1519,6 @@ def get_internal_static_libraries_recurse(self, result: OrderedSet[BuildTargetTy
if t.is_internal():
t.get_internal_static_libraries_recurse(result)

def _bundle_static_library(self, t: T.Union[BuildTargetTypes], promoted: bool = False) -> None:
if self.uses_rust():
# Rustc can bundle static libraries, no need to extract objects.
self.link_whole_targets.append(t)
elif isinstance(t, (CustomTarget, CustomTargetIndex)) or t.uses_rust():
# To extract objects from a custom target we would have to extract
# the archive, WIP implementation can be found in
# https://github.com/mesonbuild/meson/pull/9218.
# For Rust C ABI we could in theory have access to objects, but there
# are several meson issues that need to be fixed:
# https://github.com/mesonbuild/meson/issues/10722
# https://github.com/mesonbuild/meson/issues/10723
# https://github.com/mesonbuild/meson/issues/10724
m = (f'Cannot link_whole a custom or Rust target {t.name!r} into a static library {self.name!r}. '
'Instead, pass individual object files with the "objects:" keyword argument if possible.')
if promoted:
m += (f' Meson had to promote link to link_whole because {self.name!r} is installed but not {t.name!r},'
f' and thus has to include objects from {t.name!r} to be usable.')
raise InvalidArguments(m)
else:
self.objects.append(t.extract_all_objects())

def check_can_link_together(self, t: BuildTargetTypes) -> None:
links_with_rust_abi = isinstance(t, BuildTarget) and t.uses_rust_abi()
if not self.uses_rust() and links_with_rust_abi:
Expand Down Expand Up @@ -2409,6 +2350,56 @@ def get(self, lib_type: T.Literal['static', 'shared'], recursive: bool = False)
result.link_targets = [t.get(lib_type, True) for t in self.link_targets]
return result

def link_whole(
self,
targets: T.List[StaticTargetTypes],
promoted: bool = False) -> None:
for t in targets:
self.check_can_link_together(t)

# When we're a static library and we link_whole: to another static
# library, we need to add that target's objects to ourselves.
self._bundle_static_library(t, promoted)

# If we install this static library we also need to include objects
# from all uninstalled static libraries it depends on.
if self.install:
for lib in t.get_internal_static_libraries():
self._bundle_static_library(lib, True)
self.link_whole_targets.append(t)

def link(self, targets: T.List[BuildTargetTypes]) -> None:
for t in targets:
if self.install and t.is_internal():
# When we're a static library and we link_with to an
# internal/convenience library, promote to link_whole.
self.link_whole([t], promoted=True)
continue
self.check_can_link_together(t)
self.link_targets.append(t)

def _bundle_static_library(self, t: StaticTargetTypes, promoted: bool = False) -> None:
if self.uses_rust():
# Rustc can bundle static libraries, no need to extract objects.
self.link_whole_targets.append(t)
elif isinstance(t, (CustomTarget, CustomTargetIndex)) or t.uses_rust():
# To extract objects from a custom target we would have to extract
# the archive, WIP implementation can be found in
# https://github.com/mesonbuild/meson/pull/9218.
# For Rust C ABI we could in theory have access to objects, but there
# are several meson issues that need to be fixed:
# https://github.com/mesonbuild/meson/issues/10722
# https://github.com/mesonbuild/meson/issues/10723
# https://github.com/mesonbuild/meson/issues/10724
m = (f'Cannot link_whole a custom or Rust target {t.name!r} into a static library {self.name!r}. '
'Instead, pass individual object files with the "objects:" keyword argument if possible.')
if promoted:
m += (f' Meson had to promote link to link_whole because {self.name!r} is installed but not {t.name!r},'
f' and thus has to include objects from {t.name!r} to be usable.')
raise InvalidArguments(m)
else:
self.objects.append(t.extract_all_objects())

class SharedLibrary(BuildTarget):
known_kwargs = known_shlib_kwargs

Expand Down Expand Up @@ -2713,6 +2704,27 @@ def get(self, lib_type: T.Literal['static', 'shared'], recursive: bool = False)
result.link_targets = [t.get(lib_type, True) for t in self.link_targets]
return result

def link_whole(
self,
targets: T.List[StaticTargetTypes],
promoted: bool = False) -> None:
for t in targets:
self.check_can_link_together(t)
if not getattr(t, 'pic', True):
msg = f"Can't link non-PIC static library {t.name!r} into shared library {self.name!r}. "
msg += "Use the 'pic' option to static_library to build with PIC."
raise InvalidArguments(msg)
self.link_whole_targets.append(t)

def link(self, targets: T.List[BuildTargetTypes]) -> None:
for t in targets:
if isinstance(t, StaticLibrary) and not t.pic:
msg = f"Can't link non-PIC static library {t.name!r} into shared library {self.name!r}. "
msg += "Use the 'pic' option to static_library to build with PIC."
raise InvalidArguments(msg)
self.check_can_link_together(t)
self.link_targets.append(t)

# A shared library that is meant to be used with dlopen rather than linking
# into something else.
class SharedModule(SharedLibrary):
Expand Down Expand Up @@ -2775,6 +2787,10 @@ def get_default_object(self) -> T.Union[StaticLibrary, SharedLibrary]:
def get_id(self) -> str:
return self.get_default_object().get_id()

def is_linkable_target(self) -> bool:
# For polymorphism with build targets
return True

class CommandBase:

depend_files: T.List[File]
Expand Down Expand Up @@ -2827,10 +2843,10 @@ class CustomTargetBase:
def get_dependencies_recurse(self, result: OrderedSet[BuildTargetTypes], include_internals: bool = True) -> None:
pass

def get_internal_static_libraries(self) -> OrderedSet[BuildTargetTypes]:
def get_internal_static_libraries(self) -> OrderedSet[StaticTargetTypes]:
return OrderedSet()

def get_internal_static_libraries_recurse(self, result: OrderedSet[BuildTargetTypes]) -> None:
def get_internal_static_libraries_recurse(self, result: OrderedSet[StaticTargetTypes]) -> None:
pass

def get_all_linked_targets(self) -> ImmutableListProtocol[BuildTargetTypes]:
Expand Down
6 changes: 4 additions & 2 deletions mesonbuild/interpreter/kwargs.py
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,8 @@ class _BaseBuildTarget(TypedDict):
implicit_include_directories: bool
link_depends: T.List[T.Union[str, File, build.GeneratedTypes]]
link_language: T.Optional[str]
link_whole: T.List[build.StaticTargetTypes]
link_with: T.List[build.BuildTargetTypes]
name_prefix: T.Optional[str]
name_suffix: T.Optional[str]
native: MachineChoice
Expand Down Expand Up @@ -488,8 +490,8 @@ class FuncDeclareDependency(TypedDict):
extra_files: T.List[FileOrString]
include_directories: T.List[T.Union[build.IncludeDirs, str]]
link_args: T.List[str]
link_whole: T.List[T.Union[build.StaticLibrary, build.CustomTarget, build.CustomTargetIndex]]
link_with: T.List[build.LibTypes]
link_whole: T.List[build.StaticTargetTypes]
link_with: T.List[build.BuildTargetTypes]
objects: T.List[build.ExtractedObjects]
sources: T.List[T.Union[FileOrString, build.GeneratedTypes]]
variables: T.Dict[str, str]
Expand Down
21 changes: 18 additions & 3 deletions mesonbuild/interpreter/type_checking.py
Original file line number Diff line number Diff line change
Expand Up @@ -435,23 +435,36 @@ def _default_options_convertor(raw: T.Union[str, T.List[str], T.Dict[str, Elemen
default=[],
)

_link_with_error = '''can only be self-built targets, external dependencies (including libraries) must go in "dependencies".'''
_LINK_WITH_ERROR = '''can only be self-built targets, external dependencies (including libraries) must go in "dependencies".'''

def _link_with_validator(values: T.List[T.Union[BothLibraries, SharedLibrary, StaticLibrary,
CustomTarget, CustomTargetIndex, Jar, Executable,
]]
) -> T.Optional[str]:
for value in values:
if isinstance(value, Dependency):
return _LINK_WITH_ERROR
if not value.is_linkable_target():
return f'Link target "{value!s}" is not linkable'
return None

# Allow Dependency for the better error message? But then in other cases it will list this as one of the allowed types!
LINK_WITH_KW: KwargInfo[T.List[T.Union[BothLibraries, SharedLibrary, StaticLibrary, CustomTarget, CustomTargetIndex, Jar, Executable]]] = KwargInfo(
'link_with',
ContainerTypeInfo(list, (BothLibraries, SharedLibrary, StaticLibrary, CustomTarget, CustomTargetIndex, Jar, Executable, Dependency)),
listify=True,
default=[],
validator=lambda x: _link_with_error if any(isinstance(i, Dependency) for i in x) else None,
validator=_link_with_validator,
)

def link_whole_validator(values: T.List[T.Union[StaticLibrary, CustomTarget, CustomTargetIndex, Dependency]]) -> T.Optional[str]:
for l in values:
if isinstance(l, (CustomTarget, CustomTargetIndex)) and l.links_dynamically():
return f'{type(l).__name__} returning a shared library is not allowed'
if isinstance(l, Dependency):
return _link_with_error
return _LINK_WITH_ERROR
if not l.is_linkable_target():
return f'Link target "{l!s}" is not linkable'
return None

LINK_WHOLE_KW: KwargInfo[T.List[T.Union[BothLibraries, StaticLibrary, CustomTarget, CustomTargetIndex]]] = KwargInfo(
Expand Down Expand Up @@ -715,6 +728,8 @@ def _pch_convertor(args: T.List[str]) -> T.Optional[T.Tuple[str, T.Optional[str]
*_LANGUAGE_KWS,
BT_SOURCES_KW,
INCLUDE_DIRECTORIES.evolve(name='d_import_dirs'),
LINK_WHOLE_KW,
LINK_WITH_KW,
_NAME_PREFIX_KW,
_NAME_PREFIX_KW.evolve(name='name_suffix', validator=_name_suffix_validator),
RUST_CRATE_TYPE_KW,
Expand Down
2 changes: 1 addition & 1 deletion test cases/failing/52 link with executable/test.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"stdout": [
{
"line": "test cases/failing/52 link with executable/meson.build:4:4: ERROR: Link target 'prog' is not linkable."
"line": "test cases/failing/52 link with executable/meson.build:4:4: ERROR: shared_module keyword argument \"link_with\" Link target \"prog\" is not linkable"
}
]
}
2 changes: 1 addition & 1 deletion test cases/failing/59 string as link target/test.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"stdout": [
{
"line": "test cases/failing/59 string as link target/meson.build:2:0: ERROR: '' is not a target."
"line": "test cases/failing/59 string as link target/meson.build:2:0: ERROR: executable keyword argument 'link_with' was of type array[str] but should have been array[BothLibraries | SharedLibrary | StaticLibrary | CustomTarget | CustomTargetIndex | Jar | Executable | Dependency]"
}
]
}
Loading