Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 70 additions & 23 deletions .github/workflows/CD.yml
Original file line number Diff line number Diff line change
@@ -1,43 +1,90 @@
# Binding Simple CD - Template

# This template is for pure .NET bindings (no XML update) to publish NuGets to Nuget.org.
# Copy this file to your repository as `.github/workflows/CD.yml` and customize the inputs below.
# Keeps this binding on the latest stable meshoptimizer release, header and
# native libraries together.
#
# Three jobs rather than one, because the two halves cannot share a runner: the
# native libraries need a matrix across Linux, Windows and macOS, and the
# generator and packaging need a single machine holding the whole tree.
#
# `resolve` runs first and alone so exactly one place decides which revision this
# run is about. If the header and the binaries each worked it out for themselves,
# a release published midway through would leave them on different versions --
# and that particular mismatch is invisible: P/Invoke binds late, so the package
# compiles, passes CI, publishes, and throws in the consumer's application.

name: CD

on:
schedule:
# Monthly, in step with the rest of the fleet.
- cron: '0 2 1 * *'
workflow_dispatch:
inputs:
skip-assets-publishing:
description: 'Skip assets publishing'
required: false
type: boolean
default: false
force-natives:
description: 'Rebuild the native libraries even when the release has not moved'
required: false
type: boolean
default: false
force-publish:
description: 'Publish even when the generated code is unchanged'
required: false
type: boolean
default: false

jobs:
cd:
resolve:
if: github.event_name != 'schedule' || github.ref == 'refs/heads/main'
uses: EvergineTeam/Evergine.Bindings/.github/workflows/binding-simple-cd.yml@v1
uses: EvergineTeam/Evergine.Bindings/.github/workflows/binding-resolve-upstream.yml@v1

natives:
needs: resolve
# Five platform builds is not something to spend on a maybe, hence resolving
# first and asking.
if: needs.resolve.outputs.ref_moved == 'true' || inputs.force-natives
uses: ./.github/workflows/meshopt-cmake.yml
with:
ref: ${{ needs.resolve.outputs.resolved_ref }}

cd:
needs: [resolve, natives]
# `natives` is skipped whenever the release has not moved, and a skipped
# dependency would otherwise take this job with it. The run still has to
# reach the CD so it can report the no-op.
if: always() && needs.resolve.result == 'success' && needs.natives.result != 'failure' && !cancelled()
uses: EvergineTeam/Evergine.Bindings/.github/workflows/binding-tracked-cd.yml@v1
with:
generator-project: "MeshOptimizerGen/MeshOptimizerGen/MeshOptimizerGen.csproj" # Path to your generator .csproj
generator-name: "MeshOptimizer" # Name of your generator executable
binding-project: "MeshOptimizerGen/Evergine.Bindings.MeshOptimizer/Evergine.Bindings.MeshOptimizer.csproj" # Path to your binding .csproj
target-framework: "net10.0" # Target framework for generator/binding
dotnet-version: "10.x" # .NET SDK version
nuget-version: "6.x" # NuGet CLI version
runtime-identifier: "linux-x64" # Runtime identifier (win-x64, linux-x64, etc.)
build-configuration: "Release" # Build configuration (Release, Debug, etc.)
revision: ${{ github.run_number }} # Revision for date-based version (bindings style). Use with bindings.
publish-enabled: ${{ !inputs.skip-assets-publishing }} # Publish NuGets to Nuget.org
enable-email-notifications: true # Enable email notifications on failure
generator-project: "MeshOptimizerGen/MeshOptimizerGen/MeshOptimizerGen.csproj"
generator-name: "MeshOptimizer"
binding-project: "MeshOptimizerGen/Evergine.Bindings.MeshOptimizer/Evergine.Bindings.MeshOptimizer.csproj"
target-framework: "net10.0"
dotnet-version: "10.x"
nuget-version: "6.x"
# Generation runs on Windows because that is where the CppAst generator can
# read the header at all. The libclang shipped in the NuGet package carries
# no system include paths and none of its own builtin headers, so on Linux
# `#include <assert.h>` and `<stddef.h>` do not resolve and the parse fails
# before a single binding is produced. RenderDoc.NET and KTX.NET generate on
# Windows for the same reason.
#
# This does not constrain the package: the five native libraries are built
# in their own matrix job, and `runtime-identifier` here only feeds the
# generator's publish, never the packing.
runner-os: windows-latest
runtime-identifier: "win-x64"
build-configuration: "Release"
revision: ${{ github.run_number }}
# Every artifact already carries its final repository path, so unpacking
# them over the tree is the whole mapping.
natives-artifact-pattern: "natives-*"
force-publish: ${{ inputs.force-publish || false }}
publish-enabled: ${{ !inputs.skip-assets-publishing }}
enable-email-notifications: true
secrets:
NUGET_UPLOAD_TOKEN: ${{ secrets.EVERGINE_NUGETORG_TOKEN }}
WAVE_SENDGRID_TOKEN: ${{ secrets.WAVE_SENDGRID_TOKEN }}
EVERGINE_EMAILREPORT_LIST: ${{ secrets.EVERGINE_EMAILREPORT_LIST }}
EVERGINE_EMAIL: ${{ secrets.EVERGINE_EMAIL }}

# Tips:
# - For direct version (add-ons style):
# version: "3.4.22.288-local"
# - For date-based version (bindings style):
# revision: "" # Uses github.run_number or custom logic
8 changes: 7 additions & 1 deletion .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,13 @@ jobs:
generator-name: "MeshOptimizer" # Name of your generator executable
binding-project: "MeshOptimizerGen/Evergine.Bindings.MeshOptimizer/Evergine.Bindings.MeshOptimizer.csproj" # Path to your binding .csproj
target-framework: "net10.0" # Target framework for generator/binding
runtime-identifier: "linux-x64" # Runtime identifier (win-x64, linux-x64, etc.)
# Windows, because the CppAst generator cannot read the header anywhere
# else: the libclang in the NuGet package has no system include paths and
# none of its own builtin headers, so <assert.h> and <stddef.h> fail to
# resolve on Linux. CI must match CD or it validates a build the CD will
# not reproduce.
runner-os: windows-latest
runtime-identifier: "win-x64" # Runtime identifier (win-x64, linux-x64, etc.)
build-configuration: "Release" # Build configuration (Release, Debug, etc.)
nuget-artifacts: ${{ inputs.publish-artifacts || false }} # Upload NuGets as workflow artifacts
revision: ${{ github.run_number }} # Revision for date-based version (bindings style). Use with bindings.
Expand Down
34 changes: 34 additions & 0 deletions .github/workflows/api-gate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Two questions on every pull request, both about contracts nobody can see in a
# diff of 6,000 generated lines.
#
# `api` measures what the change does to the public managed API. `coherence`
# checks that every P/Invoke still resolves in the native libraries the package
# ships, on all five platforms -- the one failure that compiles, passes CI,
# publishes, and only shows up in the consumer's application.
#
# auto-merge stays off here. This binding rebuilds five native libraries when it
# bumps, and that pipeline has run a handful of times; measuring first, merging
# later.

name: API Gate

on:
pull_request:
branches: [ "main" ]

jobs:
api:
uses: EvergineTeam/Evergine.Bindings/.github/workflows/binding-api-gate.yml@v1
with:
binding-project: "MeshOptimizerGen/Evergine.Bindings.MeshOptimizer/Evergine.Bindings.MeshOptimizer.csproj"
auto-merge: false
# meshoptimizer.h reports its own version, so this constant changes on every
# release. The gate counts a changed constant value as a removal, on purpose
# -- that is how a silent renumbering gets caught -- which would make every
# bump "breaking" and the verdict worthless. Exempting it leaves the verdict
# meaning what it says.
exempt-symbols: |
\.VERSION =

coherence:
uses: EvergineTeam/Evergine.Bindings/.github/workflows/binding-native-coherence.yml@v1
113 changes: 97 additions & 16 deletions .github/workflows/meshopt-cmake.yml
Original file line number Diff line number Diff line change
@@ -1,36 +1,80 @@
# Builds the five native libraries this package ships.
#
# Callable from CD.yml, which passes the release the manifest resolved to. The
# header and these binaries must come from the same revision: P/Invoke binds
# late, so a mismatched package compiles, passes CI, publishes, and throws
# EntryPointNotFoundException in the consumer's application. Nothing between
# here and there would notice.
#
# The cmake invocations stay in this repository rather than in the toolbox.
# meshoptimizer's build has nothing in common with KTX's or xatlas's, and
# parameterising all three into one reusable workflow would produce something
# with an escape hatch per project -- worse than three small honest files.

name: Build meshoptimizer Libraries

on:
workflow_call:
inputs:
ref:
description: 'meshoptimizer revision to build'
required: true
type: string
workflow_dispatch:
inputs:
ref:
description: 'meshoptimizer revision to build (tag, branch or SHA)'
required: true
type: string
default: 'v1.2'

jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
# One platform failing must not leave the others publishing a half-updated
# set of binaries.
fail-fast: true
matrix:
include:
- os: ubuntu-latest
arch: x64
cmake-arch: x64
rid: linux-x64
libname: libmeshoptimizer.so
- os: ubuntu-latest
arch: arm64
cmake-arch: aarch64
rid: linux-arm64
libname: libmeshoptimizer.so
- os: windows-latest
arch: x64
cmake-arch: x64
rid: win-x64
libname: meshoptimizer.dll
- os: windows-latest
arch: arm64
cmake-arch: ARM64
rid: win-arm64
libname: meshoptimizer.dll
- os: macos-latest
arch: arm64
cmake-arch: arm64
rid: osx-arm64
libname: libmeshoptimizer.dylib

steps:
- name: Checkout meshoptimizer
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
repository: zeux/meshoptimizer
ref: v1.0
ref: ${{ inputs.ref }}

- name: Record what is being built
shell: bash
run: |
echo "Building ${{ inputs.ref }} for ${{ matrix.rid }}"
grep -m1 MESHOPTIMIZER_VERSION src/meshoptimizer.h

- name: Install dependencies on Ubuntu
if: matrix.os == 'ubuntu-latest'
Expand Down Expand Up @@ -80,23 +124,60 @@ jobs:
- name: Build meshoptimizer
run: cmake --build build --config Release

- name: Upload meshoptimizer.dll (Windows)
if: matrix.os == 'windows-latest'
uses: actions/upload-artifact@v4
with:
name: meshoptimizer-${{ matrix.arch }}-dll
path: build/**/*.dll
# Staged into the exact layout the package expects, so the CD can unpack
# every platform's artifact over the project directory and be done. The
# previous version uploaded `build/**/*.dll`, whose intermediate paths
# differ per platform, and a human mapped them by hand afterwards.
- name: Stage into the runtimes layout
shell: bash
run: |
set -euo pipefail
dest="staged/MeshOptimizerGen/Evergine.Bindings.MeshOptimizer/runtimes/${{ matrix.rid }}/native"
mkdir -p "$dest"
found=$(find build -name '${{ matrix.libname }}' -type f | head -1)
[ -n "$found" ] || { echo "::error::${{ matrix.libname }} not produced for ${{ matrix.rid }}"; exit 1; }
cp "$found" "$dest/${{ matrix.libname }}"
ls -la "$dest"

- name: Upload libmeshoptimizer.so (Linux)
if: matrix.os == 'ubuntu-latest'
# The exported symbols are the contract the managed binding P/Invokes
# against. Recording them makes a rebuild auditable: comparing this list
# against the previous release's is what tells you the toolchain still
# produces an equivalent library, which byte comparison never will --
# different compiler, different date, different bytes, same library.
#
# The dumper reads the binary format directly instead of scraping nm and
# dumpbin. Scraping dumpbin's prose reported the DLL's own name and
# fragments of decorated C++ names as exports, which turned a clean
# comparison into two phantom differences to explain away.
- name: Record exported symbols
shell: bash
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
mkdir -p exports
gh api "repos/EvergineTeam/Evergine.Bindings/contents/tools/dump-exports.py?ref=v1" \
-H 'Accept: application/vnd.github.raw' > dump-exports.py
python -m pip install --quiet pefile pyelftools macholib
lib="staged/MeshOptimizerGen/Evergine.Bindings.MeshOptimizer/runtimes/${{ matrix.rid }}/native/${{ matrix.libname }}"
python dump-exports.py "$lib" meshopt_ > "exports/${{ matrix.rid }}.txt"
count=$(wc -l < "exports/${{ matrix.rid }}.txt")
{
echo "### ${{ matrix.rid }} — $count exported symbols"
echo ""
echo "Built from \`${{ inputs.ref }}\`."
} >> "$GITHUB_STEP_SUMMARY"

- name: Upload native library
uses: actions/upload-artifact@v4
with:
name: meshoptimizer-${{ matrix.arch }}-so
path: build/**/*.so
name: natives-${{ matrix.rid }}
path: staged/
if-no-files-found: error

- name: Upload libmeshoptimizer.dylib (macOS)
if: matrix.os == 'macos-latest'
- name: Upload exported symbols
uses: actions/upload-artifact@v4
with:
name: meshoptimizer-${{ matrix.arch }}-dylib
path: build/**/*.dylib
name: exports-${{ matrix.rid }}
path: exports/
if-no-files-found: error
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ namespace Evergine.Bindings.MeshOptimizer
{
public static partial class MeshOptimizer
{
public const uint VERSION = 1000;
public const uint VERSION = 1020;
}
}
Loading
Loading