Implement multi-platform build system with pure Go support

- Introduced a comprehensive build system that supports multiple platforms (Linux, macOS, Windows, Android) using pure Go builds (`CGO_ENABLED=0`).
- Updated all build and test scripts to ensure compatibility with the new purego approach, allowing for dynamic loading of `libsecp256k1` at runtime.
- Added detailed documentation on the build process, platform detection, and deployment options.
- Enhanced CI/CD workflows to automate builds for all supported platforms and include necessary libraries in releases.
- Updated `.gitignore` to exclude build output files.
- Created new documentation files for deployment and multi-platform build summaries.
This commit is contained in:
2025-11-04 20:29:19 +00:00
parent e0a95ca1cd
commit 202d3171f9
22 changed files with 1732 additions and 30 deletions

190
scripts/README_BUILD.md Normal file
View File

@@ -0,0 +1,190 @@
# Multi-Platform Build Scripts
This directory contains scripts for building and running ORLY relay binaries across multiple platforms.
## Available Scripts
### `build-all-platforms.sh`
Comprehensive build script that creates binaries for all supported platforms:
- Linux (AMD64, ARM64)
- macOS (AMD64 Intel, ARM64 Apple Silicon)
- Windows (AMD64)
- Android (ARM64, AMD64) - requires Android NDK
**Usage:**
```bash
./scripts/build-all-platforms.sh
```
**Output:** All binaries are placed in `build/` directory with platform-specific names.
### `platform-detect.sh`
Helper script for platform detection and binary/library name resolution.
**Usage:**
```bash
# Detect current platform
./scripts/platform-detect.sh detect
# Output: linux-amd64
# Get binary name for version
./scripts/platform-detect.sh binary v0.25.0
# Output: orly-v0.25.0-linux-amd64
# Get library name
./scripts/platform-detect.sh library
# Output: libsecp256k1-linux-amd64.so
```
### `run-orly.sh`
Universal launcher that automatically selects and runs the correct binary for your platform.
**Usage:**
```bash
./scripts/run-orly.sh [arguments for orly]
```
Features:
- Auto-detects platform
- Sets correct library path
- Passes all arguments to the binary
- Shows helpful error if binary not found
## Build Prerequisites
### For Linux Host
```bash
# Install cross-compilation tools
sudo apt-get update
sudo apt-get install -y \
gcc-mingw-w64-x86-64 \
gcc-aarch64-linux-gnu \
autoconf automake libtool
```
### For Android (optional)
Download and install Android NDK, then:
```bash
export ANDROID_NDK_HOME=/path/to/android-ndk
```
## Quick Start
1. **Build all platforms:**
```bash
./scripts/build-all-platforms.sh
```
2. **Run the binary:**
```bash
./scripts/run-orly.sh
```
3. **Or run specific platform binary:**
```bash
# Linux
export LD_LIBRARY_PATH=./build:$LD_LIBRARY_PATH
./build/orly-v0.25.0-linux-amd64
# macOS
./build/orly-v0.25.0-darwin-arm64
# Windows
set PATH=.\build;%PATH%
.\build\orly-v0.25.0-windows-amd64.exe
```
## Platform Support Matrix
| Platform | Architecture | CGO | Library Required | Status |
|----------------|--------------|-----|------------------|--------------|
| Linux | AMD64 | ✓ | libsecp256k1.so | Full Support |
| Linux | ARM64 | ✓ | libsecp256k1.so | Full Support |
| macOS | AMD64 | ✗ | - | Full Support |
| macOS | ARM64 | ✗ | - | Full Support |
| Windows | AMD64 | ✓ | libsecp256k1.dll | Full Support |
| Android | ARM64 | ✓ | libsecp256k1.so | Experimental |
| Android | AMD64 | ✓ | libsecp256k1.so | Experimental |
**Note:** macOS builds use pure Go (no CGO) for easier distribution. Crypto operations are slower but no library dependency is required.
## Integration with Other Scripts
All deployment and test scripts have been updated to support platform detection:
- `scripts/deploy.sh` - Builds with CGO and copies libraries
- `scripts/test.sh` - Sets library paths for tests
- `scripts/benchmark.sh` - Uses platform-specific binaries
- `scripts/runtests.sh` - Sets library paths for benchmark tests
## CI/CD
GitHub Actions workflow (`.github/workflows/go.yml`) automatically:
- Builds for all major platforms on release
- Includes platform-specific libraries
- Creates SHA256 checksums
- Uploads all artifacts to GitHub releases
## File Naming Convention
Binaries:
- `orly-{version}-{os}-{arch}[.ext]`
- Example: `orly-v0.25.0-linux-amd64`
Libraries:
- `libsecp256k1-{os}-{arch}.{ext}`
- Example: `libsecp256k1-linux-amd64.so`
## Distribution
When distributing binaries, include:
1. The platform-specific binary
2. The corresponding library file (for CGO builds)
3. SHA256SUMS file for verification
Example release structure:
```
release/
├── orly-v0.25.0-linux-amd64
├── orly-v0.25.0-linux-arm64
├── orly-v0.25.0-darwin-amd64
├── orly-v0.25.0-darwin-arm64
├── orly-v0.25.0-windows-amd64.exe
├── libsecp256k1-linux-amd64.so
├── libsecp256k1-linux-arm64.so
├── libsecp256k1-windows-amd64.dll
└── SHA256SUMS-v0.25.0.txt
```
## Troubleshooting
**Binary not found:**
Run `./scripts/build-all-platforms.sh` to build binaries.
**Library not found at runtime:**
Set the library path:
```bash
# Linux
export LD_LIBRARY_PATH=./build:$LD_LIBRARY_PATH
# macOS
export DYLD_LIBRARY_PATH=./build:$DYLD_LIBRARY_PATH
# Windows
set PATH=.\build;%PATH%
```
**Cross-compilation fails:**
Install the required cross-compiler (see Build Prerequisites above).
## Performance Comparison
| Build Type | Crypto Speed | Binary Size | Distribution |
|---------------|--------------|-------------|--------------|
| CGO (Linux) | Fast (2-3x) | Smaller | Needs .so |
| Pure Go (Mac) | Slower | Larger | Self-contained |
For detailed documentation, see [docs/BUILD_PLATFORMS.md](../docs/BUILD_PLATFORMS.md).

View File

@@ -30,8 +30,14 @@ RUN_DIR="${REPORTS_DIR}/run_${TIMESTAMP}"
# Ensure the benchmark binary is built
BENCHMARK_BIN="${REPO_ROOT}/cmd/benchmark/benchmark"
if [[ ! -x "$BENCHMARK_BIN" ]]; then
echo "Building benchmark binary..."
go build -o "$BENCHMARK_BIN" "$REPO_ROOT/cmd/benchmark"
echo "Building benchmark binary (pure Go + purego)..."
cd "$REPO_ROOT/cmd/benchmark"
CGO_ENABLED=0 go build -o "$BENCHMARK_BIN" .
# Copy libsecp256k1.so if available (runtime optional)
if [[ -f "$REPO_ROOT/pkg/crypto/p8k/libsecp256k1.so" ]]; then
cp "$REPO_ROOT/pkg/crypto/p8k/libsecp256k1.so" "$(dirname "$BENCHMARK_BIN")/"
fi
cd "$REPO_ROOT"
fi
# Create output directory

169
scripts/build-all-platforms.sh Executable file
View File

@@ -0,0 +1,169 @@
#!/bin/bash
# Multi-platform build script for ORLY relay
# Builds binaries for Linux, macOS, Windows, and Android
# NOTE: All builds use CGO_ENABLED=0 since p8k library uses purego (not CGO)
# The library dynamically loads libsecp256k1 at runtime via purego
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
cd "$REPO_ROOT"
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Configuration
VERSION=$(cat pkg/version/version)
OUTPUT_DIR="$REPO_ROOT/build"
LIB_SOURCE="$REPO_ROOT/pkg/crypto/p8k"
echo -e "${BLUE}========================================${NC}"
echo -e "${BLUE}ORLY Multi-Platform Build Script${NC}"
echo -e "${BLUE}========================================${NC}"
echo -e "Version: ${GREEN}${VERSION}${NC}"
echo -e "Output directory: ${OUTPUT_DIR}"
echo -e "${BLUE}Build mode: Pure Go with purego${NC}"
echo ""
# Create output directory
mkdir -p "$OUTPUT_DIR"
# Function to build for a specific platform
build_platform() {
local goos=$1
local goarch=$2
local platform_name=$3
local binary_ext=$4
echo -e "${YELLOW}Building for ${platform_name} (pure Go + purego)...${NC}"
local output_name="orly-${VERSION}-${platform_name}${binary_ext}"
local output_path="${OUTPUT_DIR}/${output_name}"
# Build with CGO_ENABLED=0 (pure Go with purego)
if CGO_ENABLED=0 GOOS=$goos GOARCH=$goarch \
go build -ldflags "-s -w -X main.version=${VERSION}" \
-o "${output_path}" . 2>&1; then
echo -e "${GREEN}✓ Built: ${output_name}${NC}"
# Copy appropriate runtime library
case "$goos" in
linux)
if [ -f "${LIB_SOURCE}/libsecp256k1.so" ]; then
cp "${LIB_SOURCE}/libsecp256k1.so" "${OUTPUT_DIR}/libsecp256k1-${platform_name}.so"
echo -e "${GREEN} ✓ Copied libsecp256k1.so (runtime optional)${NC}"
fi
;;
darwin)
if [ -f "${LIB_SOURCE}/libsecp256k1.dylib" ]; then
cp "${LIB_SOURCE}/libsecp256k1.dylib" "${OUTPUT_DIR}/libsecp256k1-${platform_name}.dylib"
echo -e "${GREEN} ✓ Copied libsecp256k1.dylib (runtime optional)${NC}"
fi
;;
windows)
if [ -f "${LIB_SOURCE}/libsecp256k1.dll" ]; then
cp "${LIB_SOURCE}/libsecp256k1.dll" "${OUTPUT_DIR}/libsecp256k1-${platform_name}.dll"
echo -e "${GREEN} ✓ Copied libsecp256k1.dll (runtime optional)${NC}"
fi
;;
android)
if [ -f "${LIB_SOURCE}/libsecp256k1.so" ]; then
cp "${LIB_SOURCE}/libsecp256k1.so" "${OUTPUT_DIR}/libsecp256k1-${platform_name}.so"
echo -e "${GREEN} ✓ Copied libsecp256k1.so (runtime optional)${NC}"
fi
;;
esac
# Create SHA256 checksum
(cd "$OUTPUT_DIR" && sha256sum "$output_name" >> "SHA256SUMS-${VERSION}.txt")
return 0
else
echo -e "${RED}✗ Failed to build for ${platform_name}${NC}"
return 1
fi
}
# Clean old builds
echo -e "${BLUE}Cleaning old builds...${NC}"
rm -f "${OUTPUT_DIR}/orly-"* "${OUTPUT_DIR}/libsecp256k1-"* "${OUTPUT_DIR}/SHA256SUMS-"*
echo ""
echo -e "${BLUE}Note: Pure Go builds work on all platforms${NC}"
echo -e "${BLUE} libsecp256k1 is loaded at runtime if available (faster)${NC}"
echo -e "${BLUE} Falls back to pure Go p256k1 if library not found${NC}"
echo ""
# Build for each platform
echo -e "${BLUE}Starting builds...${NC}"
echo ""
# Linux AMD64
build_platform "linux" "amd64" "linux-amd64" "" || true
# Linux ARM64
build_platform "linux" "arm64" "linux-arm64" "" || true
# macOS AMD64 (Intel)
build_platform "darwin" "amd64" "darwin-amd64" "" || true
# macOS ARM64 (Apple Silicon)
build_platform "darwin" "arm64" "darwin-arm64" "" || true
# Windows AMD64
build_platform "windows" "amd64" "windows-amd64" ".exe" || true
# Android builds
echo -e "${BLUE}Building for Android...${NC}"
# Android ARM64
build_platform "android" "arm64" "android-arm64" "" || true
# Android AMD64
build_platform "android" "amd64" "android-amd64" "" || true
echo ""
echo -e "${BLUE}========================================${NC}"
echo -e "${GREEN}Build Summary${NC}"
echo -e "${BLUE}========================================${NC}"
echo ""
# List all built binaries
if ls "${OUTPUT_DIR}"/orly-* 1> /dev/null 2>&1; then
echo -e "${GREEN}Built binaries:${NC}"
ls -lh "${OUTPUT_DIR}"/orly-* | awk '{print " " $9 " (" $5 ")"}'
echo ""
if [ -f "${OUTPUT_DIR}/SHA256SUMS-${VERSION}.txt" ]; then
echo -e "${GREEN}Checksums:${NC}"
cat "${OUTPUT_DIR}/SHA256SUMS-${VERSION}.txt" | sed 's/^/ /'
echo ""
fi
echo -e "${GREEN}Libraries:${NC}"
ls -lh "${OUTPUT_DIR}"/libsecp256k1-* 2>/dev/null | awk '{print " " $9 " (" $5 ")"}' || echo " No libraries copied"
echo ""
else
echo -e "${RED}No binaries were built successfully${NC}"
exit 1
fi
echo -e "${BLUE}========================================${NC}"
echo -e "${GREEN}Build completed!${NC}"
echo -e "${BLUE}========================================${NC}"
echo ""
echo "Output directory: ${OUTPUT_DIR}"
echo ""
echo "To test a binary:"
echo " Linux: ./${OUTPUT_DIR}/orly-${VERSION}-linux-amd64"
echo " macOS: ./${OUTPUT_DIR}/orly-${VERSION}-darwin-arm64"
echo " Windows: ./${OUTPUT_DIR}/orly-${VERSION}-windows-amd64.exe"
echo ""

View File

@@ -159,9 +159,15 @@ build_application() {
./scripts/update-embedded-web.sh
# Build the binary in the current directory
log_info "Building binary in current directory..."
log_info "Building binary in current directory (pure Go + purego)..."
CGO_ENABLED=0 go build -o "$BINARY_NAME"
# Copy libsecp256k1.so next to the binary (optional, for runtime performance)
if [[ -f "pkg/crypto/p8k/libsecp256k1.so" ]]; then
cp pkg/crypto/p8k/libsecp256k1.so .
log_info "Copied libsecp256k1.so next to binary (runtime optional)"
fi
if [[ -f "./$BINARY_NAME" ]]; then
log_success "ORLY relay built successfully"
else
@@ -190,10 +196,16 @@ install_binary() {
# Ensure GOBIN directory exists
mkdir -p "$GOBIN"
# Copy binary
# Copy binary and library
cp "./$BINARY_NAME" "$GOBIN/"
chmod +x "$GOBIN/$BINARY_NAME"
# Copy library if it exists
if [[ -f "./libsecp256k1.so" ]]; then
cp "./libsecp256k1.so" "$GOBIN/"
log_info "Copied libsecp256k1.so to $GOBIN/"
fi
log_success "Binary installed to $GOBIN/$BINARY_NAME"
}

120
scripts/platform-detect.sh Executable file
View File

@@ -0,0 +1,120 @@
#!/bin/bash
# Platform detection and binary selection helper
# This script detects the current platform and returns the appropriate binary name
detect_platform() {
local os=$(uname -s | tr '[:upper:]' '[:lower:]')
local arch=$(uname -m)
# Normalize OS name
case "$os" in
linux*)
os="linux"
;;
darwin*)
os="darwin"
;;
mingw*|msys*|cygwin*)
os="windows"
;;
*)
echo "unknown"
return 1
;;
esac
# Normalize architecture
case "$arch" in
x86_64|amd64)
arch="amd64"
;;
aarch64|arm64)
arch="arm64"
;;
armv7l)
arch="arm"
;;
*)
echo "unknown"
return 1
;;
esac
echo "${os}-${arch}"
return 0
}
get_binary_name() {
local version=$1
local platform=$(detect_platform)
if [ "$platform" = "unknown" ]; then
echo "Error: Unsupported platform" >&2
return 1
fi
local ext=""
if [[ "$platform" == windows-* ]]; then
ext=".exe"
fi
echo "orly-${version}-${platform}${ext}"
return 0
}
get_library_name() {
local platform=$(detect_platform)
if [ "$platform" = "unknown" ]; then
echo "Error: Unsupported platform" >&2
return 1
fi
case "$platform" in
linux-*)
echo "libsecp256k1-${platform}.so"
;;
darwin-*)
echo "libsecp256k1-${platform}.dylib"
;;
windows-*)
echo "libsecp256k1-${platform}.dll"
;;
*)
return 1
;;
esac
return 0
}
# If called directly, run the function based on first argument
if [ "${BASH_SOURCE[0]}" -ef "$0" ]; then
case "$1" in
detect|platform)
detect_platform
;;
binary)
get_binary_name "${2:-v0.25.0}"
;;
library|lib)
get_library_name
;;
*)
echo "Usage: $0 {detect|binary <version>|library}"
echo ""
echo "Commands:"
echo " detect - Detect current platform (e.g., linux-amd64)"
echo " binary - Get binary name for current platform"
echo " library - Get library name for current platform"
echo ""
echo "Examples:"
echo " $0 detect"
echo " $0 binary v0.25.0"
echo " $0 library"
exit 1
;;
esac
fi

65
scripts/run-orly.sh Executable file
View File

@@ -0,0 +1,65 @@
#!/bin/bash
# Universal run script for ORLY relay
# Automatically selects the correct binary for the current platform
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
# Source platform detection
source "$SCRIPT_DIR/platform-detect.sh"
# Get version
VERSION=$(cat "$REPO_ROOT/pkg/version/version")
# Detect platform
PLATFORM=$(detect_platform)
if [ $? -ne 0 ]; then
echo "Error: Could not detect platform"
exit 1
fi
echo "Detected platform: $PLATFORM"
# Get binary name
BINARY_NAME=$(get_binary_name "$VERSION")
BINARY_PATH="$REPO_ROOT/build/$BINARY_NAME"
# Check if binary exists
if [ ! -f "$BINARY_PATH" ]; then
echo "Error: Binary not found: $BINARY_PATH"
echo ""
echo "Please run: ./scripts/build-all-platforms.sh"
exit 1
fi
# Get library name and set library path
LIBRARY_NAME=$(get_library_name)
LIBRARY_PATH="$REPO_ROOT/build/$LIBRARY_NAME"
if [ -f "$LIBRARY_PATH" ]; then
case "$PLATFORM" in
linux-*)
export LD_LIBRARY_PATH="$REPO_ROOT/build:${LD_LIBRARY_PATH:-}"
;;
darwin-*)
export DYLD_LIBRARY_PATH="$REPO_ROOT/build:${DYLD_LIBRARY_PATH:-}"
;;
windows-*)
export PATH="$REPO_ROOT/build:${PATH:-}"
;;
esac
echo "Library path set for: $LIBRARY_NAME"
else
echo "Warning: Library not found: $LIBRARY_PATH"
echo "Binary may not work if it requires libsecp256k1"
fi
echo "Running: $BINARY_NAME"
echo ""
# Execute the binary with all arguments passed to this script
exec "$BINARY_PATH" "$@"

View File

@@ -1,2 +1,8 @@
#!/usr/bin/env bash
# Pure Go build with purego - no CGO needed
# libsecp256k1 is loaded dynamically at runtime if available
export CGO_ENABLED=0
if [ -f "pkg/crypto/p8k/libsecp256k1.so" ]; then
export LD_LIBRARY_PATH="${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}$(pwd)/pkg/crypto/p8k"
fi
go test -v ./... -bench=. -run=xxx -benchmem

View File

@@ -162,8 +162,13 @@ else
fi
echo -e "${YELLOW}8. Testing build capability...${NC}"
if go build -o "$temp_dir/test-orly" . >/dev/null 2>&1; then
echo -e "${GREEN}✓ Project builds successfully${NC}"
if CGO_ENABLED=0 go build -o "$temp_dir/test-orly" . >/dev/null 2>&1; then
echo -e "${GREEN}✓ Project builds successfully (pure Go + purego)${NC}"
# Copy libsecp256k1.so if available (runtime optional)
if [[ -f "pkg/crypto/p8k/libsecp256k1.so" ]]; then
cp pkg/crypto/p8k/libsecp256k1.so "$temp_dir/"
echo -e "${GREEN}✓ libsecp256k1.so copied (runtime optional)${NC}"
fi
if [[ -x "$temp_dir/test-orly" ]]; then
echo -e "${GREEN}✓ Binary is executable${NC}"
else

View File

@@ -5,6 +5,13 @@
set -e
# Pure Go build with purego - no CGO needed
# libsecp256k1 is loaded dynamically at runtime if available
export CGO_ENABLED=0
if [ -f "$(dirname "$0")/../pkg/crypto/p8k/libsecp256k1.so" ]; then
export LD_LIBRARY_PATH="${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}$(dirname "$0")/../pkg/crypto/p8k"
fi
echo "🧪 Running Managed ACL Tests"
echo "=============================="

View File

@@ -12,13 +12,20 @@ echo "Checking Go version..."
go version
echo ""
# Build without cgo
echo "Building with cgo disabled..."
# Setup library path (runtime optional)
if [ -f "pkg/crypto/p8k/libsecp256k1.so" ]; then
export LD_LIBRARY_PATH="${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}$(pwd)/pkg/crypto/p8k"
echo "Library path set for libsecp256k1.so (runtime optional)"
fi
echo ""
# Build with pure Go + purego (no CGO needed)
echo "Building with pure Go + purego..."
CGO_ENABLED=0 go build -v ./...
echo ""
# Test without cgo
echo "Testing with cgo disabled..."
# Test with pure Go + purego
echo "Testing with pure Go + purego..."
CGO_ENABLED=0 go test -v $(go list ./... | xargs -n1 sh -c 'ls $0/*_test.go 1>/dev/null 2>&1 && echo $0' | grep .)
echo ""

View File

@@ -1,4 +1,11 @@
#!/usr/bin/env bash
# Pure Go build with purego - no CGO needed
# libsecp256k1 is loaded dynamically at runtime if available
export CGO_ENABLED=0
if [ -f "pkg/crypto/p8k/libsecp256k1.so" ]; then
export LD_LIBRARY_PATH="${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}$(pwd)/pkg/crypto/p8k"
fi
go mod tidy
go test ./...
cd pkg/crypto

View File

@@ -5,6 +5,13 @@
set -e
# Pure Go build with purego - no CGO needed
# libsecp256k1 is loaded dynamically at runtime if available
export CGO_ENABLED=0
if [ -f "$(dirname "$0")/../pkg/crypto/p8k/libsecp256k1.so" ]; then
export LD_LIBRARY_PATH="${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}$(dirname "$0")/../pkg/crypto/p8k"
fi
echo "🧪 Running Policy System Tests"
echo "================================"