I have a project that builds a static library. It depends on a dozen other projects (some of 'em are mine, also building static libraries) that look up through find_package()
and have imported targets. Giving a build option -DBUILD_COMBINED_STATIC=ON
, I’d like to be able to build a combined static library that includes all dependent static libraries into a single one. With GNU ar
, I need to render an MRI file and run ar -M combined.mri
. With MSVC’s librarian, it’s possible by mentioning source libraries in CLI. Looks like a task for add_custom_command()
+ add_custom_target()
…
The most problematic part is getting a list of paths to all static libraries a project’s target should combine. First of all, it must include indirect dependencies as well. Secondly, LINK_LIBRARIES
(or INTERFACE_LINK_LIBRARIES
of dependencies) may have generator expressions. The latter means I have to use file(GENERATE…)
. Thanks to transitive $<TARGET_PROPERTY:…>
genex evaluation, I can relatively easily get all dependencies recursively, having a list of all CMake targets at the end:
set_property(
TARGET mylib
PROPERTY
TRANSITIVE_LINK_PROPERTIES INTERFACE_LINK_LIBRARIES
)
file(
GENERATE
OUTPUT mylib-all-dependencies.lst
CONTENT [=[
$<LIST:REMOVE_DUPLICATES,$<TARGET_PROPERTY:mylib,INTERFACE_LINK_LIBRARIES>>
]=]
)
The list of CMake targets needs to be resolved into particular file paths and then used to form an MRI file. It’ll be nice to use $<LIST:TRANSFORM…>
somehow to replace a CMake target with $<TARGET_PROPERTY:ARCHIVE_OUTPUT_NAME>
(with fallback to OUTPUT_NAME
) for all list items… Apparently, it’s not possible for now anyway…
So, the maximum I can get after the initial CMake configuration step is this list of CMake targets that must be resolved into paths to particular static libraries (filtering out all non-static library dependencies in the middle). This also means that at build time, running cmake -P render-mri-file.cmake
via add_custom_xxx
command wouldn’t work cuz the script doesn’t have any targets, and even usage of imported targets is prohibited, so there is no way to resolve OUTPUT_NAME
property into a filename. That eventually means add_custom_command()
or …_target()
should run cmake
at build time to configure another “dummy” project that must perform all the same find_package
calls, import all the same targets and reading the list file rendered by the top-level cmake
configure step tries to render the MRI file…
That is the current “solution” I’m thinking about. I appreciate any advice (in case I’ve missed something).
The other approach I’ve tried, or I’m researching now, is to replace archive-creating commands somehow:
- It looks like it is impossible to do this per target because these commands are not properties of a target but global scope variables (set by a toolchain file?).
- Unlike shared libraries, static libraries do not have a “link” step, so overriding the
CXX_LINKER_LAUNCHER
property for the target won’t help as well ;-( - Maybe add an
MRI
“language” and redefineCMAKE_MRI_ARCHIVE_APPEND
,CMAKE_MRI_ARCHIVE_CREATE
, andCMAKE_MRI_ARCHIVE_FINISH
into CMake scripts that will start rendering MRI file (addingCREATE <archive>
preamble), appendADDLIB
commands, finalize the MRI script w/SAVE
command and executear
at the end. But, I’m not sure (yet) how to useadd_library
and specify this MRI “language”…
Any alternative suggestions are also very welcome.
PS However, in the future, I’d prefer to have a solution to build combined static libraries out of the box in CMake. I will research how to do it and make an MR. Any thoughts on this implementation are also very welcome.
PSS Casting @craig.scott into discussion