extern crate autotools; extern crate bindgen; use std::{ env, fs, path::{Path, PathBuf}, process::Command, }; fn check_installed(name: &str) -> anyhow::Result<()> { let check = Command::new(name).arg("--version").output(); match check { Ok(output) => { if !output.status.success() { return Err(anyhow::anyhow!( "{} is required, but it's not installed or not in PATH.", name )); } } Err(_) => { return Err(anyhow::anyhow!( "{} is required, but it's not installed or not in PATH.", name )); } } Ok(()) } fn main() -> anyhow::Result<()> { // Check if autoconf is installed check_installed("autoconf")?; check_installed("automake")?; let out_dir = env::var("OUT_DIR").map(PathBuf::from)?; let src_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("jq"); let build_dir = out_dir.join("jq_build"); // Copy the contents of the src_dir to build_dir within OUT_DIR // to avoid modifying the source directory during the build process. // This ensures compliance with Cargo's policy that build scripts // should not modify anything outside of OUT_DIR. if build_dir.exists() { fs::remove_dir_all(&build_dir)?; } fs::create_dir(&build_dir)?; for entry in walkdir::WalkDir::new(&src_dir) { let entry = entry?; let target_path = build_dir.join(entry.path().strip_prefix(&src_dir)?); if entry.file_type().is_dir() { fs::create_dir_all(target_path)?; } else { fs::copy(entry.path(), target_path)?; } } // It seems that modifying the timestamp of the lexer.c file by copying // it to the target directory is necessary to circumvent an error that goes something like: // cc1: fatal error: src/lexer.c: No such file or directory compilation terminated. let lexer_src = src_dir.join("src/lexer.c"); let lexer_target = build_dir.join("src/lexer.c"); fs::copy(lexer_src, lexer_target)?; // parser.c also // error: src/parser.c: No such file or directory let parser_src = src_dir.join("src/parser.c"); let parser_target = build_dir.join("src/parser.c"); fs::copy(parser_src, parser_target)?; // See https://github.com/jqlang/jq/tree/jq-1.7.1?#instructions autotools::Config::new(&build_dir) .reconf("-i") .out_dir(&out_dir) .with("oniguruma", Some("builtin")) .make_args(vec![ // See https://github.com/jqlang/jq/issues/1936 "CPPFLAGS=-D_REENTRANT".into(), ]) .build(); let lib_dir = out_dir.join("lib"); println!("cargo:rustc-link-search=native={}", lib_dir.display()); for lib in &["onig", "jq"] { println!("cargo:rustc-link-lib=static={}", lib); } let bindings = bindgen::Builder::default() .header("jq/src/jq.h") .generate()?; bindings.write_to_file(out_dir.join("bindings.rs"))?; Ok(()) }