openconnect-sys 0.1.5

Rust bindings for OpenConnect
Documentation
#![allow(unused_imports)]

mod download_prebuilt;
mod lib_prob;

use lib_prob::*;
use std::env;
use std::path::PathBuf;

use crate::download_prebuilt::download_prebuilt_from_sourceforge;

// TODO: optimize path search
fn main() {
    // if building docs, skip build
    if env::var("DOCS_RS").is_ok() {
        return;
    }

    let use_prebuilt =
        env::var("OPENCONNECT_USE_PREBUILT").unwrap_or("false".to_string()) == "true";

    let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
    let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
    let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap();

    let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
    let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
    let openconnect_src_dir = out_path.join("openconnect");

    let current_dir = env::current_dir().unwrap();

    // statically link openconnect
    let openconnect_lib = openconnect_src_dir.join(".libs");
    let static_lib = openconnect_lib.join("libopenconnect.a");

    if !static_lib.exists() {
        if use_prebuilt {
            download_prebuilt_from_sourceforge(out_path.clone());
        } else {
            let script = current_dir.join("scripts/nix.sh");
            let _ = std::process::Command::new("sh")
                .args([
                    script.to_str().unwrap(),
                    openconnect_src_dir.to_str().unwrap(),
                ])
                .output()
                .expect("failed to execute process");
        }
    }

    println!(
        "cargo:rustc-link-search={}",
        openconnect_lib.to_str().unwrap()
    );
    println!("cargo:rustc-link-lib=static=openconnect");

    // windows linking
    #[cfg(target_os = "windows")]
    {
        resolve_mingw64_lib_path();

        let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
        let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
        let target_path = out_path.ancestors().nth(3).unwrap();
        print_build_warning!("target_path: {}", target_path.to_string_lossy());
        println!("cargo:rustc-link-search={}", target_path.to_string_lossy());

        let wintun_dll_source = format!("{}/wintun.dll", manifest_dir);
        let wintun_dll_target = format!("{}/wintun.dll", target_path.to_string_lossy());
        std::fs::copy(wintun_dll_source, wintun_dll_target).unwrap();

        try_pkg_config(vec!["openssl", "libxml-2.0", "zlib", "liblz4", "iconv"]);
        println!("cargo:rustc-link-lib=static=intl");
        println!("cargo:rustc-link-lib=dylib=wintun")
    }

    // link c++ stdlib
    #[cfg(target_os = "linux")]
    {
        println!("cargo:rustc-link-search=/usr/local/lib");
        println!("cargo:rustc-link-search=/usr/lib");
        println!("cargo:rustc-link-search=/usr/lib/x86_64-linux-gnu");
        // TODO: for stdc++, optimize auto search
        println!("cargo:rustc-link-search=/usr/lib/gcc/x86_64-linux-gnu/11");

        // link for openssl
        println!("cargo:rustc-link-lib=static=crypto");
        println!("cargo:rustc-link-lib=static=ssl");

        // link for xml2
        println!("cargo:rustc-link-lib=static=xml2");
        println!("cargo:rustc-link-lib=static=z");
        println!("cargo:rustc-link-lib=static=lzma");
        println!("cargo:rustc-link-lib=static=lz4");
        println!("cargo:rustc-link-lib=static=icui18n");
        println!("cargo:rustc-link-lib=static=icudata");
        println!("cargo:rustc-link-lib=static=icuuc");

        // link for stdc++
        println!("cargo:rustc-link-lib=static=stdc++");
    }

    #[cfg(target_os = "macos")]
    {
        // the order is important!!!
        println!("cargo:rustc-link-search=/usr/lib");
        println!("cargo:rustc-link-search=/usr/local/lib");

        try_pkg_config(vec!["openssl", "libxml-2.0", "zlib", "liblz4"]);

        // link for c++ stdlib
        #[cfg(target_arch = "x86_64")]
        {
            println!("cargo:rustc-link-lib=static=intl"); // fix for x86
        }
        println!("cargo:rustc-link-lib=dylib=c++");
        println!("cargo:rustc-link-lib=dylib=c++abi");

        // if you want to link c++ stdlib statically, use llvm c++ stdlib
        // println!("cargo:rustc-link-search=/opt/homebrew/opt/llvm/lib/c++");
        // println!("cargo:rustc-link-lib=static=c++");
        // println!("cargo:rustc-link-lib=static=c++abi");
    }

    println!("cargo:rerun-if-changed=wrapper.h");
    println!("cargo:rerun-if-changed=c-src/helper.h");
    println!("cargo:rerun-if-changed=c-src/helper.c");

    // ===== compile helper.c start =====
    let mut build = cc::Build::new();
    let build = build.file("c-src/helper.c").include("c-src");
    // .include(openconnect_src_dir.to_str().unwrap()); // maybe not needed
    build.compile("helper");
    // ===== compile helper.c end =====

    // The bindgen::Builder is the main entry point
    // to bindgen, and lets you build up options for
    // the resulting bindings.
    let bindings = bindgen::Builder::default()
        // The input header we would like to generate
        // bindings for.
        .header("wrapper.h")
        .header("c-src/helper.h")
        .clang_arg(format!("-I{}", openconnect_src_dir.to_str().unwrap()))
        .enable_function_attribute_detection()
        .trust_clang_mangling(true)
        // Tell cargo to invalidate the built crate whenever any of the
        // included header files changed.
        .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
        // 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.
    bindings
        .write_to_file(manifest_dir.join(format!(
            "src/bindings_{}_{}{}.rs",
            target_arch,
            target_os,
            if target_env.is_empty() {
                "".to_string()
            } else {
                format!("_{}", target_env)
            }
        )))
        .expect("Couldn't write bindings!");
}