diff --git a/src/plugin/plugin-bridge.cpp b/src/plugin/plugin-bridge.cpp index 514ddc6f..b91a0e5c 100644 --- a/src/plugin/plugin-bridge.cpp +++ b/src/plugin/plugin-bridge.cpp @@ -422,7 +422,7 @@ intptr_t PluginBridge::dispatch(AEffect* /*plugin*/, logger.log_event(true, opcode, index, value, nullptr, option, std::nullopt); logger.log( - " WARNING: The host has dispatched an event before the plugin " + " Warning: The host has dispatched an event before the plugin " "has finished initializing, ignoring the event. (are we running " "Ardour 5.X?)"); logger.log_event_response(true, opcode, 0, nullptr, std::nullopt); diff --git a/tools/yabridgectl/Cargo.lock b/tools/yabridgectl/Cargo.lock index 0faad40f..598988cd 100644 --- a/tools/yabridgectl/Cargo.lock +++ b/tools/yabridgectl/Cargo.lock @@ -679,6 +679,7 @@ dependencies = [ "rayon", "serde", "serde_derive", + "textwrap", "toml", "walkdir", "xdg", diff --git a/tools/yabridgectl/Cargo.toml b/tools/yabridgectl/Cargo.toml index b67a0cb7..f8f2503a 100644 --- a/tools/yabridgectl/Cargo.toml +++ b/tools/yabridgectl/Cargo.toml @@ -19,6 +19,7 @@ promptly = "0.3.0" rayon = "1.3.1" serde = "1.0.114" serde_derive = "1.0.114" +textwrap = "*" toml = "0.5.6" walkdir = "2.3.1" xdg = "2.2.0" diff --git a/tools/yabridgectl/src/actions.rs b/tools/yabridgectl/src/actions.rs index 0972252c..f9b36f7c 100644 --- a/tools/yabridgectl/src/actions.rs +++ b/tools/yabridgectl/src/actions.rs @@ -26,6 +26,7 @@ use std::path::{Path, PathBuf}; use crate::config::{Config, InstallationMethod}; use crate::files; use crate::files::FoundFile; +use crate::utils::{verify_path_setup, wrap}; /// Add a direcotry to the plugin locations. Duplicates get ignord because we're using ordered sets. pub fn add_directory(config: &mut Config, path: PathBuf) -> Result<()> { @@ -46,7 +47,7 @@ pub fn remove_directory(config: &mut Config, path: &Path) -> Result<()> { let orphan_files = files::index_so_files(path); if !orphan_files.is_empty() { println!( - "WARNING: Found {} leftover '.so' files still in this directory:", + "Warning: Found {} leftover '.so' files still in this directory:", orphan_files.len() ); @@ -248,5 +249,26 @@ pub fn do_sync(config: &Config, prune: bool, verbose: bool) -> Result<()> { num_skipped_files ); + if config.method == InstallationMethod::Copy { + if let Err(shell_name) = verify_path_setup() { + println!( + "\n{}", + wrap(&format!( + "Warning: 'yabridge-host.exe' is not present in your login shell's search \ + path. Yabridge won't be able to run using the copy-based installatin method \ + until this is fixed.\n\ + Add '{}' to {}'s login shell {} environment variable. See the \ + troubleshooting section of the readme for more details. Rerun this command to \ + verify that this has been set up correctly, and then reboot your system.\n\ + \n\ + https://github.com/robbert-vdh/yabridge#troubleshooting-common-issues", + libyabridge_path.parent().unwrap().display(), + shell_name.bright_white(), + "PATH".bright_white() + )) + ) + } + } + Ok(()) } diff --git a/tools/yabridgectl/src/config.rs b/tools/yabridgectl/src/config.rs index 9d8001e8..38e2b2bd 100644 --- a/tools/yabridgectl/src/config.rs +++ b/tools/yabridgectl/src/config.rs @@ -56,7 +56,7 @@ pub struct Config { } /// Specifies how yabridge will be set up for the found plugins. -#[derive(Deserialize, Serialize, Debug)] +#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)] #[serde(rename_all = "snake_case")] pub enum InstallationMethod { /// Create a copy of `libyabridge.so` for every Windows VST2 plugin .dll file found. After diff --git a/tools/yabridgectl/src/main.rs b/tools/yabridgectl/src/main.rs index 440bf0d2..a478794a 100644 --- a/tools/yabridgectl/src/main.rs +++ b/tools/yabridgectl/src/main.rs @@ -24,9 +24,9 @@ use crate::config::Config; mod actions; mod config; mod files; +mod utils; // TODO: Naming and descriptions could be made clearer -// TODO: When creating copies, check whether `yabridge-host.exe` is in the PATH for the login shell // TODO: Reward parts of the readme fn main() -> Result<()> { diff --git a/tools/yabridgectl/src/utils.rs b/tools/yabridgectl/src/utils.rs new file mode 100644 index 00000000..05dd7a49 --- /dev/null +++ b/tools/yabridgectl/src/utils.rs @@ -0,0 +1,106 @@ +// yabridge: a Wine VST bridge +// Copyright (C) 2020 Robbert van der Helm +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! Small helper utilities. + +use colored::Colorize; +use std::env; +use std::path::Path; +use std::process::{Command, Stdio}; +use textwrap::Wrapper; + +/// Verify that `yabridge-host.exe` is accessible in a login shell. Returns unit if it is, or if we +/// the login shell is set to an unknown shell. In the last case we'll just print a warning since we +/// don't know how to invoke the shell as a login shell. This is needed when using copies to ensure +/// that yabridge can find the host binaries when the VST host is launched from the desktop +/// enviornment. +/// +/// When we could not find `yabridge-host.exe`, we'll return `Err(shell_name)` so we can print a +/// descriptive warning message. +/// +/// # TODO +/// +/// Starting from Rust 1.45 we can just modify `argv[0]` to start with a dash instead, see +/// https://doc.rust-lang.org/std/os/unix/process/trait.CommandExt.html#tymethod.arg0 +/// https://github.com/rust-lang/rust/issues/66510 +pub fn verify_path_setup() -> Result<(), String> { + match env::var("SHELL") { + Ok(shell_path) => { + // `$SHELL` will often contain a full path, but it doesn't have to + let shell = Path::new(&shell_path) + .file_name() + .and_then(|os_str| os_str.to_str()) + .unwrap_or_else(|| shell_path.as_str()); + + let mut command = Command::new(&shell_path); + let command = match shell { + // All of these shells support the `-l` flag to start a login shell and + // "-c" to directly run a command under that login shell + "ash" | "bash" | "csh" | "ksh" | "dash" | "fish" | "sh" | "tcsh" | "zsh" => command + .arg("-l") + .arg("-c") + .arg("command -v yabridge-host.exe"), + // I don't know if anyone uses PowerShell as their login shell under Linux, but it + // doesn't implement the POSIX `command` function so we'll just use which instead + "pwsh" => command.arg("-l").arg("-c").arg("which yabridge-host.exe"), + shell => { + eprintln!( + "{}", + wrap(&format!( + "WARNING: Yabridgectl does not know how to handle your login shell, \ + '{}', skipping PATH setup check. Feel free to open a bug report to \ + get yabridgectl to support your shell.\n\ + \n\ + https://github.com/robbert-vdh/yabridge/issues", + shell.bright_white(), + )) + ); + return Ok(()); + } + }; + + // For the login shell we want to a clean environment, but we still have to set `$HOME` + // or else most shells won't know which profile to load + command + .env_clear() + .env("HOME", env::var("HOME").unwrap_or_default()); + + match command.stdout(Stdio::null()).stderr(Stdio::null()).status() { + Ok(status) if status.success() => Ok(()), + Ok(_) => Err(shell.to_string()), + Err(err) => { + eprintln!( + "Warning: could not run login shell, skipping PATH setup check: {}", + err + ); + Ok(()) + } + } + } + Err(_) => { + eprintln!("\nWarning: Could not determine login shell, skipping PATH setup check"); + Ok(()) + } + } +} + +/// Wrap a long paragraph of text to terminal width, or 80 characters if the width of the terminal +/// can't be determined. Everything after the first line gets indented with four spaces. +pub fn wrap(text: &str) -> String { + let wrapper = Wrapper::with_termwidth().subsequent_indent(" "); + + wrapper.fill(text) +}