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) target_compile_definitions(mcrogueface PRIVATE Py_ENABLE_SHARED Py_PYCONFIG_H)
endif() 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) if(WIN32 AND NOT MCRF_CROSS_WINDOWS)
# MSVC-specific flags # MSVC-specific flags
set_target_properties(mcrogueface PROPERTIES if(NOT MCRF_WINDOWS_CONSOLE)
WIN32_EXECUTABLE TRUE set_target_properties(mcrogueface PROPERTIES
LINK_FLAGS "/SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup") WIN32_EXECUTABLE TRUE
LINK_FLAGS "/SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup")
endif()
elseif(MCRF_CROSS_WINDOWS) elseif(MCRF_CROSS_WINDOWS)
# MinGW cross-compilation: use -mwindows to hide console # MinGW cross-compilation
set_target_properties(mcrogueface PROPERTIES if(NOT MCRF_WINDOWS_CONSOLE)
WIN32_EXECUTABLE TRUE # Release: use -mwindows to hide console
LINK_FLAGS "-mwindows") 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() endif()
# Now the linker will find the libraries in the specified directory # 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 #ifndef __PLATFORM
#define __PLATFORM #define __PLATFORM
#define __PLATFORM_SET_PYTHON_SEARCH_PATHS 0 #define __PLATFORM_SET_PYTHON_SEARCH_PATHS 1
#include <windows.h> #include <windows.h>
std::wstring executable_path() std::wstring executable_path()

View file

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

View file

@ -28,6 +28,7 @@
#include "PyScene.h" #include "PyScene.h"
#include "PythonObjectCache.h" #include "PythonObjectCache.h"
#include <filesystem> #include <filesystem>
#include <fstream>
#include <cstring> #include <cstring>
#include <libtcod.h> #include <libtcod.h>
@ -806,13 +807,26 @@ void McRFPy_API::executeScript(std::string filename)
} }
} }
FILE* PScriptFile = fopen(script_path.string().c_str(), "r"); // Use std::ifstream + PyRun_SimpleString instead of PyRun_SimpleFile
if(PScriptFile) { // PyRun_SimpleFile has compatibility issues with MinGW-compiled code
PyRun_SimpleFile(PScriptFile, script_path.string().c_str()); std::ifstream file(script_path);
fclose(PScriptFile); if (!file.is_open()) {
} else {
std::cout << "Failed to open script: " << script_path.string() << std::endl; 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() void McRFPy_API::api_shutdown()

View file

@ -7,6 +7,7 @@
#include "PyTexture.h" #include "PyTexture.h"
#include <Python.h> #include <Python.h>
#include <iostream> #include <iostream>
#include <fstream>
#include <filesystem> #include <filesystem>
// Forward declarations // Forward declarations
@ -18,7 +19,6 @@ int main(int argc, char* argv[])
McRogueFaceConfig config; McRogueFaceConfig config;
CommandLineParser parser(argc, argv); CommandLineParser parser(argc, argv);
// Parse arguments
auto parse_result = parser.parse(config); auto parse_result = parser.parse(config);
if (parse_result.should_exit) { if (parse_result.should_exit) {
return parse_result.exit_code; return parse_result.exit_code;
@ -59,7 +59,7 @@ int run_python_interpreter(const McRogueFaceConfig& config)
// Initialize Python with configuration (argv is constructed from config) // Initialize Python with configuration (argv is constructed from config)
McRFPy_API::init_python_with_config(config); McRFPy_API::init_python_with_config(config);
// Import mcrfpy module and store reference // Import mcrfpy module and store reference
McRFPy_API::mcrf_module = PyImport_ImportModule("mcrfpy"); McRFPy_API::mcrf_module = PyImport_ImportModule("mcrfpy");
if (!McRFPy_API::mcrf_module) { if (!McRFPy_API::mcrf_module) {
@ -74,7 +74,7 @@ int run_python_interpreter(const McRogueFaceConfig& config)
PyObject_SetAttrString(McRFPy_API::mcrf_module, "default_font", McRFPy_API::default_font->pyObject()); PyObject_SetAttrString(McRFPy_API::mcrf_module, "default_font", McRFPy_API::default_font->pyObject());
PyObject_SetAttrString(McRFPy_API::mcrf_module, "default_texture", McRFPy_API::default_texture->pyObject()); PyObject_SetAttrString(McRFPy_API::mcrf_module, "default_texture", McRFPy_API::default_texture->pyObject());
} }
// Handle different Python modes // Handle different Python modes
if (!config.python_command.empty()) { if (!config.python_command.empty()) {
// Execute command from -c // Execute command from -c
@ -82,15 +82,15 @@ int run_python_interpreter(const McRogueFaceConfig& config)
// Use PyRun_String to catch SystemExit // Use PyRun_String to catch SystemExit
PyObject* main_module = PyImport_AddModule("__main__"); PyObject* main_module = PyImport_AddModule("__main__");
PyObject* main_dict = PyModule_GetDict(main_module); PyObject* main_dict = PyModule_GetDict(main_module);
PyObject* result_obj = PyRun_String(config.python_command.c_str(), PyObject* result_obj = PyRun_String(config.python_command.c_str(),
Py_file_input, main_dict, main_dict); Py_file_input, main_dict, main_dict);
if (result_obj == NULL) { if (result_obj == NULL) {
// Check if it's SystemExit // Check if it's SystemExit
if (PyErr_Occurred()) { if (PyErr_Occurred()) {
PyObject *type, *value, *traceback; PyObject *type, *value, *traceback;
PyErr_Fetch(&type, &value, &traceback); PyErr_Fetch(&type, &value, &traceback);
// If it's SystemExit and we're in interactive mode, clear it // If it's SystemExit and we're in interactive mode, clear it
if (PyErr_GivenExceptionMatches(type, PyExc_SystemExit)) { if (PyErr_GivenExceptionMatches(type, PyExc_SystemExit)) {
PyErr_Clear(); PyErr_Clear();
@ -99,7 +99,7 @@ int run_python_interpreter(const McRogueFaceConfig& config)
PyErr_Restore(type, value, traceback); PyErr_Restore(type, value, traceback);
PyErr_Print(); PyErr_Print();
} }
Py_XDECREF(type); Py_XDECREF(type);
Py_XDECREF(value); Py_XDECREF(value);
Py_XDECREF(traceback); Py_XDECREF(traceback);
@ -128,16 +128,46 @@ int run_python_interpreter(const McRogueFaceConfig& config)
} }
else if (!config.script_path.empty()) { else if (!config.script_path.empty()) {
// Execute script file (sys.argv already set at init time) // Execute script file (sys.argv already set at init time)
FILE* fp = fopen(config.script_path.string().c_str(), "r"); // Note: Using PyRun_SimpleString instead of PyRun_SimpleFile for better
if (!fp) { // 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 << "mcrogueface: can't open file '" << config.script_path << "': ";
std::cerr << "[Errno " << errno << "] " << strerror(errno) << std::endl; std::cerr << "[Errno " << errno << "] " << strerror(errno) << std::endl;
delete engine;
return 1; return 1;
} }
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(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);
}
int result = PyRun_SimpleFile(fp, config.script_path.string().c_str());
fclose(fp);
if (config.interactive_mode) { if (config.interactive_mode) {
// Even if script had SystemExit, continue to interactive mode // Even if script had SystemExit, continue to interactive mode
if (result != 0) { if (result != 0) {
@ -145,7 +175,7 @@ int run_python_interpreter(const McRogueFaceConfig& config)
if (PyErr_Occurred()) { if (PyErr_Occurred()) {
PyObject *type, *value, *traceback; PyObject *type, *value, *traceback;
PyErr_Fetch(&type, &value, &traceback); PyErr_Fetch(&type, &value, &traceback);
if (PyErr_GivenExceptionMatches(type, PyExc_SystemExit)) { if (PyErr_GivenExceptionMatches(type, PyExc_SystemExit)) {
PyErr_Clear(); PyErr_Clear();
result = 0; // Don't exit with error result = 0; // Don't exit with error
@ -153,7 +183,7 @@ int run_python_interpreter(const McRogueFaceConfig& config)
PyErr_Restore(type, value, traceback); PyErr_Restore(type, value, traceback);
PyErr_Print(); PyErr_Print();
} }
Py_XDECREF(type); Py_XDECREF(type);
Py_XDECREF(value); Py_XDECREF(value);
Py_XDECREF(traceback); Py_XDECREF(traceback);
@ -162,7 +192,7 @@ int run_python_interpreter(const McRogueFaceConfig& config)
// Run interactive mode after script // Run interactive mode after script
PyRun_InteractiveLoop(stdin, "<stdin>"); PyRun_InteractiveLoop(stdin, "<stdin>");
} }
// Run the game engine after script execution // Run the game engine after script execution
engine->run(); engine->run();
@ -197,7 +227,7 @@ int run_python_interpreter(const McRogueFaceConfig& config)
} }
return 0; return 0;
} }
delete engine; delete engine;
return 0; return 0;
} }