diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..e47423c --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,5 @@ +[alias] +xtask = "run --package xtask --release --" + +[target.x86_64-pc-windows-gnu] +rustflags = ["-C", "link-args=-lstdc++ -lws2_32 -liphlpapi -lwinmm"] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f659a9c..cd9701c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,9 +1,8 @@ name: CI on: - workflow_dispatch: push: - tags: ['v*'] + branches: [main] pull_request: branches: [main] @@ -15,26 +14,20 @@ concurrency: cancel-in-progress: true jobs: - build: + check: strategy: fail-fast: false matrix: include: - os: ubuntu-latest target: x86_64-unknown-linux-gnu - artifact: cagire-linux-x86_64 - - os: macos-15-intel - target: x86_64-apple-darwin - artifact: cagire-macos-x86_64 - os: macos-14 target: aarch64-apple-darwin - artifact: cagire-macos-aarch64 - os: windows-latest target: x86_64-pc-windows-msvc - artifact: cagire-windows-x86_64 runs-on: ${{ matrix.os }} - timeout-minutes: 30 + timeout-minutes: 20 steps: - uses: actions/checkout@v4 @@ -45,6 +38,7 @@ jobs: uses: dtolnay/rust-toolchain@stable with: targets: ${{ matrix.target }} + components: clippy - name: Cache Rust dependencies uses: Swatinem/rust-cache@v2 @@ -57,13 +51,10 @@ jobs: sudo apt-get update sudo apt-get install -y build-essential cmake pkg-config libasound2-dev libclang-dev libjack-dev \ libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev libxkbcommon-dev libssl-dev libgl1-mesa-dev - cargo install cargo-bundle - name: Install dependencies (macOS) if: runner.os == 'macOS' - run: | - brew list cmake &>/dev/null || brew install cmake - cargo install cargo-bundle + run: brew list cmake &>/dev/null || brew install cmake - name: Install dependencies (Windows) if: runner.os == 'Windows' @@ -77,229 +68,8 @@ jobs: - name: Build desktop run: cargo build --release --features desktop --bin cagire-desktop --target ${{ matrix.target }} - - name: Bundle desktop app - if: runner.os != 'Windows' - run: cargo bundle --release --features desktop --bin cagire-desktop --target ${{ matrix.target }} + - name: Test + run: cargo test --target ${{ matrix.target }} - - name: Bundle CLAP plugin - run: cargo xtask bundle cagire-plugins --release --target ${{ matrix.target }} - - - name: Zip macOS app bundle - if: runner.os == 'macOS' - run: | - cd target/${{ matrix.target }}/release/bundle/osx - zip -r Cagire.app.zip Cagire.app - - - name: Upload artifact (Unix) - if: runner.os != 'Windows' - uses: actions/upload-artifact@v4 - with: - name: ${{ matrix.artifact }} - path: target/${{ matrix.target }}/release/cagire - - - name: Upload artifact (Windows) - if: runner.os == 'Windows' - uses: actions/upload-artifact@v4 - with: - name: ${{ matrix.artifact }} - path: target/${{ matrix.target }}/release/cagire.exe - - - name: Upload desktop artifact (Linux deb) - if: runner.os == 'Linux' - uses: actions/upload-artifact@v4 - with: - name: ${{ matrix.artifact }}-desktop - path: target/${{ matrix.target }}/release/bundle/deb/*.deb - - - name: Upload desktop artifact (macOS app bundle) - if: runner.os == 'macOS' - uses: actions/upload-artifact@v4 - with: - name: ${{ matrix.artifact }}-desktop - path: target/${{ matrix.target }}/release/bundle/osx/Cagire.app.zip - - - name: Upload desktop artifact (Windows exe) - if: runner.os == 'Windows' - uses: actions/upload-artifact@v4 - with: - name: ${{ matrix.artifact }}-desktop - path: target/${{ matrix.target }}/release/cagire-desktop.exe - - - name: Upload CLAP artifact - uses: actions/upload-artifact@v4 - with: - name: ${{ matrix.artifact }}-clap - path: target/bundled/cagire-plugins.clap - - - name: Upload VST3 artifact - uses: actions/upload-artifact@v4 - with: - name: ${{ matrix.artifact }}-vst3 - path: target/bundled/cagire-plugins.vst3 - - universal-macos: - needs: build - if: startsWith(github.ref, 'refs/tags/v') - runs-on: macos-14 - timeout-minutes: 10 - - steps: - - name: Download macOS artifacts - uses: actions/download-artifact@v4 - with: - pattern: cagire-macos-* - path: artifacts - - - name: Create universal CLI binary - run: | - lipo -create \ - artifacts/cagire-macos-x86_64/cagire \ - artifacts/cagire-macos-aarch64/cagire \ - -output cagire - chmod +x cagire - lipo -info cagire - - - name: Create universal app bundle - run: | - cd artifacts/cagire-macos-aarch64-desktop - unzip Cagire.app.zip - cd ../cagire-macos-x86_64-desktop - unzip Cagire.app.zip - cd ../.. - cp -R artifacts/cagire-macos-aarch64-desktop/Cagire.app Cagire.app - lipo -create \ - artifacts/cagire-macos-x86_64-desktop/Cagire.app/Contents/MacOS/cagire-desktop \ - artifacts/cagire-macos-aarch64-desktop/Cagire.app/Contents/MacOS/cagire-desktop \ - -output Cagire.app/Contents/MacOS/cagire-desktop - lipo -info Cagire.app/Contents/MacOS/cagire-desktop - zip -r Cagire.app.zip Cagire.app - - - name: Create universal CLAP plugin - run: | - mkdir -p cagire-plugins.clap/Contents/MacOS - cp artifacts/cagire-macos-aarch64-clap/cagire-plugins.clap/Contents/Info.plist \ - cagire-plugins.clap/Contents/ 2>/dev/null || true - cp artifacts/cagire-macos-aarch64-clap/cagire-plugins.clap/Contents/PkgInfo \ - cagire-plugins.clap/Contents/ 2>/dev/null || true - lipo -create \ - artifacts/cagire-macos-x86_64-clap/cagire-plugins.clap/Contents/MacOS/cagire-plugins \ - artifacts/cagire-macos-aarch64-clap/cagire-plugins.clap/Contents/MacOS/cagire-plugins \ - -output cagire-plugins.clap/Contents/MacOS/cagire-plugins - lipo -info cagire-plugins.clap/Contents/MacOS/cagire-plugins - - - name: Create universal VST3 plugin - run: | - mkdir -p cagire-plugins.vst3/Contents/MacOS - cp -R artifacts/cagire-macos-aarch64-vst3/cagire-plugins.vst3/Contents/Info.plist \ - cagire-plugins.vst3/Contents/ 2>/dev/null || true - cp artifacts/cagire-macos-aarch64-vst3/cagire-plugins.vst3/Contents/PkgInfo \ - cagire-plugins.vst3/Contents/ 2>/dev/null || true - cp -R artifacts/cagire-macos-aarch64-vst3/cagire-plugins.vst3/Contents/Resources \ - cagire-plugins.vst3/Contents/ 2>/dev/null || true - lipo -create \ - artifacts/cagire-macos-x86_64-vst3/cagire-plugins.vst3/Contents/MacOS/cagire-plugins \ - artifacts/cagire-macos-aarch64-vst3/cagire-plugins.vst3/Contents/MacOS/cagire-plugins \ - -output cagire-plugins.vst3/Contents/MacOS/cagire-plugins - lipo -info cagire-plugins.vst3/Contents/MacOS/cagire-plugins - - - name: Build .pkg installer - run: | - VERSION="${GITHUB_REF_NAME#v}" - mkdir -p pkg-root/Applications pkg-root/usr/local/bin - cp -R Cagire.app pkg-root/Applications/ - cp cagire pkg-root/usr/local/bin/ - pkgbuild --analyze --root pkg-root component.plist - plutil -replace BundleIsRelocatable -bool NO component.plist - pkgbuild --root pkg-root --identifier com.sova.cagire \ - --version "$VERSION" --install-location / \ - --component-plist component.plist \ - "Cagire-${VERSION}-universal.pkg" - - - name: Upload universal CLI - uses: actions/upload-artifact@v4 - with: - name: cagire-macos-universal - path: cagire - - - name: Upload universal app bundle - uses: actions/upload-artifact@v4 - with: - name: cagire-macos-universal-desktop - path: Cagire.app.zip - - - name: Upload universal CLAP plugin - uses: actions/upload-artifact@v4 - with: - name: cagire-macos-universal-clap - path: cagire-plugins.clap - - - name: Upload universal VST3 plugin - uses: actions/upload-artifact@v4 - with: - name: cagire-macos-universal-vst3 - path: cagire-plugins.vst3 - - - name: Upload .pkg installer - uses: actions/upload-artifact@v4 - with: - name: cagire-macos-universal-pkg - path: Cagire-*-universal.pkg - - release: - needs: [build, universal-macos] - if: startsWith(github.ref, 'refs/tags/v') - runs-on: ubuntu-latest - timeout-minutes: 10 - permissions: - contents: write - - steps: - - name: Download all artifacts - uses: actions/download-artifact@v4 - with: - path: artifacts - - - name: Prepare release files - run: | - mkdir -p release - for dir in artifacts/*/; do - name=$(basename "$dir") - if [[ "$name" == "cagire-macos-universal-pkg" ]]; then - cp "$dir"/*.pkg release/ - elif [[ "$name" == "cagire-macos-universal-desktop" ]]; then - cp "$dir/Cagire.app.zip" "release/cagire-macos-universal-desktop.app.zip" - elif [[ "$name" == "cagire-macos-universal" ]]; then - cp "$dir/cagire" "release/cagire-macos-universal" - elif [[ "$name" == "cagire-macos-universal-clap" ]]; then - cd "$dir" && zip -r "../../release/cagire-macos-universal-clap.zip" cagire-plugins.clap && cd ../.. - elif [[ "$name" == "cagire-macos-universal-vst3" ]]; then - cd "$dir" && zip -r "../../release/cagire-macos-universal-vst3.zip" cagire-plugins.vst3 && cd ../.. - elif [[ "$name" == *-clap ]]; then - base="${name%-clap}" - cd "$dir" && zip -r "../../release/${base}-clap.zip" cagire-plugins.clap && cd ../.. - elif [[ "$name" == *-vst3 ]]; then - base="${name%-vst3}" - cd "$dir" && zip -r "../../release/${base}-vst3.zip" cagire-plugins.vst3 && cd ../.. - elif [[ "$name" == *-desktop ]]; then - base="${name%-desktop}" - if ls "$dir"/*.deb 1>/dev/null 2>&1; then - cp "$dir"/*.deb "release/${base}-desktop.deb" - elif [ -f "$dir/Cagire.app.zip" ]; then - cp "$dir/Cagire.app.zip" "release/${base}-desktop.app.zip" - elif [ -f "$dir/cagire-desktop.exe" ]; then - cp "$dir/cagire-desktop.exe" "release/${base}-desktop.exe" - fi - else - if [ -f "$dir/cagire.exe" ]; then - cp "$dir/cagire.exe" "release/${name}.exe" - elif [ -f "$dir/cagire" ]; then - cp "$dir/cagire" "release/${name}" - fi - fi - done - - - name: Create Release - uses: softprops/action-gh-release@v2 - with: - files: release/* - generate_release_notes: true + - name: Clippy + run: cargo clippy --target ${{ matrix.target }} -- -D warnings diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..7915fde --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,368 @@ +name: Release + +on: + workflow_dispatch: + push: + tags: ['v*'] + +env: + CARGO_TERM_COLOR: always + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-latest + target: x86_64-unknown-linux-gnu + artifact: cagire-linux-x86_64 + - os: macos-15-intel + target: x86_64-apple-darwin + artifact: cagire-macos-x86_64 + - os: macos-14 + target: aarch64-apple-darwin + artifact: cagire-macos-aarch64 + - os: windows-latest + target: x86_64-pc-windows-msvc + artifact: cagire-windows-x86_64 + + runs-on: ${{ matrix.os }} + timeout-minutes: 30 + + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ matrix.target }} + + - name: Cache Rust dependencies + uses: Swatinem/rust-cache@v2 + with: + key: ${{ matrix.target }} + + - name: Install cargo-binstall + uses: cargo-bins/cargo-binstall@main + + - name: Install dependencies (Linux) + if: runner.os == 'Linux' + run: | + sudo apt-get update + sudo apt-get install -y build-essential cmake pkg-config libasound2-dev libclang-dev libjack-dev \ + libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev libxkbcommon-dev libssl-dev libgl1-mesa-dev + cargo binstall -y cargo-bundle + + - name: Install dependencies (macOS) + if: runner.os == 'macOS' + run: | + brew list cmake &>/dev/null || brew install cmake + cargo binstall -y cargo-bundle + + - name: Install dependencies (Windows) + if: runner.os == 'Windows' + run: | + choco install cmake --installargs 'ADD_CMAKE_TO_PATH=System' + echo "C:\Program Files\CMake\bin" >> $env:GITHUB_PATH + + - name: Build + run: cargo build --release --target ${{ matrix.target }} + + - name: Build desktop + run: cargo build --release --features desktop --bin cagire-desktop --target ${{ matrix.target }} + + - name: Bundle desktop app + if: runner.os != 'Windows' + run: cargo bundle --release --features desktop --bin cagire-desktop --target ${{ matrix.target }} + + - name: Build AppImages (Linux) + if: runner.os == 'Linux' + run: | + mkdir -p target/releases + scripts/make-appimage.sh target/${{ matrix.target }}/release/cagire x86_64 target/releases + scripts/make-appimage.sh target/${{ matrix.target }}/release/cagire-desktop x86_64 target/releases + + - name: Upload AppImage artifacts (Linux) + if: runner.os == 'Linux' + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.artifact }}-appimage + path: target/releases/*.AppImage + + - name: Bundle CLAP plugin + run: cargo xtask bundle cagire-plugins --release --target ${{ matrix.target }} + + - name: Zip macOS app bundle + if: runner.os == 'macOS' + run: | + cd target/${{ matrix.target }}/release/bundle/osx + zip -r Cagire.app.zip Cagire.app + + - name: Upload artifact (Unix) + if: runner.os != 'Windows' + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.artifact }} + path: target/${{ matrix.target }}/release/cagire + + - name: Upload artifact (Windows) + if: runner.os == 'Windows' + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.artifact }} + path: target/${{ matrix.target }}/release/cagire.exe + + - name: Upload desktop artifact (Linux deb) + if: runner.os == 'Linux' + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.artifact }}-desktop + path: target/${{ matrix.target }}/release/bundle/deb/*.deb + + - name: Upload desktop artifact (macOS app bundle) + if: runner.os == 'macOS' + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.artifact }}-desktop + path: target/${{ matrix.target }}/release/bundle/osx/Cagire.app.zip + + - name: Upload desktop artifact (Windows exe) + if: runner.os == 'Windows' + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.artifact }}-desktop + path: target/${{ matrix.target }}/release/cagire-desktop.exe + + - name: Upload CLAP artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.artifact }}-clap + path: target/bundled/cagire-plugins.clap + + - name: Upload VST3 artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.artifact }}-vst3 + path: target/bundled/cagire-plugins.vst3 + + build-cross: + runs-on: ubuntu-latest + timeout-minutes: 45 + + strategy: + fail-fast: false + matrix: + include: + - target: aarch64-unknown-linux-gnu + artifact: cagire-linux-aarch64 + + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ matrix.target }} + + - name: Cache Rust dependencies + uses: Swatinem/rust-cache@v2 + with: + key: ${{ matrix.target }} + + - name: Install cross + run: cargo install cross --git https://github.com/cross-rs/cross + + - name: Build + run: cross build --release --target ${{ matrix.target }} + + - name: Build desktop + run: cross build --release --features desktop --bin cagire-desktop --target ${{ matrix.target }} + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.artifact }} + path: target/${{ matrix.target }}/release/cagire + + - name: Upload desktop artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.artifact }}-desktop + path: target/${{ matrix.target }}/release/cagire-desktop + + universal-macos: + needs: build + runs-on: macos-14 + timeout-minutes: 10 + + steps: + - name: Download macOS artifacts + uses: actions/download-artifact@v4 + with: + pattern: cagire-macos-* + path: artifacts + + - name: Create universal CLI binary + run: | + lipo -create \ + artifacts/cagire-macos-x86_64/cagire \ + artifacts/cagire-macos-aarch64/cagire \ + -output cagire + chmod +x cagire + lipo -info cagire + + - name: Create universal app bundle + run: | + cd artifacts/cagire-macos-aarch64-desktop + unzip Cagire.app.zip + cd ../cagire-macos-x86_64-desktop + unzip Cagire.app.zip + cd ../.. + cp -R artifacts/cagire-macos-aarch64-desktop/Cagire.app Cagire.app + lipo -create \ + artifacts/cagire-macos-x86_64-desktop/Cagire.app/Contents/MacOS/cagire-desktop \ + artifacts/cagire-macos-aarch64-desktop/Cagire.app/Contents/MacOS/cagire-desktop \ + -output Cagire.app/Contents/MacOS/cagire-desktop + lipo -info Cagire.app/Contents/MacOS/cagire-desktop + zip -r Cagire.app.zip Cagire.app + + - name: Create universal CLAP plugin + run: | + mkdir -p cagire-plugins.clap/Contents/MacOS + cp artifacts/cagire-macos-aarch64-clap/cagire-plugins.clap/Contents/Info.plist \ + cagire-plugins.clap/Contents/ 2>/dev/null || true + cp artifacts/cagire-macos-aarch64-clap/cagire-plugins.clap/Contents/PkgInfo \ + cagire-plugins.clap/Contents/ 2>/dev/null || true + lipo -create \ + artifacts/cagire-macos-x86_64-clap/cagire-plugins.clap/Contents/MacOS/cagire-plugins \ + artifacts/cagire-macos-aarch64-clap/cagire-plugins.clap/Contents/MacOS/cagire-plugins \ + -output cagire-plugins.clap/Contents/MacOS/cagire-plugins + lipo -info cagire-plugins.clap/Contents/MacOS/cagire-plugins + + - name: Create universal VST3 plugin + run: | + mkdir -p cagire-plugins.vst3/Contents/MacOS + cp -R artifacts/cagire-macos-aarch64-vst3/cagire-plugins.vst3/Contents/Info.plist \ + cagire-plugins.vst3/Contents/ 2>/dev/null || true + cp artifacts/cagire-macos-aarch64-vst3/cagire-plugins.vst3/Contents/PkgInfo \ + cagire-plugins.vst3/Contents/ 2>/dev/null || true + cp -R artifacts/cagire-macos-aarch64-vst3/cagire-plugins.vst3/Contents/Resources \ + cagire-plugins.vst3/Contents/ 2>/dev/null || true + lipo -create \ + artifacts/cagire-macos-x86_64-vst3/cagire-plugins.vst3/Contents/MacOS/cagire-plugins \ + artifacts/cagire-macos-aarch64-vst3/cagire-plugins.vst3/Contents/MacOS/cagire-plugins \ + -output cagire-plugins.vst3/Contents/MacOS/cagire-plugins + lipo -info cagire-plugins.vst3/Contents/MacOS/cagire-plugins + + - name: Build .pkg installer + run: | + VERSION="${GITHUB_REF_NAME#v}" + mkdir -p pkg-root/Applications pkg-root/usr/local/bin + cp -R Cagire.app pkg-root/Applications/ + cp cagire pkg-root/usr/local/bin/ + pkgbuild --analyze --root pkg-root component.plist + plutil -replace BundleIsRelocatable -bool NO component.plist + pkgbuild --root pkg-root --identifier com.sova.cagire \ + --version "$VERSION" --install-location / \ + --component-plist component.plist \ + "Cagire-${VERSION}-universal.pkg" + + - name: Upload universal CLI + uses: actions/upload-artifact@v4 + with: + name: cagire-macos-universal + path: cagire + + - name: Upload universal app bundle + uses: actions/upload-artifact@v4 + with: + name: cagire-macos-universal-desktop + path: Cagire.app.zip + + - name: Upload universal CLAP plugin + uses: actions/upload-artifact@v4 + with: + name: cagire-macos-universal-clap + path: cagire-plugins.clap + + - name: Upload universal VST3 plugin + uses: actions/upload-artifact@v4 + with: + name: cagire-macos-universal-vst3 + path: cagire-plugins.vst3 + + - name: Upload .pkg installer + uses: actions/upload-artifact@v4 + with: + name: cagire-macos-universal-pkg + path: Cagire-*-universal.pkg + + release: + needs: [build, build-cross, universal-macos] + if: startsWith(github.ref, 'refs/tags/v') + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + contents: write + + steps: + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + path: artifacts + + - name: Prepare release files + run: | + mkdir -p release + for dir in artifacts/*/; do + name=$(basename "$dir") + if [[ "$name" == "cagire-macos-universal-pkg" ]]; then + cp "$dir"/*.pkg release/ + elif [[ "$name" == "cagire-macos-universal-desktop" ]]; then + cp "$dir/Cagire.app.zip" "release/cagire-macos-universal-desktop.app.zip" + elif [[ "$name" == "cagire-macos-universal" ]]; then + cp "$dir/cagire" "release/cagire-macos-universal" + elif [[ "$name" == "cagire-macos-universal-clap" ]]; then + cd "$dir" && zip -r "../../release/cagire-macos-universal-clap.zip" cagire-plugins.clap && cd ../.. + elif [[ "$name" == "cagire-macos-universal-vst3" ]]; then + cd "$dir" && zip -r "../../release/cagire-macos-universal-vst3.zip" cagire-plugins.vst3 && cd ../.. + elif [[ "$name" == *-clap ]]; then + base="${name%-clap}" + cd "$dir" && zip -r "../../release/${base}-clap.zip" cagire-plugins.clap && cd ../.. + elif [[ "$name" == *-vst3 ]]; then + base="${name%-vst3}" + cd "$dir" && zip -r "../../release/${base}-vst3.zip" cagire-plugins.vst3 && cd ../.. + elif [[ "$name" == *-appimage ]]; then + cp "$dir"/*.AppImage release/ + elif [[ "$name" == *-desktop ]]; then + base="${name%-desktop}" + if ls "$dir"/*.deb 1>/dev/null 2>&1; then + cp "$dir"/*.deb "release/${base}-desktop.deb" + elif [ -f "$dir/Cagire.app.zip" ]; then + cp "$dir/Cagire.app.zip" "release/${base}-desktop.app.zip" + elif [ -f "$dir/cagire-desktop.exe" ]; then + cp "$dir/cagire-desktop.exe" "release/${base}-desktop.exe" + fi + else + if [ -f "$dir/cagire.exe" ]; then + cp "$dir/cagire.exe" "release/${name}.exe" + elif [ -f "$dir/cagire" ]; then + cp "$dir/cagire" "release/${name}" + fi + fi + done + + - name: Create Release + uses: softprops/action-gh-release@v2 + with: + files: release/* + generate_release_notes: true diff --git a/.gitignore b/.gitignore index 80bdebb..ddc5aa4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,11 @@ /target +/.cache Cargo.lock *.prof .DS_Store -# Cargo config -.cargo/config.toml +# Local cargo overrides (doux path patch) +.cargo/config.local.toml # Claude .claude/ diff --git a/BUILDING.md b/BUILDING.md index 5bfd0ea..5b2a3b2 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -1,5 +1,15 @@ # Building Cagire +## Quick Start + +```bash +git clone --recursive https://github.com/Bubobubobubobubo/cagire +cd cagire +cargo build --release +``` + +The `doux` audio engine is fetched automatically from git. No local path setup needed. + ## Prerequisites **Rust** (stable toolchain): https://rustup.rs @@ -68,6 +78,14 @@ Desktop (egui window): cargo build --release --features desktop --bin cagire-desktop ``` +Plugins (CLAP/VST3): + +```bash +cargo xtask bundle cagire-plugins --release +``` + +The xtask alias is defined in `.cargo/config.toml` (committed). Plugin bundles are output to `target/bundled/`. + ## Run Terminal (default): @@ -89,3 +107,81 @@ cargo run --release --features desktop --bin cagire-desktop | `-i, --input ` | Input audio device | | `-c, --channels ` | Output channel count | | `-b, --buffer ` | Audio buffer size | + +## Cross-Compilation + +[cross](https://github.com/cross-rs/cross) uses Docker to build for other platforms without installing their toolchains locally. It works on any OS that runs Docker. + +### Targets + +| Target | Method | Binaries | +|--------|--------|----------| +| aarch64-apple-darwin | Native (macOS ARM only) | `cagire`, `cagire-desktop` | +| x86_64-apple-darwin | Native (macOS only) | `cagire`, `cagire-desktop` | +| x86_64-unknown-linux-gnu | `cross build` | `cagire`, `cagire-desktop` | +| aarch64-unknown-linux-gnu (RPi 64-bit) | `cross build` | `cagire`, `cagire-desktop` | +| x86_64-pc-windows-gnu | `cross build` | `cagire`, `cagire-desktop` | + +macOS targets can only be built on macOS — Apple does not support cross-compilation to macOS from other platforms. Linux and Windows targets can be cross-compiled from any OS. The aarch64-unknown-linux-gnu target covers Raspberry Pi (64-bit OS). + +### Windows ABI + +CI produces `x86_64-pc-windows-msvc` binaries (native Windows build, better compatibility). Local cross-compilation from non-Windows hosts produces `x86_64-pc-windows-gnu` binaries (MinGW via Docker). Both work; MSVC is preferred for releases. + +### Prerequisites + +1. **Docker**: https://docs.docker.com/get-docker/ +2. **cross**: `cargo install cross --git https://github.com/cross-rs/cross` +3. On macOS, add the Intel target: `rustup target add x86_64-apple-darwin` + +Docker must be running before invoking `cross` or `scripts/build-all.sh`. + +### Building Individual Targets + +```bash +# Linux x86_64 +cross build --release --target x86_64-unknown-linux-gnu +cross build --release --features desktop --bin cagire-desktop --target x86_64-unknown-linux-gnu + +# Linux aarch64 +cross build --release --target aarch64-unknown-linux-gnu +cross build --release --features desktop --bin cagire-desktop --target aarch64-unknown-linux-gnu + +# Windows x86_64 +cross build --release --target x86_64-pc-windows-gnu +cross build --release --features desktop --bin cagire-desktop --target x86_64-pc-windows-gnu +``` + +### Building All Targets (macOS only) + +```bash +# Interactive (prompts for platform/target selection): +scripts/build-all.sh + +# Non-interactive: +scripts/build-all.sh --platforms macos-arm64,linux-x86_64 --targets cli,desktop --yes +scripts/build-all.sh --all --yes +``` + +Builds selected targets, producing binaries in `target/releases/`. + +Platform aliases: `macos-arm64`, `macos-x86_64`, `linux-x86_64`, `linux-aarch64`, `windows-x86_64`. +Target aliases: `cli`, `desktop`, `plugins`. + +### Linux AppImage Packaging + +Linux releases ship as AppImages — self-contained executables that bundle all shared library dependencies (ALSA, JACK, X11, OpenGL). No runtime dependencies required. + +After building a Linux target, produce an AppImage with: + +```bash +scripts/make-appimage.sh target/x86_64-unknown-linux-gnu/release/cagire x86_64 target/releases +``` + +`scripts/build-all.sh` does this automatically for every Linux target selected. The CI pipeline produces AppImages for the x86_64 Linux build. Cross-arch AppImage building (e.g. aarch64 on x86_64) is not supported — run on a matching host or in CI. + +### Notes + +- Custom Dockerfiles in `cross/` install the native libraries Cagire depends on (ALSA, JACK, X11, cmake, libclang, etc.). `Cross.toml` maps each target to its Dockerfile. +- The first build per target downloads Docker base images and installs packages. Subsequent builds use cached layers. +- Cross-architecture Docker builds (e.g. aarch64 on x86_64 or vice versa) run under QEMU emulation and are significantly slower. diff --git a/Cargo.toml b/Cargo.toml index aaff0b3..35ca3d0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,7 +51,7 @@ cagire-forth = { path = "crates/forth" } cagire-markdown = { path = "crates/markdown" } cagire-project = { path = "crates/project" } cagire-ratatui = { path = "crates/ratatui" } -doux = { path = "/Users/bubo/doux", features = ["native", "soundfont"] } +doux = { git = "https://github.com/sova-org/doux", features = ["native", "soundfont"] } rusty_link = "0.4" ratatui = "0.30" crossterm = "0.29" diff --git a/Cross.toml b/Cross.toml index bb0fdf4..c1c90f4 100644 --- a/Cross.toml +++ b/Cross.toml @@ -1,5 +1,8 @@ -[build] -volumes = ["/Users/bubo/doux:/Users/bubo/doux"] - [target.aarch64-unknown-linux-gnu] dockerfile = "./cross/aarch64-linux.Dockerfile" + +[target.x86_64-unknown-linux-gnu] +dockerfile = "./cross/x86_64-linux.Dockerfile" + +[target.x86_64-pc-windows-gnu] +dockerfile = "./cross/x86_64-windows.Dockerfile" diff --git a/assets/Cagire.png b/assets/Cagire.png index f7a4b23..092177d 100644 Binary files a/assets/Cagire.png and b/assets/Cagire.png differ diff --git a/assets/cagire.desktop b/assets/cagire.desktop new file mode 100644 index 0000000..7988fb3 --- /dev/null +++ b/assets/cagire.desktop @@ -0,0 +1,7 @@ +[Desktop Entry] +Type=Application +Name=Cagire +Comment=Forth-based music sequencer +Exec=cagire +Icon=cagire +Categories=Audio;Music;AudioVideo; diff --git a/build.rs b/build.rs index 40d4ac1..ec85c8e 100644 --- a/build.rs +++ b/build.rs @@ -1,6 +1,15 @@ //! Build script — embeds Windows application resources (icon, metadata). fn main() { + let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap_or_default(); + + if target_os == "windows" { + // rusty_link's build.rs only links stdc++ on linux — add it for windows too + println!("cargo:rustc-link-lib=stdc++"); + println!("cargo:rustc-link-lib=ws2_32"); + println!("cargo:rustc-link-lib=iphlpapi"); + } + #[cfg(windows)] { let mut res = winres::WindowsResource::new(); diff --git a/crates/ratatui/Cargo.toml b/crates/ratatui/Cargo.toml index b5d20d1..3d1cf4e 100644 --- a/crates/ratatui/Cargo.toml +++ b/crates/ratatui/Cargo.toml @@ -11,4 +11,4 @@ description = "TUI components for cagire sequencer" rand = "0.8" ratatui = "0.30" regex = "1" -tui-textarea = { git = "https://github.com/phsym/tui-textarea", branch = "main", features = ["search"] } +tui-textarea = { git = "https://github.com/phsym/tui-textarea", rev = "e2ec4d3", features = ["search"] } diff --git a/cross/x86_64-linux.Dockerfile b/cross/x86_64-linux.Dockerfile new file mode 100644 index 0000000..45847cd --- /dev/null +++ b/cross/x86_64-linux.Dockerfile @@ -0,0 +1,14 @@ +FROM ghcr.io/cross-rs/x86_64-unknown-linux-gnu:main + +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + cmake \ + libclang-dev \ + libasound2-dev \ + libjack-dev \ + libxcb-render0-dev \ + libxcb-shape0-dev \ + libxcb-xfixes0-dev \ + libxkbcommon-dev \ + libgl1-mesa-dev \ + && rm -rf /var/lib/apt/lists/* diff --git a/cross/x86_64-windows.Dockerfile b/cross/x86_64-windows.Dockerfile new file mode 100644 index 0000000..9af0293 --- /dev/null +++ b/cross/x86_64-windows.Dockerfile @@ -0,0 +1,13 @@ +FROM ghcr.io/cross-rs/x86_64-pc-windows-gnu:main + +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + cmake \ + clang \ + libclang-dev \ + mingw-w64-tools \ + mingw-w64-x86-64-dev \ + && rm -rf /var/lib/apt/lists/* \ + && ln -sf windows.h /usr/x86_64-w64-mingw32/include/Windows.h \ + && ln -sf winsock2.h /usr/x86_64-w64-mingw32/include/WinSock2.h \ + && ln -sf ws2tcpip.h /usr/x86_64-w64-mingw32/include/WS2tcpip.h diff --git a/plugins/cagire-plugins/Cargo.toml b/plugins/cagire-plugins/Cargo.toml index bd4f408..20b3953 100644 --- a/plugins/cagire-plugins/Cargo.toml +++ b/plugins/cagire-plugins/Cargo.toml @@ -14,7 +14,7 @@ cagire = { path = "../..", default-features = false, features = ["block-renderer cagire-forth = { path = "../../crates/forth" } cagire-project = { path = "../../crates/project" } cagire-ratatui = { path = "../../crates/ratatui" } -doux = { path = "/Users/bubo/doux", features = ["native", "soundfont"] } +doux = { git = "https://github.com/sova-org/doux", features = ["native", "soundfont"] } nih_plug = { git = "https://github.com/robbert-vdh/nih-plug", features = ["standalone"] } nih_plug_egui = { git = "https://github.com/robbert-vdh/nih-plug" } egui_ratatui = "2.1" diff --git a/scripts/build-all.sh b/scripts/build-all.sh new file mode 100755 index 0000000..9c3f974 --- /dev/null +++ b/scripts/build-all.sh @@ -0,0 +1,428 @@ +#!/usr/bin/env bash +set -euo pipefail + +cd "$(git rev-parse --show-toplevel)" + +PLUGIN_NAME="cagire-plugins" +LIB_NAME="cagire_plugins" # cargo converts hyphens to underscores +OUT="target/releases" + +PLATFORMS=( + "aarch64-apple-darwin" + "x86_64-apple-darwin" + "x86_64-unknown-linux-gnu" + "aarch64-unknown-linux-gnu" + "x86_64-pc-windows-gnu" +) + +PLATFORM_LABELS=( + "macOS aarch64 (native)" + "macOS x86_64 (native)" + "Linux x86_64 (cross)" + "Linux aarch64 (cross)" + "Windows x86_64 (cross)" +) + +PLATFORM_ALIASES=( + "macos-arm64" + "macos-x86_64" + "linux-x86_64" + "linux-aarch64" + "windows-x86_64" +) + +# --- CLI argument parsing --- + +cli_platforms="" +cli_targets="" +cli_yes=false +cli_all=false + +while [[ $# -gt 0 ]]; do + case "$1" in + --platforms) cli_platforms="$2"; shift 2 ;; + --targets) cli_targets="$2"; shift 2 ;; + --yes) cli_yes=true; shift ;; + --all) cli_all=true; shift ;; + -h|--help) + echo "Usage: $0 [OPTIONS]" + echo "" + echo "Options:" + echo " --platforms Comma-separated: macos-arm64,macos-x86_64,linux-x86_64,linux-aarch64,windows-x86_64" + echo " --targets Comma-separated: cli,desktop,plugins" + echo " --all Build all platforms and targets" + echo " --yes Skip confirmation prompt" + echo "" + echo "Without options, runs interactively." + exit 0 + ;; + *) echo "Unknown option: $1"; exit 1 ;; + esac +done + +resolve_platform_alias() { + local alias="$1" + for i in "${!PLATFORM_ALIASES[@]}"; do + if [[ "${PLATFORM_ALIASES[$i]}" == "$alias" ]]; then + echo "$i" + return + fi + done + echo "Unknown platform: $alias" >&2 + exit 1 +} + +# --- Helpers --- + +prompt_platforms() { + echo "Select platform (0=all, comma-separated):" + echo " 0) All" + for i in "${!PLATFORMS[@]}"; do + echo " $((i+1))) ${PLATFORM_LABELS[$i]}" + done + read -rp "> " choice + + if [[ "$choice" == "0" || -z "$choice" ]]; then + selected_platforms=("${PLATFORMS[@]}") + selected_labels=("${PLATFORM_LABELS[@]}") + else + IFS=',' read -ra indices <<< "$choice" + selected_platforms=() + selected_labels=() + for idx in "${indices[@]}"; do + idx="${idx// /}" + idx=$((idx - 1)) + if (( idx < 0 || idx >= ${#PLATFORMS[@]} )); then + echo "Invalid platform index: $((idx+1))" + exit 1 + fi + selected_platforms+=("${PLATFORMS[$idx]}") + selected_labels+=("${PLATFORM_LABELS[$idx]}") + done + fi +} + +prompt_targets() { + echo "" + echo "Select targets (0=all, comma-separated):" + echo " 0) All" + echo " 1) cagire" + echo " 2) cagire-desktop" + echo " 3) cagire-plugins (CLAP/VST3)" + read -rp "> " choice + + build_cagire=false + build_desktop=false + build_plugins=false + + if [[ "$choice" == "0" || -z "$choice" ]]; then + build_cagire=true + build_desktop=true + build_plugins=true + else + IFS=',' read -ra targets <<< "$choice" + for t in "${targets[@]}"; do + t="${t// /}" + case "$t" in + 1) build_cagire=true ;; + 2) build_desktop=true ;; + 3) build_plugins=true ;; + *) echo "Invalid target: $t"; exit 1 ;; + esac + done + fi +} + +confirm_summary() { + echo "" + echo "=== Build Summary ===" + echo "" + echo "Platforms:" + for label in "${selected_labels[@]}"; do + echo " - $label" + done + echo "" + echo "Targets:" + $build_cagire && echo " - cagire" + $build_desktop && echo " - cagire-desktop" + $build_plugins && echo " - cagire-plugins (CLAP/VST3)" + echo "" + read -rp "Proceed? [Y/n] " yn + case "${yn,,}" in + n|no) echo "Aborted."; exit 0 ;; + esac +} + +platform_os() { + case "$1" in + *windows*) echo "windows" ;; + *linux*) echo "linux" ;; + *apple*) echo "macos" ;; + esac +} + +platform_arch() { + case "$1" in + aarch64*) echo "aarch64" ;; + x86_64*) echo "x86_64" ;; + esac +} + +platform_suffix() { + case "$1" in + *windows*) echo ".exe" ;; + *) echo "" ;; + esac +} + +is_cross_target() { + case "$1" in + *linux*|*windows*) return 0 ;; + *) return 1 ;; + esac +} + +native_target() { + [[ "$1" == "aarch64-apple-darwin" ]] +} + +release_dir() { + if native_target "$1"; then + echo "target/release" + else + echo "target/$1/release" + fi +} + +target_flag() { + if native_target "$1"; then + echo "" + else + echo "--target $1" + fi +} + +builder_for() { + if is_cross_target "$1"; then + echo "cross" + else + echo "cargo" + fi +} + +build_binary() { + local platform="$1" + shift + local builder + builder=$(builder_for "$platform") + local tf + tf=$(target_flag "$platform") + # shellcheck disable=SC2086 + $builder build --release $tf "$@" +} + +bundle_plugins_native() { + local platform="$1" + local tf + tf=$(target_flag "$platform") + # shellcheck disable=SC2086 + cargo xtask bundle "$PLUGIN_NAME" --release $tf +} + +bundle_plugins_cross() { + local platform="$1" + local rd + rd=$(release_dir "$platform") + local os + os=$(platform_os "$platform") + local arch + arch=$(platform_arch "$platform") + + # Build the cdylib with cross + # shellcheck disable=SC2046 + build_binary "$platform" -p "$PLUGIN_NAME" + + # Determine source library file + local src_lib + case "$os" in + linux) src_lib="$rd/lib${LIB_NAME}.so" ;; + windows) src_lib="$rd/${LIB_NAME}.dll" ;; + esac + + if [[ ! -f "$src_lib" ]]; then + echo " ERROR: Expected library not found: $src_lib" + return 1 + fi + + # Assemble CLAP bundle (flat file) + local clap_out="$OUT/${PLUGIN_NAME}-${os}-${arch}.clap" + cp "$src_lib" "$clap_out" + echo " CLAP -> $clap_out" + + # Assemble VST3 bundle (directory tree) + local vst3_dir="$OUT/${PLUGIN_NAME}-${os}-${arch}.vst3" + local vst3_contents + case "$os" in + linux) + vst3_contents="$vst3_dir/Contents/${arch}-linux" + mkdir -p "$vst3_contents" + cp "$src_lib" "$vst3_contents/${PLUGIN_NAME}.so" + ;; + windows) + vst3_contents="$vst3_dir/Contents/${arch}-win" + mkdir -p "$vst3_contents" + cp "$src_lib" "$vst3_contents/${PLUGIN_NAME}.vst3" + ;; + esac + echo " VST3 -> $vst3_dir/" +} + +copy_artifacts() { + local platform="$1" + local rd + rd=$(release_dir "$platform") + local os + os=$(platform_os "$platform") + local arch + arch=$(platform_arch "$platform") + local suffix + suffix=$(platform_suffix "$platform") + + if $build_cagire; then + local src="$rd/cagire${suffix}" + local dst="$OUT/cagire-${os}-${arch}${suffix}" + cp "$src" "$dst" + echo " cagire -> $dst" + fi + + if $build_desktop; then + local src="$rd/cagire-desktop${suffix}" + local dst="$OUT/cagire-desktop-${os}-${arch}${suffix}" + cp "$src" "$dst" + echo " cagire-desktop -> $dst" + fi + + # AppImage for Linux targets + if [[ "$os" == "linux" ]]; then + if $build_cagire; then + scripts/make-appimage.sh "$rd/cagire" "$arch" "$OUT" + fi + if $build_desktop; then + scripts/make-appimage.sh "$rd/cagire-desktop" "$arch" "$OUT" + fi + fi + + # Plugin artifacts for native targets (cross handled in bundle_plugins_cross) + if $build_plugins && ! is_cross_target "$platform"; then + local bundle_dir="$rd/bundle" + + # CLAP + local clap_src="$bundle_dir/${PLUGIN_NAME}.clap" + if [[ -e "$clap_src" ]]; then + local clap_dst="$OUT/${PLUGIN_NAME}-${os}-${arch}.clap" + cp -r "$clap_src" "$clap_dst" + echo " CLAP -> $clap_dst" + fi + + # VST3 + local vst3_src="$bundle_dir/${PLUGIN_NAME}.vst3" + if [[ -d "$vst3_src" ]]; then + local vst3_dst="$OUT/${PLUGIN_NAME}-${os}-${arch}.vst3" + rm -rf "$vst3_dst" + cp -r "$vst3_src" "$vst3_dst" + echo " VST3 -> $vst3_dst/" + fi + fi +} + +# --- Main --- + +if $cli_all; then + selected_platforms=("${PLATFORMS[@]}") + selected_labels=("${PLATFORM_LABELS[@]}") + build_cagire=true + build_desktop=true + build_plugins=true +elif [[ -n "$cli_platforms" || -n "$cli_targets" ]]; then + # Resolve platforms from CLI + if [[ -n "$cli_platforms" ]]; then + selected_platforms=() + selected_labels=() + IFS=',' read -ra aliases <<< "$cli_platforms" + for alias in "${aliases[@]}"; do + alias="${alias// /}" + idx=$(resolve_platform_alias "$alias") + selected_platforms+=("${PLATFORMS[$idx]}") + selected_labels+=("${PLATFORM_LABELS[$idx]}") + done + else + selected_platforms=("${PLATFORMS[@]}") + selected_labels=("${PLATFORM_LABELS[@]}") + fi + + # Resolve targets from CLI + build_cagire=false + build_desktop=false + build_plugins=false + if [[ -n "$cli_targets" ]]; then + IFS=',' read -ra tgts <<< "$cli_targets" + for t in "${tgts[@]}"; do + t="${t// /}" + case "$t" in + cli) build_cagire=true ;; + desktop) build_desktop=true ;; + plugins) build_plugins=true ;; + *) echo "Unknown target: $t (expected: cli, desktop, plugins)"; exit 1 ;; + esac + done + else + build_cagire=true + build_desktop=true + build_plugins=true + fi +else + prompt_platforms + prompt_targets +fi + +if ! $cli_yes && [[ -z "$cli_platforms" ]] && ! $cli_all; then + confirm_summary +fi + +mkdir -p "$OUT" + +step=0 +total=${#selected_platforms[@]} + +for platform in "${selected_platforms[@]}"; do + step=$((step + 1)) + echo "" + echo "=== [$step/$total] $platform ===" + + if $build_cagire; then + echo " -> cagire" + build_binary "$platform" + fi + + if $build_desktop; then + echo " -> cagire-desktop" + build_binary "$platform" --features desktop --bin cagire-desktop + fi + + if $build_plugins; then + echo " -> cagire-plugins" + if is_cross_target "$platform"; then + bundle_plugins_cross "$platform" + else + bundle_plugins_native "$platform" + fi + fi + + echo " Copying artifacts..." + copy_artifacts "$platform" +done + +echo "" +echo "=== Done ===" +echo "" +ls -lhR "$OUT/" diff --git a/scripts/make-appimage.sh b/scripts/make-appimage.sh new file mode 100755 index 0000000..e496e0b --- /dev/null +++ b/scripts/make-appimage.sh @@ -0,0 +1,136 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Usage: scripts/make-appimage.sh +# Produces an AppImage from a Linux binary using linuxdeploy. + +if [[ $# -ne 3 ]]; then + echo "Usage: $0 " + exit 1 +fi + +BINARY="$1" +ARCH="$2" +OUTDIR="$3" + +REPO_ROOT="$(git rev-parse --show-toplevel)" +CACHE_DIR="$REPO_ROOT/.cache" +APP_NAME="$(basename "$BINARY")" +APPDIR="$(mktemp -d)/AppDir" + +LINUXDEPLOY_URL="https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-${ARCH}.AppImage" +LINUXDEPLOY="$CACHE_DIR/linuxdeploy-${ARCH}.AppImage" +RUNTIME_URL="https://github.com/AppImage/type2-runtime/releases/download/continuous/runtime-${ARCH}" +RUNTIME="$CACHE_DIR/runtime-${ARCH}" + +# Map arch to linuxdeploy's expected values +case "$ARCH" in + x86_64) export LDAI_ARCH="x86_64" ;; + aarch64) export LDAI_ARCH="aarch64" ;; + *) echo "Unsupported arch: $ARCH"; exit 1 ;; +esac + +download_tools() { + mkdir -p "$CACHE_DIR" + if [[ ! -f "$LINUXDEPLOY" ]]; then + echo " Downloading linuxdeploy for $ARCH..." + curl -fSL "$LINUXDEPLOY_URL" -o "$LINUXDEPLOY" + chmod +x "$LINUXDEPLOY" + fi + if [[ ! -f "$RUNTIME" ]]; then + echo " Downloading AppImage runtime for $ARCH..." + curl -fSL "$RUNTIME_URL" -o "$RUNTIME" + fi +} + +build_appdir() { + mkdir -p "$APPDIR/usr/bin" + cp "$BINARY" "$APPDIR/usr/bin/cagire" + chmod +x "$APPDIR/usr/bin/cagire" + + mkdir -p "$APPDIR/usr/share/icons/hicolor/512x512/apps" + cp "$REPO_ROOT/assets/Cagire.png" "$APPDIR/usr/share/icons/hicolor/512x512/apps/cagire.png" + + cp "$REPO_ROOT/assets/cagire.desktop" "$APPDIR/cagire.desktop" +} + +run_linuxdeploy_native() { + export ARCH="$LDAI_ARCH" + export LDAI_RUNTIME_FILE="$RUNTIME" + "$LINUXDEPLOY" \ + --appimage-extract-and-run \ + --appdir "$APPDIR" \ + --desktop-file "$APPDIR/cagire.desktop" \ + --icon-file "$APPDIR/usr/share/icons/hicolor/512x512/apps/cagire.png" \ + --output appimage +} + +run_linuxdeploy_docker() { + local platform + case "$ARCH" in + x86_64) platform="linux/amd64" ;; + aarch64) platform="linux/arm64" ;; + esac + + local image_tag="cagire-appimage-${ARCH}" + + echo " Building Docker image $image_tag ($platform)..." + docker build --platform "$platform" -q -t "$image_tag" - <<'DOCKERFILE' +FROM ubuntu:22.04 +RUN apt-get update && apt-get install -y --no-install-recommends \ + file \ + libasound2 \ + libjack0 \ + libxcb-render0 \ + libxcb-shape0 \ + libxcb-xfixes0 \ + libxkbcommon0 \ + libgl1 \ + && rm -rf /var/lib/apt/lists/* +DOCKERFILE + + echo " Running linuxdeploy inside Docker ($image_tag)..." + docker run --rm --platform "$platform" \ + -v "$REPO_ROOT:/project" \ + -v "$APPDIR:/appdir" \ + -v "$CACHE_DIR:/cache" \ + -e ARCH="$LDAI_ARCH" \ + -e LDAI_RUNTIME_FILE="/cache/runtime-${ARCH}" \ + -w /project \ + "$image_tag" \ + bash -c " + chmod +x /cache/linuxdeploy-${ARCH}.AppImage && \ + /cache/linuxdeploy-${ARCH}.AppImage \ + --appimage-extract-and-run \ + --appdir /appdir \ + --desktop-file /appdir/cagire.desktop \ + --icon-file /appdir/usr/share/icons/hicolor/512x512/apps/cagire.png \ + --output appimage + " +} + +HOST_ARCH="$(uname -m)" + +download_tools +build_appdir + +echo " Building AppImage for cagire ($ARCH)..." + +if [[ "$HOST_ARCH" == "$ARCH" ]] && [[ "$(uname -s)" == "Linux" ]]; then + run_linuxdeploy_native +else + run_linuxdeploy_docker +fi + +mkdir -p "$OUTDIR" + +# linuxdeploy outputs to cwd; find and move the AppImage +APPIMAGE=$(ls -1t ./*.AppImage 2>/dev/null | head -1 || true) +if [[ -z "$APPIMAGE" ]]; then + echo " ERROR: No AppImage produced" + exit 1 +fi + +FINAL_NAME="${APP_NAME}-linux-${ARCH}.AppImage" +mv "$APPIMAGE" "$OUTDIR/$FINAL_NAME" +echo " AppImage -> $OUTDIR/$FINAL_NAME"