use std::path::{PathBuf, MAIN_SEPARATOR};
use crate::{Env, PackageInfo};
mod starting_binary;
pub fn current_exe() -> std::io::Result<PathBuf> {
self::starting_binary::STARTING_BINARY.cloned()
}
pub fn target_triple() -> crate::Result<String> {
let arch = if cfg!(target_arch = "x86") {
"i686"
} else if cfg!(target_arch = "x86_64") {
"x86_64"
} else if cfg!(target_arch = "arm") {
"armv7"
} else if cfg!(target_arch = "aarch64") {
"aarch64"
} else {
return Err(crate::Error::Architecture);
};
let os = if cfg!(target_os = "linux") {
"unknown-linux"
} else if cfg!(target_os = "macos") {
"apple-darwin"
} else if cfg!(target_os = "windows") {
"pc-windows"
} else if cfg!(target_os = "freebsd") {
"unknown-freebsd"
} else {
return Err(crate::Error::Os);
};
let os = if cfg!(target_os = "macos") || cfg!(target_os = "freebsd") {
String::from(os)
} else {
let env = if cfg!(target_env = "gnu") {
"gnu"
} else if cfg!(target_env = "musl") {
"musl"
} else if cfg!(target_env = "msvc") {
"msvc"
} else {
return Err(crate::Error::Environment);
};
format!("{os}-{env}")
};
Ok(format!("{arch}-{os}"))
}
#[allow(unused_variables)]
pub fn resource_dir(package_info: &PackageInfo, env: &Env) -> crate::Result<PathBuf> {
let exe = current_exe()?;
let exe_dir = exe.parent().expect("failed to get exe directory");
let curr_dir = exe_dir.display().to_string();
if curr_dir.ends_with(format!("{MAIN_SEPARATOR}target{MAIN_SEPARATOR}debug").as_str())
|| curr_dir.ends_with(format!("{MAIN_SEPARATOR}target{MAIN_SEPARATOR}release").as_str())
|| cfg!(target_os = "windows")
{
return Ok(exe_dir.to_path_buf());
}
#[allow(unused_mut, unused_assignments)]
let mut res = Err(crate::Error::UnsupportedPlatform);
#[cfg(target_os = "linux")]
{
res = if curr_dir.ends_with("/data/usr/bin") {
exe_dir
.join(format!("../lib/{}", package_info.package_name()))
.canonicalize()
.map_err(Into::into)
} else if let Some(appdir) = &env.appdir {
let appdir: &std::path::Path = appdir.as_ref();
Ok(PathBuf::from(format!(
"{}/usr/lib/{}",
appdir.display(),
package_info.package_name()
)))
} else {
Ok(PathBuf::from(format!(
"/usr/lib/{}",
package_info.package_name()
)))
};
}
#[cfg(target_os = "macos")]
{
res = exe_dir
.join("../Resources")
.canonicalize()
.map_err(Into::into);
}
res
}
#[cfg(windows)]
pub use windows_platform::{is_windows_7, windows_version};
#[cfg(windows)]
mod windows_platform {
use std::{iter::once, os::windows::prelude::OsStrExt};
use windows::{
core::{PCSTR, PCWSTR},
Win32::{
Foundation::FARPROC,
System::{
LibraryLoader::{GetProcAddress, LoadLibraryW},
SystemInformation::OSVERSIONINFOW,
},
},
};
pub fn is_windows_7() -> bool {
if let Some(v) = windows_version() {
if v.0 == 6 && v.1 == 1 {
return true;
}
}
false
}
fn encode_wide(string: impl AsRef<std::ffi::OsStr>) -> Vec<u16> {
string.as_ref().encode_wide().chain(once(0)).collect()
}
fn get_function_impl(library: &str, function: &str) -> Option<FARPROC> {
let library = encode_wide(library);
assert_eq!(function.chars().last(), Some('\0'));
let function = PCSTR::from_raw(function.as_ptr());
let module = unsafe { LoadLibraryW(PCWSTR::from_raw(library.as_ptr())) }.unwrap_or_default();
if module.is_invalid() {
None
} else {
Some(unsafe { GetProcAddress(module, function) })
}
}
macro_rules! get_function {
($lib:expr, $func:ident) => {
get_function_impl(concat!($lib, '\0'), concat!(stringify!($func), '\0'))
.map(|f| unsafe { std::mem::transmute::<windows::Win32::Foundation::FARPROC, $func>(f) })
};
}
pub fn windows_version() -> Option<(u32, u32, u32)> {
type RtlGetVersion = unsafe extern "system" fn(*mut OSVERSIONINFOW) -> i32;
let handle = get_function!("ntdll.dll", RtlGetVersion);
if let Some(rtl_get_version) = handle {
unsafe {
let mut vi = OSVERSIONINFOW {
dwOSVersionInfoSize: 0,
dwMajorVersion: 0,
dwMinorVersion: 0,
dwBuildNumber: 0,
dwPlatformId: 0,
szCSDVersion: [0; 128],
};
let status = (rtl_get_version)(&mut vi as _);
if status >= 0 {
Some((vi.dwMajorVersion, vi.dwMinorVersion, vi.dwBuildNumber))
} else {
None
}
}
} else {
None
}
}
}