From efbb72e3a558a055f9614b4b481e881b3c87b8a1 Mon Sep 17 00:00:00 2001 From: fyryNy Date: Tue, 28 Jul 2026 19:18:44 +0200 Subject: [PATCH 1/3] Add Linux build support through msvc-wine - add Linux configure and build presets for all Gothic targets - add configurable msvc-wine x86 toolchain and linker wrapper - generate preset-aware IntelliSense compilation entries - provide separate Linux and Windows VS Code configurations - package VDF files through Wine on Linux - ensure UnionAPI.dll is copied before VDF packaging - document Linux setup, building, packaging, and IntelliSense --- .clangd | 4 + .gitignore | 6 +- .vscode/c_cpp_properties.json | 38 ++++++++ CMakeLists.txt | 31 +++++-- CMakePresets.json | 41 ++++++++- CMakeUserConfigPresets.json | 9 +- README.md | 77 +++++++++++++++- cmake/configure_msvc_wine_intellisense.cmake | 97 ++++++++++++++++++++ cmake/msvc-wine-link.in | 20 ++++ cmake/msvc-wine-x86.cmake | 92 +++++++++++++++++++ cmake/run_gothic_vdfs.cmake | 46 ++++++++++ 11 files changed, 442 insertions(+), 19 deletions(-) create mode 100644 .clangd create mode 100644 .vscode/c_cpp_properties.json create mode 100644 cmake/configure_msvc_wine_intellisense.cmake create mode 100644 cmake/msvc-wine-link.in create mode 100644 cmake/msvc-wine-x86.cmake create mode 100644 cmake/run_gothic_vdfs.cmake diff --git a/.clangd b/.clangd new file mode 100644 index 0000000..14fece3 --- /dev/null +++ b/.clangd @@ -0,0 +1,4 @@ +CompileFlags: + CompilationDatabase: . + Add: + - --target=i686-pc-windows-msvc diff --git a/.gitignore b/.gitignore index 4850fd4..707de92 100644 --- a/.gitignore +++ b/.gitignore @@ -2,12 +2,14 @@ .vs/* !.vs/launch.vs.json -.vscode/ +.vscode/* +!.vscode/c_cpp_properties.json .idea/ # Build directories /out/ +/compile_commands.json # Build files @@ -16,4 +18,4 @@ # CMake user config -CMakeUserConfigPresets.json \ No newline at end of file +CMakeUserConfigPresets.json diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..56073a3 --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,38 @@ +{ + "configurations": [ + { + "name": "Linux", + "includePath": [ + "${workspaceFolder}/**" + ], + "defines": [ + "WIN32", + "_WIN32", + "_M_IX86=600" + ], + "compilerPath": "/usr/bin/clang-cl", + "compilerArgs": [ + "--target=i686-pc-windows-msvc" + ], + "intelliSenseMode": "windows-clang-x86", + "compileCommands": "${workspaceFolder}/compile_commands.json", + "browse": { + "limitSymbolsToIncludedHeaders": true + } + }, + { + "name": "Win32", + "configurationProvider": "ms-vscode.cmake-tools", + "includePath": [ + "${workspaceFolder}/**" + ], + "defines": [ + "WIN32", + "_WIN32", + "_M_IX86=600" + ], + "intelliSenseMode": "windows-msvc-x86" + } + ], + "version": 4 +} diff --git a/CMakeLists.txt b/CMakeLists.txt index 0e64573..09f0c6e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,7 @@ set(PROJECT_VERSION "1.0.0.0" CACHE STRING "Project version") include(target_add_versioninfo) include(generate_ipp_translation_units) +include(configure_msvc_wine_intellisense) project(UnionPlugin DESCRIPTION "Union plugin for Gothic Games" @@ -43,12 +44,14 @@ target_sources(${CMAKE_PROJECT_NAME} target_include_directories(${CMAKE_PROJECT_NAME} INTERFACE - "include/" + "${CMAKE_CURRENT_SOURCE_DIR}/include" +) - PRIVATE - BEFORE "userapi/" - "src/" - "signatures/" +target_include_directories(${CMAKE_PROJECT_NAME} + BEFORE PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/userapi" + "${CMAKE_CURRENT_SOURCE_DIR}/src" + "${CMAKE_CURRENT_SOURCE_DIR}/signatures" ) target_add_versioninfo(${CMAKE_PROJECT_NAME} @@ -66,8 +69,7 @@ if (GENERATE_VDF) set(VDF_BUILD_VM_FILE "${VDF_BUILD_DIR}build.vm") configure_file("${VDF_DIR}build.vm" "${VDF_BUILD_VM_FILE}") - - string(REPLACE "/" "\\" VDF_BUILD_VM_FILE "${VDF_BUILD_VM_FILE}") + set(VDF_OUTPUT_FILE "${VDF_BUILD_DIR}${CMAKE_PROJECT_NAME}.vdf") if (GENERATE_UNION_API_DLL) add_custom_target(copy_union_api_dll @@ -88,11 +90,22 @@ if (GENERATE_VDF) add_custom_target(build_vdf ALL COMMAND ${CMAKE_COMMAND} -E echo "[VDF] Building ${CMAKE_PROJECT_NAME}.vdf ..." - COMMAND "${VDF_BUILD_DIR}/GothicVDFS.exe" /B "${VDF_BUILD_VM_FILE}" + COMMAND ${CMAKE_COMMAND} + "-DVDF_EXECUTABLE=${VDF_BUILD_DIR}/GothicVDFS.exe" + "-DVDF_SCRIPT=${VDF_BUILD_VM_FILE}" + "-DVDF_OUTPUT=${VDF_OUTPUT_FILE}" + "-DVDF_WORKING_DIRECTORY=${VDF_BUILD_DIR}" + -P "${CMAKE_SOURCE_DIR}/cmake/run_gothic_vdfs.cmake" COMMAND ${CMAKE_COMMAND} -E echo "[VDF] ${CMAKE_PROJECT_NAME}.vdf build successfully!" WORKING_DIRECTORY "${VDF_BUILD_DIR}" DEPENDS ${CMAKE_PROJECT_NAME} ) + + if(GENERATE_UNION_API_DLL) + add_dependencies(build_vdf copy_union_api_dll) + endif() endif() -add_subdirectory(dependencies) \ No newline at end of file +add_subdirectory(dependencies) + +configure_msvc_wine_intellisense(${CMAKE_PROJECT_NAME} union) diff --git a/CMakePresets.json b/CMakePresets.json index 53c7e35..9223835 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -19,6 +19,21 @@ "CMAKE_CXX_COMPILER": "cl.exe" } }, + { + "name": "msvc-wine", + "hidden": true, + "generator": "Ninja", + "binaryDir": "${sourceDir}/out/build/${presetName}", + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Linux" + }, + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/cmake/msvc-wine-x86.cmake", + "CMAKE_EXPORT_COMPILE_COMMANDS": true + } + }, { "name": "x86", "hidden": true, @@ -81,7 +96,17 @@ { "name": "G2A-Debug", "inherits": [ "windows", "x86", "debug", "g2a", "config" ] }, { "name": "G2A-Release", "inherits": [ "windows", "x86", "release", "g2a", "config" ] }, { "name": "MP-Debug", "inherits": [ "windows", "x86", "debug", "mp", "config" ] }, - { "name": "MP-Release", "inherits": [ "windows", "x86", "release", "mp", "config" ] } + { "name": "MP-Release", "inherits": [ "windows", "x86", "release", "mp", "config" ] }, + { "name": "G1-Debug-msvc-wine", "inherits": [ "msvc-wine", "x86", "debug", "g1", "msvc-wine-config" ] }, + { "name": "G1-Release-msvc-wine", "inherits": [ "msvc-wine", "x86", "release", "g1", "msvc-wine-config" ] }, + { "name": "G1A-Debug-msvc-wine", "inherits": [ "msvc-wine", "x86", "debug", "g1a", "msvc-wine-config" ] }, + { "name": "G1A-Release-msvc-wine", "inherits": [ "msvc-wine", "x86", "release", "g1a", "msvc-wine-config" ] }, + { "name": "G2-Debug-msvc-wine", "inherits": [ "msvc-wine", "x86", "debug", "g2", "msvc-wine-config" ] }, + { "name": "G2-Release-msvc-wine", "inherits": [ "msvc-wine", "x86", "release", "g2", "msvc-wine-config" ] }, + { "name": "G2A-Debug-msvc-wine", "inherits": [ "msvc-wine", "x86", "debug", "g2a", "msvc-wine-config" ] }, + { "name": "G2A-Release-msvc-wine", "inherits": [ "msvc-wine", "x86", "release", "g2a", "msvc-wine-config" ] }, + { "name": "MP-Debug-msvc-wine", "inherits": [ "msvc-wine", "x86", "debug", "mp", "msvc-wine-config" ] }, + { "name": "MP-Release-msvc-wine", "inherits": [ "msvc-wine", "x86", "release", "mp", "msvc-wine-config" ] } ], "buildPresets": [ { "name": "G1-Debug", "configurePreset": "G1-Debug" }, @@ -93,6 +118,16 @@ { "name": "G2A-Debug", "configurePreset": "G2A-Debug" }, { "name": "G2A-Release", "configurePreset": "G2A-Release" }, { "name": "MP-Debug", "configurePreset": "MP-Debug" }, - { "name": "MP-Release", "configurePreset": "MP-Release" } + { "name": "MP-Release", "configurePreset": "MP-Release" }, + { "name": "G1-Debug-msvc-wine", "configurePreset": "G1-Debug-msvc-wine" }, + { "name": "G1-Release-msvc-wine", "configurePreset": "G1-Release-msvc-wine" }, + { "name": "G1A-Debug-msvc-wine", "configurePreset": "G1A-Debug-msvc-wine" }, + { "name": "G1A-Release-msvc-wine", "configurePreset": "G1A-Release-msvc-wine" }, + { "name": "G2-Debug-msvc-wine", "configurePreset": "G2-Debug-msvc-wine" }, + { "name": "G2-Release-msvc-wine", "configurePreset": "G2-Release-msvc-wine" }, + { "name": "G2A-Debug-msvc-wine", "configurePreset": "G2A-Debug-msvc-wine" }, + { "name": "G2A-Release-msvc-wine", "configurePreset": "G2A-Release-msvc-wine" }, + { "name": "MP-Debug-msvc-wine", "configurePreset": "MP-Debug-msvc-wine" }, + { "name": "MP-Release-msvc-wine", "configurePreset": "MP-Release-msvc-wine" } ] -} \ No newline at end of file +} diff --git a/CMakeUserConfigPresets.json b/CMakeUserConfigPresets.json index d025c00..0f26f7c 100644 --- a/CMakeUserConfigPresets.json +++ b/CMakeUserConfigPresets.json @@ -7,6 +7,13 @@ "cacheVariables": { "GAME_PATH": "C:\\Program Files (x86)\\JoWood\\Gothic II\\System\\Gothic2.exe" } + }, + { + "name": "msvc-wine-config", + "hidden": true, + "cacheVariables": { + "MSVC_WINE_ROOT": "$env{HOME}/my_msvc/opt/msvc" + } } ] -} \ No newline at end of file +} diff --git a/README.md b/README.md index 73c3ce2..19084ae 100644 --- a/README.md +++ b/README.md @@ -13,11 +13,18 @@ It provides a preconfigured base code designed to simplify the development proce Before you start making your own plugin, you need to install some software first, here's a full list of things that you'll need to install to be able to build the union plugin: - [git](https://git-scm.com/) **Required** for version control and to clone the project repository - [git lfs](https://git-lfs.com/) **Required** to clone the project repository binary files (e.g: `GothicVDFS.exe`) -- [CMake](https://cmake.org/) **Optional** if you plan to use Visual Studio -- [Visual Studio](https://visualstudio.microsoft.com/pl/) **Essential** for compiling the plugin using the MSVC toolset +- [CMake](https://cmake.org/) **Required** for command-line and Linux builds; optional when using Visual Studio's bundled CMake +- [Visual Studio](https://visualstudio.microsoft.com/pl/) **Required on Windows** for compiling the plugin using the MSVC toolset (make sure to install **C++ Workload** and **CMake Tools for Visual Studio**) -The Union plugin requires the MSVC toolset for compatibility, so alternative toolchains like MinGW are not supported. +Linux builds additionally require: +- [mstorsjo/msvc-wine](https://github.com/mstorsjo/msvc-wine) with the x86 MSVC and Windows SDK files installed +- [Ninja](https://ninja-build.org/) +- [Wine](https://www.winehq.org/) +- Clang/`clang-cl` for VS Code IntelliSense + +The Union plugin still uses the real MSVC toolset on Linux through msvc-wine. +Alternative toolchains such as MinGW are not used. # Fetching the source code @@ -32,6 +39,23 @@ git clone --recursive URL_TO_YOUR_REPO The plugin template contains special file called `CMakeUserConfigPresets.json` in which you can manully specify the variables for your own needs. +Linux users should set the root of their msvc-wine installation in the +`msvc-wine-config` preset: + +```json +{ + "name": "msvc-wine-config", + "hidden": true, + "cacheVariables": { + "MSVC_WINE_ROOT": "$env{HOME}/my_msvc/opt/msvc" + } +} +``` + +Change only the value when msvc-wine is installed elsewhere. Use an absolute +path or `$env{HOME}`; CMake presets do not expand `~`. The selected directory +must contain `bin/x86`, `VC/Tools/MSVC`, and `Windows Kits/10`. + **NOTE!** By default this file is tracked via git, but it shouldn't be, to not track it, type this command in root repo dir: ```bash @@ -45,7 +69,7 @@ Some of the common things that you should propably change are: - **project version** default value is **1.0.0.0** -# Building +# Building on Windows Follow the steps below to compile the plugin: @@ -70,6 +94,51 @@ Follow the steps below to compile the plugin: 1. Once everything is configured, click **Build Solution** (or press **Ctrl+Shift+B**). 2. If you've configured everything correctly, the build process should complete successfully. +# Building on Linux + +After installing msvc-wine and setting `MSVC_WINE_ROOT`, configure and build +the preset matching the desired game and build type: + +```bash +cmake --fresh --preset G2A-Release-msvc-wine +cmake --build --preset G2A-Release-msvc-wine +``` + +Available Linux presets follow the same `G1`, `G1A`, `G2`, `G2A`, and `MP` +plus `Debug` or `Release` naming as the Windows presets, with +`-msvc-wine` appended. For example: + +```text +G1-Debug-msvc-wine +G1-Release-msvc-wine +G2A-Release-msvc-wine +MP-Release-msvc-wine +``` + +Use MP only when the plugin supports all four engines. The Linux build creates +a 32-bit Windows DLL and packages the VDF in `out/build/PRESET_NAME/vdf/`. +`GothicVDFS.exe` runs through Wine automatically on Linux. + +## VS Code IntelliSense on Linux + +The repository includes configurations for Microsoft C/C++ and clangd. +Configuring a Linux preset generates `compile_commands.json` and selects that +preset for IntelliSense automatically. The current `.ipp` sources receive +editor-only compilation entries with the matching Gothic engine context; +these entries do not add objects to normal plugin builds. + +The C/C++ extension automatically selects the `Linux` configuration on Linux +and the `Win32` configuration on Windows. Windows uses the CMake Tools +configuration provider instead of the Linux compilation database. A root +symlink left by Linux is harmless on Windows and remains available when the +workspace is opened on Linux again. + +After configuring for the first time or changing presets, run +**C/C++: Reset IntelliSense Database** and **Developer: Reload Window** in VS +Code. Open `src/Plugin.ipp` and run **C/C++: Log Diagnostics** to verify that +the active translation unit contains the MSVC, Windows SDK, Gothic API, and +Union API include paths. + # Plugin installation Once the plugin has been compiled successfully, you can tell the game to load it during startup by placing it in `Game/System/autorun` subdirectory. diff --git a/cmake/configure_msvc_wine_intellisense.cmake b/cmake/configure_msvc_wine_intellisense.cmake new file mode 100644 index 0000000..8c5690b --- /dev/null +++ b/cmake/configure_msvc_wine_intellisense.cmake @@ -0,0 +1,97 @@ +function(configure_msvc_wine_intellisense target union_target) + if(NOT CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux" + OR NOT CMAKE_EXPORT_COMPILE_COMMANDS) + return() + endif() + + file(GLOB_RECURSE _editor_ipp_files CONFIGURE_DEPENDS + "${CMAKE_SOURCE_DIR}/src/*.ipp" + ) + + if(_editor_ipp_files) + set(_editor_context_header + "${CMAKE_BINARY_DIR}/intellisense/IntelliSenseContext.h" + ) + file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/intellisense") + file(GENERATE + OUTPUT "${_editor_context_header}" + CONTENT [=[#pragma once +#include +#include +]=] + ) + + set(_editor_engines "") + if(GOTHIC_API_G1) + list(APPEND _editor_engines + g1 Gothic_I_Classic Engine_G1 + ) + endif() + if(GOTHIC_API_G1A) + list(APPEND _editor_engines + g1a Gothic_I_Addon Engine_G1A + ) + endif() + if(GOTHIC_API_G2) + list(APPEND _editor_engines + g2 Gothic_II_Classic Engine_G2 + ) + endif() + if(GOTHIC_API_G2A) + list(APPEND _editor_engines + g2a Gothic_II_Addon Engine_G2A + ) + endif() + + while(_editor_engines) + list(POP_FRONT _editor_engines + _editor_suffix _editor_namespace _editor_engine + ) + set(_editor_target "${target}_intellisense_${_editor_suffix}") + + add_library("${_editor_target}" OBJECT EXCLUDE_FROM_ALL + ${_editor_ipp_files} + ) + set_source_files_properties(${_editor_ipp_files} + PROPERTIES LANGUAGE CXX + ) + target_include_directories("${_editor_target}" BEFORE PRIVATE + "${CMAKE_SOURCE_DIR}/userapi" + "${CMAKE_SOURCE_DIR}/src" + "${CMAKE_SOURCE_DIR}/signatures" + ) + target_compile_definitions("${_editor_target}" PRIVATE + "GOTHIC_NAMESPACE=${_editor_namespace}" + "ENGINE=${_editor_engine}" + ) + target_compile_options("${_editor_target}" PRIVATE + "/FI${_editor_context_header}" + ) + target_link_libraries("${_editor_target}" PRIVATE + "${union_target}" + ) + endwhile() + endif() + + set(_editor_compile_commands "${CMAKE_SOURCE_DIR}/compile_commands.json") + + if(IS_SYMLINK "${_editor_compile_commands}") + file(REMOVE "${_editor_compile_commands}") + elseif(EXISTS "${_editor_compile_commands}") + message(FATAL_ERROR + "${_editor_compile_commands} exists and is not a symlink.") + endif() + + file(CREATE_LINK + "${CMAKE_BINARY_DIR}/compile_commands.json" + "${_editor_compile_commands}" + SYMBOLIC + RESULT _editor_compile_commands_result + ) + + if(NOT _editor_compile_commands_result STREQUAL "0") + message(FATAL_ERROR + "Could not select the active editor compilation database: " + "${_editor_compile_commands_result}") + endif() +endfunction() diff --git a/cmake/msvc-wine-link.in b/cmake/msvc-wine-link.in new file mode 100644 index 0000000..5f01ece --- /dev/null +++ b/cmake/msvc-wine-link.in @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +set -euo pipefail + +msvc_wine_root='@MSVC_WINE_ROOT@' +source "$msvc_wine_root/bin/x86/msvcenv.sh" + +# msvc-wine 14.51 can emit the first WINEPATH entries as root-relative +# Windows paths (\home\...) instead of Wine drive paths (z:\home\...). +# link.exe needs the corrected SDK bin entry to spawn rc.exe for release +# manifests. +IFS=';' read -r -a wine_paths <<< "$WINEPATH" +for i in "${!wine_paths[@]}"; do + if [[ "${wine_paths[$i]}" == \\* ]]; then + wine_paths[$i]="z:${wine_paths[$i]}" + fi +done +WINEPATH=$(IFS=';'; printf '%s' "${wine_paths[*]}") +export WINEPATH + +exec "$msvc_wine_root/bin/x86/wine-msvc.sh" "$BINDIR/link.exe" "$@" diff --git a/cmake/msvc-wine-x86.cmake b/cmake/msvc-wine-x86.cmake new file mode 100644 index 0000000..40894d6 --- /dev/null +++ b/cmake/msvc-wine-x86.cmake @@ -0,0 +1,92 @@ +# Cross-compile 32-bit Windows binaries with mstorsjo/msvc-wine. +# A CMake cache value (for example from CMakeUserConfigPresets.json) takes +# precedence over the MSVC_WINE_ROOT environment variable and the default. +set(_MSVC_WINE_DEFAULT_ROOT "$ENV{HOME}/my_msvc/opt/msvc") +if(DEFINED ENV{MSVC_WINE_ROOT} AND NOT "$ENV{MSVC_WINE_ROOT}" STREQUAL "") + set(_MSVC_WINE_DEFAULT_ROOT "$ENV{MSVC_WINE_ROOT}") +endif() + +set(MSVC_WINE_ROOT "${_MSVC_WINE_DEFAULT_ROOT}" CACHE PATH + "Root of the installed msvc-wine toolchain") + +if(MSVC_WINE_ROOT STREQUAL "") + message(FATAL_ERROR + "MSVC_WINE_ROOT is empty. Set it in CMakeUserConfigPresets.json, " + "as an environment variable, or with -DMSVC_WINE_ROOT=/path/to/msvc.") +endif() + +set(_MSVC_WINE_BIN "${MSVC_WINE_ROOT}/bin/x86") + +file(GLOB _MSVC_WINE_MSVC_DIRS LIST_DIRECTORIES true + "${MSVC_WINE_ROOT}/VC/Tools/MSVC/*") +file(GLOB _MSVC_WINE_SDK_DIRS LIST_DIRECTORIES true + "${MSVC_WINE_ROOT}/Windows Kits/10/Include/*") +list(SORT _MSVC_WINE_MSVC_DIRS COMPARE NATURAL ORDER DESCENDING) +list(SORT _MSVC_WINE_SDK_DIRS COMPARE NATURAL ORDER DESCENDING) + +if(NOT _MSVC_WINE_MSVC_DIRS OR NOT _MSVC_WINE_SDK_DIRS) + message(FATAL_ERROR + "Could not locate the MSVC or Windows SDK include directories below " + "${MSVC_WINE_ROOT}. Set MSVC_WINE_ROOT in " + "CMakeUserConfigPresets.json, as an environment variable, or with " + "-DMSVC_WINE_ROOT=/path/to/msvc. The selected directory must contain " + "bin/x86, VC/Tools/MSVC, and Windows Kits/10.") +endif() + +list(GET _MSVC_WINE_MSVC_DIRS 0 _MSVC_WINE_MSVC_DIR) +list(GET _MSVC_WINE_SDK_DIRS 0 _MSVC_WINE_SDK_DIR) + +set(_MSVC_WINE_SYSTEM_INCLUDES + "${_MSVC_WINE_MSVC_DIR}/atlmfc/include" + "${_MSVC_WINE_MSVC_DIR}/include" + "${_MSVC_WINE_SDK_DIR}/shared" + "${_MSVC_WINE_SDK_DIR}/ucrt" + "${_MSVC_WINE_SDK_DIR}/um" + "${_MSVC_WINE_SDK_DIR}/winrt" +) + +foreach(_tool cl link rc mt) + if(NOT EXISTS "${_MSVC_WINE_BIN}/${_tool}") + message(FATAL_ERROR + "msvc-wine tool '${_tool}' was not found at " + "${_MSVC_WINE_BIN}/${_tool}. Set MSVC_WINE_ROOT to the directory " + "containing bin/x86.") + endif() +endforeach() + +set(CMAKE_SYSTEM_NAME Windows) +set(CMAKE_SYSTEM_PROCESSOR x86) + +configure_file( + "${CMAKE_CURRENT_LIST_DIR}/msvc-wine-link.in" + "${CMAKE_BINARY_DIR}/msvc-wine-link" + @ONLY +) +file(CHMOD "${CMAKE_BINARY_DIR}/msvc-wine-link" + PERMISSIONS + OWNER_READ OWNER_WRITE OWNER_EXECUTE + GROUP_READ GROUP_EXECUTE + WORLD_READ WORLD_EXECUTE +) + +set(CMAKE_C_COMPILER "${_MSVC_WINE_BIN}/cl" CACHE FILEPATH "" FORCE) +set(CMAKE_CXX_COMPILER "${_MSVC_WINE_BIN}/cl" CACHE FILEPATH "" FORCE) +set(CMAKE_LINKER "${CMAKE_BINARY_DIR}/msvc-wine-link" CACHE FILEPATH "" FORCE) +set(CMAKE_RC_COMPILER "${_MSVC_WINE_BIN}/rc" CACHE FILEPATH "" FORCE) +set(CMAKE_MT "${_MSVC_WINE_BIN}/mt" CACHE FILEPATH "" FORCE) + +# The Wine wrappers provide these paths through INCLUDE, which real MSVC can +# consume but Linux editor tooling cannot discover. Emitting them explicitly +# also puts them into compile_commands.json for clangd and VS Code cpptools. +set(CMAKE_C_STANDARD_INCLUDE_DIRECTORIES + "${_MSVC_WINE_SYSTEM_INCLUDES}" CACHE STRING "" FORCE) +set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES + "${_MSVC_WINE_SYSTEM_INCLUDES}" CACHE STRING "" FORCE) + +unset(_MSVC_WINE_BIN) +unset(_MSVC_WINE_DEFAULT_ROOT) +unset(_MSVC_WINE_MSVC_DIRS) +unset(_MSVC_WINE_SDK_DIRS) +unset(_MSVC_WINE_MSVC_DIR) +unset(_MSVC_WINE_SDK_DIR) +unset(_MSVC_WINE_SYSTEM_INCLUDES) diff --git a/cmake/run_gothic_vdfs.cmake b/cmake/run_gothic_vdfs.cmake new file mode 100644 index 0000000..ec5c276 --- /dev/null +++ b/cmake/run_gothic_vdfs.cmake @@ -0,0 +1,46 @@ +foreach(_required_variable + VDF_EXECUTABLE + VDF_SCRIPT + VDF_OUTPUT + VDF_WORKING_DIRECTORY) + if(NOT DEFINED ${_required_variable} + OR "${${_required_variable}}" STREQUAL "") + message(FATAL_ERROR "${_required_variable} was not provided.") + endif() +endforeach() + +if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux") + find_program(WINE_EXECUTABLE wine REQUIRED) + get_filename_component(_vdf_script_name "${VDF_SCRIPT}" NAME) + set(_vdf_command "${WINE_EXECUTABLE}" "${VDF_EXECUTABLE}") + set(_vdf_script_argument ".\\${_vdf_script_name}") +else() + file(TO_NATIVE_PATH "${VDF_SCRIPT}" _vdf_script_argument) + set(_vdf_command "${VDF_EXECUTABLE}") +endif() + +# GothicVDFS can return success after falling back to its GUI. Remove any old +# package first and verify that this invocation really produced a new one. +file(REMOVE "${VDF_OUTPUT}") + +execute_process( + COMMAND ${_vdf_command} /B "${_vdf_script_argument}" + WORKING_DIRECTORY "${VDF_WORKING_DIRECTORY}" + RESULT_VARIABLE _vdf_result + OUTPUT_VARIABLE _vdf_stdout + ERROR_VARIABLE _vdf_stderr +) + +if(NOT _vdf_result EQUAL 0) + message(FATAL_ERROR + "GothicVDFS failed with exit code ${_vdf_result}.\n" + "${_vdf_stdout}${_vdf_stderr}" + ) +endif() + +if(NOT EXISTS "${VDF_OUTPUT}") + message(FATAL_ERROR + "GothicVDFS returned success but did not create '${VDF_OUTPUT}'.\n" + "${_vdf_stdout}${_vdf_stderr}" + ) +endif() From ec95da6258d77bce21067e8db8da461d3917f415 Mon Sep 17 00:00:00 2001 From: Patrix9999 Date: Thu, 30 Jul 2026 18:45:44 +0200 Subject: [PATCH 2/3] Refactored `clangd` linux support: - Removed duplicated logic - Added `get_targeted_gothic_engines` utility function - Added named arguments into cmake utility functions for better readability - Corrected some mistakes in `CMakeLists.txt` (wrong placement of `BEFORE` specifier in `target_include_directories`) --- CMakeLists.txt | 27 ++++-- cmake/configure_msvc_wine_intellisense.cmake | 97 ++++++++------------ cmake/generate_ipp_translation_units.cmake | 36 +++----- cmake/get_targeted_gothic_engines.cmake | 29 ++++++ 4 files changed, 105 insertions(+), 84 deletions(-) create mode 100644 cmake/get_targeted_gothic_engines.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 09f0c6e..42f3067 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,11 +29,18 @@ if (GENERATE_UNION_API_DLL AND NOT GENERATE_VDF) message(WARNING "GENERATE_UNION_API_DLL is ON but GENERATE_VDF is OFF. UnionAPI.dll won't be included in the release.") endif() +file(GLOB_RECURSE IPP_FILES + "src/*.ipp" +) + file(GLOB_RECURSE SRC "src/*.cpp" ) -generate_ipp_translation_units(IPP_TRANSLATION_UNITS) +generate_ipp_translation_units( + INPUT ${IPP_FILES} + OUTPUT IPP_TRANSLATION_UNITS +) add_library(${CMAKE_PROJECT_NAME} SHARED) target_sources(${CMAKE_PROJECT_NAME} @@ -48,10 +55,13 @@ target_include_directories(${CMAKE_PROJECT_NAME} ) target_include_directories(${CMAKE_PROJECT_NAME} - BEFORE PRIVATE - "${CMAKE_CURRENT_SOURCE_DIR}/userapi" - "${CMAKE_CURRENT_SOURCE_DIR}/src" - "${CMAKE_CURRENT_SOURCE_DIR}/signatures" + PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/src" +) + +target_include_directories(${CMAKE_PROJECT_NAME} + BEFORE PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/userapi" ) target_add_versioninfo(${CMAKE_PROJECT_NAME} @@ -108,4 +118,9 @@ endif() add_subdirectory(dependencies) -configure_msvc_wine_intellisense(${CMAKE_PROJECT_NAME} union) +if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_EXPORT_COMPILE_COMMANDS) + configure_msvc_wine_intellisense( + TARGET ${CMAKE_PROJECT_NAME} + INPUT ${IPP_FILES} + ) +endif() \ No newline at end of file diff --git a/cmake/configure_msvc_wine_intellisense.cmake b/cmake/configure_msvc_wine_intellisense.cmake index 8c5690b..c9fcf7b 100644 --- a/cmake/configure_msvc_wine_intellisense.cmake +++ b/cmake/configure_msvc_wine_intellisense.cmake @@ -1,17 +1,21 @@ -function(configure_msvc_wine_intellisense target union_target) - if(NOT CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux" - OR NOT CMAKE_EXPORT_COMPILE_COMMANDS) +include(get_targeted_gothic_engines) + +function(configure_msvc_wine_intellisense) + cmake_parse_arguments(ARG "" "TARGET;INPUT" "" ${ARGN}) + + if (NOT DEFINED ARG_TARGET) + message(SEND_ERROR "configure_msvc_wine_intellisense function requires TARGET argument") return() endif() - file(GLOB_RECURSE _editor_ipp_files CONFIGURE_DEPENDS - "${CMAKE_SOURCE_DIR}/src/*.ipp" - ) + if (NOT DEFINED ARG_INPUT) + message(SEND_ERROR "configure_msvc_wine_intellisense function requires INPUT argument") + return() + endif() - if(_editor_ipp_files) - set(_editor_context_header - "${CMAKE_BINARY_DIR}/intellisense/IntelliSenseContext.h" - ) + if(ARG_INPUT) + set(_editor_context_header "${CMAKE_BINARY_DIR}/intellisense/IntelliSenseContext.h") + file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/intellisense") file(GENERATE OUTPUT "${_editor_context_header}" @@ -21,55 +25,37 @@ function(configure_msvc_wine_intellisense target union_target) ]=] ) - set(_editor_engines "") - if(GOTHIC_API_G1) - list(APPEND _editor_engines - g1 Gothic_I_Classic Engine_G1 - ) - endif() - if(GOTHIC_API_G1A) - list(APPEND _editor_engines - g1a Gothic_I_Addon Engine_G1A - ) - endif() - if(GOTHIC_API_G2) - list(APPEND _editor_engines - g2 Gothic_II_Classic Engine_G2 - ) - endif() - if(GOTHIC_API_G2A) - list(APPEND _editor_engines - g2a Gothic_II_Addon Engine_G2A - ) - endif() + get_targeted_gothic_engines(_editor_engines) while(_editor_engines) - list(POP_FRONT _editor_engines - _editor_suffix _editor_namespace _editor_engine - ) - set(_editor_target "${target}_intellisense_${_editor_suffix}") + list(POP_FRONT _editor_engines _editor_suffix _editor_namespace) - add_library("${_editor_target}" OBJECT EXCLUDE_FROM_ALL - ${_editor_ipp_files} - ) - set_source_files_properties(${_editor_ipp_files} + string(TOLOWER "${_editor_suffix}" _editor_suffix_lowercase) + set(_editor_target "${ARG_TARGET}_intellisense_${_editor_suffix_lowercase}") + + add_library("${_editor_target}" OBJECT EXCLUDE_FROM_ALL) + + target_sources(${_editor_target} PRIVATE ${ARG_INPUT}) + set_source_files_properties(${ARG_INPUT} PROPERTIES LANGUAGE CXX ) - target_include_directories("${_editor_target}" BEFORE PRIVATE - "${CMAKE_SOURCE_DIR}/userapi" - "${CMAKE_SOURCE_DIR}/src" - "${CMAKE_SOURCE_DIR}/signatures" - ) - target_compile_definitions("${_editor_target}" PRIVATE - "GOTHIC_NAMESPACE=${_editor_namespace}" - "ENGINE=${_editor_engine}" - ) - target_compile_options("${_editor_target}" PRIVATE - "/FI${_editor_context_header}" + + get_target_property(_target_private_include_dirs ${CMAKE_PROJECT_NAME} INCLUDE_DIRECTORIES) + target_include_directories("${_editor_target}" PRIVATE ${_target_private_include_dirs}) + + target_compile_definitions("${_editor_target}" + PRIVATE + "GOTHIC_NAMESPACE=${_editor_namespace}" + "ENGINE=ENGINE_${_editor_suffix}" ) - target_link_libraries("${_editor_target}" PRIVATE - "${union_target}" + + target_compile_options("${_editor_target}" + PRIVATE + "/FI${_editor_context_header}" ) + + get_target_property(_target_private_link_libs ${CMAKE_PROJECT_NAME} LINK_LIBRARIES) + target_link_libraries("${_editor_target}" PRIVATE ${_target_private_link_libs}) endwhile() endif() @@ -78,8 +64,7 @@ function(configure_msvc_wine_intellisense target union_target) if(IS_SYMLINK "${_editor_compile_commands}") file(REMOVE "${_editor_compile_commands}") elseif(EXISTS "${_editor_compile_commands}") - message(FATAL_ERROR - "${_editor_compile_commands} exists and is not a symlink.") + message(FATAL_ERROR "${_editor_compile_commands} exists and is not a symlink.") endif() file(CREATE_LINK @@ -90,8 +75,6 @@ function(configure_msvc_wine_intellisense target union_target) ) if(NOT _editor_compile_commands_result STREQUAL "0") - message(FATAL_ERROR - "Could not select the active editor compilation database: " - "${_editor_compile_commands_result}") + message(FATAL_ERROR "Could not select the active editor compilation database: ${_editor_compile_commands_result}") endif() endfunction() diff --git a/cmake/generate_ipp_translation_units.cmake b/cmake/generate_ipp_translation_units.cmake index d262662..320f613 100644 --- a/cmake/generate_ipp_translation_units.cmake +++ b/cmake/generate_ipp_translation_units.cmake @@ -1,27 +1,21 @@ -function(generate_ipp_translation_units out_variable) - set(ipp_translation_units "") - - file(GLOB_RECURSE IppFiles - "src/*.ipp" - ) +include(get_targeted_gothic_engines) - set(GothicEngines "") +function(generate_ipp_translation_units) + cmake_parse_arguments(ARG "" "INPUT;OUTPUT" "" ${ARGN}) - if(GOTHIC_API_G1) - list(APPEND GothicEngines G1 Gothic_I_Classic) - endif() + if (NOT DEFINED ARG_INPUT) + message(SEND_ERROR "generate_ipp_translation_units function requires INPUT argument") + return() + endif() - if(GOTHIC_API_G1A) - list(APPEND GothicEngines G1A Gothic_I_Addon) - endif() + if (NOT DEFINED ARG_OUTPUT) + message(SEND_ERROR "generate_ipp_translation_units function requires OUTPUT argument") + return() + endif() - if(GOTHIC_API_G2) - list(APPEND GothicEngines G2 Gothic_II_Classic) - endif() + set(ipp_translation_units "") - if(GOTHIC_API_G2A) - list(APPEND GothicEngines G2A Gothic_II_Addon) - endif() + get_targeted_gothic_engines(GothicEngines) # Generate helper variable that points to dir where .cpp translation units resides set(GENERATED_IPP_DIR "${CMAKE_CURRENT_BINARY_DIR}/src/generated_ipp") @@ -34,7 +28,7 @@ function(generate_ipp_translation_units out_variable) string(TOLOWER "${PlatformSuffix}" platform_suffix_lowercase) - foreach(ipp ${IppFiles}) + foreach(ipp ${ARG_INPUT}) # Extract relative src filepath string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/src" "" filepath "${ipp}") @@ -65,5 +59,5 @@ function(generate_ipp_translation_units out_variable) endforeach() endwhile() - set(${out_variable} ${ipp_translation_units} PARENT_SCOPE) + set(${ARG_OUTPUT} ${ipp_translation_units} PARENT_SCOPE) endfunction() \ No newline at end of file diff --git a/cmake/get_targeted_gothic_engines.cmake b/cmake/get_targeted_gothic_engines.cmake new file mode 100644 index 0000000..320f0a0 --- /dev/null +++ b/cmake/get_targeted_gothic_engines.cmake @@ -0,0 +1,29 @@ +function(get_targeted_gothic_engines out_var) + set(engines "") + + if(GOTHIC_API_G1) + list(APPEND engines + G1 Gothic_I_Classic + ) + endif() + + if(GOTHIC_API_G1A) + list(APPEND engines + G1A Gothic_I_Addon + ) + endif() + + if(GOTHIC_API_G2) + list(APPEND engines + G2 Gothic_II_Classic + ) + endif() + + if(GOTHIC_API_G2A) + list(APPEND engines + G2A Gothic_II_Addon + ) + endif() + + set(${out_var} "${engines}" PARENT_SCOPE) +endfunction() \ No newline at end of file From 9958d869f39245a8b5fba450956df2ae0d7fc199 Mon Sep 17 00:00:00 2001 From: fyryNy Date: Thu, 30 Jul 2026 19:01:22 +0200 Subject: [PATCH 3/3] Removed marking source files as C++ --- cmake/configure_msvc_wine_intellisense.cmake | 3 --- 1 file changed, 3 deletions(-) diff --git a/cmake/configure_msvc_wine_intellisense.cmake b/cmake/configure_msvc_wine_intellisense.cmake index c9fcf7b..b92266e 100644 --- a/cmake/configure_msvc_wine_intellisense.cmake +++ b/cmake/configure_msvc_wine_intellisense.cmake @@ -36,9 +36,6 @@ function(configure_msvc_wine_intellisense) add_library("${_editor_target}" OBJECT EXCLUDE_FROM_ALL) target_sources(${_editor_target} PRIVATE ${ARG_INPUT}) - set_source_files_properties(${ARG_INPUT} - PROPERTIES LANGUAGE CXX - ) get_target_property(_target_private_include_dirs ${CMAKE_PROJECT_NAME} INCLUDE_DIRECTORIES) target_include_directories("${_editor_target}" PRIVATE ${_target_private_include_dirs})