This is, disappointingly, quite hard. We managed to do it on the Halide team, but only because it was a hard requirement from a corporate client. To other readers without such constraints, I say this: here be dragons. Use CMake's usual targets and dependencies and let it put all the static libraries on the end-product's link line.
To OP, I say, try this:
First, create a CMakeLists.txt in your vendor
directory with the following content:
# 0. Convenience variable
set(proprietary_lib "${CMAKE_CURRENT_SOURCE_DIR}/proprietary.a")
# 1. Get list of objects inside static lib
execute_process(COMMAND "${CMAKE_AR}" -t "${proprietary_lib}"
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
OUTPUT_VARIABLE proprietary_objects)
string(STRIP "${proprietary_objects}" proprietary_objects)
string(REPLACE "\n" ";" proprietary_objects "${proprietary_objects}")
# 2. Attach configure dependency to the static lib
set_property(DIRECTORY . APPEND PROPERTY
CMAKE_CONFIGURE_DEPENDS proprietary.a)
# 3. Extract the lib at build time
add_custom_command(
OUTPUT ${proprietary_objects}
COMMAND "${CMAKE_AR}" -x "${proprietary_lib}"
DEPENDS "${proprietary_lib}")
# 4. Get absolute paths to the extracted objects
list(TRANSFORM proprietary_objects
PREPEND "${CMAKE_CURRENT_BINARY_DIR}/")
# 5. Attach the objects to a driver target so the
# custom command doesn't race
add_custom_target(proprietary.extract DEPENDS ${proprietary_objects})
# 6. Add a target to encapsulate this
add_library(proprietary OBJECT IMPORTED GLOBAL)
set_target_properties(proprietary PROPERTIES
IMPORTED_OBJECTS "${proprietary_objects}")
# TODO: add usage requirements
# target_include_directories(proprietary INTERFACE ...)
# 7. Force proprietary to run completely after extraction
add_dependencies(proprietary proprietary.extract)
There's a lot going on here, but ultimately the steps are straightforward and the complications are with explaining the dependencies to CMake. Also, this comes with the caveat that it is Linux-only (or at least GNU-ar-compatible archiver only). It is possible to do something similar for MSVC, but it would be too much for this answer.
So first we ask the archiver which objects are in the library and we lightly process its one-object-per-line output into a CMake list. That's step 1 above.
Step 2 tells CMake that if the timestamp on proprietary.a
is ever updated, then it will need to re-run CMake (and thereby get a new list of objects).
Step 3 creates a custom command which will, at build time, run the archiver tool to extract the objects into the vendor
build directory.
Step 4 turns the (relative) list of objects into a list of absolute paths to those objects after the custom command runs. This is for the benefit of add_custom_target
which expects absolute paths (or rather, does weird things with relative paths if certain policies are enabled).
Step 5 creates a custom target to drive the archive extraction.
Step 6 creates an imported object library to encapsulate the extracted library. It has to be global because imported targets are directory-scoped by default and this is an abuse of the imported-library feature. You can add additional usage requirements here.
Finally, step 7 puts a dependency to the driver target on the object library.
This can then be used transparently. Here's an example:
cmake_minimum_required(VERSION 3.16)
project(example)
add_subdirectory(vendor)
add_library(library library.cpp)
target_link_libraries(library PRIVATE proprietary)