From 3016c479ac77f387545d9c93f4025b96610ff554 Mon Sep 17 00:00:00 2001 From: g0ldminer <154477347+g0ldminer@users.noreply.github.com> Date: Wed, 4 Dec 2024 02:37:45 +0100 Subject: [PATCH] Feature flags to configure linking (dynamic or static) (#82) * add feature flags to configure linking * prefer dynamic linking for macOS targets. * attempt to remove RUSTFLAGS for unit tests in CI. --------- Co-authored-by: Rachid Mzannar --- .github/workflows/rust.yml | 16 +++++----- bindings/rust/Cargo.toml | 13 ++++++++ bindings/rust/build.rs | 61 +++++++++++++++++++++++++++++++++++--- 3 files changed, 78 insertions(+), 12 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index c3bd6c7..ee6f780 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -43,10 +43,10 @@ jobs: - name: Packages run: brew install llvm@15 - name: Build - run: RUSTFLAGS="-L$(brew --prefix zstd)/lib" NYXSTONE_LLVM_PREFIX="$(brew --prefix llvm@15)" NYXSTONE_LINK_FFI=1 cargo build + run: NYXSTONE_LLVM_PREFIX="$(brew --prefix llvm@15)" NYXSTONE_LINK_FFI=1 cargo build working-directory: ${{ env.working-dir }} - name: Run tests - run: RUSTFLAGS="-L$(brew --prefix zstd)/lib" RUSTDOCFLAGS="-L$(brew --prefix zstd)/lib" NYXSTONE_LLVM_PREFIX="$(brew --prefix llvm@15)" NYXSTONE_LINK_FFI=1 cargo test + run: NYXSTONE_LLVM_PREFIX="$(brew --prefix llvm@15)" NYXSTONE_LINK_FFI=1 cargo test working-directory: ${{ env.working-dir }} mac-llvm-16: @@ -58,10 +58,10 @@ jobs: - name: Packages run: brew install llvm@16 - name: Build - run: RUSTFLAGS="-L$(brew --prefix zstd)/lib" NYXSTONE_LLVM_PREFIX="$(brew --prefix llvm@16)" NYXSTONE_LINK_FFI=1 cargo build + run: NYXSTONE_LLVM_PREFIX="$(brew --prefix llvm@16)" NYXSTONE_LINK_FFI=1 cargo build working-directory: ${{ env.working-dir }} - name: Run tests - run: RUSTFLAGS="-L$(brew --prefix zstd)/lib" RUSTDOCFLAGS="-L$(brew --prefix zstd)/lib" NYXSTONE_LLVM_PREFIX="$(brew --prefix llvm@16)" NYXSTONE_LINK_FFI=1 cargo test + run: NYXSTONE_LLVM_PREFIX="$(brew --prefix llvm@16)" NYXSTONE_LINK_FFI=1 cargo test working-directory: ${{ env.working-dir }} mac-llvm-17: @@ -73,10 +73,10 @@ jobs: - name: Packages run: brew install llvm@17 - name: Build - run: RUSTFLAGS="-L$(brew --prefix zstd)/lib" NYXSTONE_LLVM_PREFIX="$(brew --prefix llvm@17)" NYXSTONE_LINK_FFI=1 cargo build + run: NYXSTONE_LLVM_PREFIX="$(brew --prefix llvm@17)" NYXSTONE_LINK_FFI=1 cargo build working-directory: ${{ env.working-dir }} - name: Run tests - run: RUSTFLAGS="-L$(brew --prefix zstd)/lib" RUSTDOCFLAGS="-L$(brew --prefix zstd)/lib" NYXSTONE_LLVM_PREFIX="$(brew --prefix llvm@17)" NYXSTONE_LINK_FFI=1 cargo test + run: NYXSTONE_LLVM_PREFIX="$(brew --prefix llvm@17)" NYXSTONE_LINK_FFI=1 cargo test working-directory: ${{ env.working-dir }} mac-llvm-18: @@ -88,9 +88,9 @@ jobs: - name: Packages run: brew install llvm@18 - name: Build - run: RUSTFLAGS="-L$(brew --prefix zstd)/lib" NYXSTONE_LLVM_PREFIX="$(brew --prefix llvm@18)" NYXSTONE_LINK_FFI=1 cargo build + run: NYXSTONE_LLVM_PREFIX="$(brew --prefix llvm@18)" NYXSTONE_LINK_FFI=1 cargo build working-directory: ${{ env.working-dir }} - name: Run tests - run: RUSTFLAGS="-L$(brew --prefix zstd)/lib" RUSTDOCFLAGS="-L$(brew --prefix zstd)/lib" NYXSTONE_LLVM_PREFIX="$(brew --prefix llvm@18)" NYXSTONE_LINK_FFI=1 cargo test + run: NYXSTONE_LLVM_PREFIX="$(brew --prefix llvm@18)" NYXSTONE_LINK_FFI=1 cargo test working-directory: ${{ env.working-dir }} diff --git a/bindings/rust/Cargo.toml b/bindings/rust/Cargo.toml index 806db84..5e531ea 100644 --- a/bindings/rust/Cargo.toml +++ b/bindings/rust/Cargo.toml @@ -25,5 +25,18 @@ clap = { version = "4.5", features = ["derive"] } cxx-build = "1.0.94" anyhow = { version = "1.0.68", default-features = true } +[features] +# Linking preference. +# If none of these is selected, it defaults to force static linking to match +# the behaviour before this feature is introduced. +# Prefer dynamic linking to LLVM library if possible. +prefer-dynamic = [] +# Force dynamic linking. +force-dynamic = [] +# Prefer static linking to LLVM library if possible. +prefer-static = [] +# Force static linking +force-static = [] + [lib] path = "src/lib.rs" diff --git a/bindings/rust/build.rs b/bindings/rust/build.rs index 81d06bb..ca0ac6a 100644 --- a/bindings/rust/build.rs +++ b/bindings/rust/build.rs @@ -56,6 +56,8 @@ fn main() { println!("cargo:config_path={}", llvm_config_path.display()); // will be DEP_LLVM_CONFIG_PATH println!("cargo:libdir={}", libdir); // DEP_LLVM_LIBDIR + let preferences = LinkingPreferences::init(); + // Link LLVM libraries println!("cargo:rustc-link-search=native={}", libdir); for link_search_dir in get_system_library_dirs() { @@ -63,7 +65,7 @@ fn main() { } // We need to take note of what kind of libraries we linked to, so that // we can link to the same kind of system libraries - let (kind, libs) = get_link_libraries(&llvm_config_path); + let (kind, libs) = get_link_libraries(&llvm_config_path, &preferences); for name in libs { println!("cargo:rustc-link-lib={}={}", kind.string(), name); } @@ -384,8 +386,50 @@ impl LibraryKind { } } +#[derive(Debug, Clone, Copy)] +struct LinkingPreferences { + /// Prefer static linking over dynamic linking. + prefer_static: bool, + /// Force the use of the preferred kind of linking. + force: bool, +} + +impl LinkingPreferences { + fn init() -> LinkingPreferences { + let prefer_static = cfg!(feature = "prefer-static"); + let prefer_dynamic = cfg!(feature = "prefer-dynamic"); + let force_static = cfg!(feature = "force-static"); + let force_dynamic = cfg!(feature = "force-dynamic"); + + // more than one preference is an error + if [prefer_static, prefer_dynamic, force_static, force_dynamic] + .iter() + .filter(|&&x| x) + .count() + > 1 + { + panic!( + "Only one of the features `prefer-static`, `prefer-dynamic`, `force-static`, \ + `force-dynamic` can be enabled at once" + ); + } + + // if no preference is given, default to prefer static linking or dynamic linking for macOS + // targets + let prefer_static = match target_os_is("macos") { + true => false, + false => prefer_static || !(prefer_dynamic || force_static || force_dynamic), + }; + + LinkingPreferences { + prefer_static: force_static || prefer_static, + force: force_static || force_dynamic, + } + } +} + /// Get the names of libraries to link against, along with whether it is static or shared library. -fn get_link_libraries(llvm_config_path: &Path) -> (LibraryKind, Vec) { +fn get_link_libraries(llvm_config_path: &Path, preferences: &LinkingPreferences) -> (LibraryKind, Vec) { // Using --libnames in conjunction with --libdir is particularly important // for MSVC when LLVM is in a path with spaces, but it is generally less of // a hack than parsing linker flags output from --libs and --ldflags. @@ -404,8 +448,17 @@ fn get_link_libraries(llvm_config_path: &Path) -> (LibraryKind, Vec) { llvm_config_ex(llvm_config_path, ["--libnames", link_arg]) } - // Prefer static linking - let preferences = [LibraryKind::Static, LibraryKind::Dynamic]; + let LinkingPreferences { prefer_static, force } = preferences; + let one = [*prefer_static]; + let both = [*prefer_static, !*prefer_static]; + + let preferences = if *force { &one[..] } else { &both[..] }.iter().map(|is_static| { + if *is_static { + LibraryKind::Static + } else { + LibraryKind::Dynamic + } + }); for kind in preferences { match get_link_libraries_impl(llvm_config_path, kind) {