Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use pkg-config #39

Merged
merged 1 commit into from
Oct 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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`");
}
Loading