use std::env;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::Command;
const REPO: &str = "bloxbean/cardano-client-bindings";
fn main() {
let out_dir = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR"));
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap_or_default();
let lib_file = lib_filename(&target_os);
let src = resolve_source_lib(&lib_file, &out_dir);
let staged = out_dir.join(&lib_file);
if src != staged {
fs::copy(&src, &staged)
.unwrap_or_else(|e| panic!("staging {} -> {}: {e}", src.display(), staged.display()));
}
if target_os == "macos" {
let _ = Command::new("install_name_tool")
.args(["-id", &format!("@rpath/{lib_file}")])
.arg(&staged)
.status();
}
if target_os == "windows" {
if let Some(src_dir) = src.parent() {
let import_src = src_dir.join("libccl.lib");
if import_src.exists() {
let import_dst = out_dir.join("ccl.lib");
fs::copy(&import_src, &import_dst).unwrap_or_else(|e| {
panic!(
"staging {} -> {}: {e}",
import_src.display(),
import_dst.display()
)
});
}
}
}
println!("cargo:rustc-link-search=native={}", out_dir.display());
println!("cargo:rustc-link-lib=dylib=ccl");
if target_os != "windows" {
println!("cargo:rustc-link-arg=-Wl,-rpath,{}", out_dir.display());
}
println!("cargo:rerun-if-env-changed=CCL_LIB_PATH");
println!("cargo:rerun-if-env-changed=CCL_LIB_VERSION");
println!("cargo:rerun-if-changed=build.rs");
}
fn lib_filename(target_os: &str) -> String {
match target_os {
"macos" => "libccl.dylib",
"windows" => "libccl.dll",
_ => "libccl.so",
}
.to_string()
}
fn resolve_source_lib(lib_file: &str, out_dir: &Path) -> PathBuf {
if let Ok(dir) = env::var("CCL_LIB_PATH") {
let p = Path::new(&dir).join(lib_file);
if p.exists() {
return p;
}
}
let in_tree = Path::new("../../core/build/native/nativeCompile").join(lib_file);
if in_tree.exists() {
return in_tree;
}
download_lib(lib_file, out_dir)
}
fn download_lib(lib_file: &str, out_dir: &Path) -> PathBuf {
let version = env::var("CCL_LIB_VERSION").unwrap_or_else(|_| {
format!(
"v{}",
env::var("CARGO_PKG_VERSION").expect("CARGO_PKG_VERSION")
)
});
let platform = platform_tag();
let tarball = format!("cardano-client-lib-{version}-{platform}.tar.gz");
let url = format!("https://github.com/{REPO}/releases/download/{version}/{tarball}");
let dl = out_dir.join(&tarball);
let ok = Command::new("curl")
.args(["-fsSL", "-o"])
.arg(&dl)
.arg(&url)
.status()
.map(|s| s.success())
.unwrap_or(false);
assert!(
ok,
"could not download libccl from {url}\n\
If you are building this repo from source, there is no release for an unreleased version: \
build the native library (./gradlew :core:nativeCompile) or set CCL_LIB_PATH to a \
directory containing {lib_file}."
);
run(
Command::new("tar")
.arg("xzf")
.arg(&dl)
.arg("-C")
.arg(out_dir),
"extract native library",
);
let extracted = out_dir.join(lib_file);
assert!(
extracted.exists(),
"native library {lib_file} not found after extracting {tarball}"
);
extracted
}
fn platform_tag() -> String {
let os = env::var("CARGO_CFG_TARGET_OS").unwrap_or_default();
let arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_default();
if os == "macos" && arch == "x86_64" {
panic!(
"no prebuilt libccl for macOS x86_64 (Intel); build libccl from source and set CCL_LIB_PATH"
);
}
let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap_or_default();
if os == "linux" && target_env == "musl" {
if arch != "x86_64" {
panic!(
"no prebuilt musl libccl for linux/{arch} (GraalVM's --libc=musl is x86_64-only); \
build libccl from source and set CCL_LIB_PATH"
);
}
return "linux-musl-x86_64".to_string();
}
let os = match os.as_str() {
"macos" => "macos",
"windows" => "windows",
_ => "linux",
};
let arch = match arch.as_str() {
"aarch64" => "aarch64",
_ => "x86_64",
};
format!("{os}-{arch}")
}
fn run(cmd: &mut Command, what: &str) {
let status = cmd
.status()
.unwrap_or_else(|e| panic!("failed to run ({what}): {e}"));
assert!(status.success(), "command failed ({what}): {status}");
}