Skip to main content

Visual Studio to CMake mapping

Rules for translating manual Visual C++ project files (.vcxproj, .sln) into target-based CMake during migration. .vcxproj files are reference inputs for migration only — not ongoing build authority.

Authority model

EraWindows build authority
Legacy.vcxproj / .sln (manual)
MigrationCMake (.vcxproj read for parity validation)
TargetCMake only; VS opens folder via native CMake mode

Visual Studio is a consumer of CMake after migration. Developers do not hand-edit .vcxproj to add sources.

On Windows, the Visual Studio generator (multi-config) is a first-class mode for interactive debugging and for MSBuild-based installer/release pipelines driven by CMake-generated project files. Ninja is also supported for fast CLI builds. See conan-cmake-rollout.md.

Conceptual mapping table

MSVC / .vcxproj conceptCMake constructNotes
Project / target nameadd_library / add_executable nameStable across platforms
ClCompile itemstarget_sources(... PRIVATE ...)Prefer explicit lists over GLOB
ClIncludeListed in target_sources or IDE HEADER_FILE_ONLY via target_sourcesHeaders optional for build; help IDE
AdditionalIncludeDirectoriestarget_include_directories(... PRIVATE/PUBLIC/INTERFACE ...)Match PUBLIC vs PRIVATE semantics
PreprocessorDefinitionstarget_compile_definitionsPer-config: generator expressions
AdditionalOptions (compile)target_compile_optionsUse $<$<CONFIG:Debug>:...>
LanguageStandardtarget_compile_features or CXX_STANDARD target propertyPrefer cxx_std_17 etc.
AdditionalDependenciestarget_link_libraries(... PRIVATE ...)Use imported targets when possible
AdditionalLibraryDirectoriesAvoid; use imported targets / find_packageLegacy: link_directories discouraged
AdditionalOptions (link)target_link_options/SUBSYSTEM, /ENTRY, etc.
.def filetarget_sources + LINK_FLAGS or module definition propertyPlatform: WIN32 only
.rc resourcestarget_sources on Windowsif(WIN32)
CustomBuildadd_custom_command + target_sources with GENERATEDPreserve dep graph
PreBuildEvent / PostBuildEventadd_custom_command PRE_BUILD / POST_BUILDCross-platform alternatives preferred
OutDir / TargetNameRUNTIME_OUTPUT_DIRECTORY, OUTPUT_NAMEMulti-config aware
ProjectReferencetarget_link_libraries to CMake targetNot path to .vcxproj
Configuration type (DLL/EXE/STATIC)SHARED / STATIC / executable/MD vs /MT via toolchain

Target-based CMake preference

Migrate toward these APIs exclusively at project boundary:

add_library(mylib STATIC ...)
target_sources(mylib PRIVATE src/a.cpp src/b.cpp)
target_include_directories(mylib
PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
target_compile_definitions(mylib PRIVATE MY_FEATURE=1)
target_compile_options(mylib PRIVATE $<$<CXX_COMPILER_ID:MSVC>:/W4>)
target_link_libraries(mylib PUBLIC SomeDep::SomeDep)
target_link_options(mylib PRIVATE $<$<CONFIG:Release>:/OPT:REF>)

Replace migration blockers:

Legacy CMake / VS patternReplacement
Global include_directoriestarget_include_directories per target
Global add_definitionstarget_compile_definitions
Global /D in VSPer-target definitions
Folder-based VS filters onlyReal CMake targets

Configuration mapping (Debug / Release / RelWithDebInfo)

Visual Studio is multi-config by default. Linux CLI is often single-config (CMAKE_BUILD_TYPE).

VS configurationLinux single-config presetCMake approach
Debugdev-debug with DebugCMAKE_BUILD_TYPE=Debug
Releaseci-release with ReleaseCMAKE_BUILD_TYPE=Release
RelWithDebInfodedicated presetSame pattern

Per-configuration compile/link differences in .vcxproj map to generator expressions:

target_compile_options(mytarget PRIVATE
$<$<CONFIG:Debug>:/Od>
$<$<CONFIG:Release>:/O2>)

Document any configuration that exists only on Windows (e.g. ReleaseWithExports) and either map to nearest CMake config or add custom CMAKE_CONFIGURATION_TYPES.

Platform-specific sources

target_sources(myapp PRIVATE
main.cpp
$<$<BOOL:${WIN32}>:app.rc>
$<$<BOOL:${WIN32}>:module.def>)
CaseHandling
Windows-only .cppGenerator expression or if(WIN32) block
Unix-only .cppif(UNIX)
macOS bundleMACOSX_BUNDLE target property

Generated code

.vcxproj CustomBuild often encodes codegen. CMake pattern:

add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/generated.h
COMMAND codegen_tool ...
DEPENDS schema.xml
VERBATIM)
target_sources(mylib PRIVATE
${CMAKE_CURRENT_BINARY_DIR}/generated.h)

Rules:

  • Declare OUTPUT files explicitly
  • Attach outputs to targets that compile them
  • Preserve incremental build correctness (timestamps, DEPENDS)

Post-build and custom commands

VS eventCMakeCross-platform note
Post-build copy DLLadd_custom_command(TARGET ... POST_BUILD)Prefer cmake -E copy; avoid xcopy only
Sign binaryPOST_BUILD on WIN32Document exception
Run testsCTest add_testNot POST_BUILD unless intentional

Replace shell-specific commands with cmake -E where possible.

Windows path and toolchain pitfalls

IssueMigration action
Backslash in AdditionalIncludeDirectoriesNormalize to CMake paths; use / or CMAKE_CURRENT_SOURCE_DIR
Mixed $(ProjectDir) macrosReplace with CMake variables
Hardcoded C:\Program Files\... libsConan find_package or imported target
/MD vs /MT runtimeSet via Conan CMakeToolchain / compiler.runtime
Unicode vs ANSIMatch via compile definitions and UNICODE/_UNICODE

Parity validation method

For each migrated project:

  1. Build legacy Windows — MSBuild on reference .vcxproj (saved log)
  2. Build CMake in VS — Open folder, same configuration, build same target
  3. Compare artifacts — binary name, PE type, linked DLL set (where applicable)
  4. Compare flagscmake --build ... --verbose vs MSBuild log (defines, key /O, /D)
  5. Run tests — same inputs, same exit codes
  6. Record sign-off in project migration doc

Acceptable differences must be documented (e.g. PDB path, incremental linker metadata). Unacceptable: missing definitions, wrong CRT linkage, wrong output name consumed by downstream.

When to remove .vcxproj

Remove legacy .vcxproj / .sln files per project when all are true:

  • CMake build succeeds locally on Linux, Windows, and macOS (presets + Conan as applicable)
  • CMake build succeeds in CI on Linux, Windows, and macOS for that project
  • Windows: parity validation signed off (legacy MSBuild vs CMake-on-VS)
  • No other project references the .vcxproj in solution-only coupling
  • Removal recorded in project migration status doc

Policy: removal follows proven working local and CI CMake builds — not an arbitrary release-cycle delay. Do not keep .vcxproj as a parallel authority once CMake is verified.