Skip to content

Commit

Permalink
Framework target link support
Browse files Browse the repository at this point in the history
  • Loading branch information
2xsaiko committed Jan 25, 2025
1 parent f753b6c commit 3825453
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 7 deletions.
29 changes: 23 additions & 6 deletions mesonbuild/backend/backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from .. import mesonlib
from .. import mlog
from ..compilers import LANGUAGES_USING_LDFLAGS, detect, lang_suffixes
from ..compilers.mixins.clang import ClangCompiler
from ..mesonlib import (
File, MachineChoice, MesonException, OrderedSet,
ExecutableSerialisation, EnvironmentException,
Expand Down Expand Up @@ -343,22 +344,30 @@ def get_build_dir_include_args(self, target: build.BuildTarget, compiler: 'Compi
return compiler.get_include_args(curdir, False)

def get_target_filename_for_linking(self, target: T.Union[build.Target, build.CustomTargetIndex]) -> T.Optional[str]:
if isinstance(target, (build.BuildTarget, build.CustomTarget, build.CustomTargetIndex)):
target_filename = target.get_filename()

if isinstance(target, build.BundleTarget):
# TODO: what's the difference linking a framework like this vs. the bespoke framework arg on macOS's ld?
# (this also has the advantage of working on other platforms!)
target_filename = os.path.join(target_filename, target.get_bundle_info().get_executable_path())

# On some platforms (msvc for instance), the file that is used for
# dynamic linking is not the same as the dynamic library itself. This
# file is called an import library, and we want to link against that.
# On all other platforms, we link to the library directly.
if isinstance(target, build.SharedLibrary):
link_lib = target.get_import_filename() or target.get_filename()
link_lib = target.get_import_filename() or target_filename
# In AIX, if we archive .so, the blibpath must link to archived shared library otherwise to the .so file.
if mesonlib.is_aix() and target.aix_so_archive:
link_lib = re.sub('[.][a]([.]?([0-9]+))*([.]?([a-z]+))*', '.a', link_lib.replace('.so', '.a'))
return Path(self.get_target_dir(target), link_lib).as_posix()
elif isinstance(target, build.StaticLibrary):
return Path(self.get_target_dir(target), target.get_filename()).as_posix()
return Path(self.get_target_dir(target), target_filename).as_posix()
elif isinstance(target, (build.CustomTarget, build.CustomTargetIndex)):
if not target.is_linkable_target():
raise MesonException(f'Tried to link against custom target "{target.name}", which is not linkable.')
return Path(self.get_target_dir(target), target.get_filename()).as_posix()
return Path(self.get_target_dir(target), target_filename).as_posix()
elif isinstance(target, build.Executable):
if target.import_filename:
return Path(self.get_target_dir(target), target.get_import_filename()).as_posix()
Expand Down Expand Up @@ -817,6 +826,7 @@ def determine_rpath_dirs(self, target: T.Union[build.BuildTarget, build.CustomTa
# Need a copy here
result = OrderedSet(target.get_link_dep_subdirs())
else:
# TODO: Bundle handling
result = OrderedSet()
result.add('meson-out')
if isinstance(target, build.BuildTarget):
Expand Down Expand Up @@ -1077,11 +1087,18 @@ def generate_basic_compiler_args(self, target: build.BuildTarget, compiler: 'Com
commands += dep.get_exe_args(compiler)
# For 'automagic' deps: Boost and GTest. Also dependency('threads').
# pkg-config puts the thread flags itself via `Cflags:`
# Fortran requires extra include directives.
if compiler.language == 'fortran':
for lt in chain(target.link_targets, target.link_whole_targets):

for lt in chain(target.link_targets, target.link_whole_targets):
# Fortran requires extra include directives.
if compiler.language == 'fortran':
priv_dir = self.get_target_private_dir(lt)
commands += compiler.get_include_args(priv_dir, False)

# Add linked frameworks to include path
if isinstance(lt, build.FrameworkBundle):
# TODO: Handle this in the compiler class?
assert isinstance(compiler, ClangCompiler), 'Linking against frameworks requires clang'
commands += [f'-F{self.get_target_dir(lt)}', '-framework', lt.get_basename()]
return commands

def build_target_link_arguments(self, compiler: 'Compiler', deps: T.List[build.Target]) -> T.List[str]:
Expand Down
14 changes: 14 additions & 0 deletions mesonbuild/backend/ninjabackend.py
Original file line number Diff line number Diff line change
Expand Up @@ -3248,6 +3248,15 @@ def generate_single_compile(self, target: build.BuildTarget, src,
element.add_dep(pch_dep)
for i in self.get_fortran_orderdeps(target, compiler):
element.add_orderdep(i)

for lt in itertools.chain(target.link_targets, target.link_whole_targets):
# Linking against frameworks also pulls in its headers, therefore wait for it to be assembled. This is not
# exactly ideal, since it will also wait for the library to be linked, but it's better than no dep at all.
# TODO: check whether linking against framework is possible when only headers have been installed into the
# bundle
if isinstance(lt, build.FrameworkBundle):
element.add_orderdep(self.get_target_filename(lt))

if dep_file:
element.add_item('DEPFILE', dep_file)
if compiler.get_language() == 'cuda':
Expand Down Expand Up @@ -3684,6 +3693,11 @@ def generate_link(self, target: build.BuildTarget, outname, obj_list, linker: T.
self.get_target_dir(target))
else:
target_slashname_workaround_dir = self.get_target_dir(target)

if isinstance(target, build.BundleTarget):
target_slashname_workaround_dir = str(PurePath() / target_slashname_workaround_dir / target.get_filename() /
target.get_bundle_info().get_executable_folder_path())

(rpath_args, target.rpath_dirs_to_remove) = (
linker.build_rpath_args(self.environment,
self.environment.get_build_dir(),
Expand Down
5 changes: 4 additions & 1 deletion mesonbuild/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -1127,7 +1127,10 @@ def get_transitive_link_deps_mapping(self, prefix: str) -> T.Mapping[str, str]:
def get_link_dep_subdirs(self) -> T.AbstractSet[str]:
result: OrderedSet[str] = OrderedSet()
for i in self.link_targets:
if not isinstance(i, StaticLibrary):
if isinstance(i, FrameworkBundle):
result.add(str(pathlib.PurePath() / i.get_subdir() / i.get_filename() /
i.get_bundle_info().get_executable_folder_path()))
elif not isinstance(i, StaticLibrary):
result.add(i.get_subdir())
result.update(i.get_link_dep_subdirs())
return result
Expand Down

0 comments on commit 3825453

Please sign in to comment.