What is the correct or best way to build/install 3rdParty code with multiple configuraions?

My company uses 3rdParty libraries with Windows and Linux compiles. We will compile these 3rdParty libraries once and deploy them to a common location on a file share.

Using the glorious magic of find_package() developers are able to find and integrate the 3rd Party libraries.

Recently, we started using a library whose Release binary won’t link with out VS debug binaries.

So I thought the fix would be to compile Release and Debug and then install and the glory of relocatable packages would return the correct image.

Sadly this isn’t true in this scenario because the library has the same name be it release or debug and it’s deployed to the same folder.

Is there an easy CMake way around this that doesn’t require that I edit the 3rd Party CMakefile install() logic?

I was toying with ideas like deploying release and debug to different locations, but that won’t work with find_package() logic because we don’t know the build configuration at config time for Visual Studio compiles, and that would be required to correctly set the prefix search path for find_package() to find the debug or release version of the library.

I cannot believe that I’m the first to encounter this problem, and so would like to learn what others have done in this scenario.

I found a workaround, but I’m hoping to learn other ways.

I created a top-level CMakeLists.txt file that incorporates the 3rdParty library file using add_subdirectory()

After doing add_subdirectory() I found all the targets under the current folder using the following function that I created after some StackOverflow learning.

function(stvn_get_all_cmake_targets OUT_VAR START_DIR)
    get_property(CMAKE_TGTS DIRECTORY ${START_DIR} PROPERTY BUILDSYSTEM_TARGETS)
    get_property(SUBDIRS DIRECTORY ${START_DIR} PROPERTY SUBDIRECTORIES)

    foreach(SUBD ${SUBDIRS})
        stvn_get_all_cmake_targets(SUBD_TGTS ${SUBD})
        list(APPEND CMAKE_TGTS ${SUBD_TGTS})
    endforeach()

    set(${OUT_VAR} ${CMAKE_TGTS} PARENT_SCOPE)
endfunction()

Then I loop over each of the targets that were found and set the DEBUG_POSTFIX, RELEASE_POSTFIX and RELWITHDBINFO_POSTFIX values for each one.


This adds R, D or RD to the end of each library or executable that’s generated, making them unique entries in the install folder. And the cmake relocatable package logic correctly points to the appropriate file for the desired configuration.

i.e. fooTarget-release points at the R image, fooTarget-debug points at the D image, etc.

This works, but I’m a little nervous because I don’t know for certain if the 3rd Party libray has done anything special with names, etc.

Is there a better way?

As a small improvement, note that when a target is created, its property DEBUG_POSTFIX is initialised with the value of the variable CMAKE_DEBUG_POSTFIX if it’s set (see docs of <CONFIG>_POSTFIX for details).

This means that if you set CMAKE_DEBUG_POSTFIX before calling add_subdirectory(), you should get the targets set up without the need for enumerating them and postprocessing them individually.

1 Like

@Angew,

Thank you for that tip.

As a company we’ve been leaning into target specific settings rather than global settings. My mentor, no longer available, was saying it’s due to better control on a target by target basis.

I really like the simplicity of what you’ve shared, but I have so many “don’t do that” code review memories… would you be able to shed some light on on this topic of global vs. targeted settings?

I know this is a shift in focus, but I would appreciate your insights.

Thank you.

Modern CMake is moving towards a target-centric approach, specifying settings using build targets and their properties instead of global settings. Note, however, that what I suggested is in line with that approach. CMAKE_DEBUG_POSTFIX is not a global setting; it’s an initialiser of a per-target property, simply a shorthand for setting the propeties on many related targets (those in the given subdir tree) at the same time. Its use would look like this:

set(CMAKE_DEBUG_POSTFIX D)
set(CMAKE_RELEASE_POSTFIX R)
set(CMAKE_RELWITHDEBINFO_POSTFIX RD)
add_subdirectory(the3rdPartyLib)
unset(CMAKE_DEBUG_POSTFIX)
unset(CMAKE_RELEASE_POSTFIX)
unset(CMAKE_RELWITHDEBINFO_POSTFIX)

Contrast this with a truly global setting such as CMAKE_CXX_FLAGS. That is a variable which affects the build directly; its final value affects all targets for which it is in scope, regardless of the value it had when such targets were created. Global settings such as these is what modern CMake is moving away from, and I wholeheartedly support this move to a target-centric approach.

1 Like