From ad5a85e1de0c53fcf22439af048b8928c712a778 Mon Sep 17 00:00:00 2001 From: Wouter Bokslag Date: Thu, 26 Mar 2026 11:34:51 +0100 Subject: [PATCH] Initial release for automated binary releases for amd64 and aach64 targets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Automatically builds amd64 and aach64 target through gitlab workflow. Also allows for local cross-compilation through `cross` tool Co-authored-by: Frédéric Druppel --- .github/workflows/build.yml | 113 +++++++++++ Cross.toml | 2 + contrib/cross-compile/Dockerfile.aarch64 | 18 ++ contrib/cross-compile/setup_cross_compile.sh | 194 +++++++++++++++++++ 4 files changed, 327 insertions(+) create mode 100644 .github/workflows/build.yml create mode 100644 Cross.toml create mode 100644 contrib/cross-compile/Dockerfile.aarch64 create mode 100755 contrib/cross-compile/setup_cross_compile.sh diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..afd6c70 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,113 @@ +name: Build release binaries + +on: + push: + branches: + - main + - feature/cross_compile + workflow_dispatch: + +permissions: + contents: write + +jobs: + build: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - target: x86_64-unknown-linux-gnu + bin_path: target/x86_64-unknown-linux-gnu/release/tetra-bluestation + build_cmd: cargo build --release --target x86_64-unknown-linux-gnu -p bluestation-bs --bin tetra-bluestation + artifact_name: tetra-bluestation-x86_64 + + - target: aarch64-unknown-linux-gnu + bin_path: target/aarch64-unknown-linux-gnu/release/tetra-bluestation + build_cmd: cross build --release --target aarch64-unknown-linux-gnu -p bluestation-bs --bin tetra-bluestation + artifact_name: tetra-bluestation-aarch64-generic + + - target: aarch64-unknown-linux-gnu + bin_path: target/aarch64-unknown-linux-gnu/release/tetra-bluestation + build_cmd: cross build --release --target aarch64-unknown-linux-gnu -p bluestation-bs --bin tetra-bluestation + artifact_name: tetra-bluestation-rpi4 + cpu_rustflags: "-C target-cpu=cortex-a72" + + - target: aarch64-unknown-linux-gnu + bin_path: target/aarch64-unknown-linux-gnu/release/tetra-bluestation + build_cmd: cross build --release --target aarch64-unknown-linux-gnu -p bluestation-bs --bin tetra-bluestation + artifact_name: tetra-bluestation-rpi5 + cpu_rustflags: "-C target-cpu=cortex-a76" + + steps: + - name: Checkout + uses: actions/checkout@v4.2.2 + + - name: Install Rust toolchain + uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + target: ${{ matrix.target }} + + - name: Install cross + if: matrix.target == 'aarch64-unknown-linux-gnu' + run: cargo install cross --locked + + - name: Apply CPU-specific RUSTFLAGS + if: matrix.cpu_rustflags != '' + run: echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_RUSTFLAGS=${{ matrix.cpu_rustflags }}" >> "$GITHUB_ENV" + + - name: Install native build dependencies (amd64) + if: matrix.target == 'x86_64-unknown-linux-gnu' + run: | + sudo apt-get update + sudo apt-get install -y --no-install-recommends \ + git make g++ cmake pkg-config \ + libsoapysdr-dev \ + soapysdr-tools \ + libasound2-dev \ + clang llvm-dev libclang-dev + + echo "PKG_CONFIG_PATH=/usr/lib/x86_64-linux-gnu/pkgconfig" >> "$GITHUB_ENV" + pkg-config --modversion SoapySDR + + - name: Build + run: ${{ matrix.build_cmd }} + + - name: Rename binary + run: cp ${{ matrix.bin_path }} ${{ matrix.artifact_name }} + + - name: Upload binary + uses: actions/upload-artifact@v4.6.0 + with: + name: ${{ matrix.artifact_name }} + path: ${{ matrix.artifact_name }} + + release: + name: Publish latest release + runs-on: ubuntu-latest + needs: build + if: github.ref == 'refs/heads/main' + steps: + - name: Checkout + uses: actions/checkout@v4.2.2 + + - name: Read version from Cargo.toml + id: version + run: | + VERSION=$(grep '^version' Cargo.toml | head -1 | cut -d'"' -f2) + SHORT_SHA=$(git rev-parse --short HEAD) + echo "tag=v$VERSION-$SHORT_SHA" >> "$GITHUB_OUTPUT" + echo "name=v$VERSION-$SHORT_SHA" >> "$GITHUB_OUTPUT" + + - name: Download all binaries + uses: actions/download-artifact@v4.2.1 + with: + path: artifacts + merge-multiple: true + + - name: Publish release + uses: softprops/action-gh-release@v2.3.2 + with: + tag_name: ${{ steps.version.outputs.tag }} + name: ${{ steps.version.outputs.name }} + files: artifacts/* diff --git a/Cross.toml b/Cross.toml new file mode 100644 index 0000000..5b9ce42 --- /dev/null +++ b/Cross.toml @@ -0,0 +1,2 @@ +[target.aarch64-unknown-linux-gnu] +dockerfile = "contrib/cross-compile/Dockerfile.aarch64" diff --git a/contrib/cross-compile/Dockerfile.aarch64 b/contrib/cross-compile/Dockerfile.aarch64 new file mode 100644 index 0000000..cd268e2 --- /dev/null +++ b/contrib/cross-compile/Dockerfile.aarch64 @@ -0,0 +1,18 @@ +FROM ghcr.io/cross-rs/aarch64-unknown-linux-gnu:main + +RUN dpkg --add-architecture arm64 && \ + apt-get update && \ + apt-get install -y --no-install-recommends \ + cmake git g++ && \ + rm -rf /var/lib/apt/lists/* + +RUN git clone https://github.com/pothosware/SoapySDR.git --branch soapy-sdr-0.8.1 && \ + cd SoapySDR && \ + mkdir build && cd build && \ + cmake .. \ + -DCMAKE_INSTALL_PREFIX=/usr \ + -DCMAKE_INSTALL_LIBDIR=lib/aarch64-linux-gnu \ + -DCMAKE_TOOLCHAIN_FILE=/opt/toolchain.cmake && \ + make -j"$(nproc)" && \ + make install && \ + cd / && rm -rf SoapySDR diff --git a/contrib/cross-compile/setup_cross_compile.sh b/contrib/cross-compile/setup_cross_compile.sh new file mode 100755 index 0000000..8f9230d --- /dev/null +++ b/contrib/cross-compile/setup_cross_compile.sh @@ -0,0 +1,194 @@ +#!/usr/bin/env bash +set -euo pipefail + +# setup_cross_compile.sh +# +# Installs: +# - Docker Engine / docker.io +# - docker group membership for the invoking user +# - Rust `cross` +# - Adds cargo bin directory to $PATH +# +# Assumptions: +# - Rust + cargo + rustup are already installed +# +# Usage: +# ./setup_cross_compile.sh +# +# Notes: +# - Must not be run as root. +# - Group membership changes require a new login session. +# - Path change needs new shell. + +require_cmd() { + if ! command -v "$1" >/dev/null 2>&1; then + echo "[-] error: required command not found: $1" >&2 + exit 1 + fi +} + +ensure_not_root() { + if [[ "${EUID}" -eq 0 ]]; then + echo "[-] error: do not run this script as root" >&2 + echo " Run it as your normal user. The script will use sudo when needed." >&2 + exit 1 + fi +} + +confirm_plan() { + cat <<'EOF' +[i] This script will perform the following actions: + + 1. Install Docker via apt if it is not already installed. + 2. Enable and start the Docker service. + 3. Add your user to the docker group if needed. + 4. Install the Rust `cross` tool if it is not already installed. + 5. Ensure $HOME/.cargo/bin is added to your PATH in ~/.bashrc if needed. + + This script may call sudo for system changes. + It must be run as a normal user, not as root. + +Proceed? [y/N] +EOF + + read -r reply + case "${reply}" in + y|Y|yes|YES) + ;; + *) + echo "[i] Aborted." + exit 0 + ;; + esac +} + +need_root() { + if [[ "${EUID}" -ne 0 ]]; then + require_cmd sudo + sudo "$@" + else + "$@" + fi +} + +detect_user() { + if [[ -n "${SUDO_USER:-}" && "${SUDO_USER}" != "root" ]]; then + printf '%s\n' "${SUDO_USER}" + else + id -un + fi +} + +user_home() { + local target_user + target_user="$(detect_user)" + getent passwd "${target_user}" | cut -d: -f6 +} + +install_docker_debian() { + if command -v docker >/dev/null 2>&1; then + echo "[+] docker already installed" + return + fi + + echo "[i] installing docker" + need_root apt-get update + need_root apt-get install -y docker.io + need_root systemctl enable --now docker +} + +ensure_docker_group_membership() { + local target_user + target_user="$(detect_user)" + + if id -nG "${target_user}" | tr ' ' '\n' | grep -qx docker; then + echo "[+] user '${target_user}' is already in docker group" + return + fi + + echo "[i] adding user '${target_user}' to docker group" + need_root usermod -aG docker "${target_user}" + + cat </dev/null 2>&1; then + echo "[+] cross already installed: $(command -v cross)" + return + fi + + echo "[i] installing cross" + cargo install cross --locked +} + +ensure_cargo_bin_in_bashrc_if_needed() { + local target_user home_dir bashrc cargo_bin line + target_user="$(detect_user)" + home_dir="$(user_home)" + bashrc="${home_dir}/.bashrc" + cargo_bin="${home_dir}/.cargo/bin" + line='export PATH="$HOME/.cargo/bin:$PATH"' + + if command -v cross >/dev/null 2>&1; then + echo "[+] cross is already available in PATH: $(command -v cross)" + return + fi + + echo "[!] cross is installed but not currently available in PATH" + + mkdir -p "${cargo_bin}" + + if [[ ! -f "${bashrc}" ]]; then + touch "${bashrc}" + chown "${target_user}:${target_user}" "${bashrc}" 2>/dev/null || true + fi + + if ! grep -Fqx "${line}" "${bashrc}"; then + printf '\n%s\n' "${line}" >> "${bashrc}" + chown "${target_user}:${target_user}" "${bashrc}" 2>/dev/null || true + echo "[+] added Cargo bin directory to ${bashrc}" + else + echo "[+] Cargo bin PATH line already present in ${bashrc}" + fi + + cat <