From 1b12ad6b85fcddb31fa042edaaa60114693a5c0f Mon Sep 17 00:00:00 2001 From: robot9001 Date: Sat, 7 Oct 2023 18:25:53 +0200 Subject: [PATCH] feat!: Use `pkg-config` to link Signed-off-by: robot9001 --- Cargo.toml | 1 + README.md | 95 ++++++++++++++++++++++++++++++++++++++---------------- build.rs | 84 ++++++++++++++++++----------------------------- 3 files changed, 100 insertions(+), 80 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 556f49e..a3e03c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,6 +49,7 @@ v5-38 = ["v5-35"] v5-40 = ["v5-38"] [build-dependencies] +pkg-config = "0.3.27" vcpkg = "0.2.15" [package.metadata.vcpkg] diff --git a/README.md b/README.md index 90dec75..0bbf32f 100644 --- a/README.md +++ b/README.md @@ -21,29 +21,20 @@ The `rustdoc` is available on [docs.rs](https://docs.rs/magic-sys). # Requirements -This crate requires the `libmagic` C library in version 5. +This crate requires the `libmagic` C library in version 5.39 or newer. -You need to specify your `libmagic` version by activating the matching `magic-sys` feature. -Each API version has a crate feature like "v5-38" (v5.38 is also the default), see [Cargo.toml](Cargo.toml) -If you use a different version of `libmagic`, adjust your configuration: -```toml -[dependencies.magic-sys] -version = "0.3" -default-features = false -features = ["v5-41"] -``` -Note that those version features are additive, so "v5-41" implies "v5-40" and other previous versions. +## libmagic -`libmagic` needs to be installed in a standard location (also see [issue #1](https://github.com/robo9k/rust-magic-sys/issues/1)). +If you don't want to configure the build, `libmagic` has to be available in a standard location +for either `pkg-config` or `vcpkg`, see [Building](#Building). -On a Debian based Linux system this can be achieved like this: +On a Debian based Linux system such as Ubuntu this can be achieved like this: ```sh sudo apt-get install libmagic1 libmagic-dev ``` On RHEL/Cent OS, Gentoo and others you will need to install the `file` package. - On Mac OS X you can use [Homebrew](https://brew.sh/): ```sh brew install libmagic @@ -51,14 +42,31 @@ brew install libmagic Feedback for Windows ([issue #2](https://github.com/robo9k/rust-magic-sys/issues/2)) support is appreciated! -You can use Microsoft's [`vcpkg`](https://vcpkg.io) via [`vcpkg-rs`](https://docs.rs/vcpkg) and [`cargo-vcpkg`](https://crates.io/crates/cargo-vcpkg). -If you choose the latter, that means you'll have to: +You can use Microsoft's [`vcpkg`](https://vcpkg.io) via [`cargo-vcpkg`](https://crates.io/crates/cargo-vcpkg): ```sh cargo install cargo-vcpkg cargo vcpkg build ``` Afterwards, you can `cargo build` etc. your crate as usual. +## Version features + +The `libmagic` API is extended with new backwards-compatible features every now and then.\ +To use newly added `libmagic` functionality, you need to use a corresponding `libmagic` version. + +You need to specify your `libmagic` version by activating the matching `magic-sys` feature.\ +Each API version has a crate feature like "v5-38" (v5.38 is also the default), see [Cargo.toml](Cargo.toml)\ +If you use a different version of `libmagic`, adjust your configuration: +```toml +[dependencies.magic-sys] +version = "0.3" +default-features = false +features = ["v5-41"] +``` +Note that those version features are additive, so "v5-41" implies "v5-40" and other previous versions. + +If you want to use a newer/different `libmagic` version, you will have to [link it](#Building) accordingly. + # MSRV The Minimum Supported Rust Version (MSRV) is Rust 1.54 or higher. @@ -67,22 +75,55 @@ This version might be changed in the future, but it will be done with a crate ve # Building -By default `libmagic` will be searched in the system library paths. If you need to use a different library or are cross-compiling, you can set the `MAGIC_DIR` and `MAGIC_STATIC` environment variables. +To determine which `libmagic` to link against, this crate uses +[`pkg-config`](https://www.freedesktop.org/wiki/Software/pkg-config/) +and [`vcpkg`](https://vcpkg.io/). + +In general you can link statically or dynamically against `libmagic`. + +With static linkage your binary/library includes the `libmagic` code and _does not_ have a run-time dependency. -## `MAGIC_DIR`, `_MAGIC_DIR` -Tells `rustc` where to find `libmagic.so` / `libmagic.a`. Can have a target-specific prefix like `X86_64_UNKNOWN_LINUX_MUSL_MAGIC_DIR` +With dynamic linkage your binary/library _does not_ include the `libmagic` code and _does_ have a run-time dependency on a `libmagic.dll` / `libmagic.so` / `libmagic.dylib` depending on your platform (Windows / Linux / macOS).\ +You might have to ship this `libmagic` shared library with your binary/library if you do not expect your users to have a compatible version installed on their system. -## `MAGIC_STATIC`, `_MAGIC_STATIC` -Controls static linking with `libmagic`. Enabled automatically if there's only a `libmagic.a` in the (provided) search path or if explicitly enabled like `MAGIC_STATIC=true`. Can have a target-specific prefix like `X86_64_UNKNOWN_LINUX_MUSL_MAGIC_STATIC` +You might want to ship a copy of the default `libmagic` / `file` database with your binary/library if you do not expect your users to have a compatible `libmagic` installed on their system. -Similarly `MAGIC_STATIC=false` can be used to choose to link `libmagic` dynamically. -If unset but both libraries are available, the build will bail out with an error and you have to set one option explicitly. +## pkg-config + +This uses the [`pkg-config` crate](https://docs.rs/pkg-config), so check its documentation for details. + +You can use e.g. the following environment variables: +- `LIBMAGIC_NO_PKG_CONFIG` if set, will skip `pkg-config` +- `LIBMAGIC_STATIC` if set, instructs `pkg-config` to link statically +- `LIBMAGIC_DYNAMIC` if set, instructs `pkg-config` to link dynamically + +By default dynamic linkage is used. ## vcpkg -The optional `vcpkg` integration has its own set of environment variables, see [`vcpkg` crate docs](https://docs.rs/vcpkg/#environment-variables). -If you do not use `cargo vcpkg build`, you will have to either -* `vcpkg install libmagic` and set the environment variables for your `vcpkg` root directory -* `vcpkg integrate install` your `vcpkg` root user-wide + +This uses the [`vcpkg` crate](https://docs.rs/vcpkg), so check its documentation for details. + +You can use e.g. the following environment variables: +- `VCPKGRS_NO_LIBMAGIC` if set, will skip `vcpkg` +- `VCPKGRS_DYNAMIC` if set, instructs `vcpkg` to link dynamically + +By default static linkage is used. + +You can use `vcpkg` standalone or by using [`cargo-vcpkg`](https://crates.io/crates/cargo-vcpkg). + +If you do _not_ use `cargo vcpkg build`, you will have to either +- `vcpkg install libmagic` and set the `VCPKG_ROOT` environment variable for your `vcpkg` root directory +- `vcpkg integrate install` your `vcpkg` root user-wide + +## Custom + +If you skip both `pkg-config` and `vcpkg` the `magic-sys` build script will fail.\ +Especially linking statically to `libmagic` requires additional libraries that depend on your version and system. + +You can skip the `magic-sys` build script entirely by [overriding it](https://doc.rust-lang.org/cargo/reference/build-scripts.html#overriding-build-scripts).\ +This is an option if you want to use neither `pkg-config` nor `vcpkg`. + +The `magic-sys` crate does not offer to link a against a bundled `libmagic` version. # License diff --git a/build.rs b/build.rs index d3c2634..75c8827 100644 --- a/build.rs +++ b/build.rs @@ -1,63 +1,41 @@ -fn env(name: &str) -> Option { - let target = std::env::var("TARGET").expect("Cargo didn't provide `TARGET` environment var"); - let target = target.to_uppercase().replace("-", "_"); - let prefixed_name = format!("{}_{}", target, name); - println!("cargo:rerun-if-env-changed={}", prefixed_name); - match std::env::var_os(prefixed_name) { - Some(v) => Some(v), - None => { - println!("cargo:rerun-if-env-changed={}", name); - std::env::var_os(name) - } - } -} +// SPDX-FileCopyrightText: © The `magic-sys` Rust crate authors +// SPDX-License-Identifier: MIT OR Apache-2.0 fn main() { - if let Some(magic_dir) = env("MAGIC_DIR").map(std::path::PathBuf::from) { - if !std::path::Path::new(&magic_dir).exists() { - panic!("Magic library directory {:?} does not exist", magic_dir); - } - println!( - "cargo:rustc-link-search=native={}", - magic_dir.to_string_lossy() - ); - - let static_lib = magic_dir.join("libmagic.a"); - let shared_lib = magic_dir.join("libmagic.so"); - match env("MAGIC_STATIC").as_ref().and_then(|s| s.to_str()) { - Some("false") | Some("FALSE") | Some("0") => { - if !shared_lib.exists() { - panic!("No libmagic.so found in {:?}", magic_dir); - } - println!("cargo:rustc-link-lib=dylib=magic"); - } - Some(_) => { - if !static_lib.exists() { - panic!("No libmagic.a found in {:?}", magic_dir); - } - println!("cargo:rustc-link-lib=static=magic"); + let lib = pkg_config::Config::new() + .atleast_version("5.39") + .probe("libmagic"); + match lib { + Err(err) => match err { + pkg_config::Error::EnvNoPkgConfig(_) => { + println!("pkg-config skipped: {}", err); } - None => { - match (static_lib.exists(), shared_lib.exists()) { - (false, false) => panic!("Neither libmagic.so, nor libmagic.a was found in {:?}", magic_dir), - (true, false) => println!("cargo:rustc-link-lib=static=magic"), - (false, true) => println!("cargo:rustc-link-lib=dylib=magic"), - (true, true) => panic!("Both a static and a shared library were found in {:?}\nspecify a choice with `MAGIC_STATIC=true|false`", magic_dir), - } + _ => { + println!("cargo:warning=pkg-config failed: {}", err); } + }, + Ok(lib) => { + println!("pkg-config success: {:?}", lib); + return; } - } else { - if let Err(err) = vcpkg::find_package("libmagic") { - println!("Could not find vcpkg package: {}", err); - } else if cfg!(windows) { - // workaround, see https://github.com/robo9k/rust-magic-sys/pull/16#issuecomment-949094327 - println!("cargo:rustc-link-lib=shlwapi"); + } - // vcpkg was successful, don't print anything else + let lib = vcpkg::find_package("libmagic"); + match lib { + Err(err) => match err { + vcpkg::Error::DisabledByEnv(_) => { + println!("vcpkg skipped: {}", err); + } + _ => { + println!("cargo:warning=vcpkg failed: {}", err); + } + }, + Ok(lib) => { + println!("vcpkg success: {:?}", lib); return; } - - // default fall through: try linking dynamically to just `libmagic` without further config - println!("cargo:rustc-link-lib=dylib=magic"); } + + // if we're reach here, this means that either both pkg-config and vcpkg got skipped or both failed + panic!("could not link to `libmagic` with neither `pkg-config` nor `vcpkg`"); }