Trouble with specifying CONFIGURATIONS in installs

I’m trying to install a static library into my company’s directory structure, which has separate branches for debug and release versions of libraries.

Clearly, I want the debug build to go to the debug branch and the release build to go to the release branch. So my installation code looks like this:

        install(TARGETS ${target_name} ARCHIVE
                DESTINATION ${LIBS_INSTALL_DIR_CMAKE_D}
                PUBLIC_HEADER DESTINATION ${INCLUDE_INSTALL_DIR_ROOT}/${target_name}_${VER}/${target_name}
                CONFIGURATIONS Debug)

        install(TARGETS ${target_name} ARCHIVE
                DESTINATION ${LIBS_INSTALL_DIR_CMAKE_R}
                PUBLIC_HEADER DESTINATION ${INCLUDE_INSTALL_DIR_ROOT}/${target_name}_${VER}/${target_name}
                CONFIGURATIONS Release RelWithDebInfo MinSizeRel)

For the example of the debug build, I set up the build system (on Linux) thus:

    cmake -S . -B ../../build/solutions/myLib/debug -DAPPLICATION_NAME=myLib -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Debug 

…and then compile and install the library by typing:

cmake --build ../../build/solutions/myLib/debug --target install 

Everything compiles and installs, and what I expect to happen is that, since I’m using the debug makefile, the debug version of the library will be built and installed in ‘LIBS_INSTALL_DIR_CMAKE_D’. What actually happens is that the debug version of the library builds and then gets installed in both LIBS_INSTALL_DIR_CMAKE_D and LIBS_INSTALL_DIR_CMAKE_R, overwriting whatever release version library may already have been there.

Needless to say, the same thing happens the other way around.

What am I doing wrong?

ARCHIVE and PUBLIC_HEADER each take a set of following options. So your ARCHIVE artifact is actually missing the CONFIGURATIONS option.

Read the help for the install commands again :wink:

I must admit it’s not at all clear to me from the documentation; but having read it through again, I can only think that you mean the section that says.

If this option is used multiple times in a single call, its list of configurations accumulates. If an install(TARGETS) call uses arguments, a separate list of configurations is accumulated for each kind of artifact.

If I’m reading that correctly in the context of your reply, that means I think I need to write

        install(TARGETS ${target_name} 
                ARCHIVE DESTINATION ${LIBS_INSTALL_DIR_CMAKE_D}
                CONFIGURATIONS Debug
                PUBLIC_HEADER DESTINATION ${INCLUDE_INSTALL_DIR_ROOT}/${target_name}_${VER}/${target_name}
                CONFIGURATIONS Debug)

        install(TARGETS ${target_name} 
                ARCHIVE DESTINATION ${LIBS_INSTALL_DIR_CMAKE_R}
                CONFIGURATIONS Release RelWithDebInfo MinSizeRel
                PUBLIC_HEADER DESTINATION ${INCLUDE_INSTALL_DIR_ROOT}/${target_name}_${VER}/${target_name}
                CONFIGURATIONS Release RelWithDebInfo MinSizeRel)

i.e. with each install component having its own CONFIGURATIONS specification.

Am I on the right track now?

That looks like it will work, but in future you might also need to add similar things for RUNTIME and LIBRARY destinations.

What I would do (instead of explicit DESTINATION for different CONFIGURATIONS) is setting CMAKE_INSTALL_PREFIX, especially that you do already run configuration twice (in order to change CMAKE_BUILD_TYPE), so:

$ cmake [...] -DCMAKE_BUILD_TYPE=Debug \
    -DCMAKE_INSTALL_PREFIX="/path/to/your/company/folders/debug"
$ cmake --build [...] --target install

$ cmake [...] -DCMAKE_BUILD_TYPE=Release \
    -DCMAKE_INSTALL_PREFIX="/path/to/your/company/folders/release"
$ cmake --build [...] --target install

That way you won’t need to split install statements to set different DESTINATION values for different CONFIGURATIONS:

# for CMAKE_INSTALL_LIBDIR, CMAKE_INSTALL_BINDIR, CMAKE_INSTALL_INCLUDEDIR and others
include(GNUInstallDirs)

install(TARGETS ${target_name}
    #EXPORT "${target_name}Targets"
    # these get default values from GNUInstallDirs, no need to set them
    #RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} # bin
    #LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} # lib
    #ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} # lib
    PUBLIC_HEADER DESTINATION ${INCLUDE_INSTALL_DIR_ROOT}/${target_name}_${VER}/${target_name}
    #PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${target_name}"
    #INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
1 Like

Yes, that was the right track, and it worked.

I had some other problems because there were a lot of lines in my codebase of the form:

install(CODE "...."
        CONFIGURATIONS Release RelWithDebInfo MinSizeRel )

…and it took me a while to realise that, even though this didn’t cause any errors or warnings, the CONFIGURATIONS specifier is completely ignored for install(CODE…). I couldn’t find the place in the documentation, if any, where this was mentioned.

To achieve the desired result, I had to resort to some very baroque generator expressions of the form:

    set(do_the_thing_if_not_debug "$<$<CONFIG:Debug>:\"${CMAKE_COMMAND} -E echo \">$<$<NOT:$<CONFIG:Debug>>:The thing I wanted to do...>")
    INSTALL(CODE "execute_process(COMMAND ${do_the_thing_if_not_debug})") 

Either way I got there in the end.