// build.rs // // This file is a part of the eXtremeDB source code // Copyright (c) 2020 McObject LLC // All Rights Reserved use std::env; use std::fs::File; use std::path::{Path, PathBuf}; use serde::Deserialize; use serde_json; enum TransactionManager { Exclusive, MURSIW, MVCC, } const TMGR_EXCL: &str = "excl"; const TMGR_MURSIW: &str = "mursiw"; const TMGR_MVCC: &str = "mvcc"; const ENV_MCO_ROOT: &str = "MCO_ROOT"; const ENV_CFG_DYLIB: &str = "MCORS_CFG_DYLIB"; const ENV_CFG_DPTR: &str = "MCORS_CFG_DPTR"; const ENV_CFG_DISK: &str = "MCORS_CFG_DISK"; const ENV_CFG_SHMEM: &str = "MCORS_CFG_SHMEM"; const ENV_CFG_TMGR: &str = "MCORS_CFG_TMGR"; const MCO_API_VER_CFG_KEY: &str = "mco_api_ver"; #[derive(Debug, Deserialize)] struct Features { #[serde(rename = "MCO_API_VERSION")] ver_api: u32, #[serde(rename = "MCO_PRODUCT_BUILD")] build: u32, #[serde(rename = "MCO_PRODUCT_MAGIC")] magic: u32, #[serde(rename = "MCO_PRODUCT_REVISION")] rev: String, #[serde(rename = "MCO_PRODUCT_VERSION_MAJOR")] ver_major: u32, #[serde(rename = "MCO_PRODUCT_VERSION_MINOR")] ver_minor: u32, #[serde(rename = "MCO_CFG_WRAPPER_CLUSTER_SUPPORT")] cluster: bool, #[serde(rename = "MCO_CFG_WRAPPER_HA_SUPPORT")] ha: bool, #[serde(rename = "MCO_CFG_WRAPPER_IOT_SUPPORT")] iot: bool, #[serde(rename = "MCO_CFG_WRAPPER_LUAUDF_SUPPORT")] lua_udf: bool, #[serde(rename = "MCO_CFG_WRAPPER_PERFMON_SUPPORT")] perfmon: bool, #[serde(rename = "MCO_CFG_WRAPPER_SEQUENCE_SUPPORT")] seq: bool, #[serde(rename = "MCO_CFG_WRAPPER_SQL_SUPPORT")] sql: bool, #[serde(rename = "MCO_CFG_WRAPPER_TL_SUPPORT")] tl: bool, } struct BuildConfig { debug: bool, x64: bool, direct_ptr: bool, link_shared: bool, trans_mgr: TransactionManager, persistent: bool, shared_mem: bool, sequences: bool, sql: bool, rsql: bool, features: Option, } impl BuildConfig { fn create() -> Self { let link_shared = BuildConfig::get_env_bool(ENV_CFG_DYLIB); let direct_ptr = BuildConfig::get_env_bool(ENV_CFG_DPTR); let persistent = BuildConfig::get_env_bool(ENV_CFG_DISK); let shared_mem = BuildConfig::get_env_bool(ENV_CFG_SHMEM); let trans_mgr_s = BuildConfig::get_env_enum( ENV_CFG_TMGR, vec![ TMGR_EXCL.to_string(), TMGR_MURSIW.to_string(), TMGR_MVCC.to_string(), ], ); if direct_ptr && persistent { panic!("{} conflicts with {}", ENV_CFG_DPTR, ENV_CFG_DISK) } let trans_mgr = match trans_mgr_s.as_str() { TMGR_EXCL => TransactionManager::Exclusive, TMGR_MURSIW => TransactionManager::MURSIW, TMGR_MVCC => TransactionManager::MVCC, _ => panic!("Unexpected transaction manager"), }; BuildConfig { debug: cfg!(debug_assertions), x64: cfg!(target_pointer_width = "64"), direct_ptr, link_shared, trans_mgr, persistent, shared_mem, sequences: cfg!(feature = "sequences"), sql: cfg!(feature = "sql"), rsql: cfg!(feature = "rsql"), features: BuildConfig::read_features(), } } fn read_features() -> Option { let path = Path::new(&BuildConfig::get_env(ENV_MCO_ROOT)).join("include/mcofeatures.json"); let f = File::open(path).ok()?; Some(serde_json::from_reader(f).unwrap()) } fn get_env_bool(name: &str) -> bool { let val = BuildConfig::get_env(name); match val.as_str() { "0" => false, "1" => true, _ => panic!("${}: not a boolean value", name), } } fn get_env_enum(name: &str, values: Vec) -> String { let val = BuildConfig::get_env(name); if values.contains(&val) { val } else { panic!("${}: unexpected value {}", name, val) } } fn get_env(name: &str) -> String { env::var(name).unwrap_or_else(|_| panic!("environment variable not set: {}", name)) } } fn mco_libraries_subdir(cfg: &BuildConfig) -> String { String::from(match (cfg.direct_ptr, cfg.link_shared) { (false, false) => "target/bin", (false, true) => "target/bin.so", (true, false) => "target/bin.dptr", (true, true) => "target/bin.dptr.so", }) } fn mco_libraries(cfg: &BuildConfig) -> Vec { let mut ret = vec![]; if cfg.sql { ret.push("mcosql"); } if cfg.rsql { ret.push("mcorsql"); } if cfg.sequences { ret.push("mcoseq"); ret.push("mcoseqmath"); } let tmgr_lib = match cfg.trans_mgr { TransactionManager::Exclusive => "mcotexcl", TransactionManager::MURSIW => "mcotmursiw", TransactionManager::MVCC => "mcotmvcc", }; ret.push(tmgr_lib); ret.extend(vec![ "mcoseri", "mcolib", "mcocryptstub", "mcotrace", "mcosallatches", "mcosalatomic", "mcosaltimer", "mcosalsmp", "mcosalmem", "mcosaldload", "mconet", "mcobackup", "mcouwrt", ]); let disk_lib = if cfg!(target_os = "macos") || cfg!(target_os = "linux") { "mcofu98zip" } else if cfg!(target_os = "windows") { "mcofw32" } else { panic!("No shared memory library for this operating system") }; if cfg.persistent { ret.push("mcovtdsk"); ret.push(disk_lib); } else { ret.push("mcovtmem"); } let shmem_lib = if cfg!(target_os = "macos") || cfg!(target_os = "linux") { "mcomipc" } else if cfg!(target_os = "windows") { "mcomw32" } else { panic!("No shared memory library for this operating system") }; if cfg.shared_mem { ret.push(shmem_lib); } else { ret.push("mcomconv"); } let sync_lib = if cfg!(target_os = "macos") { "mcosmacos" } else if cfg!(target_os = "linux") { "mcoslnxp" } else if cfg!(target_os = "windows") { "mcosw32" } else { panic!("No sync library for this operating system"); }; ret.push(sync_lib); let suffix = String::from(if cfg.debug { "_debug" } else { "" }); ret.into_iter() .map(|lib| lib.to_string() + &suffix) .collect() } fn cpp_stdlib() -> Option { if cfg!(target_os = "linux") { Some("stdc++".to_string()) } else if cfg!(target_os = "macos") { Some("c++".to_string()) } else { None } } fn output_libraries(build_cfg: &BuildConfig, mco_lib_dir: &Path) { println!( "cargo:rustc-link-search=native={}", mco_lib_dir.to_str().unwrap() ); let libs = mco_libraries(build_cfg); for lib in libs { println!("cargo:rustc-link-lib={}", lib); } if let Some(cpplib) = cpp_stdlib() { println!("cargo:rustc-link-lib={}", cpplib); } } fn config_cargo_rerun() { println!("cargo:rerun-if-env-changed={}", ENV_MCO_ROOT); println!("cargo:rerun-if-env-changed={}", ENV_CFG_DYLIB); println!("cargo:rerun-if-env-changed={}", ENV_CFG_DPTR); println!("cargo:rerun-if-env-changed={}", ENV_CFG_DISK); println!("cargo:rerun-if-env-changed={}", ENV_CFG_SHMEM); println!("cargo:rerun-if-env-changed={}", ENV_CFG_TMGR); } fn output_api_ver_string(suffix: &str, api_ver: u32) { println!( "cargo:rustc-cfg={}_{}=\"{}\"", MCO_API_VER_CFG_KEY, suffix, api_ver ); } fn output_api_ver_ge(api_ver: u32) { output_api_ver_string("ge", api_ver); } fn output_api_ver_lt(api_ver: u32) { output_api_ver_string("lt", api_ver); } fn output_api_ver_config(api_ver: u32) { output_api_ver_string("eq", api_ver); if api_ver >= 13 { output_api_ver_ge(13); } if api_ver >= 14 { output_api_ver_ge(14); } if api_ver >= 15 { output_api_ver_ge(15); } if api_ver < 14 { output_api_ver_lt(14); } } fn main() { if env::var("DOCS_RS").unwrap_or(String::from("")) == "1" { return; } config_cargo_rerun(); let build_cfg = BuildConfig::create(); let mco_root = PathBuf::from(env::var(ENV_MCO_ROOT).unwrap()); let mco_lib = mco_root.join(mco_libraries_subdir(&build_cfg)); output_libraries(&build_cfg, &mco_lib); build_cfg.features.map(|f| output_api_ver_config(f.ver_api)); }