Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions CMakePresets.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,22 @@
"patch": 0
},
"configurePresets": [
{
"name": "msvc-wine",
"displayName": "Windows 32bit MSVC 2022 via Wine",
"generator": "Ninja",
"hidden": false,
"binaryDir": "${sourceDir}/build/${presetName}",
"cacheVariables": {
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON",
"CMAKE_MSVC_RUNTIME_LIBRARY": "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL",
"CMAKE_BUILD_TYPE": "Release",
"CMAKE_C_FLAGS": "/std:c17",
"CMAKE_CXX_FLAGS": "/std:c++20 /EHsc /Zc:__cplusplus",
"CMAKE_C_STANDARD": "17",
"RTS_FLAGS": "/W3"
}
},
{
"name": "vc6",
"displayName": "Windows 32bit VC6 Release",
Expand Down
8 changes: 4 additions & 4 deletions GeneralsMD/Code/Main/RTS.RC
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"
#include "winresrc.h"

/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
Expand All @@ -27,18 +27,18 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
// TEXTINCLUDE
//

1 TEXTINCLUDE DISCARDABLE
1 TEXTINCLUDE DISCARDABLE
BEGIN
"resource.h\0"
END

2 TEXTINCLUDE DISCARDABLE
2 TEXTINCLUDE DISCARDABLE
BEGIN
"#include ""afxres.h""\r\n"
"\0"
END

3 TEXTINCLUDE DISCARDABLE
3 TEXTINCLUDE DISCARDABLE
BEGIN
"\r\n"
"\0"
Expand Down
83 changes: 83 additions & 0 deletions resources/dockerbuild-msvc/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
FROM debian:12-slim

# Build arguments
ARG CMAKE_VERSION="3.31.6"
ARG GIT_VERSION="2.49.0"
ARG UID=1000
ARG GID=1000

# Install system dependencies (Wine, Python for msvc-wine, and build tools)
RUN apt-get update \
&& dpkg --add-architecture i386 \
&& apt-get install -y --no-install-recommends \
wget gpg unzip git ca-certificates \
python3 msitools \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

# Install Wine from WineHQ
RUN mkdir -pm755 /etc/apt/keyrings \
&& wget -O - https://dl.winehq.org/wine-builds/winehq.key | gpg --dearmor -o /etc/apt/keyrings/winehq-archive.key - \
&& wget -NP /etc/apt/sources.list.d/ https://dl.winehq.org/wine-builds/debian/dists/bookworm/winehq-bookworm.sources \
&& apt-get update \
&& apt-get install -y --no-install-recommends winehq-stable \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

# Setup build folder
RUN mkdir -p /build/tools /build/tmp /build/tmp/home \
&& chown -R ${UID}:${GID} /build

USER ${UID}:${GID}
WORKDIR /build/tools

# Install Windows CMake
RUN wget -q https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/cmake-${CMAKE_VERSION}-windows-x86_64.zip \
&& unzip -q cmake-${CMAKE_VERSION}-windows-x86_64.zip -d /build/tools/ \
&& mv /build/tools/cmake-${CMAKE_VERSION}-windows-x86_64 /build/tools/cmake \
&& rm -f *.zip

# Install Windows Git
RUN wget -q https://github.com/git-for-windows/git/releases/download/v${GIT_VERSION}.windows.1/MinGit-${GIT_VERSION}-64-bit.zip \
&& unzip -q MinGit-${GIT_VERSION}-64-bit.zip -d /build/tools/ \
&& mv /build/tools/cmd /build/tools/git \
&& rm -f *.zip

# Install Windows Ninja
RUN wget -q https://github.com/ninja-build/ninja/releases/download/v1.13.1/ninja-win.zip \
&& unzip -q ninja-win.zip -d /build/tools/ \
&& rm -f *.zip

# Install MSVC Build Tools via msvc-wine
RUN git clone --depth 1 https://github.com/mstorsjo/msvc-wine.git /build/tools/msvc-wine

# Download MSVC 2022 and Windows SDK (x86 target for 32-bit game)
# --msvc-version 17 = VS 2022, host x64, target x86
ENV WINEDEBUG=-all
ENV WINEARCH=win64
ENV WINEPREFIX=/build/prefix64
ENV HOME=/build/tmp/home

RUN /build/tools/msvc-wine/vsdownload.py \
--dest /build/tools/msvc \
--accept-license \
&& /build/tools/msvc-wine/install.sh /build/tools/msvc

# Setup environment for MSVC 2022
# The install.sh creates wrapper scripts in msvc/bin/x86/ and msvc/bin/x64/
# We use x86 for 32-bit compilation
ENV CC="Z:\\build\\tools\\msvc\\bin\\x86\\cl.exe"
ENV CXX="Z:\\build\\tools\\msvc\\bin\\x86\\cl.exe"
ENV PRESET=msvc-wine

# Create empty TEMP folder for linking
ENV TMP="Z:\\build\\tmp"
ENV TEMP="Z:\\build\\tmp"
ENV TEMPDIR="Z:\\build\\tmp"

WORKDIR /build/cnc

USER root
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
CMD ["/entrypoint.sh"]
110 changes: 110 additions & 0 deletions resources/dockerbuild-msvc/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
#!/bin/bash
set -euo pipefail
cd /build/cnc

# Use a fresh Wine prefix owned by the current user
export WINEPREFIX="/tmp/wineprefix"
mkdir -p "$WINEPREFIX"

# Initialize Wine prefix
wineboot --init 2>/dev/null || true

# MSVC paths - replicate what msvcenv.sh sets
BASE="Z:\\build\\tools\\msvc"
MSVCVER="14.50.35717"
SDKVER="10.0.26100.0"
ARCH="x86"

MSVCDIR="${BASE}\\VC\\Tools\\MSVC\\${MSVCVER}"
SDKBASE="${BASE}\\Windows Kits\\10"
SDKINCLUDE="${SDKBASE}\\Include\\${SDKVER}"
SDKLIB="${SDKBASE}\\Lib\\${SDKVER}"

# Real Windows executables (not the Unix wrapper scripts)
CL_WIN="${MSVCDIR}\\bin\\Hostx64\\${ARCH}\\cl.exe"
LINK_WIN="${MSVCDIR}\\bin\\Hostx64\\${ARCH}\\link.exe"

# Environment for cl.exe to find headers and libraries
export INCLUDE="${MSVCDIR}\\atlmfc\\include;${MSVCDIR}\\include;${SDKINCLUDE}\\shared;${SDKINCLUDE}\\ucrt;${SDKINCLUDE}\\um;${SDKINCLUDE}\\winrt"
export LIB="${MSVCDIR}\\atlmfc\\lib\\${ARCH};${MSVCDIR}\\lib\\${ARCH};${SDKLIB}\\ucrt\\${ARCH};${SDKLIB}\\um\\${ARCH}"
export LIBPATH="${LIB}"

# Wine needs the Hostx64/x64 dir in PATH for DLLs (vcruntime140.dll etc.)
export WINEPATH="${MSVCDIR}\\bin\\Hostx64\\${ARCH};${MSVCDIR}\\bin\\Hostx64\\x64;${SDKBASE}\\bin\\${SDKVER}\\x64"
export WINEDLLOVERRIDES="vcruntime140=n;vcruntime140_1=n"

BUILD_DIR="/build/cnc/build/${PRESET}"
#
# Symlink "Windows Kits" to a path without spaces so Wine doesn't generate
# 8.3 short names (e.g. WIND~2DP) that can't be resolved on Linux filesystems.
WINKITS="/build/tools/msvc/Windows Kits"
WINKITS_LINK="/build/tools/msvc/WindowsKits"
if [ -d "$WINKITS" ] && [ ! -e "$WINKITS_LINK" ]; then
ln -s "$WINKITS" "$WINKITS_LINK"
fi

RC_COMPILER="Z:/build/tools/msvc/WindowsKits/10/bin/${SDKVER}/x64/rc.exe"
RC_INCLUDE="Z:/build/tools/msvc/WindowsKits/10/Include/${SDKVER}"
RC_FLAGS="-I \"${RC_INCLUDE}/um\" -I \"${RC_INCLUDE}/shared\""

# Configure if needed
if [ "${FORCE_CMAKE:-}" = "true" ] || [ ! -f "${BUILD_DIR}/build.ninja" ]; then
rm -f "${BUILD_DIR}/CMakeCache.txt"

wine /build/tools/cmake/bin/cmake.exe \
--preset ${PRESET} \
-DCMAKE_SYSTEM="Windows" \
-DCMAKE_SYSTEM_NAME="Windows" \
-DCMAKE_SIZEOF_VOID_P=4 \
-DCMAKE_MAKE_PROGRAM="Z:/build/tools/ninja.exe" \
-DCMAKE_C_COMPILER="${CL_WIN}" \
-DCMAKE_CXX_COMPILER="${CL_WIN}" \
-DCMAKE_LINKER="${LINK_WIN}" \
-DCMAKE_C_COMPILER_ID=MSVC \
-DCMAKE_CXX_COMPILER_ID=MSVC \
-DCMAKE_C_COMPILER_VERSION=19.50.35726 \
-DCMAKE_CXX_COMPILER_VERSION=19.50.35726 \
-DMSVC_VERSION=1950 \
-DMSVC=1 \
-DCMAKE_C_STANDARD_COMPUTED_DEFAULT=17 \
-DCMAKE_C_EXTENSIONS_COMPUTED_DEFAULT=OFF \
-DCMAKE_CXX_STANDARD_COMPUTED_DEFAULT=20 \
-DCMAKE_CXX_EXTENSIONS_COMPUTED_DEFAULT=OFF \
-DCMAKE_SUPPRESS_REGENERATION=ON \
-DCMAKE_C_COMPILER_WORKS=1 \
-DCMAKE_CXX_COMPILER_WORKS=1 \
-DGIT_EXECUTABLE="Z:/build/tools/git/git.exe" \
-DCMAKE_FIND_ROOT_PATH_MODE_PROGRAM=NEVER \
-DCMAKE_FIND_ROOT_PATH_MODE_LIBRARY=ONLY \
-DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE=ONLY \
-DCMAKE_FIND_ROOT_PATH_MODE_PACKAGE=ONLY \
-DCMAKE_RC_COMPILER="${RC_COMPILER}" \
-DCMAKE_RC_FLAGS="${RC_FLAGS}" \
-B "${BUILD_DIR}"
fi

# Fix PCH paths: CMake generates Unix paths for /FI, /Yc, /Fp flags.
# MSVC under Wine needs Z: drive prefix.
echo "Fixing PCH paths for Wine..."
sed -i \
-e 's|/Yc/build/cnc/|/YcZ:/build/cnc/|g' \
-e 's|/Yu/build/cnc/|/YuZ:/build/cnc/|g' \
-e 's|/Fp/build/cnc/|/FpZ:/build/cnc/|g' \
-e 's|/FI/build/cnc/|/FIZ:/build/cnc/|g' \
-e 's| -c /build/cnc/| -c Z:/build/cnc/|g' \
-e 's| -c \\build\\cnc\\| -c Z:\\build\\cnc\\|g' \
-e 's| -I \\build\\cnc\\| -I Z:\\build\\cnc\\|g' \
-e 's| -I /build/cnc/| -I Z:/build/cnc/|g' \
-e 's|-LIBPATH:\\build\\cnc\\|-LIBPATH:Z:\\build\\cnc\\|g' \
-e 's|-LIBPATH:/build/cnc/|-LIBPATH:Z:/build/cnc/|g' \
"${BUILD_DIR}/build.ninja"

# Remove the CMake regeneration rule so Ninja doesn't overwrite our fixes
sed -i '/^build build.ninja:/,/^$/d' "${BUILD_DIR}/build.ninja"

FIXED=$(grep -c 'Z:/build/cnc/' "${BUILD_DIR}/build.ninja" || true)
echo "Fixed paths: ${FIXED} occurrences with Z: prefix"

# Build - pass MSVC environment into the Windows cmd session
cd "${BUILD_DIR}"
wine cmd /c "set TMP=Z:\build\tmp& set TEMP=Z:\build\tmp& set INCLUDE=${INCLUDE}& set LIB=${LIB}& set LIBPATH=${LIBPATH}& set PATH=${WINEPATH};%PATH%& Z:\build\tools\ninja.exe ${MAKE_TARGET:-z_generals}"
75 changes: 75 additions & 0 deletions scripts/docker-build-msvc.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#!/usr/bin/env bash
#
# Build script for compiling Generals/Zero Hour on Linux using Docker + Wine + MSVC 2022
#
# This script builds Windows executables using a Docker container with Wine and MSVC Build Tools.
# The resulting binaries are identical to native Windows VS2022 builds.
#
# Usage:
# ./scripts/docker-build-msvc.sh # Full build of Zero Hour (z_generals)
# ./scripts/docker-build-msvc.sh --clean # Clean build directory
# ./scripts/docker-build-msvc.sh --cmake # Force CMake reconfiguration
# ./scripts/docker-build-msvc.sh --interactive # Enter container shell
#

set -euo pipefail

SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
DOCKER_DIR="$PROJECT_DIR/resources/dockerbuild-msvc"
IMAGE_NAME="generals-msvc-build"
PRESET="msvc-wine"

MAKE_TARGET=""
FORCE_CMAKE="false"
INTERACTIVE="false"

while [[ $# -gt 0 ]]; do
case "$1" in
--target)
MAKE_TARGET="$2"
shift 2
;;
--clean)
echo "Cleaning build directory..."
rm -rf "$PROJECT_DIR/build/$PRESET"
exit 0
;;
--cmake)
FORCE_CMAKE="true"
shift
;;
--interactive)
INTERACTIVE="true"
shift
;;
*)
echo "Unknown option: $1"
exit 1
;;
esac
done

# Build Docker image (this takes a while the first time - downloads MSVC)
echo "Building Docker image (first run downloads ~3GB of MSVC tools)..."
docker build \
--build-arg UID="$(id -u)" \
--build-arg GID="$(id -g)" \
-t "$IMAGE_NAME" \
"$DOCKER_DIR"

DOCKER_ARGS=(
--rm
-v "$PROJECT_DIR:/build/cnc"
-e "PRESET=$PRESET"
-e "FORCE_CMAKE=$FORCE_CMAKE"
-e "MAKE_TARGET=${MAKE_TARGET}"
)

if [ "$INTERACTIVE" = "true" ]; then
echo "Entering interactive shell..."
docker run -it "${DOCKER_ARGS[@]}" "$IMAGE_NAME" /bin/bash
else
echo "Building..."
docker run "${DOCKER_ARGS[@]}" "$IMAGE_NAME"
fi
Loading