Use new rust-bindgen crate for faster builds

Rather than cloning the bindgen repository and building from source, use
the packaged crate. The result is the rebuilds are much, much faster.
Also, had to make numerous changes for the new version of bindgen.

cargo test passes
This commit is contained in:
Nathan Fiedler
2016-08-03 20:36:52 -07:00
parent 2c94dab05a
commit eec7998d25
6 changed files with 82 additions and 51 deletions

View File

@ -5,6 +5,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
## [0.5.3] - ## [0.5.3] -
### Changed ### Changed
- Update to 0.19.0 version of rust-bindgen; rebuilds are much faster.
- gadomski: add `set_option()` method to wand API. - gadomski: add `set_option()` method to wand API.
- gadomski: add `write_images_blob()` to create animated GIFs. - gadomski: add `write_images_blob()` to create animated GIFs.

View File

@ -7,6 +7,8 @@ A somewhat safe Rust interface to the [ImageMagick](http://www.imagemagick.org/)
* Rust * Rust
* Cargo * Cargo
* ImageMagick (version 6.9 or higher) * ImageMagick (version 6.9 or higher)
* [rust-bindgen](https://github.com/Yamakaky/rust-bindgen) (version 0.19 or higher)
- This will be installed automatically if it is missing.
See the `docs/Development_Setup.md` file for details particular to each platform. See the `docs/Development_Setup.md` file for details particular to each platform.

119
build.rs
View File

@ -14,9 +14,10 @@
* limitations under the License. * limitations under the License.
*/ */
use std::env;
use std::fs::File; use std::fs::File;
use std::io::prelude::*; use std::io::prelude::*;
use std::path::Path; use std::path::{Path, PathBuf};
use std::process::Command; use std::process::Command;
static HEADER: &'static str = "#include <wand/MagickWand.h>\n"; static HEADER: &'static str = "#include <wand/MagickWand.h>\n";
@ -30,33 +31,31 @@ fn main() {
let out_dir = ::std::env::var("OUT_DIR").unwrap(); let out_dir = ::std::env::var("OUT_DIR").unwrap();
let bindings_path_str = out_dir.clone() + "/bindings.rs"; let bindings_path_str = out_dir.clone() + "/bindings.rs";
if !Path::new(&bindings_path_str).exists() { if !Path::new(&bindings_path_str).exists() {
let bindgen_path_str = out_dir.clone() + "/rust-bindgen"; // Install rust-bindgen so we can generate the bindings. While the
let bindgen_path = Path::new(&bindgen_path_str); // bindgen crate is nice, it does not appear to support setting
if !bindgen_path.exists() { // environment variables like DYLD_LIBRARY_PATH.
Command::new("git") let home_dir = env::home_dir().expect("home directory");
.arg("clone") let mut cargo_bin = PathBuf::from(home_dir);
.arg("https://github.com/crabtw/rust-bindgen.git") cargo_bin.push(".cargo");
.arg(bindgen_path) cargo_bin.push("bin");
.status().expect("git clone rust-bindgen"); cargo_bin.push("bindgen");
// Checkout a version of rust-bindgen that is known to work; println!("BINDGEN_PATH={:?}", cargo_bin);
// more recent versions produce code that does not compile (the if !cargo_bin.exists() {
// commit after 8a51860 changes the way enums are generated). Command::new("cargo")
Command::new("git") .arg("install")
.arg("checkout") .arg("bindgen")
.arg("8a51860") .status().expect("cargo install bindgen");
.current_dir(bindgen_path) }
.status().expect("git checkout");
// Check that MagickWand is installed before proceeding.
if !Command::new("pkg-config")
.arg("--exists")
.arg("MagickWand")
.status().unwrap().success() {
panic!("MagickWand library must be installed")
} }
let mut bindgen_bin = bindgen_path.to_path_buf();
bindgen_bin.push("target/debug/bindgen"); // Get the compiler flags for the MagickWand library.
if !bindgen_bin.exists() {
let mut cmd = Command::new("cargo");
cmd.arg("build").current_dir(bindgen_path);
println!("BINDGEN_BUILD={:?}", cmd);
cmd.status().expect("cargo build");
}
// Get the compiler and linker flags for the MagickWand library.
let mw_cflags_output = Command::new("pkg-config") let mw_cflags_output = Command::new("pkg-config")
.arg("--cflags") .arg("--cflags")
.arg("MagickWand") .arg("MagickWand")
@ -64,13 +63,18 @@ fn main() {
let mw_cflags = std::str::from_utf8(&mw_cflags_output.stdout).unwrap().trim(); 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_cflags_arr: Vec<&str> = mw_cflags.split_whitespace().collect();
println!("CFLAGS={:?}", mw_cflags_arr); println!("CFLAGS={:?}", mw_cflags_arr);
// Extract the library name for use with the --link option below.
// This just happens to be the first argument from the output of
// pkg-config when given the --libs-only-l option. We lop off the
// leading "-l" since we only need the name (e.g. "MagickWand-6").
let mw_ldflags_output = Command::new("pkg-config") let mw_ldflags_output = Command::new("pkg-config")
.arg("--libs") .arg("--libs-only-l")
.arg("MagickWand") .arg("MagickWand")
.output().expect("pkg-config --libs MagickWand"); .output().expect("pkg-config --libs-only-l MagickWand");
let mw_ldflags = std::str::from_utf8(&mw_ldflags_output.stdout).unwrap().trim(); let mw_ldflags = std::str::from_utf8(&mw_ldflags_output.stdout).unwrap().trim();
let mw_ldflags_arr: Vec<&str> = mw_ldflags.split_whitespace().collect(); let mw_ldflags_arr: Vec<&str> = mw_ldflags.split_whitespace().collect();
println!("LDFLAGS={:?}", mw_ldflags_arr); let link_arg = &mw_ldflags_arr[0][2..];
let gen_h_path = out_dir.clone() + "/gen.h"; let gen_h_path = out_dir.clone() + "/gen.h";
// Create the header file that rust-bindgen needs as input. // Create the header file that rust-bindgen needs as input.
@ -78,7 +82,7 @@ fn main() {
gen_h.write_all(HEADER.as_bytes()).expect("could not write header file"); gen_h.write_all(HEADER.as_bytes()).expect("could not write header file");
// Combine all of that in the invocation of rust-bindgen. // Combine all of that in the invocation of rust-bindgen.
let mut cmd = &mut Command::new(bindgen_bin); let mut cmd = &mut Command::new(cargo_bin);
if cfg!(target_os = "macos") { if cfg!(target_os = "macos") {
// Mac requires that the xcode tools are installed so that // Mac requires that the xcode tools are installed so that
// rustc can find the clang.dylib file. See also issue // rustc can find the clang.dylib file. See also issue
@ -88,26 +92,49 @@ fn main() {
panic!("missing {}, run xcode-select --install", LIBPATH); panic!("missing {}, run xcode-select --install", LIBPATH);
} }
cmd.env("DYLD_LIBRARY_PATH", LIBPATH); cmd.env("DYLD_LIBRARY_PATH", LIBPATH);
// For the sake of easily building and testing on Mac, include
// For the sake of easily building and testing on Mac, include the path // the path to MagickWand. Chances are MagickWand is in
// to MagickWand. Chances are MagickWand is in /usr/local/lib, or // /usr/local/lib, or somewhere else that rustc can find it.
// somewhere else that rustc can find it.
println!("cargo:rustc-link-search=native=/usr/local/lib"); println!("cargo:rustc-link-search=native=/usr/local/lib");
} }
cmd.args(&mw_cflags_arr[..]) let mut output_arg = String::from("--output");
.arg("-builtins") output_arg.push('=');
.arg("-o") output_arg.push_str(&bindings_path_str);
.arg(bindings_path_str) cmd.arg("--builtins")
.args(&mw_ldflags_arr[..]) // For the time being, avoid switching to proper Rust enums, as
.arg(&gen_h_path); // that would change quite a bit of the existing code. We can
// change that at a later time, and then remove this option.
.arg("--no-rust-enums")
// There are a few places where our code is still using `libc::`
// types, rather than the new default `std::os::raw` types. We
// can switch at a later time and then remove this option.
.arg("--ctypes-prefix=libc")
// Inform bindgen of the library to which we are linking,
// otherwise it may compile but the tests will fail to link
// properly. The -L and -l arguments provided by pkg-config
// apparently go unused with (newer versions of?) clang.
.arg(format!("--link={}", link_arg))
.arg(output_arg)
.arg(&gen_h_path)
.arg("--");
if cfg!(target_os = "macos") {
// Work around issue #361 in rust-bindgen for the time being.
cmd.arg("-U__BLOCKS__");
}
cmd.args(&mw_cflags_arr[..]);
println!("BINDING_GENERATION={:?}", cmd); println!("BINDING_GENERATION={:?}", cmd);
cmd.status().expect("rust-bindgen invocation"); cmd.status().expect("rust-bindgen invocation");
// 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);
std::fs::remove_file(&gen_h_path).expect("could not remove header file"); std::fs::remove_file(&gen_h_path).expect("could not remove header file");
// Work around the include! issue in rustc (as described in the
// rust-bindgen README file) by wrapping the generated code in a
// `pub mod` declaration; see issue #359 in rust-bindgen.
let mut bind_f = File::open(&bindings_path_str).expect("could not open bindings file");
let mut bind_text = String::new();
bind_f.read_to_string(&mut bind_text).expect("could not read bindings file");
let mut file = File::create(&bindings_path_str).expect("could not create bindings file");
file.write(b"pub mod bindings {\n").unwrap();
file.write(bind_text.as_bytes()).unwrap();
file.write(b"\n}").unwrap();
} }
} }

View File

@ -34,7 +34,7 @@ extern crate libc;
mod wand; mod wand;
mod conversions; mod conversions;
pub mod filters; pub mod filters;
mod bindings { include!(concat!(env!("OUT_DIR"), "/bindings.rs")); } include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
pub use wand::*; pub use wand::*;

View File

@ -1,9 +1,9 @@
# #
# Vagrantfile for Ubuntu Linux 14.04 test environment. # Vagrantfile for Ubuntu Linux 16.04 test environment.
# #
Vagrant.configure(2) do |config| Vagrant.configure(2) do |config|
config.vm.box = 'ubuntu/trusty64' config.vm.box = 'ubuntu/xenial64'
# need enough memory to build syntex_syntax crate # need enough memory to build syntex_syntax crate
config.vm.provider 'virtualbox' do |vb| config.vm.provider 'virtualbox' do |vb|

View File

@ -41,11 +41,12 @@ def all():
run('./rustup.sh --yes') run('./rustup.sh --yes')
run('rm -f rustup.sh') run('rm -f rustup.sh')
sudo('apt-get -q -y build-dep imagemagick') sudo('apt-get -q -y build-dep imagemagick')
run('wget -q http://www.imagemagick.org/download/ImageMagick-6.9.5-2.tar.gz') run('wget -q http://www.imagemagick.org/download/ImageMagick-6.9.5-3.tar.gz')
run('tar zxf ImageMagick-6.9.5-2.tar.gz') run('tar zxf ImageMagick-6.9.5-3.tar.gz')
with cd('ImageMagick-*'): with cd('ImageMagick-*'):
run('./configure') run('./configure')
run('make') run('make')
sudo('make install') sudo('make install')
run('rm -rf ImageMagick*') run('rm -rf ImageMagick*')
run('sudo apt-get -q -y install libclang-dev') run('sudo apt-get -q -y install libclang-dev')
run("echo 'export LIBCLANG_PATH=/usr/lib/llvm-3.8/lib' >> .bashrc")