mirror of
https://github.com/mesonbuild/meson.git
synced 2026-06-30 19:57:45 +00:00
ninjabackend: rust: record Apple frameworks in rlibs
When rustc builds an rlib, it records -l and -L flags in the rlib's metadata. Then when the final binary links against that rlib, rustc knows what native libraries are needed. Right now, frameworks are passed via -Clink-arg=-F and -Clink-arg=-framework, which just passes raw flags to the linker. So if an rlib depends on a framework and is installed, that information is lost and cargo will fail to produce binaries from the rlib. Fixes: #10725
This commit is contained in:
committed by
Dylan Baker
parent
9db31dc6f1
commit
4e9ba3decf
@@ -32,7 +32,7 @@ from ..mesonlib import (
|
||||
File, LibType, MachineChoice, MesonBugException, MesonException, OrderedSet, PerMachine,
|
||||
ProgressBar, quote_arg, unique_list
|
||||
)
|
||||
from ..mesonlib import get_compiler_for_source, has_path_sep, is_parent_path
|
||||
from ..mesonlib import get_compiler_for_source, has_path_sep, is_parent_path, lookbehind
|
||||
from ..options import OptionKey
|
||||
from .backends import CleanTrees
|
||||
from ..build import GeneratedList, InvalidArguments
|
||||
@@ -2151,10 +2151,21 @@ class NinjaBackend(backends.Backend):
|
||||
args.append(f'-Clink-arg={lib}')
|
||||
|
||||
for e in external_deps:
|
||||
for a in e.get_link_args():
|
||||
if a.startswith('-L'):
|
||||
prev: T.Optional[str] = None
|
||||
for prev, a in lookbehind(e.get_link_args()):
|
||||
if prev == '-framework':
|
||||
args.append(f'-lframework={a}')
|
||||
continue
|
||||
elif a.startswith('-L'):
|
||||
args.append(a)
|
||||
continue
|
||||
elif a.startswith('-F'):
|
||||
path = a[2:]
|
||||
args.append(f'-Lframework={path}')
|
||||
continue
|
||||
elif a == '-framework':
|
||||
# handled once the framework name is available
|
||||
continue
|
||||
elif is_library(a):
|
||||
if isinstance(target, build.StaticLibrary):
|
||||
static = a.endswith(('.a', '.lib'))
|
||||
|
||||
@@ -139,6 +139,7 @@ __all__ = [
|
||||
'listify',
|
||||
'listify_array_value',
|
||||
'lookahead',
|
||||
'lookbehind',
|
||||
'partition',
|
||||
'path_is_in_root',
|
||||
'pickle_load',
|
||||
@@ -2536,6 +2537,24 @@ def get_subproject_dir(directory: str = '.') -> T.Optional[str]:
|
||||
return intr.extract_subproject_dir() or 'subprojects'
|
||||
|
||||
|
||||
def lookbehind(it_: T.Iterable[_T]) -> T.Iterator[T.Tuple[T.Optional[_T], _T]]:
|
||||
"""Get the current value of the iterable, and the previous if possible.
|
||||
|
||||
:param iter: The iterable to look into
|
||||
:yield: A tuple of the previous value if possible, and the current one
|
||||
:return: nothing
|
||||
"""
|
||||
prev: T.Optional[_T] = None
|
||||
it: T.Iterator[_T] = iter(it_)
|
||||
while True:
|
||||
try:
|
||||
current = next(it)
|
||||
yield prev, current
|
||||
prev = current
|
||||
except StopIteration:
|
||||
break
|
||||
|
||||
|
||||
def lookahead(it_: T.Iterable[_T]) -> T.Iterator[T.Tuple[_T, T.Optional[_T]]]:
|
||||
"""Get the current value of the iterable, and the next if possible.
|
||||
|
||||
|
||||
7
test cases/rust/35 apple framework/lib.rs
Normal file
7
test cases/rust/35 apple framework/lib.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
extern "C" {
|
||||
fn CFAbsoluteTimeGetCurrent() -> f64;
|
||||
}
|
||||
|
||||
pub fn get_absolute_time() -> f64 {
|
||||
unsafe { CFAbsoluteTimeGetCurrent() }
|
||||
}
|
||||
6
test cases/rust/35 apple framework/main.rs
Normal file
6
test cases/rust/35 apple framework/main.rs
Normal file
@@ -0,0 +1,6 @@
|
||||
extern crate timelib;
|
||||
|
||||
fn main() {
|
||||
let time = timelib::get_absolute_time();
|
||||
println!("Current CFAbsoluteTime: {}", time);
|
||||
}
|
||||
13
test cases/rust/35 apple framework/meson.build
Normal file
13
test cases/rust/35 apple framework/meson.build
Normal file
@@ -0,0 +1,13 @@
|
||||
project('rust apple framework', ['c', 'rust'], meson_version: '>=1.3.0')
|
||||
|
||||
if host_machine.system() != 'darwin'
|
||||
error('MESON_SKIP_TEST: this test is only for macOS')
|
||||
endif
|
||||
|
||||
foundation = dependency('appleframeworks', modules: 'Foundation')
|
||||
|
||||
timelib = static_library('timelib', 'lib.rs', dependencies: foundation, rust_abi: 'rust')
|
||||
alias_target('timelib', timelib)
|
||||
|
||||
exe = executable('main', 'main.rs', link_with: timelib)
|
||||
test('main', exe)
|
||||
@@ -174,3 +174,23 @@ class DarwinTests(BasePlatformTests):
|
||||
# Those RPATHs are no longer valid and should not be present after installation
|
||||
rpaths = self._get_darwin_rpaths(os.path.join(self.installdir, 'usr/lib/libbar.dylib'))
|
||||
self.assertListEqual(rpaths, [])
|
||||
|
||||
@skip_if_not_language('rust')
|
||||
def test_rust_apple_framework_rlib(self):
|
||||
'''
|
||||
Test that Rust rlibs properly record Apple framework dependencies,
|
||||
so that external tools (like cargo) can link against them without
|
||||
meson's help.
|
||||
'''
|
||||
testdir = os.path.join(self.rust_test_dir, '35 apple framework')
|
||||
self.init(testdir)
|
||||
# Build only the library, not the executable
|
||||
self.build(target='timelib')
|
||||
# Manually invoke rustc to build the executable, using the rlib.
|
||||
# This simulates what cargo or another build system would do.
|
||||
rlib = os.path.join(self.builddir, 'libtimelib.rlib')
|
||||
main_rs = os.path.join(testdir, 'main.rs')
|
||||
out_exe = os.path.join(self.builddir, 'manual_main')
|
||||
subprocess.check_call(['rustc', '--extern', f'timelib={rlib}', main_rs, '-o', out_exe])
|
||||
# Run the executable to verify it works
|
||||
subprocess.check_call([out_exe])
|
||||
|
||||
Reference in New Issue
Block a user