build(meson): link migrated Rust code into native libraries
Build a configuration-matched Cargo archive from Meson and flatten its object members into static libzstd. This gives C consumers one archive even though the migrated Rust objects and remaining C code refer to each other. Shared libraries whole-archive the Cargo output to retain Rust-only ABI exports. The implementation supports Meson 0.50's generated-archive linking rules, matching HUF mode and 32-bit configuration, and documents cross-build target and archiver selection. Test Plan: - fresh Meson both-build smoke consumers and invalidDictionaries - modern static/shared/both, forced-HUF, i686, install, and fuzzer paths - real Meson 0.50 static and shared smoke builds - cargo clippy, cargo clippy --benches, cargo clippy --tests, and nightly fmt Refs: build/meson/README.md archive and cross-build documentation
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
#!/usr/bin/env python3
|
||||
# ################################################################
|
||||
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
# All rights reserved.
|
||||
#
|
||||
# This source code is licensed under both the BSD-style license (found in the
|
||||
# LICENSE file in the root directory of this source tree) and the GPLv2 (found
|
||||
# in the COPYING file in the root directory of this source tree).
|
||||
# You may select, at your option, one of the above-listed licenses.
|
||||
# ################################################################
|
||||
|
||||
"""Flatten Cargo's staticlib into Meson's libzstd static archive."""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import argparse
|
||||
import os
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
|
||||
def run(command, **kwargs):
|
||||
try:
|
||||
return subprocess.run(command, check=True, **kwargs)
|
||||
except subprocess.CalledProcessError as error:
|
||||
print('command failed: {}'.format(' '.join(command)), file=sys.stderr)
|
||||
return error
|
||||
|
||||
|
||||
def extract_members(archiver, archive, destination, prefix):
|
||||
members = subprocess.check_output(
|
||||
[archiver, 't', str(archive)], universal_newlines=True
|
||||
).splitlines()
|
||||
if not members:
|
||||
raise RuntimeError('{} contains no object members'.format(archive))
|
||||
|
||||
objects = []
|
||||
for index, member in enumerate(members):
|
||||
object_file = destination / '{}-{:04d}.o'.format(prefix, index)
|
||||
with object_file.open('wb') as output:
|
||||
run_result = run(
|
||||
[archiver, 'p', str(archive), member], stdout=output
|
||||
)
|
||||
if isinstance(run_result, subprocess.CalledProcessError):
|
||||
raise RuntimeError('could not extract {} from {}'.format(member, archive))
|
||||
objects.append(object_file)
|
||||
return objects
|
||||
|
||||
|
||||
def merge_with_ar(args, output):
|
||||
with tempfile.TemporaryDirectory(prefix='zstd-rust-archive-') as temporary:
|
||||
temporary_path = Path(temporary)
|
||||
c_objects = extract_members(
|
||||
args.archiver, Path(args.c_archive), temporary_path, 'c'
|
||||
)
|
||||
rust_objects = extract_members(
|
||||
args.archiver, Path(args.rust_archive), temporary_path, 'rust'
|
||||
)
|
||||
temporary_output = temporary_path / output.name
|
||||
run_result = run(
|
||||
[args.archiver, 'rcs', str(temporary_output)]
|
||||
+ [str(object_file) for object_file in c_objects + rust_objects]
|
||||
)
|
||||
if isinstance(run_result, subprocess.CalledProcessError):
|
||||
raise RuntimeError('could not create {}'.format(output))
|
||||
shutil.move(str(temporary_output), str(output))
|
||||
|
||||
|
||||
def merge_with_msvc(args, output):
|
||||
temporary_output = output.with_name(output.name + '.tmp')
|
||||
run_result = run(
|
||||
[
|
||||
args.archiver,
|
||||
'/OUT:{}'.format(temporary_output),
|
||||
args.c_archive,
|
||||
args.rust_archive,
|
||||
]
|
||||
)
|
||||
if isinstance(run_result, subprocess.CalledProcessError):
|
||||
raise RuntimeError('could not create {}'.format(output))
|
||||
os.replace(str(temporary_output), str(output))
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--archiver', required=True)
|
||||
parser.add_argument('--c-archive', required=True)
|
||||
parser.add_argument('--rust-archive', required=True)
|
||||
parser.add_argument('--output', required=True)
|
||||
parser.add_argument('--msvc', action='store_true')
|
||||
args = parser.parse_args()
|
||||
|
||||
output = Path(args.output)
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
try:
|
||||
if args.msvc:
|
||||
merge_with_msvc(args, output)
|
||||
else:
|
||||
merge_with_ar(args, output)
|
||||
except (OSError, RuntimeError, subprocess.CalledProcessError) as error:
|
||||
print('could not merge Rust archive: {}'.format(error), file=sys.stderr)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user