38

I'm trying to build one of my CMake-based projects from a couple of years ago with Visual Studio 2010 and I'm running into problems to do with the output directory for a project. Visual Studio has always been very keen on adding Debug/ and Release/ subdirectories when outputting binaries, and for various reasons I've always been very keen on removing them - now that I'm using a new version of CMake and a new version of Visual Studio, the old workaround in CMake no longer seems to work, and I'm looking to find out the "new" way of doing it.

With a previous version of CMake (2.6) and a previous version of Visual Studio (2008), I used the following:

IF(MSVC_IDE)
    # A hack to get around the "Debug" and "Release" directories Visual Studio tries to add
    SET_TARGET_PROPERTIES(${targetname} PROPERTIES PREFIX "../")
    SET_TARGET_PROPERTIES(${targetname} PROPERTIES IMPORT_PREFIX "../")
ENDIF(MSVC_IDE)

This worked fine, but no longer seems to do the trick. Please does anyone know of a similar but more up-to-date workaround that will work with CMake 2.8.6 and Visual Studio 2010?

Stuart Golodetz
  • 20,238
  • 4
  • 51
  • 80

4 Answers4

70

It depends a bit on what you want precisely, but I would recommend to take a look at the available target properties, similar to this question.

It depends a bit on what you want exactly. For each target, you could manually set the library_output_directory or runtime_output_directory properties.

if ( MSVC )
    set_target_properties( ${targetname} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${youroutputdirectory} )
    set_target_properties( ${targetname} PROPERTIES LIBRARY_OUTPUT_DIRECTORY_DEBUG ${youroutputdirectory} )
    set_target_properties( ${targetname} PROPERTIES LIBRARY_OUTPUT_DIRECTORY_RELEASE ${youroutputdirectory} )
    # etc for the other available configuration types (MinSizeRel, RelWithDebInfo)
endif ( MSVC )

You could also do this globally for all sub-projects, using something like this:

# First for the generic no-config case (e.g. with mingw)
set( CMAKE_RUNTIME_OUTPUT_DIRECTORY ${youroutputdirectory} )
set( CMAKE_LIBRARY_OUTPUT_DIRECTORY ${youroutputdirectory} )
set( CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${youroutputdirectory} )
# Second, for multi-config builds (e.g. msvc)
foreach( OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES} )
    string( TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG )
    set( CMAKE_RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${youroutputdirectory} )
    set( CMAKE_LIBRARY_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${youroutputdirectory} )
    set( CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${youroutputdirectory} )
endforeach( OUTPUTCONFIG CMAKE_CONFIGURATION_TYPES )
Community
  • 1
  • 1
André
  • 18,348
  • 6
  • 60
  • 74
  • 5
    Brilliant, thanks - it turns out I was looking for the ARCHIVE_OUTPUT_DIRECTORY_{config} properties on the target. Cheers! :) – Stuart Golodetz Oct 13 '11 at 18:06
  • I do exactly the same and it works until e.g. add_executable(test test.cpp) get_target_property(test_EXE test LOCATION) is called, it still has the D:/Codebase/test/bin/$(Configuration)/test.exe any idea how to fix this issue? – choosyg Aug 21 '14 at 09:19
  • 1
    Which variable will hold the output folder for the current project (Without working with the different configurations)? Namely if I work on release will point to the `x64\Release\` and if I run debug mode will point to the `x64\Debug\` folder? Namely I don't want to change it, I just want to access it. – Royi Feb 07 '20 at 19:59
12

https://cmake.org/cmake/help/latest/prop_tgt/LIBRARY_OUTPUT_DIRECTORY.html explains that:

Multi-configuration generators (VS, Xcode) append a per-configuration subdirectory to the specified directory unless a generator expression is used.

Thus, the only workaround is to use a generator expression. One cool way to do it is by using $<0:> at the end of your path. So if your path is /what/ever/, you will need to replace it with /what/ever/$<0:>.

Example with the target: Nuua and the path: C:/Nuua/bin/

set_target_properties(nuua PROPERTIES RUNTIME_OUTPUT_DIRECTORY C:/Nuua/bin/$<0:>)
Erik Campobadal
  • 867
  • 9
  • 14
  • What does `$<0:>` signify? Just a generator expression which evaluates to nil? – legends2k Mar 31 '21 at 04:11
  • It evalutes to an empty string. Without the expression the generator is free to set `RUNTIME_OUTPUT_DIRECTORY` to per-configuration sub-directory. Having the expression forces the generator to evaluate and set this property to the expected directory! – legends2k Nov 19 '21 at 13:05
7

In current versions of CMake you can use a generator expression for LIBRARY_OUTPUT_DIRECTORY to avoid the configuration-specific suffix.

I just added $<$<CONFIG:Debug>:>, which always expands to nothing, to mine. This looks a bit weird, but it does work, and it's not so weird you can't explain it with a brief comment:

# Use a generator expression so that the specified folder is used directly, without any
# configuration-dependent suffix.
#
# See https://cmake.org/cmake/help/v3.8/prop_tgt/LIBRARY_OUTPUT_DIRECTORY.html
set_target_properties(library PROPERTIES
                      LIBRARY_OUTPUT_DIRECTORY my/folder/$<$<CONFIG:Debug>:>)
Tom Seddon
  • 2,648
  • 1
  • 19
  • 28
  • 4
    I wish there was a SO feature for third party to review questions on CMake over time and mark answers made 6 years later as "forget everything mentioned before, THIS is how you do the X in 2019" – Artalus Feb 08 '19 at 09:19
  • I've a bit confused with word "Debug" in the suggested expression, possible it can be simplified to "$<$:>" ? – Pavel K. Feb 22 '19 at 09:31
  • 1
    @PavelK. I think the `$` test requires a configuration name. Perhaps you could use `$<0:>` or something instead, though. – Tom Seddon Feb 23 '19 at 12:44
  • Erik's answer quotes CMake documentation that explains why this works. – legends2k Mar 31 '21 at 04:15
2

Big thanks to Erik's for the hint to use $<0:>

If you have multiple targets that you want that solution for you could do something like

# Get Targets in ${CMAKE_CURRENT_SOURCE_DIR}
get_property(current_targets DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY BUILDSYSTEM_TARGETS)
foreach(TARGET ${current_targets})
    set_target_properties(${TARGET} PROPERTIES
                        RUNTIME_OUTPUT_DIRECTORY  ${CMAKE_BINARY_DIR}/$<0:> 
                        LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/$<0:>
                        ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/$<0:>)
endforeach()

to perform it for all targets in ${CMAKE_CURRENT_SOURCE_DIR} without having to add it for each executable/library individually

TimS
  • 21
  • 1