use std::{
env, fs, io,
path::{Path, PathBuf},
process::Command,
};
const BRIDGE_SOURCE_ROOT: &str = "bridge/src/main/java";
fn main() {
println!("cargo:rerun-if-changed={BRIDGE_SOURCE_ROOT}");
println!("cargo:rerun-if-env-changed=GHIDRA_INSTALL_DIR");
let install_dir = match env::var_os("GHIDRA_INSTALL_DIR") {
Some(value) => PathBuf::from(value),
None => return,
};
let sources =
java_sources(Path::new(BRIDGE_SOURCE_ROOT)).expect("discover Java bridge sources");
let out_dir = PathBuf::from(env::var_os("OUT_DIR").expect("OUT_DIR is set"));
let classes = out_dir.join("classes");
let bridge_jar = out_dir.join("ghidra-bridge.jar");
if classes.exists() {
fs::remove_dir_all(&classes).expect("clear Java class output directory");
}
fs::create_dir_all(&classes).expect("create Java class output directory");
let classpath = env::join_paths(ghidra_jars(&install_dir).expect("discover Ghidra jars"))
.expect("build javac classpath");
let status = Command::new("javac")
.arg("-cp")
.arg(classpath)
.arg("-proc:none")
.arg("-d")
.arg(&classes)
.args(&sources)
.status()
.expect("run javac for Ghidra bridge");
assert!(status.success(), "javac failed for Ghidra bridge sources");
let status = Command::new("jar")
.arg("--create")
.arg("--file")
.arg(&bridge_jar)
.arg("-C")
.arg(&classes)
.arg(".")
.status()
.expect("package Ghidra bridge jar");
assert!(status.success(), "jar failed for {}", bridge_jar.display());
println!("cargo:rustc-env=GHIDRA_BRIDGE_JAR={}", bridge_jar.display());
}
fn java_sources(root: &Path) -> io::Result<Vec<PathBuf>> {
let mut sources = Vec::new();
visit_java(root, &mut sources)?;
sources.sort();
Ok(sources)
}
fn visit_java(path: &Path, sources: &mut Vec<PathBuf>) -> io::Result<()> {
for entry in fs::read_dir(path)? {
let entry = entry?;
let path = entry.path();
if path.is_dir() {
visit_java(&path, sources)?;
} else if path.extension().is_some_and(|ext| ext == "java") {
sources.push(path);
}
}
Ok(())
}
fn ghidra_jars(install_dir: &Path) -> io::Result<Vec<PathBuf>> {
let root = install_dir.join("Ghidra");
let mut jars = Vec::new();
visit_jars(&root, &mut jars)?;
jars.sort();
Ok(jars)
}
fn visit_jars(path: &Path, jars: &mut Vec<PathBuf>) -> io::Result<()> {
for entry in fs::read_dir(path)? {
let entry = entry?;
let path = entry.path();
if path.is_dir() {
visit_jars(&path, jars)?;
} else if path.extension().is_some_and(|ext| ext == "jar") {
jars.push(path);
}
}
Ok(())
}