OK, I figured it out: this is much more painful than it should be. Until very recently, people at Kitware didn't understand why anyone would ever want to create a DLL from static libs. Their argument is that there should always be source files in the main (e.g. top_project
in my case) directory because it is effectively a project of its own. I see things differently & I need to break top_project
into smaller subprojects which should not exist independently (i.e. there is no point in creating a full-blown project for them & add them using ExternalProject_Add
). Besides, when I ship my shared library (for use, e.g. with a Java Native Interface), I don't want to ship dozens of shared libraries because that would amount to exposing the internal layout of my project. Anyway, having - I think - made a case for creating a shared library from static libraries, I'll proceed to the technical details.
In the CMakeLists.txt of subproject1
and subproject2
, you should create your target using the OBJECT library feature (introduced in CMake 2.8.8):
add_library(${PROJECT_NAME} OBJECT ${SRC})
where SRC
designates the list of source files (note that these should be set explicitly in the CMakeLists.txt file as it allows make to re-launch CMake when a modification of CMakeLists.txt is detected, e.g. when adding or removing a file)
In the top_project
, add the subprojects using:
add_subdirectory(subproject1)
add_subdirectory(subproject2)
In order to see the symbols from the static library, use:
set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--export-all-symbols")
You can then create the shared library using:
add_library(${PROJECT_NAME} SHARED $<TARGET_OBJECTS:subproject1>
$<TARGET_OBJECTS:subproject2>)
I've found that any "normal" library (i.e. not object) needs to be added in a separate add_library
command, otherwise it is simply ignored.
For executables, you can use:
add_executable(name_of_executable $<TARGET_OBJECTS:subproject1>
$<TARGET_OBJECTS:subproject2>)
set(LINK_FLAGS ${LINK_FLAGS} "-Wl,-whole-archive")
target_link_libraries(name_of_executable ${PROJECT_NAME}
I repeat that this only works as of version 2.8.8 of CMake. Just as well CMake manages the dependencies extremely well & is cross-platform because it's not much less painful than plain old Makefiles & certainly less flexible.