If /usr/local/lib is in the search path, a Homebrew installed libJPEG
can end up conflicting with the system libJPEG as such:
```
$ otool -L /System/Library/Frameworks/ImageIO.framework/Versions/A/ImageIO
/System/Library/Frameworks/ImageIO.framework/Versions/A/ImageIO:
... snip ...
/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libJPEG.dylib (compatibility version 1.0.0, current version 1.0.0)
... snip ...
```
```
$ cargo clean && cargo test
... snip ...
Doc-tests magick_rust
dyld: Symbol not found: __cg_jpeg_resync_to_restart
Referenced from: /System/Library/Frameworks/ImageIO.framework/Versions/A/ImageIO
Expected in: /usr/local/lib/libJPEG.dylib
in /System/Library/Frameworks/ImageIO.framework/Versions/A/ImageIO
```
Removing the /usr/local/lib link search path allows successful
compilation and testing on my OSX 10.11.6, with jpeg 8d installed into
/usr/local/lib via Homebrew. I realize this might be a problem with my
setup and not a universal issue, but as Homebrew is so common I thought
I'd submit this patch anyways.
137 lines
6.2 KiB
Rust
137 lines
6.2 KiB
Rust
/*
|
|
* 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::env;
|
|
use std::fs::File;
|
|
use std::io::prelude::*;
|
|
use std::path::{Path, PathBuf};
|
|
use std::process::Command;
|
|
|
|
static HEADER: &'static str = "#include <wand/MagickWand.h>\n";
|
|
static LIBPATH: &'static str = "/Library/Developer/CommandLineTools/usr/lib";
|
|
|
|
fn main() {
|
|
//
|
|
// If the MagickWand bindings are missing, generate them using
|
|
// rust-bindgen.
|
|
//
|
|
let out_dir = ::std::env::var("OUT_DIR").unwrap();
|
|
let bindings_path_str = out_dir.clone() + "/bindings.rs";
|
|
if !Path::new(&bindings_path_str).exists() {
|
|
// Install rust-bindgen so we can generate the bindings. While the
|
|
// bindgen crate is nice, it does not appear to support setting
|
|
// environment variables like DYLD_LIBRARY_PATH.
|
|
let home_dir = env::home_dir().expect("home directory");
|
|
let mut cargo_bin = PathBuf::from(home_dir);
|
|
cargo_bin.push(".cargo");
|
|
cargo_bin.push("bin");
|
|
cargo_bin.push("bindgen");
|
|
println!("BINDGEN_PATH={:?}", cargo_bin);
|
|
if !cargo_bin.exists() {
|
|
Command::new("cargo")
|
|
.arg("install")
|
|
.arg("bindgen")
|
|
.status().expect("cargo install bindgen");
|
|
}
|
|
|
|
// 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")
|
|
}
|
|
|
|
// Get the compiler flags for the MagickWand library.
|
|
let mw_cflags_output = Command::new("pkg-config")
|
|
.arg("--cflags")
|
|
.arg("MagickWand")
|
|
.output().expect("pkg-config --cflags MagickWand");
|
|
let mw_cflags = std::str::from_utf8(&mw_cflags_output.stdout).unwrap().trim();
|
|
let mw_cflags_arr: Vec<&str> = mw_cflags.split_whitespace().collect();
|
|
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")
|
|
.arg("--libs-only-l")
|
|
.arg("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_arr: Vec<&str> = mw_ldflags.split_whitespace().collect();
|
|
let link_arg = &mw_ldflags_arr[0][2..];
|
|
|
|
let gen_h_path = out_dir.clone() + "/gen.h";
|
|
// Create the header file that rust-bindgen needs as input.
|
|
let mut gen_h = File::create(&gen_h_path).expect("could not create file");
|
|
gen_h.write_all(HEADER.as_bytes()).expect("could not write header file");
|
|
|
|
// Combine all of that in the invocation of rust-bindgen.
|
|
let mut cmd = &mut Command::new(cargo_bin);
|
|
if cfg!(target_os = "macos") {
|
|
// Mac requires that the xcode tools are installed so that
|
|
// rustc can find the clang.dylib file. See also issue
|
|
// https://github.com/crabtw/rust-bindgen/issues/89
|
|
let lib_path = Path::new(LIBPATH);
|
|
if !lib_path.exists() {
|
|
panic!("missing {}, run xcode-select --install", LIBPATH);
|
|
}
|
|
cmd.env("DYLD_LIBRARY_PATH", LIBPATH);
|
|
}
|
|
let mut output_arg = String::from("--output");
|
|
output_arg.push('=');
|
|
output_arg.push_str(&bindings_path_str);
|
|
cmd.arg("--builtins")
|
|
// For the time being, avoid switching to proper Rust enums, as
|
|
// 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);
|
|
cmd.status().expect("rust-bindgen invocation");
|
|
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();
|
|
}
|
|
}
|