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;
@ -128,15 +128,45 @@ 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();
int result = PyRun_SimpleFile(fp, config.script_path.string().c_str()); // Set __file__ before execution
fclose(fp); 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) { if (config.interactive_mode) {
// Even if script had SystemExit, continue to interactive mode // Even if script had SystemExit, continue to interactive mode