Automatically build ImageMagick bindings

Leave the generated bindings out of the source repository and instead
have Cargo automatically build them if they are missing. This allows the
version of MagickWand to be incorporated into the bindings
automatically. For instance, in Homebrew the version is 6.Q16, while on
FreeBSD 10.2 it is 6.9, and only be generating the bindings for each
system can we ensure a smooth compilation process.
This commit is contained in:
Nathan Fiedler
2016-01-24 21:56:11 -08:00
parent 34c4fec9fe
commit 04e1052266
5 changed files with 99 additions and 8721 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
target
Cargo.lock
rust-bindgen

View File

@ -2,6 +2,12 @@
A somewhat safe Rust interface to the [ImageMagick](http://www.imagemagick.org/) system, in particular, the MagickWand library. Many of the functions in the MagickWand API are still missing, and those that are needed will be gradually added.
## Dependenices
* Rust
* Cargo
* ImageMagick
## Build and Test
Pretty simple for now.
@ -33,25 +39,3 @@ fn resize() -> Result<Vec<u8>, &'static str> {
wand.write_image_blob("jpeg")
}
```
## Generating Bindings
To generate the ImageMagick bindings, we use [rust-bindgen](https://github.com/crabtw/rust-bindgen), which reads the C header files and produces a suitable wrapper in Rust.
This example is using the [Homebrew](http://brew.sh) installed version of ImageMagick, and the LLVM compiler suite provided in the Command Line Tools from Apple. The only real difference for Mac OS X is the `DYLD_LIBRARY_PATH` that is needed to work around [issue #89](https://github.com/crabtw/rust-bindgen/issues/89) in rust-bindgen. Otherwise, the same basic steps should work on any Rust-supported system.
```
$ git clone https://github.com/crabtw/rust-bindgen.git
$ cd rust-bindgen
$ cargo build
$ echo '#include <wand/MagickWand.h>' > ~/gen.h
$ DYLD_LIBRARY_PATH=/Library/Developer/CommandLineTools/usr/lib \
./target/debug/bindgen \
`MagickWand-config --cflags` \
-builtins \
-o ~/bindings.rs \
`MagickWand-config --ldflags` \
~/gen.h
```
Then copy the `~/bindings.rs` file into the `src` directory of this project, and rebuild everything (`cargo clean` and `cargo test`). Hopefully it still works.

View File

@ -1,6 +1,96 @@
// build.rs
/*
* Copyright 2016 Nathan Fiedler
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use std::error::Error;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;
use std::process::Command;
static HEADER: &'static str = "#include <wand/MagickWand.h>\n";
fn main() {
//
// If the MagickWand bindings are missing, generate them using
// rust-bindgen.
//
let bindings_path = Path::new("src/bindings.rs");
if !bindings_path.exists() {
let bindgen_path = Path::new("rust-bindgen");
if !bindgen_path.exists() {
Command::new("git")
.arg("clone")
.arg("https://github.com/crabtw/rust-bindgen.git")
.status().unwrap();
// Checkout a version of rust-bindgen that is known to work;
// more recent versions produce code that does not compile (the
// commit after 8a51860 changes the way enums are generated).
Command::new("git")
.arg("checkout")
.arg("8a51860")
.current_dir("rust-bindgen")
.status().unwrap();
Command::new("cargo")
.arg("build")
.current_dir("rust-bindgen")
.status().unwrap();
}
// Create the header file that rust-bindgen needs as input.
let mut gen_h = match File::create("gen.h") {
Err(why) => panic!("could not create gen.h file: {}", Error::description(&why)),
Ok(file) => file
};
match gen_h.write_all(HEADER.as_bytes()) {
Err(why) => panic!("could not write to gen.h: {}", Error::description(&why)),
Ok(_) => ()
};
// Get the compiler and linker flags for the MagickWand library.
let mw_cflags_output = Command::new("MagickWand-config")
.arg("--cflags")
.output().unwrap();
let mw_cflags = std::str::from_utf8(&mw_cflags_output.stdout).unwrap().trim();
let mw_cflags_arr: Vec<&str> = mw_cflags.split_whitespace().collect();
let mw_ldflags_output = Command::new("MagickWand-config")
.arg("--ldflags")
.output().unwrap();
let mw_ldflags = std::str::from_utf8(&mw_ldflags_output.stdout).unwrap().trim();
let mw_ldflags_arr: Vec<&str> = mw_ldflags.split_whitespace().collect();
// Combine all of that in the invocation of rust-bindgen.
Command::new("./rust-bindgen/target/debug/bindgen")
// always include this even though it only makes sense on Mac to
// work around https://github.com/crabtw/rust-bindgen/issues/89
.env("DYLD_LIBRARY_PATH", "/Library/Developer/CommandLineTools/usr/lib")
.args(&mw_cflags_arr[..])
.arg("-builtins")
.arg("-o")
.arg("src/bindings.rs")
.args(&mw_ldflags_arr[..])
.arg("gen.h")
.status().unwrap();
// how to get the output of the command...
// let output = Commad::new(...).output().unwrap();
// let out = std::str::from_utf8(&output.stdout).unwrap();
// println!("cargo:output={}", out);
// let err = std::str::from_utf8(&output.stderr).unwrap();
// println!("cargo:error={}", err);
match std::fs::remove_file("gen.h") {
Err(why) => panic!("could not remove gen.h: {}", Error::description(&why)),
Ok(_) => ()
}
}
// For the sake of easily building and testing on Mac, include the path
// to MagickWand. Chances are MagickWand is in /usr/local/lib, or
// somewhere else that rustc can find it.

1
src/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
bindings.rs

File diff suppressed because it is too large Load Diff