Add proper CMake MCRF_HEADLESS option for headless builds
- Add option(MCRF_HEADLESS) to CMakeLists.txt for official headless support - Conditional compilation: skip ImGui sources when headless - Conditional linking: no SFML/OpenGL libraries in headless mode - Auto-define MCRF_HEADLESS preprocessor flag Verified: - Zero SFML/OpenGL dynamic dependencies (ldd confirms) - Python interpreter fully functional in headless mode - Core mcrfpy types work: Vector, Color, Scene, Frame, Grid - Binary size: 1.6 MB headless vs 2.5 MB normal (36% reduction) Build with: cmake .. -DMCRF_HEADLESS=ON Contributes to #158 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
7621ae35bb
commit
4c70aee020
2 changed files with 92 additions and 28 deletions
|
|
@ -8,6 +8,13 @@ project(McRogueFace)
|
||||||
set(CMAKE_CXX_STANDARD 20)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||||
|
|
||||||
|
# Headless build option (no SFML, no graphics - for server/testing/Emscripten prep)
|
||||||
|
option(MCRF_HEADLESS "Build without graphics dependencies (SFML, ImGui)" OFF)
|
||||||
|
|
||||||
|
if(MCRF_HEADLESS)
|
||||||
|
message(STATUS "Building in HEADLESS mode - no SFML/ImGui dependencies")
|
||||||
|
endif()
|
||||||
|
|
||||||
# Detect cross-compilation for Windows (MinGW)
|
# Detect cross-compilation for Windows (MinGW)
|
||||||
if(CMAKE_CROSSCOMPILING AND WIN32)
|
if(CMAKE_CROSSCOMPILING AND WIN32)
|
||||||
set(MCRF_CROSS_WINDOWS TRUE)
|
set(MCRF_CROSS_WINDOWS TRUE)
|
||||||
|
|
@ -36,36 +43,65 @@ else()
|
||||||
include_directories(${CMAKE_SOURCE_DIR}/deps/Python)
|
include_directories(${CMAKE_SOURCE_DIR}/deps/Python)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# ImGui and ImGui-SFML include directories
|
# ImGui and ImGui-SFML include directories (not needed in headless mode)
|
||||||
include_directories(${CMAKE_SOURCE_DIR}/modules/imgui)
|
if(NOT MCRF_HEADLESS)
|
||||||
include_directories(${CMAKE_SOURCE_DIR}/modules/imgui-sfml)
|
include_directories(${CMAKE_SOURCE_DIR}/modules/imgui)
|
||||||
|
include_directories(${CMAKE_SOURCE_DIR}/modules/imgui-sfml)
|
||||||
|
|
||||||
# ImGui source files
|
# ImGui source files
|
||||||
set(IMGUI_SOURCES
|
set(IMGUI_SOURCES
|
||||||
${CMAKE_SOURCE_DIR}/modules/imgui/imgui.cpp
|
${CMAKE_SOURCE_DIR}/modules/imgui/imgui.cpp
|
||||||
${CMAKE_SOURCE_DIR}/modules/imgui/imgui_draw.cpp
|
${CMAKE_SOURCE_DIR}/modules/imgui/imgui_draw.cpp
|
||||||
${CMAKE_SOURCE_DIR}/modules/imgui/imgui_tables.cpp
|
${CMAKE_SOURCE_DIR}/modules/imgui/imgui_tables.cpp
|
||||||
${CMAKE_SOURCE_DIR}/modules/imgui/imgui_widgets.cpp
|
${CMAKE_SOURCE_DIR}/modules/imgui/imgui_widgets.cpp
|
||||||
${CMAKE_SOURCE_DIR}/modules/imgui-sfml/imgui-SFML.cpp
|
${CMAKE_SOURCE_DIR}/modules/imgui-sfml/imgui-SFML.cpp
|
||||||
)
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Collect all the source files
|
# Collect all the source files
|
||||||
file(GLOB_RECURSE SOURCES "src/*.cpp")
|
file(GLOB_RECURSE SOURCES "src/*.cpp")
|
||||||
|
|
||||||
# Add ImGui sources to the build
|
# Add ImGui sources to the build (only if not headless)
|
||||||
list(APPEND SOURCES ${IMGUI_SOURCES})
|
if(NOT MCRF_HEADLESS)
|
||||||
|
list(APPEND SOURCES ${IMGUI_SOURCES})
|
||||||
|
endif()
|
||||||
|
|
||||||
# Find OpenGL (required by ImGui-SFML)
|
# Find OpenGL (required by ImGui-SFML) - not needed in headless mode
|
||||||
if(MCRF_CROSS_WINDOWS)
|
if(NOT MCRF_HEADLESS)
|
||||||
# For cross-compilation, OpenGL is provided by MinGW
|
if(MCRF_CROSS_WINDOWS)
|
||||||
set(OPENGL_LIBRARIES opengl32)
|
# For cross-compilation, OpenGL is provided by MinGW
|
||||||
else()
|
set(OPENGL_LIBRARIES opengl32)
|
||||||
find_package(OpenGL REQUIRED)
|
else()
|
||||||
set(OPENGL_LIBRARIES OpenGL::GL)
|
find_package(OpenGL REQUIRED)
|
||||||
|
set(OPENGL_LIBRARIES OpenGL::GL)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Create a list of libraries to link against
|
# Create a list of libraries to link against
|
||||||
if(MCRF_CROSS_WINDOWS)
|
if(MCRF_HEADLESS)
|
||||||
|
# Headless build: no SFML, no OpenGL
|
||||||
|
if(WIN32 OR MCRF_CROSS_WINDOWS)
|
||||||
|
set(LINK_LIBS
|
||||||
|
libtcod
|
||||||
|
python314)
|
||||||
|
if(MCRF_CROSS_WINDOWS)
|
||||||
|
include_directories(${CMAKE_SOURCE_DIR}/deps/platform/windows)
|
||||||
|
link_directories(${CMAKE_SOURCE_DIR}/__lib_windows/libtcod/lib)
|
||||||
|
link_directories(${CMAKE_SOURCE_DIR}/__lib_windows)
|
||||||
|
else()
|
||||||
|
include_directories(${CMAKE_SOURCE_DIR}/deps/platform/windows)
|
||||||
|
link_directories(${CMAKE_SOURCE_DIR}/__lib)
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
# Unix/Linux headless build
|
||||||
|
set(LINK_LIBS
|
||||||
|
tcod
|
||||||
|
python3.14
|
||||||
|
m dl util pthread)
|
||||||
|
include_directories(${CMAKE_SOURCE_DIR}/deps/platform/linux)
|
||||||
|
link_directories(${CMAKE_SOURCE_DIR}/__lib)
|
||||||
|
endif()
|
||||||
|
elseif(MCRF_CROSS_WINDOWS)
|
||||||
# MinGW cross-compilation: use full library names
|
# MinGW cross-compilation: use full library names
|
||||||
set(LINK_LIBS
|
set(LINK_LIBS
|
||||||
sfml-graphics
|
sfml-graphics
|
||||||
|
|
@ -128,6 +164,11 @@ add_executable(mcrogueface ${SOURCES})
|
||||||
# Define NO_SDL for libtcod-headless headers (excludes SDL-dependent code)
|
# Define NO_SDL for libtcod-headless headers (excludes SDL-dependent code)
|
||||||
target_compile_definitions(mcrogueface PRIVATE NO_SDL)
|
target_compile_definitions(mcrogueface PRIVATE NO_SDL)
|
||||||
|
|
||||||
|
# Define MCRF_HEADLESS for headless builds (excludes SFML/ImGui code)
|
||||||
|
if(MCRF_HEADLESS)
|
||||||
|
target_compile_definitions(mcrogueface PRIVATE MCRF_HEADLESS)
|
||||||
|
endif()
|
||||||
|
|
||||||
# On Windows, define Py_ENABLE_SHARED for proper Python DLL imports
|
# On Windows, define Py_ENABLE_SHARED for proper Python DLL imports
|
||||||
# Py_PYCONFIG_H prevents Include/pyconfig.h (Linux config) from being included
|
# Py_PYCONFIG_H prevents Include/pyconfig.h (Linux config) from being included
|
||||||
# (PC/pyconfig.h already defines HAVE_DECLSPEC_DLL and MS_WINDOWS)
|
# (PC/pyconfig.h already defines HAVE_DECLSPEC_DLL and MS_WINDOWS)
|
||||||
|
|
|
||||||
|
|
@ -679,9 +679,9 @@ The stub file grew from ~700 lines to ~900 lines with additional types and metho
|
||||||
# Normal SFML build (default)
|
# Normal SFML build (default)
|
||||||
make
|
make
|
||||||
|
|
||||||
# Headless build (no SFML dependency)
|
# Headless build (no SFML/ImGui dependencies)
|
||||||
mkdir build-headless && cd build-headless
|
mkdir build-headless && cd build-headless
|
||||||
cmake .. -DCMAKE_CXX_FLAGS="-DMCRF_HEADLESS" -DCMAKE_BUILD_TYPE=Debug
|
cmake .. -DMCRF_HEADLESS=ON -DCMAKE_BUILD_TYPE=Release
|
||||||
make
|
make
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -700,9 +700,32 @@ The libtcod approach of `#ifndef NO_SDL` guards works when **all platform includ
|
||||||
|
|
||||||
**Total**: ~3 hours for clean headless compilation
|
**Total**: ~3 hours for clean headless compilation
|
||||||
|
|
||||||
### Next Steps
|
### Completed Milestones
|
||||||
|
|
||||||
1. **Test Python bindings** - Ensure mcrfpy module loads in headless mode
|
1. ✅ **Test Python bindings** - mcrfpy module loads and works in headless mode
|
||||||
2. **Add CMake option** - `option(MCRF_HEADLESS "Build without graphics" OFF)`
|
- Vector, Color, Scene, Frame, Grid all functional
|
||||||
3. **Link-time validation** - Verify no SFML symbols are referenced
|
- libtcod integrations (BSP, pathfinding) available
|
||||||
4. **Emscripten testing** - Try building with emcc
|
2. ✅ **Add CMake option** - `option(MCRF_HEADLESS "Build without graphics" OFF)`
|
||||||
|
- Proper conditional compilation and linking
|
||||||
|
- No SFML symbols in headless binary
|
||||||
|
3. ✅ **Link-time validation** - `ldd` confirms zero SFML/OpenGL dependencies
|
||||||
|
4. ✅ **Binary size reduction** - Headless is 1.6 MB vs 2.5 MB normal build (36% smaller)
|
||||||
|
|
||||||
|
### Python Test Results (Headless Mode)
|
||||||
|
|
||||||
|
```python
|
||||||
|
# All these work in headless build:
|
||||||
|
import mcrfpy
|
||||||
|
v = mcrfpy.Vector(10, 20) # ✅
|
||||||
|
c = mcrfpy.Color(255, 128, 64) # ✅
|
||||||
|
scene = mcrfpy.Scene('test') # ✅
|
||||||
|
frame = mcrfpy.Frame(pos=(0,0)) # ✅
|
||||||
|
grid = mcrfpy.Grid(grid_size=(10,10)) # ✅
|
||||||
|
```
|
||||||
|
|
||||||
|
### Remaining Steps for Emscripten
|
||||||
|
|
||||||
|
1. **Main loop extraction** - Extract `GameEngine::doFrame()` for callback-based loop
|
||||||
|
2. **Emscripten toolchain** - Add CMake toolchain file for emcc
|
||||||
|
3. **VRSFML integration** - Replace stubs with actual WebGL rendering
|
||||||
|
4. **Python-in-WASM** - Test CPython/Pyodide integration (highest risk)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue