diff --git a/CHANGELOG.md b/CHANGELOG.md index 56a67c03..836ba84e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,16 @@ Versioning](https://semver.org/spec/v2.0.0.html). - Fixed a regression where the `editor_double_embed` option would cause X11 errors and crash yabridge. +### yabridgectl + +- When running `yabridgectl sync`, existing .so files will no longer be + recreated unless necessary. This prevents hosts from rescanning all plugins + after setting up a single new plugin through yabridgectl. Running + `yabridgectl sync` after updating yabridge will still recreate all existing + .so files as usual. +- Added a `--force` option to `yabridgectl sync` to always recreate existing .so + files like in previous versions. + ## [1.7.0] - 2020-10-13 ### Changed diff --git a/tools/yabridgectl/src/actions.rs b/tools/yabridgectl/src/actions.rs index 2122f6de..b6bce6c0 100644 --- a/tools/yabridgectl/src/actions.rs +++ b/tools/yabridgectl/src/actions.rs @@ -145,6 +145,7 @@ pub fn set_settings(config: &mut Config, options: &SetOptions) -> Result<()> { /// Options passed to `yabridgectl sync`, see `main()` for the definitions of these options. pub struct SyncOptions { + pub force: bool, pub no_verify: bool, pub prune: bool, pub verbose: bool, @@ -154,6 +155,7 @@ pub struct SyncOptions { /// `.so` files if the prune option is set. pub fn do_sync(config: &mut Config, options: &SyncOptions) -> Result<()> { let libyabridge_path = config.libyabridge()?; + let libyabridge_hash = utils::hash_file(&libyabridge_path)?; println!("Using '{}'\n", libyabridge_path.display()); let results = config @@ -162,6 +164,7 @@ pub fn do_sync(config: &mut Config, options: &SyncOptions) -> Result<()> { // Keep track of some global statistics let mut num_installed = 0; + let mut num_new = 0; let mut skipped_dll_files: Vec = Vec::new(); let mut orphan_so_files: Vec = Vec::new(); for (path, search_results) in results { @@ -173,14 +176,45 @@ pub fn do_sync(config: &mut Config, options: &SyncOptions) -> Result<()> { println!("{}:", path.display()); } for plugin in search_results.vst2_files { - // If the target file already exists, we'll remove it first to prevent issues with - // mixing symlinks and regular files. We check `std::fs::symlink_metadata` instead of - // `Path::exists()` because the latter reports false for broken symlinks. let target_path = plugin.with_extension("so"); - if fs::symlink_metadata(&target_path).is_ok() { - utils::remove_file(&target_path)?; - } + // We'll only recreate existing files when updating yabridge, when switching between the + // symlink and copy installation methods, or when the `force` option is set. If the + // target file already exists and does not require updating, we'll just skip the file + // since some DAWs will otherwise unnecessarily reindex the file. + // We check `std::fs::symlink_metadata` instead of `Path::exists()` because the latter + // reports false for broken symlinks. + if let Ok(metadata) = fs::symlink_metadata(&target_path) { + match (options.force, &config.method) { + (false, InstallationMethod::Copy) => { + // If the target file is already a real file (not a symlink) and its hash is + // the same as the `libyabridge.so` file we're trying to copy there, then we + // don't have to do anything + if metadata.file_type().is_file() + && utils::hash_file(&target_path)? == libyabridge_hash + { + continue; + } + } + (false, InstallationMethod::Symlink) => { + // If the target file is already a symlink to `libyabridge.so`, then we can + // skip this file + if metadata.file_type().is_symlink() + && target_path.read_link()? == libyabridge_path + { + continue; + } + } + // With the force option we always want to recreate existing .so files + (true, _) => (), + } + + utils::remove_file(&target_path)?; + }; + + // Since we skip some files, we'll also keep track of how many new file we've actually + // set up + num_new += 1; match config.method { InstallationMethod::Copy => { utils::copy(&libyabridge_path, &target_path)?; @@ -234,9 +268,10 @@ pub fn do_sync(config: &mut Config, options: &SyncOptions) -> Result<()> { } println!( - "Finished setting up {} plugins using {}, skipped {} non-plugin .dll files", + "Finished setting up {} plugins using {} ({} new), skipped {} non-plugin .dll files", num_installed, config.method.plural_name(), + num_new, num_skipped_files ); diff --git a/tools/yabridgectl/src/main.rs b/tools/yabridgectl/src/main.rs index 0496e568..86f3eb6b 100644 --- a/tools/yabridgectl/src/main.rs +++ b/tools/yabridgectl/src/main.rs @@ -100,6 +100,12 @@ fn main() -> Result<()> { .subcommand( App::new("sync") .about("Set up or update yabridge for all plugins") + .arg( + Arg::with_name("force") + .short('f') + .long("force") + .about("Always update files, even not necessary"), + ) .arg( Arg::with_name("no-verify") .short('n') @@ -156,6 +162,7 @@ fn main() -> Result<()> { ("sync", Some(options)) => actions::do_sync( &mut config, &actions::SyncOptions { + force: options.is_present("force"), no_verify: options.is_present("no-verify"), prune: options.is_present("prune"), verbose: options.is_present("verbose"),