use std::{collections::HashSet, env, path::PathBuf, process::Command}; const MIN_VERSION: &str = "1.1.2"; const MAX_VERSION: &str = "1.2.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: {}", 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: {}", d.to_string_lossy()); } println!("cargo:include={}", d.to_string_lossy()); } println!("cargo:rerun-if-env-changed=OPENCC_LIBS"); let target = env::var("TARGET").unwrap(); 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()] // TODO: not sure } 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() { env::set_var(name, value); } } fn find_opencc_lib_dirs() -> Vec { 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::>()) .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") } fn find_opencc_include_dirs() -> Vec { 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::>()) .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") } fn find_opencc_dir() -> Result { println!("cargo:rerun-if-env-changed=OPENCC_DIR"); env::var("OPENCC_DIR").map(PathBuf::from) } fn determine_mode>(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()) .map(|e| e.unwrap()) .map(|e| e.file_name()) .filter_map(|e| e.into_string().ok()) .collect::>(); 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 the required files to either statically \ or dynamically link OpenCC", libdirs ); }, (true, true) => {}, } "dylib" } fn run_pkg_config() -> pkg_config::Library { pkg_config::Config::new() .cargo_metadata(false) .atleast_version(MIN_VERSION) .probe("opencc") .unwrap(); if !Command::new("pkg-config") .arg(format!("--max-version={}", MAX_VERSION)) .arg("opencc") .status() .unwrap() .success() { panic!("OpenCC version must be no higher than {}", MAX_VERSION); } pkg_config::Config::new().cargo_metadata(false).probe("opencc").unwrap() }