Visual Studio Generators
On Windows, CMake's most common generator targets Visual Studio's MSBuild system. The Visual Studio generator creates .sln and .vcxproj files that can be opened directly in the IDE or built from the command line with msbuild. Unlike single-configuration generators (Makefiles, Ninja), Visual Studio generators are multi-configuration — one build tree supports Debug, Release, RelWithDebInfo, and MinSizeRel simultaneously.
# Generate Visual Studio 2022 solution (64-bit)
cmake -G "Visual Studio 17 2022" -A x64 -S . -B build
# Generate for 32-bit (rare today but still supported)
cmake -G "Visual Studio 17 2022" -A Win32 -S . -B build32
# Generate for ARM64 (Windows on ARM)
cmake -G "Visual Studio 17 2022" -A ARM64 -S . -B build-arm64
# Build from command line (Release configuration)
cmake --build build --config Release --parallel
# Build specific target
cmake --build build --config Debug --target mylib
-G Ninja with the MSVC toolchain for faster builds. Run from a "Developer Command Prompt" or use vcvarsall.bat to set up the environment first: call "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat" x64
# Using Ninja with MSVC (from Developer Command Prompt)
cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -S . -B build-ninja
cmake --build build-ninja --parallel
# Or use CMake Presets to avoid manual environment setup
cmake --preset=windows-release
cmake --build --preset=windows-release
MSVC Toolchain Detection
CMake automatically detects the MSVC compiler when using a Visual Studio generator or when running from a configured environment. The detection populates variables like MSVC, MSVC_VERSION, and CMAKE_CXX_COMPILER_ID. Understanding the version mapping is critical for conditional logic:
cmake_minimum_required(VERSION 3.21)
project(WindowsDetection LANGUAGES CXX)
# MSVC version detection
if(MSVC)
message(STATUS "MSVC version: ${MSVC_VERSION}")
# 1920-1929 = VS 2019 (v16.x)
# 1930-1939 = VS 2022 (v17.0-v17.9)
# 1940+ = VS 2022 (v17.10+)
if(MSVC_VERSION GREATER_EQUAL 1930)
message(STATUS "Visual Studio 2022 detected")
endif()
endif()
# Compiler ID is always "MSVC" for cl.exe
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
# Set MSVC-specific compile options
target_compile_options(mylib PRIVATE
/W4 # Warning level 4
/WX # Warnings as errors
/permissive- # Standards conformance
/Zc:__cplusplus # Correct __cplusplus macro
)
endif()
# Detect toolset version (v143 for VS 2022)
message(STATUS "Toolset: ${CMAKE_VS_PLATFORM_TOOLSET}")
vcpkg Integration
vcpkg is Microsoft's cross-platform package manager for C++ libraries. CMake integration is achieved through a toolchain file that intercepts find_package() calls, redirecting them to vcpkg-installed libraries. The recommended approach uses manifest mode with a vcpkg.json file at your project root.
# Clone vcpkg (one-time setup)
git clone https://github.com/microsoft/vcpkg.git C:\vcpkg
C:\vcpkg\bootstrap-vcpkg.bat
# Configure with vcpkg toolchain
cmake -G "Visual Studio 17 2022" -A x64 ^
-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake ^
-S . -B build
# Or set as environment variable (recommended)
set VCPKG_ROOT=C:\vcpkg
cmake -G "Visual Studio 17 2022" -A x64 ^
-DCMAKE_TOOLCHAIN_FILE=%VCPKG_ROOT%/scripts/buildsystems/vcpkg.cmake ^
-S . -B build
{
"$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg.schema.json",
"name": "my-project",
"version-semver": "1.0.0",
"dependencies": [
"fmt",
"spdlog",
"boost-asio",
{
"name": "openssl",
"version>=": "3.0.0"
}
],
"builtin-baseline": "a1a1cbc975767298ab7da9f51da175a33a56a764",
"overrides": [
{ "name": "fmt", "version": "10.2.1" }
]
}
# CMakeLists.txt — vcpkg packages are found normally
cmake_minimum_required(VERSION 3.21)
project(VcpkgDemo LANGUAGES CXX)
# vcpkg integrates transparently with find_package
find_package(fmt CONFIG REQUIRED)
find_package(spdlog CONFIG REQUIRED)
find_package(Boost REQUIRED COMPONENTS asio)
find_package(OpenSSL REQUIRED)
add_executable(app main.cpp)
target_link_libraries(app PRIVATE
fmt::fmt
spdlog::spdlog
Boost::asio
OpenSSL::SSL
OpenSSL::Crypto
)
-DVCPKG_TARGET_TRIPLET=x64-windows explicitly. The default triplet may be x86-windows which causes link errors on 64-bit builds. For static libraries, use x64-windows-static.
DLL Export Macros
Windows requires explicit symbol export/import declarations for shared libraries (DLLs). CMake's GenerateExportHeader module automates this with a generated header containing platform-appropriate macros. This eliminates the need for manual __declspec(dllexport)/__declspec(dllimport) management.
cmake_minimum_required(VERSION 3.21)
project(ExportDemo LANGUAGES CXX)
# Create shared library
add_library(mylib SHARED
src/core.cpp
src/utils.cpp
)
# Generate export header automatically
include(GenerateExportHeader)
generate_export_header(mylib
EXPORT_FILE_NAME "${CMAKE_BINARY_DIR}/include/mylib/export.h"
EXPORT_MACRO_NAME MYLIB_API
STATIC_DEFINE MYLIB_STATIC
)
# Make the generated header accessible
target_include_directories(mylib PUBLIC
$<BUILD_INTERFACE:${CMAKE_BINARY_DIR}/include>
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)
# Enable hidden visibility (consistent with GCC/Clang default)
set_target_properties(mylib PROPERTIES
CXX_VISIBILITY_PRESET hidden
VISIBILITY_INLINES_HIDDEN ON
WINDOWS_EXPORT_ALL_SYMBOLS OFF # Use explicit exports
)
// include/mylib/core.h
#pragma once
#include "mylib/export.h"
// MYLIB_API expands to __declspec(dllexport) when building,
// expands to __declspec(dllimport) when consuming
class MYLIB_API CoreEngine {
public:
CoreEngine();
~CoreEngine();
void initialize();
int process(const char* input);
};
// Free function with export
MYLIB_API int compute_result(int a, int b);
Windows-Specific APIs
Windows applications often need special CMake properties to configure the application subsystem, link against Windows libraries, and handle platform-specific resources like icons and manifests.
cmake_minimum_required(VERSION 3.21)
project(WindowsApp LANGUAGES CXX)
# GUI application (uses WinMain entry point, no console window)
add_executable(myapp WIN32
src/main.cpp
src/app.cpp
resources/app.rc # Windows resource file (icon, manifest)
)
# WIN32_EXECUTABLE property (alternative to WIN32 keyword)
# set_target_properties(myapp PROPERTIES WIN32_EXECUTABLE TRUE)
# Link common Windows libraries
target_link_libraries(myapp PRIVATE
ws2_32 # Winsock2
winmm # Multimedia (timers)
shlwapi # Shell Lightweight Utility
dbghelp # Debugging helpers
comctl32 # Common Controls
)
# Add version information resource
configure_file(
"${CMAKE_SOURCE_DIR}/resources/version.rc.in"
"${CMAKE_BINARY_DIR}/version.rc"
@ONLY
)
target_sources(myapp PRIVATE "${CMAKE_BINARY_DIR}/version.rc")
# Application manifest for DPI awareness and admin elevation
set_target_properties(myapp PROPERTIES
VS_DPI_AWARE "PerMonitor"
VS_USER_PROPS ""
)
Console vs GUI Application Switching
Use a CMake option to build either a console or GUI application from the same source. This is common for applications that need console output during development but run as GUI-only in production:
option(BUILD_CONSOLE "Build as console application" OFF)
add_executable(myapp src/main.cpp)
if(BUILD_CONSOLE)
# Console application — has stdout, uses main()
set_target_properties(myapp PROPERTIES WIN32_EXECUTABLE FALSE)
else()
# GUI application — no console, uses WinMain()
set_target_properties(myapp PROPERTIES WIN32_EXECUTABLE TRUE)
target_compile_definitions(myapp PRIVATE APP_GUI_MODE)
endif()
Unicode/MBCS Configuration
Windows APIs come in two variants: wide-character (Unicode, W suffix) and multi-byte (ANSI, A suffix). Modern applications should always target Unicode. CMake can configure this consistently across all source files:
cmake_minimum_required(VERSION 3.21)
project(UnicodeApp LANGUAGES CXX)
add_executable(app src/main.cpp)
# Define UNICODE and _UNICODE for Windows API wide-char variants
target_compile_definitions(app PRIVATE
UNICODE
_UNICODE
WIN32_LEAN_AND_MEAN # Exclude rarely-used Windows headers
NOMINMAX # Prevent min/max macro conflicts
_CRT_SECURE_NO_WARNINGS # Suppress deprecated CRT warnings
)
# For MBCS (legacy, not recommended for new code)
# target_compile_definitions(app PRIVATE _MBCS)
min and max as macros, which conflict with std::min and std::max. Always define NOMINMAX before including any Windows headers — or better, set it globally via CMake so you never forget.
Windows SDK Selection
Visual Studio generators allow you to target a specific Windows SDK version. This determines which Windows APIs are available at compile time. The SDK version is separate from the platform toolset version.
# Specify SDK version at generation time
cmake -G "Visual Studio 17 2022" -A x64 ^
-DCMAKE_SYSTEM_VERSION=10.0.22621.0 ^
-S . -B build
# Or target the latest installed SDK
cmake -G "Visual Studio 17 2022" -A x64 ^
-DCMAKE_SYSTEM_VERSION=10.0 ^
-S . -B build
cmake_minimum_required(VERSION 3.21)
project(SdkDemo LANGUAGES CXX)
# Report detected SDK
message(STATUS "Windows SDK: ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}")
# Conditionally use APIs based on SDK version
if(CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION VERSION_GREATER_EQUAL "10.0.19041.0")
target_compile_definitions(app PRIVATE HAS_WIN10_2004_APIS)
endif()
# Target minimum Windows version via _WIN32_WINNT
# 0x0A00 = Windows 10, 0x0601 = Windows 7
target_compile_definitions(app PRIVATE
_WIN32_WINNT=0x0A00
WINVER=0x0A00
)
Debugging with Visual Studio
CMake can configure debugging properties that appear in the Visual Studio project settings, including working directories, command-line arguments, and environment variables for debug sessions.
cmake_minimum_required(VERSION 3.21)
project(DebugDemo LANGUAGES CXX)
add_executable(app src/main.cpp)
# Set the working directory for F5 debugging in Visual Studio
set_target_properties(app PROPERTIES
VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
VS_DEBUGGER_COMMAND_ARGUMENTS "--config dev --verbose"
VS_DEBUGGER_ENVIRONMENT "PATH=${CMAKE_BINARY_DIR}/lib;$ENV{PATH}\nDATA_DIR=${CMAKE_SOURCE_DIR}/data"
)
# Set app as startup project (first target added to root CMakeLists.txt)
set_property(DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY
VS_STARTUP_PROJECT app
)
# Generate PDB for Release builds (useful for crash dump analysis)
target_compile_options(app PRIVATE
$<$<CONFIG:Release>:/Zi>
)
target_link_options(app PRIVATE
$<$<CONFIG:Release>:/DEBUG /OPT:REF /OPT:ICF>
)
Common MSVC Warnings Configuration
MSVC uses a numeric warning level system (/W0 through /W4, plus /Wall) that differs from GCC/Clang's -Wall -Wextra flags. A professional CMake setup configures warnings per-target using generator expressions for cross-platform compatibility.
cmake_minimum_required(VERSION 3.21)
project(WarningsDemo LANGUAGES CXX)
add_library(mylib src/lib.cpp)
# Cross-platform warning configuration
target_compile_options(mylib PRIVATE
$<$<CXX_COMPILER_ID:MSVC>:
/W4 # High warning level
/WX # Warnings as errors
/permissive- # ISO C++ conformance
/w14242 # Conversion narrowing
/w14254 # Operator conversion
/w14263 # Member function hides virtual
/w14265 # Class has virtual functions but no virtual dtor
/w14287 # Unsigned/negative mismatch
/w14296 # Expression is always false
/w14311 # Pointer truncation (64→32 bit)
/w14545 # Expression before comma evaluates to function
/w14546 # Function call before comma missing arg list
/w14547 # Operator before comma has no effect
/w14549 # Operator before comma has no effect
/w14555 # Expression has no effect
/w14619 # Unknown pragma warning
/w14640 # Thread-unsafe static local initialization
/w14826 # Conversion is sign-extended
/w14905 # Wide string literal cast to LPSTR
/w14906 # String literal cast to LPWSTR
/w14928 # Illegal copy-initialization
/wd4068 # Disable: unknown pragma (for GCC pragmas)
>
$<$<CXX_COMPILER_ID:GNU,Clang,AppleClang>:
-Wall -Wextra -Wpedantic -Werror
-Wconversion -Wsign-conversion
-Wnon-virtual-dtor -Wold-style-cast
>
)
# Suppress specific warnings for third-party code
target_compile_options(mylib PRIVATE
$<$<CXX_COMPILER_ID:MSVC>:
/external:anglebrackets # Treat #include <...> as external
/external:W0 # No warnings from external headers
>
)
/external:anglebrackets flag tells MSVC to treat all angle-bracket includes as "external" code and suppress their warnings. Combined with /external:W0, this eliminates noise from system and third-party headers while keeping strict warnings on your own code.
Complete Windows CMake Preset
A production-ready CMakePresets.json for Windows development combining all the patterns from this guide — Visual Studio generator, vcpkg, Unicode, strict warnings, and debugging configuration:
{
"version": 6,
"cmakeMinimumRequired": { "major": 3, "minor": 25, "patch": 0 },
"configurePresets": [
{
"name": "windows-base",
"hidden": true,
"generator": "Visual Studio 17 2022",
"architecture": { "value": "x64", "strategy": "set" },
"toolchainFile": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake",
"cacheVariables": {
"CMAKE_CXX_STANDARD": "20",
"CMAKE_CXX_STANDARD_REQUIRED": "ON"
}
},
{
"name": "windows-debug",
"inherits": "windows-base",
"displayName": "Windows Debug",
"binaryDir": "${sourceDir}/build/debug"
},
{
"name": "windows-release",
"inherits": "windows-base",
"displayName": "Windows Release",
"binaryDir": "${sourceDir}/build/release"
}
],
"buildPresets": [
{
"name": "windows-debug",
"configurePreset": "windows-debug",
"configuration": "Debug"
},
{
"name": "windows-release",
"configurePreset": "windows-release",
"configuration": "Release"
}
]
}