diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..220f6aa --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +Cargo.lock linguist-generated diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ef8b859..919e907 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -23,8 +23,8 @@ jobs: # - {os: macos-14, config: release, compiler: clang_64, aqt_os: mac, aqt_compiler: clang_64} runs-on: ${{ matrix.variants.os }} steps: - - uses: actions/checkout@v5 - - uses: actions/cache@v4 + - uses: actions/checkout@v7 + - uses: actions/cache@v6 with: path: | ~/.cargo/bin/ diff --git a/build.rs b/build.rs index ef07124..7874dcd 100644 --- a/build.rs +++ b/build.rs @@ -1,22 +1,28 @@ #[allow(dead_code)] // build.rs // https://doc.rust-lang.org/cargo/reference/build-scripts.html -use std::env; +use std:: env; -const QT_VERSION: &'static str = "6.10.0"; -const BASE_DIR: &'static str = "."; -const LIBDIE_BASE_DIR: &'static str = "./libdie++"; -const LIBDIE_BUILD_DIR: &'static str = "./libdie++/build"; -const LIBDIE_INSTALL_DIR: &'static str = "./libdie++/install"; -const LIB_DIE_PATH: &'static str = "./libdie++/build/_deps/dielibrary-build/src"; +#[cfg(target_os = "windows")] +use std::{ + fs, + path::{Path, PathBuf} +}; + +const QT_VERSION: &str = "6.10.0"; +const BASE_DIR: &str = "."; +const LIBDIE_BASE_DIR: &str = "./libdie++"; +const LIBDIE_BUILD_DIR: &str = "./libdie++/build"; +const LIBDIE_INSTALL_DIR: &str = "./libdie++/install"; +const LIB_DIE_PATH: &str = "./libdie++/build/_deps/dielibrary-build/src"; #[cfg(target_os = "windows")] -const MSVC_PATH: &'static str = r"C:\Program Files (x86)\Windows Kits\10\Lib\10.0.22000.0"; +const WINDOWS_KITS_LIB_DIR: &str = r"C:\Program Files (x86)\Windows Kits\10\Lib"; #[cfg(debug_assertions)] -const BUILD_TYPE: &'static str = "Debug"; +const BUILD_TYPE: &str = "Debug"; #[cfg(not(debug_assertions))] -const BUILD_TYPE: &'static str = "Release"; +const BUILD_TYPE: &str = "Release"; fn get_qt_libs_path() -> String { #[cfg(target_os = "windows")] @@ -41,7 +47,7 @@ fn qt_download() { assert!( std::process::Command::new("python") .current_dir(BASE_DIR) - .args(["-m", "pip", "install", "--user", "--upgrade", "aqtinstall"]) + .args(["-m", "pip", "install", "--upgrade", "aqtinstall"]) .spawn() .unwrap() .wait() @@ -73,7 +79,7 @@ fn qt_download() { cmd.spawn() .unwrap() .wait() - .expect(format!("failed to install Qt {QT_VERSION} using AQT").as_str()) + .unwrap_or_else(|_| panic!("failed to install Qt {QT_VERSION} using AQT")) .success() ); } @@ -138,6 +144,74 @@ fn cmake_build_die() { } } +#[cfg(target_os = "windows")] +fn has_windows_ucrt_libs(path: &Path) -> bool { + path.join("ucrt").join("x64").exists() +} + +#[cfg(target_os = "windows")] +fn normalized_windows_sdk_version(version: &str) -> &str { + version.trim_end_matches(['\\', '/']) +} + +#[cfg(target_os = "windows")] +fn parse_windows_sdk_version(version: &str) -> Option> { + normalized_windows_sdk_version(version) + .split('.') + .map(|segment| segment.parse::().ok()) + .collect() +} + +#[cfg(target_os = "windows")] +fn find_latest_windows_sdk_dir(base_dir: &Path) -> Option { + fs::read_dir(base_dir) + .ok()? + .filter_map(|entry| { + let entry = entry.ok()?; + let path = entry.path(); + + if !path.is_dir() || !has_windows_ucrt_libs(&path) { + return None; + } + + let version = entry.file_name(); + let version = version.to_str()?; + let version = parse_windows_sdk_version(version)?; + + Some((version, path)) + }) + .max_by(|(left, _), (right, _)| left.cmp(right)) + .map(|(_, path)| path) +} + +#[cfg(target_os = "windows")] +fn resolve_windows_sdk_dir() -> Option { + if let Some(path) = env::var_os("MSVC_PATH") { + let path = PathBuf::from(path); + if has_windows_ucrt_libs(&path) { + return Some(path); + } + } + + if let Some(root) = env::var_os("WindowsSdkDir") { + let version = env::var("WindowsSDKLibVersion") + .or_else(|_| env::var("WindowsSDKVersion")) + .ok(); + + if let Some(version) = version { + let path = PathBuf::from(root) + .join("Lib") + .join(normalized_windows_sdk_version(&version)); + + if has_windows_ucrt_libs(&path) { + return Some(path); + } + } + } + + find_latest_windows_sdk_dir(Path::new(WINDOWS_KITS_LIB_DIR)) +} + fn setup_common() { // die & die++ println!("cargo:rustc-link-lib=static=die++"); @@ -185,6 +259,7 @@ fn install() { LIB_DIE_PATH, _mod ); } + println!("cargo:rustc-link-arg=-Wl,-rpath,{}/die/lib", LIBDIE_INSTALL_DIR); } #[cfg(target_os = "macos")] @@ -216,6 +291,11 @@ fn install() { #[cfg(target_os = "windows")] fn install() { + println!("cargo:rerun-if-env-changed=MSVC_PATH"); + println!("cargo:rerun-if-env-changed=WindowsSdkDir"); + println!("cargo:rerun-if-env-changed=WindowsSDKLibVersion"); + println!("cargo:rerun-if-env-changed=WindowsSDKVersion"); + match BUILD_TYPE { "Release" => { println!("cargo:rustc-link-lib=static=Qt6Core"); @@ -226,13 +306,18 @@ fn install() { println!("cargo:rustc-link-lib=dylib=Qt6Network"); } "Debug" => { + let windows_sdk_dir = resolve_windows_sdk_dir().expect( + "failed to locate Windows SDK ucrt path; set MSVC_PATH or install a Windows 10 SDK", + ); + let ucrt_dir = windows_sdk_dir.join("ucrt").join("x64"); + println!("cargo:rustc-link-lib=static=Qt6Cored"); println!("cargo:rustc-link-lib=static=Qt6Qmld"); println!("cargo:rustc-link-lib=static=Qt6Networkd"); println!("cargo:rustc-link-lib=dylib=Qt6Cored"); println!("cargo:rustc-link-lib=dylib=Qt6Qmld"); println!("cargo:rustc-link-lib=dylib=Qt6Networkd"); - println!("cargo:rustc-link-search=native={}/ucrt/x64", MSVC_PATH); + println!("cargo:rustc-link-search=native={}", ucrt_dir.display()); println!("cargo:rustc-link-lib=static=ucrtd"); } _ => { @@ -270,7 +355,7 @@ fn install() { } fn is_qt_missing() -> bool { - std::path::Path::new(get_qt_libs_path().as_str()).exists() == false + !std::path::Path::new(get_qt_libs_path().as_str()).exists() } fn should_rebuild_libdie() -> bool { @@ -297,7 +382,7 @@ fn should_rebuild_libdie() -> bool { #[cfg(target_os = "macos")] fpath.push("lib/libdie.a"); - return fpath.exists() == false; + !fpath.exists() } fn main() { @@ -317,3 +402,46 @@ fn main() { println!("cargo:rerun-if-changed=src/lib.rs"); } + +#[cfg(test)] +mod tests { + use super::*; + use std::time::{SystemTime, UNIX_EPOCH}; + + fn temp_test_dir(name: &str) -> PathBuf { + let timestamp = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_nanos(); + env::temp_dir().join(format!("die-rust-build-rs-{name}-{timestamp}")) + } + + #[test] + fn normalizes_windows_sdk_version() { + assert_eq!( + normalized_windows_sdk_version("10.0.26100.0\\"), + "10.0.26100.0" + ); + assert_eq!( + normalized_windows_sdk_version("10.0.26100.0/"), + "10.0.26100.0" + ); + } + + #[test] + fn finds_latest_windows_sdk_dir() { + let base_dir = temp_test_dir("windows-sdk"); + let older = base_dir.join("10.0.22000.0").join("ucrt").join("x64"); + let newer = base_dir.join("10.0.26100.0").join("ucrt").join("x64"); + let invalid = base_dir.join("invalid").join("ucrt").join("x64"); + + fs::create_dir_all(&older).unwrap(); + fs::create_dir_all(&newer).unwrap(); + fs::create_dir_all(&invalid).unwrap(); + + let latest = find_latest_windows_sdk_dir(&base_dir).unwrap(); + assert_eq!(latest, base_dir.join("10.0.26100.0")); + + fs::remove_dir_all(&base_dir).unwrap(); + } +} diff --git a/src/lib.rs b/src/lib.rs index b20ee53..a837d50 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -263,7 +263,7 @@ pub fn scan_memory_with_db(mem: &[u8], flags: ScanFlags, db_path: &Path) -> Resu /// # Returns /// /// * `Result<()>` - On success, returns `Ok()`. -/// On failure, returns an `Err` with the error details. +/// On failure, returns an `Err` with the error details. /// /// # Examples ///