// plctag-rs // // a rust wrapper of libplctag, with rust style APIs and useful extensions. // Copyright: 2022, Joylei // License: MIT extern crate bindgen; extern crate cmake; extern crate pkg_config; use fs_extra::dir::CopyOptions; use std::{ env, ffi::OsStr, fs, path::{Component, Path, PathBuf}, }; fn main() { let is_static = check_static(); if is_static { eprintln!("static build"); } let (lib_path, header_file) = if let Ok(lib_path) = env::var("LIBPLCTAG_PATH") { eprintln!("use lib path from env:LIBPLCTAG_PATH"); let lib_path: PathBuf = lib_path.into(); let header_file = "libplctag.h".to_owned(); println!("cargo:rerun-if-changed={}", header_file); (lib_path, header_file) } else { let source_dir = { let source_dir: PathBuf = "libplctag".into(); // fix publish issue: Build scripts should not modify anything outside of OUT_DIR let dst_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); fs_extra::dir::copy( source_dir, &dst_dir, &CopyOptions { overwrite: true, ..CopyOptions::new() }, ) .expect("failed to copy libplctag to OUT_DIR"); dst_dir.join("libplctag") }; let mut config = cmake::Config::new(&source_dir); // do not build examples config.define("BUILD_EXAMPLES", "0"); if is_static { config.static_crt(true); } let out_dir = config.build(); eprintln!("cmake build out dir: {:?}", &out_dir); let header_file = source_dir.join("src").join("lib").join("libplctag.h"); println!("cargo:rerun-if-changed={}", header_file.display()); (out_dir, header_file.display().to_string()) }; println!("cargo:rerun-if-env-changed=LIBPLCTAG_STATIC"); println!("cargo:rerun-if-env-changed=LIBPLCTAG_DYNAMIC"); if cfg!(target_os = "windows") && is_static { println!("cargo:rustc-link-lib=ws2_32"); println!("cargo:rustc-link-lib=static=plctag_static"); } else { println!("cargo:rustc-link-lib=plctag"); } println!("cargo:rustc-link-search={}", lib_path.display()); println!("cargo:rustc-link-search={}", lib_path.join("lib").display()); println!( "cargo:rustc-link-search={}", lib_path.join("Release").display() ); //generate bindings let bindings = bindgen::Builder::default() .header(header_file) .allowlist_var("PLCTAG_.*") .allowlist_function("plc_tag_.*") .parse_callbacks(Box::new(bindgen::CargoCallbacks)) .rustfmt_bindings(true) .generate() .expect("Unable to generate bindings"); let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); eprintln!("OUT_DIR={:?}", out_path); bindings .write_to_file(out_path.join("bindings.rs")) .expect("Couldn't write bindings!"); #[cfg(target_os = "windows")] if !is_static { install_lib_files(lib_path, out_path); } } #[cfg(target_os = "windows")] fn install_lib_files(lib_path: impl AsRef, out_path: impl AsRef) { let folders = &["", "lib", "Release"]; let mut dll_file = None; for folder in folders { let path = lib_path.as_ref().join(folder).join("plctag.dll"); if path.is_file() && path.exists() { dll_file = Some(path); break; } } if let Some(dll_file) = dll_file { let target_dir = find_target_profile_dir(out_path).unwrap(); eprintln!("target profile dir={}", target_dir.display()); //copy lib to target dir let dest_file = target_dir.join(dll_file.file_name().unwrap()); fs::copy(dll_file, dest_file).unwrap(); } else { eprintln!("plctag.dll not found"); } } fn find_target_profile_dir<'a>(dir: impl AsRef + 'a) -> Option { //out dir looks like ...\plctag-rs\target\debug\build\XXXXX //profile dir looks like ...\plctag-rs\target\debug\ let target = Some(Component::Normal(OsStr::new("build"))); let mut dir = dir.as_ref(); loop { if let Some(p) = dir.parent() { let last_part = p.components().last(); if last_part == target { return p.parent().map(|v| v.to_owned()); } dir = p; } else { return None; } } } /// check if static build in the order of: /// PLCTAG_STATIC, PLCTAG_DYNAMIC, rustflags: +crt-static fn check_static() -> bool { if let Some(v) = get_env_bool("LIBPLCTAG_STATIC") { return v; } if let Some(v) = get_env_bool("LIBPLCTAG_DYNAMIC") { return !v; } cfg!(target_feature = "crt-static") } fn get_env_bool(key: &str) -> Option { env::var(key).ok().map(|v| { let v = v.to_lowercase(); v == "1" || v == "true" || v == "on" }) }