Skip to content

IDEA-CMAKE - Abandoned CMake / Ninja / vcpkg Adoption Exploration

Abandoned historical idea. This is not an active implementation plan, not Release 1 scope, and not current branch direction. Operator decision on 2026-05-21 closed the linked backlog record as WONT_DO. The supported build entrypoint remains python -m emule_workspace. Archived sections below preserve migration wording from the original note; read them as provenance, not scheduled work.

Explored target stack: CMake 3.25+ · Ninja · MSVC cl.exe (v143) · vcpkg · VS Code Archived assumption: Windows-only, no VS IDE, no direct MSBuild workflow.


Table of Contents

  1. Overview & Goals
  2. Prerequisites & Tooling Setup
  3. Repository Structure After Migration
  4. Phase 1 — Dependency CMakeLists
  5. Phase 2 — vcpkg Integration
  6. Phase 3 — Root CMakeLists.txt
  7. Phase 4 — Main App CMakeLists.txt
  8. Phase 5 — Build & Configure Commands
  9. Phase 6 — VS Code Integration
  10. Killing the Old VS Files
  11. Known Pain Points & Gotchas
  12. Migration Checklist

1. Overview & Goals

Why migrate?

The current build system is a tangle of hand-rolled .cmd scripts, PowerShell workspace glue, MSBuild .vcxproj files, and per-dependency VS project wrappers (the zlib vcxproj is literally generated from a template at configure time). The result is:

  • A full Visual Studio 2022 installation is required just to run msbuild.exe.
  • Build output directories are deeply nested paths baked into vcxproj files with $(Platform)\$(Configuration) path gymnastics.
  • Adding a new dependency means writing yet another wrapper project.
  • No standard way for editors other than VS to understand the build graph.

CMake + Ninja + vcpkg gives:

  • A single cmake --build command that works from any terminal with MSVC on PATH.
  • compile_commands.json generated by Ninja/CMake, understood by clangd, VS Code IntelliSense, and every modern C++ tooling.
  • vcpkg handles binary dependency resolution and caching.
  • Presets encode the four canonical configurations (x64-debug, x64-release, x86-debug, x86-release) in a checked-in JSON file.

What changes

Before After
emule.vcxproj eMule/srchybrid/CMakeLists.txt
Per-dep wrapper .vcxproj files Per-dep CMakeLists.txt or add_subdirectory
00-setup-and-build-release.cmd cmake --preset x64-release && cmake --build --preset x64-release
$(CryptoPpRoot) property sheet variable vcpkg installed package, found via find_package(cryptopp)
Bison/Flex custom build steps Removed — Parser.cpp / Scanner.cpp are pre-committed plain sources
_SpecialBootstrapNodes config Dropped entirely
46-DLL satellite i18n build Out of scope, unchanged / done separately
VS IDE required VS Build Tools only (no IDE needed)

What stays the same

  • Compiler: cl.exe (MSVC v143), Windows SDK 10.0.
  • App type: Win32 GUI, static MFC, Unicode.
  • Runtime: /MT Release, /MTd Debug.
  • Architectures: x86 (Win32) and x64.
  • Configurations: Debug and Release.
  • All source files, resource file (emule.rc), manifests.
  • Version.h is still manually maintained — no git-driven version generation.
  • Parser.cpp and Scanner.cpp are plain committed source files.
  • MediaInfo is a runtime-loaded DLL — no build-time change needed.

2. Prerequisites & Tooling Setup

2.1 Visual Studio Build Tools (cl.exe without the IDE)

You do not need the full VS 2022 IDE. Install VS Build Tools 2022 with the following workloads:

winget install --id Microsoft.VisualStudio.2022.BuildTools --override "--quiet --add Microsoft.VisualStudio.Workload.VCTools --add Microsoft.VisualStudio.Component.VC.Tools.x86.x64 --add Microsoft.VisualStudio.Component.Windows10SDK.19041 --includeRecommended"

This installs: - cl.exe, link.exe, lib.exe (v143 toolset) - Windows SDK 10.0 - MFC and ATL static libraries (the --includeRecommended flag pulls these in) - vcvarsall.bat at C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Auxiliary\Build\vcvarsall.bat

Verify MFC is present. After install, confirm that the static MFC lib directory exists:

dir "C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\14.xx.xxxxx\atlmfc\lib\x64\nafxcw.lib"

If it is missing, re-run the VS installer and add the component "C++ MFC for latest v143 build tools (x86 & x64)" (Microsoft.VisualStudio.Component.VC.MFC.ARM.Spectre is not what you want — look for Microsoft.VisualStudio.Component.VC.ATLMFC).

2.2 CMake 3.25+

winget install Kitware.CMake

Verify: cmake --version (must be >= 3.25.0).

CMake 3.25 is the minimum because: - target_precompile_headers with MSVC-specific generator expressions was solidified. - CMAKE_MSVC_RUNTIME_LIBRARY policy CMP0091 is set to NEW by default. - MSVC_RUNTIME_LIBRARY target property works reliably.

2.3 Ninja

winget install Ninja-build.Ninja

Or install via Scoop: scoop install ninja

Verify: ninja --version (must be >= 1.11).

Why Ninja and not the "Visual Studio" CMake generator? The Visual Studio generator produces .sln/.vcxproj files and delegates to MSBuild — the opposite of what we want. Ninja is a pure-dependency-graph executor that calls cl.exe directly. It also generates compile_commands.json when CMAKE_EXPORT_COMPILE_COMMANDS=ON.

2.4 vcpkg

# Choose a permanent home — NOT inside the repo
cd C:\tools
git clone https://github.com/microsoft/vcpkg.git
cd vcpkg
.\bootstrap-vcpkg.bat -disableMetrics

Set the environment variable (add to your user profile permanently):

[System.Environment]::SetEnvironmentVariable("VCPKG_ROOT", "C:\tools\vcpkg", "User")

You do not need to run vcpkg integrate install in the new CMake workflow — integration happens through the CMake toolchain file instead.

winget install Microsoft.VisualStudioCode
code --install-extension ms-vscode.cpptools
code --install-extension ms-vscode.cmake-tools
code --install-extension twxs.cmake

2.6 Setting up the MSVC environment without the IDE

Every build command must be run from an MSVC-activated terminal. There are two ways:

Option A — Developer Command Prompt shortcut (interactive)

Start Menu → "Developer Command Prompt for VS 2022 Build Tools". This runs vcvarsall.bat x64 automatically.

Option B — Activate in any terminal (for scripts)

call "C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Auxiliary\Build\vcvarsall.bat" x64

Use x86 for 32-bit builds, x64 for 64-bit builds. You must call the matching architecture variant before configuring CMake — CMake captures the compiler at configure time and the architecture must match the target.

Option C — VS Code terminal integration

Set in .vscode/settings.json (see Phase 6). The CMake Tools extension can call vcvarsall.bat automatically when using a kit.


3. Repository Structure After Migration

The migration adds new CMakeLists.txt files and a vcpkg.json manifest. Nothing is moved or deleted from existing subdirectories (except the files listed in Section 10). The tree below shows only new/changed items.

C:\prj\p2p\eMule\eMulebb\emulebb-build\
├── CMakeLists.txt                          ← NEW: top-level root
├── CMakePresets.json                       ← NEW: 4 build presets
├── vcpkg.json                              ← NEW: vcpkg manifest
├── vcpkg-configuration.json               ← NEW: vcpkg baseline pin
│
├── eMule/
│   └── srchybrid/
│       ├── CMakeLists.txt                  ← NEW: main app target
│       ├── emule.vcxproj                   ← KEEP (reference) then delete
│       ├── Stdafx.h / Stdafx.cpp           ← unchanged
│       ├── Parser.cpp / Parser.y           ← .cpp used as plain source
│       ├── Scanner.cpp / Scanner.l         ← .cpp used as plain source
│       ├── emule.rc                        ← unchanged
│       └── res/
│           ├── emuleWin32.manifest         ← unchanged
│           └── emulex64.manifest           ← unchanged
│
├── emulebb-cryptopp/
│   └── (source tree, not built by CMake — vcpkg provides cryptopp)
│
├── emulebb-id3lib/
│   ├── CMakeLists.txt                      ← NEW: id3lib static lib target
│   └── (existing source tree unchanged)
│
├── emulebb-mbedtls/
│   └── CMakeLists.txt                      ← EXISTING upstream, used as-is
│
├── emulebb-miniupnp/
│   └── miniupnpc/
│       └── CMakeLists.txt                  ← EXISTING upstream, used as-is
│
├── emulebb-resizablelib/
│   └── ResizableLib/
│       └── CMakeLists.txt                  ← NEW: ResizableLib static lib target
│
└── emulebb-zlib/
    └── CMakeLists.txt                      ← EXISTING upstream, used as-is

Key layout decisions:

  • The root CMakeLists.txt lives at the workspace root (eMulebb/), not inside the eMule/ subfolder. This is so the root can add_subdirectory all sibling dependency trees.
  • emulebb-cryptopp/ source is kept for reference but not compiled by CMake — vcpkg's cryptopp package replaces it entirely. You may archive or delete it after confirming the vcpkg build works.
  • vcpkg operates in manifest mode: vcpkg.json at the root declares dependencies, vcpkg installs them into build/<preset>/vcpkg_installed/ automatically during CMake configure.

4. Phase 1 — Dependency CMakeLists

4.1 zlib — use existing upstream CMakeLists

zlib already has a CMakeLists.txt. Add it as a subdirectory from the root. The only thing to control is disabling the shared library and the minizip addon:

# In root CMakeLists.txt (see Phase 3):
set(ZLIB_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
add_subdirectory(emulebb-zlib EXCLUDE_FROM_ALL)
# zlib's CMakeLists exports target "zlib" (static) or "zlibstatic" depending on version.
# Modern zlib (1.3+) always exports "zlibstatic" when BUILD_SHARED_LIBS=OFF.
# Alias for uniformity:
if(NOT TARGET ZLIB::ZLIB)
    add_library(ZLIB::ZLIB ALIAS zlibstatic)
endif()

Judgement call: We use add_subdirectory rather than FetchContent because the source is already vendored in the repo as emulebb-zlib/. FetchContent would re-download it at configure time, which is unnecessary and breaks offline builds.

4.2 mbedTLS — use existing upstream CMakeLists

mbedTLS upstream already has a CMakeLists.txt. Key options to set before add_subdirectory:

# In root CMakeLists.txt:
set(ENABLE_TESTING OFF CACHE BOOL "" FORCE)
set(ENABLE_PROGRAMS OFF CACHE BOOL "" FORCE)
set(USE_SHARED_MBEDTLS_LIBRARY OFF CACHE BOOL "" FORCE)
set(USE_STATIC_MBEDTLS_LIBRARY ON CACHE BOOL "" FORCE)
add_subdirectory(emulebb-mbedtls EXCLUDE_FROM_ALL)
# mbedTLS exports: MbedTLS::mbedtls, MbedTLS::mbedcrypto, MbedTLS::mbedx509

The MBEDTLS_ALLOW_PRIVATE_ACCESS preprocessor define is needed by the eMule app code (it accesses internal mbedTLS struct fields). This define must be set on the eMule app target, not on the mbedTLS library itself.

4.3 miniupnpc — use existing upstream CMakeLists

# In root CMakeLists.txt:
set(UPNPC_BUILD_SHARED OFF CACHE BOOL "" FORCE)
set(UPNPC_BUILD_TESTS OFF CACHE BOOL "" FORCE)
set(UPNPC_BUILD_SAMPLE OFF CACHE BOOL "" FORCE)
set(NO_GETADDRINFO OFF CACHE BOOL "" FORCE)
add_subdirectory(emulebb-miniupnp/miniupnpc EXCLUDE_FROM_ALL)
# miniupnpc exports target "libminiupnpc-static"

The MINIUPNP_STATICLIB define is needed by code that includes miniupnpc headers. Set it on the eMule app target (and propagate it as a public define on the miniupnpc target if needed — see Phase 4.3 detail in Phase 7).

4.4 id3lib — write CMakeLists.txt from scratch

id3lib is not in vcpkg mainstream and its autoconf build system does not work on Windows. The existing emulebb-id3lib/libprj/id3lib.vcxproj reveals everything needed:

  • Sources: all .cpp files in ../src/ (relative to libprj/)
  • Include dirs: ., .., ../include, ../include/id3, plus zlib headers
  • Defines: _LIB, HAVE_CONFIG_H, ID3LIB_LINKOPTION=1
  • Config header: config.h must come from config.h.win32
  • No PCH used in the library itself

Create C:\prj\p2p\eMule\eMulebb\emulebb-build\emulebb-id3lib\CMakeLists.txt:

cmake_minimum_required(VERSION 3.25)
project(id3lib LANGUAGES CXX)

# id3lib needs zlib. It must already be in scope via the parent project.
# We accept either the upstream target name or our alias.
if(NOT TARGET ZLIB::ZLIB)
    message(FATAL_ERROR "id3lib requires ZLIB::ZLIB target to be defined before add_subdirectory(emulebb-id3lib)")
endif()

add_library(id3lib STATIC
    src/c_wrapper.cpp
    src/field.cpp
    src/field_binary.cpp
    src/field_integer.cpp
    src/field_string_ascii.cpp
    src/field_string_unicode.cpp
    src/frame.cpp
    src/frame_impl.cpp
    src/frame_parse.cpp
    src/frame_render.cpp
    src/globals.cpp
    src/header.cpp
    src/header_frame.cpp
    src/header_tag.cpp
    src/helpers.cpp
    src/io.cpp
    src/io_decorators.cpp
    src/io_helpers.cpp
    src/misc_support.cpp
    src/mp3_parse.cpp
    src/readers.cpp
    src/spec.cpp
    src/tag.cpp
    src/tag_file.cpp
    src/tag_find.cpp
    src/tag_impl.cpp
    src/tag_parse.cpp
    src/tag_parse_lyrics3.cpp
    src/tag_parse_musicmatch.cpp
    src/tag_parse_v1.cpp
    src/tag_render.cpp
    src/utils.cpp
    src/writer_decorators.cpp
    src/writers.cpp
)

# The Windows config header lives at config.h.win32 in the root of the id3lib tree.
# We copy it to the build directory as "config.h" so #include "config.h" resolves.
configure_file(
    "${CMAKE_CURRENT_SOURCE_DIR}/config.h.win32"
    "${CMAKE_CURRENT_BINARY_DIR}/config.h"
    COPYONLY
)

target_include_directories(id3lib
    PUBLIC
        # Consumers need the public API headers
        "${CMAKE_CURRENT_SOURCE_DIR}/include"
        "${CMAKE_CURRENT_SOURCE_DIR}/include/id3"
    PRIVATE
        # Internal includes
        "${CMAKE_CURRENT_SOURCE_DIR}"
        "${CMAKE_CURRENT_SOURCE_DIR}/src"
        # config.h lives in the binary dir after configure_file above
        "${CMAKE_CURRENT_BINARY_DIR}"
)

target_compile_definitions(id3lib
    PUBLIC
        ID3LIB_LINKOPTION=1
    PRIVATE
        _LIB
        HAVE_CONFIG_H
        # Suppress MSVC warnings about deprecated POSIX names and unsafe CRT
        _CRT_SECURE_NO_WARNINGS
        _CRT_NONSTDC_NO_DEPRECATE
        # id3lib uses some old-style code that triggers these
        _SILENCE_ALL_CXX17_DEPRECATION_WARNINGS
)

target_link_libraries(id3lib PRIVATE ZLIB::ZLIB)

# Inherit MSVC runtime from the project-wide setting (set by root CMakeLists)
set_target_properties(id3lib PROPERTIES
    MSVC_RUNTIME_LIBRARY "${CMAKE_MSVC_RUNTIME_LIBRARY}"
)

Why configure_file instead of adding the source dir? The id3lib source files do #include "config.h" (without any path prefix). Placing config.h.win32 as config.h in the build directory and adding that to the include path is the cleanest approach — it avoids polluting the source tree and works with out-of-source builds.

Why no PCH for id3lib? The original vcxproj does not use a PCH for this library. The sources are heterogeneous enough that a PCH would not save meaningful time, and it adds complexity.

4.5 ResizableLib — write CMakeLists.txt from scratch

ResizableLib is an MFC extension library. It uses StdAfx.h/StdAfx.cpp as its PCH. The original vcxproj uses UseOfMfc=Static for the configurations we care about.

Create C:\prj\p2p\eMule\eMulebb\emulebb-build\emulebb-resizablelib\ResizableLib\CMakeLists.txt:

cmake_minimum_required(VERSION 3.25)
project(ResizableLib LANGUAGES CXX)

add_library(ResizableLib STATIC
    ResizableComboBox.cpp
    ResizableComboLBox.cpp
    ResizableDialog.cpp
    ResizableDialogEx.cpp
    ResizableFormView.cpp
    ResizableFrame.cpp
    ResizableGrip.cpp
    ResizableLayout.cpp
    ResizableMDIChild.cpp
    ResizableMDIFrame.cpp
    ResizableMinMax.cpp
    ResizableMsgSupport.cpp
    ResizablePage.cpp
    ResizablePageEx.cpp
    ResizableSheet.cpp
    ResizableSheetEx.cpp
    ResizableSheetState.cpp
    ResizableSplitterWnd.cpp
    ResizableState.cpp
    ResizableVersion.cpp
    ResizableWndState.cpp
    # StdAfx.cpp is the PCH creation unit — listed here and marked below
    StdAfx.cpp
)

target_include_directories(ResizableLib
    PUBLIC
        "${CMAKE_CURRENT_SOURCE_DIR}"
)

target_compile_definitions(ResizableLib
    PRIVATE
        WIN32
        _WINDOWS
        UNICODE
        _UNICODE
        _CRT_SECURE_NO_WARNINGS
)

# Static MFC: tell MSVC to link against the static MFC libraries.
# This is done via the /MT or /MTd runtime selection plus the AFX static define.
# With CMake's MSVC_RUNTIME_LIBRARY mechanism, setting the runtime to
# MultiThreaded (no DLL) is sufficient — MFC picks up the right lib.
set_target_properties(ResizableLib PROPERTIES
    MSVC_RUNTIME_LIBRARY "${CMAKE_MSVC_RUNTIME_LIBRARY}"
)

# PCH: StdAfx.h is the precompiled header, StdAfx.cpp creates it.
# CMake 3.16+ target_precompile_headers handles this correctly with MSVC.
target_precompile_headers(ResizableLib PRIVATE StdAfx.h)

# Mark StdAfx.cpp as the PCH creation unit. With MSVC, CMake generates /Yc
# on this file automatically when target_precompile_headers is used.
# No manual set_source_files_properties needed for the creation unit.

# All other .cpp files get /Yu (use PCH) automatically from target_precompile_headers.
# StdAfx.cpp gets /Yc automatically.

Important note on static MFC in a static library: When ResizableLib is a static .lib that is linked into the eMule executable, the MFC linkage decision is made at the executable level, not the library level. The library just needs to be compiled with the right runtime (/MT or /MTd). The eMule executable's CMakeLists.txt sets USE_STATIC_MFC which pulls in the static MFC headers via afxwin.h. There is no separate /MD vs /MT negotiation needed for the .lib itself beyond matching the runtime.


5. Phase 2 — vcpkg Integration

5.1 Manifest mode

vcpkg operates in two modes: classic mode (global vcpkg install cryptopp) and manifest mode (vcpkg.json in the project root). We use manifest mode because:

  • Dependencies are version-pinned and committed to the repo.
  • Each preset can get its own installed directory (build/<preset>/vcpkg_installed/).
  • No global vcpkg state pollution.

5.2 vcpkg.json

Create C:\prj\p2p\eMule\eMulebb\emulebb-build\vcpkg.json:

{
  "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg/master/scripts/vcpkg.schema.json",
  "name": "emule-broadband",
  "version": "0.72",
  "dependencies": [
    {
      "name": "cryptopp",
      "features": []
    }
  ]
}

Why only cryptopp? - zlib, mbedTLS, miniupnpc: already vendored with their own CMakeLists — add_subdirectory is used. - id3lib: not in vcpkg mainstream — custom CMakeLists written above. - ResizableLib: not in vcpkg — custom CMakeLists written above. - cryptopp: available in vcpkg as cryptopp, and the existing source tree in emulebb-cryptopp/ is a verbatim copy of the upstream anyway. vcpkg is the cleaner path.

5.3 vcpkg-configuration.json — baseline pin

Create C:\prj\p2p\eMule\eMulebb\emulebb-build\vcpkg-configuration.json:

{
  "default-registry": {
    "kind": "git",
    "baseline": "3508985146f1b1d248c67ead13f8f54be5b4f5da",
    "repository": "https://github.com/microsoft/vcpkg"
  },
  "registries": []
}

Update the baseline: Run git -C %VCPKG_ROOT% rev-parse HEAD and paste the hash here. This pins all package versions to a specific vcpkg commit, ensuring reproducible builds. Bump this intentionally when you want dependency updates.

5.4 Triplets for x86 and x64 static

vcpkg triplets encode the target architecture and linkage. The relevant triplets are:

CMake Preset vcpkg Triplet
x64-release, x64-debug x64-windows-static
x86-release, x86-debug x86-windows-static

The -static triplets build all vcpkg packages with /MT (static CRT). This matches our CMAKE_MSVC_RUNTIME_LIBRARY setting.

These triplets are set in CMakePresets.json (see Phase 6) via the CMake variable VCPKG_TARGET_TRIPLET.

5.5 How vcpkg integrates with CMake

The vcpkg toolchain file is passed to CMake via -DCMAKE_TOOLCHAIN_FILE. It:

  1. Sets up compiler detection to include vcpkg's installed headers.
  2. Provides find_package() support for vcpkg packages.
  3. Automatically installs missing packages when VCPKG_MANIFEST_MODE=ON (the default when vcpkg.json exists).

After CMake configure completes, vcpkg-installed headers are under:

build/<preset>/vcpkg_installed/<triplet>/include/

The cryptopp package exports a CMake target cryptopp::cryptopp via find_package(cryptopp CONFIG REQUIRED).


6. Phase 3 — Root CMakeLists.txt

Create C:\prj\p2p\eMule\eMulebb\emulebb-build\CMakeLists.txt:

cmake_minimum_required(VERSION 3.25)

# ---------------------------------------------------------------------------
# Policy declarations — must come before project()
# ---------------------------------------------------------------------------
# CMP0091: MSVC_RUNTIME_LIBRARY abstraction. NEW = use the property.
cmake_policy(SET CMP0091 NEW)
# CMP0077: option() honours existing variables. Prevents dependencies from
# overriding our cache variables set before add_subdirectory.
cmake_policy(SET CMP0077 NEW)

project(eMuleBroadband
    VERSION 0.72.0
    LANGUAGES CXX RC
)

# ---------------------------------------------------------------------------
# Global settings
# ---------------------------------------------------------------------------
# Enforce C++17 across all targets
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

# Windows-only guard
if(NOT WIN32)
    message(FATAL_ERROR "This project targets Windows only.")
endif()

# Generate compile_commands.json (used by clangd / VS Code IntelliSense)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

# ---------------------------------------------------------------------------
# MSVC runtime library selection
# Use the abstract property rather than injecting /MT flags manually.
# CMP0091 NEW makes this work correctly.
# ---------------------------------------------------------------------------
if(NOT DEFINED CMAKE_MSVC_RUNTIME_LIBRARY)
    set(CMAKE_MSVC_RUNTIME_LIBRARY
        "MultiThreaded$<$<CONFIG:Debug>:Debug>"
        CACHE STRING "MSVC runtime library selection" FORCE
    )
endif()
# This expands to:
#   Release  → MultiThreaded      → /MT
#   Debug    → MultiThreadedDebug → /MTd

# ---------------------------------------------------------------------------
# Output directories
# Keep binaries and libs in well-known locations within the build tree.
# ---------------------------------------------------------------------------
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")

# ---------------------------------------------------------------------------
# vcpkg integration
# The toolchain file is passed in via -DCMAKE_TOOLCHAIN_FILE (set in presets).
# find_package for vcpkg packages becomes available automatically.
# ---------------------------------------------------------------------------
find_package(cryptopp CONFIG REQUIRED)

# ---------------------------------------------------------------------------
# zlib
# ---------------------------------------------------------------------------
set(ZLIB_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
set(BUILD_SHARED_LIBS    OFF CACHE BOOL "" FORCE)
add_subdirectory(emulebb-zlib EXCLUDE_FROM_ALL)
# Provide a canonical ZLIB::ZLIB alias regardless of zlib version
if(TARGET zlibstatic AND NOT TARGET ZLIB::ZLIB)
    add_library(ZLIB::ZLIB ALIAS zlibstatic)
elseif(TARGET zlib AND NOT TARGET ZLIB::ZLIB)
    add_library(ZLIB::ZLIB ALIAS zlib)
endif()

# ---------------------------------------------------------------------------
# id3lib (custom CMakeLists written in Phase 1)
# Must come after zlib because id3lib links against ZLIB::ZLIB.
# ---------------------------------------------------------------------------
add_subdirectory(emulebb-id3lib EXCLUDE_FROM_ALL)

# ---------------------------------------------------------------------------
# mbedTLS
# ---------------------------------------------------------------------------
set(ENABLE_TESTING              OFF CACHE BOOL "" FORCE)
set(ENABLE_PROGRAMS             OFF CACHE BOOL "" FORCE)
set(USE_SHARED_MBEDTLS_LIBRARY  OFF CACHE BOOL "" FORCE)
set(USE_STATIC_MBEDTLS_LIBRARY  ON  CACHE BOOL "" FORCE)
# Disable TF-PSA-Crypto subproject programs/tests as well
set(TF_PSA_CRYPTO_BUILD_TESTING OFF CACHE BOOL "" FORCE)
add_subdirectory(emulebb-mbedtls EXCLUDE_FROM_ALL)

# ---------------------------------------------------------------------------
# miniupnpc
# ---------------------------------------------------------------------------
set(UPNPC_BUILD_SHARED  OFF CACHE BOOL "" FORCE)
set(UPNPC_BUILD_TESTS   OFF CACHE BOOL "" FORCE)
set(UPNPC_BUILD_SAMPLE  OFF CACHE BOOL "" FORCE)
set(UPNPC_NO_GETADDRINFO OFF CACHE BOOL "" FORCE)
add_subdirectory(emulebb-miniupnp/miniupnpc EXCLUDE_FROM_ALL)

# ---------------------------------------------------------------------------
# ResizableLib (custom CMakeLists written in Phase 1)
# ---------------------------------------------------------------------------
add_subdirectory(emulebb-resizablelib/ResizableLib EXCLUDE_FROM_ALL)

# ---------------------------------------------------------------------------
# Main application
# ---------------------------------------------------------------------------
add_subdirectory(eMule/srchybrid)

Why EXCLUDE_FROM_ALL? This prevents dependency targets from showing up in the default cmake --build target unless they are actually needed by another target. Without it, a bare cmake --build would try to compile everything including test programs inside mbedTLS and miniupnpc (which we disabled above, but EXCLUDE_FROM_ALL is a second line of defence).

Why cmake_policy(SET CMP0077 NEW)? Without this, option() calls inside dependency CMakeLists.txt can silently ignore the cache variables we set before add_subdirectory. With CMP0077 NEW, cache variables win.


7. Phase 4 — Main App CMakeLists.txt

7.1 Source file list: glob vs explicit

Decision: explicit list.

file(GLOB ...) is convenient but has a critical flaw: CMake does not re-run configure when you add or delete a source file in a globbed directory. You must manually re-run cmake --preset after adding a file, and it is easy to forget. With 247 source files that rarely change, an explicit list is more reliable and makes the build graph deterministic.

The explicit list is long but maintainable. It is organized by subsystem.

7.2 The full srchybrid/CMakeLists.txt

Create C:\prj\p2p\eMule\eMulebb\emulebb-build\eMule\srchybrid\CMakeLists.txt:

cmake_minimum_required(VERSION 3.25)

# ---------------------------------------------------------------------------
# Source files — explicit list
# ---------------------------------------------------------------------------
set(EMULE_SOURCES
    # Core / startup
    Emule.cpp
    EmuleDlg.cpp
    Version.cpp

    # File handling
    AbstractFile.cpp
    KnownFile.cpp
    KnownFileList.cpp
    PartFile.cpp
    PartFileConvert.cpp
    PartFileWriteThread.cpp
    SharedFileList.cpp
    ShareableFile.cpp
    StatisticFile.cpp
    CollectionFile.cpp
    SafeFile.cpp

    # Download system
    DownloadQueue.cpp
    DownloadClient.cpp
    DownloadClientsCtrl.cpp
    DownloadListCtrl.cpp

    # Upload system
    UploadQueue.cpp
    UploadClient.cpp
    UploadBandwidthThrottler.cpp
    UploadDiskIOThread.cpp
    UploadListCtrl.cpp

    # Network / sockets
    AsyncSocketEx.cpp
    ClientUDPSocket.cpp
    EMSocket.cpp
    EncryptedDatagramSocket.cpp
    EncryptedStreamSocket.cpp
    HttpClientReqSocket.cpp
    ListenSocket.cpp
    ServerConnect.cpp
    ServerSocket.cpp
    UDPSocket.cpp
    URLClient.cpp
    WebSocket.cpp
    BindAddressResolver.cpp
    TLSthreading.cpp

    # Server
    Server.cpp
    ServerList.cpp
    ServerListCtrl.cpp
    ServerWnd.cpp

    # Clients / credits
    BaseClient.cpp
    ClientCredits.cpp
    ClientList.cpp
    ClientListCtrl.cpp
    Friend.cpp
    FriendList.cpp
    FriendListCtrl.cpp

    # Kademlia
    kademlia/io/BufferedFileIO.cpp
    kademlia/io/ByteIO.cpp
    kademlia/io/DataIO.cpp
    kademlia/io/FileIO.cpp
    kademlia/io/IOException.cpp
    kademlia/kademlia/Entry.cpp
    kademlia/kademlia/Indexed.cpp
    kademlia/kademlia/Kademlia.cpp
    kademlia/kademlia/Prefs.cpp
    kademlia/kademlia/Search.cpp
    kademlia/kademlia/SearchManager.cpp
    kademlia/kademlia/UDPFirewallTester.cpp
    kademlia/net/KademliaUDPListener.cpp
    kademlia/net/PacketTracking.cpp
    kademlia/routing/Contact.cpp
    kademlia/routing/RoutingBin.cpp
    kademlia/routing/RoutingZone.cpp
    kademlia/utils/LookupHistory.cpp
    kademlia/utils/MiscUtils.cpp
    kademlia/utils/ThreadName.cpp
    kademlia/utils/UInt128.cpp

    # Hashing / crypto
    MD4.cpp
    MD5Sum.cpp
    SHA.cpp
    SHAHashSet.cpp
    AICHSyncThread.cpp
    FileIdentifier.cpp
    ClientCredits.cpp
    CorruptionBlackBox.cpp
    SelfTest.cpp

    # Search
    SearchDlg.cpp
    SearchFile.cpp
    SearchList.cpp
    SearchListCtrl.cpp
    SearchParamsWnd.cpp
    SearchResultsWnd.cpp
    ED2KLink.cpp
    ED2kLinkDlg.cpp

    # Bison/Flex generated — plain committed sources, NO generator step
    Parser.cpp
    Scanner.cpp

    # Preferences / settings
    Preferences.cpp
    PreferencesDlg.cpp
    PPgConnection.cpp
    PPgDebug.cpp
    PPgDirectories.cpp
    PPgDisplay.cpp
    PPgFiles.cpp
    PPgGeneral.cpp
    PPgIRC.cpp
    PPgMessages.cpp
    PPgNotify.cpp
    PPgScheduler.cpp
    PPgSecurity.cpp
    PPgServer.cpp
    PPgStats.cpp
    PPgTweaks.cpp
    PPgWebServer.cpp
    Scheduler.cpp
    Wizard.cpp
    PShtWiz1.cpp

    # Statistics / logging
    Statistics.cpp
    StatisticsDlg.cpp
    StatisticsTree.cpp
    Log.cpp
    PerfLog.cpp

    # UI — dialogs
    AddFriend.cpp
    AddSourceDlg.cpp
    ArchivePreviewDlg.cpp
    CatDialog.cpp
    ChatSelector.cpp
    ChatWnd.cpp
    ClientDetailDialog.cpp
    CollectionCreateDialog.cpp
    CollectionListCtrl.cpp
    CollectionViewDialog.cpp
    CommentDialog.cpp
    CommentDialogLst.cpp
    CommentListCtrl.cpp
    CreditsDlg.cpp
    CreditsThread.cpp
    DialogMinTrayBtn.cpp
    DirectDownloadDlg.cpp
    ExitBox.cpp
    FileDetailDialog.cpp
    FileDetailDialogInfo.cpp
    FileDetailDialogName.cpp
    FileDetailDlgStatistics.cpp
    FileInfoDialog.cpp
    HttpDownloadDlg.cpp
    InputBox.cpp
    IrcChannelListCtrl.cpp
    IrcChannelTabCtrl.cpp
    IrcMain.cpp
    IrcNickListCtrl.cpp
    IrcSocket.cpp
    IrcWnd.cpp
    KadContactHistogramCtrl.cpp
    KadContactListCtrl.cpp
    KadLookupGraph.cpp
    KadSearchListCtrl.cpp
    KademliaWnd.cpp
    ListViewSearchDlg.cpp
    ListViewWalkerPropertySheet.cpp
    MetaDataDlg.cpp
    MuleSystrayDlg.cpp
    NetworkInfoDlg.cpp
    PreviewDlg.cpp
    QueueListCtrl.cpp
    SMTPdialog.cpp
    SendMail.cpp
    SharedDirsTreeCtrl.cpp
    SharedFilesCtrl.cpp
    SharedFilesWnd.cpp
    SmileySelector.cpp
    TransferDlg.cpp
    TransferWnd.cpp
    TrayDialog.cpp

    # UI — controls / widgets
    3DPreviewControl.cpp
    BarShader.cpp
    BuddyButton.cpp
    ButtonsTabCtrl.cpp
    ClosableTabCtrl.cpp
    ColorButton.cpp
    ColourPopup.cpp
    ComboBoxEx2.cpp
    CustomAutoComplete.cpp
    DirectoryTreeCtrl.cpp
    DropDownButton.cpp
    DropTarget.cpp
    EditDelayed.cpp
    EditX.cpp
    EnBitmap.cpp
    GDIThread.cpp
    GradientStatic.cpp
    HTRichEditCtrl.cpp
    I18n.cpp
    IconStatic.cpp
    LayeredWindowHelperST.cpp
    ListBoxST.cpp
    ListCtrlEditable.cpp
    ListCtrlX.cpp
    MeterIcon.cpp
    MuleListCtrl.cpp
    MuleStatusBarCtrl.cpp
    MuleToolBarCtrl.cpp
    OScopeCtrl.cpp
    ProgressCtrlX.cpp
    Quantize.cpp
    RichEditCtrlX.cpp
    RichEditStream.cpp
    SplitterControl.cpp
    TabCtrl.cpp
    TaskbarNotifier.cpp
    TextToSpeech.cpp
    TitledMenu.cpp
    ToolBarCtrlX.cpp
    ToolTipCtrlX.cpp
    ToolbarWnd.cpp
    TrayMenuBtn.cpp
    TreeOptionsCtrl.cpp
    TreeOptionsCtrlEx.cpp
    TreePropSheet.cpp
    TreePropSheetPgFrame.cpp
    TreePropSheetPgFrameDef.cpp

    # Media / preview
    ArchiveRecovery.cpp
    CaptchaGenerator.cpp
    FrameGrabThread.cpp
    GZipFile.cpp
    IP2Country.cpp
    IPFilter.cpp
    IPFilterDlg.cpp
    MediaInfo.cpp
    MediaInfo_RIFF.cpp
    MediaInfo_RealMedia.cpp
    MediaInfo_WindowsMedia.cpp
    Pinger.cpp
    Preview.cpp
    RARFile.cpp
    ZIPFile.cpp

    # Misc utilities
    DeadSourceList.cpp
    Ini2.cpp
    Mdump.cpp
    OtherFunctions.cpp
    Packets.cpp
    StringConversion.cpp
    TimeTick.cpp
    WebServer.cpp
    UPnPImpl.cpp
    UPnPImplMiniLib.cpp
    UPnPImplWinServ.cpp
    UPnPImplWrapper.cpp

    # PCH creation unit — must be listed
    Stdafx.cpp

    # Resource
    emule.rc
)

# ---------------------------------------------------------------------------
# Target definition
# ---------------------------------------------------------------------------
add_executable(emule WIN32 ${EMULE_SOURCES})

# ---------------------------------------------------------------------------
# Platform / architecture manifest selection
# WIN32 target → emuleWin32.manifest
# x64  target → emulex64.manifest
# The manifest is embedded by the linker via /MANIFEST:EMBED /MANIFESTINPUT:
# We control this through target_sources + set_source_files_properties, or
# alternatively through linker flags. We use linker flags (see below) to
# stay consistent with how the vcxproj worked.
# ---------------------------------------------------------------------------

# ---------------------------------------------------------------------------
# Precompiled header
# ---------------------------------------------------------------------------
# target_precompile_headers tells CMake/MSVC:
#   - Compile Stdafx.cpp with /Yc"Stdafx.h" (create PCH)
#   - Compile all other sources with /Yu"Stdafx.h" (use PCH)
# The PCH file name becomes <target>.pch in the build dir.
# IMPORTANT: Stdafx.cpp must be in the source list (it is, above).
target_precompile_headers(emule PRIVATE Stdafx.h)

# ---------------------------------------------------------------------------
# Include directories
# ---------------------------------------------------------------------------
# Find the MSVC ATLMFC directory for MFC headers.
# When invoked from a vcvarsall-activated environment, the compiler knows
# where MFC lives. We still set it explicitly so CMake can show it in the
# compilation database.
cmake_path(GET CMAKE_CXX_COMPILER PARENT_PATH _msvc_bin_dir)
# Walk up from bin/<arch>/ to the MSVC toolset root
cmake_path(GET _msvc_bin_dir PARENT_PATH _msvc_hostarch_dir)
cmake_path(GET _msvc_hostarch_dir PARENT_PATH _msvc_tools_ver_dir)
cmake_path(GET _msvc_tools_ver_dir PARENT_PATH _msvc_tools_dir)   # VC\Tools\MSVC
cmake_path(GET _msvc_tools_dir PARENT_PATH _vc_dir)               # VC
set(_atlmfc_include "${_vc_dir}/../../Tools/MSVC/${CMAKE_VS_PLATFORM_TOOLSET_VERSION}/atlmfc/include")
# Fallback: use environment variable set by vcvarsall
if(NOT EXISTS "${_atlmfc_include}" AND DEFINED ENV{VCToolsInstallDir})
    set(_atlmfc_include "$ENV{VCToolsInstallDir}/atlmfc/include")
endif()

target_include_directories(emule PRIVATE
    # srchybrid itself (. and ..)
    "${CMAKE_CURRENT_SOURCE_DIR}"
    "${CMAKE_CURRENT_SOURCE_DIR}/.."
    # id3lib public headers
    # (these come via the target link, but explicit here helps IntelliSense)
    # mbedTLS, miniupnpc headers come via target_link_libraries below
    # MFC / ATL headers — required for static MFC compilation
    "${_atlmfc_include}"
)

# ---------------------------------------------------------------------------
# Compile definitions
# ---------------------------------------------------------------------------
target_compile_definitions(emule PRIVATE
    # Project-specific
    ID3LIB_LINKOPTION=1
    MINIUPNP_STATICLIB
    SUPPORT_LARGE_FILES
    MBEDTLS_ALLOW_PRIVATE_ACCESS

    # Windows targeting
    WIN32
    _WINDOWS
    UNICODE
    _UNICODE
    WINVER=0x0601
    _WIN32_WINNT=0x0601
    WIN32_LEAN_AND_MEAN

    # MFC static link: AFX_STATIC is not the right macro.
    # For static MFC, we rely on the /MT runtime + the MFC lib selection
    # which is driven by #pragma comment(lib, ...) inside afxwin.h.
    # No additional define is needed beyond what the vcxproj had.

    # Debug: inject _DEBUG (cl.exe does this automatically for /MTd,
    # but being explicit avoids surprises with CMake generator expressions)
    $<$<CONFIG:Debug>:_DEBUG>
    $<$<CONFIG:Release>:NDEBUG>

    # Suppress legacy CRT warnings
    _CRT_SECURE_NO_WARNINGS
    _CRT_NONSTDC_NO_DEPRECATE
)

# ---------------------------------------------------------------------------
# Compiler flags
# ---------------------------------------------------------------------------
target_compile_options(emule PRIVATE
    # All warnings
    /W4

    # Standards conformance
    /Zc:throwingNew       # STL assumption: operator new throws, never returns null
    /Zc:inline            # Remove unreferenced COMDAT
    /utf-8                # Source and execution charset UTF-8

    # x86 only: /safeseh (structured exception handler table)
    $<$<STREQUAL:${CMAKE_GENERATOR_PLATFORM},Win32>:/safeseh>

    # Release-only optimizations
    $<$<CONFIG:Release>:/O2>          # Maximize speed
    $<$<CONFIG:Release>:/Oi>          # Intrinsic functions
    $<$<CONFIG:Release>:/Oy->         # Omit frame pointers  (/Oy- = omit, counterintuitive)
                                       # NOTE: /Oy- means "disable omit frame pointers" in MSVC.
                                       # The vcxproj <OmitFramePointers>true</OmitFramePointers>
                                       # maps to /Oy (no minus). Use /Oy here.
    $<$<CONFIG:Release>:/Oy>          # Correct flag for "omit frame pointers"
    $<$<CONFIG:Release>:/GL>          # Whole program optimization (enables LTCG)
    $<$<CONFIG:Release>:/Gy>          # Function-level linking (COMDAT)

    # Debug
    $<$<CONFIG:Debug>:/Od>            # Disable optimization
    $<$<CONFIG:Debug>:/Zi>            # Full debug info
    $<$<CONFIG:Debug>:/D _DEBUG>      # Redundant with compile_definitions but harmless
)

# Remove the duplicate /Oy- — see note above. Only /Oy is needed.
# (The two lines above are intentionally left so the reader sees the correction inline.)

# ---------------------------------------------------------------------------
# Link libraries
# ---------------------------------------------------------------------------
target_link_libraries(emule PRIVATE
    # Dependency targets from add_subdirectory
    id3lib
    ResizableLib
    ZLIB::ZLIB
    MbedTLS::mbedtls
    MbedTLS::mbedcrypto
    MbedTLS::mbedx509
    libminiupnpc-static

    # vcpkg cryptopp
    cryptopp::cryptopp

    # Windows SDK libraries
    ADSIId.lib      # ADSI (Active Directory) — used for auth
    bcrypt.lib
    crypt32.lib
    delayimp.lib    # Required for /DELAYLOAD linker flag
    iphlpapi.lib
    version.lib
    winmm.lib
    ws2_32.lib
)

# ---------------------------------------------------------------------------
# Linker flags
# ---------------------------------------------------------------------------
# Note: target_link_options appends flags. MSVC link.exe flags use / prefix.

# Delay-load DLLs — loaded on first use, not at process startup.
# /DELAYLOAD requires delayimp.lib (added above).
set(_delay_load_flags
    /DELAYLOAD:gdiplus.dll
    /DELAYLOAD:msimg32.dll
    /DELAYLOAD:oleacc.dll
    /DELAYLOAD:ws2_32.dll
)

# Ignore specific default libraries — these are pulled in by MFC/ATL headers
# on some SDK configurations but we don't want them.
set(_ignore_libs
    /NODEFAULTLIB:mpr.lib
    /NODEFAULTLIB:rasapi32.lib
    /NODEFAULTLIB:secur32.lib
    /NODEFAULTLIB:sensapi.lib
    /NODEFAULTLIB:vfw32.lib
)

# Manifest embedding — embed the platform-specific manifest.
# The manifest file name is: emuleWin32.manifest (x86) or emulex64.manifest (x64).
# We use a generator expression to select the right file.
# /MANIFEST:EMBED embeds the manifest into the PE.
# /MANIFESTINPUT: provides additional manifest fragments to merge.
set(_manifest_file
    "$<IF:$<STREQUAL:${CMAKE_GENERATOR_PLATFORM},Win32>,${CMAKE_CURRENT_SOURCE_DIR}/res/emuleWin32.manifest,${CMAKE_CURRENT_SOURCE_DIR}/res/emulex64.manifest>"
)

target_link_options(emule PRIVATE
    ${_delay_load_flags}
    ${_ignore_libs}

    # Embed manifest
    /MANIFEST:EMBED
    /MANIFESTINPUT:${_manifest_file}

    # Release: LTCG + COMDAT folding + reference optimization
    $<$<CONFIG:Release>:/LTCG>
    $<$<CONFIG:Release>:/OPT:ICF>     # Identical COMDAT folding
    $<$<CONFIG:Release>:/OPT:REF>     # Remove unreferenced code/data

    # Debug: no LTCG, keep all code
    $<$<CONFIG:Debug>:/DEBUG:FULL>
    $<$<CONFIG:Debug>:/OPT:NOICF>
    $<$<CONFIG:Debug>:/OPT:NOREF>

    # Windows subsystem (redundant with WIN32 keyword on add_executable but explicit)
    /SUBSYSTEM:WINDOWS
)

# ---------------------------------------------------------------------------
# Static MFC linkage
# ---------------------------------------------------------------------------
# CMake does not have a built-in "use static MFC" switch for non-VS generators.
# When compiling with /MT(d), MFC's own headers use #pragma comment(lib, ...) to
# pull in the static MFC libraries (nafxcw.lib / nafxcwd.lib) automatically —
# provided the ATLMFC lib directory is on the library search path.
#
# vcvarsall.bat sets LIB to include the ATLMFC lib dir. In a Ninja build
# invoked from an activated prompt, this just works.
#
# The additional macro _AFXDLL must NOT be defined (that enables MFC DLL mode).
# Setting UNICODE and _UNICODE is sufficient.
#
# If you see linker errors about nafxcw.lib, add this:
# target_link_directories(emule PRIVATE "$ENV{VCToolsInstallDir}/atlmfc/lib/x64")
# (or x86 as appropriate — use a generator expression for arch)
target_link_directories(emule PRIVATE
    "$<IF:$<STREQUAL:${CMAKE_GENERATOR_PLATFORM},Win32>,$ENV{VCToolsInstallDir}/atlmfc/lib/x86,$ENV{VCToolsInstallDir}/atlmfc/lib/x64>"
)

# ---------------------------------------------------------------------------
# MSVC runtime — already set globally in root CMakeLists.
# Set it explicitly on the target as well for belt-and-suspenders.
# ---------------------------------------------------------------------------
set_target_properties(emule PROPERTIES
    MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>"
)

# ---------------------------------------------------------------------------
# Output name
# ---------------------------------------------------------------------------
set_target_properties(emule PROPERTIES
    OUTPUT_NAME "emule"
    PDB_NAME    "emule"
)

Important correction on /Oy vs /Oy-: In MSVC, /Oy means "omit frame pointers" and /Oy- means "do not omit frame pointers". The MSBuild <OmitFramePointers>true</OmitFramePointers> maps to /Oy. The target_compile_options block above has a note about this — remove the /Oy-> line and keep only $<$<CONFIG:Release>:/Oy>.

A note on WIN32 keyword on add_executable: add_executable(emule WIN32 ...) passes /SUBSYSTEM:WINDOWS to the linker and sets WinMain as the entry point instead of main. This is what the vcxproj <SubSystem>Windows</SubSystem> did.

A note on the RC file: Listing emule.rc in the source list is sufficient. CMake detects .rc files and compiles them with rc.exe automatically when the RC language is enabled in project(). The CMAKE_RC_FLAGS variable can be used to pass additional flags if needed (e.g., include paths for the RC compiler).


8. Phase 5 — Build & Configure Commands

All commands must be run from an MSVC-activated terminal (i.e., after vcvarsall.bat).

The working directory for all commands is C:\prj\p2p\eMule\eMulebb\emulebb-build\ (the workspace root where CMakeLists.txt and CMakePresets.json live).

8.1 First-time setup (run once)

rem Activate MSVC environment for x64 (needed for configure step)
call "C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Auxiliary\Build\vcvarsall.bat" x64

rem Set VCPKG_ROOT if not already in environment
set VCPKG_ROOT=C:\tools\vcpkg

cd C:\prj\p2p\eMule\eMulebb\emulebb-build

8.2 Configure — x64 Release

cmake --preset x64-release

This: 1. Reads CMakePresets.json, finds the x64-release preset. 2. Creates build/x64-release/. 3. Invokes vcpkg to install cryptopp into build/x64-release/vcpkg_installed/x64-windows-static/. 4. Runs cmake -G Ninja -A x64 -DCMAKE_BUILD_TYPE=Release ... internally. 5. Generates build/x64-release/build.ninja and build/x64-release/compile_commands.json.

Expected output (abbreviated):

-- Running vcpkg install
-- Installing: x64-windows-static cryptopp
-- Configuring done
-- Build files have been written to: C:/prj/p2p/eMulebb/build/x64-release

8.3 Build — x64 Release

cmake --build --preset x64-release

Or with a specific job count:

cmake --build --preset x64-release --parallel 8

Output binary: build/x64-release/bin/emule.exe

8.4 Configure — x64 Debug

call "C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Auxiliary\Build\vcvarsall.bat" x64
cmake --preset x64-debug

8.5 Build — x64 Debug

cmake --build --preset x64-debug

Output binary: build/x64-debug/bin/emule.exe

8.6 Configure — x86 Release

rem IMPORTANT: vcvarsall for x86, not x64
call "C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Auxiliary\Build\vcvarsall.bat" x86
cmake --preset x86-release

Why call vcvarsall.bat x86 for x86 builds? CMake captures the compiler path at configure time. If you run vcvarsall x64 and then cmake --preset x86-release, you get a 64-bit compiler trying to target x86 cross-compilation. While MSVC supports cross-compilation (vcvarsall.bat amd64_x86), the simplest and most reliable approach is to activate the native x86 environment. The CMakePresets.json also sets CMAKE_GENERATOR_PLATFORM to Win32 which further guides the selection.

8.7 Build — x86 Release

cmake --build --preset x86-release

8.8 Incremental rebuild

CMake/Ninja automatically detects changed files. Just re-run the build command:

cmake --build --preset x64-release

Ninja will only recompile changed translation units.

8.9 Clean build

cmake --build --preset x64-release --target clean

Or delete the build directory entirely:

rmdir /s /q build\x64-release
cmake --preset x64-release
cmake --build --preset x64-release

8.10 Verbose build (to see exact cl.exe command lines)

cmake --build --preset x64-release --verbose

Or set VERBOSE=1:

set VERBOSE=1
cmake --build --preset x64-release

9. Phase 6 — VS Code Integration

9.1 CMakePresets.json

Create C:\prj\p2p\eMule\eMulebb\emulebb-build\CMakePresets.json:

{
  "version": 6,
  "cmakeMinimumRequired": {
    "major": 3,
    "minor": 25,
    "patch": 0
  },
  "configurePresets": [
    {
      "name": "base",
      "hidden": true,
      "generator": "Ninja",
      "binaryDir": "${sourceDir}/build/${presetName}",
      "toolchainFile": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake",
      "cacheVariables": {
        "CMAKE_EXPORT_COMPILE_COMMANDS": "ON",
        "VCPKG_MANIFEST_MODE": "ON",
        "VCPKG_MANIFEST_DIR": "${sourceDir}"
      }
    },
    {
      "name": "x64-debug",
      "displayName": "x64 Debug",
      "description": "64-bit Debug build with /MTd",
      "inherits": "base",
      "architecture": {
        "value": "x64",
        "strategy": "set"
      },
      "cacheVariables": {
        "CMAKE_BUILD_TYPE": "Debug",
        "CMAKE_GENERATOR_PLATFORM": "x64",
        "VCPKG_TARGET_TRIPLET": "x64-windows-static",
        "CMAKE_MSVC_RUNTIME_LIBRARY": "MultiThreadedDebug"
      }
    },
    {
      "name": "x64-release",
      "displayName": "x64 Release",
      "description": "64-bit Release build with /MT and LTO",
      "inherits": "base",
      "architecture": {
        "value": "x64",
        "strategy": "set"
      },
      "cacheVariables": {
        "CMAKE_BUILD_TYPE": "Release",
        "CMAKE_GENERATOR_PLATFORM": "x64",
        "VCPKG_TARGET_TRIPLET": "x64-windows-static",
        "CMAKE_MSVC_RUNTIME_LIBRARY": "MultiThreaded"
      }
    },
    {
      "name": "x86-debug",
      "displayName": "x86 Debug",
      "description": "32-bit Debug build with /MTd",
      "inherits": "base",
      "architecture": {
        "value": "x86",
        "strategy": "set"
      },
      "cacheVariables": {
        "CMAKE_BUILD_TYPE": "Debug",
        "CMAKE_GENERATOR_PLATFORM": "Win32",
        "VCPKG_TARGET_TRIPLET": "x86-windows-static",
        "CMAKE_MSVC_RUNTIME_LIBRARY": "MultiThreadedDebug"
      }
    },
    {
      "name": "x86-release",
      "displayName": "x86 Release",
      "description": "32-bit Release build with /MT and LTO",
      "inherits": "base",
      "architecture": {
        "value": "x86",
        "strategy": "set"
      },
      "cacheVariables": {
        "CMAKE_BUILD_TYPE": "Release",
        "CMAKE_GENERATOR_PLATFORM": "Win32",
        "VCPKG_TARGET_TRIPLET": "x86-windows-static",
        "CMAKE_MSVC_RUNTIME_LIBRARY": "MultiThreaded"
      }
    }
  ],
  "buildPresets": [
    {
      "name": "x64-debug",
      "configurePreset": "x64-debug",
      "displayName": "x64 Debug"
    },
    {
      "name": "x64-release",
      "configurePreset": "x64-release",
      "displayName": "x64 Release"
    },
    {
      "name": "x86-debug",
      "configurePreset": "x86-debug",
      "displayName": "x86 Debug"
    },
    {
      "name": "x86-release",
      "configurePreset": "x86-release",
      "displayName": "x86 Release"
    }
  ]
}

Notes on preset design:

  • "version": 6 requires CMake 3.25+.
  • toolchainFile uses $env{VCPKG_ROOT} which reads the VCPKG_ROOT environment variable. If the user has not set it, configure will fail with a clear error.
  • "strategy": "set" for architecture means CMake sets CMAKE_GENERATOR_PLATFORM from the architecture field. This is necessary for Ninja, which does not use the VS generator's -A flag mechanism.
  • Each preset gets its own binaryDir via ${presetName}, so all four build trees coexist without interference.

9.2 VS Code settings

Create C:\prj\p2p\eMule\eMulebb\emulebb-build\.vscode\settings.json (or merge into existing):

{
  "cmake.useCMakePresets": "always",
  "cmake.defaultConfigurePreset": "x64-debug",
  "cmake.defaultBuildPreset": "x64-debug",
  "cmake.generator": "Ninja",
  "cmake.buildDirectory": "${workspaceFolder}/build/${buildPreset}",
  "cmake.sourceDirectory": "${workspaceFolder}",
  "cmake.configureOnOpen": false,
  "cmake.automaticReconfigure": false,
  "cmake.exportCompileCommandsFile": true,

  "C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools",
  "C_Cpp.default.compileCommands": "${workspaceFolder}/build/x64-debug/compile_commands.json",
  "C_Cpp.intelliSenseEngine": "default",

  "terminal.integrated.env.windows": {
    "VCPKG_ROOT": "C:\\tools\\vcpkg"
  },

  "cmake.environment": {
    "VCPKG_ROOT": "C:\\tools\\vcpkg"
  }
}

Notes:

  • "cmake.useCMakePresets": "always" tells the CMake Tools extension to use CMakePresets.json exclusively rather than its own kit/variant system.
  • "cmake.configureOnOpen": false prevents VS Code from silently configuring without the right MSVC environment activated. Manually trigger configure (Ctrl+Shift+P → CMake: Configure) after opening.
  • C_Cpp.default.compileCommands points to the x64-debug compilation database for IntelliSense. Change this to whichever preset you use most.
  • VCPKG_ROOT is set in both terminal.integrated.env.windows (for terminal commands) and cmake.environment (for the CMake Tools extension's own invocations).

9.3 VS Code kit configuration

The CMake Tools extension uses "kits" to locate compilers. With CMakePresets.json and "useCMakePresets": "always", kits are less relevant — the toolchain file (vcpkg + MSVC) drives compiler detection. However, you should still select the Visual Studio Build Tools 2022 kit when prompted, so the extension knows where vcvarsall.bat is for activating the terminal.

In VS Code: Ctrl+Shift+P → CMake: Select a Kit → Visual Studio Build Tools 2022 Release - amd64


10. Killing the Old VS Files

10.1 Files to DELETE after migration is verified working

eMule/srchybrid/emule.vcxproj
eMule/srchybrid/emule.vcxproj.filters
eMule/srchybrid/emule.vcxproj.user      (if present)
emulebb-cryptopp/cryptlib.vcxproj
emulebb-cryptopp/cryptlib.vcxproj.filters (if present)
emulebb-id3lib/libprj/id3lib.vcxproj
emulebb-id3lib/libprj/id3lib.vcxproj.filters (if present)
emulebb-id3lib/libprj/id3lib.sln
emulebb-resizablelib/ResizableLib/ResizableLib.vcxproj
emulebb-resizablelib/ResizableLib/ResizableLib.vcxproj.filters (if present)
templates/zlib/zlib.vcxproj             (the generated template)
templates/mbedtls/mbedTLS.vcxproj       (the generated template)

Also delete or archive the old build .cmd scripts and PowerShell workspace files once they are no longer needed:

00-setup-and-build-release.cmd
10-build-libs-release.cmd
11-build-libs-debug.cmd
20-build-emule-release.cmd
21-build-emule-debug.cmd
22-build-emule-release-incremental.cmd
23-build-emule-debug-incremental.cmd
24-build-emule-release-incremental-run-and-package.cmd
25-build-emule-debug-incremental-and-run.cmd
python -m emule_workspace
deps.json
scripts/   (all MSBuild/PS helper scripts)
helpers/   (review — some may be packaging helpers worth keeping)

10.2 Files to KEEP

eMule/srchybrid/Parser.y      — keep for reference; generated Parser.cpp is the actual source
eMule/srchybrid/Scanner.l     — keep for reference; generated Scanner.cpp is the actual source
eMule/srchybrid/Parser.cpp    — KEEP: this is a committed plain source file
eMule/srchybrid/Parser.hpp    — KEEP
eMule/srchybrid/Scanner.cpp   — KEEP: this is a committed plain source file
eMule/srchybrid/Scanner.h     — KEEP
eMule/srchybrid/Version.h     — KEEP: manually maintained
eMule/srchybrid/emule.rc      — KEEP
eMule/srchybrid/res/          — KEEP all .manifest, .ico, .bmp etc.
emulebb-id3lib/config.h.win32   — KEEP: used by the new CMakeLists via configure_file

10.3 .gitignore additions

Add to .gitignore (or create if absent at workspace root):

# CMake build trees
/build/

# vcpkg installed packages (inside build trees — already covered by /build/)
vcpkg_installed/

# CMake cache and generated files
CMakeCache.txt
CMakeFiles/
cmake_install.cmake
*.ninja
*.ninja_deps
*.ninja_log

# VS Code local settings (machine-specific paths)
.vscode/settings.json.local

# Ninja build files
build.ninja

11. Known Pain Points & Gotchas

11.1 Static MFC with CMake — the most likely source of failures

CMake has no first-class "static MFC" concept outside of the Visual Studio generator. With Ninja + MSVC, there is no MSBuild magic to auto-link MFC. The mechanism that makes it work is:

  1. #include <afxwin.h> (in Stdafx.h) causes MSVC to emit #pragma comment(lib, "nafxcw.lib") (Release) or #pragma comment(lib, "nafxcwd.lib") (Debug).
  2. These pragmas only work if the ATLMFC lib directory is on the linker's library search path (LIB environment variable).
  3. vcvarsall.bat sets LIB to include $(VCToolsInstallDir)\atlmfc\lib\x64 (or x86).
  4. If you build from a non-activated terminal, you will get LNK1104: cannot open file 'nafxcw.lib'.

The target_link_directories call in srchybrid/CMakeLists.txt provides a belt-and-suspenders fix by explicitly adding the ATLMFC lib path. However, it relies on VCToolsInstallDir being set — which vcvarsall.bat does.

Do not define _AFXDLL — that switches MFC to DLL mode, which is the opposite of what we want.

11.2 MSVC manifest embedding with Ninja

With the VS generator, manifest embedding is handled transparently by MSBuild's MT.exe invocation. With Ninja, you must pass /MANIFEST:EMBED and /MANIFESTINPUT:path explicitly as linker flags. The target_link_options in Phase 7 does this.

Caveat: The res/ directory has three manifests: emuleWin32.manifest, emulex64.manifest, and emuleARM64.manifest. The CMakePresets.json does not include an ARM64 preset (not in scope), so only the Win32 and x64 manifests are referenced in the generator expression. If ARM64 support is added later, a fifth preset and an ARM64 branch in the generator expression will be needed.

Caveat 2: CMake's add_executable(... WIN32 ...) generates a default manifest and embeds it automatically. When you also pass /MANIFESTINPUT:, the linker merges both manifests. To avoid manifest conflicts, you may need to disable CMake's automatic manifest generation:

set_target_properties(emule PROPERTIES
    VS_DEBUGGER_WORKING_DIRECTORY ""    # not relevant for Ninja
)
# Tell CMake not to generate its own default manifest:
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /MANIFEST:EMBED")

A more robust approach is to use set(CMAKE_NINJA_FORCE_RESPONSE_FILE 1) and control manifest generation entirely through target_link_options. Test with sigcheck.exe -m emule.exe (Sysinternals) to verify the embedded manifest is correct.

11.3 Delay-load with Ninja/CMake

/DELAYLOAD:foo.dll must be accompanied by delayimp.lib in the link libraries. This is correctly handled in the target_link_libraries call. The flag syntax for target_link_options uses the forward-slash form (/DELAYLOAD:...) which is correct for link.exe.

A subtle issue: if ws2_32.lib is both delay-loaded (/DELAYLOAD:ws2_32.dll) AND listed in target_link_libraries (it is), the linker imports the stub __delayLoadHelper2 from delayimp.lib and the actual DLL is loaded lazily. This is intentional — the vcxproj had the same configuration. The ws2_32.lib in the link list provides the import library; the /DELAYLOAD flag replaces the immediate IAT entry with a lazy stub.

11.4 PCH with Ninja — filename collision

CMake's target_precompile_headers generates a PCH file named cmake_pch.hxx (or cmake_pch.h) in the build directory and creates a wrapper #include "cmake_pch.hxx" that also #includes the specified header. This means the actual PCH file is not named Stdafx.pch — it is named cmake_pch.hxx.pch or similar.

This is fine for the build — it is transparent. However, if any source file does #include "Stdafx.h" explicitly (which all 247 files likely do), that include is resolved from the source tree as normal. The PCH provides the pre-compiled version of Stdafx.h's content, but the explicit #include "Stdafx.h" in each source file is what triggers the /Yu"Stdafx.h" mechanism.

Verification: After configure, check build/x64-debug/CMakeFiles/emule.dir/flags.make (or the equivalent Ninja rule) to confirm /Yu"Stdafx.h" is present on non-PCH sources and /Yc"Stdafx.h" is on Stdafx.cpp.

Actually, target_precompile_headers with MSVC in CMake 3.16+ uses the specified header name directly with /Yu. No wrapper file is generated. The PCH output file lives in ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/<target>.dir/cmake_pch.hxx.pch. The Ninja rules will correctly use /Yu"Stdafx.h" because we passed Stdafx.h (not a path) to target_precompile_headers.

11.5 cryptopp AVX2 flags

The cryptopp package in vcpkg may compile certain source files with /arch:AVX2. If you are running on hardware without AVX2 (any pre-Haswell CPU), the resulting binary will crash at the first cryptographic operation with an "illegal instruction" exception.

The vcpkg x64-windows-static triplet compiles cryptopp with the upstream's own CPU detection code, which is generally safe (it uses CPUID to detect AVX2 at runtime and falls back). However, if you see crashes, rebuild vcpkg with a custom triplet that forces a safer instruction set:

Create C:\tools\vcpkg\triplets\custom\x64-windows-static-noavx2.cmake:

set(VCPKG_TARGET_ARCHITECTURE x64)
set(VCPKG_CRT_LINKAGE static)
set(VCPKG_LIBRARY_LINKAGE static)
set(VCPKG_BUILD_TYPE release)
set(VCPKG_C_FLAGS "/arch:SSE2")
set(VCPKG_CXX_FLAGS "/arch:SSE2")

Then set VCPKG_TARGET_TRIPLET to custom-x64-windows-static-noavx2 in the affected preset.

11.6 id3lib quirks

  • id3lib's source code predates modern C++ and uses std::auto_ptr, deprecated register keyword, and other C++11-deprecated constructs. The _SILENCE_ALL_CXX17_DEPRECATION_WARNINGS define suppresses these. You may also need /wd4996 to suppress deprecation warnings from MSVC itself.
  • id3lib uses <zlib.h> internally. The ZLIB::ZLIB target must be available before add_subdirectory(emulebb-id3lib). The root CMakeLists.txt orders add_subdirectory(emulebb-zlib) before add_subdirectory(emulebb-id3lib) for this reason.
  • config.h must resolve at compile time. The configure_file approach copies config.h.win32${CMAKE_CURRENT_BINARY_DIR}/config.h. The PRIVATE include of CMAKE_CURRENT_BINARY_DIR in target_include_directories makes this work.

11.7 miniupnpc MINIUPNP_STATICLIB propagation

The miniupnpc upstream CMakeLists.txt may or may not define MINIUPNP_STATICLIB as a public compile definition on libminiupnpc-static. Check the upstream CMakeLists after adding the subdirectory:

# Check if libminiupnpc-static propagates MINIUPNP_STATICLIB
grep -r "MINIUPNP_STATICLIB" emulebb-miniupnp/miniupnpc/CMakeLists.txt

If it does not, add it manually in the root CMakeLists.txt after add_subdirectory:

target_compile_definitions(libminiupnpc-static PUBLIC MINIUPNP_STATICLIB)

This ensures that when emule includes miniupnpc headers via #include "miniupnpc.h", the correct static-linkage ABI is used.

11.8 mbedTLS tf-psa-crypto subdirectory

Recent mbedTLS versions (3.5+) include a tf-psa-crypto/ subdirectory with its own CMakeLists. The eMule vcxproj includes headers from: - $(MbedTlsRoot)include - $(MbedTlsRoot)tf-psa-crypto\include - $(MbedTlsRoot)tf-psa-crypto\drivers\builtin\include

The mbedTLS CMake build should expose these through the MbedTLS::mbedcrypto target's INTERFACE_INCLUDE_DIRECTORIES. If it does not, add them explicitly:

target_include_directories(emule PRIVATE
    "${CMAKE_SOURCE_DIR}/emulebb-mbedtls/include"
    "${CMAKE_SOURCE_DIR}/emulebb-mbedtls/tf-psa-crypto/include"
    "${CMAKE_SOURCE_DIR}/emulebb-mbedtls/tf-psa-crypto/drivers/builtin/include"
)

11.9 zlib target name variance

Different versions of zlib's CMakeLists.txt export different target names: - Older zlib (< 1.2.12): exports zlib (shared) and may not export zlibstatic at all. - Modern zlib (>= 1.3): exports zlibstatic when BUILD_SHARED_LIBS=OFF.

The alias block in the root CMakeLists.txt handles both cases. If neither zlibstatic nor zlib exist as targets after add_subdirectory, zlib's CMakeLists has a non-standard structure — check the vendored version.

11.10 The _DEBUG define injection

The vcxproj has <AdditionalOptions>/D _DEBUG</AdditionalOptions> for Debug configurations. This is unusual — normally _DEBUG is set automatically by the CRT headers when /MTd is used. The explicit /D _DEBUG in the vcxproj is belt-and-suspenders. The target_compile_definitions in Phase 7 replicates this with $<$<CONFIG:Debug>:_DEBUG>.

11.11 /safeseh and x86

/safeseh (Safe Exception Handlers) is an x86-only linker flag. It has no effect on x64 (x64 uses a different SEH model). The generator expression $<$<STREQUAL:${CMAKE_GENERATOR_PLATFORM},Win32>:/safeseh> ensures it is only applied for 32-bit builds. Note: /safeseh applies to the assembler (for .asm files) and the linker. In this project it appears in <AdditionalOptions> which applies to the compiler — check whether it is actually needed or if it was a historical artifact. The flag is valid for cl.exe on x86 (it affects how the compiler generates SEH unwind tables).


12. Migration Checklist

Setup

  • [ ] Install VS Build Tools 2022 with VC++ tools, Windows SDK 10.0, and MFC/ATL
  • [ ] Verify nafxcw.lib exists in the ATLMFC lib directory
  • [ ] Install CMake 3.25+ (winget install Kitware.CMake)
  • [ ] Install Ninja (winget install Ninja-build.Ninja)
  • [ ] Clone vcpkg to C:\tools\vcpkg and run bootstrap-vcpkg.bat
  • [ ] Set VCPKG_ROOT=C:\tools\vcpkg as a permanent user environment variable
  • [ ] Install VS Code + CMake Tools + C/C++ extensions

Phase 1 — Dependency CMakeLists

  • [ ] Create emulebb-id3lib/CMakeLists.txt (Section 4.4)
  • [ ] Verify all 36 source files listed match the actual emulebb-id3lib/src/ directory
  • [ ] Verify config.h.win32 exists at emulebb-id3lib/config.h.win32
  • [ ] Test standalone: cmake -B build-id3lib-test -S emulebb-id3lib -DCMAKE_TOOLCHAIN_FILE=%VCPKG_ROOT%/scripts/buildsystems/vcpkg.cmake
  • [ ] Create emulebb-resizablelib/ResizableLib/CMakeLists.txt (Section 4.5)
  • [ ] Verify all .cpp files listed match the actual directory
  • [ ] Confirm StdAfx.h / StdAfx.cpp exist
  • [ ] Test standalone compile

Phase 2 — vcpkg

  • [ ] Create vcpkg.json (Section 5.2)
  • [ ] Create vcpkg-configuration.json with current vcpkg HEAD baseline (Section 5.3)
  • [ ] Run %VCPKG_ROOT%\vcpkg install --triplet x64-windows-static from the workspace root to test
  • [ ] Verify cryptopp::cryptopp target is found by find_package(cryptopp CONFIG REQUIRED)

Phase 3 — Root CMakeLists.txt

  • [ ] Create CMakeLists.txt at workspace root (Section 6)
  • [ ] Verify cmake_policy(SET CMP0091 NEW) and CMP0077 NEW are present before project()
  • [ ] Verify CMAKE_MSVC_RUNTIME_LIBRARY is set before any add_subdirectory
  • [ ] Test configure with just the dependencies (comment out the emule subdirectory) to catch errors early: cmake --preset x64-debug
  • [ ] Confirm all dependency targets are created without errors

Phase 4 — Main App CMakeLists.txt

  • [ ] Create eMule/srchybrid/CMakeLists.txt (Section 7)
  • [ ] Cross-check source file list against emule.vcxproj — ensure no files are missing
  • [ ] All 247 ClCompile entries accounted for
  • [ ] kademlia/ subdirectory sources use forward slashes in the CMake list
  • [ ] Parser.cpp and Scanner.cpp included as plain sources
  • [ ] Stdafx.cpp included (PCH creation unit)
  • [ ] emule.rc included
  • [ ] Verify target_precompile_headers(emule PRIVATE Stdafx.h) is present
  • [ ] Verify all four preprocessor defines are present: ID3LIB_LINKOPTION=1, MINIUPNP_STATICLIB, SUPPORT_LARGE_FILES, MBEDTLS_ALLOW_PRIVATE_ACCESS
  • [ ] Verify all delay-load DLLs are listed: gdiplus.dll, msimg32.dll, oleacc.dll, ws2_32.dll
  • [ ] Verify delayimp.lib is in target_link_libraries
  • [ ] Verify ignore-default-lib entries: mpr.lib, rasapi32.lib, secur32.lib, sensapi.lib, vfw32.lib
  • [ ] Verify /GL and /LTCG are in Release compile/link options respectively
  • [ ] Verify manifest embedding flags for both x86 and x64
  • [ ] Remove the erroneous /Oy-> line (keep only /Oy for Release)

Phase 5 — Presets

  • [ ] Create CMakePresets.json (Section 9.1)
  • [ ] Verify toolchainFile uses $env{VCPKG_ROOT}
  • [ ] Verify each preset has the correct VCPKG_TARGET_TRIPLET
  • [ ] Verify binaryDir is ${sourceDir}/build/${presetName}

First Build

  • [ ] Activate MSVC: call vcvarsall.bat x64
  • [ ] Configure: cmake --preset x64-debug
  • [ ] vcpkg installs cryptopp without errors
  • [ ] All CMake configure steps complete
  • [ ] No "cannot find target" errors
  • [ ] Build: cmake --build --preset x64-debug
  • [ ] PCH compiles (Stdafx.cpp with /Yc)
  • [ ] All source files compile with /Yu
  • [ ] All dependency libraries link successfully
  • [ ] emule.exe is produced in build/x64-debug/bin/
  • [ ] Test the binary: launch emule.exe, confirm startup
  • [ ] Repeat for x64-release:
  • [ ] /GL appears in compile commands (verify with --verbose)
  • [ ] /LTCG appears in link command
  • [ ] emule.exe produced in build/x64-release/bin/
  • [ ] Repeat for x86-debug and x86-release (with vcvarsall.bat x86)

VS Code

  • [ ] Create .vscode/settings.json (Section 9.2)
  • [ ] Open VS Code in workspace root
  • [ ] CMake Tools extension detects CMakePresets.json
  • [ ] Select kit: Visual Studio Build Tools 2022 Release - amd64
  • [ ] Select configure preset: x64-debug
  • [ ] Trigger configure (Ctrl+Shift+P → CMake: Configure)
  • [ ] Verify IntelliSense uses compile_commands.json (no red squiggles on MFC includes)
  • [ ] Trigger build from VS Code status bar

Cleanup

  • [ ] Delete emule.vcxproj and .vcxproj.filters
  • [ ] Delete cryptlib.vcxproj
  • [ ] Delete id3lib.vcxproj and id3lib.sln
  • [ ] Delete ResizableLib.vcxproj
  • [ ] Delete generated template vcxproj files in templates/
  • [ ] Archive or delete old .cmd build scripts
  • [ ] Add build/ to .gitignore
  • [ ] Update README.md with new build instructions
  • [ ] Commit: new CMakeLists.txt files, CMakePresets.json, vcpkg.json, vcpkg-configuration.json, updated .gitignore

End of migration plan.


Feature Identifier

PLAN_003: CMake Migration

This document describes the plan for migrating the eMulebb build system from Visual Studio .vcxproj/.sln files to CMake. This enables cross-IDE support, easier dependency management, and integration with modern CI/CD pipelines.

Status: Planning phase. The current build remains .vcxproj-based.