Files
zstd-rs/build/meson/lib/merge_rust_archive.py
T
ddidderr 3d679116d8 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
2026-07-10 22:08:55 +02:00

111 lines
3.8 KiB
Python

#!/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())