visual-studiocmakevcxproj

How to set the Output and Intermediate Directory in Visual Studio C++ projects generated by CMake, using Visual Studio Macros?


Here is the final desired result in Visual Studio 2019 for any C/C++ project:

enter image description here

The key part that I am looking for are the top 2 lines in this MSVC project properties dialog:

As you can see, both use Visual Studio $(xyz) macros to define the target paths.

The question(s) that I've been struggling with:

  1. How can I make CMake spit out these strings instead of already expanded paths into the generated vcxproj files?

  2. If I want this to happen to multiple (or all) generated MSVC C/C++ project files (libraries, executables, etc.), where do I set this up then? Do I need to patch template files or can this be done via commandline or configuration file settings, which then override the CMakeLists.txt internally defined values?

  3. Anything special I need to mind / watch out for, if I want this setting for all vcxproj build targets as shown in the screenshot?

    (in my case that's generally Debug+Release for both x86 and x64, so 2 Configurations and 2 Platforms in Visual Studio speak)

The other questions on SO (at least the ones I dug up) do not address this at all AFAICT as they replace one 'hardcoded' path with another using CMake (just using different CMake variables, but all expand to absolute paths before the vcxproj is produced).


Solution

  • At time of writing (CMake 3.20 and below), it is not possible to set the intermediate directory in Visual Studio. This was requested a long time ago, but no progress has been made. See: https://gitlab.kitware.com/cmake/cmake/-/issues/14999

    Note that a single CMake build can only target one architecture, so the resulting Visual Studio solution will only ever let you pick the configuration, not the platform. Writing $(PlatformArchitecture) would work (returning 64 or 32 appropriately), but it would be pointlessly non-portable. Also, CMake gives you portable access to $(Configuration) in multi-config generators via the CMAKE_CFG_INTDIR variable.

    So the best thing to do is to set CMAKE_*_OUTPUT_DIRECTORY in your top-level CMakeLists.txt.

    get_property(is_multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
    if (is_multi_config)
      # Compute the bitness of the target, same as VS's $(PlatformArchitecture)
      math(EXPR PlatformArchitecture "8 * ${CMAKE_SIZEOF_VOID_P}")
    
      set(CMAKE_RUNTIME_OUTPUT_DIRECTORY 
          "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}-${PlatformArchitecture}")
      set(CMAKE_LIBRARY_OUTPUT_DIRECTORY 
         "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}-${PlatformArchitecture}")
      set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY 
          "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}-${PlatformArchitecture}")
    endif ()
    

    This checks if the generator is multi-config (like Visual Studio, Xcode, or Ninja Multi-Config) and, if so, adjusts the output paths. This will give you the option later of switching away from Visual Studio to Ninja Multi-Config if you want faster builds.

    Finally, you might want to use if (WIN32 AND is_multi_config) instead, if you only want to adjust the output paths on Windows (and not macOS or Linux).