use std::path::{Path, PathBuf};
use std::{env, fs};
fn main() {
let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
let src_dir = manifest_dir.join("vendor/udpipe/src");
let mut sources = Vec::new();
let subdirs = [
"model",
"morphodita",
"parsito",
"sentence",
"unilib",
"tokenizer",
"trainer",
"utils",
];
for entry in fs::read_dir(&src_dir).expect("Failed to read UDPipe src directory") {
let entry = entry.expect("Failed to read directory entry");
let path = entry.path();
if path.extension().is_some_and(|ext| ext == "cpp") {
sources.push(path);
}
}
for subdir in &subdirs {
let subdir_path = src_dir.join(subdir);
if subdir_path.exists() {
collect_sources_recursive(&subdir_path, &mut sources);
}
}
let mut build = cc::Build::new();
let target = env::var("TARGET").unwrap();
let rustflags = env::var("RUSTFLAGS").unwrap_or_default();
let coverage_enabled = env::var("CARGO_LLVM_COV").is_ok() && target.contains("linux");
let asan_enabled = rustflags.contains("sanitizer=address");
let tsan_enabled = rustflags.contains("sanitizer=thread");
let sanitizer_enabled = asan_enabled || tsan_enabled;
if coverage_enabled {
build
.flag("-fprofile-instr-generate")
.flag("-fcoverage-mapping")
.flag("-O1")
.flag("-g");
} else if sanitizer_enabled {
build.flag("-O1").flag("-g").flag("-fno-omit-frame-pointer");
if asan_enabled {
build.flag("-fsanitize=address");
}
if tsan_enabled {
build.flag("-fsanitize=thread");
}
build.flag("-fsanitize=undefined");
} else {
build.opt_level(2).define("NDEBUG", None);
}
build
.cpp(true)
.flag_if_supported("-std=c++11")
.flag_if_supported("-w") .include(manifest_dir.join("include"))
.include(&src_dir)
.include(src_dir.join("model"))
.include(src_dir.join("morphodita"))
.include(src_dir.join("parsito"))
.include(src_dir.join("sentence"))
.include(src_dir.join("unilib"))
.include(src_dir.join("utils"))
.include(src_dir.join("tokenizer"))
.include(src_dir.join("trainer"));
for source in &sources {
build.file(source);
}
build.file(manifest_dir.join("src/udpipe_wrapper.cpp"));
build.compile("udpipe");
if target.contains("apple") {
println!("cargo:rustc-link-lib=c++");
} else if target.contains("windows") && target.contains("msvc") {
} else {
println!("cargo:rustc-link-lib=stdc++");
}
println!("cargo:rerun-if-changed=src/udpipe_wrapper.cpp");
println!("cargo:rerun-if-changed=include/udpipe_rs/udpipe_wrapper.h");
println!("cargo:rerun-if-changed=vendor/udpipe/src");
}
fn collect_sources_recursive(dir: &Path, sources: &mut Vec<PathBuf>) {
let entries = fs::read_dir(dir)
.unwrap_or_else(|e| panic!("Failed to read directory {}: {e}", dir.display()));
for entry in entries {
let entry = entry.expect("Failed to read directory entry");
let path = entry.path();
if path.is_dir() {
collect_sources_recursive(&path, sources);
} else if path.extension().is_some_and(|ext| ext == "cpp") {
sources.push(path);
}
}
}