[CMake] C library export management, please critique and improve and tell me if there's a better way

remleduff aaron at assonance.org
Thu May 3 13:52:03 EDT 2018


Hello!

I'm converting a legacy C code base with a slightly unusual problem, but one
that I've seen elsewhere occasionally. Basically, the libraries were not
written with any concept of namespace cleanliness, and when they are linked
together there are many symbol conflicts. Worse, there are symbol conflicts
in object files with the same name, which apparently silently link
successfully without warning!

The legacy build system used some scripting to process all libraries and
object files through objcopy, and I'm attempting to do something similar in
cmake.

I'd appreciate any critique and improvements for this code. Particularly,
I'm not sure why using
"${TARGET_PROPERTY:${target},INTERFACE_LINK_LIBRARIES>" doesn't work. I'd
also love help simplifying the two custom commands.


The approach I'm taking is to define a new target: ${target}-doexport which
renames all symbols in the library except for the exported ones. I then
create an INTERFACE library named ${target}-export which copies the
INTERFACE properties from the orginal target and uses output that has been
properly prefixed. FInally, I create an alias target to the new library,
which is what is actually used in the rest of the codebase to link against.


It's expect to be used like so:
add_library(foo bar.c)
target_link_libraries(foo PUBLIC Threads::Threads)
target_include_directories(foo PUBLIC inc)
target_export_symbols(foo project::foo
   symbol1
   another_symbol
   )

#or refer to symbols in another file
target_export_symbols(foo project::foo
   INTERFACE foo_exports
   )

function(target_export_symbols target export_target)
    set(options)
    set(oneValueArgs INTERFACE)
    set(multiValueArgs)
    cmake_parse_arguments(PARSE_ARGV 2
            export
            "${options}"
            "${oneValueArgs}"
            "${multiValueArgs}")

    set(name "${CMAKE_CURRENT_BINARY_DIR}/lib${target}.a")
    set(export_name "${CMAKE_CURRENT_BINARY_DIR}/lib${target}-export.a")
    set(stamp ${CMAKE_CURRENT_BINARY_DIR}/${target}-export.stamp)
    set(prefix ${target}_)
    set(exports ${CMAKE_CURRENT_BINARY_DIR}/${target}-exported_symbols)
    set(renames ${CMAKE_CURRENT_BINARY_DIR}/${target}-renamed_symbols)

    if(export_INTERFACE AND EXISTS
${CMAKE_CURRENT_SOURCE_DIR}/${export_INTERFACE})
        set(interface ${CMAKE_CURRENT_SOURCE_DIR}/${export_INTERFACE})
        set(interface_cmd "`cat ${interface}`")
    else()
        set(interface)
        set(interface_cmd)
    endif()

    add_custom_command(OUTPUT ${exports}
            DEPENDS "${interface}" ${CMAKE_CURRENT_LIST_FILE}
            COMMAND echo ${interface_cmd} ${export_UNPARSED_ARGUMENTS} |
perl -lane \'print \"\$$_\\$$\" for @F\' > ${exports}
            )

    add_custom_command(OUTPUT ${stamp} ${export_name}
        COMMENT "Unexporting symbols from ${target}"
        DEPENDS ${exports} ${target}
        COMMAND nm --defined-only -g ${name} | grep -v -e\'^ W\' -e\'^ V\'
-f ${exports} | awk \'NF>2 { print \$$3 \" ${prefix}\" \$$3 }\' | sort -u >
${renames}
        COMMAND objcopy --redefine-syms=${renames} ${name} ${export_name}
        COMMAND cmake -E touch ${stamp}
    )
    add_library(${target}-export INTERFACE)
    add_custom_target(${target}-doexport DEPENDS ${stamp})
    add_dependencies(${target}-export ${target} ${target}-doexport)

    foreach(prop INTERFACE_COMPILE_DEFINITIONS INTERFACE_COMPILE_OPTIONS
INTERFACE_LINK_LIBRARIES INTERFACE_INCLUDE_DIRECTORIES)
        set(val "$<TARGET_PROPERTY:${target},${prop}>")
# INTERFACE_LINK_LIBRARIES causes linker failures when used with above
generator expression...
        if (${prop} MATCHES INTERFACE_LINK_LIBRARIES)
            get_target_property(val ${target} ${prop})
            if (NOT val)
                set(val "")
            endif()
            list(APPEND val ${export_name})
        endif()
        if(val)
            set_target_properties(${target}-export PROPERTIES "${prop}"
"${val}")
        endif()
    endforeach()
    add_library(${export_target} ALIAS ${target}-export)

endfunction()



--
Sent from: http://cmake.3232098.n2.nabble.com/


More information about the CMake mailing list