libssh2-sys 0.2.10

Native bindings to the libssh2 library
Documentation
extern crate pkg_config;
extern crate cmake;

#[cfg(target_env = "msvc")]
extern crate vcpkg;

use std::env;
use std::fs::File;
use std::io::prelude::*;
use std::path::{PathBuf, Path};
use std::process::Command;

fn main() {
    if try_vcpkg() {
        return;
    }

    register_dep("Z");
    register_dep("OPENSSL");

    // The system copy of libssh2 is not used by default because it
    // can lead to having two copies of libssl loaded at once.
    // See https://github.com/alexcrichton/ssh2-rs/pull/88
    if env::var("LIBSSH2_SYS_USE_PKG_CONFIG").is_ok() {
        if let Ok(lib) = pkg_config::find_library("libssh2") {
            for path in &lib.include_paths {
                println!("cargo:include={}", path.display());
            }
            return
        }
    }

    if !Path::new("libssh2/.git").exists() {
        let _ = Command::new("git").args(&["submodule", "update", "--init"])
                                   .status();
    }

    let mut cfg = cmake::Config::new("libssh2");

    let target = env::var("TARGET").unwrap();

    // Don't use OpenSSL on Windows, instead use the native Windows backend.
    if target.contains("windows") {
        cfg.define("CRYPTO_BACKEND", "WinCNG");
    } else {
        cfg.define("CRYPTO_BACKEND", "OpenSSL");
    }

    // If libz-sys was built it'll give us an include directory to learn how to
    // link to it, and for MinGW targets we just pass a dummy include dir to
    // ensure it's detected (apparently it isn't otherwise?)
    if !target.contains("windows") && env::var_os("DEP_Z_INCLUDE").is_some() {
        cfg.define("ENABLE_ZLIB_COMPRESSION", "ON")
            .register_dep("Z");
    } else {
        cfg.define("ENABLE_ZLIB_COMPRESSION", "OFF");
    }

    if let Some(path) = env::var_os("DEP_OPENSSL_INCLUDE") {
        if let Some(path) = env::split_paths(&path).next() {
            if let Some(path) = path.to_str() {
                if path.len() > 0 {
                    cfg.define("OPENSSL_INCLUDE_DIR", path);
                }
            }
        }
    }

    // Homebrew deprecated OpenSSL and deliberately hides it from cmake, requiring such opt-in
    if target.contains("darwin") && Path::new("/usr/local/opt/openssl/include/openssl/ssl.h").exists() {
        cfg.define("OPENSSL_ROOT_DIR", "/usr/local/opt/openssl/");
    }

    let dst = cfg.define("BUILD_SHARED_LIBS", "OFF")
                 .define("CMAKE_INSTALL_LIBDIR", "lib")
                 .define("BUILD_EXAMPLES", "OFF")
                 .define("BUILD_TESTING", "OFF")
                 .register_dep("OPENSSL")
                 .register_dep("Z")
                 .build();

    // Unfortunately the pkg-config file generated for libssh2 indicates
    // that it depends on zlib, but most systems don't actually have a
    // zlib.pc, so pkg-config will say that libssh2 doesn't exist. We
    // generally take care of the zlib dependency elsewhere, so we just
    // remove that part from the pkg-config file
    let mut pc = String::new();
    let pkgconfig = dst.join("lib/pkgconfig/libssh2.pc");
    if let Ok(mut f) = File::open(&pkgconfig) {
        f.read_to_string(&mut pc).unwrap();;
        drop(f);
        let pc = pc.replace(",zlib", "");
        let bytes = pc.as_bytes();
        File::create(pkgconfig).unwrap().write_all(bytes).unwrap();
    }

    if target.contains("windows") {
        println!("cargo:rustc-link-lib=bcrypt");
        println!("cargo:rustc-link-lib=crypt32");
        println!("cargo:rustc-link-lib=user32");
    }

    // msvc generates libssh2.lib, everywhere else generates libssh2.a
    if target.contains("msvc") {
        println!("cargo:rustc-link-lib=static=libssh2");
    } else {
        println!("cargo:rustc-link-lib=static=ssh2");
    }
    println!("cargo:rustc-link-search=native={}/lib", dst.display());
    println!("cargo:include={}/include", dst.display());
}

fn register_dep(dep: &str) {
    if let Some(s) = env::var_os(&format!("DEP_{}_ROOT", dep)) {
        prepend("PKG_CONFIG_PATH", Path::new(&s).join("lib/pkgconfig"));
        return
    }
    if let Some(s) = env::var_os(&format!("DEP_{}_INCLUDE", dep)) {
        let root = Path::new(&s).parent().unwrap();
        env::set_var(&format!("DEP_{}_ROOT", dep), root);
        let path = root.join("lib/pkgconfig");
        if path.exists() {
            prepend("PKG_CONFIG_PATH", path);
            return
        }
    }
}

fn prepend(var: &str, val: PathBuf) {
    let prefix = env::var(var).unwrap_or(String::new());
    let mut v = vec![val];
    v.extend(env::split_paths(&prefix));
    env::set_var(var, &env::join_paths(v).unwrap());
}

#[cfg(not(target_env = "msvc"))]
fn try_vcpkg() -> bool { false }

#[cfg(target_env = "msvc")]
fn try_vcpkg() -> bool {
    vcpkg::Config::new()
        .emit_includes(true)
        .probe("libssh2").map(|_| {

        // found libssh2 which depends on openssl and zlib
        vcpkg::Config::new()
            .lib_name("libeay32")
            .lib_name("ssleay32")
            .probe("openssl").expect("configured libssh2 from vcpkg but could not \
                                      find openssl libraries that it depends on");

        vcpkg::Config::new()
            .lib_names("zlib", "zlib1")
            .probe("zlib").expect("configured libssh2 from vcpkg but could not \
                                   find the zlib library that it depends on");

        println!("cargo:rustc-link-lib=crypt32");
        println!("cargo:rustc-link-lib=gdi32");
        println!("cargo:rustc-link-lib=user32");
    }).is_ok()
}