Skip to content

user_impl_workflow

DiasFranciscoA edited this page Feb 16, 2026 · 8 revisions

Implementation Workflow 🛠️

This page explains where you implement your extension, how the generated CMake project is intended to be used, and how the workflow differs by target/platform.

extgen generates a multi-platform CMake project, but the extension logic itself is always your responsibility and lives inside the src/ folder (plus target-specific glue for some platforms).

Key idea: extgen generates the bridge + build system, you implement the feature code.


📁 Project Layout (What You Own vs What extgen Owns)

After running extgen, your folder usually looks like:

  • code_gen/ → generated bridge code (do not hand-edit)

  • cmake/ → helper scripts (packaging, xcframework, etc.)

  • src/ ✅ → your implementation lives here

    • src/CMakeLists.txt ✅ → you control which files build per platform
    • src/native/, src/ios/, src/tvos/, etc. ✅ → your platform implementations
  • third_party/ → optional integration point for SDKs / libraries

The workflow is:

  1. Edit GMIDL → defines the public API
  2. Run extgen → regenerates bindings + project
  3. Implement platform behavior in src/
  4. Build via CMake presets (or via IDE toolchains)

🧠 Platform Detection Helpers (CMake)

The root CMakeLists.txt provides platform classification booleans like:

set(EXTGEN_IS_IOS FALSE)
set(EXTGEN_IS_TVOS FALSE)
set(EXTGEN_IS_APPLE_MOBILE FALSE)
set(EXTGEN_IS_PS4 FALSE)
set(EXTGEN_IS_PS5 FALSE)

if(APPLE)
  if(CMAKE_OSX_SYSROOT MATCHES "iphone")
    set(EXTGEN_IS_IOS TRUE)
    set(EXTGEN_IS_APPLE_MOBILE TRUE)
  elseif(CMAKE_OSX_SYSROOT MATCHES "appletv")
    set(EXTGEN_IS_TVOS TRUE)
    set(EXTGEN_IS_APPLE_MOBILE TRUE)
  endif()
endif()

if(CMAKE_SYSTEM_NAME STREQUAL "ORBIS")
  set(EXTGEN_IS_PS4 TRUE)
elseif(CMAKE_SYSTEM_NAME STREQUAL "Prospero")
  set(EXTGEN_IS_PS5 TRUE)
endif()

You can use these in src/CMakeLists.txt to decide what to compile, which defines to add, and which files belong to each platform.


🍏 iOS / tvOS XCFramework Headers (Required)

When building an XCFramework, we must tell the packaging step which public headers to include.

In your src/CMakeLists.txt, extgen places something like:

set(EXT_PUBLIC_HEADERS_IOS
  "${CMAKE_SOURCE_DIR}/src/ios/${PROJECT_NAME}_ios.h;${CMAKE_SOURCE_DIR}/code_gen/ios/${PROJECT_NAME}Internal_ios.h"
  CACHE STRING "Semicolon-separated list of public headers to embed in the iOS xcframework")

set(EXT_PUBLIC_HEADERS_TVOS
  "${CMAKE_SOURCE_DIR}/src/tvos/${PROJECT_NAME}_tvos.h;${CMAKE_SOURCE_DIR}/code_gen/tvos/${PROJECT_NAME}Internal_tvos.h"
  CACHE STRING "Semicolon-separated list of public headers to embed in the tvOS xcframework")

You must keep this list correct if you change your src/ios or src/tvos layout.

Why this matters: xcodebuild -create-xcframework requires a headers folder. extgen collects these files into that folder during packaging.


✅ Workflow by Target


🤖 Android (Java & Kotlin modes)

What gets generated

  • No C/C++ library build
  • Pure Java/Kotlin glue code emitted into the target output folder (default: ../AndroidSource/...)

What you implement

You implement the platform behavior in the generated Java/Kotlin layer.

A module-named file is generated, for example:

  • ../AndroidSource/Java/<ModuleName>.java

And an internal interface is generated, for example:

  • ../AndroidSource/Java/code_gen/<ModuleName>Interface.java

✅ Your implementation should implement that interface.

Why this is good

  • The interface forces your Java/Kotlin implementation to stay in sync with GMIDL
  • When you regenerate, compile errors immediately point to missing functions

📝 Tip: Treat the interface as the “contract.” If GMIDL changes, you update your implementation accordingly.


🖥️ Native Targets (Desktop + Consoles + JNI + Apple Native)

This applies to:

  • ✅ Windows
  • ✅ macOS
  • ✅ Linux
  • ✅ Xbox
  • ✅ PS4 / PS5
  • ✅ Switch
  • ✅ Android (JNI mode)
  • ✅ iOS/tvOS (Native mode)

Where you implement

In src/, typically organized like:

  • src/native/ (shared native implementation)
  • src/windows/, src/macos/, etc. (optional splits)
  • src/ios/, src/tvos/ (platform wrappers if needed)

What you change

In src/CMakeLists.txt, you choose which sources compile per platform.

Example pattern:

if(WIN32)
  file(GLOB TARGET_SRCS CONFIGURE_DEPENDS "${SRC_DIR}/native/*.cpp" "${SRC_DIR}/windows/*.cpp")
elseif(APPLE)
  if(EXTGEN_IS_IOS)
    file(GLOB TARGET_SRCS CONFIGURE_DEPENDS "${SRC_DIR}/native/*.cpp" "${SRC_DIR}/ios/*.mm")
  elseif(EXTGEN_IS_TVOS)
    file(GLOB TARGET_SRCS CONFIGURE_DEPENDS "${SRC_DIR}/native/*.cpp" "${SRC_DIR}/tvos/*.mm")
  else()
    file(GLOB TARGET_SRCS CONFIGURE_DEPENDS "${SRC_DIR}/native/*.cpp" "${SRC_DIR}/macos/*.mm")
  endif()
elseif(ANDROID)
  file(GLOB TARGET_SRCS CONFIGURE_DEPENDS "${SRC_DIR}/native/*.cpp")
else()
  file(GLOB TARGET_SRCS CONFIGURE_DEPENDS "${SRC_DIR}/native/*.cpp")
endif()

target_sources(${PROJECT_NAME} PRIVATE ${TARGET_SRCS})

Build output behavior

  • Most targets build a shared library
  • iOS/tvOS build a static slice that is packaged into an XCFramework

🧩 Tip: If you support many platforms, use one “core” folder (like src/native/) and keep platform folders thin.


🍎 iOS / tvOS (ObjC, Swift, Native Modes)

iOS and tvOS development with extgen is designed to:

  • ✅ Keep all implementation inside your repository (src/)
  • ✅ Allow full Xcode debugging & real-device testing
  • ✅ Avoid manual project injection
  • ✅ Be repeatable and automation-friendly

The integration into the GameMaker-generated Xcode project is handled automatically via a dedicated CMake build target.


✅ Recommended Workflow (macOS)

1) Run extgen

Generate bindings and project files from GMIDL:

extgen --config config.json

2) Configure CMake

Run configure (via preset or manually). This prepares the extension project and build targets.

You do not need to manually open or inject anything into Xcode.


3) Export the GameMaker iOS/tvOS Project (Suppress Run)

From GameMaker:

  • Build for iOS/tvOS
  • Enable Suppress Run

This generates the Xcode project for the game.


4) Configure the GameMaker Xcode Project Path

In your CMake configuration (or preset), set:

# =============================================================================
# GameMaker iOS integration (optional)
# =============================================================================
set(EXT_GM_XCODEPROJ_OVERRIDE
    "~/GameMakerStudio2/GM_IOS/YourGame/YourGame/YourGame.xcodeproj"
    CACHE FILEPATH "" FORCE)

set(EXT_GM_APP_TARGET_OVERRIDE "" CACHE STRING "" FORCE)

What These Variables Do

Variable Purpose
EXT_GM_XCODEPROJ_OVERRIDE Path to the GameMaker-generated .xcodeproj
EXT_GM_APP_TARGET_OVERRIDE Optional override for the app target name

Because EXT_GM_XCODEPROJ_OVERRIDE is a FILEPATH, CMake automatically:

  • Expands ~
  • Normalizes the path
  • Validates it properly in GUIs and presets

5) Run the Integration Target

Instead of manually injecting the extension project into Xcode, run:

cmake --build <build-folder> --target integrate_gamemaker

This target automatically:

  • Adds the extension project to the GameMaker Xcode project
  • Creates required project references
  • Adds the static library dependency
  • Links it into the correct GameMaker target
  • Safely handles repeated runs (idempotent)

No Xcode UI steps required.


6) Open the GameMaker Xcode Project

Now open the GameMaker-generated .xcodeproj or workspace.

Your extension is already integrated.

You can:

  • Build for device or simulator
  • Debug on real hardware
  • Use full Xcode tooling
  • Edit extension source directly from the injected reference

🧠 How Source Editing Works

The integration adds a project reference, not copied files.

That means:

  • Editing code inside Xcode edits your real extension source
  • No duplicated files
  • No manual syncing required
  • Clean Git history

Switching between device/simulator is simply a configuration change.


🔁 Typical Iteration Cycle

  1. Edit GMIDL

  2. Run extgen

  3. Implement logic in src/

  4. Export from GameMaker (Suppress Run)

  5. Run:

    cmake --build <build-folder> --target integrate_gamemaker
  6. Open Xcode and build/debug normally

If you re-export from GameMaker, simply re-run the integration target.


📦 Final Packaging (XCFramework)

When ready for distribution:

cmake --build <build-folder> --target package_ios_xcframework
cmake --build <build-folder> --target package_tvos_xcframework

This produces:

  • <ExtensionName>.xcframework
  • A zipped distribution artifact (if configured)

🔥 Why This Workflow Is Preferred

  • No manual Xcode project editing
  • Deterministic and repeatable
  • CI-friendly
  • Safe to re-run after every GameMaker export
  • Single source of truth for extension code
  • Works cleanly for teams

✅ Summary Table

Target Type Where You Implement What You Build
Android (Java/Kotlin) Android output folder (../AndroidSource/...) No native lib
Desktop + Consoles + JNI src/ via CMakeLists Shared library
iOS/tvOS Native mode src/ + XCFramework packaging XCFramework
iOS/tvOS ObjC/Swift modes src/ + Xcode dev workflow XCFramework (final)

🧠 Best Practices

  • ✅ Keep core logic platform-agnostic (src/native/)
  • ✅ Use platform folders only for wrappers/entry points
  • ✅ Keep src/CMakeLists.txt clean and explicit
  • ✅ Always maintain the XCFramework public header list for Apple targets
  • ✅ Regenerate often — treat “compile errors after regeneration” as your update checklist

Clone this wiki locally