Skip to content

Latest commit

 

History

History
404 lines (302 loc) · 12.8 KB

File metadata and controls

404 lines (302 loc) · 12.8 KB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview

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.

Common Build Commands

# 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 architecture

Build System Architecture

Simple Two-Phase Build Flow

Phase 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}/*.so files (compiled cores)

Ruby Library Organization

  • lib/cores_builder.rb - Main orchestrator; coordinates fetching and building
  • lib/cpu_config.rb - Extracts CPU config from recipe YAML files into Ruby objects
  • lib/source_fetcher.rb - Clones/fetches git repositories at specific commits
  • lib/core_builder.rb - Executes builds for individual cores
  • lib/logger.rb - Centralized logging with sections and timestamps

Data Flow

recipes/linux/{cpu}.yml → source_fetcher → output/cores/
                              ↓
                        core_builder → output/{cpu}/*.so

Key Configuration Files

recipes/linux/{cpu}.yml - Source of Truth

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.so

Required fields:

  • repo - GitHub org/repo (URL auto-constructed)
  • commit - Git SHA (always required, used for fetching)
  • build_type - make or cmake
  • 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 updates
  • submodules: true - Only include if needed
  • extra_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-atomics for ARM64 atomics compatibility)
  • extra_ldflags - Additional LDFLAGS to append
  • cmake_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:
    cmake_env:
      CC: gcc        # Use native compiler for host tools
      CXX: g++
      AR: ar
      CFLAGS: ""     # Clear cross-compile flags
      CXXFLAGS: ""
      LDFLAGS: ""
    Note: cmake still uses cross-compiler via -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

CPU Configuration (Embedded in YAML)

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 flags
  • target_float - Floating point ABI and FPU settings
  • target_cflags / target_cxxflags - Base compiler optimization flags

Do not edit the config section unless you understand ARM CPU architecture specifics.

Adding New Cores

Simple 3-step process:

1. Find the commit

git ls-remote --heads https://github.com/libretro/libretro-atari800.git | grep master
# Result: 6a18cb23cc4a7cecabd9b16143d2d7332ae8d44b

Or check the core's GitHub releases/tags for stable versions.

2. Add to recipe (alphabetically)

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

3. Test and replicate

# Test on one architecture first
make core-arm64-atari800

# If successful, copy entry to arm32.yml
make core-arm32-atari800

Helper script for inspecting new cores:

./scripts/inspect-core libretro/libretro-atari800 6a18cb23cc4a

See docs/adding-cores.md for detailed guide with examples.

Updating Cores

The recipe system supports package-manager-style updates using the target field.

Automatic Updates (Recommended)

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-all

The update command:

  • Only updates cores that have a target field (opt-in)
  • Resolves the target (branch/tag) to latest commit SHA
  • Updates the commit field in-place while preserving formatting
  • Skips cores without target field (pinned cores)

Manual Updates (For Pinned Cores)

For cores without target field:

  1. Find the commit:

    git ls-remote --heads https://github.com/libretro/libretro-atari800.git | grep master
  2. Update the commit hash:

    vim recipes/linux/arm64.yml
    # Change the commit field for the core
  3. Clean and rebuild:

    rm -rf output/cores-arm64/libretro-atari800
    make core-arm64-atari800

Making a Core Updateable

Add a target field to enable automatic updates:

gambatte:
  target: master  # Track master branch
  commit: <sha>   # Will be auto-updated
  # ... rest of config

Supported targets:

  • Branch names: master, main, develop
  • Version tags: v1.18.1, v2.0

MinUI Required Cores

These 13 cores MUST be present on all CPU families for MinUI compatibility:

  1. fake08 - PICO-8 (fantasy console)
  2. fceumm - Nintendo Entertainment System
  3. gambatte - Game Boy / Game Boy Color
  4. gpsp - Game Boy Advance (ARM32 optimized)
  5. beetle-pce-fast - PC Engine / TurboGrafx-16 (mednafen_pce_fast)
  6. supafaust - Super Nintendo (mednafen_supafaust)
  7. beetle-vb - Virtual Boy (mednafen_vb)
  8. mgba - Game Boy Advance (ARM64 / general purpose)
  9. pcsx - PlayStation (pcsx_rearmed)
  10. picodrive - Sega Genesis / Mega Drive / 32X
  11. pokemini - Pokemon Mini
  12. race - Neo Geo Pocket / Neo Geo Pocket Color
  13. 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).

Build Environment

  • 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 -j flag or JOBS environment variable

Architecture Details

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

Troubleshooting

Build Failures

  1. Check build log: output/logs/{cpu}-build.log
  2. Test single core: make core-arm64-{corename}
  3. Verify recipe exists: grep -A 10 "^ {corename}:" recipes/linux/{cpu}.yml

Missing Cores

  1. Check if core is in recipe: grep "^ {corename}:" recipes/linux/{cpu}.yml
  2. Add core to recipe file if missing (see "Adding New Cores" above)
  3. Rebuild: make build-{cpu}

Cross-Compilation Issues

  • 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 Structure

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

Development Workflow

Testing a New Core

# 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

Debugging Build Issues

# 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=1

Release and Deployment

Creating a Release

The 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 --force

The release script will:

  1. Validate you're on the develop branch with no uncommitted changes
  2. Generate a UTC date-based tag with auto-incrementing build number (e.g., 20251115-0, 20251115-1, etc.)
  3. If any tags exist for today and --force flag used, delete all tags/releases for today and restart at -0
  4. Execute git flow release start/finish
  5. 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.

GitHub Actions Workflow

When a tag matching YYYYMMDD-N format is pushed to main:

  1. Builds Docker image
  2. Runs make build-all for both architectures
  3. Packages builds using make package-all
  4. Creates GitHub Release with:
    • linux-arm32.zip - ARM32 cores
    • linux-arm64.zip - ARM64 cores
  5. Updates latest tag to point to newest release

Prerequisites for Deployment

  • git-flow must be installed: brew install git-flow (macOS) or apt-get install git-flow (Linux)
  • Repository must be on develop branch
  • All changes must be committed
  • GitHub repository must have Actions enabled

Important Notes

  • 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-all overnight
  • Multiple releases per day supported - Auto-incrementing build numbers (YYYYMMDD-0, -1, -2, etc.)