Add Windows cross-build and Gitea release; share CI setup
- just windows-deps / windows / package-windows: cross-compile with Odin objects + lld-link against an xwin SDK, static VC runtime - Release workflow runs on v* tags, builds Linux and Windows packages, uploads them to the package registry and creates a Gitea release - Shared toolchain setup as a local composite action Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
9ee2eabe9a
commit
e893c45d4f
6 files changed
+165
-66
No files matched your search
@@ -0,0 +1,44 @@
|
|||||||
|
#
|
||||||
|
# .gitea/actions/setup/action.yaml
|
||||||
|
#
|
||||||
|
# Installs the toolchain shared by all workflows: system packages, Odin and just.
|
||||||
|
#
|
||||||
|
|
||||||
|
name: "Setup Odin toolchain"
|
||||||
|
description: "Install system packages, Odin and just"
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
odin-version:
|
||||||
|
description: "Odin release tag (keep in sync with odin_version in the justfile)"
|
||||||
|
default: "dev-2026-09"
|
||||||
|
packages:
|
||||||
|
description: "Extra apt packages to install"
|
||||||
|
default: ""
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: "composite"
|
||||||
|
steps:
|
||||||
|
- name: Install System Packages
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
SUDO=""; command -v sudo >/dev/null && SUDO=sudo
|
||||||
|
$SUDO apt-get update
|
||||||
|
# clang links Odin programs on Linux; raylib needs X11
|
||||||
|
$SUDO apt-get install -y --no-install-recommends \
|
||||||
|
clang libx11-dev curl ca-certificates unzip ${{ inputs.packages }}
|
||||||
|
|
||||||
|
- name: Install Odin
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
curl -fsSL -o /tmp/odin.tar.gz \
|
||||||
|
https://github.com/odin-lang/Odin/releases/download/${{ inputs.odin-version }}/odin-linux-amd64-${{ inputs.odin-version }}.tar.gz
|
||||||
|
mkdir -p "$HOME/odin"
|
||||||
|
tar -xzf /tmp/odin.tar.gz -C "$HOME/odin" --strip-components=1
|
||||||
|
echo "$HOME/odin" >> "$GITHUB_PATH"
|
||||||
|
|
||||||
|
- name: Install just
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
mkdir -p "$HOME/.local/bin"
|
||||||
|
curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | bash -s -- --to "$HOME/.local/bin"
|
||||||
|
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# .gitea/workflows/gitea-build.yaml
|
# .gitea/workflows/gitea-build.yaml
|
||||||
#
|
#
|
||||||
# Builds and tests on every push and pull request to main and develop.
|
# Checks, tests and builds on every push and pull request to main and develop.
|
||||||
#
|
#
|
||||||
|
|
||||||
name: "Build and Test"
|
name: "Build and Test"
|
||||||
@@ -11,9 +11,6 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
branches: [main, develop]
|
branches: [main, develop]
|
||||||
|
|
||||||
env:
|
|
||||||
ODIN_VERSION: dev-2026-09
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -21,25 +18,8 @@ jobs:
|
|||||||
- name: Checkout Repository
|
- name: Checkout Repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install System Dependencies
|
- name: Setup Toolchain
|
||||||
run: |
|
uses: ./.gitea/actions/setup
|
||||||
SUDO=""; command -v sudo >/dev/null && SUDO=sudo
|
|
||||||
$SUDO apt-get update
|
|
||||||
# clang links Odin programs; raylib needs X11 on Linux
|
|
||||||
$SUDO apt-get install -y --no-install-recommends clang libx11-dev curl ca-certificates
|
|
||||||
|
|
||||||
- name: Install Odin
|
|
||||||
run: |
|
|
||||||
curl -fsSL -o odin.tar.gz https://github.com/odin-lang/Odin/releases/download/$ODIN_VERSION/odin-linux-amd64-$ODIN_VERSION.tar.gz
|
|
||||||
mkdir -p "$HOME/odin"
|
|
||||||
tar -xzf odin.tar.gz -C "$HOME/odin" --strip-components=1
|
|
||||||
echo "$HOME/odin" >> "$GITHUB_PATH"
|
|
||||||
|
|
||||||
- name: Install just
|
|
||||||
run: |
|
|
||||||
mkdir -p "$HOME/.local/bin"
|
|
||||||
curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | bash -s -- --to "$HOME/.local/bin"
|
|
||||||
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
|
|
||||||
|
|
||||||
- name: Check
|
- name: Check
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -1,47 +1,33 @@
|
|||||||
#
|
#
|
||||||
# .gitea/workflows/gitea-release.yaml
|
# .gitea/workflows/gitea-release.yaml
|
||||||
#
|
#
|
||||||
# Builds a release package on version branches (e.g. 1.0) and uploads it to
|
# On a version tag (e.g. v1.0): tests, builds Linux and Windows packages,
|
||||||
# the Gitea generic package registry.
|
# uploads them to the Gitea generic package registry and creates a Gitea release.
|
||||||
#
|
#
|
||||||
|
|
||||||
name: "Publish"
|
name: "Release"
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
tags:
|
||||||
- '*.*'
|
- 'v*'
|
||||||
|
|
||||||
env:
|
env:
|
||||||
ODIN_VERSION: dev-2026-09
|
|
||||||
PROJECT_NAME: HanjaKanaMun
|
PROJECT_NAME: HanjaKanaMun
|
||||||
GAME_VERSION: ${{gitea.ref_name}}
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
release:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Repository
|
- name: Checkout Repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install System Dependencies
|
- name: Setup Toolchain
|
||||||
run: |
|
uses: ./.gitea/actions/setup
|
||||||
SUDO=""; command -v sudo >/dev/null && SUDO=sudo
|
with:
|
||||||
$SUDO apt-get update
|
packages: lld zip
|
||||||
# clang links Odin programs; raylib needs X11 on Linux
|
|
||||||
$SUDO apt-get install -y --no-install-recommends clang libx11-dev curl ca-certificates
|
|
||||||
|
|
||||||
- name: Install Odin
|
- name: Set Version
|
||||||
run: |
|
run: echo "GAME_VERSION=${GITHUB_REF_NAME#v}" >> "$GITHUB_ENV"
|
||||||
curl -fsSL -o odin.tar.gz https://github.com/odin-lang/Odin/releases/download/$ODIN_VERSION/odin-linux-amd64-$ODIN_VERSION.tar.gz
|
|
||||||
mkdir -p "$HOME/odin"
|
|
||||||
tar -xzf odin.tar.gz -C "$HOME/odin" --strip-components=1
|
|
||||||
echo "$HOME/odin" >> "$GITHUB_PATH"
|
|
||||||
|
|
||||||
- name: Install just
|
|
||||||
run: |
|
|
||||||
mkdir -p "$HOME/.local/bin"
|
|
||||||
curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | bash -s -- --to "$HOME/.local/bin"
|
|
||||||
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
|
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
run: just test
|
run: just test
|
||||||
@@ -49,10 +35,30 @@ jobs:
|
|||||||
- name: Linux Build
|
- name: Linux Build
|
||||||
run: just package "$GAME_VERSION"
|
run: just package "$GAME_VERSION"
|
||||||
|
|
||||||
- name: Upload Artifact Linux
|
- name: Windows Build
|
||||||
|
run: |
|
||||||
|
# Debian/Ubuntu may only ship a versioned lld-link
|
||||||
|
LLD_LINK="$(command -v lld-link || ls /usr/lib/llvm-*/bin/lld-link | sort -V | tail -1)"
|
||||||
|
export LLD_LINK
|
||||||
|
just windows-deps
|
||||||
|
just package-windows "$GAME_VERSION"
|
||||||
|
|
||||||
|
- name: Upload Artifacts
|
||||||
run: |
|
run: |
|
||||||
ls -la build
|
ls -la build
|
||||||
FILE="${PROJECT_NAME}_${GAME_VERSION}_linux_x86_64.tar.gz"
|
for FILE in build/${PROJECT_NAME}_${GAME_VERSION}_*; do
|
||||||
curl --fail --user ${{ github.repository_owner }}:${{ secrets.TOKEN }} \
|
NAME="$(basename "$FILE")"
|
||||||
--upload-file "build/$FILE" \
|
echo "Uploading $NAME"
|
||||||
https://gitea.sanplex.xyz/api/packages/sansan/generic/$PROJECT_NAME/$GAME_VERSION/$FILE
|
curl --fail --user ${{ github.repository_owner }}:${{ secrets.TOKEN }} \
|
||||||
|
--upload-file "$FILE" \
|
||||||
|
https://gitea.sanplex.xyz/api/packages/sansan/generic/$PROJECT_NAME/$GAME_VERSION/$NAME
|
||||||
|
done
|
||||||
|
|
||||||
|
- name: Create Release
|
||||||
|
uses: akkuman/gitea-release-action@v1
|
||||||
|
env:
|
||||||
|
NODE_OPTIONS: '--experimental-fetch' # if nodejs < 18
|
||||||
|
with:
|
||||||
|
files: |-
|
||||||
|
build/${{ env.PROJECT_NAME }}_*.tar.gz
|
||||||
|
build/${{ env.PROJECT_NAME }}_*.zip
|
||||||
@@ -33,6 +33,7 @@ Commands (see `justfile`): `just run`, `just check`, `just test`, `just package
|
|||||||
2. **Old font failure root cause**: `DroidSansJapanese.ttf` was a GitHub HTML page saved from a blob URL, not a font. Always download fonts from raw URLs and check them with `file`.
|
2. **Old font failure root cause**: `DroidSansJapanese.ttf` was a GitHub HTML page saved from a blob URL, not a font. Always download fonts from raw URLs and check them with `file`.
|
||||||
3. **Thai shaping**: raylib does no text shaping, so Thai combining vowels/tone marks may be misplaced. Prefer standalone consonants on Thai cards.
|
3. **Thai shaping**: raylib does no text shaping, so Thai combining vowels/tone marks may be misplaced. Prefer standalone consonants on Thai cards.
|
||||||
4. Tests share the memory game's global state, so they must run single-threaded (`just test` sets `ODIN_TEST_THREADS=1`).
|
4. Tests share the memory game's global state, so they must run single-threaded (`just test` sets `ODIN_TEST_THREADS=1`).
|
||||||
|
6. **Windows builds are cross-compiled**: Odin can't link Windows binaries on Linux, so `just windows` builds objects with `-build-mode:obj` and links with `lld-link` against an xwin SDK. The VC runtime is linked statically; only Windows system DLLs are needed. Not yet tested on a real Windows machine.
|
||||||
5. The game changes directory to the executable's folder at startup so `assets/` is found from anywhere.
|
5. The game changes directory to the executable's folder at startup so `assets/` is found from anywhere.
|
||||||
|
|
||||||
## File Structure
|
## File Structure
|
||||||
@@ -43,9 +44,11 @@ HanjaKanaMun/
|
|||||||
├── justfile # build/run/check/test/package recipes
|
├── justfile # build/run/check/test/package recipes
|
||||||
├── ols.json # ols language server settings (neovim)
|
├── ols.json # ols language server settings (neovim)
|
||||||
├── odinfmt.json # odinfmt formatter settings (4 spaces, LF)
|
├── odinfmt.json # odinfmt formatter settings (4 spaces, LF)
|
||||||
├── .gitea/workflows/
|
├── .gitea/
|
||||||
│ ├── gitea-build.yaml # check + test + build on main/develop
|
│ ├── actions/setup/ # shared CI toolchain setup (apt, Odin, just)
|
||||||
│ └── gitea-release.yaml # package on version branches (*.*), upload to Gitea packages
|
│ └── workflows/
|
||||||
|
│ ├── gitea-build.yaml # check + test + build on main/develop
|
||||||
|
│ └── gitea-release.yaml # on tags v*: Linux + Windows packages, package registry upload, Gitea release
|
||||||
├── main.odin # Window/audio setup, screen state machine
|
├── main.odin # Window/audio setup, screen state machine
|
||||||
├── src/
|
├── src/
|
||||||
│ ├── themes/themes.odin # Theme struct, flag colors, Screen/Flag enums, layout constants
|
│ ├── themes/themes.odin # Theme struct, flag colors, Screen/Flag enums, layout constants
|
||||||
|
|||||||
@@ -10,9 +10,15 @@ Requires [Odin](https://odin-lang.org) (dev-2026-09) and [just](https://just.sys
|
|||||||
just run # build and play (debug)
|
just run # build and play (debug)
|
||||||
just check # type-check with -vet
|
just check # type-check with -vet
|
||||||
just test # run tests
|
just test # run tests
|
||||||
just package 1.0 # release build -> build/HanjaKanaMun_1.0_<os>_<arch>.tar.gz
|
just package 1.0 # native release build -> build/HanjaKanaMun_1.0_<os>_<arch>.tar.gz
|
||||||
|
just windows-deps # Linux only: fetch Windows SDK/CRT (xwin) and raylib.lib
|
||||||
|
just package-windows 1.0 # cross-compiled Windows build -> build/HanjaKanaMun_1.0_windows_x86_64.zip
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The Windows build needs `lld-link`. On macOS, `brew install lld` and set `LLD_LINK`, and point `XWIN_SDK` and
|
||||||
|
`RAYLIB_WINDOWS` at an xwin splat and Odin's Windows `vendor/raylib/windows` folder (the Linux/macOS Odin releases
|
||||||
|
don't include `raylib.lib`).
|
||||||
|
|
||||||
### Neovim
|
### Neovim
|
||||||
|
|
||||||
`ols.json` and `odinfmt.json` configure the [ols](https://github.com/DanielGavin/ols) language server and formatter.
|
`ols.json` and `odinfmt.json` configure the [ols](https://github.com/DanielGavin/ols) language server and formatter.
|
||||||
@@ -22,8 +28,12 @@ Formatting goes through ols (`vim.lsp.buf.format()`), or `just fmt` if `odinfmt`
|
|||||||
## CI
|
## CI
|
||||||
|
|
||||||
- `.gitea/workflows/gitea-build.yaml`: check, test and build on pushes and PRs to `main` and `develop`.
|
- `.gitea/workflows/gitea-build.yaml`: check, test and build on pushes and PRs to `main` and `develop`.
|
||||||
- `.gitea/workflows/gitea-release.yaml`: on version branches like `1.0`, packages the Linux build and uploads it to
|
- `.gitea/workflows/gitea-release.yaml`: on a version tag like `v1.0`, builds Linux and Windows packages, uploads them
|
||||||
the Gitea generic package registry (`HanjaKanaMun/<version>`). Needs a `TOKEN` secret with package write access.
|
to the Gitea generic package registry (`HanjaKanaMun/1.0`) and creates a Gitea release with both attached.
|
||||||
|
Needs a `TOKEN` secret with package write access.
|
||||||
|
- `.gitea/actions/setup`: shared toolchain setup (apt packages, Odin, just).
|
||||||
|
|
||||||
|
Release: `git tag v1.0 && git push origin v1.0`
|
||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,13 @@
|
|||||||
name := "HanjaKanaMun"
|
name := "HanjaKanaMun"
|
||||||
build_dir := "build"
|
build_dir := "build"
|
||||||
|
odin_version := "dev-2026-09"
|
||||||
|
xwin_version := "0.10.0"
|
||||||
|
|
||||||
|
# Windows cross-compilation inputs (see `windows-deps`)
|
||||||
|
xwin := env("XWIN", build_dir / "xwin")
|
||||||
|
xwin_sdk := env("XWIN_SDK", build_dir / "xwin-sdk")
|
||||||
|
raylib_windows := env("RAYLIB_WINDOWS", build_dir / "raylib-windows")
|
||||||
|
lld_link := env("LLD_LINK", "lld-link")
|
||||||
|
|
||||||
# List available recipes
|
# List available recipes
|
||||||
default:
|
default:
|
||||||
@@ -22,17 +30,65 @@ test:
|
|||||||
mkdir -p {{build_dir}}
|
mkdir -p {{build_dir}}
|
||||||
odin test tests -vet -define:ODIN_TEST_THREADS=1 -out:{{build_dir}}/tests
|
odin test tests -vet -define:ODIN_TEST_THREADS=1 -out:{{build_dir}}/tests
|
||||||
|
|
||||||
# Optimized build into build/, with the assets it needs at runtime
|
# Optimized native build into build/<os>/, with the assets it needs at runtime
|
||||||
release:
|
release:
|
||||||
mkdir -p {{build_dir}}/{{name}}
|
mkdir -p {{build_dir}}/{{os()}}/{{name}}
|
||||||
odin build . -vet -o:speed -out:{{build_dir}}/{{name}}/{{name}}
|
odin build . -vet -o:speed -out:{{build_dir}}/{{os()}}/{{name}}/{{name}}
|
||||||
cp -R assets {{build_dir}}/{{name}}/
|
cp -R assets {{build_dir}}/{{os()}}/{{name}}/
|
||||||
|
|
||||||
# Release build packed as build/<name>_<version>_<os>_<arch>.tar.gz
|
# Native release build packed as build/<name>_<version>_<os>_<arch>.tar.gz
|
||||||
package version="dev": release
|
package version="dev": release
|
||||||
tar -czf {{build_dir}}/{{name}}_{{version}}_{{os()}}_{{arch()}}.tar.gz -C {{build_dir}} {{name}}
|
tar -czf {{build_dir}}/{{name}}_{{version}}_{{os()}}_{{arch()}}.tar.gz -C {{build_dir}}/{{os()}} {{name}}
|
||||||
@echo "{{build_dir}}/{{name}}_{{version}}_{{os()}}_{{arch()}}.tar.gz"
|
@echo "{{build_dir}}/{{name}}_{{version}}_{{os()}}_{{arch()}}.tar.gz"
|
||||||
|
|
||||||
|
# Download the Windows SDK + CRT (via xwin, accepting Microsoft's license) and raylib.lib. Linux x86_64 only.
|
||||||
|
windows-deps:
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
mkdir -p {{build_dir}}
|
||||||
|
if [ ! -x "{{xwin}}" ]; then
|
||||||
|
curl -fsSL https://github.com/Jake-Shadle/xwin/releases/download/{{xwin_version}}/xwin-{{xwin_version}}-x86_64-unknown-linux-musl.tar.gz \
|
||||||
|
| tar -xz -C {{build_dir}} --strip-components=1 xwin-{{xwin_version}}-x86_64-unknown-linux-musl/xwin
|
||||||
|
fi
|
||||||
|
if [ ! -d "{{xwin_sdk}}/crt" ]; then
|
||||||
|
"{{xwin}}" --accept-license --arch x86_64 --cache-dir {{build_dir}}/xwin-cache splat --output "{{xwin_sdk}}"
|
||||||
|
fi
|
||||||
|
# Odin's Linux/macOS releases don't ship raylib's Windows library, so take it from the Windows release
|
||||||
|
if [ ! -f "{{raylib_windows}}/raylib.lib" ]; then
|
||||||
|
mkdir -p "{{raylib_windows}}"
|
||||||
|
curl -fsSL -o {{build_dir}}/odin-windows.zip \
|
||||||
|
https://github.com/odin-lang/Odin/releases/download/{{odin_version}}/odin-windows-amd64-{{odin_version}}.zip
|
||||||
|
unzip -o -j {{build_dir}}/odin-windows.zip 'dist/vendor/raylib/windows/raylib.lib' -d "{{raylib_windows}}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Cross-compile a Windows x86_64 build into build/windows/ (run `just windows-deps` first)
|
||||||
|
windows:
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
obj_dir={{build_dir}}/obj-windows
|
||||||
|
out_dir={{build_dir}}/windows/{{name}}
|
||||||
|
mkdir -p "$obj_dir" "$out_dir"
|
||||||
|
rm -f "$obj_dir"/*.obj
|
||||||
|
odin build . -vet -o:speed -target:windows_amd64 -build-mode:obj -out:"$obj_dir/{{name}}.obj"
|
||||||
|
# Odin can't link for Windows from other platforms, so link with lld-link.
|
||||||
|
# The VC runtime is linked statically (no VCRUNTIME140.dll needed); the
|
||||||
|
# Universal CRT is used from Windows itself (built into Windows 10+).
|
||||||
|
{{lld_link}} /nologo /machine:x64 /subsystem:windows /entry:mainCRTStartup \
|
||||||
|
/out:"$out_dir/{{name}}.exe" "$obj_dir"/*.obj "{{raylib_windows}}/raylib.lib" \
|
||||||
|
/libpath:"{{xwin_sdk}}/crt/lib/x86_64" \
|
||||||
|
/libpath:"{{xwin_sdk}}/sdk/lib/um/x86_64" \
|
||||||
|
/libpath:"{{xwin_sdk}}/sdk/lib/ucrt/x86_64" \
|
||||||
|
/nodefaultlib:msvcrt /nodefaultlib:vcruntime /nodefaultlib:libucrt \
|
||||||
|
libcmt.lib libvcruntime.lib ucrt.lib \
|
||||||
|
kernel32.lib user32.lib gdi32.lib winmm.lib shell32.lib opengl32.lib \
|
||||||
|
advapi32.lib bcrypt.lib ntdll.lib synchronization.lib
|
||||||
|
cp -R assets "$out_dir/"
|
||||||
|
|
||||||
|
# Windows build packed as build/<name>_<version>_windows_x86_64.zip
|
||||||
|
package-windows version="dev": windows
|
||||||
|
cd {{build_dir}}/windows && zip -qr ../{{name}}_{{version}}_windows_x86_64.zip {{name}}
|
||||||
|
@echo "{{build_dir}}/{{name}}_{{version}}_windows_x86_64.zip"
|
||||||
|
|
||||||
# Format all Odin files with odinfmt (settings in odinfmt.json)
|
# Format all Odin files with odinfmt (settings in odinfmt.json)
|
||||||
fmt:
|
fmt:
|
||||||
odinfmt -w .
|
odinfmt -w .
|
||||||
|
|||||||
Reference in new issue
Block a user