use std::{collections::HashSet, env, path::PathBuf, process::Command};
const MIN_VERSION: &str = "1.4.0";
const MAX_VERSION: &str = "1.5.0";
fn main() {
if env::var("DOCS_RS").is_ok() {
return;
}
if cfg!(target_os = "freebsd") {
env_var_set_default("OPENCC_INCLUDE_DIRS", "/usr/include/opencc");
env_var_set_default("OPENCC_LIB_DIRS", "/usr/lib");
env_var_set_default("OPENCC_LIBS", "opencc");
}
let lib_dirs = find_opencc_lib_dirs();
for d in &lib_dirs {
if !d.exists() {
panic!(
"OpenCC library directory does not exist: {}. Set OPENCC_LIB_DIRS or OPENCC_DIR \
to the correct OpenCC location.",
d.to_string_lossy()
);
}
println!("cargo:rustc-link-search=native={}", d.to_string_lossy());
}
let include_dirs = find_opencc_include_dirs();
for d in &include_dirs {
if !d.exists() {
panic!(
"OpenCC include directory does not exist: {}. Set OPENCC_INCLUDE_DIRS or \
OPENCC_DIR to the correct OpenCC location.",
d.to_string_lossy()
);
}
println!("cargo:include={}", d.to_string_lossy());
}
println!("cargo:rerun-if-env-changed=OPENCC_LIBS");
let target = env::var("TARGET").expect("Cargo did not provide TARGET to the build script.");
let libs_env = env::var("OPENCC_LIBS").ok();
let libs = match libs_env {
Some(ref v) => v.split(':').map(|x| x.to_owned()).collect(),
None => {
#[allow(clippy::if_same_then_else)]
if target.contains("windows") {
vec!["opencc".to_string()] } else if target.contains("freebsd") {
vec!["opencc".to_string()]
} else {
run_pkg_config().libs
}
},
};
let kind = determine_mode(&lib_dirs, libs.as_slice());
for lib in libs.into_iter() {
println!("cargo:rustc-link-lib={}={}", kind, lib);
}
println!("cargo:rerun-if-env-changed=OPENCC_DYLIB_STDCPP");
let kind = env::var("OPENCC_DYLIB_STDCPP");
match kind.as_ref().map(|s| &s[..]) {
Ok("0") => (),
Ok(_) => println!("cargo:rustc-link-lib=dylib=stdc++"),
Err(_) => (),
}
println!("cargo:rerun-if-env-changed=OPENCC_STATIC_STDCPP");
let kind = env::var("OPENCC_STATIC_STDCPP");
match kind.as_ref().map(|s| &s[..]) {
Ok("0") => (),
Ok(_) => println!("cargo:rustc-link-lib=static=stdc++"),
Err(_) => (),
}
}
fn env_var_set_default(name: &str, value: &str) {
if env::var(name).is_err() {
unsafe {
env::set_var(name, value);
}
}
}
fn find_opencc_lib_dirs() -> Vec<PathBuf> {
println!("cargo:rerun-if-env-changed=OPENCC_LIB_DIRS");
let sep = if cfg!(target_os = "windows") { ";" } else { ":" };
env::var("OPENCC_LIB_DIRS")
.map(|x| x.split(sep).map(PathBuf::from).collect::<Vec<PathBuf>>())
.or_else(|_| Ok(vec![find_opencc_dir()?.join("lib")]))
.or_else(|_: env::VarError| -> Result<_, env::VarError> { Ok(run_pkg_config().link_paths) })
.expect(
"Couldn't find OpenCC library directory. Set OPENCC_LIB_DIRS, OPENCC_DIR, or make \
pkg-config find OpenCC.",
)
}
fn find_opencc_include_dirs() -> Vec<PathBuf> {
println!("cargo:rerun-if-env-changed=OPENCC_INCLUDE_DIRS");
let sep = if cfg!(target_os = "windows") { ";" } else { ":" };
env::var("OPENCC_INCLUDE_DIRS")
.map(|x| x.split(sep).map(PathBuf::from).collect::<Vec<PathBuf>>())
.or_else(|_| Ok(vec![find_opencc_dir()?.join("include")]))
.or_else(|_: env::VarError| -> Result<_, env::VarError> {
Ok(run_pkg_config().include_paths)
})
.expect(
"Couldn't find OpenCC include directory. Set OPENCC_INCLUDE_DIRS, OPENCC_DIR, or make \
pkg-config find OpenCC.",
)
}
fn find_opencc_dir() -> Result<PathBuf, env::VarError> {
println!("cargo:rerun-if-env-changed=OPENCC_DIR");
env::var("OPENCC_DIR").map(PathBuf::from)
}
fn determine_mode<T: AsRef<str>>(libdirs: &[PathBuf], libs: &[T]) -> &'static str {
println!("cargo:rerun-if-env-changed=OPENCC_STATIC");
let kind = env::var("OPENCC_STATIC").ok();
match kind.as_ref().map(|s| &s[..]) {
Some("0") => return "dylib",
Some(_) => return "static",
None => {},
}
let files = libdirs
.iter()
.flat_map(|d| {
d.read_dir().unwrap_or_else(|error| {
panic!("Cannot read OpenCC library directory {}: {}", d.to_string_lossy(), error)
})
})
.map(|e| e.expect("Cannot read an entry from the OpenCC library directory."))
.map(|e| e.file_name())
.filter_map(|e| e.into_string().ok())
.collect::<HashSet<_>>();
let can_static = libs.iter().all(|l| {
files.contains(&format!("lib{}.a", l.as_ref()))
|| files.contains(&format!("{}.lib", l.as_ref()))
});
let can_dylib = libs.iter().all(|l| {
files.contains(&format!("lib{}.so", l.as_ref()))
|| files.contains(&format!("{}.dll", l.as_ref()))
|| files.contains(&format!("lib{}.dylib", l.as_ref()))
});
match (can_static, can_dylib) {
(true, false) => return "static",
(false, true) => return "dylib",
(false, false) => {
panic!(
"OpenCC libdirs at `{:?}` do not contain libraries for {:?}. Set OPENCC_LIBS or \
check OPENCC_LIB_DIRS.",
libdirs,
libs.iter().map(|l| l.as_ref()).collect::<Vec<_>>()
);
},
(true, true) => {},
}
"dylib"
}
fn run_pkg_config() -> pkg_config::Library {
let library = pkg_config::Config::new()
.cargo_metadata(false)
.atleast_version(MIN_VERSION)
.probe("opencc")
.expect(
"pkg-config could not find OpenCC. Set OPENCC_DIR, OPENCC_LIB_DIRS, \
OPENCC_INCLUDE_DIRS, and OPENCC_LIBS if OpenCC is not installed for pkg-config.",
);
if !Command::new("pkg-config")
.arg(format!("--max-version={}", MAX_VERSION))
.arg("opencc")
.status()
.expect("Failed to run pkg-config for OpenCC version checking.")
.success()
{
panic!("OpenCC version must be no higher than {}", MAX_VERSION);
}
library
}