A better Rust ATProto crate
at main 10 kB view raw
1#!/usr/bin/env bash 2set -euo pipefail 3 4# Script to package jacquard-lexicon binaries for distribution using Nix cross-compilation 5# Creates tar.xz archives with binaries, man pages, completions, README, LICENSE, and config 6# 7# Generates two versions: 8# - Unversioned archives in binaries/ (tracked in git, overwritten each build) 9# - Versioned archives in binaries/releases/ (gitignored, for GitHub releases) 10 11# Determine project root (script is in scripts/ subdirectory) 12SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 13PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" 14cd "$PROJECT_ROOT" 15 16# Parse version from workspace Cargo.toml 17VERSION=$(grep '^version = ' Cargo.toml | head -1 | sed 's/version = "\(.*\)"/\1/') 18echo "Packaging version: $VERSION" 19 20# Detect current system 21CURRENT_SYSTEM=$(nix eval --impure --expr 'builtins.currentSystem' --raw) 22echo "Current system: $CURRENT_SYSTEM" 23 24# Map target triples to nix package names 25declare -A TARGET_TO_PACKAGE=( 26 ["x86_64-unknown-linux-gnu"]="jacquard-lexgen-x86_64-linux" 27 ["aarch64-unknown-linux-gnu"]="jacquard-lexgen-aarch64-linux" 28 ["x86_64-apple-darwin"]="jacquard-lexgen-x86_64-darwin" 29 ["aarch64-apple-darwin"]="jacquard-lexgen-aarch64-darwin" 30 ["x86_64-pc-windows-gnu"]="jacquard-lexgen-x86_64-windows" 31 ["aarch64-pc-windows-gnullvm"]="jacquard-lexgen-aarch64-windows" 32) 33 34# Determine which targets we can build from the current system 35TARGETS=() 36case "$CURRENT_SYSTEM" in 37 x86_64-linux) 38 # Linux can cross-compile to other Linux archs and Windows x86_64 39 # macOS requires remote builders or actual macOS hardware 40 # aarch64-windows: nixpkgs mingw-w64-pthreads broken for aarch64 (missing winver.h) 41 TARGETS=( 42 "x86_64-unknown-linux-gnu" 43 "aarch64-unknown-linux-gnu" 44 "x86_64-pc-windows-gnu" 45 # "aarch64-pc-windows-gnullvm" # TODO: nixpkgs cross-compile broken 46 ) 47 echo "Building from x86_64-linux: Linux (x86_64, aarch64) + Windows (x86_64)" 48 echo "Note: macOS cross-compilation requires remote builders or macOS hardware" 49 echo "Note: aarch64-windows cross-compilation broken in nixpkgs (mingw-w64-pthreads build fails)" 50 ;; 51 aarch64-linux) 52 # Linux can cross-compile to other Linux archs and Windows x86_64 53 # macOS requires remote builders or actual macOS hardware 54 # aarch64-windows: nixpkgs mingw-w64-pthreads broken for aarch64 (missing winver.h) 55 TARGETS=( 56 "aarch64-unknown-linux-gnu" 57 "x86_64-unknown-linux-gnu" 58 "x86_64-pc-windows-gnu" 59 # "aarch64-pc-windows-gnullvm" # TODO: nixpkgs cross-compile broken 60 ) 61 echo "Building from aarch64-linux: Linux (aarch64, x86_64) + Windows (x86_64)" 62 echo "Note: macOS cross-compilation requires remote builders or macOS hardware" 63 echo "Note: aarch64-windows cross-compilation broken in nixpkgs (mingw-w64-pthreads build fails)" 64 ;; 65 x86_64-darwin) 66 # macOS cross-compilation is limited 67 TARGETS=( 68 "x86_64-apple-darwin" 69 ) 70 echo "Building from x86_64-darwin: x86_64-darwin only" 71 ;; 72 aarch64-darwin) 73 # macOS aarch64 can build both macOS targets via rosetta 74 TARGETS=( 75 "aarch64-apple-darwin" 76 "x86_64-apple-darwin" 77 ) 78 echo "Building from aarch64-darwin: macOS targets (aarch64 + x86_64 via rosetta)" 79 ;; 80 *) 81 echo "Error: Unknown system: $CURRENT_SYSTEM" 82 echo "This script supports: x86_64-linux, aarch64-linux, x86_64-darwin, aarch64-darwin" 83 exit 1 84 ;; 85esac 86 87echo "" 88echo "Will build for: ${TARGETS[*]}" 89echo "" 90 91# Output directories 92OUTPUT_DIR="binaries" 93RELEASES_DIR="binaries/releases" 94mkdir -p "$OUTPUT_DIR" 95mkdir -p "$RELEASES_DIR" 96 97# Helper function to package for a target 98package_target() { 99 local TARGET=$1 100 local PACKAGE_NAME="${TARGET_TO_PACKAGE[$TARGET]}" 101 102 echo "" 103 echo "======================================" 104 echo "Building for $TARGET" 105 echo "======================================" 106 107 # Build with nix using cross-compilation package 108 echo "Running: nix build .#${PACKAGE_NAME}" 109 if ! nix build ".#${PACKAGE_NAME}" -o "result-${TARGET}"; then 110 echo "Error: nix build failed for $TARGET" 111 return 1 112 fi 113 114 # Package each binary separately 115 for BINARY in lex-fetch jacquard-codegen; do 116 echo " Packaging binary: $BINARY" 117 package_binary "$TARGET" "$BINARY" 118 done 119 120 # Cleanup 121 rm -f "$PROJECT_ROOT/result-${TARGET}" 122 cd "$PROJECT_ROOT" 123} 124 125# Helper function to package a single binary 126package_binary() { 127 local TARGET=$1 128 local BINARY=$2 129 130 # Determine binary extension 131 local BINARY_EXT="" 132 if [[ "$TARGET" == *"windows"* ]]; then 133 BINARY_EXT=".exe" 134 fi 135 136 # Check if binary exists 137 if [[ ! -f "result-${TARGET}/bin/${BINARY}${BINARY_EXT}" ]]; then 138 echo " Warning: ${BINARY}${BINARY_EXT} not found, skipping" 139 return 0 140 fi 141 142 # Names for versioned and unversioned archives 143 local VERSIONED_NAME="${BINARY}_${TARGET}_v${VERSION}" 144 local UNVERSIONED_NAME="${BINARY}_${TARGET}" 145 146 # Create staging directory 147 local STAGE_DIR="/tmp/${VERSIONED_NAME}" 148 rm -rf "$STAGE_DIR" 149 mkdir -p "$STAGE_DIR" 150 151 # Detect if this is a Windows target 152 if [[ "$TARGET" == *"windows"* ]]; then 153 # Windows: binary, README, LICENSE, example config (for lex-fetch only) 154 cp "result-${TARGET}/bin/${BINARY}.exe" "$STAGE_DIR/" 155 cp LICENSE "$STAGE_DIR/" 156 cp README.md "$STAGE_DIR/" 157 158 # Only include example config for lex-fetch 159 if [[ "$BINARY" == "lex-fetch" ]]; then 160 mkdir -p "$STAGE_DIR/examples" 161 cp crates/jacquard-lexgen/lexicons.kdl.example "$STAGE_DIR/examples/" 2>/dev/null || true 162 fi 163 else 164 # Unix (Linux/macOS): binary, man page, completions, README, LICENSE 165 mkdir -p "$STAGE_DIR/bin" 166 cp "result-${TARGET}/bin/${BINARY}" "$STAGE_DIR/bin/" 167 168 cp LICENSE "$STAGE_DIR/" 169 cp README.md "$STAGE_DIR/" 170 171 # Copy man page if it exists 172 if [[ -f "result-${TARGET}/share/man/man1/${BINARY}.1.gz" ]]; then 173 mkdir -p "$STAGE_DIR/share/man/man1" 174 cp "result-${TARGET}/share/man/man1/${BINARY}.1.gz" "$STAGE_DIR/share/man/man1/" 175 fi 176 177 # Copy completions if they exist 178 for shell_dir in bash fish zsh; do 179 local comp_dir="result-${TARGET}/share/$shell_dir/site-functions" 180 if [[ "$shell_dir" == "bash" ]]; then 181 comp_dir="result-${TARGET}/share/bash-completion/completions" 182 elif [[ "$shell_dir" == "fish" ]]; then 183 comp_dir="result-${TARGET}/share/fish/vendor_completions.d" 184 fi 185 186 if [[ -d "$comp_dir" ]]; then 187 for comp in "$comp_dir"/*; do 188 local comp_name=$(basename "$comp") 189 # Only copy completions for this specific binary 190 if [[ "$comp_name" == "${BINARY}"* ]] || [[ "$comp_name" == "_${BINARY}" ]]; then 191 mkdir -p "$STAGE_DIR/share/$(dirname "${comp#result-${TARGET}/share/}")" 192 cp "$comp" "$STAGE_DIR/share/$(dirname "${comp#result-${TARGET}/share/}")/" 193 fi 194 done 195 fi 196 done 197 198 # Only include example config for lex-fetch 199 if [[ "$BINARY" == "lex-fetch" ]]; then 200 mkdir -p "$STAGE_DIR/share/doc/jacquard-lexicon" 201 cp crates/jacquard-lexgen/lexicons.kdl.example "$STAGE_DIR/share/doc/jacquard-lexgen/" 2>/dev/null || true 202 fi 203 fi 204 205 # Create versioned archive (for releases) 206 cd /tmp 207 208 # Use .zip for Windows, .tar.xz for Unix 209 if [[ "$TARGET" == *"windows"* ]]; then 210 zip -r "${VERSIONED_NAME}.zip" "$VERSIONED_NAME" 211 mv "${VERSIONED_NAME}.zip" "$PROJECT_ROOT/$RELEASES_DIR/" 212 echo " Created: ${RELEASES_DIR}/${VERSIONED_NAME}.zip" 213 214 # Rename and create unversioned archive 215 mv "$VERSIONED_NAME" "$UNVERSIONED_NAME" 216 zip -r "${UNVERSIONED_NAME}.zip" "$UNVERSIONED_NAME" 217 mv "${UNVERSIONED_NAME}.zip" "$PROJECT_ROOT/$OUTPUT_DIR/" 218 echo " Created: ${OUTPUT_DIR}/${UNVERSIONED_NAME}.zip" 219 else 220 tar -cJf "${VERSIONED_NAME}.tar.xz" "$VERSIONED_NAME" 221 mv "${VERSIONED_NAME}.tar.xz" "$PROJECT_ROOT/$RELEASES_DIR/" 222 echo " Created: ${RELEASES_DIR}/${VERSIONED_NAME}.tar.xz" 223 224 # Rename and create unversioned archive 225 mv "$VERSIONED_NAME" "$UNVERSIONED_NAME" 226 tar -cJf "${UNVERSIONED_NAME}.tar.xz" "$UNVERSIONED_NAME" 227 mv "${UNVERSIONED_NAME}.tar.xz" "$PROJECT_ROOT/$OUTPUT_DIR/" 228 echo " Created: ${OUTPUT_DIR}/${UNVERSIONED_NAME}.tar.xz" 229 fi 230 231 # Cleanup 232 rm -rf "$UNVERSIONED_NAME" 233 234 # Return to project root 235 cd "$PROJECT_ROOT" 236} 237 238# Build for all targets 239for target in "${TARGETS[@]}"; do 240 package_target "$target" || echo "Warning: build failed for $target, continuing..." 241done 242 243# Print summary 244echo "" 245echo "Packaging complete!" 246echo "" 247echo "Tracked archives (binaries/):" 248ls -lh "$OUTPUT_DIR"/*.tar.xz 2>/dev/null || true 249ls -lh "$OUTPUT_DIR"/*.zip 2>/dev/null || true 250echo "" 251echo "Release archives (binaries/releases/):" 252ls -lh "$RELEASES_DIR"/*.tar.xz 2>/dev/null || true 253ls -lh "$RELEASES_DIR"/*.zip 2>/dev/null || true 254 255# Generate checksums for tracked archives 256echo "" 257echo "Generating checksums for tracked archives..." 258cd "$OUTPUT_DIR" 259sha256sum *.tar.xz *.zip 2>/dev/null > SHA256SUMS || true 260echo "Checksums written to ${OUTPUT_DIR}/SHA256SUMS" 261cat SHA256SUMS 262 263# Generate checksums for release archives 264echo "" 265echo "Generating checksums for release archives..." 266cd "$PROJECT_ROOT/$RELEASES_DIR" 267sha256sum *.tar.xz *.zip 2>/dev/null > SHA256SUMS || true 268echo "Checksums written to ${RELEASES_DIR}/SHA256SUMS" 269cat SHA256SUMS