use std::env; use std::ffi::{OsStr, OsString}; use std::fs; use std::io; use std::path::{Path, PathBuf}; use anyhow::{anyhow, ensure, Result}; use url::Url; // there's probably a crate for this somewhere fn os_concat(a: impl AsRef, b: impl AsRef) -> OsString { let mut result = OsString::from(&a); result.push(b); result } fn out_dir() -> PathBuf { PathBuf::from(env::var_os("OUT_DIR").expect("no OUT_DIR found")) } fn download(url: &str) -> Result { let url = Url::parse(url)?; let file_name = url .path_segments() .and_then(|x| x.last()) .ok_or_else(|| anyhow!("Weird URL downloaded"))?; let out_path = out_dir().join(file_name); if !out_path.exists() { let mut body_reader = ureq::request_url("GET", &url).call()?.into_reader(); let mut out_file = fs::File::create(&out_path)?; io::copy(&mut body_reader, &mut out_file)?; } Ok(out_path) } fn un7z(archive: PathBuf) -> Result<()> { // WARNING: This code is horrifying and also bad. // It should never be used by anyone. // TODO port the 7zip LZMA SDK to Rust use std::process::{Command, Stdio}; let my_7z_exe = Path::new(r"C:\Program Files\7-Zip\7z.exe"); ensure!(my_7z_exe.exists()); let output_folder = archive .parent() .ok_or_else(|| anyhow!("archive with no parent"))?; let output_folder_arg = os_concat("-o", &output_folder); let extract_result = Command::new(my_7z_exe) .args(["x", "-aos"]) .arg(output_folder_arg) .arg(&archive) .stdin(Stdio::null()) .stdout(Stdio::null()) .status()?; ensure!(extract_result.success()); Ok(()) } fn main() -> Result<()> { println!("cargo:rerun-if-changed=build.rs"); let target = env::var("TARGET").expect("no TARGET found"); match target.as_str() { "x86_64-pc-windows-msvc" => build_x64_windows_msvc(), _ => panic!("unsupported target: {}", target), } } /* 1 1 lib\vc14x_x64_dll 1 1 lib\vc14x_x64_lib */ fn build_x64_windows_msvc() -> Result<()> { let headers = download("https://github.com/wxWidgets/wxWidgets/releases/download/v3.1.5/wxWidgets-3.1.5-headers.7z")?; un7z(headers)?; // TODO make sure VS2015+ is actually the right thing let libs = download("https://github.com/wxWidgets/wxWidgets/releases/download/v3.1.5/wxMSW-3.1.5_vc14x_x64_Dev.7z")?; un7z(libs)?; let out_dir = out_dir(); // TODO consider actually parsing wxwidgets.props at build time instead of doing it manually // TODO bindgen ahead of time // TODO can we really get away with this to do static linking let wx_using_lib = true; if wx_using_lib { let dll_folder = out_dir.join("lib/vc14x_x64_dll"); let lib_folder = out_dir.join("lib/vc14x_x64_lib"); if dll_folder.exists() && !lib_folder.exists() { fs::rename(dll_folder, lib_folder)?; } } let libs_folder = out_dir.join(if wx_using_lib { "lib/vc14x_x64_lib" } else { "lib/vc14x_x64_dll" }); let wx_using_dll = !wx_using_lib; let debug = env::var("PROFILE")? == "release"; let wx_suffix = if debug { "u" } else { "ud" }; let mut defines = vec![ "__WXMSW__", "wxMSVC_VERSION_AUTO", "wxMSVC_VERSION_ABI_COMPAT", "_UNICODE", ]; if debug { defines.push("_DEBUG"); } if wx_using_dll { defines.push("WXUSINGDLL"); } let mut include_dirs = vec![out_dir.join("include/msvc"), out_dir.join("include")]; // TODO this is only in ResourceCompile in wxwidgets.props, is that bad include_dirs.push(libs_folder.join(format!("msw{}", wx_suffix))); let bindings = bindgen::Builder::default() .header("wrapper.hpp") // pain .clang_arg("-Wno-invalid-token-paste") .clang_args(defines.iter().map(|x| format!("-D{}", x))) // TODO don't .display() .clang_args(include_dirs.iter().map(|x| format!("-I{}", x.display()))) .opaque_type("std::.*") // pain 2: the sequel to pain .blocklist_type("CharType") .blocklist_type("value_type") .blocklist_type("const_reference") // pain 3: the shriequel to pain .blocklist_type("wxBaseArray_CMPFUNC") .blocklist_type("wxBaseObjectArray") .blocklist_type("wxBaseObjectArray_base") .blocklist_type("pointer") .blocklist_type("const_pointer") .blocklist_type("wxBaseObjectArrayForwxDateTimeArray") .blocklist_type("wxBaseObjectArrayForwxIconArray") .blocklist_type("wxBaseObjectArrayForwxStatusBarPaneArray") .opaque_type("_IMAGE_TLS_DIRECTORY64") // that's enough pain? maybe? .generate() .map_err(|_| anyhow!("failed to generate bindings"))?; let out_path = out_dir.join("bindings.rs"); bindings.write_to_file(&out_path)?; let win32_libs = [ "kernel32", "user32", "gdi32", "comdlg32", "winspool", "shell32", "shlwapi", "ole32", "oleaut32", "uuid", "advapi32", "version", "comctl32", "rpcrt4", "wsock32", "wininet", "winmm", ]; for lib in win32_libs { // TODO dylib or static println!("cargo:rustc-link-lib={}", lib); } // TODO is "native=" the right thing there (or display() for that matter) println!("cargo:rustc-link-search=native={}", libs_folder.display()); // TODO which libs do we actually want to link println!("cargo:rustc-link-lib=static=wxbase31{}", wx_suffix); println!("cargo:rustc-link-lib=static=wxmsw31{}_core", wx_suffix); Ok(()) }