rusty_libimobiledevice 0.2.3

An ergonomic library to communicate with iOS devices
Documentation
// jkcoxson

extern crate bindgen;

use std::{env, fs::canonicalize, path::PathBuf};

fn main() {
    // Tell cargo to invalidate the built crate whenever build files change
    println!("cargo:rerun-if-changed=wrapper.h");
    println!("cargo:rerun-if-changed=build.rs");

    ////////////////////////////
    //   BINDGEN GENERATION   //
    ////////////////////////////

    if cfg!(feature = "pls-generate") {
        // Get gnutls path per OS
        let gnutls_path = match env::consts::OS {
            "linux" => "/usr/include",
            "macos" => "/opt/homebrew/include",
            "windows" => {
                panic!("Generating bindings on Windows is broken, pls remove the pls-generate feature.");
            }
            _ => panic!("Unsupported OS"),
        };

        let bindings = bindgen::Builder::default()
            // The input header we would like to generate
            // bindings for.
            .header("wrapper.h")
            // Include in clang build
            .clang_arg(format!("-I{}", gnutls_path))
            // Tell cargo to invalidate the built crate whenever any of the
            // included header files changed.
            .parse_callbacks(Box::new(bindgen::CargoCallbacks))
            // Finish the builder and generate the bindings.
            .generate()
            // Unwrap the Result and panic on failure.
            .expect("Unable to generate bindings");

        // Write the bindings to the $OUT_DIR/bindings.rs file.
        let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
        bindings
            .write_to_file(out_path.join("bindings.rs"))
            .expect("Couldn't write bindings!");
    }

    if cfg!(feature = "vendored") {
        // Change current directory to OUT_DIR
        let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
        env::set_current_dir(&out_path).unwrap();
        env::set_var("PKG_CONFIG_PATH", out_path.join("lib/pkgconfig"));

        // Lib path setup
        let lib_path = out_path.join("lib");
        if !lib_path.exists() {
            // Create lib directory
            std::fs::create_dir(&lib_path).unwrap();
        }
        let mut lib_path = lib_path.canonicalize().unwrap().display().to_string();

        // Set the LD_LIBRARY_PATH environment variable to lib_path
        env::set_var("LD_LIBRARY_PATH", lib_path.clone());

        // Include path setup
        let include_path = out_path.join("include");
        if !include_path.exists() {
            // Create include directory
            std::fs::create_dir(&include_path).unwrap();
        }
        let mut include_path = include_path.canonicalize().unwrap().display().to_string();

        // Search for where openssl-src placed my libs
        env::set_current_dir("../../").unwrap();
        let mut openssl_found = false;
        loop {
            for path in std::fs::read_dir(".").unwrap() {
                let path = path.unwrap().path();
                if !path.is_dir() {
                    continue;
                }
                if path.to_str().unwrap().contains("openssl-sys") {
                    let install_path = path.join("out").join("openssl-build").join("install");
                    if install_path.exists() {
                        println!(
                            "cargo:rustc-link-search=native={}",
                            install_path.join("lib").canonicalize().unwrap().display()
                        );
                        include_path = format!(
                            "{} -I{}",
                            include_path,
                            install_path
                                .join("include")
                                .canonicalize()
                                .unwrap()
                                .display()
                        );
                        lib_path = format!(
                            "{} -L{}",
                            lib_path,
                            install_path.join("lib").canonicalize().unwrap().display()
                        );
                        // Copy the pkgconfig files to the OUT_DIR
                        let ssl_pkg_config_path = install_path.join("lib").join("pkgconfig");

                        // Create the path
                        std::fs::create_dir_all(&ssl_pkg_config_path).unwrap();
                        let ssl_pkg_config_path = ssl_pkg_config_path.canonicalize().unwrap();

                        let out_path = PathBuf::from(env::var("OUT_DIR").unwrap())
                            .join("lib")
                            .join("pkgconfig");
                        std::fs::create_dir_all(&out_path).unwrap();
                        let out_path = out_path.canonicalize().unwrap();
                        for path in std::fs::read_dir(&ssl_pkg_config_path).unwrap() {
                            let path = path.unwrap().path();
                            if !path.is_file() {
                                continue;
                            }
                            let file_name = path.file_name().unwrap().to_str().unwrap();
                            std::fs::copy(&path, out_path.join(file_name)).unwrap();
                        }
                        openssl_found = true;
                    }
                }
            }
            if openssl_found {
                break;
            }
            std::thread::sleep(std::time::Duration::from_secs(10));
        }
        if !openssl_found {
            panic!("\nopenssl-src was not found, exiting\n");
        }

        // Clone the vendored libraries
        repo_setup("https://github.com/libimobiledevice/libplist.git");
        repo_setup("https://github.com/libimobiledevice/libimobiledevice-glue.git");
        repo_setup("https://github.com/libimobiledevice/libusbmuxd.git");
        repo_setup("https://github.com/libimobiledevice/libtatsu.git");
        repo_setup("https://github.com/libimobiledevice/libimobiledevice.git");

        // Remove tools from libimobiledevice's makefile. There's no point to build them, and they cause errors on MacOS.
        let mut makefile = std::fs::read_to_string("libimobiledevice/Makefile.in").unwrap();
        makefile = makefile.replace("tools", "");
        std::fs::write("libimobiledevice/Makefile.in", makefile).unwrap();

        let mut c_flags = vec![];
        let mut cxx_flags = vec![];

        // If building for Windows, set the env var for mbedtls
        if env::var("TARGET").unwrap().contains("windows") {
            env::set_var("WINDOWS_BUILD", "1");

            // Windows needs extra crap
            println!("cargo:rustc-link-lib=dylib=iphlpapi");
            println!("cargo:rustc-link-lib=dylib=shell32");
            println!("cargo:rustc-link-lib=dylib=ole32");
        }
        if env::var("TARGET").unwrap().contains("apple") {
            println!("cargo:rustc-env=MACOSX_DEPLOYMENT_TARGET=10.13");
            c_flags.push("-mmacosx-version-min=10.13".to_string());
            cxx_flags.push("-mmacosx-version-min=10.13".to_string());
        }
        c_flags.push(format!("-L{} -I{}", lib_path, include_path));

        // Build those bad bois
        let mut dst = autotools::Config::new("libplist");
        let mut dst = dst.without("cython", None);
        for flag in &c_flags {
            dst = dst.cflag(flag);
        }
        for flag in &cxx_flags {
            dst = dst.cxxflag(flag);
        }
        let dst = dst.build();

        println!(
            "cargo:rustc-link-search=native={}",
            dst.join("lib").display()
        );

        let mut dst = autotools::Config::new("libimobiledevice-glue");
        let dst = dst.without("cython", None);
        let mut dst = dst.env("PKG_CONFIG_PATH", out_path.join("lib/pkgconfig"));
        for flag in &c_flags {
            dst = dst.cflag(flag);
        }
        for flag in &cxx_flags {
            dst = dst.cxxflag(flag);
        }
        let dst = dst.build();

        println!("cargo:rustc-link-search=native={}", dst.display());

        let mut dst = autotools::Config::new("libusbmuxd");
        let dst = dst.without("cython", None);
        let mut dst = dst.env("PKG_CONFIG_PATH", out_path.join("lib/pkgconfig"));
        for flag in &c_flags {
            dst = dst.cflag(flag);
        }
        for flag in &cxx_flags {
            dst = dst.cxxflag(flag);
        }
        let dst = dst.build();

        println!(
            "cargo:rustc-link-search=native={}",
            dst.join("lib").display()
        );

        let mut dst = autotools::Config::new("libtatsu");
        let dst = dst.without("cython", None);
        let mut dst = dst.env("PKG_CONFIG_PATH", out_path.join("lib/pkgconfig"));
        for flag in &c_flags {
            dst = dst.cflag(flag);
        }
        for flag in &cxx_flags {
            dst = dst.cxxflag(flag);
        }
        let dst = dst.build();

        println!(
            "cargo:rustc-link-search=native={}",
            dst.join("lib").display()
        );

        let mut dst = autotools::Config::new("libimobiledevice");
        let dst = dst.without("cython", None);
        let mut dst = dst.env("PKG_CONFIG_PATH", out_path.join("lib/pkgconfig"));
        for flag in &c_flags {
            dst = dst.cflag(flag);
        }
        for flag in &cxx_flags {
            dst = dst.cxxflag(flag);
        }
        let dst = dst.build();

        println!(
            "cargo:rustc-link-search=native={}",
            dst.join("lib").display()
        );
    } else {
        // Check if folder ./override exists
        let override_path = PathBuf::from("./override").join(env::var("TARGET").unwrap());
        if override_path.exists() {
            println!(
                "cargo:rustc-link-search={}",
                canonicalize(&override_path).unwrap().display()
            );
        }

        println!("cargo:rustc-link-search=/usr/local/lib");
        println!("cargo:rustc-link-search=/usr/lib");
        println!("cargo:rustc-link-search=/opt/homebrew/lib");
        println!("cargo:rustc-link-search=/usr/local/opt/libimobiledevice/lib");
        println!("cargo:rustc-link-search=/usr/local/opt/libusbmuxd/lib");
        println!("cargo:rustc-link-search=/usr/local/opt/libimobiledevice-glue/lib");
    }
    let location_determinator = if cfg!(feature = "static") {
        "static"
    } else {
        "dylib"
    };

    // Link libi* deps
    println!(
        "cargo:rustc-link-lib={}=imobiledevice-1.0",
        location_determinator
    );
    println!("cargo:rustc-link-lib={}=usbmuxd-2.0", location_determinator);
    println!(
        "cargo:rustc-link-lib={}=imobiledevice-glue-1.0",
        location_determinator
    );
    println!("cargo:rustc-link-lib={}=plist-2.0", location_determinator);

    println!("cargo:rustc-link-lib={location_determinator}=crypto");
    println!("cargo:rustc-link-lib={location_determinator}=ssl");
}

fn repo_setup(url: &str) {
    let mut cmd = std::process::Command::new("git");
    cmd.arg("clone");
    cmd.arg("--depth=1");
    cmd.arg(url);
    cmd.output().unwrap();
    env::set_current_dir(url.split('/').last().unwrap().replace(".git", "")).unwrap();
    env::set_var("NOCONFIGURE", "1");
    let mut cmd = std::process::Command::new("./autogen.sh");
    let _ = cmd.output();
    env::remove_var("NOCONFIGURE");
    env::set_current_dir("..").unwrap();
}