Skip to content

feat: ODiff v3 SIMD #748

feat: ODiff v3 SIMD

feat: ODiff v3 SIMD #748

Workflow file for this run

name: Build
on:
pull_request:
push:
branches:
- main
tags:
- "v*.*.*"
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
include:
- target: x86_64-linux-gnu
os: ubuntu-latest
artifact: "linux-x64"
triplet: "x64-linux"
- target: aarch64-linux-gnu
os: ubuntu-latest
artifact: "linux-arm64"
triplet: "arm64-linux"
toolchain: "gcc-aarch64-linux-gnu g++-aarch64-linux-gnu"
- target: x86_64-windows-gnu
os: windows-latest
artifact: "windows-x64.exe"
triplet: "x64-mingw-static"
- target: aarch64-windows-gnu
os: ubuntu-latest
artifact: "windows-arm64.exe"
triplet: "arm64-mingw-static"
toolchain: "llvm-mingw"
- target: aarch64-macos
os: macos-latest
artifact: "macos-arm64"
triplet: "arm64-osx"
- target: x86_64-macos
os: macos-13
artifact: "macos-x64"
triplet: "x64-osx"
defaults:
run:
shell: bash
steps:
- name: Checkout code
uses: actions/checkout@v4
- if: runner.os == 'Windows'
run: |
rm -rf $(which pkg-config)
choco install pkgconfiglite
- name: Install cross-compilation toolchain
if: runner.os == 'Linux' && matrix.toolchain
run: |
if [[ "${{ matrix.target }}" == "aarch64-windows-gnu" ]]; then
wget https://github.com/mstorsjo/llvm-mingw/releases/download/20240417/llvm-mingw-20240417-ucrt-ubuntu-20.04-x86_64.tar.xz
tar -xf llvm-mingw-20240417-ucrt-ubuntu-20.04-x86_64.tar.xz
echo "${GITHUB_WORKSPACE}/llvm-mingw-20240417-ucrt-ubuntu-20.04-x86_64/bin" >> $GITHUB_PATH
sudo apt-get update
sudo apt-get install -y pkg-config
echo "CC=aarch64-w64-mingw32-clang" >> $GITHUB_ENV
echo "CXX=aarch64-w64-mingw32-clang++" >> $GITHUB_ENV
echo "AR=aarch64-w64-mingw32-llvm-ar" >> $GITHUB_ENV
echo "LD=aarch64-w64-mingw32-ld" >> $GITHUB_ENV
echo "STRIP=aarch64-w64-mingw32-llvm-strip" >> $GITHUB_ENV
echo "PKG_CONFIG=pkg-config" >> $GITHUB_ENV
else
# Regular GCC cross-compilation for Linux ARM64
sudo apt-get update
sudo apt-get install -y ${{ matrix.toolchain }}
fi
- name: Setup Zig
uses: goto-bus-stop/setup-zig@v2
with:
version: 0.15.1
- uses: lukka/get-cmake@latest
- name: Setup vcpkg
uses: lukka/run-vcpkg@v11
env:
VCPKG_DEFAULT_TRIPLET: ${{ matrix.triplet }}
VCPKG_DEFAULT_HOST_TRIPLET: ${{ matrix.triplet }}
VCPKG_BUILD_TYPE: release
with:
runVcpkgInstall: true
runVcpkgFormatString: '["install", "--clean-after-build"]'
- name: Set pkg-config path on Unix
if: runner.os != 'Windows'
run: |
ls "${GITHUB_WORKSPACE}/vcpkg_installed/${VCPKG_DEFAULT_TRIPLET}/lib/pkgconfig"
echo "PKG_CONFIG_PATH=${GITHUB_WORKSPACE}/vcpkg_installed/${VCPKG_DEFAULT_TRIPLET}/lib/pkgconfig" >> $GITHUB_ENV
- name: Set pkg-config path on Windows
shell: bash
if: runner.os == 'Windows'
run: |
echo "PKG_CONFIG_PATH=${GITHUB_WORKSPACE}\\vcpkg_installed\\${VCPKG_DEFAULT_TRIPLET}\\lib\\pkgconfig" >> $GITHUB_ENV
- name: Build for ${{ matrix.target }}
env:
VCPKG_ROOT: ${{ github.workspace }}/vcpkg_installed
run: |
echo "🔧 Building for target: ${{ matrix.target }}"
echo "📁 VCPKG_ROOT: $VCPKG_ROOT"
echo "🔗 PKG_CONFIG_PATH: $PKG_CONFIG_PATH"
# Create unique build directory to prevent matrix job collisions
BUILD_DIR="zig-out-${{ matrix.artifact }}"
echo "🏗️ Using isolated build directory: $BUILD_DIR"
# Clean any existing build directory
rm -rf zig-out "$BUILD_DIR"
# Verify vcpkg installation
if [[ ! -d "$VCPKG_ROOT/${{ matrix.triplet }}" ]]; then
echo "❌ ERROR: vcpkg triplet directory not found: $VCPKG_ROOT/${{ matrix.triplet }}"
echo "Available directories in $VCPKG_ROOT:"
ls -la "$VCPKG_ROOT/" || echo "VCPKG_ROOT directory not found"
exit 1
fi
# Show available libraries
echo "📚 Available pkg-config libraries:"
pkg-config --list-all | grep -E "(spng|jpeg|tiff|zlib)" || echo "No image libraries found via pkg-config"
# Build with Zig (outputs to default zig-out/)
echo "🚀 Starting Zig build..."
if ! zig build -Dtarget=${{ matrix.target }} -Doptimize=ReleaseFast --verbose; then
echo "❌ Build failed for target ${{ matrix.target }}"
echo "📋 Build environment info:"
echo " - Runner OS: ${{ runner.os }}"
echo " - Target: ${{ matrix.target }}"
echo " - Triplet: ${{ matrix.triplet }}"
exit 1
fi
echo "✅ Build completed successfully"
echo "🔍 Checking default zig-out directory:"
ls -la zig-out/bin/ || echo "Default zig-out directory not found"
# Move binary to isolated directory to prevent matrix job conflicts
echo "📦 Moving binary to isolated directory: $BUILD_DIR/bin/"
mkdir -p "$BUILD_DIR/bin"
echo "🔍 What did Zig actually produce?"
ls -la zig-out/bin/
# Find and move the actual binary (handle various naming)
if [[ -f "zig-out/bin/odiff.exe" ]]; then
mv "zig-out/bin/odiff.exe" "$BUILD_DIR/bin/odiff.exe"
echo "✅ Moved zig-out/bin/odiff.exe to $BUILD_DIR/bin/odiff.exe"
elif [[ -f "zig-out/bin/odiff" ]]; then
mv "zig-out/bin/odiff" "$BUILD_DIR/bin/odiff"
echo "✅ Moved zig-out/bin/odiff to $BUILD_DIR/bin/odiff"
else
# Find any file that starts with "odiff" (handle edge cases)
FOUND_BINARY=$(find zig-out/bin/ -name "odiff*" -type f | head -1)
if [[ -n "$FOUND_BINARY" ]]; then
BASENAME=$(basename "$FOUND_BINARY")
mv "$FOUND_BINARY" "$BUILD_DIR/bin/$BASENAME"
echo "✅ Moved $FOUND_BINARY to $BUILD_DIR/bin/$BASENAME"
else
echo "❌ ERROR: No binary found in zig-out/bin/"
ls -la zig-out/bin/ || echo "zig-out/bin/ directory not found"
exit 1
fi
fi
echo "📁 Final build output location: $BUILD_DIR/bin/"
ls -la "$BUILD_DIR/bin/"
echo "🔍 Checking if there are any committed binaries in repo:"
find . -name "odiff*" -type f -exec file {} \; || echo "No odiff binaries found in repo"
- name: Validate binary architecture
shell: bash
run: |
echo "Validating binary for target: ${{ matrix.target }}"
BUILD_DIR="zig-out-${{ matrix.artifact }}"
# Find the correct binary path (handle various binary names)
echo "🔍 Checking for binaries in $BUILD_DIR/bin/"
echo " - Looking for $BUILD_DIR/bin/odiff.exe: $(test -f "$BUILD_DIR/bin/odiff.exe" && echo "EXISTS" || echo "NOT FOUND")"
echo " - Looking for $BUILD_DIR/bin/odiff: $(test -f "$BUILD_DIR/bin/odiff" && echo "EXISTS" || echo "NOT FOUND")"
if [[ -f "$BUILD_DIR/bin/odiff.exe" ]]; then
BINARY_PATH="$BUILD_DIR/bin/odiff.exe"
echo "✅ Using odiff.exe"
elif [[ -f "$BUILD_DIR/bin/odiff" ]]; then
BINARY_PATH="$BUILD_DIR/bin/odiff"
echo "✅ Using odiff"
else
# Find any file that starts with "odiff" (handle edge cases)
echo "🔍 Searching for any odiff* files..."
FOUND_BINARY=$(find "$BUILD_DIR/bin/" -name "odiff*" -type f | head -1)
if [[ -n "$FOUND_BINARY" ]]; then
BINARY_PATH="$FOUND_BINARY"
echo "✅ Using found binary: $FOUND_BINARY"
else
echo "ERROR: No binary found in $BUILD_DIR/bin/"
echo "Contents of $BUILD_DIR/bin/:"
ls -la "$BUILD_DIR/bin/" || echo "Build directory not found"
exit 1
fi
fi
echo "Found binary at: $BINARY_PATH"
# Get binary info
echo "Binary info:"
file "$BINARY_PATH"
# Validate target architecture matches expectation
BINARY_INFO=$(file "$BINARY_PATH")
case "${{ matrix.target }}" in
*"windows"*)
if [[ ! "$BINARY_INFO" =~ "PE32+".*"x86-64" ]] && [[ ! "$BINARY_INFO" =~ "PE32+".*"Aarch64" ]] && [[ ! "$BINARY_INFO" =~ "COFF" ]]; then
echo "ERROR: Expected Windows PE/COFF executable, got: $BINARY_INFO"
exit 1
fi
;;
*"linux"*)
if [[ ! "$BINARY_INFO" =~ "ELF".*"x86-64" ]] && [[ ! "$BINARY_INFO" =~ "ELF".*"aarch64" ]]; then
echo "ERROR: Expected Linux ELF executable, got: $BINARY_INFO"
exit 1
fi
;;
*"macos"*)
if [[ ! "$BINARY_INFO" =~ "Mach-O".*"x86_64" ]] && [[ ! "$BINARY_INFO" =~ "Mach-O".*"arm64" ]]; then
echo "ERROR: Expected macOS Mach-O executable, got: $BINARY_INFO"
exit 1
fi
;;
*)
echo "WARNING: Unknown target type, skipping validation"
;;
esac
echo "✅ Binary validation passed for ${{ matrix.target }}"
- name: Test binary execution (if possible)
shell: bash
run: |
# Only test execution on native or compatible targets
SHOULD_TEST=false
case "${{ matrix.target }}" in
"x86_64-linux-gnu")
if [[ "${{ runner.os }}" == "Linux" ]]; then SHOULD_TEST=true; fi
;;
"x86_64-windows-gnu")
if [[ "${{ runner.os }}" == "Windows" ]]; then SHOULD_TEST=true; fi
;;
"x86_64-macos")
if [[ "${{ runner.os }}" == "macOS" ]]; then SHOULD_TEST=true; fi
;;
"aarch64-macos")
if [[ "${{ runner.os }}" == "macOS" ]] && [[ "$(uname -m)" == "arm64" ]]; then SHOULD_TEST=true; fi
;;
esac
if [[ "$SHOULD_TEST" == "true" ]]; then
echo "Testing binary execution on compatible platform"
BUILD_DIR="zig-out-${{ matrix.artifact }}"
if [[ "${{ matrix.target }}" == *"windows"* ]]; then
"$BUILD_DIR/bin/odiff.exe" --help || "$BUILD_DIR/bin/odiff" --help
else
"$BUILD_DIR/bin/odiff" --help
fi
echo "✅ Binary execution test passed"
else
echo "⏭️ Skipping execution test (cross-compiled binary)"
fi
- name: Copy binary to artifact name
run: |
BUILD_DIR="zig-out-${{ matrix.artifact }}"
# Copy the correct binary based on target
if [[ "${{ matrix.target }}" == *"windows"* ]]; then
# For Windows targets, check if .exe exists
if [[ -f "$BUILD_DIR/bin/odiff.exe" ]]; then
cp "$BUILD_DIR/bin/odiff.exe" "odiff-${{ matrix.artifact }}"
elif [[ -f "$BUILD_DIR/bin/odiff" ]]; then
cp "$BUILD_DIR/bin/odiff" "odiff-${{ matrix.artifact }}"
else
echo "ERROR: No binary found for Windows target in $BUILD_DIR/bin/"
ls -la "$BUILD_DIR/bin/" || echo "Build directory not found"
exit 1
fi
else
# For Unix targets
if [[ -f "$BUILD_DIR/bin/odiff" ]]; then
cp "$BUILD_DIR/bin/odiff" "odiff-${{ matrix.artifact }}"
else
echo "ERROR: No binary found for Unix target in $BUILD_DIR/bin/"
ls -la "$BUILD_DIR/bin/" || echo "Build directory not found"
exit 1
fi
fi
echo "✅ Binary copied to odiff-${{ matrix.artifact }}"
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
if-no-files-found: error
name: odiff-${{ matrix.artifact }}
path: odiff-${{ matrix.artifact }}
retention-days: 14
test:
runs-on: ubuntu-latest
name: Run Tests
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Zig
uses: goto-bus-stop/setup-zig@v2
with:
version: 0.15.1
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y libspng-dev libjpeg-dev libtiff-dev pkg-config
- name: Run tests
run: |
zig build test-all
spellcheck:
name: Spell check
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Run typos
uses: crate-ci/typos@master
with:
config: ./typos.toml
publish:
name: Publish release
needs: [build, test]
if: startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Download built binaries
uses: actions/download-artifact@v4
with:
pattern: odiff-*
merge-multiple: true
path: npm_package/raw_binaries
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
always-auth: true
registry-url: "https://registry.npmjs.org"
cache-dependency-path: "package-lock.json"
- name: Publish npm package
working-directory: npm_package
run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Create github release
uses: softprops/action-gh-release@v1
with:
files: "npm_package/raw_binaries/*"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}