cmake_minimum_required(VERSION 3.28) ## # Merge all the static library dependencies of TARGET into the library as a # POST_BUILD step. function(_bundle_static_replace VAR BEFORE AFTER) string(REPLACE "$<" "$\\\\<" AFTER "${AFTER}") string(REPLACE ">" "$" AFTER "${AFTER}") string(REPLACE "," "$" AFTER "${AFTER}") string(REPLACE ";" "$" AFTER "${AFTER}") set("${VAR}" "$") set("${VAR}" "$") set("${VAR}" "$" PARENT_SCOPE) endfunction() function(_bundle_static_check_output VAR) execute_process(COMMAND ${ARGN} OUTPUT_VARIABLE "${VAR}" RESULT_VARIABLE "_${VAR}" ERROR_QUIET) if (_${VAR}) set("${VAR}" "") endif () set("${VAR}" "${${VAR}}" PARENT_SCOPE) endfunction() function(_bundle_static_is_apple_libtool result_var item) _bundle_static_check_output(version_info "${item}" -V) if (NOT version_info MATCHES "Apple,? Inc\\.") set(${result_var} 0 PARENT_SCOPE) endif () endfunction() function(bundle_static TARGET) get_property(type TARGET "${TARGET}" PROPERTY TYPE) if (NOT type STREQUAL "STATIC_LIBRARY") return() endif () # The following code is quite subtle. First, it "recursively" (up to a depth # limit) expands all the INTERFACE_LINK_LIBRARIES of the TARGET. Once the # full set of library dependencies has been determined, it filters just # the static libraries and replaces them with their on-disk locations. # Start with the $> dependencies of # the target. These are the privately-linked static and interface libraries # that the user intends to delete upon export. set(cmd "$") set(cmd "$") # Repeatedly expand and flatten: T ~> T, T.INTERFACE_LINK_LIBRARIES foreach (i RANGE 5) _bundle_static_replace( cmd "(.+)" "$<$:\\1;$>" ) set(cmd "$>") endforeach () # Ensure we are only including targets _bundle_static_replace(cmd "(.+)" "$") # Rewrite T ~> T^T.TYPE -- we use ^ as a delimiter _bundle_static_replace(cmd "(.+)" "\\1^$") set(cmd "$") # Select exactly the set of static libraries set(cmd "$") # Rewrite T^... ~> $ _bundle_static_replace(cmd "^([^^]+)\\^.+$" "$") # Rename the target to target.tmp add_custom_command( TARGET "${TARGET}" POST_BUILD COMMAND "${CMAKE_COMMAND}" -E rename "$" "$.tmp" VERBATIM ) # Finally merge everything together using the platform tool. find_program(LIB lib.exe HINTS "${CMAKE_AR}") if (WIN32 AND LIB) add_custom_command( TARGET "${TARGET}" POST_BUILD COMMAND "${LIB}" "/out:$" "$.tmp" "${cmd}" COMMAND_EXPAND_LISTS VERBATIM ) return() endif () find_program(LIBTOOL libtool VALIDATOR _bundle_static_is_apple_libtool) if (APPLE AND LIBTOOL) add_custom_command( TARGET "${TARGET}" POST_BUILD COMMAND "${LIBTOOL}" -static -o "$" "$.tmp" "${cmd}" COMMAND_EXPAND_LISTS VERBATIM ) return() endif () _bundle_static_check_output(version_info "${CMAKE_AR}" V) if (version_info MATCHES "GNU|LLVM") string(CONFIGURE [[ create $ addlib $.tmp $, > save end ]] mri_script) string(REGEX REPLACE "(^|\n) +" "\\1" mri_script "${mri_script}") file(GENERATE OUTPUT "fuse-${TARGET}.mri" CONTENT "${mri_script}" TARGET "${TARGET}") add_custom_command( TARGET "${TARGET}" POST_BUILD COMMAND "${CMAKE_AR}" -M < "${CMAKE_CURRENT_BINARY_DIR}/fuse-${TARGET}.mri" VERBATIM ) return() endif () message(FATAL_ERROR "bundle_static_libs not implemented for the present toolchain") endfunction()