diff --git a/.ci/build.sh b/.ci/build.sh new file mode 100644 index 0000000..a0f4d05 --- /dev/null +++ b/.ci/build.sh @@ -0,0 +1,85 @@ +#!/usr/bin/env bash +set -euo pipefail + +: "${TARGET:?TARGET env var is required}" +: "${TAG_NAME:?TAG_NAME env var is required}" + +echo "▶ Building target='$TARGET' tag='$TAG_NAME'" + +BUILD_CDYLIB=true + +# ── Per-target naming conventions ──────────────────────────────────────────── +case "$TARGET" in + *windows-msvc*) + LIB_PREFIX="" + LIB_EXT=".dll" + STATIC_LIB_PREFIX="" + STATIC_LIB_EXT=".lib" + ;; + *windows-gnu*) + LIB_PREFIX="" + LIB_EXT=".dll" + STATIC_LIB_PREFIX="lib" + STATIC_LIB_EXT=".a" + ;; + *apple*) + LIB_PREFIX="lib" + LIB_EXT=".dylib" + STATIC_LIB_PREFIX="lib" + STATIC_LIB_EXT=".a" + ;; + *musl*) + BUILD_CDYLIB=false + LIB_PREFIX="lib" + LIB_EXT=".so" + STATIC_LIB_PREFIX="lib" + STATIC_LIB_EXT=".a" + ;; + *) + LIB_PREFIX="lib" + LIB_EXT=".so" + STATIC_LIB_PREFIX="lib" + STATIC_LIB_EXT=".a" + ;; +esac + +# ── Patch Cargo.toml ────────────────────────────────────────────────────────── +CRATE_TYPES="\"rlib\", \"staticlib\"" +if [ "$BUILD_CDYLIB" = true ]; then + CRATE_TYPES="\"rlib\", \"cdylib\", \"staticlib\"" +fi + +# macOS (Darwin) requires an empty string for the -i flag +if [[ "$(uname)" == "Darwin" ]]; then + sed -i '' "s/crate-type = \[\"rlib\"\]/crate-type = \[$CRATE_TYPES\]/" Cargo.toml +else + # Linux and Windows (Git Bash) + sed -i "s/crate-type = \[\"rlib\"\]/crate-type = \[$CRATE_TYPES\]/" Cargo.toml +fi + +# ── Build ───────────────────────────────────────────────────────────────────── +# Use native cargo on macOS and Windows runners (where SDKs are present). +# Use cargo-zigbuild on Linux runners for cross-compilation. +if [[ "$(uname)" == "Darwin" ]] || [[ "$(uname)" == *"MINGW"* ]] || [[ "$(uname)" == *"MSYS"* ]]; then + cargo build --release --target "$TARGET" +else + cargo zigbuild --release --target "$TARGET" +fi + +# ── Collect into staging ───────────────────────────────────────────────────── +STAGING="staging/${TARGET}" +mkdir -p "$STAGING" artifacts + +if [ "$BUILD_CDYLIB" = true ]; then + cp "target/${TARGET}/release/${LIB_PREFIX}spf${LIB_EXT}" "$STAGING/" +fi +cp "target/${TARGET}/release/${STATIC_LIB_PREFIX}spf${STATIC_LIB_EXT}" "$STAGING/" +cp spf.h "$STAGING/" +cp LICENSE-APACHE "$STAGING/" + +# ── Package ─────────────────────────────────────────────────────────────────── +TARBALL="artifacts/spf.${TAG_NAME}.${TARGET}.tar.gz" +tar -czf "$TARBALL" -C staging "${TARGET}" +rm -rf staging + +echo "✅ Artifact ready: $TARBALL" \ No newline at end of file diff --git a/.ci/build_tarballs.jl b/.ci/build_tarballs.jl deleted file mode 100644 index 25b4ca0..0000000 --- a/.ci/build_tarballs.jl +++ /dev/null @@ -1,76 +0,0 @@ -# Note that this script can accept some limited command-line arguments, run -# `julia build_tarballs.jl --help` to see a usage message. -using Pkg -Pkg.instantiate() -try - using BinaryBuilder -catch - Pkg.add("BinaryBuilder") - using BinaryBuilder -end - -name = "spf" -version = VersionNumber(ENV["VERSION"]) -sha = ENV["SHA"] - -# Collection of sources required to complete build -sources = [ - GitSource("https://github.com/SimplePixelFont/spf.rs", sha) - DirectorySource("target") -] - -# Bash recipe for building across all platforms -script = raw""" -cd $WORKSPACE/srcdir -cd spf.rs -mkdir target -cargo rustc --release --no-default-features --features "ffi,std" --crate-type cdylib --crate-type staticlib -- -C target-feature=-crt-static - -if [[ "${rust_target}" == "x86_64-pc-windows-gnu" ]]; then - install -D -m 755 "target/${rust_target}/release/spf.${dlext}" "${libdir}/libspf.${dlext}" -elif [[ "${rust_target}" == "i686-unknown-linux-musl" || - "${rust_target}" == "x86_64-unknown-linux-musl" || - "${rust_target}" == "aarch64-unknown-linux-musl" || - "${rust_target}" == "arm-unknown-linux-musleabihf" || - "${rust_target}" == "armv7-unknown-linux-musleabihf" ]]; then - install -D -m 755 "target/${rust_target}/release/deps/libspf.${dlext}" "${libdir}/libspf.${dlext}" -else - install -D -m 755 "target/${rust_target}/release/libspf.${dlext}" "${libdir}/libspf.${dlext}" -fi - -install -D -m 755 "../spf.h" "${includedir}/spf.h" -install_license LICENSE-APACHE -""" - -# These are the platforms we will build for by default, unless further -# platforms are passed in on the command line -platforms = [ - Platform("armv7l", "linux"; call_abi="eabihf", libc="glibc"), - Platform("armv7l", "linux"; call_abi="eabihf", libc="musl"), - Platform("i686", "linux"; libc="musl"), - Platform("i686", "linux"; libc="glibc"), - Platform("armv6l", "linux"; call_abi="eabihf", libc="glibc"), - Platform("powerpc64le", "linux"; libc="glibc"), - Platform("x86_64", "macos";), - Platform("x86_64", "linux"; libc="glibc"), - Platform("aarch64", "linux"; libc="musl"), - Platform("armv6l", "linux"; call_abi="eabihf", libc="musl"), - Platform("x86_64", "linux"; libc="musl"), - Platform("x86_64", "freebsd";), - Platform("x86_64", "windows";), - Platform("aarch64", "macos";), - Platform("aarch64", "linux"; libc="glibc") -] - - -# The products that we will ensure are always built -products = [ - LibraryProduct("libspf", :libspf) -] - -# Dependencies that must be installed before this package can be built -dependencies = Dependency[ -] - -# Build the tarballs, and possibly a `build.jl` as well. -build_tarballs(ARGS, name, version, sources, script, platforms, products, dependencies; julia_compat="1.6", compilers=[:rust, :c]) diff --git a/.ci/release.jl b/.ci/release.jl deleted file mode 100644 index 9b034ba..0000000 --- a/.ci/release.jl +++ /dev/null @@ -1,93 +0,0 @@ -### Version Number ### -tag_name = ENV["TAG_NAME"] -version = tag_name -if startswith(tag_name, "v") - version = tag_name[2:end] -end -sha = ENV["SHA"] - -### Header Files Generation (used by BinaryBuilder) ### -run(`sh -c "cargo install cbindgen"`) -run(`sh -c "cbindgen --output target/spf.h --lang c --cpp-compat"`) - -### BinaryBuilder Builds ### - -jl_platforms = [ - "'i686-linux-gnu'" - "'x86_64-linux-gnu'" - "'aarch64-linux-gnu'" - "'armv6l-linux-gnueabihf'" - "'armv7l-linux-gnueabihf'" - "'powerpc64le-linux-gnu'" - # "'riscv64-linux-gnu'" no rust toolchain for this platform - "'i686-linux-musl'" - "'x86_64-linux-musl'" - "'aarch64-linux-musl'" - "'armv6l-linux-musleabihf'" - "'armv7l-linux-musleabihf'" - "'x86_64-apple-darwin'" - "'aarch64-apple-darwin'" - "'x86_64-unknown-freebsd'" - # "'aarch64-unknown-freebsd'" no rust toolchain for this platform - # "'i686-w64-mingw32'" fails to build on this platform - "'x86_64-w64-mingw32'" -] - -# Github Actions runs out of disk space: The solution is to delete all artifacts after each platform build. -for platform in jl_platforms - run(`sh -c "VERSION='$version' SHA='$sha' BINARYBUILDER_RUNNER='privileged' BINARYBUILDER_AUTOMATIC_APPLE=true julia -- ./.ci/build_tarballs.jl $platform"`) - run(`sh -c "sudo rm -rf '/home/runner/.julia/artifacts/'"`) -end - -### Extra Platform Builds ### - -# Windows MSVC build -run(`sh -c "cargo install --locked cargo-xwin"`) -run(`sh -c "rustup target add x86_64-pc-windows-msvc"`) -run(`sh -c "cargo xwin rustc --release --target x86_64-pc-windows-msvc --no-default-features --features 'ffi,std' --crate-type cdylib --crate-type staticlib"`) - -# Will be figured out during 0.6.x -# WASM build -# run(`sh -c "rustup target add wasm32-unknown-unknown"`) -# run(`sh -c "cargo build --target wasm32-unknown-unknown --release"`) - -# Will be figured out during 0.6.x -### WASM-Bindgen Generation ### -# run(`sh -c "cargo install wasm-bindgen-cli"`) -# run(`sh -c "wasm-bindgen --out-dir target/wasm32-unknown-unknown/dist target/wasm32-unknown-unknown/release/spf.wasm"`) - -### Bring Builds Together ### -mkdir("artifacts") -for platform in jl_platforms - platform = platform[2:end-1] - mv("products/spf.$tag_name.$platform.tar.gz", "artifacts/spf.$tag_name.$platform.tar.gz") -end - -using Pkg -Pkg.add("Tar") -using Tar - -mkdir("target/x86_64-pc-windows-msvc/release/spf.$tag_name.x86_64-w64-msvc") -mkpath("target/x86_64-pc-windows-msvc/release/spf.$tag_name.x86_64-w64-msvc/lib") -mkpath("target/x86_64-pc-windows-msvc/release/spf.$tag_name.x86_64-w64-msvc/include") -mkpath("target/x86_64-pc-windows-msvc/release/spf.$tag_name.x86_64-w64-msvc/share/licenses/spf") - -mv("target/x86_64-pc-windows-msvc/release/spf.dll", "target/x86_64-pc-windows-msvc/release/spf.$tag_name.x86_64-w64-msvc/lib/spf.dll") -cp("target/spf.h", "target/x86_64-pc-windows-msvc/release/spf.$tag_name.x86_64-w64-msvc/include/spf.h") -cp("LICENSE-APACHE", "target/x86_64-pc-windows-msvc/release/spf.$tag_name.x86_64-w64-msvc/share/licenses/spf/LICENSE-APACHE") - -Tar.create("target/x86_64-pc-windows-msvc/release/spf.$tag_name.x86_64-w64-msvc", "artifacts/spf.$tag_name.x86_64-w64-msvc.tar.gz") - -# Will be figured out during 0.6.x -# mkdir("target/wasm32-unknown-unknown/release/spf.v0.5.0.wasm32-unknown-unknown") -# mkpath("target/wasm32-unknown-unknown/release/spf.v0.5.0.wasm32-unknown-unknown/lib") -# mkpath("target/wasm32-unknown-unknown/release/spf.v0.5.0.wasm32-unknown-unknown/include") -# mkpath("target/wasm32-unknown-unknown/release/spf.v0.5.0.wasm32-unknown-unknown/share/licenses/spf") - -# mv("target/wasm32-unknown-unknown/release/spf.wasm", "target/wasm32-unknown-unknown/release/spf.v0.5.0.wasm32-unknown-unknown/lib/spf_pure.wasm") -# mv("target/wasm32-unknown-unknown/dist/spf_bg.wasm", "target/wasm32-unknown-unknown/release/spf.v0.5.0.wasm32-unknown-unknown/lib/spf_bg.wasm") -# mv("target/wasm32-unknown-unknown/dist/spf.js", "target/wasm32-unknown-unknown/release/spf.v0.5.0.wasm32-unknown-unknown/include/spf.js") -# mv("target/wasm32-unknown-unknown/dist/spf_bg.js", "target/wasm32-unknown-unknown/release/spf.v0.5.0.wasm32-unknown-unknown/include/spf_bg.js") -# cp("LICENSE-APACHE", "target/wasm32-unknown-unknown/release/spf.v0.5.0.wasm32-unknown-unknown/share/licenses/spf/LICENSE-APACHE") - -# Tar.create("target/wasm32-unknown-unknown/release/spf.v0.5.0.wasm32-unknown-unknown", "artifacts/spf.v0.5.0.wasm32-unknown-unknown.tar.gz") diff --git a/.github/workflows/builder.yml b/.github/workflows/builder.yml index 15852a1..af1d15c 100644 --- a/.github/workflows/builder.yml +++ b/.github/workflows/builder.yml @@ -4,73 +4,134 @@ on: workflow_dispatch: inputs: tag_name: - description: "The tag name of the release to add assets to (e.g., v1.0.0)" + description: "Tag name (e.g. v1.0.0)" required: true - asset_folder: - description: "Path to the folder containing assets (relative to repo root, e.g., artifacts)" - required: true - default: "artifacts" # Optional: Set a default folder jobs: - builder: - name: Upload release assets + parse-tag: + name: Parse Tag runs-on: ubuntu-latest outputs: - outcome: ${{ steps.upload.outcome }} - permissions: - contents: write # Required to write release assets - + type: ${{ steps.parse.outputs.type }} + version: ${{ steps.parse.outputs.version }} + sha: ${{ steps.parse.outputs.sha }} steps: - - name: Checkout repository - uses: actions/checkout@v4 + - uses: actions/checkout@v4 with: fetch-depth: 0 - - - name: Get SHA of tag_name - id: get_sha + - name: Parse tag + id: parse run: | - SHA=$(git rev-list -n 1 ${{ inputs.tag_name }}) + TAG="${{ inputs.tag_name }}" + SHA=$(git rev-list -n 1 "$TAG") echo "sha=$SHA" >> $GITHUB_OUTPUT + VERSION=$(echo "$TAG" | awk -F'-v' '{print $2}') + echo "type=library" >> $GITHUB_OUTPUT + echo "version=$VERSION" >> $GITHUB_OUTPUT + + header-file: + name: Generate ABI Header + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - name: Install cbindgen + run: cargo install cbindgen --locked + - name: Generate header + run: cbindgen --output spf.h --lang c --cpp-compat + - name: Upload ABI Header + uses: actions/upload-artifact@v4 + with: + name: spf-header + path: spf.h - - uses: julia-actions/setup-julia@v2 + build: + name: Build (${{ matrix.target }}) + needs: [parse-tag, header-file] + strategy: + fail-fast: false + matrix: + include: + # Linux GNU + - { target: x86_64-unknown-linux-gnu, os: ubuntu-latest } + - { target: aarch64-unknown-linux-gnu, os: ubuntu-latest } + - { target: i686-unknown-linux-gnu, os: ubuntu-latest } + - { target: armv7-unknown-linux-gnueabihf, os: ubuntu-latest } + # Linux MUSL + - { target: x86_64-unknown-linux-musl, os: ubuntu-latest } + - { target: aarch64-unknown-linux-musl, os: ubuntu-latest } + # Windows GNU (Cross-compiles easily from Linux) + - { target: x86_64-pc-windows-gnu, os: ubuntu-latest } + # Windows MSVC (Switched to windows-latest to use native MSVC tools) + - { target: x86_64-pc-windows-msvc, os: windows-latest } + # Other + - { target: x86_64-unknown-freebsd, os: ubuntu-latest } + # macOS + - { target: x86_64-apple-darwin, os: macos-latest } + - { target: aarch64-apple-darwin, os: macos-latest } + runs-on: ${{ matrix.os }} + env: + TAG_NAME: ${{ inputs.tag_name }} + TARGET: ${{ matrix.target }} + + steps: + - uses: actions/checkout@v4 with: - version: "1.7" - - run: TAG_NAME='${{ inputs.tag_name }}' SHA='${{ steps.get_sha.outputs.sha }}' julia -- ./.ci/release.jl + ref: ${{ needs.parse-tag.outputs.sha }} + + - name: Download ABI Header + uses: actions/download-artifact@v4 + with: + name: spf-header + - uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ matrix.target }} - - name: List files in folder + # Install zigbuild only on Linux runners + - name: Install Zig + cargo-zigbuild + if: matrix.os == 'ubuntu-latest' run: | - echo "Looking for assets in folder: ${{ github.workspace }}/${{ inputs.asset_folder }}" - ls -l "${{ github.workspace }}/${{ inputs.asset_folder }}" + pip install ziglang --quiet + cargo install cargo-zigbuild --locked + + - name: Build + shell: bash + run: bash .ci/build.sh - - name: Upload assets to release + - uses: actions/upload-artifact@v4 + with: + name: artifact-${{ matrix.target }} + path: artifacts/*.tar.gz + + release: + name: Upload Release Assets + needs: [parse-tag, build] + outputs: + outcome: ${{ steps.upload.outcome }} + if: always() && needs.build.result != 'cancelled' + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/download-artifact@v4 + with: + pattern: artifact-* + merge-multiple: true + path: artifacts/ + - uses: softprops/action-gh-release@v1 id: upload - env: - GITHUB_TOKEN: ${{ secrets.TOKEN }} - TAG_NAME: ${{ inputs.tag_name }} - ASSET_FOLDER: ${{ inputs.asset_folder }} - run: | - # Check if folder exists and is not empty - if [ -d "$ASSET_FOLDER" ] && [ "$(ls -A $ASSET_FOLDER)" ]; then - echo "Uploading assets from $ASSET_FOLDER to release $TAG_NAME..." - # Use find to handle files potentially containing spaces or special characters - # Upload files one by one to avoid issues with too many arguments for gh release upload * - find "$ASSET_FOLDER" -maxdepth 1 -type f -print0 | while IFS= read -r -d $'\0' file; do - echo "Uploading '$file'..." - gh release upload "$TAG_NAME" "$file" --clobber - done - echo "Asset upload complete." - else - echo "Warning: Asset folder '$ASSET_FOLDER' not found or is empty. No assets uploaded." - fi + with: + tag_name: ${{ inputs.tag_name }} + files: artifacts/* badge: + name: Update Badge runs-on: ubuntu-latest - needs: builder + needs: release if: always() - steps: - name: passing - if: needs.builder.outputs.outcome == 'success' + if: needs.release.outputs.outcome == 'success' uses: SimplePixelFont/badge-spf-action@main with: filename: compatibility.png @@ -80,9 +141,8 @@ jobs: color: "#00bfa3" logo: "https://raw.githubusercontent.com/The-Nice-One/GalleryArt/refs/heads/main/emojis_flattened/version_control.png" font: "https://raw.githubusercontent.com/SimplePixelFont/web-spf/refs/heads/main/Monogram5x8-light.spf" - - name: failing - if: needs.builder.outputs.outcome == 'failure' || needs.builder.outputs.outcome == 'cancelled' || needs.builder.outputs.outcome == 'skipped' + if: needs.release.outputs.outcome != 'success' uses: SimplePixelFont/badge-spf-action@main with: filename: compatibility.png @@ -92,11 +152,10 @@ jobs: color: "#d94a69" logo: "https://raw.githubusercontent.com/The-Nice-One/GalleryArt/refs/heads/main/emojis_flattened/version_control.png" font: "https://raw.githubusercontent.com/SimplePixelFont/web-spf/refs/heads/main/Monogram5x8-light.spf" - - name: deploy uses: exuanbo/actions-deploy-gist@v1 with: token: ${{ secrets.GIST_SECRET }} gist_id: cfebb0fe555ac7e77ada109c469cdeb4 file_path: compatibility.png - file_type: binary + file_type: binary \ No newline at end of file