diff --git a/CHANGELOG.md b/CHANGELOG.md index 93cd0812..87f6bf3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -70,6 +70,11 @@ Versioning](https://semver.org/spec/v2.0.0.html). - Added support for setting up merged VST3 bundles with a 32-bit version of `libyabridge-vst3.so`. +- Fixed the post-installation setup checks when the default Wine prefix over at + `~/.wine` was created with `WINEARCH=win32` set. This would otherwise result + in an `00cc:err:process:exec_process` error when running `yabridgectl sync` + because yabridgectl would try to run the 64-bit `yabridge-host.exe` in that + prefix. - Merged VST3 bundles set up in `~/.vst3/yabridge` are now always cleared before yabridgectl adds new files to them. This makes it easier to switch from the 64-bit version of a plugin to the 32-bit version, or from a 64-bit version of diff --git a/tools/yabridgectl/src/config.rs b/tools/yabridgectl/src/config.rs index 28c8757f..f0fe1d82 100644 --- a/tools/yabridgectl/src/config.rs +++ b/tools/yabridgectl/src/config.rs @@ -42,6 +42,10 @@ pub const LIBYABRIDGE_VST2_NAME: &str = "libyabridge-vst2.so"; pub const LIBYABRIDGE_VST3_NAME: &str = "libyabridge-vst3.so"; /// The name of the script we're going to run to verify that everything's working correctly. pub const YABRIDGE_HOST_EXE_NAME: &str = "yabridge-host.exe"; +/// The 32-bit verison of `YABRIDGE_HOST_EXE_NAME`. If `~/.wine` was somehow created with +/// `WINEARCH=win32` set, then it won't be possible to run the 64-bit `yabridge-host.exe` in there. +/// In that case we'll just run the 32-bit version isntead, if it exists. +pub const YABRIDGE_HOST_32_EXE_NAME: &str = "yabridge-host-32.exe"; /// The name of the XDG base directory prefix for yabridge's own files, relative to /// `$XDG_CONFIG_HOME` and `$XDG_DATA_HOME`. const YABRIDGE_PREFIX: &str = "yabridge"; @@ -149,10 +153,15 @@ pub struct YabridgeFiles { pub libyabridge_vst3: Option<(PathBuf, LibArchitecture)>, /// The path to `yabridge-host.exe`. This is the path yabridge will actually use, and it does /// not have to be relative to `yabridge_home`. - pub yabridge_host_exe: PathBuf, + pub yabridge_host_exe: Option, /// The actual Winelib binary for `yabridge-host.exe`. Will be hashed to check whether the user /// has updated yabridge. - pub yabridge_host_exe_so: PathBuf, + pub yabridge_host_exe_so: Option, + /// The same as `yabridge_host_exe`, but for the 32-bit verison. + pub yabridge_host_32_exe: Option, + /// The same as `yabridge_host_exe_so`, but for the 32-bit verison. We will hash this instead of + /// there's no 64-bit version available. + pub yabridge_host_32_exe_so: Option, } impl Default for Config { @@ -279,22 +288,26 @@ impl Config { // `yabridge-host.exe` should either be in the search path, or it should be in // `~/.local/share/yabridge` - let yabridge_host_exe = match which(YABRIDGE_HOST_EXE_NAME) + let yabridge_host_exe = which(YABRIDGE_HOST_EXE_NAME) .ok() - .or_else(|| xdg_dirs.find_data_file(YABRIDGE_HOST_EXE_NAME)) - { - Some(path) => path, - _ => { - return Err(anyhow!("Could not locate '{}'.", YABRIDGE_HOST_EXE_NAME)); - } - }; - let yabridge_host_exe_so = yabridge_host_exe.with_extension("exe.so"); + .or_else(|| xdg_dirs.find_data_file(YABRIDGE_HOST_EXE_NAME)); + let yabridge_host_exe_so = yabridge_host_exe + .as_ref() + .map(|path| path.with_extension("exe.so")); + let yabridge_host_32_exe = which(YABRIDGE_HOST_32_EXE_NAME) + .ok() + .or_else(|| xdg_dirs.find_data_file(YABRIDGE_HOST_32_EXE_NAME)); + let yabridge_host_32_exe_so = yabridge_host_32_exe + .as_ref() + .map(|path| path.with_extension("exe.so")); Ok(YabridgeFiles { libyabridge_vst2, libyabridge_vst3, yabridge_host_exe, yabridge_host_exe_so, + yabridge_host_32_exe, + yabridge_host_32_exe_so, }) } diff --git a/tools/yabridgectl/src/utils.rs b/tools/yabridgectl/src/utils.rs index 69cca610..a2e7027e 100644 --- a/tools/yabridgectl/src/utils.rs +++ b/tools/yabridgectl/src/utils.rs @@ -23,14 +23,14 @@ use std::collections::hash_map::DefaultHasher; use std::env; use std::fs; use std::hash::Hasher; -use std::io::{Read, Seek, SeekFrom}; +use std::io::{BufRead, BufReader, Read, Seek, SeekFrom}; use std::os::unix::fs as unix_fs; use std::os::unix::process::CommandExt; use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; use textwrap::Wrapper; -use crate::config::{self, Config, KnownConfig, YABRIDGE_HOST_EXE_NAME}; +use crate::config::{self, Config, KnownConfig, YABRIDGE_HOST_32_EXE_NAME, YABRIDGE_HOST_EXE_NAME}; use crate::files::{LibArchitecture, NativeFile}; /// (Part of) the expected output when running `yabridge-host.exe`. Used to verify that everything's @@ -132,6 +132,33 @@ pub fn get_file_type(path: PathBuf) -> Option { } } +/// Get the architecture (either 64-bit or 32-bit) of the default Wine prefix in `~/.wine`. Defaults +/// to 64-bit if `~/.wine` doesn't exist or if the prefix is invalid. +pub fn get_default_wine_prefix_arch() -> LibArchitecture { + let wine_system_reg_path = PathBuf::from(env::var("HOME").expect("$HOME is not set")) + .join(".wine") + .join("system.reg"); + + // Fall back to 64-bit if the prefix doesn't exist + let wine_system_reg = match fs::File::open(wine_system_reg_path) { + Ok(file) => file, + _ => return LibArchitecture::Lib64, + }; + + for line in BufReader::new(wine_system_reg) + .lines() + .filter_map(|l| l.ok()) + { + match line.as_str() { + "#arch=win32" => return LibArchitecture::Lib32, + "#arch=win64" => break, + _ => (), + }; + } + + LibArchitecture::Lib64 +} + /// Hash the conetnts of a file as an `i64` using Rust's built in hasher. Collisions are not a big /// issue in our situation so we can get away with this. /// @@ -310,8 +337,14 @@ pub fn verify_wine_setup(config: &mut Config) -> Result<()> { .context(format!("Could not find '{}'", YABRIDGE_HOST_EXE_NAME))?; // Hash the contents of `yabridge-host.exe.so` since `yabridge-host.exe` is only a Wine - // generated shell script - let yabridge_host_hash = hash_file(&files.yabridge_host_exe_so)?; + // generated shell script. If somehow only the 32-bit verison is installed, we'll just hash that + // one. + let yabridge_host_hash = hash_file( + &files + .yabridge_host_exe_so + .or(files.yabridge_host_32_exe_so) + .with_context(|| format!("Could not locate '{}.so'", YABRIDGE_HOST_EXE_NAME))?, + )?; // Since these checks can take over a second if wineserver isn't already running we'll only // perform them when something has changed @@ -323,9 +356,20 @@ pub fn verify_wine_setup(config: &mut Config) -> Result<()> { return Ok(()); } - let output = Command::new(&files.yabridge_host_exe) + // It could be that the default Wine prefix was created with `WINEARCH=win32` set. In that case + // we should run the 32-bit `yabridge-host.exe` since the 64-bit verison won't be able to run. + let host_binary_path = match get_default_wine_prefix_arch() { + LibArchitecture::Lib32 => files + .yabridge_host_32_exe + .with_context(|| format!("Could not find '{}'", YABRIDGE_HOST_32_EXE_NAME)), + LibArchitecture::Lib64 => files + .yabridge_host_exe + .with_context(|| format!("Could not find '{}'", YABRIDGE_HOST_EXE_NAME)), + }?; + + let output = Command::new(&host_binary_path) .output() - .with_context(|| format!("Could not run '{}'", files.yabridge_host_exe.display()))?; + .with_context(|| format!("Could not run '{}'", host_binary_path.display()))?; let stderr = String::from_utf8(output.stderr)?; // There are three scenarios here: