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:
+168
-31
@@ -46,10 +46,99 @@ libzstd_sources = [join_paths(zstd_rootdir, 'lib/common/entropy_common.c'),
|
||||
join_paths(zstd_rootdir, 'lib/dictBuilder/divsufsort.c'),
|
||||
join_paths(zstd_rootdir, 'lib/dictBuilder/zdict.c')]
|
||||
|
||||
# C compatibility shims in the source list above are implemented by the Rust
|
||||
# static library. Build a Cargo archive in the Meson build directory so its
|
||||
# configuration cannot leak into another Meson setup.
|
||||
rust_huf_force_x1 = get_option('huf_force_decompress_x1')
|
||||
rust_huf_force_x2 = get_option('huf_force_decompress_x2')
|
||||
rust_uses_m32 = false
|
||||
foreach c_arg : get_option('c_args')
|
||||
if c_arg == '-m32'
|
||||
rust_uses_m32 = true
|
||||
endif
|
||||
if c_arg == '-DHUF_FORCE_DECOMPRESS_X1' or \
|
||||
c_arg.startswith('-DHUF_FORCE_DECOMPRESS_X1=') or \
|
||||
c_arg == '/DHUF_FORCE_DECOMPRESS_X1' or \
|
||||
c_arg.startswith('/DHUF_FORCE_DECOMPRESS_X1=')
|
||||
rust_huf_force_x1 = true
|
||||
endif
|
||||
if c_arg == '-DHUF_FORCE_DECOMPRESS_X2' or \
|
||||
c_arg.startswith('-DHUF_FORCE_DECOMPRESS_X2=') or \
|
||||
c_arg == '/DHUF_FORCE_DECOMPRESS_X2' or \
|
||||
c_arg.startswith('/DHUF_FORCE_DECOMPRESS_X2=')
|
||||
rust_huf_force_x2 = true
|
||||
endif
|
||||
endforeach
|
||||
if rust_huf_force_x1 and rust_huf_force_x2
|
||||
error('HUF_FORCE_DECOMPRESS_X1 and HUF_FORCE_DECOMPRESS_X2 are mutually exclusive')
|
||||
endif
|
||||
|
||||
rust_features = ['compression', 'decompression']
|
||||
rust_huf_mode = 'default'
|
||||
rust_huf_c_args = []
|
||||
if rust_huf_force_x1
|
||||
rust_features += 'huf-force-decompress-x1'
|
||||
rust_huf_mode = 'huf-force-decompress-x1'
|
||||
rust_huf_c_args += '-DHUF_FORCE_DECOMPRESS_X1'
|
||||
elif rust_huf_force_x2
|
||||
rust_features += 'huf-force-decompress-x2'
|
||||
rust_huf_mode = 'huf-force-decompress-x2'
|
||||
rust_huf_c_args += '-DHUF_FORCE_DECOMPRESS_X2'
|
||||
endif
|
||||
|
||||
rust_target = get_option('zstd_rust_target')
|
||||
if rust_target == ''
|
||||
if host_machine_os == os_linux and \
|
||||
(host_machine.cpu_family() == 'x86' or rust_uses_m32)
|
||||
rust_target = 'i686-unknown-linux-gnu'
|
||||
elif meson.is_cross_build()
|
||||
error('Set -Dzstd_rust_target to the Cargo target triple for this cross build')
|
||||
endif
|
||||
endif
|
||||
|
||||
rust_build_config = 'c1-d1-' + rust_huf_mode
|
||||
rust_target_dir = join_paths(meson.current_build_dir(), 'rust-target', rust_build_config)
|
||||
is_msvc = cc_id == compiler_msvc or cc_id == 'clang-cl'
|
||||
rust_staticlib_name = is_msvc ? 'zstd_rs.lib' : 'libzstd_rs.a'
|
||||
zstd_staticlib_name = is_msvc ? 'zstd.lib' : 'libzstd.a'
|
||||
|
||||
cargo = find_program('cargo', required: true)
|
||||
python = find_program('python3', 'python', required: true)
|
||||
rust_manifest = files(join_paths(zstd_rootdir, 'rust/Cargo.toml'))
|
||||
rust_lockfile = files(join_paths(zstd_rootdir, 'rust/Cargo.lock'))
|
||||
cargo_staticlib_py = files('cargo_staticlib.py')
|
||||
rust_archive_command = [
|
||||
python,
|
||||
cargo_staticlib_py,
|
||||
'--cargo', cargo,
|
||||
'--manifest-path', rust_manifest,
|
||||
'--target-dir', rust_target_dir,
|
||||
'--staticlib-name', rust_staticlib_name,
|
||||
'--output', '@OUTPUT@',
|
||||
'--features', ','.join(rust_features),
|
||||
]
|
||||
if rust_target != ''
|
||||
rust_archive_command += ['--target', rust_target]
|
||||
endif
|
||||
rust_archive = custom_target('zstd_rust_archive',
|
||||
output: rust_staticlib_name,
|
||||
command: rust_archive_command,
|
||||
depend_files: [rust_lockfile],
|
||||
build_always_stale: true)
|
||||
|
||||
# Meson's c_args controls compilation only. A native -m32 configuration also
|
||||
# needs its final C and C++ link commands to select the i686 linker emulation.
|
||||
if rust_uses_m32
|
||||
m32_link_args = cc.get_supported_link_arguments('-m32')
|
||||
add_project_link_arguments(m32_link_args, language: ['c', 'cpp'])
|
||||
endif
|
||||
|
||||
# really we need anything that defines __GNUC__ as that is what ZSTD_ASM_SUPPORTED is gated on
|
||||
# but these are the two compilers that are supported in tree and actually handle this correctly
|
||||
# Otherwise, explicitly disable assembly.
|
||||
if [compiler_gcc, compiler_clang].contains(cc_id)
|
||||
# but these are the two compilers that are supported in tree and actually handle this correctly.
|
||||
# The assembly source is AMD64-only: do not add it for an i686 (-m32) or other
|
||||
# cross target, even when the build host itself is x86_64.
|
||||
if [compiler_gcc, compiler_clang].contains(cc_id) and \
|
||||
['x86_64', 'amd64'].contains(host_machine.cpu_family()) and not rust_uses_m32
|
||||
libzstd_sources += join_paths(zstd_rootdir, 'lib/decompress/huf_decompress_amd64.S')
|
||||
else
|
||||
add_project_arguments('-DZSTD_DISABLE_ASM', language: 'c')
|
||||
@@ -101,6 +190,7 @@ if host_machine_os == os_windows and cc_id == compiler_gcc
|
||||
mingw_ansi_stdio_flags = [ '-D__USE_MINGW_ANSI_STDIO' ]
|
||||
endif
|
||||
libzstd_c_args += mingw_ansi_stdio_flags
|
||||
libzstd_c_args += rust_huf_c_args
|
||||
|
||||
libzstd_debug_cflags = []
|
||||
if use_debug
|
||||
@@ -115,56 +205,103 @@ if use_debug
|
||||
endif
|
||||
libzstd_c_args += cc.get_supported_arguments(libzstd_debug_cflags)
|
||||
|
||||
libzstd = library('zstd',
|
||||
# Keep the C archive separate until Cargo has finished. The final public
|
||||
# static archive below contains the object members of both archives, which is
|
||||
# necessary because C shims call Rust and Rust calls C helpers.
|
||||
build_static = default_library_type != 'shared'
|
||||
libzstd_c_static = static_library('zstd_c',
|
||||
libzstd_sources,
|
||||
include_directories: libzstd_includes,
|
||||
c_args: libzstd_c_args,
|
||||
gnu_symbol_visibility: 'hidden',
|
||||
dependencies: libzstd_deps,
|
||||
install: true,
|
||||
version: zstd_libversion)
|
||||
build_by_default: build_static)
|
||||
|
||||
libzstd_dep = declare_dependency(link_with: libzstd,
|
||||
include_directories: join_paths(zstd_rootdir,'lib')) # Do not expose private headers
|
||||
rust_archiver_name = get_option('zstd_rust_archiver')
|
||||
if rust_archiver_name == ''
|
||||
rust_archiver_name = is_msvc ? 'lib' : 'ar'
|
||||
endif
|
||||
rust_archiver = find_program(rust_archiver_name, required: true)
|
||||
merge_rust_archive_py = files('merge_rust_archive.py')
|
||||
merge_rust_archive_command = [
|
||||
python,
|
||||
merge_rust_archive_py,
|
||||
'--archiver', rust_archiver,
|
||||
'--c-archive', '@INPUT0@',
|
||||
'--rust-archive', '@INPUT1@',
|
||||
'--output', '@OUTPUT@',
|
||||
]
|
||||
if is_msvc
|
||||
merge_rust_archive_command += '--msvc'
|
||||
endif
|
||||
libzstd_static = custom_target('zstd_static',
|
||||
input: [libzstd_c_static, rust_archive],
|
||||
output: zstd_staticlib_name,
|
||||
command: merge_rust_archive_command,
|
||||
install: build_static,
|
||||
install_dir: get_option('libdir'),
|
||||
build_by_default: build_static)
|
||||
libzstd_static_dep = declare_dependency(
|
||||
sources: libzstd_static,
|
||||
dependencies: libzstd_deps,
|
||||
include_directories: join_paths(zstd_rootdir, 'lib'))
|
||||
|
||||
# we link to both:
|
||||
# - the shared library (for public symbols)
|
||||
# - the static library (for private symbols)
|
||||
#
|
||||
# this is needed because internally private symbols are used all the time, and
|
||||
# -fvisibility=hidden means those cannot be found
|
||||
if get_option('default_library') == 'static'
|
||||
libzstd_static = libzstd
|
||||
libzstd_internal_dep = declare_dependency(link_with: libzstd,
|
||||
include_directories: libzstd_includes)
|
||||
# We link to both a shared library (for public symbols) and the C-only static
|
||||
# archive (for private symbols). The latter must stay C-only here: the Rust
|
||||
# symbols are already supplied by the shared library. MSVC rejects this
|
||||
# combination, so its internal users link the flattened static archive.
|
||||
if default_library_type == 'static'
|
||||
libzstd_dep = libzstd_static_dep
|
||||
libzstd_internal_dep = declare_dependency(
|
||||
sources: libzstd_static,
|
||||
dependencies: libzstd_deps,
|
||||
include_directories: libzstd_includes)
|
||||
else
|
||||
if get_option('default_library') == 'shared'
|
||||
libzstd_static = static_library('zstd_objlib',
|
||||
objects: libzstd.extract_all_objects(recursive: true),
|
||||
build_by_default: false)
|
||||
if host_machine_os == os_darwin
|
||||
rust_shared_link_args = ['-Wl,-force_load,' + rust_archive.full_path()]
|
||||
elif is_msvc
|
||||
rust_shared_link_args = ['/WHOLEARCHIVE:' + rust_archive.full_path()]
|
||||
else
|
||||
libzstd_static = libzstd.get_static_lib()
|
||||
rust_shared_link_args = [
|
||||
'-Wl,--whole-archive',
|
||||
rust_archive.full_path(),
|
||||
'-Wl,--no-whole-archive',
|
||||
]
|
||||
endif
|
||||
libzstd_shared = shared_library('zstd',
|
||||
libzstd_sources,
|
||||
include_directories: libzstd_includes,
|
||||
c_args: libzstd_c_args,
|
||||
gnu_symbol_visibility: 'hidden',
|
||||
dependencies: libzstd_deps,
|
||||
link_args: rust_shared_link_args,
|
||||
link_depends: rust_archive,
|
||||
install: true,
|
||||
version: zstd_libversion)
|
||||
libzstd_dep = declare_dependency(link_with: libzstd_shared,
|
||||
include_directories: join_paths(zstd_rootdir, 'lib')) # Do not expose private headers
|
||||
|
||||
if cc_id == compiler_msvc
|
||||
# msvc does not actually support linking to both, but errors out with:
|
||||
# error LNK2005: ZSTD_<foo> already defined in zstd.lib(zstd-1.dll)
|
||||
libzstd_internal_dep = declare_dependency(link_with: libzstd_static,
|
||||
if is_msvc
|
||||
libzstd_internal_dep = declare_dependency(
|
||||
sources: libzstd_static,
|
||||
dependencies: libzstd_deps,
|
||||
include_directories: libzstd_includes)
|
||||
else
|
||||
libzstd_internal_dep = declare_dependency(link_with: libzstd,
|
||||
libzstd_internal_dep = declare_dependency(link_with: libzstd_shared,
|
||||
# the static library must be linked after the shared one
|
||||
dependencies: declare_dependency(link_with: libzstd_static),
|
||||
dependencies: declare_dependency(link_with: libzstd_c_static),
|
||||
include_directories: libzstd_includes)
|
||||
endif
|
||||
endif
|
||||
|
||||
pkgconfig.generate(libzstd,
|
||||
pkgconfig.generate(
|
||||
name: 'libzstd',
|
||||
filebase: 'libzstd',
|
||||
description: 'fast lossless compression algorithm library',
|
||||
version: zstd_libversion,
|
||||
url: 'https://facebook.github.io/zstd/')
|
||||
url: 'https://facebook.github.io/zstd/',
|
||||
libraries: ['-L${libdir}', '-lzstd'],
|
||||
libraries_private: libzstd_deps)
|
||||
|
||||
install_headers(join_paths(zstd_rootdir, 'lib/zstd.h'),
|
||||
join_paths(zstd_rootdir, 'lib/zdict.h'),
|
||||
|
||||
Reference in New Issue
Block a user