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)
+}