rust_jsc_sys 0.2.2

Low-level bindings to JavaScriptCore
Documentation
use std::env;
extern crate pkg_config;

fn check_supported_platform() {
    let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
    let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();

    if target_os != "linux" && target_os != "macos" {
        panic!("Unsupported target OS: {}", target_os);
    }

    if target_arch != "x86_64" && target_arch != "aarch64" {
        panic!("Unsupported target architecture: {}", target_arch);
    }
}

fn static_lib_file() -> String {
    let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
    let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
    let platform = match (target_os.as_ref(), target_arch.as_ref()) {
        ("linux", "x86_64") => "x86_64-unknown-linux-gnu",
        ("linux", "aarch64") => "aarch64-unknown-linux-gnu",
        ("macos", "x86_64") => "x86_64-apple-darwin",
        ("macos", "aarch64") => "aarch64-apple-darwin",
        // TODO: Support windows
        // ("windows", "x86_64") => "x86_64-pc-windows-msvc",
        // ("windows", "i686") => "i686-pc-windows-msvc",
        _ => panic!("Unsupported target OS or architecture: {}-{}", target_os, target_arch),
    };
    format!("libjsc-{}.a.gz", platform)
}

fn static_lib_url() -> String {
    if let Ok(custom_archive) = env::var("RUST_JSC_CUSTOM_ARCHIVE") {
        return custom_archive;
    }

    check_supported_platform();

    let default_base = "https://github.com/kevincaicedo/rust-jsc/releases/download";
    let base = env::var("RUST_JSC_MIRROR").unwrap_or_else(|_| default_base.into());
    let version = env::var("CARGO_PKG_VERSION").unwrap();

    let platform_file = static_lib_file();
    format!("{}/v{}/{}", base, version, platform_file)
}

// use a python script that receives the URL and downloads the file also passing the output path
fn fetch_static_lib() {
    let url = static_lib_url();
    let output_path = env::var("OUT_DIR").unwrap();
    let filename = static_lib_file();
    let version = env::var("CARGO_PKG_VERSION").unwrap();

    let output_path = format!("{}/{}", output_path, version);

    let output = std::process::Command::new("python3")
        .arg("scripts/download_file.py")
        .arg(url.clone())
        .arg(output_path)
        .arg(filename)
        .output();

    if let Err(e) = output {
        // panic and show the error and url
        panic!("Failed to download static library: {}\n{}", e, url);
    }

    let output = output.unwrap();
    if !output.status.success() {
        panic!("Failed to download static library: {:?}", output);
    }
}

fn extract_static_lib() {
    let output_path = env::var("OUT_DIR").unwrap();
    let version = env::var("CARGO_PKG_VERSION").unwrap();
    let output_path = format!("{}/{}", output_path, version);
    let filename = static_lib_file();

    let output = std::process::Command::new("tar")
        .arg("-xvf")
        .arg(format!("{}/{}", output_path, filename))
        .arg("-C")
        .arg(output_path)
        .output()
        .expect("Failed to extract static library");

    if !output.status.success() {
        panic!("Failed to extract static library: {:?}", output);
    }
}

#[cfg(target_os = "macos")]
#[cfg(feature = "patches")]
fn main() {
    let is_cargo_doc = env::var_os("DOCS_RS").is_some();
    // if building docs, don't download the static lib
    if is_cargo_doc {
        return;
    }

    println!("cargo:rerun-if-env-changed={}", "RUST_JSC_CUSTOM_BUILD_PATH");
    println!("cargo:rerun-if-env-changed={}", "SYSTEM_LIBS_PATH");
    println!("cargo:rerun-if-env-changed={}", "RUST_JSC_MIRROR");
    println!("cargo:rerun-if-env-changed={}", "RUST_JSC_CUSTOM_ARCHIVE");

    // if custom path for the static lib is set use it, otherwise download the static lib
    if let Ok(custom_build_path) = env::var("RUST_JSC_CUSTOM_BUILD_PATH") {
        println!("cargo:rustc-link-search=native={}", custom_build_path);
    } else {
        let output_path = env::var("OUT_DIR").unwrap();
        let version = env::var("CARGO_PKG_VERSION").unwrap();
        let output_path = format!("{}/{}", output_path, version);
        let static_lib_file = static_lib_file();

        // if archive file is not found in outdir, download it
        if !std::path::Path::new(&format!("{}/{}", output_path, static_lib_file)).exists() {
            fetch_static_lib();
            extract_static_lib();
        }

        // set search native path to the output directory
        println!("cargo:rustc-link-search=native={}", output_path);
    }

    let lib_dir = env::var("SYSTEM_LIBS_PATH").unwrap_or_else(|_| "/usr/lib".into());
    println!("cargo:rustc-link-search={}", lib_dir);

    // dylib
    println!("cargo:rustc-link-lib=dylib=c++");
    println!("cargo:rustc-link-lib=dylib=m");
    println!("cargo:rustc-link-lib=dylib=dl");
    println!("cargo:rustc-link-lib=dylib=icucore");

    // static libs
    println!("cargo:rustc-link-lib=static=JavaScriptCore");
    println!("cargo:rustc-link-lib=static=WTF");
    println!("cargo:rustc-link-lib=static=bmalloc");
}

// is macOS and not using custom JavaScriptCore framework
#[cfg(target_os = "macos")]
#[cfg(not(feature = "patches"))]
fn main() {
    println!("cargo:rustc-link-lib=framework=JavaScriptCore");
}

#[cfg(target_os = "linux")]
#[cfg(feature = "patches")]
fn main() {
    let is_cargo_doc = env::var_os("DOCS_RS").is_some();
    // if building docs, don't download the static lib
    if is_cargo_doc {
        return;
    }

    println!("cargo:rerun-if-env-changed={}", "RUST_JSC_CUSTOM_BUILD_PATH");
    println!("cargo:rerun-if-env-changed={}", "SYSTEM_LIBS_PATH");
    println!("cargo:rerun-if-env-changed={}", "RUST_JSC_MIRROR");
    println!("cargo:rerun-if-env-changed={}", "RUST_JSC_CUSTOM_ARCHIVE");
    
    // if custom path for the static lib is set use it, otherwise download the static lib
    if let Ok(custom_build_path) = env::var("RUST_JSC_CUSTOM_BUILD_PATH") {
        println!("cargo:rustc-link-search=native={}", custom_build_path);
    } else {
        let output_path = env::var("OUT_DIR").unwrap();
        let version = env::var("CARGO_PKG_VERSION").unwrap();
        let output_path = format!("{}/{}", output_path, version);
        let static_lib_file = static_lib_file();

        // if archive file is not found in outdir, download it
        if !std::path::Path::new(&format!("{}/{}", output_path, static_lib_file)).exists()
        {
            fetch_static_lib();
            extract_static_lib();
        }

        // set search native path to the output directory
        println!("cargo:rustc-link-search=native={}", output_path);
    }

    // static libs
    println!("cargo:rustc-link-lib=static=stdc++");
    println!("cargo:rustc-link-lib=static=icui18n");
    println!("cargo:rustc-link-lib=static=icuuc");
    println!("cargo:rustc-link-lib=static=icudata");
    println!("cargo:rustc-link-lib=static=atomic");

    println!("cargo:rustc-link-lib=static=JavaScriptCore");
    println!("cargo:rustc-link-lib=static=WTF");
    println!("cargo:rustc-link-lib=static=bmalloc");
}

#[cfg(target_os = "linux")]
#[cfg(not(feature = "patches"))]
fn main() {
    pkg_config::probe_library("javascriptcoregtk-6.0").unwrap();
}