mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-05-07 03:50:11 +02:00
Merge branch 'feature/cli-tools'
This commit is contained in:
+37
-28
@@ -11,8 +11,6 @@ on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
release:
|
||||
types: [created]
|
||||
|
||||
defaults:
|
||||
run:
|
||||
@@ -106,32 +104,43 @@ jobs:
|
||||
name: ${{ env.ARCHIVE_NAME }}
|
||||
path: ${{ env.ARCHIVE_NAME }}
|
||||
|
||||
upload-releases:
|
||||
name: Upload the created artifacts to the releases page
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build-bionic, build-focal]
|
||||
if: ${{ github.event_name == 'release' }}
|
||||
build-yabridgectl:
|
||||
name: Build yabridgectl
|
||||
runs-on: ubuntu-18.04
|
||||
outputs:
|
||||
artifact-name: ${{ env.ARCHIVE_NAME }}
|
||||
defaults:
|
||||
run:
|
||||
working-directory: tools/yabridgectl
|
||||
steps:
|
||||
# They don't allow you to specify multiple file names for these actions
|
||||
- uses: actions/download-artifact@v2
|
||||
- uses: actions/checkout@v2
|
||||
# Needed for git-describe to do anything useful
|
||||
- name: Fetch all git history
|
||||
run: git fetch --force --prune --tags --unshallow
|
||||
- name: Determine build archive name
|
||||
run: |
|
||||
export ARCHIVE_NAME=yabridgectl-$(git describe --always).tar.gz
|
||||
echo ::set-env "name=ARCHIVE_NAME::$ARCHIVE_NAME"
|
||||
- name: Set up Rust toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
name: ${{ needs.build-bionic.outputs.artifact-name }}
|
||||
- uses: actions/download-artifact@v2
|
||||
toolchain: stable
|
||||
profile: minimal
|
||||
default: true
|
||||
- name: Build the binaries
|
||||
run: cargo build --release
|
||||
- name: Strip remaining debug symbols
|
||||
run: strip target/release/yabridgectl
|
||||
- name: Create an archive for the binaries
|
||||
run: |
|
||||
mkdir yabridgectl
|
||||
cp target/release/yabridgectl README.md yabridgectl
|
||||
|
||||
tar -caf "$ARCHIVE_NAME" yabridgectl
|
||||
rm -rf yabridgectl
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ${{ needs.build-focal.outputs.artifact-name }}
|
||||
- uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ github.event.release.upload_url }}
|
||||
asset_path: ./${{ needs.build-bionic.outputs.artifact-name }}
|
||||
asset_name: $${{ needs.build-bionic.outputs.artifact-name }}
|
||||
asset_content_type: application/x-compressed-tar
|
||||
- uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ github.event.release.upload_url }}
|
||||
asset_path: ./${{ needs.build-focal.outputs.artifact-name }}
|
||||
asset_name: $${{ needs.build-focal.outputs.artifact-name }}
|
||||
asset_content_type: application/x-compressed-tar
|
||||
name: ${{ env.ARCHIVE_NAME }}
|
||||
# For some reason there's no way to tell GitHub actions to run actions
|
||||
# in a subdirectory
|
||||
path: tools/yabridgectl/${{ env.ARCHIVE_NAME }}
|
||||
|
||||
@@ -6,6 +6,19 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic
|
||||
Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- By somewhat popular demand, yabridge now comes with yabridgectl, a utility
|
||||
that can automatically set up and manage yabridge for you. It also performs
|
||||
some basic checks to ensure that everything has been set up correctly.
|
||||
Yabridgectl can be downloaded separately from the GitHub releases page and its
|
||||
use is completely optional, so you don't have to use it if you don't want to.
|
||||
Check out the
|
||||
[readme](https://github.com/robbert-vdh/yabridge/tree/feature/cli-tools/tools/yabridgectl/README.md)
|
||||
for more information on how it works.
|
||||
|
||||
## [1.2.1] - 2020-06-20
|
||||
|
||||
### Changed
|
||||
|
||||
@@ -27,6 +27,12 @@ Please let me know if there are any issues with other VST hosts.
|
||||
|
||||
## Usage
|
||||
|
||||
TODO: Refactor these sections to refer to yabridgectl for most of the setup. If
|
||||
you are reading this, then you can either follow the instructions below or you
|
||||
can download a preview version of yabridgectl from the [automated
|
||||
builds](https://github.com/robbert-vdh/yabridge/actions?query=workflow%3A%22Automated+builds%22+branch%3Amaster)
|
||||
page.
|
||||
|
||||
You can either download a prebuilt version of yabridge through the GitHub
|
||||
[releases](https://github.com/robbert-vdh/yabridge/releases) section, or you can
|
||||
compile it from source using the instructions in the [build](#Building) section
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
target/
|
||||
Generated
+507
@@ -0,0 +1,507 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "043164d8ba5c4c3035fec9bbee8647c0261d788f3474306f93bb65901cae0e86"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "3.0.0-beta.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "860643c53f980f0d38a5e25dfab6c3c93b2cb3aa1fe192643d17a293c6c41936"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"bitflags",
|
||||
"clap_derive",
|
||||
"indexmap",
|
||||
"lazy_static",
|
||||
"os_str_bytes",
|
||||
"strsim",
|
||||
"term_size 1.0.0-beta1",
|
||||
"termcolor",
|
||||
"textwrap",
|
||||
"unicode-width",
|
||||
"vec_map",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "3.0.0-beta.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb51c9e75b94452505acd21d929323f5a5c6c4735a852adbd39ef5fb1b014f30"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colored"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"lazy_static",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285"
|
||||
dependencies = [
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
"maybe-uninit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
"lazy_static",
|
||||
"maybe-uninit",
|
||||
"memoffset",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-queue"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
"maybe-uninit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if",
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
|
||||
dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c398b2b113b55809ceb9ee3e753fcbac793f1956663f3c36549c1346015c2afe"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kernel32-sys"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
||||
dependencies = [
|
||||
"winapi 0.2.8",
|
||||
"winapi-build",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.72"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9f8082297d534141b30c8d39e9b1773713ab50fdbe4ff30f750d063b3bfd701"
|
||||
|
||||
[[package]]
|
||||
name = "maybe-uninit"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c198b026e1bbf08a937e94c6c60f9ec4a2267f5b0d2eec9c1b21b061ce2be55f"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "os_str_bytes"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06de47b848347d8c4c94219ad8ecd35eb90231704b067e67e6ae2e36ee023510"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "18f33027081eba0a6d8aba6d1b1c3a3be58cbb12106341c2d5759fcd9b5277e7"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a5b4b77fdb63c1eca72173d68d24501c54ab1269409f6b672c85deb18af69de"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn-mid",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62f02856753d04e03e26929f820d0a0a337ebe71f849801eea335d464b349080"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"crossbeam-deque",
|
||||
"either",
|
||||
"rayon-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e92e15d89083484e11353891f1af602cc661426deb9564c298b270c726973280"
|
||||
dependencies = [
|
||||
"crossbeam-deque",
|
||||
"crossbeam-queue",
|
||||
"crossbeam-utils",
|
||||
"lazy_static",
|
||||
"num_cpus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.114"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5317f7588f0a5078ee60ef675ef96735a1442132dc645eb1d12c018620ed8cd3"
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.114"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a0be94b04690fbaed37cddffc5c134bf537c8e3329d53e982fe04c374978f8e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8d5d96e8cbb005d6959f119f773bfaebb5684296108fb32600c00cde305b2cd"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn-mid"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "term_size"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "term_size"
|
||||
version = "1.0.0-beta1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8a17d8699e154863becdf18e4fd28bd0be27ca72856f54daf75c00f2566898f"
|
||||
dependencies = [
|
||||
"kernel32-sys",
|
||||
"libc",
|
||||
"winapi 0.2.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
||||
dependencies = [
|
||||
"term_size 0.3.2",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
||||
|
||||
[[package]]
|
||||
name = "vec_map"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d"
|
||||
dependencies = [
|
||||
"same-file",
|
||||
"winapi 0.3.9",
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-build"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||
dependencies = [
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "xdg"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57"
|
||||
|
||||
[[package]]
|
||||
name = "yabridgectl"
|
||||
version = "1.2.1"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"clap",
|
||||
"colored",
|
||||
"lazy_static",
|
||||
"rayon",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"toml",
|
||||
"walkdir",
|
||||
"xdg",
|
||||
]
|
||||
@@ -0,0 +1,22 @@
|
||||
[package]
|
||||
name = "yabridgectl"
|
||||
# This version is linked to yabridge's version for clarity's sake and because
|
||||
# there's not a lot going on here
|
||||
version = "1.2.1"
|
||||
authors = ["Robbert van der Helm <mail@robbertvanderhelm.nl>"]
|
||||
edition = "2018"
|
||||
description = "Optional utility to help set up yabridge"
|
||||
repository = "https://github.com/robbert-vdh/yabridge"
|
||||
license = "GPL-3.0-or-later"
|
||||
|
||||
[dependencies]
|
||||
aho-corasick = "0.7.13"
|
||||
colored = "2.0.0"
|
||||
clap = { version = "3.0.0-beta.1", features = ["wrap_help"] }
|
||||
lazy_static = "1.4.0"
|
||||
rayon = "1.3.1"
|
||||
serde = "1.0.114"
|
||||
serde_derive = "1.0.114"
|
||||
toml = "0.5.6"
|
||||
walkdir = "2.3.1"
|
||||
xdg = "2.2.0"
|
||||
@@ -0,0 +1,103 @@
|
||||
# yabridgectl
|
||||
|
||||
A small, optional utility to set up
|
||||
[yabridge](https://github.com/robbert-vdh/yabridge) for several directories at
|
||||
once and to keep them updated.
|
||||
|
||||
## Usage
|
||||
|
||||
Yabridgectl can be downloaded from the GitHub releases section, and it can run
|
||||
from anywhere. All of the information below can also be found by running
|
||||
`yabridgectl --help`.
|
||||
|
||||
### Yabridge path
|
||||
|
||||
Yabrdgectl will need to know where it can find `libyabridge.so`. By default it
|
||||
will look for it in both `~/.local/share/yabridge` (the recommended installation
|
||||
directory when using the prebuilt binaries) and in `/usr/lib` (used when using
|
||||
the AUR packages). You can use the command below to override this behaviour and
|
||||
to use a custom installation directory instead.
|
||||
|
||||
```shell
|
||||
yabridgectl set --path=<path/to/directory/containing/yabridge>
|
||||
```
|
||||
|
||||
### Installation methods
|
||||
|
||||
By default, yabridgectl will use the copy-based installation method for yabridge
|
||||
since this installation method works everywhere. If you are using a DAW that
|
||||
supports individually sandboxed plugins, then you can choose between using
|
||||
copies and symlinks using the command below.
|
||||
|
||||
```shell
|
||||
yabridgectl set --method=<copy|symlink>
|
||||
```
|
||||
|
||||
### Managing directories
|
||||
|
||||
Yabridgectl manage Windows VST plugin install locations for you. To add, remove
|
||||
and list directories, or to list the plugins currently installed inside one of
|
||||
those directories, you can use the command below.
|
||||
|
||||
```shell
|
||||
# Add a directory to watch
|
||||
# For instance, use the below command for the most common VST2 plugin directory
|
||||
# yabridgectl add "$HOME/.wine/drive_c/Program Files/Steinberg/VstPlugins"
|
||||
yabridgectl add <path/to/plugins>
|
||||
# No longer watch a directory, this will ask you if you want to remove any leftover yabridge files
|
||||
yabridgectl rm <path/to/plugins>
|
||||
# List the currently watched directories
|
||||
yabridgectl list
|
||||
# Show the current settings and the installation status for all plugins in the watched directories
|
||||
yabridgectl status
|
||||
```
|
||||
|
||||
### Installing and updating
|
||||
|
||||
Finally you can set up or update yabridge for all of your plugins at once using
|
||||
the command below. By default yabridgectl will warn you if it finds `.so` files
|
||||
without an accompanying `.dll` file, but it will only delete those when using
|
||||
the `--prune` option.
|
||||
|
||||
```shell
|
||||
# Set up copies or symlinks of yabridge for all plugins under the watched directories
|
||||
yabridgectl sync
|
||||
# Set up yabridge, and also remove any '.so' still leftover after removing a plugin
|
||||
yabridgectl sync --prune
|
||||
```
|
||||
|
||||
## Alternatives
|
||||
|
||||
If you want to script your own installation behaviour and don't feel like using
|
||||
yabridgectl, then you could use one of the below bash snippets as a base. This
|
||||
approach is slightly less robust and does not do any problem detection or status
|
||||
reporting, but it will get you started.
|
||||
|
||||
```shell
|
||||
# For use with symlinks
|
||||
yabridge_home=$HOME/.local/share/yabridge
|
||||
plugin_dir="$HOME/.wine/drive_c/Program Files/Steinberg/VstPlugins"
|
||||
|
||||
find -L "$plugin_dir" -type f -iname '*.dll' -print0 |
|
||||
xargs -0 -P$(nproc) -I{} bash -c "(winedump -j export '{}' | grep -qE 'VSTPluginMain|main|main_plugin') && printf '{}\0'" |
|
||||
sed -z 's/\.dll$/.so/' |
|
||||
xargs -0 -n1 ln -sf "$yabridge_home/libyabridge.so"
|
||||
|
||||
# For use with copies
|
||||
yabridge_home=$HOME/.local/share/yabridge
|
||||
plugin_dir="$HOME/.wine/drive_c/Program Files/Steinberg/VstPlugins"
|
||||
|
||||
find -L "$plugin_dir" -type f -iname '*.dll' -print0 |
|
||||
xargs -0 -P$(nproc) -I{} bash -c "(winedump -j export '{}' | grep -qE 'VSTPluginMain|main|main_plugin') && printf '{}\0'" |
|
||||
sed -z 's/\.dll$/.so/' |
|
||||
xargs -0 -n1 cp "$yabridge_home/libyabridge.so"
|
||||
```
|
||||
|
||||
## Building
|
||||
|
||||
After installing [Rust](https://rustup.rs/), simply run the below to compile and
|
||||
run:
|
||||
|
||||
```shell
|
||||
cargo run --release
|
||||
```
|
||||
@@ -0,0 +1,200 @@
|
||||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
use rayon::prelude::*;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::fmt::Display;
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
use xdg::BaseDirectories;
|
||||
|
||||
use crate::files::{self, SearchResults};
|
||||
|
||||
/// The name of the config file, relative to `$XDG_CONFIG_HOME/CONFIG_PREFIX`.
|
||||
const CONFIG_FILE_NAME: &str = "config.toml";
|
||||
/// The name of the XDG base directory prefix for yabridgectl, relative to `$XDG_CONFIG_HOME` and
|
||||
/// `$XDG_DATA_HOME`.
|
||||
const YABRIDGECTL_PREFIX: &str = "yabridgectl";
|
||||
|
||||
/// The name of the library file we're searching for.
|
||||
const LIBYABRIDGE_NAME: &str = "libyabridge.so";
|
||||
/// 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";
|
||||
|
||||
/// The configuration used for yabridgectl. This will be serialized to and deserialized from
|
||||
/// `$XDG_CONFIG_HOME/yabridge/config.toml`.
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
pub struct Config {
|
||||
/// The installation method to use. We will default to creating copies since that works
|
||||
/// everywehre.
|
||||
pub method: InstallationMethod,
|
||||
/// The path to the directory containing `libyabridge.so`. If not set, then yabridgectl will
|
||||
/// look in `/usr/lib` and `$XDG_DATA_HOME/yabridge` since those are the expected locations for
|
||||
/// yabridge to be installed in.
|
||||
pub yabridge_home: Option<PathBuf>,
|
||||
/// Directories to search for Windows VST plugins. We're using an ordered set here out of
|
||||
/// convenience so we can't get duplicates and the config file is always sorted.
|
||||
pub plugin_dirs: BTreeSet<PathBuf>,
|
||||
}
|
||||
|
||||
/// Specifies how yabridge will be set up for the found plugins.
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum InstallationMethod {
|
||||
/// Create a copy of `libyabridge.so` for every Windows VST2 plugin .dll file found. After
|
||||
/// updating yabridge, the user will have to rerun `yabridgectl sync` to copy over the new
|
||||
/// version.
|
||||
Copy,
|
||||
/// This will create a symlink to `libyabridge.so` for every VST2 .dll file in the plugin
|
||||
/// directories. As explained in the readme, this makes updating easier and remvoes the need to
|
||||
/// modify the `PATH` environment variable.
|
||||
Symlink,
|
||||
}
|
||||
|
||||
impl InstallationMethod {
|
||||
/// The plural term for this installation methodd, using in string formatting.
|
||||
pub fn plural(&self) -> &str {
|
||||
match &self {
|
||||
InstallationMethod::Copy => "copies",
|
||||
InstallationMethod::Symlink => "symlinks",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for InstallationMethod {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match &self {
|
||||
InstallationMethod::Copy => write!(f, "copy"),
|
||||
InstallationMethod::Symlink => write!(f, "symlink"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
/// Try to read the config file, creating a new default file if necessary. This will fail if the
|
||||
/// file could not be created or if it could not be parsed.
|
||||
pub fn read() -> Result<Config, String> {
|
||||
match yabridgectl_directories()?.find_config_file(CONFIG_FILE_NAME) {
|
||||
Some(path) => {
|
||||
let toml_str = fs::read_to_string(&path).map_err(|err| {
|
||||
format!(
|
||||
"Could not read config file at '{}': {}",
|
||||
path.display(),
|
||||
err
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(toml::from_str(&toml_str)
|
||||
.map_err(|err| format!("Could not parse TOML: {}", err))?)
|
||||
}
|
||||
None => {
|
||||
let defaults = Config {
|
||||
method: InstallationMethod::Copy,
|
||||
yabridge_home: None,
|
||||
plugin_dirs: BTreeSet::new(),
|
||||
};
|
||||
|
||||
// If no existing config file exists, then write a new config file with default
|
||||
// values
|
||||
defaults.write()?;
|
||||
|
||||
Ok(defaults)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Write the config to disk, creating the file if it does not yet exist.
|
||||
pub fn write(&self) -> Result<(), String> {
|
||||
let toml_str = toml::to_string_pretty(&self)
|
||||
.map_err(|err| format!("Could not format TOML: {}", err))?;
|
||||
let config_file = yabridgectl_directories()?
|
||||
.place_config_file(CONFIG_FILE_NAME)
|
||||
.map_err(|err| format!("Could not write config file: {}", err))?;
|
||||
|
||||
fs::write(&config_file, toml_str).map_err(|err| {
|
||||
format!(
|
||||
"Could not write config file to '{}': {}",
|
||||
config_file.display(),
|
||||
err
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// Return the path to `libyabridge.so`, or a descriptive error if it can't be found. If
|
||||
/// `yabridge_home` is `None`, then we'll search in both `/usr/lib` and
|
||||
/// `$XDG_DATA_HOME/yabridge`.
|
||||
pub fn libyabridge(&self) -> Result<PathBuf, String> {
|
||||
match &self.yabridge_home {
|
||||
Some(directory) => {
|
||||
let candidate = directory.join(LIBYABRIDGE_NAME);
|
||||
if candidate.exists() {
|
||||
Ok(candidate)
|
||||
} else {
|
||||
Err(format!(
|
||||
"Could not find '{}' in '{}'.",
|
||||
LIBYABRIDGE_NAME,
|
||||
directory.display()
|
||||
))
|
||||
}
|
||||
}
|
||||
None => {
|
||||
// Search in the two common installation locations if no path was set explicitely
|
||||
let system_path = Path::new("/usr/lib");
|
||||
let user_path = yabridge_directories()?.get_data_home();
|
||||
for directory in &[system_path, &user_path] {
|
||||
let candidate = directory.join(LIBYABRIDGE_NAME);
|
||||
if candidate.exists() {
|
||||
return Ok(candidate);
|
||||
}
|
||||
}
|
||||
|
||||
Err(format!(
|
||||
"Could not find '{}' in either '{}' or '{}'. You can tell yabridgectl where \
|
||||
to search for it using 'yabridgectl set --path=<path>'.",
|
||||
LIBYABRIDGE_NAME,
|
||||
system_path.display(),
|
||||
user_path.display()
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Search for VST2 plugins in all of the registered plugins directories. This will return an
|
||||
/// error if `winedump` could not be called.
|
||||
pub fn index_directories(&self) -> Result<BTreeMap<&Path, SearchResults>, std::io::Error> {
|
||||
self.plugin_dirs
|
||||
.par_iter()
|
||||
.map(|path| files::index(path).map(|search_results| (path.as_path(), search_results)))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
/// Fetch the XDG base directories for yabridge's own files, converting any error messages if this
|
||||
/// somehow fails into a printable string to reduce boiler plate. This is only used when searching
|
||||
/// for `libyabridge.so` when no explicit search path has been set.
|
||||
fn yabridge_directories() -> Result<BaseDirectories, String> {
|
||||
BaseDirectories::with_prefix(YABRIDGE_PREFIX)
|
||||
.map_err(|err| format!("Error while parsing base directories: {}", err))
|
||||
}
|
||||
|
||||
/// Fetch the XDG base directories used for yabridgectl, converting any error messages if this
|
||||
/// somehow fails into a printable string to reduce boiler plate.
|
||||
fn yabridgectl_directories() -> Result<BaseDirectories, String> {
|
||||
BaseDirectories::with_prefix(YABRIDGECTL_PREFIX)
|
||||
.map_err(|err| format!("Error while parsing base directories: {}", err))
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! Functions to index plugins and to set up yabridge for those plugins.
|
||||
|
||||
use aho_corasick::AhoCorasick;
|
||||
use lazy_static::lazy_static;
|
||||
use rayon::prelude::*;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
/// Stores the results from searching for Windows VST plugin `.dll` files and native Linux `.so`
|
||||
/// files inside of a directory. These `.so` files are kept track of so we can report the current
|
||||
/// installation status and to be able to prune orphan files.
|
||||
#[derive(Debug)]
|
||||
pub struct SearchResults {
|
||||
/// Absolute paths to the found VST2 `.dll` files.
|
||||
pub vst2_files: Vec<PathBuf>,
|
||||
/// `.dll` files skipped over during the serach. Used for printing statistics and shown when
|
||||
/// running `yabridgectl sync --verbose`.
|
||||
pub skipped_files: Vec<PathBuf>,
|
||||
/// Absolute paths to any `.so` files inside of the directory, and whether they're a symlink or
|
||||
/// a regular file.
|
||||
pub so_files: Vec<FoundFile>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum FoundFile {
|
||||
Symlink(PathBuf),
|
||||
Regular(PathBuf),
|
||||
}
|
||||
|
||||
impl SearchResults {
|
||||
/// For every found VST2 plugin, find the associated copy or symlink of `libyabridge.so`. The
|
||||
/// returned hashmap will contain a `None` value for plugins that have not yet been set up.
|
||||
///
|
||||
/// These two functions could be combined into a single function, but speed isn't really an
|
||||
/// issue here and it's a bit more organized this way.
|
||||
pub fn installation_status(&self) -> BTreeMap<&Path, Option<&FoundFile>> {
|
||||
let so_files: HashMap<&Path, &FoundFile> = self
|
||||
.so_files
|
||||
.iter()
|
||||
.map(|file| (file.path(), file))
|
||||
.collect();
|
||||
|
||||
self.vst2_files
|
||||
.iter()
|
||||
.map(
|
||||
|path| match so_files.get(path.with_extension("so").as_path()) {
|
||||
Some(&file) => (path.as_path(), Some(file)),
|
||||
None => (path.as_path(), None),
|
||||
},
|
||||
)
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Find all `.so` files in the search results that do not belong to a VST2 plugin `.dll` file.
|
||||
pub fn orphans(&self) -> Vec<&FoundFile> {
|
||||
// We need to store these in a map so we can easily entries with corresponding `.dll` files
|
||||
let mut orphans: HashMap<&Path, &FoundFile> = self
|
||||
.so_files
|
||||
.iter()
|
||||
.map(|file| (file.path(), file))
|
||||
.collect();
|
||||
for vst2_path in &self.vst2_files {
|
||||
orphans.remove(vst2_path.with_extension("so").as_path());
|
||||
}
|
||||
|
||||
orphans.into_iter().map(|(_, file)| file).collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl FoundFile {
|
||||
/// Return the path of a found `.so` file.
|
||||
pub fn path(&self) -> &Path {
|
||||
match &self {
|
||||
FoundFile::Symlink(path) => path,
|
||||
FoundFile::Regular(path) => path,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Search for Windows VST2 plugins and .so files under a directory. This will return an error if
|
||||
/// the directory does not exist, or if `winedump` could not be found.
|
||||
pub fn index(directory: &Path) -> Result<SearchResults, std::io::Error> {
|
||||
// First we'll find all .dll and .so files in the directory
|
||||
let mut dll_files: Vec<PathBuf> = Vec::new();
|
||||
let mut so_files: Vec<FoundFile> = Vec::new();
|
||||
// XXX: We're silently skipping directories and files we don't have permission to read. This
|
||||
// sounds like the expected behavior, but I"m not entirely sure.
|
||||
for entry in WalkDir::new(directory)
|
||||
.follow_links(true)
|
||||
.into_iter()
|
||||
.filter_map(|e| e.ok())
|
||||
.filter(|x| !x.file_type().is_dir())
|
||||
{
|
||||
match entry.path().extension().and_then(|os| os.to_str()) {
|
||||
Some("dll") => dll_files.push(entry.into_path()),
|
||||
Some("so") => {
|
||||
if entry.path_is_symlink() {
|
||||
so_files.push(FoundFile::Symlink(entry.into_path()));
|
||||
} else {
|
||||
so_files.push(FoundFile::Regular(entry.into_path()));
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref VST2_AUTOMATON: AhoCorasick =
|
||||
AhoCorasick::new_auto_configured(&["VSTPluginMain", "main", "main_plugin"]);
|
||||
}
|
||||
|
||||
// THne we'll figure out which `.dll` files are VST2 plugins and which should be skipped by
|
||||
// checking whether the file contains one of the VST2 entry point functions. The boolean flag in
|
||||
// this vector indicates whether it is a VST2 plugin.
|
||||
let dll_files: Vec<(PathBuf, bool)> = dll_files
|
||||
.into_par_iter()
|
||||
.map(|path| {
|
||||
let exported_functions = Command::new("winedump")
|
||||
.arg("-j")
|
||||
.arg("export")
|
||||
.arg(&path)
|
||||
.output()?
|
||||
.stdout;
|
||||
|
||||
Ok((path, VST2_AUTOMATON.is_match(exported_functions)))
|
||||
})
|
||||
.collect::<Result<_, std::io::Error>>()?;
|
||||
|
||||
let mut vst2_files = Vec::new();
|
||||
let mut skipped_files = Vec::new();
|
||||
for (path, is_vst2_plugin) in dll_files {
|
||||
if is_vst2_plugin {
|
||||
vst2_files.push(path);
|
||||
} else {
|
||||
skipped_files.push(path);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(SearchResults {
|
||||
vst2_files,
|
||||
skipped_files,
|
||||
so_files,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,364 @@
|
||||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
use clap::{app_from_crate, App, AppSettings, Arg, ArgMatches};
|
||||
use colored::Colorize;
|
||||
use std::fs;
|
||||
use std::os::unix::fs::symlink;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::exit;
|
||||
|
||||
use crate::config::{Config, InstallationMethod};
|
||||
use crate::files::FoundFile;
|
||||
|
||||
mod config;
|
||||
mod files;
|
||||
|
||||
// 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: Check for left over files when removing directory
|
||||
// TODO: Reward parts of the readme
|
||||
|
||||
fn main() {
|
||||
let mut config = match Config::read() {
|
||||
Ok(config) => config,
|
||||
Err(err) => {
|
||||
eprintln!("Error while reading config:\n\n{}", err);
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
// Used for validation in `yabridgectl rm <path>`
|
||||
let plugin_directories: Vec<&str> = config
|
||||
.plugin_dirs
|
||||
.iter()
|
||||
.map(|path| path.to_str().expect("Path contains invalid unicode"))
|
||||
.collect();
|
||||
|
||||
let matches = app_from_crate!()
|
||||
.setting(AppSettings::SubcommandRequiredElseHelp)
|
||||
.subcommand(
|
||||
App::new("add").about("Add a plugin install location").arg(
|
||||
Arg::with_name("path")
|
||||
.about("Path to a directory containing Windows VST plugins")
|
||||
.validator(validate_path)
|
||||
.takes_value(true)
|
||||
.required(true),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
App::new("rm")
|
||||
.about("Remove a plugin install location")
|
||||
.arg(
|
||||
Arg::with_name("path")
|
||||
.about("Path to a directory")
|
||||
.possible_values(&plugin_directories)
|
||||
.takes_value(true)
|
||||
.required(true),
|
||||
),
|
||||
)
|
||||
.subcommand(App::new("list").about("List the plugin install locations"))
|
||||
.subcommand(App::new("status").about("Show the installation status for all plugins"))
|
||||
.subcommand(
|
||||
App::new("set")
|
||||
.about("Change installation method or yabridge path")
|
||||
.setting(AppSettings::ArgRequiredElseHelp)
|
||||
.arg(
|
||||
Arg::with_name("method")
|
||||
.long("method")
|
||||
.about("The installation method to use")
|
||||
.long_about("The installation method to use.")
|
||||
.possible_values(&["copy", "symlink"])
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("path")
|
||||
.long("path")
|
||||
.about("Path to the directory containing 'libyabridge.so'")
|
||||
.long_about("Path to the directory containing 'libyabridge.so'. If this is not set, then yabridgectl will look in both '/usr/lib' and '~/.local/share/yabridge' by default.")
|
||||
.validator(validate_path)
|
||||
.takes_value(true),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
App::new("sync")
|
||||
.about("Set up or update yabridge for all plugins")
|
||||
.arg(
|
||||
Arg::with_name("prune")
|
||||
.short('p')
|
||||
.long("prune")
|
||||
.about("Remove unrelated or leftover '.so' files"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("verbose")
|
||||
.short('v')
|
||||
.long("verbose")
|
||||
.about("Print information about plugins being set up or skipped"),
|
||||
),
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
match matches.subcommand() {
|
||||
("add", Some(options)) => add_directory(&mut config, options.value_of_t_or_exit("path")),
|
||||
("rm", Some(options)) => {
|
||||
remove_directory(&mut config, &options.value_of_t_or_exit::<PathBuf>("path"))
|
||||
}
|
||||
("list", _) => list_directories(&config),
|
||||
("status", _) => show_status(&config),
|
||||
("set", Some(options)) => set_settings(&mut config, options),
|
||||
("sync", Some(options)) => do_sync(
|
||||
&config,
|
||||
options.is_present("prune"),
|
||||
options.is_present("verbose"),
|
||||
),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a direcotry to the plugin locations. Duplicates get ignord because we're using ordered sets.
|
||||
fn add_directory(config: &mut Config, path: PathBuf) {
|
||||
config.plugin_dirs.insert(path);
|
||||
if let Err(err) = config.write() {
|
||||
eprintln!("Error while writing config file: {}", err);
|
||||
exit(1);
|
||||
};
|
||||
}
|
||||
|
||||
/// Remove a direcotry to the plugin locations. The path is assumed to be part of
|
||||
/// `config.plugin_dirs`, otherwise this si silently ignored.
|
||||
fn remove_directory(config: &mut Config, path: &Path) {
|
||||
// We've already verified that this path is in `config.plugin_dirs`
|
||||
// XXS: Would it be a good idea to warn about leftover .so files?
|
||||
config.plugin_dirs.remove(path);
|
||||
if let Err(err) = config.write() {
|
||||
eprintln!("Error while writing config file: {}", err);
|
||||
exit(1);
|
||||
};
|
||||
}
|
||||
|
||||
/// List the plugin locations.
|
||||
fn list_directories(config: &Config) {
|
||||
for directory in &config.plugin_dirs {
|
||||
println!("{}", directory.display());
|
||||
}
|
||||
}
|
||||
|
||||
/// Print the current configuration and the installation status for all found plugins.
|
||||
fn show_status(config: &Config) {
|
||||
let results = match config.index_directories() {
|
||||
Ok(results) => results,
|
||||
Err(err) => {
|
||||
eprintln!("Error while searching for plugins: {}", err);
|
||||
exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
println!(
|
||||
"yabridge path: {}",
|
||||
config
|
||||
.yabridge_home
|
||||
.as_ref()
|
||||
.map(|path| format!("'{}'", path.display()))
|
||||
.unwrap_or_else(|| String::from("<auto>"))
|
||||
);
|
||||
println!(
|
||||
"libyabridge.so: {}",
|
||||
config
|
||||
.libyabridge()
|
||||
.map(|path| format!("'{}'", path.display()))
|
||||
.unwrap_or_else(|_| format!("{}", "<not found>".red()))
|
||||
);
|
||||
println!("installation method: {}", config.method);
|
||||
|
||||
for (path, search_results) in results {
|
||||
println!("\n{}:", path.display());
|
||||
|
||||
for (plugin, status) in search_results.installation_status() {
|
||||
let status_str = match status {
|
||||
Some(FoundFile::Regular(_)) => "copy".green(),
|
||||
Some(FoundFile::Symlink(_)) => "symlink".green(),
|
||||
None => "not installed".red(),
|
||||
};
|
||||
|
||||
println!(" {} :: {}", plugin.display(), status_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Change configuration settings. The actual options are defined in the clap [app](clap::App).
|
||||
fn set_settings(config: &mut Config, options: &ArgMatches) {
|
||||
match options.value_of("method") {
|
||||
Some("copy") => config.method = InstallationMethod::Copy,
|
||||
Some("symlink") => config.method = InstallationMethod::Symlink,
|
||||
Some(s) => unimplemented!("Unexpected installation method '{}'", s),
|
||||
None => (),
|
||||
}
|
||||
|
||||
match options.value_of_t("path") {
|
||||
Ok(path) => config.yabridge_home = Some(path),
|
||||
Err(clap::Error {
|
||||
kind: clap::ErrorKind::ArgumentNotFound,
|
||||
..
|
||||
}) => (),
|
||||
// I don't think we can get any parsing errors here since we already validated that the
|
||||
// argument has to be a valid path, but you never know
|
||||
Err(err) => err.exit(),
|
||||
}
|
||||
|
||||
if let Err(err) = config.write() {
|
||||
eprintln!("Error while writing config file: {}", err);
|
||||
exit(1);
|
||||
};
|
||||
}
|
||||
|
||||
/// Set up yabridge for all Windows VST2 plugins in the plugin directories. Will also remove orphan
|
||||
/// `.so` files if the prune option is set.
|
||||
fn do_sync(config: &Config, prune: bool, verbose: bool) {
|
||||
let libyabridge_path = match config.libyabridge() {
|
||||
Ok(path) => {
|
||||
println!("Using '{}'\n", path.display());
|
||||
path
|
||||
}
|
||||
Err(err) => {
|
||||
// The error messages here are already formatted
|
||||
eprintln!("{}", err);
|
||||
exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
let results = match config.index_directories() {
|
||||
Ok(results) => results,
|
||||
Err(err) => {
|
||||
eprintln!("Error while searching for plugins: {}", err);
|
||||
exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
// Keep track of some global statistics
|
||||
let mut num_installed = 0;
|
||||
let mut skipped_dll_files: Vec<PathBuf> = Vec::new();
|
||||
let mut orphan_so_files: Vec<FoundFile> = Vec::new();
|
||||
for (path, search_results) in results {
|
||||
num_installed += search_results.vst2_files.len();
|
||||
orphan_so_files.extend(search_results.orphans().into_iter().cloned());
|
||||
skipped_dll_files.extend(search_results.skipped_files);
|
||||
|
||||
if verbose {
|
||||
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
|
||||
let target_path = plugin.with_extension("so");
|
||||
if target_path.exists() {
|
||||
fs::remove_file(&target_path).unwrap_or_else(|err| {
|
||||
eprintln!("Could not remove '{}': {}", target_path.display(), err);
|
||||
exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
match config.method {
|
||||
InstallationMethod::Copy => {
|
||||
fs::copy(&libyabridge_path, &target_path).unwrap_or_else(|err| {
|
||||
eprintln!(
|
||||
"Error copying '{}' to '{}': {}",
|
||||
libyabridge_path.display(),
|
||||
target_path.display(),
|
||||
err
|
||||
);
|
||||
exit(1);
|
||||
});
|
||||
}
|
||||
InstallationMethod::Symlink => {
|
||||
symlink(&libyabridge_path, &target_path).unwrap_or_else(|err| {
|
||||
eprintln!(
|
||||
"Error symlinking '{}' to '{}': {}",
|
||||
libyabridge_path.display(),
|
||||
target_path.display(),
|
||||
err
|
||||
);
|
||||
exit(1);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if verbose {
|
||||
println!(" {}", plugin.display());
|
||||
}
|
||||
}
|
||||
if verbose {
|
||||
println!();
|
||||
}
|
||||
}
|
||||
|
||||
// We'll print the skipped files all at once to prevetn clutter
|
||||
let num_skipped_files = skipped_dll_files.len();
|
||||
if verbose && !skipped_dll_files.is_empty() {
|
||||
println!("Skipped files:");
|
||||
for path in skipped_dll_files {
|
||||
println!("- {}", path.display());
|
||||
}
|
||||
println!();
|
||||
}
|
||||
|
||||
// Always warn about leftover files sicne those might cause warnings or errors when a VST host
|
||||
// tries to load them
|
||||
if !orphan_so_files.is_empty() {
|
||||
if prune {
|
||||
println!("Removing {} leftover '.so' file(s):", orphan_so_files.len());
|
||||
} else {
|
||||
println!(
|
||||
"Found {} leftover '.so' file(s), rerun with the '--prune' option to remove them:",
|
||||
orphan_so_files.len()
|
||||
);
|
||||
}
|
||||
|
||||
for file in orphan_so_files {
|
||||
let path = file.path();
|
||||
|
||||
println!("- {}", path.display());
|
||||
if prune {
|
||||
fs::remove_file(path).unwrap_or_else(|err| {
|
||||
eprintln!("Error while trying to remove '{}': {}", path.display(), err);
|
||||
exit(1);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
println!();
|
||||
}
|
||||
|
||||
println!(
|
||||
"Finished setting up {} plugins using {}, skipped {} non-plugin '.dll' files.",
|
||||
num_installed,
|
||||
config.method.plural(),
|
||||
num_skipped_files
|
||||
)
|
||||
}
|
||||
|
||||
/// Verify that a path exists, used for validating arguments.
|
||||
fn validate_path(path: &str) -> Result<(), String> {
|
||||
let path = Path::new(path);
|
||||
|
||||
if path.exists() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(format!(
|
||||
"File or directory '{}' could not be found",
|
||||
path.display()
|
||||
))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user