This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
lessui-cores is a build system for creating architecture-optimized libretro emulator cores for ARM-based retro handhelds. It builds cores per architecture, supporting all required cores plus additional systems.
Key Architecture: Manual YAML recipes define which cores to build for each CPU family. Recipes are the single source of truth - edit them directly to add/update/remove cores.
MinUI Compatibility: All 13 MinUI required cores are built for all CPU families to ensure upgrade compatibility from MinUI.
# Build cores for specific architecture
make build-arm32 # ARM32: All 32-bit devices
make build-arm64 # ARM64: All 64-bit devices
# Build all (1-3 hours per architecture)
make build-all
# Build single core for testing/debugging
make core-arm64-gambatte
make core-arm32-flycast
# Package builds
make package-arm64
make package-all
# Clean
make clean # Clean all build outputs
make clean-arm64 # Clean specific architecturePhase 1: Recipes → Fetched Sources
- Input:
recipes/linux/{cpu}.yml(YAML files with core definitions) - Script:
lib/source_fetcher.rb - Output:
output/cores/{corename}/(git clones at specific commits)
Phase 2: Sources → Built Cores
- Input: Fetched sources + CPU config from YAML (compiler flags)
- Script:
lib/core_builder.rb - Output:
output/{cpu}/*.sofiles (compiled cores)
lib/cores_builder.rb- Main orchestrator; coordinates fetching and buildinglib/cpu_config.rb- Extracts CPU config from recipe YAML files into Ruby objectslib/source_fetcher.rb- Clones/fetches git repositories at specific commitslib/core_builder.rb- Executes builds for individual coreslib/logger.rb- Centralized logging with sections and timestamps
recipes/linux/{cpu}.yml → source_fetcher → output/cores/
↓
core_builder → output/{cpu}/*.so
These are manually maintained YAML files that define which cores to build for each CPU family. Edit them directly to add/update/remove cores.
Location:
recipes/linux/arm32.yml- ARMv7VE + NEON-VFPv4 (All ARM32 devices)recipes/linux/arm64.yml- ARMv8-A + NEON (All ARM64 devices)
Recipe Entry Format (Explicit & Minimal):
# Updateable core - tracks upstream branch/tag
gambatte:
repo: libretro/gambatte-libretro
target: master # Branch or tag to track
commit: 6924c76ba03dadddc6e97fa3660f3d3bc08faa94 # Resolved SHA (auto-updated)
build_type: make
makefile: Makefile.libretro
build_dir: "."
platform: unix
so_file: gambatte_libretro.so
# Pinned core - no auto-updates
atari800:
repo: libretro/libretro-atari800
commit: 6a18cb23cc4a7cecabd9b16143d2d7332ae8d44b # Fixed SHA (manually updated)
build_type: make
makefile: Makefile
build_dir: "."
platform: unix
so_file: atari800_libretro.soRequired fields:
repo- GitHub org/repo (URL auto-constructed)commit- Git SHA (always required, used for fetching)build_type-makeorcmake- For make:
makefile,build_dir,platform,so_file - For cmake:
cmake_opts,so_file
Optional fields:
target- Branch name (e.g.,master,main) or version tag (e.g.,v1.18.1) to track for updatessubmodules: true- Only include if neededextra_args: [...]- Only for special cases (make builds only)extra_cflags- Additional CFLAGS to append (e.g.,-Wno-error)extra_cxxflags- Additional CXXFLAGS to append (e.g.,-mno-outline-atomicsfor ARM64 atomics compatibility)extra_ldflags- Additional LDFLAGS to appendcmake_env- (cmake only) Override environment variables for the build process. Useful when a cmake project spawns host tool builds (e.g., mruby in tic80) that incorrectly inherit cross-compiler env vars. Example:Note: cmake still uses cross-compiler viacmake_env: CC: gcc # Use native compiler for host tools CXX: g++ AR: ar CFLAGS: "" # Clear cross-compile flags CXXFLAGS: "" LDFLAGS: ""
-DCMAKE_C_COMPILER, but subprocesses inherit these env vars.
Important Notes:
- Recipes are explicit - no guessing or fallbacks
- Auto-constructs GitHub tarball URLs from repo + commit
- Uses actual .so output names (no renaming)
- Cores sorted alphabetically
- Missing required fields = immediate clear error
Each recipe YAML file now contains a config: section with CPU-specific compiler flags and architecture settings. This eliminates the need for separate .config files.
Key configuration fields:
arch- Architecture (arm, aarch64)target_cross- Compiler prefix (arm-linux-gnueabihf-, aarch64-linux-gnu-)target_optimization- CPU-specific march/mcpu/mtune flagstarget_float- Floating point ABI and FPU settingstarget_cflags/target_cxxflags- Base compiler optimization flags
Do not edit the config section unless you understand ARM CPU architecture specifics.
Simple 3-step process:
git ls-remote --heads https://github.com/libretro/libretro-atari800.git | grep master
# Result: 6a18cb23cc4a7cecabd9b16143d2d7332ae8d44bOr check the core's GitHub releases/tags for stable versions.
Edit recipes/linux/arm64.yml:
cores:
atari800:
repo: libretro/libretro-atari800
commit: 6a18cb23cc4a7cecabd9b16143d2d7332ae8d44b
build_type: make
makefile: Makefile
build_dir: "."
platform: unix
so_file: atari800_libretro.so# Test on one architecture first
make core-arm64-atari800
# If successful, copy entry to arm32.yml
make core-arm32-atari800Helper script for inspecting new cores:
./scripts/inspect-core libretro/libretro-atari800 6a18cb23cc4aSee docs/adding-cores.md for detailed guide with examples.
The recipe system supports package-manager-style updates using the target field.
For cores with target field:
# Update all cores with target field for arm64
make update-recipes-arm64
# Update specific core
make update-core-arm64-gambatte
# Dry-run (check without updating)
make update-recipes-arm64 DRY=1
# Update all architectures
make update-recipes-allThe update command:
- Only updates cores that have a
targetfield (opt-in) - Resolves the target (branch/tag) to latest commit SHA
- Updates the
commitfield in-place while preserving formatting - Skips cores without
targetfield (pinned cores)
For cores without target field:
-
Find the commit:
git ls-remote --heads https://github.com/libretro/libretro-atari800.git | grep master -
Update the commit hash:
vim recipes/linux/arm64.yml # Change the commit field for the core -
Clean and rebuild:
rm -rf output/cores-arm64/libretro-atari800 make core-arm64-atari800
Add a target field to enable automatic updates:
gambatte:
target: master # Track master branch
commit: <sha> # Will be auto-updated
# ... rest of configSupported targets:
- Branch names:
master,main,develop - Version tags:
v1.18.1,v2.0
These 13 cores MUST be present on all CPU families for MinUI compatibility:
- fake08 - PICO-8 (fantasy console)
- fceumm - Nintendo Entertainment System
- gambatte - Game Boy / Game Boy Color
- gpsp - Game Boy Advance (ARM32 optimized)
- beetle-pce-fast - PC Engine / TurboGrafx-16 (mednafen_pce_fast)
- supafaust - Super Nintendo (mednafen_supafaust)
- beetle-vb - Virtual Boy (mednafen_vb)
- mgba - Game Boy Advance (ARM64 / general purpose)
- pcsx - PlayStation (pcsx_rearmed)
- picodrive - Sega Genesis / Mega Drive / 32X
- pokemini - Pokemon Mini
- race - Neo Geo Pocket / Neo Geo Pocket Color
- snes9x2005 - Super Nintendo (snes9x2005_plus)
Core Naming: Some cores have different names in MinUI vs libretro. Our recipes use libretro names (e.g., beetle-vb instead of mednafen_vb).
- Docker: Debian Buster (for glibc 2.28 compatibility)
- Compiler: GCC 8.3.0
- Toolchains: arm-linux-gnueabihf (ARM32), aarch64-linux-gnu (ARM64)
- Parallel builds: Controlled by
-jflag orJOBSenvironment variable
| Package | Architecture | Devices |
|---|---|---|
| arm32 | ARMv7VE + NEON-VFPv4 | All ARM32 devices (Miyoo Mini, RG35XX, Trimui Smart) |
| arm64 | ARMv8-A + NEON | All ARM64 devices (RG28xx/40xx, CubeXX, Trimui) |
Architecture Notes:
- arm32: Cortex-A7 baseline optimizations, compatible with all ARM32 handhelds
- arm64: Cortex-A53 baseline for maximum ARM64 compatibility
- Check build log:
output/logs/{cpu}-build.log - Test single core:
make core-arm64-{corename} - Verify recipe exists:
grep -A 10 "^ {corename}:" recipes/linux/{cpu}.yml
- Check if core is in recipe:
grep "^ {corename}:" recipes/linux/{cpu}.yml - Add core to recipe file if missing (see "Adding New Cores" above)
- Rebuild:
make build-{cpu}
- Ensure Docker image is built:
make docker-build - Check CPU config in recipe YAML:
recipes/linux/{cpu}.yml(config section) - Verify toolchain is available in Docker container:
make shell
output/
├── cores-arm32/ # Fetched sources for arm32
├── cores-arm64/ # Fetched sources for arm64
├── logs/ # Build logs
├── dist/ # Packaged zips
├── arm32/*.so # ARM32 cores
└── arm64/*.so # ARM64 cores
# 1. Add core to recipes/linux/arm64.yml under cores: section
# 2. Build just that core
make core-arm64-{corename}
# 3. If successful, add to arm32.yml and test
make core-arm32-{corename}
# 4. Build for both architectures
make build-all# Open shell in build container
make shell
# Manually inspect source
cd output/cores/{corename}
ls -la
# Try manual build with verbose output
make -f Makefile.libretro platform=unix CC=gcc V=1The project uses git flow with automated releases triggered by UTC date-based tags (YYYYMMDD-N format):
# Create a new release (must be on develop branch)
./scripts/release
# Force re-release (deletes ALL releases from today and recreates as -0)
./scripts/release --forceThe release script will:
- Validate you're on the
developbranch with no uncommitted changes - Generate a UTC date-based tag with auto-incrementing build number (e.g.,
20251115-0,20251115-1, etc.) - If any tags exist for today and
--forceflag used, delete all tags/releases for today and restart at-0 - Execute git flow release start/finish
- Push branches and tag to trigger GitHub Actions
Multiple builds per day: Simply run ./scripts/release again - it automatically increments the build number (e.g., -0 → -1 → -2).
Force Mode: Use --force to delete ALL releases from today and restart at -0 (e.g., after a bad build early in the day). Requires gh CLI for release deletion.
When a tag matching YYYYMMDD-N format is pushed to main:
- Builds Docker image
- Runs
make build-allfor both architectures - Packages builds using
make package-all - Creates GitHub Release with:
linux-arm32.zip- ARM32 coreslinux-arm64.zip- ARM64 cores
- Updates
latesttag to point to newest release
- git-flow must be installed:
brew install git-flow(macOS) orapt-get install git-flow(Linux) - Repository must be on
developbranch - All changes must be committed
- GitHub repository must have Actions enabled
- Never commit
output/directory - It contains build artifacts and fetched sources - Recipes are git-tracked - Your edits to YAML files are preserved in version control
- Recipes are manually maintained - No automatic generation; edit YAML files directly
- Each architecture needs ~5GB disk space - Plan accordingly
- Build times are 1-3 hours per architecture - Use
build-allovernight - Multiple releases per day supported - Auto-incrementing build numbers (YYYYMMDD-0, -1, -2, etc.)