A better Rust ATProto crate
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 and friendly names
25declare -A TARGET_TO_PACKAGE=(
26 ["x86_64-unknown-linux-gnu"]="jacquard-lexicon-x86_64-linux"
27 ["aarch64-unknown-linux-gnu"]="jacquard-lexicon-aarch64-linux"
28 ["x86_64-apple-darwin"]="jacquard-lexicon-x86_64-darwin"
29 ["aarch64-apple-darwin"]="jacquard-lexicon-aarch64-darwin"
30 ["x86_64-pc-windows-gnu"]="jacquard-lexicon-x86_64-windows"
31 ["aarch64-pc-windows-gnullvm"]="jacquard-lexicon-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 everything
39 TARGETS=(
40 "x86_64-unknown-linux-gnu"
41 "aarch64-unknown-linux-gnu"
42 "x86_64-apple-darwin"
43 "aarch64-apple-darwin"
44 "x86_64-pc-windows-gnu"
45 "aarch64-pc-windows-gnullvm"
46 )
47 echo "Building from x86_64-linux: All targets (Linux, macOS, Windows for x86_64 and aarch64)"
48 ;;
49 aarch64-linux)
50 # Linux can cross-compile to everything
51 TARGETS=(
52 "aarch64-unknown-linux-gnu"
53 "x86_64-unknown-linux-gnu"
54 "x86_64-apple-darwin"
55 "aarch64-apple-darwin"
56 "x86_64-pc-windows-gnu"
57 "aarch64-pc-windows-gnullvm"
58 )
59 echo "Building from aarch64-linux: All targets (Linux, macOS, Windows for x86_64 and aarch64)"
60 ;;
61 x86_64-darwin)
62 # macOS cross-compilation is limited
63 TARGETS=(
64 "x86_64-apple-darwin"
65 )
66 echo "Building from x86_64-darwin: x86_64-darwin only"
67 echo "Note: Cross to aarch64-darwin needs rosetta, cross to Linux/Windows needs more setup"
68 ;;
69 aarch64-darwin)
70 # macOS aarch64 can build both macOS targets via rosetta
71 TARGETS=(
72 "aarch64-apple-darwin"
73 "x86_64-apple-darwin"
74 )
75 echo "Building from aarch64-darwin: macOS targets (aarch64 + x86_64 via rosetta)"
76 echo "Note: Cross to Linux/Windows needs more setup"
77 ;;
78 *)
79 echo "Error: Unknown system: $CURRENT_SYSTEM"
80 echo "This script supports: x86_64-linux, aarch64-linux, x86_64-darwin, aarch64-darwin"
81 exit 1
82 ;;
83esac
84
85echo ""
86echo "Will build for: ${TARGETS[*]}"
87echo ""
88
89# Output directories
90OUTPUT_DIR="binaries"
91RELEASES_DIR="binaries/releases"
92mkdir -p "$OUTPUT_DIR"
93mkdir -p "$RELEASES_DIR"
94
95# Helper function to package for a target
96package_target() {
97 local TARGET=$1
98 local PACKAGE_NAME="${TARGET_TO_PACKAGE[$TARGET]}"
99
100 echo ""
101 echo "======================================"
102 echo "Building for $TARGET"
103 echo "======================================"
104
105 # Build with nix using cross-compilation package
106 echo "Running: nix build .#${PACKAGE_NAME}"
107 if ! nix build ".#${PACKAGE_NAME}" -o "result-${TARGET}"; then
108 echo "Error: nix build failed for $TARGET"
109 return 1
110 fi
111
112 # Names for versioned and unversioned archives
113 local VERSIONED_NAME="jacquard-lexicon_${TARGET}_v${VERSION}"
114 local UNVERSIONED_NAME="jacquard-lexicon_${TARGET}"
115
116 # Create staging directory
117 local STAGE_DIR="/tmp/${VERSIONED_NAME}"
118 rm -rf "$STAGE_DIR"
119 mkdir -p "$STAGE_DIR"
120
121 # Detect if this is a Windows target
122 if [[ "$TARGET" == *"windows"* ]]; then
123 # Windows: just binaries, README, LICENSE, example config
124 mkdir -p "$STAGE_DIR/bin"
125 cp "result-${TARGET}"/bin/*.exe "$STAGE_DIR/bin/" 2>/dev/null || true
126 cp LICENSE "$STAGE_DIR/"
127 cp README.md "$STAGE_DIR/"
128
129 # Copy example config to a more Windows-friendly location
130 mkdir -p "$STAGE_DIR/examples"
131 cp crates/jacquard-lexicon/lexicons.kdl.example "$STAGE_DIR/examples/" 2>/dev/null || true
132 else
133 # Unix (Linux/macOS): full structure with man pages and completions
134 cp -r "result-${TARGET}"/* "$STAGE_DIR/"
135 cp LICENSE "$STAGE_DIR/"
136 cp README.md "$STAGE_DIR/"
137 fi
138
139 # Create versioned archive (for releases)
140 cd /tmp
141
142 # Use .zip for Windows, .tar.xz for Unix
143 if [[ "$TARGET" == *"windows"* ]]; then
144 zip -r "${VERSIONED_NAME}.zip" "$VERSIONED_NAME"
145 mv "${VERSIONED_NAME}.zip" "$PROJECT_ROOT/$RELEASES_DIR/"
146 echo " Created: ${RELEASES_DIR}/${VERSIONED_NAME}.zip"
147
148 # Rename and create unversioned archive
149 mv "$VERSIONED_NAME" "$UNVERSIONED_NAME"
150 zip -r "${UNVERSIONED_NAME}.zip" "$UNVERSIONED_NAME"
151 mv "${UNVERSIONED_NAME}.zip" "$PROJECT_ROOT/$OUTPUT_DIR/"
152 echo " Created: ${OUTPUT_DIR}/${UNVERSIONED_NAME}.zip"
153 else
154 tar -cJf "${VERSIONED_NAME}.tar.xz" "$VERSIONED_NAME"
155 mv "${VERSIONED_NAME}.tar.xz" "$PROJECT_ROOT/$RELEASES_DIR/"
156 echo " Created: ${RELEASES_DIR}/${VERSIONED_NAME}.tar.xz"
157
158 # Rename and create unversioned archive
159 mv "$VERSIONED_NAME" "$UNVERSIONED_NAME"
160 tar -cJf "${UNVERSIONED_NAME}.tar.xz" "$UNVERSIONED_NAME"
161 mv "${UNVERSIONED_NAME}.tar.xz" "$PROJECT_ROOT/$OUTPUT_DIR/"
162 echo " Created: ${OUTPUT_DIR}/${UNVERSIONED_NAME}.tar.xz"
163 fi
164
165 # Cleanup
166 rm -rf "$UNVERSIONED_NAME"
167 rm -f "$PROJECT_ROOT/result-${TARGET}"
168 cd "$PROJECT_ROOT"
169}
170
171# Build for all targets
172for target in "${TARGETS[@]}"; do
173 package_target "$target" || echo "Warning: build failed for $target, continuing..."
174done
175
176# Print summary
177echo ""
178echo "Packaging complete!"
179echo ""
180echo "Tracked archives (binaries/):"
181ls -lh "$OUTPUT_DIR"/*.tar.xz 2>/dev/null || true
182ls -lh "$OUTPUT_DIR"/*.zip 2>/dev/null || true
183echo ""
184echo "Release archives (binaries/releases/):"
185ls -lh "$RELEASES_DIR"/*.tar.xz 2>/dev/null || true
186ls -lh "$RELEASES_DIR"/*.zip 2>/dev/null || true
187
188# Generate checksums for tracked archives
189echo ""
190echo "Generating checksums for tracked archives..."
191cd "$OUTPUT_DIR"
192sha256sum *.tar.xz *.zip 2>/dev/null > SHA256SUMS || true
193echo "Checksums written to ${OUTPUT_DIR}/SHA256SUMS"
194cat SHA256SUMS
195
196# Generate checksums for release archives
197echo ""
198echo "Generating checksums for release archives..."
199cd "$PROJECT_ROOT/$RELEASES_DIR"
200sha256sum *.tar.xz *.zip 2>/dev/null > SHA256SUMS || true
201echo "Checksums written to ${RELEASES_DIR}/SHA256SUMS"
202cat SHA256SUMS