tensorflow-sys 0.1.0

The package provides bindings to TensorFlow.
extern crate pkg_config;

use std::path::{Path, PathBuf};
use std::process::Command;

const SOURCE_URL: &'static str = "https://github.com/tensorflow/tensorflow.git";

macro_rules! get(($name:expr) => (::std::env::var($name).unwrap()));

fn main() {
    if pkg_config::find_library("tensorflow").is_ok() {
        return;
    }

    let output = PathBuf::from(&get!("OUT_DIR"));

    if link(&output) {
        return;
    }

    let source = PathBuf::from(&get!("CARGO_MANIFEST_DIR")).join("source");

    if !Path::new(&source.join(".git")).exists() {
        run("git", |command| command.arg("clone")
                                    .arg("--branch=v0.9.0rc0")
                                    .arg("--recurse-submodules")
                                    .arg(SOURCE_URL)
                                    .arg(&source));
    }

    run("./configure", |command| command.current_dir(&source));

    run("bazel", |command| command.current_dir(&source)
                                  .arg(format!("--output_base={}", output.display()))
                                  .arg("build")
                                  .arg(format!("--jobs={}", get!("NUM_JOBS")))
                                  .arg("--compilation_mode=opt")
                                  .arg("tensorflow:libtensorflow.so"));

    assert!(link(&output));
}

fn find(directory: &Path, file: &str) -> Option<PathBuf> {
    for entry in std::fs::read_dir(directory).unwrap() {
        let entry = entry.unwrap();
        let path = entry.path();
        if path.is_dir() {
            if let Some(path) = find(&path, file) {
                return Some(path);
            }
        } else if path.is_file() && path.ends_with(file) {
            return Some(directory.into());
        }
    }
    None
}

fn link(directory: &Path) -> bool {
    match find(directory, "libtensorflow.so") {
        Some(directory) => {
            println!("cargo:rustc-link-lib=dylib=tensorflow");
            println!("cargo:rustc-link-search={}", directory.display());
            true
        },
        _ => false,
    }
}

fn run<F>(name: &str, mut configure: F) where F: FnMut(&mut Command) -> &mut Command {
    let mut command = Command::new(name);
    if !configure(&mut command).status().unwrap().success() {
        panic!("failed to execute {:?}", command);
    }
}