mingw toolchain and final fixes for Windows. Closes #162

This commit is contained in:
John McCardle 2026-01-08 21:16:27 -05:00
commit 1438044c6a
6 changed files with 129 additions and 30 deletions

View file

@ -135,17 +135,28 @@ if(WIN32 OR MCRF_CROSS_WINDOWS)
target_compile_definitions(mcrogueface PRIVATE Py_ENABLE_SHARED Py_PYCONFIG_H)
endif()
# On Windows, set subsystem to WINDOWS to hide console
# On Windows, set subsystem to WINDOWS to hide console (release builds only)
# Use -DMCRF_WINDOWS_CONSOLE=ON for debug builds with console output
option(MCRF_WINDOWS_CONSOLE "Keep console window visible for debugging" OFF)
if(WIN32 AND NOT MCRF_CROSS_WINDOWS)
# MSVC-specific flags
if(NOT MCRF_WINDOWS_CONSOLE)
set_target_properties(mcrogueface PROPERTIES
WIN32_EXECUTABLE TRUE
LINK_FLAGS "/SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup")
endif()
elseif(MCRF_CROSS_WINDOWS)
# MinGW cross-compilation: use -mwindows to hide console
# MinGW cross-compilation
if(NOT MCRF_WINDOWS_CONSOLE)
# Release: use -mwindows to hide console
set_target_properties(mcrogueface PROPERTIES
WIN32_EXECUTABLE TRUE
LINK_FLAGS "-mwindows")
else()
# Debug: keep console for stdout/stderr output
message(STATUS "Windows console enabled for debugging")
endif()
endif()
# Now the linker will find the libraries in the specified directory

View file

@ -0,0 +1,34 @@
# CMake toolchain file for cross-compiling to Windows using MinGW-w64
# Usage: cmake -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/mingw-w64-x86_64.cmake ..
set(CMAKE_SYSTEM_NAME Windows)
set(CMAKE_SYSTEM_PROCESSOR x86_64)
# Specify the cross-compiler (use posix variant for std::mutex support)
set(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc-posix)
set(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++-posix)
set(CMAKE_RC_COMPILER x86_64-w64-mingw32-windres)
# Target environment location
set(CMAKE_FIND_ROOT_PATH /usr/x86_64-w64-mingw32)
# Add MinGW system include directories for Windows headers
include_directories(SYSTEM /usr/x86_64-w64-mingw32/include)
# Adjust search behavior
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
# Static linking of libgcc and libstdc++ to avoid runtime dependency issues
# Enable auto-import for Python DLL data symbols
set(CMAKE_EXE_LINKER_FLAGS_INIT "-static-libgcc -static-libstdc++ -Wl,--enable-auto-import")
set(CMAKE_SHARED_LINKER_FLAGS_INIT "-static-libgcc -static-libstdc++ -Wl,--enable-auto-import")
# Windows-specific defines
add_definitions(-DWIN32 -D_WIN32 -D_WINDOWS)
add_definitions(-DMINGW_HAS_SECURE_API)
# Disable console window for GUI applications (optional, can be overridden)
# set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -mwindows")

View file

@ -1,6 +1,6 @@
#ifndef __PLATFORM
#define __PLATFORM
#define __PLATFORM_SET_PYTHON_SEARCH_PATHS 0
#define __PLATFORM_SET_PYTHON_SEARCH_PATHS 1
#include <windows.h>
std::wstring executable_path()

View file

@ -65,11 +65,21 @@ GameEngine::GameEngine(const McRogueFaceConfig& cfg)
!config.python_mode;
if (should_load_game) {
std::cerr << "[DEBUG] GameEngine: loading default game.py" << std::endl;
std::cerr.flush();
if (!Py_IsInitialized()) {
std::cerr << "[DEBUG] GameEngine: initializing Python API" << std::endl;
std::cerr.flush();
McRFPy_API::api_init();
}
std::cerr << "[DEBUG] GameEngine: importing mcrfpy" << std::endl;
std::cerr.flush();
McRFPy_API::executePyString("import mcrfpy");
std::cerr << "[DEBUG] GameEngine: executing scripts/game.py" << std::endl;
std::cerr.flush();
McRFPy_API::executeScript("scripts/game.py");
std::cerr << "[DEBUG] GameEngine: game.py execution complete" << std::endl;
std::cerr.flush();
}
// Note: --exec scripts are NOT executed here.

View file

@ -28,6 +28,7 @@
#include "PyScene.h"
#include "PythonObjectCache.h"
#include <filesystem>
#include <fstream>
#include <cstring>
#include <libtcod.h>
@ -806,13 +807,26 @@ void McRFPy_API::executeScript(std::string filename)
}
}
FILE* PScriptFile = fopen(script_path.string().c_str(), "r");
if(PScriptFile) {
PyRun_SimpleFile(PScriptFile, script_path.string().c_str());
fclose(PScriptFile);
} else {
// Use std::ifstream + PyRun_SimpleString instead of PyRun_SimpleFile
// PyRun_SimpleFile has compatibility issues with MinGW-compiled code
std::ifstream file(script_path);
if (!file.is_open()) {
std::cout << "Failed to open script: " << script_path.string() << std::endl;
return;
}
std::string script_content((std::istreambuf_iterator<char>(file)),
std::istreambuf_iterator<char>());
file.close();
// Set __file__ before execution
PyObject* main_module = PyImport_AddModule("__main__");
PyObject* main_dict = PyModule_GetDict(main_module);
PyObject* py_filename = PyUnicode_FromString(script_path.string().c_str());
PyDict_SetItemString(main_dict, "__file__", py_filename);
Py_DECREF(py_filename);
PyRun_SimpleString(script_content.c_str());
}
void McRFPy_API::api_shutdown()

View file

@ -7,6 +7,7 @@
#include "PyTexture.h"
#include <Python.h>
#include <iostream>
#include <fstream>
#include <filesystem>
// Forward declarations
@ -18,7 +19,6 @@ int main(int argc, char* argv[])
McRogueFaceConfig config;
CommandLineParser parser(argc, argv);
// Parse arguments
auto parse_result = parser.parse(config);
if (parse_result.should_exit) {
return parse_result.exit_code;
@ -128,15 +128,45 @@ int run_python_interpreter(const McRogueFaceConfig& config)
}
else if (!config.script_path.empty()) {
// Execute script file (sys.argv already set at init time)
FILE* fp = fopen(config.script_path.string().c_str(), "r");
if (!fp) {
// Note: Using PyRun_SimpleString instead of PyRun_SimpleFile for better
// cross-platform compatibility (PyRun_SimpleFile has issues with MinGW/Wine)
// Read file contents
std::ifstream file(config.script_path);
if (!file.is_open()) {
std::cerr << "mcrogueface: can't open file '" << config.script_path << "': ";
std::cerr << "[Errno " << errno << "] " << strerror(errno) << std::endl;
delete engine;
return 1;
}
std::string script_content((std::istreambuf_iterator<char>(file)),
std::istreambuf_iterator<char>());
file.close();
int result = PyRun_SimpleFile(fp, config.script_path.string().c_str());
fclose(fp);
// Set __file__ before execution
PyObject* main_module = PyImport_AddModule("__main__");
PyObject* main_dict = PyModule_GetDict(main_module);
PyObject* py_filename = PyUnicode_FromString(config.script_path.string().c_str());
PyDict_SetItemString(main_dict, "__file__", py_filename);
Py_DECREF(py_filename);
int result = PyRun_SimpleString(script_content.c_str());
// Flush Python stdout/stderr to ensure output appears before engine->run() blocks
PyObject* sys_module = PyImport_ImportModule("sys");
if (sys_module) {
PyObject* stdout_obj = PyObject_GetAttrString(sys_module, "stdout");
if (stdout_obj) {
PyObject_CallMethod(stdout_obj, "flush", NULL);
Py_DECREF(stdout_obj);
}
PyObject* stderr_obj = PyObject_GetAttrString(sys_module, "stderr");
if (stderr_obj) {
PyObject_CallMethod(stderr_obj, "flush", NULL);
Py_DECREF(stderr_obj);
}
Py_DECREF(sys_module);
}
if (config.interactive_mode) {
// Even if script had SystemExit, continue to interactive mode