Cannot use "Modern CMake" approach with "object libraries", as they may not use target_link_libraries()
First let me explain my use-case. There is an embedded firmware, which is meant to be used with a bootloader. This bootloader has an ability to verify firmware integrity by analyzing a checksum which is embedded inside the firmware - if the checksum does not match, then the firmware is not allowed to execute. It is most practical to have the checksum not only in the binary image of the firmware, but also in the ELF file which is a product of linking - this way you can also debug the firmware or upload it with a JTAG (and obviously the checksum is also present in the binary image). Until now I have built the firmware using following two-step procedure:
- link all object files into a intermediate ELF file, dump it to binary image, calculate its checksum and convert this checksum into another object file
- link all object files AND the checksum (in the form of object file) into the final ELF file.
This is pretty obvious that both steps have to use exactly the same object files, otherwise there may be some differences if you would compile them twice (for example if somewhere in the code you use __TIME__
preprocessor constant).
However if the project also uses some external third-party modules (in the form of static libraries), there is this one major issue which makes it impossible to follow "Modern CMake" approach, where dependencies/requirements should be propagated via uses of targets in target_link_libraries()
.
Imagine you compiled your application into an object library like this:
add_library(application OBJECT main.cpp ...)
This application may use a dozen static libraries, each having its own include paths, preprocessor directives, sometimes even special compilation flags (for example the RTOS framework may require the application to use -fno-use-cxa-atexit
). As each library was prepared to follow "Modern CMake" approach it usually presents its requirements in the form of PUBLIC/INTERFACE properties of the target, for example like this:
add_library(lwIP STATIC ${CMAKE_CURRENT_LIST_DIR}/src/api/api_lib.c ...)
target_include_directories(lwIP PUBLIC
${CMAKE_CURRENT_LIST_DIR}/src/include
${CMAKE_SOURCE_DIR}/lwIP-integration/include)
Now in case of a "normal" executable target (or a static library) you would just do:
target_link_libraries(application PRIVATE
lwIP)
However trying the same with object libraries results in following error:
CMake Error at CMakeLists.txt:154 (target_link_libraries):
Object library target "application" may not link to
anything.
-- Configuring incomplete, errors occurred!
Solving this - without using global variables for include folders and compiler flags ("the old way") - requires some nasty functions to recursively read properties from the targets, so they could be used directly for the object library. This is not trivial, because one target may "link" to more targets, which also "link" to some more targets. Therefore I would propose to remove this restriction that object libraries may not use target_link_libraries()
- I this this is counterproductive. For example static libraries also cannot actually "link" to anything - this is up to the user of the library, the process of creating a static library ends when all object files are archived, linker is not involved at any stage. However CMake is perfectly fine with using target_link_libraries()
for static libraries - exactly for the purpose of "Modern CMake" approach.