Louis Dionne | db7d4c5 | 2019-10-08 21:10:20 +0000 | [diff] [blame] | 1 | # This function defines a linker script in place of the symlink traditionally |
| 2 | # created for shared libraries. |
| 3 | # |
| 4 | # More specifically, this function goes through the PUBLIC and INTERFACE |
| 5 | # library dependencies of <target> and gathers them into a linker script, |
| 6 | # such that those libraries are linked against when the shared library for |
| 7 | # <target> is linked against. |
| 8 | # |
| 9 | # Arguments: |
| 10 | # <target>: A target representing a shared library. A linker script will be |
| 11 | # created in place of that target's TARGET_LINKER_FILE, which is |
| 12 | # the symlink pointing to the actual shared library (usually |
| 13 | # libFoo.so pointing to libFoo.so.1, which itself points to |
| 14 | # libFoo.so.1.0). |
| 15 | |
| 16 | function(define_linker_script target) |
| 17 | if (NOT TARGET "${target}") |
| 18 | message(FATAL_ERROR "The provided target '${target}' is not actually a target.") |
| 19 | endif() |
| 20 | |
| 21 | get_target_property(target_type "${target}" TYPE) |
| 22 | if (NOT "${target_type}" STREQUAL "SHARED_LIBRARY") |
| 23 | message(FATAL_ERROR "The provided target '${target}' is not a shared library (its type is '${target_type}').") |
| 24 | endif() |
| 25 | |
| 26 | set(symlink "$<TARGET_LINKER_FILE:${target}>") |
| 27 | set(soname "$<TARGET_SONAME_FILE_NAME:${target}>") |
| 28 | |
| 29 | get_target_property(interface_libs "${target}" INTERFACE_LINK_LIBRARIES) |
| 30 | |
| 31 | set(link_libraries) |
| 32 | if (interface_libs) |
| 33 | foreach(lib IN LISTS interface_libs) |
Louis Dionne | 2422a9f | 2020-06-26 16:05:55 -0400 | [diff] [blame] | 34 | if ("${lib}" STREQUAL "cxx-headers") |
| 35 | continue() |
| 36 | endif() |
Louis Dionne | 4a3e496 | 2020-07-31 11:18:01 -0400 | [diff] [blame^] | 37 | # If ${lib} is not a target, we use a dummy target which we know will |
| 38 | # have an OUTPUT_NAME property so that CMake doesn't fail when evaluating |
| 39 | # the non-selected branch of the `IF`. It doesn't matter what it evaluates |
| 40 | # to because it's not selected, but it must not cause an error. |
| 41 | # See https://gitlab.kitware.com/cmake/cmake/-/issues/21045. |
| 42 | set(output_name_tgt "$<IF:$<TARGET_EXISTS:${lib}>,${lib},${target}>") |
| 43 | set(libname "$<IF:$<TARGET_EXISTS:${lib}>,$<TARGET_PROPERTY:${output_name_tgt},OUTPUT_NAME>,${lib}>") |
Louis Dionne | 8a51e95 | 2019-10-11 14:42:26 -0400 | [diff] [blame] | 44 | list(APPEND link_libraries "${CMAKE_LINK_LIBRARY_FLAG}${libname}") |
Louis Dionne | db7d4c5 | 2019-10-08 21:10:20 +0000 | [diff] [blame] | 45 | endforeach() |
| 46 | endif() |
Louis Dionne | e3f002f | 2019-10-08 21:33:35 +0000 | [diff] [blame] | 47 | string(REPLACE ";" " " link_libraries "${link_libraries}") |
Louis Dionne | db7d4c5 | 2019-10-08 21:10:20 +0000 | [diff] [blame] | 48 | |
| 49 | set(linker_script "INPUT(${soname} ${link_libraries})") |
| 50 | add_custom_command(TARGET "${target}" POST_BUILD |
| 51 | COMMAND "${CMAKE_COMMAND}" -E remove "${symlink}" |
| 52 | COMMAND "${CMAKE_COMMAND}" -E echo "${linker_script}" > "${symlink}" |
| 53 | COMMENT "Generating linker script: '${linker_script}' as file ${symlink}" |
| 54 | VERBATIM |
| 55 | ) |
| 56 | endfunction() |