diff options
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | Cargo.toml | 10 | ||||
-rw-r--r-- | build.rs | 177 | ||||
-rw-r--r-- | examples/hello-world.rs | 20 | ||||
-rw-r--r-- | src/lib.rs | 9 | ||||
-rw-r--r-- | wrapper.hpp | 1 |
6 files changed, 219 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..96ef6c0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..3dd46f0 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "shit-wx-sys" +version = "0.1.0" +edition = "2021" + +[build-dependencies] +anyhow = "1.0.44" +bindgen = { version = "0.59.1" } +ureq = "2.3.0" +url = "2.2.2" diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..f22e64f --- /dev/null +++ b/build.rs @@ -0,0 +1,177 @@ +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<OsStr>, b: impl AsRef<OsStr>) -> 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<PathBuf> { + 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), + } +} + +/* + <Choose> + <When Condition="Exists('lib\vc14x_x64_dll\wxbase31$(wxSuffix).lib')"> + <PropertyGroup Label="UserMacros"> + <wxUsingVersionABICompat>1</wxUsingVersionABICompat> + <wxUsingDll>1</wxUsingDll> + <wxLibOrDllDir>lib\vc14x_x64_dll</wxLibOrDllDir> + </PropertyGroup> + </When> + + <When Condition="Exists('lib\vc14x_x64_lib\wxbase31$(wxSuffix).lib')"> + <PropertyGroup Label="UserMacros"> + <wxUsingVersionABICompat>1</wxUsingVersionABICompat> + <wxUsingLib>1</wxUsingLib> + <wxLibOrDllDir>lib\vc14x_x64_lib</wxLibOrDllDir> + </PropertyGroup> + </When> + </Choose> +*/ + +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(()) +} diff --git a/examples/hello-world.rs b/examples/hello-world.rs new file mode 100644 index 0000000..fb15d85 --- /dev/null +++ b/examples/hello-world.rs @@ -0,0 +1,20 @@ +use std::ffi::CString; + +use shit_wx_sys::*; + +fn main() { + unsafe { + let message = "It's alive!!"; + let c_string_message = CString::new(message).unwrap(); + let message = + wxString::FromAscii(c_string_message.as_ptr(), message.len().try_into().unwrap()); + wxMessageBox( + (&message) as *const wxString, + (&message) as *const wxString, + (wxOK as i32) | wxGeometryCentre_wxCENTER, + NULL as *mut wxWindow, + wxDefaultCoord, + wxDefaultCoord, + ); + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..dca544b --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,9 @@ +#![allow( + non_camel_case_types, + non_snake_case, + unaligned_references, + non_upper_case_globals, + deref_nullptr +)] + +include!(concat!(env!("OUT_DIR"), "/bindings.rs")); diff --git a/wrapper.hpp b/wrapper.hpp new file mode 100644 index 0000000..2c9356c --- /dev/null +++ b/wrapper.hpp @@ -0,0 +1 @@ +#include <wx/wx.h> |