build: attempt to normalize Rust handling to support static linking
Co-authored-by: Jade Lovelance <jade@lix.systems> Change-Id: Icfd3cabaa6abc0a39f501b4b6c6b73df4cb66e6a
This commit is contained in:
+16
-7
@@ -3,22 +3,31 @@ rowan = dependency('rowan-0.15-rs')
|
||||
|
||||
rust = import('rust')
|
||||
|
||||
# This hack is required by the wombo combo of meson bugs:
|
||||
# Meson does not set the soname for us: https://github.com/mesonbuild/meson/issues/13537
|
||||
# Meson ignores link_args for Rust targets: https://github.com/mesonbuild/meson/issues/13538
|
||||
lix_doc_rust_args = [
|
||||
# link-arg=-soname has no effect when building a static lib,
|
||||
# since the linker is never actually invoked.
|
||||
'-C', f'link-arg=-Wl,@soname_arg@,liblix_doc.@dylib_suffix@',
|
||||
]
|
||||
|
||||
lix_doc = static_library(
|
||||
'lix_doc',
|
||||
sources : files('src/lib.rs'),
|
||||
rust_abi : 'c',
|
||||
dependencies : [
|
||||
rowan,
|
||||
rnix,
|
||||
],
|
||||
# If an installed static library depends on this target, then Meson will force
|
||||
# that to link with `-Wl,--whole-archive`, unless we also install this target.
|
||||
# `-Wl,--whole-archive` can cause some Problems when linking multiple nested
|
||||
# static libraries, so let's just install the damn thing.
|
||||
install : is_static,
|
||||
rust_args : [
|
||||
# Empty when default_library=static
|
||||
rust_dynamic_args,
|
||||
lix_doc_rust_args,
|
||||
],
|
||||
)
|
||||
|
||||
liblix_doc = declare_dependency(
|
||||
# NOTE: this dependency object is only used for `rust_monocrate`
|
||||
liblix_doc_internal = declare_dependency(
|
||||
link_with : lix_doc,
|
||||
)
|
||||
|
||||
|
||||
@@ -25,6 +25,13 @@ install_headers(config_h, subdir : 'lix')
|
||||
# Subcomponents: these link into artifacts themselves, and have interdependencies.
|
||||
subdir('lix-doc')
|
||||
|
||||
subdir('rust-monocrate')
|
||||
|
||||
# liblix_doc is actually rust-monocrate in a trenchcoat.
|
||||
liblix_doc = declare_dependency(
|
||||
link_with : rust_monocrate,
|
||||
)
|
||||
|
||||
subdir('libutil')
|
||||
# Load-bearing order. libstore depends on libutil.
|
||||
subdir('libstore')
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
# This may not actually build with Cargo, but exists to help Meson and to manage lockfiles.
|
||||
[package]
|
||||
name = "rust-monocrate"
|
||||
edition.workspace = true
|
||||
|
||||
[lib]
|
||||
crate-type = ["staticlib"]
|
||||
lix-doc = { path = "../lix-doc" }
|
||||
@@ -0,0 +1,19 @@
|
||||
# What the hell is this?
|
||||
|
||||
This is a cursed solution to the intersection of the following problems:
|
||||
|
||||
- Lix is separated into multiple libraries, which are intended to be buildable as dynamic/shared libraries *or* statlic libraries.
|
||||
- Those libraries are linked into the Lix executable binary.
|
||||
- Static libraries also statically link their library dependencies.
|
||||
- We want to be able to use Rust code in multiple places in the codebase.
|
||||
- Meson has poor support for Rust and non-Rust sources in the same target, with [internal errors](https://github.com/mesonbuild/meson/issues/15435) preventing mixed targets from linking against non-mixed targets.
|
||||
- Linking more than one Rust `crate-type = staticlib` is [unsupported](https://github.com/rust-lang/rust/issues/44322) with Rust's standard library.
|
||||
- Meson [does not support dynamically linking the Rust standard library for `crate-type = staticlib`](https://mesonbuild.com/Release-notes-for-1-9-0.html#new-experimental-option-rust_dynamic_std).
|
||||
- Meson [ignores `link_args` for Rust targets](https://github.com/mesonbuild/meson/issues/13538)
|
||||
- Meson [does not set soname of Rust cdylibs](https://github.com/mesonbuild/meson/issues/13537)
|
||||
|
||||
So! What do we do? For any Rust code that Lix wants to use, we always link to a single crate, which will reëxport Rust code from any crate (in-tree or out-of-tree) we wish to use.
|
||||
This "monocrate" is *always* built as a static library, and anything in Lix that depends on Rust code will statically link it in.
|
||||
When the Lix executable is linked against Lix libraries that are built statically, we do *not* `link_whole` them.
|
||||
As far as we::Qyriad can tell, this is sufficient to not break everything.
|
||||
But long-term, we should probably solve this problem by migrating to Rust code at top-level, and having Rustc do the final link.
|
||||
@@ -0,0 +1,19 @@
|
||||
# This crate is what ultimately links to Lix. All other Rust crates Lix uses must go through this.
|
||||
|
||||
rust_monocrate_sources = files(
|
||||
'src/lib.rs',
|
||||
)
|
||||
|
||||
rust_monocrate = static_library(
|
||||
'rust_monocrate',
|
||||
rust_abi : 'c',
|
||||
sources : rust_monocrate_sources,
|
||||
dependencies : [
|
||||
liblix_doc_internal,
|
||||
rnix,
|
||||
rowan,
|
||||
],
|
||||
# If any installed target links against a static library,
|
||||
# the static library must also be installed.
|
||||
install : true,
|
||||
)
|
||||
@@ -0,0 +1,12 @@
|
||||
//! This is a hack that forces Rust to link all the Lix libs as a single static
|
||||
//! library for usage when building Lix itself in static mode.
|
||||
//!
|
||||
//! The reason for this is that Rust does not support linking multiple
|
||||
//! staticlibs into one executable, as it will jam a libstd into every single
|
||||
//! one of them. This is ridiculously goofy because it would be trivially
|
||||
//! solved by linking libstd separately.
|
||||
//!
|
||||
//! https://github.com/rust-lang/rust/issues/44322
|
||||
//!
|
||||
//! It re-exports all the symbols that should be exported to C++.
|
||||
pub use lix_doc::*;
|
||||
+33
@@ -223,6 +223,14 @@ is_darwin = host_machine.system() == 'darwin'
|
||||
is_freebsd = host_machine.system() == 'freebsd'
|
||||
is_x64 = host_machine.cpu_family() == 'x86_64'
|
||||
|
||||
# Meson knows these, but won't tell us. We need them to hack around some linker shenanigans.
|
||||
# See the 'Rust Support' section below.
|
||||
if is_darwin
|
||||
dylib_suffix = 'dylib'
|
||||
elif is_linux
|
||||
dylib_suffix = 'so'
|
||||
endif
|
||||
|
||||
# Per-platform arguments that you should probably pass to shared_module() invocations.
|
||||
# Something like add_project_arguments() can't be scoped on only shared modules, so this
|
||||
# variable is here instead.
|
||||
@@ -238,6 +246,30 @@ elif is_linux
|
||||
endif
|
||||
configdata = { }
|
||||
|
||||
#
|
||||
# Rust Support
|
||||
#
|
||||
|
||||
if is_static
|
||||
rust_dynamic_args = []
|
||||
else
|
||||
rust_dynamic_args = ['-C', 'prefer-dynamic']
|
||||
endif
|
||||
|
||||
# This hack is required by the wombo combo of meson bugs:
|
||||
# Meson does not set the soname for us: https://github.com/mesonbuild/meson/issues/13537
|
||||
# Meson ignores link_args for Rust targets: https://github.com/mesonbuild/meson/issues/13538
|
||||
# Thus we have to, when the build is dynamic, pass these in rust_args to make it link properly.
|
||||
if cxx.get_linker_id().startswith('ld64')
|
||||
# Rust actually sets the -install_name if you pass -C rpath=yes on macOS targets (?!):
|
||||
# https://github.com/rust-lang/rust/blob/2a8af4f7c8262a90b886bc063fe0c271d2b51a45/compiler/rustc_codegen_ssa/src/back/linker.rs#L441-L445
|
||||
# https://rust-lang.zulipchat.com/#narrow/channel/182449-t-compiler.2Fhelp/topic/libstd.2C.20rpath.2C.20dylibs.20and.20packaging
|
||||
# This seems like a bug, so let's keep passing it deliberately.
|
||||
soname_arg = '-install_name'
|
||||
elif cxx.get_linker_id().startswith('ld')
|
||||
soname_arg = '-soname'
|
||||
endif
|
||||
|
||||
#
|
||||
# Dependencies
|
||||
#
|
||||
@@ -404,6 +436,7 @@ endif
|
||||
# Build-time tools
|
||||
#
|
||||
dot = find_program('dot', required : false, native : true)
|
||||
cbindgen = find_program('cbindgen', native : true)
|
||||
pymod = import('python')
|
||||
python = pymod.find_installation('python3')
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
rapidcheck,
|
||||
removeReferencesTo,
|
||||
rustPlatform,
|
||||
rust-cbindgen,
|
||||
rustc,
|
||||
sqlite,
|
||||
systemtap-lix ? __forDefaults.systemtap-lix,
|
||||
@@ -339,6 +340,7 @@ stdenv.mkDerivation (finalAttrs: {
|
||||
meson
|
||||
ninja
|
||||
cmake
|
||||
rust-cbindgen
|
||||
rustc
|
||||
capnproto
|
||||
# Required for libstd++ assertions that leaks inside of the final binary.
|
||||
|
||||
Reference in New Issue
Block a user