|
- #!/usr/bin/env bash
- set -Eeuo pipefail
-
- # Orange Pi Plus 2E / Armbian Bookworm build helper for fm-rds-tx
- #
- # Goals:
- # - install build + runtime dependencies
- # - install libiio / Pluto-related userspace bits
- # - build fmrtx for Linux ARM
- # - collect binary + shared libraries into dist/orangepi/
- #
- # Notes:
- # - Linux Pluto build is libiio-first (`-tags pluto`).
- # - SoapySDR is optional fallback/debug tooling, not the primary Pluto path.
- # - Windows Pluto path remains separate and untouched by this script.
- #
- # Usage:
- # chmod +x scripts/orangepi-build-libiio.sh
- # ./scripts/orangepi-build-libiio.sh
- #
- # Optional env:
- # PREFIX=/opt/fm-rds-tx
- # DIST_DIR=dist/orangepi
- # GO_VERSION=1.22.12
- # SKIP_APT=1
- # SKIP_GO_INSTALL=1
- # BUILD_TAGS=pluto
- #
- # If you want to install the packaged result into a target directory:
- # PREFIX=/opt/fm-rds-tx ./scripts/orangepi-build-libiio.sh
-
- SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
- REPO_DIR="$(cd -- "${SCRIPT_DIR}/.." && pwd)"
- DIST_DIR="${DIST_DIR:-${REPO_DIR}/dist/orangepi}"
- BUILD_DIR="${DIST_DIR}/build"
- RUNTIME_DIR="${DIST_DIR}/runtime"
- PREFIX="${PREFIX:-/opt/fm-rds-tx}"
- GO_VERSION="${GO_VERSION:-1.22.12}"
- BUILD_TAGS="${BUILD_TAGS:-pluto}"
- ARCH="$(dpkg --print-architecture 2>/dev/null || true)"
-
- log() {
- printf '\n[%s] %s\n' "$(date '+%H:%M:%S')" "$*"
- }
-
- need_cmd() {
- command -v "$1" >/dev/null 2>&1 || {
- echo "Missing required command: $1" >&2
- exit 1
- }
- }
-
- apt_install_if_missing() {
- local missing=()
- for pkg in "$@"; do
- if ! dpkg -s "$pkg" >/dev/null 2>&1; then
- missing+=("$pkg")
- fi
- done
- if ((${#missing[@]})); then
- log "Installing missing packages: ${missing[*]}"
- sudo apt-get install -y "${missing[@]}"
- fi
- }
-
- install_go() {
- if command -v go >/dev/null 2>&1; then
- log "Go already present: $(go version)"
- return
- fi
-
- if [[ "${SKIP_GO_INSTALL:-0}" == "1" ]]; then
- echo "go not found and SKIP_GO_INSTALL=1 set" >&2
- exit 1
- fi
-
- local go_arch
- case "$ARCH" in
- armhf) go_arch="armv6l" ;;
- arm64) go_arch="arm64" ;;
- amd64) go_arch="amd64" ;;
- *)
- echo "Unsupported architecture for automated Go install: $ARCH" >&2
- exit 1
- ;;
- esac
-
- local tarball="go${GO_VERSION}.linux-${go_arch}.tar.gz"
- local url="https://go.dev/dl/${tarball}"
- local tmp="/tmp/${tarball}"
-
- log "Installing Go ${GO_VERSION} for ${go_arch}"
- wget -O "$tmp" "$url"
- sudo rm -rf /usr/local/go
- sudo tar -C /usr/local -xzf "$tmp"
- export PATH="/usr/local/go/bin:${PATH}"
-
- if ! grep -q '/usr/local/go/bin' "$HOME/.profile" 2>/dev/null; then
- printf '\nexport PATH="/usr/local/go/bin:$PATH"\n' >> "$HOME/.profile"
- fi
-
- log "Go installed: $(/usr/local/go/bin/go version)"
- }
-
- resolve_lib() {
- local name="$1"
- local ldconfig_bin=""
- if command -v ldconfig >/dev/null 2>&1; then
- ldconfig_bin="$(command -v ldconfig)"
- elif [[ -x /sbin/ldconfig ]]; then
- ldconfig_bin="/sbin/ldconfig"
- elif [[ -x /usr/sbin/ldconfig ]]; then
- ldconfig_bin="/usr/sbin/ldconfig"
- fi
-
- if [[ -n "$ldconfig_bin" ]]; then
- "$ldconfig_bin" -p 2>/dev/null | awk -v lib="$name" '$1 == lib { print $NF; exit }'
- return 0
- fi
-
- find /lib /usr/lib /usr/local/lib -name "$name" 2>/dev/null | head -n 1
- }
-
- copy_lib_if_found() {
- local libname="$1"
- local path
- path="$(resolve_lib "$libname" || true)"
- if [[ -z "$path" ]]; then
- path="$(find /lib /usr/lib /usr/local/lib -name "$libname" 2>/dev/null | head -n 1 || true)"
- fi
- if [[ -n "$path" && -f "$path" ]]; then
- cp -Lv "$path" "$RUNTIME_DIR/lib/"
- else
- log "Library not found: $libname"
- fi
- }
-
- find_soapy_plugin_path() {
- local candidate=""
-
- if command -v SoapySDRUtil >/dev/null 2>&1; then
- candidate="$(SoapySDRUtil --info 2>/dev/null | awk -F': ' '/Search path:/ {print $2; exit}')"
- if [[ -n "$candidate" && -d "$candidate" ]]; then
- printf '%s\n' "$candidate"
- return 0
- fi
- fi
-
- for candidate in \
- /usr/local/lib/SoapySDR/modules0.8-3 \
- /usr/local/lib/SoapySDR/modules0.8 \
- /usr/lib/arm-linux-gnueabihf/SoapySDR/modules0.8-3 \
- /usr/lib/arm-linux-gnueabihf/SoapySDR/modules0.8 \
- /usr/lib/SoapySDR/modules0.8-3 \
- /usr/lib/SoapySDR/modules0.8
- do
- if [[ -d "$candidate" ]]; then
- printf '%s\n' "$candidate"
- return 0
- fi
- done
-
- return 1
- }
-
- copy_soapy_plugins_if_found() {
- local plugin_path
- plugin_path="$(find_soapy_plugin_path || true)"
- if [[ -n "$plugin_path" && -d "$plugin_path" ]]; then
- mkdir -p "$RUNTIME_DIR/soapy-modules"
- cp -Lv "$plugin_path"/* "$RUNTIME_DIR/soapy-modules/" 2>/dev/null || true
- printf '%s\n' "$plugin_path" > "$DIST_DIR/SOAPY_PLUGIN_PATH.txt"
- log "Copied Soapy plugins from: $plugin_path"
- else
- log "Soapy plugin path not found"
- fi
- }
-
- write_runner() {
- cat > "${DIST_DIR}/run-fmrtx.sh" <<'EOF'
- #!/usr/bin/env bash
- set -euo pipefail
- SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
-
- SYSTEM_LIB_DIRS="/usr/local/lib:/usr/lib:/usr/lib/arm-linux-gnueabihf:/lib:/lib/arm-linux-gnueabihf"
- export LD_LIBRARY_PATH="${SCRIPT_DIR}/runtime/lib:${SYSTEM_LIB_DIRS}:${LD_LIBRARY_PATH:-}"
-
- if [[ -d "${SCRIPT_DIR}/runtime/soapy-modules" ]]; then
- export SOAPY_SDR_PLUGIN_PATH="${SCRIPT_DIR}/runtime/soapy-modules:${SOAPY_SDR_PLUGIN_PATH:-}"
- elif [[ -f "${SCRIPT_DIR}/SOAPY_PLUGIN_PATH.txt" ]]; then
- export SOAPY_SDR_PLUGIN_PATH="$(cat "${SCRIPT_DIR}/SOAPY_PLUGIN_PATH.txt"):${SOAPY_SDR_PLUGIN_PATH:-}"
- fi
-
- exec "${SCRIPT_DIR}/runtime/bin/fmrtx" "$@"
- EOF
- chmod +x "${DIST_DIR}/run-fmrtx.sh"
- }
-
- write_install_helper() {
- cat > "${DIST_DIR}/install.sh" <<EOF
- #!/usr/bin/env bash
- set -euo pipefail
- PREFIX="{1:-$PREFIX}"
- sudo mkdir -p "\$PREFIX/bin" "\$PREFIX/lib"
- sudo cp -v "${DIST_DIR}/runtime/bin/fmrtx" "\$PREFIX/bin/"
- sudo cp -v ${DIST_DIR}/runtime/lib/* "\$PREFIX/lib/" 2>/dev/null || true
- cat <<'EON'
- Installed.
- Run with e.g.:
- LD_LIBRARY_PATH=\$PREFIX/lib \$PREFIX/bin/fmrtx --help
- EON
- EOF
- # replace placeholder introduced to avoid accidental shell expansion confusion
- sed -i 's/{1:-/\${1:-/g' "${DIST_DIR}/install.sh"
- chmod +x "${DIST_DIR}/install.sh"
- }
-
- main() {
- need_cmd bash
- need_cmd uname
- need_cmd awk
- need_cmd sed
- need_cmd cp
- need_cmd mkdir
- need_cmd ldd
-
- mkdir -p "$BUILD_DIR" "$RUNTIME_DIR/bin" "$RUNTIME_DIR/lib"
-
- if [[ "${SKIP_APT:-0}" != "1" ]]; then
- log "Refreshing apt metadata"
- sudo apt-get update
-
- apt_install_if_missing \
- ca-certificates \
- curl \
- wget \
- git \
- build-essential \
- pkg-config \
- gcc \
- g++ \
- make \
- file \
- binutils \
- tar \
- xz-utils \
- libiio0 \
- libiio-dev \
- libusb-1.0-0 \
- libxml2 \
- libxml2-dev
-
- # Optional / best-effort packages. Not all repos expose them on every arch.
- sudo apt-get install -y soapysdr-tools libsoapysdr0.8 libsoapysdr-dev 2>/dev/null || true
- sudo apt-get install -y soapy-module-plutosdr 2>/dev/null || true
- sudo apt-get install -y iio-oscilloscope 2>/dev/null || true
- sudo apt-get install -y libusb-1.0-0-dev 2>/dev/null || true
- fi
-
- install_go
- need_cmd go
-
- export PATH="/usr/local/go/bin:${PATH}"
- export CGO_ENABLED=1
- export GOOS=linux
-
- case "$ARCH" in
- armhf)
- export GOARCH=arm
- export GOARM=7
- ;;
- arm64)
- export GOARCH=arm64
- ;;
- amd64)
- export GOARCH=amd64
- ;;
- *)
- echo "Unsupported architecture: $ARCH" >&2
- exit 1
- ;;
- esac
-
- log "Build environment"
- go version
- echo "ARCH=${ARCH} GOOS=${GOOS} GOARCH=${GOARCH:-} GOARM=${GOARM:-} CGO_ENABLED=${CGO_ENABLED}"
-
- log "Tidying modules"
- (cd "$REPO_DIR" && go mod tidy)
-
- log "Building fmrtx with Linux Pluto/libiio-first backend (tags: $BUILD_TAGS)"
- (cd "$REPO_DIR" && go build -v -tags "$BUILD_TAGS" -o "$RUNTIME_DIR/bin/fmrtx" ./cmd/fmrtx)
-
- log "Collecting runtime libraries"
- copy_lib_if_found "libiio.so.0"
- copy_lib_if_found "libSoapySDR.so.0.8"
- copy_lib_if_found "libusb-1.0.so.0"
- copy_lib_if_found "libxml2.so.2"
- copy_lib_if_found "libstdc++.so.6"
- copy_lib_if_found "libgcc_s.so.1"
- copy_lib_if_found "libm.so.6"
- copy_lib_if_found "libc.so.6"
- if [[ "$BUILD_TAGS" == *soapy* ]]; then
- copy_soapy_plugins_if_found
- else
- log "Skipping Soapy plugin copy (BUILD_TAGS=$BUILD_TAGS)"
- fi
-
- log "Writing helper scripts"
- write_runner
- write_install_helper
-
- log "Writing build manifest"
- cat > "${DIST_DIR}/BUILD-INFO.txt" <<EOF
- fm-rds-tx Orange Pi build
- ========================
- Date: $(date -Is)
- Host: $(uname -a)
- Repo: $REPO_DIR
- Dist: $DIST_DIR
- Architecture: $ARCH
- Go: $(go version)
-
- Build command:
- go build -tags $BUILD_TAGS -o runtime/bin/fmrtx ./cmd/fmrtx
-
- Important note:
- Linux Pluto builds should prefer the native libiio path (`-tags pluto`).
- SoapySDR is optional fallback/debug infrastructure only.
- Windows Pluto handling is separate and intentionally untouched by this script.
- EOF
-
- log "Binary info"
- file "$RUNTIME_DIR/bin/fmrtx" || true
-
- log "Dynamic dependencies of built binary"
- ldd "$RUNTIME_DIR/bin/fmrtx" || true
-
- if [[ "$BUILD_TAGS" == *soapy* ]]; then
- log "Wrapper self-test: fmrtx --list-devices"
- "${DIST_DIR}/run-fmrtx.sh" --list-devices || true
- else
- log "Wrapper self-test: fmrtx --print-config"
- "${DIST_DIR}/run-fmrtx.sh" --print-config || true
- fi
-
- log "Done. Artifacts are in: $DIST_DIR"
- cat <<EOF
-
- Next steps:
- 1. Copy/use: $DIST_DIR/runtime/bin/fmrtx
- 2. Libraries are in: $DIST_DIR/runtime/lib/
- 3. Launch via wrapper:
- $DIST_DIR/run-fmrtx.sh --help
- 4. Install to target prefix:
- $DIST_DIR/install.sh $PREFIX
-
- Reminder:
- Default Linux packaging now prefers the native libiio Pluto backend.
- Use BUILD_TAGS=soapy only when you explicitly want the Soapy path.
- Windows Pluto support is intentionally left on its separate path.
- EOF
- }
-
- main "$@"
|