Thanks @SychPL.
This commit is contained in:
1 parent
a2245b2f51
commit
ff7f3204d5
5 files changed
+63
-6
No files matched your search
Generated
+1
@@ -4255,6 +4255,7 @@ dependencies = [
|
||||
"toml",
|
||||
"ttf-parser",
|
||||
"web-time",
|
||||
"winreg",
|
||||
"zip 3.0.0",
|
||||
]
|
||||
|
||||
|
||||
@@ -28,6 +28,11 @@ zip = { workspace = true }
|
||||
# Rasterises the icon SVGs (assets/icons/ui/design/svg) at the size they are drawn.
|
||||
tiny-skia = { workspace = true }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
# Updates: the folder the Windows installer recorded, so an install outside Program Files is still
|
||||
# updated by its installer (MIT; already in the tree as a build dependency).
|
||||
winreg = "0.56"
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
|
||||
# The offscreen viewport render test opens a device without a window.
|
||||
pollster = { workspace = true }
|
||||
|
||||
@@ -144,7 +144,7 @@ pub enum InstallKind {
|
||||
Rpm,
|
||||
/// The plain `.tar.gz`, unpacked somewhere.
|
||||
LinuxTarball,
|
||||
/// Installed by the `.msi` (under Program Files).
|
||||
/// Installed by the `.msi` (under Program Files, or in the folder the installer recorded).
|
||||
WindowsMsi,
|
||||
/// The portable `.zip`, unpacked somewhere.
|
||||
WindowsPortable,
|
||||
@@ -165,6 +165,9 @@ pub struct Probe<'a> {
|
||||
/// The running executable.
|
||||
pub exe: Option<PathBuf>,
|
||||
pub exists: &'a dyn Fn(&Path) -> bool,
|
||||
/// Where the Windows installer put SolidCraft (`HKLM\Software\SolidCraft`, `InstallDir`): a
|
||||
/// folder chosen in its wizard can be anywhere, not only under Program Files (#1870).
|
||||
pub msi_dir: Option<PathBuf>,
|
||||
}
|
||||
|
||||
pub fn detect(p: &Probe) -> InstallKind {
|
||||
@@ -202,7 +205,8 @@ pub fn detect(p: &Probe) -> InstallKind {
|
||||
}
|
||||
"windows" => {
|
||||
let under = |k: &str| env(k).is_some_and(|dir| starts_with_ci(&exe, Path::new(&dir)));
|
||||
if under("ProgramFiles") || under("ProgramW6432") || under("ProgramFiles(x86)") {
|
||||
let installed = p.msi_dir.as_deref().is_some_and(|dir| starts_with_ci(&exe, dir));
|
||||
if installed || under("ProgramFiles") || under("ProgramW6432") || under("ProgramFiles(x86)") {
|
||||
return InstallKind::WindowsMsi;
|
||||
}
|
||||
if exe.parent().is_some_and(|d| (p.exists)(&d.join("solidcraft-cli.exe"))) {
|
||||
@@ -240,10 +244,30 @@ pub fn this_install() -> InstallKind {
|
||||
{
|
||||
let env = |k: &str| std::env::var(k).ok();
|
||||
let exists = |p: &Path| p.exists();
|
||||
detect(&Probe { os: std::env::consts::OS, release_build: release_build(), env: &env, exe: std::env::current_exe().ok(), exists: &exists })
|
||||
let msi_dir = msi_install_dir();
|
||||
detect(&Probe {
|
||||
os: std::env::consts::OS,
|
||||
release_build: release_build(),
|
||||
env: &env,
|
||||
exe: std::env::current_exe().ok(),
|
||||
exists: &exists,
|
||||
msi_dir,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// The folder the Windows installer recorded (solidcraft.wxs writes it for upgrades), if any.
|
||||
#[cfg(windows)]
|
||||
fn msi_install_dir() -> Option<PathBuf> {
|
||||
let key = winreg::RegKey::predef(winreg::enums::HKEY_LOCAL_MACHINE).open_subkey(r"Software\SolidCraft").ok()?;
|
||||
key.get_value::<String, _>("InstallDir").ok().map(PathBuf::from)
|
||||
}
|
||||
|
||||
#[cfg(all(not(windows), not(target_arch = "wasm32")))]
|
||||
fn msi_install_dir() -> Option<PathBuf> {
|
||||
None
|
||||
}
|
||||
|
||||
impl InstallKind {
|
||||
/// The package manager updates it: Download isn't the way (the window shows the command).
|
||||
pub fn system_managed(&self) -> bool {
|
||||
|
||||
@@ -32,7 +32,7 @@ fn probe(os: &str, release: bool, env: &[(&str, &str)], exe: &str, files: &[&str
|
||||
let files: Vec<PathBuf> = files.iter().map(PathBuf::from).collect();
|
||||
let get = |k: &str| env.get(k).cloned();
|
||||
let exists = |p: &Path| files.iter().any(|f| f == p);
|
||||
detect(&Probe { os, release_build: release, env: &get, exe: Some(PathBuf::from(exe)), exists: &exists })
|
||||
detect(&Probe { os, release_build: release, env: &get, exe: Some(PathBuf::from(exe)), exists: &exists, msi_dir: None })
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -92,7 +92,34 @@ fn portable_windows_layout_is_recognised() {
|
||||
let exe = PathBuf::from("/portable/solidcraft.exe");
|
||||
let get = |k: &str| (k == "ProgramFiles").then(|| "C:\\Program Files".to_string());
|
||||
let exists = |p: &Path| p == Path::new("/portable/solidcraft-cli.exe");
|
||||
assert_eq!(detect(&Probe { os: "windows", release_build: true, env: &get, exe: Some(exe), exists: &exists }), InstallKind::WindowsPortable);
|
||||
assert_eq!(
|
||||
detect(&Probe { os: "windows", release_build: true, env: &get, exe: Some(exe), exists: &exists, msi_dir: None }),
|
||||
InstallKind::WindowsPortable
|
||||
);
|
||||
}
|
||||
|
||||
/// #1870: the installer's wizard can put SolidCraft anywhere, and it puts solidcraft-cli.exe
|
||||
/// beside it as the portable zip does. The folder it recorded is what tells its copy apart.
|
||||
#[test]
|
||||
fn an_msi_install_outside_program_files_is_recognised() {
|
||||
let exe = PathBuf::from(r"D:\Program Files\SolidCraft\solidcraft.exe");
|
||||
let get = |k: &str| match k {
|
||||
"ProgramFiles" | "ProgramW6432" => Some(r"C:\Program Files".to_string()),
|
||||
"ProgramFiles(x86)" => Some(r"C:\Program Files (x86)".to_string()),
|
||||
_ => None,
|
||||
};
|
||||
// solidcraft-cli.exe is beside it.
|
||||
let exists = |_: &Path| true;
|
||||
let kind = |msi_dir: Option<&str>| {
|
||||
detect(&Probe { os: "windows", release_build: true, env: &get, exe: Some(exe.clone()), exists: &exists, msi_dir: msi_dir.map(PathBuf::from) })
|
||||
};
|
||||
// The installer records [INSTALLFOLDER], which ends in a backslash; Windows paths ignore case.
|
||||
assert_eq!(kind(Some(r"D:\Program Files\SolidCraft\")), InstallKind::WindowsMsi);
|
||||
assert_eq!(kind(Some(r"d:\program files\solidcraft")), InstallKind::WindowsMsi);
|
||||
// No installer copy here: the portable zip, as before.
|
||||
assert_eq!(kind(None), InstallKind::WindowsPortable);
|
||||
assert_eq!(kind(Some("")), InstallKind::WindowsPortable);
|
||||
assert_eq!(kind(Some(r"D:\Program Files\SolidCraft2\")), InstallKind::WindowsPortable, "a folder whose name only starts the same");
|
||||
}
|
||||
|
||||
/// The assets of v0.10.0, as published.
|
||||
|
||||
@@ -88,7 +88,7 @@ and SolidCraft picks the one matching how it was installed:
|
||||
| `.deb` | under `/usr`, dpkg knows `solidcraft` | `solidcraft-linux-<arch>.deb` | `sudo apt install ./solidcraft-linux-<arch>.deb` |
|
||||
| `.rpm` | under `/usr`, an rpm database exists | `solidcraft-linux-<arch>.rpm` | `sudo dnf install ./solidcraft-linux-<arch>.rpm` |
|
||||
| `.tar.gz` | anywhere else on Linux | `solidcraft-<version>-linux-<arch>.tar.gz` | Unpack over the old copy |
|
||||
| Windows installer | under Program Files | `solidcraft-windows-<arch>.msi` | Run the installer |
|
||||
| Windows installer | under Program Files, or in the folder the installer recorded | `solidcraft-windows-<arch>.msi` | Run the installer |
|
||||
| Windows portable | `solidcraft-cli.exe` beside it | `solidcraft-windows-<arch>-portable.zip` | Unzip over the old copy |
|
||||
| Windows `.exe` | anywhere else on Windows | `SolidCraft-<version>-windows-x64.exe` | Replace the old `.exe` |
|
||||
| macOS | inside `SolidCraft.app` | `solidcraft-macos-universal.dmg` | Drag the new app to Applications |
|
||||
|
||||
Reference in new issue
Block a user