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
| Era | Windows build authority |
|---|---|
| Legacy | .vcxproj / .sln (manual) |
| Migration | CMake (.vcxproj read for parity validation) |
| Target | CMake 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 concept | CMake construct | Notes |
|---|---|---|
| Project / target name | add_library / add_executable name | Stable across platforms |
ClCompile items | target_sources(... PRIVATE ...) | Prefer explicit lists over GLOB |
ClInclude | Listed in target_sources or IDE HEADER_FILE_ONLY via target_sources | Headers optional for build; help IDE |
AdditionalIncludeDirectories | target_include_directories(... PRIVATE/PUBLIC/INTERFACE ...) | Match PUBLIC vs PRIVATE semantics |
PreprocessorDefinitions | target_compile_definitions | Per-config: generator expressions |
AdditionalOptions (compile) | target_compile_options | Use $<$<CONFIG:Debug>:...> |
LanguageStandard | target_compile_features or CXX_STANDARD target property | Prefer cxx_std_17 etc. |
AdditionalDependencies | target_link_libraries(... PRIVATE ...) | Use imported targets when possible |
AdditionalLibraryDirectories | Avoid; use imported targets / find_package | Legacy: link_directories discouraged |
AdditionalOptions (link) | target_link_options | /SUBSYSTEM, /ENTRY, etc. |
.def file | target_sources + LINK_FLAGS or module definition property | Platform: WIN32 only |
.rc resources | target_sources on Windows | if(WIN32) |
CustomBuild | add_custom_command + target_sources with GENERATED | Preserve dep graph |
PreBuildEvent / PostBuildEvent | add_custom_command PRE_BUILD / POST_BUILD | Cross-platform alternatives preferred |
OutDir / TargetName | RUNTIME_OUTPUT_DIRECTORY, OUTPUT_NAME | Multi-config aware |
ProjectReference | target_link_libraries to CMake target | Not 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 pattern | Replacement |
|---|---|
Global include_directories | target_include_directories per target |
Global add_definitions | target_compile_definitions |
Global /D in VS | Per-target definitions |
| Folder-based VS filters only | Real CMake targets |
Configuration mapping (Debug / Release / RelWithDebInfo)
Visual Studio is multi-config by default. Linux CLI is often single-config (CMAKE_BUILD_TYPE).
| VS configuration | Linux single-config preset | CMake approach |
|---|---|---|
| Debug | dev-debug with Debug | CMAKE_BUILD_TYPE=Debug |
| Release | ci-release with Release | CMAKE_BUILD_TYPE=Release |
| RelWithDebInfo | dedicated preset | Same 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>)
| Case | Handling |
|---|---|
Windows-only .cpp | Generator expression or if(WIN32) block |
Unix-only .cpp | if(UNIX) |
| macOS bundle | MACOSX_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
OUTPUTfiles explicitly - Attach outputs to targets that compile them
- Preserve incremental build correctness (timestamps,
DEPENDS)
Post-build and custom commands
| VS event | CMake | Cross-platform note |
|---|---|---|
| Post-build copy DLL | add_custom_command(TARGET ... POST_BUILD) | Prefer cmake -E copy; avoid xcopy only |
| Sign binary | POST_BUILD on WIN32 | Document exception |
| Run tests | CTest add_test | Not POST_BUILD unless intentional |
Replace shell-specific commands with cmake -E where possible.
Windows path and toolchain pitfalls
| Issue | Migration action |
|---|---|
Backslash in AdditionalIncludeDirectories | Normalize to CMake paths; use / or CMAKE_CURRENT_SOURCE_DIR |
Mixed $(ProjectDir) macros | Replace with CMake variables |
Hardcoded C:\Program Files\... libs | Conan find_package or imported target |
/MD vs /MT runtime | Set via Conan CMakeToolchain / compiler.runtime |
| Unicode vs ANSI | Match via compile definitions and UNICODE/_UNICODE |
Parity validation method
For each migrated project:
- Build legacy Windows — MSBuild on reference
.vcxproj(saved log) - Build CMake in VS — Open folder, same configuration, build same target
- Compare artifacts — binary name, PE type, linked DLL set (where applicable)
- Compare flags —
cmake --build ... --verbosevs MSBuild log (defines, key/O,/D) - Run tests — same inputs, same exit codes
- 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
.vcxprojin 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.
Related documents
- legacy-superbuild-migration.md — Phase 3 procedure
- conan-cmake-rollout.md — dependency layer after CMake targets exist
- cmake-conan-policy.md — long-term policy