Cpack multi config packaging overwrites the binaries

I’m using ninja multi configuration to configure my project and building both debug and release versions of the embedded software

When using Cpack to package both configurations, I end up with only one set of binaries, as during packaging one overwrites the other (as it seems)

Commands:

cmake -B build -G "Ninja Multi-Config"
cmake --build build --config Release
cmake --build build --config Debug
cd build
cpack -C "Debug;Release"

I have the cmake variable CMAKE_RUNTIME_OUTPUT_DIRECTORY set to bin so both build directories (Release and Debug) are under build/bin

Here is the packaging part of my top level CMakeLists.txt

include(GNUInstallDirs)
install(TARGETS ${PROJECT_NAME})

set(CPACK_PACKAGE_INSTALL_DIRECTORY ${CPACK_PACKAGE_NAME})
set(CPACK_VERBATIM_VARIABLES YES)
set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}-TriCore")
set(CPACK_OUTPUT_FILE_PREFIX "${CMAKE_BINARY_DIR}/package")
set(CPACK_INCLUDE_TOPLEVEL_DIRECTORY OFF)
set(CPACK_SYSTEM_NAME "tricore")
set(CPACK_GENERATOR "TGZ")
include(CPack)

I end up with tar.gz which contains only one bin directory under which only on .elf file for the target I’m installing.

my expectation was to have a .tar.gz file with the following structure
– bin/Debug/app.elf
– bin/Release/app.elf

What am I missing here? Shouldn’t the cpack -C "Debug;Release" package both and avoids overwriting content on top of each other by separating directories?

@craig.scott I’m following the chapter 28.3.2 from the 14th edition of your book!

CMake is doing what you are telling it to do. The installed directory structure is independent of the directory structure used in the build directory. In your case, you have let install(TARGETS) use the default install destinations, which is appropriate when only installing a single configuration. However, as you’ve discovered, if you install multiple configurations, the last one will overwrite the earlier installed configuration’s binaries.

You need decide whether you want to install binaries to the same directory but with different file names, or with the same file names but installed to different directories. Either way, you will need to specify additional details compared to what you have now. Of the two strategies, personally I’d probably go with same directory, different file names. There is precedent for that with libraries, with debug libraries often having a d or _debug suffix on the library’s base name. On linux, this would give you files like libblah.a for Release and libblahd.a or libblah_debug.a for Debug. You would set these most easily with the OUTPUT_NAME or OUTPUT_NAME_<CONFIG> target properties. This approach also has the advantage that it doesn’t change the relative paths to things, so embedded RPATH details will be much easier to specify.

Thanks Craig, very insightful as usual.

For my use case, this is a bare-metal embedded software project, the package will be delivered to production plants as well as development team, so not to confuse production (they need to use release), I went with specifying the different install directories solution.

install(
  TARGETS ${PROJECT_NAME}
  CONFIGURATIONS Debug Release RelWithDebInfo MinSizeRel
  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}/$<LOWER_CASE:$<CONFIG>>
  PERMISSIONS OWNER_READ GROUP_READ WORLD_READ)

However, I’m thinking another better solution for the bare-metal embedded SW case is to take the .elf file from the debug build (since it’s what’s really useful) and the hex file (used in production) from the release build, it should work smoothly using RUNTIME DESTINATION couples with OUTPUT_NAME_<CONFIG>