Skip to content

Commit

Permalink
feat!: Use pkg-config to link
Browse files Browse the repository at this point in the history
Signed-off-by: robot9001 <[email protected]>
  • Loading branch information
robo9k committed Oct 7, 2023
1 parent 8a4f15c commit bbddb20
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 80 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
95 changes: 68 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,44 +21,52 @@ 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
```

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.
Expand All @@ -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`, `<TARGET>_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`, `<TARGET>_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

Expand Down
84 changes: 31 additions & 53 deletions build.rs
Original file line number Diff line number Diff line change
@@ -1,63 +1,41 @@
fn env(name: &str) -> Option<std::ffi::OsString> {
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`");
}

0 comments on commit bbddb20

Please sign in to comment.