Build: Refactor code-sharing primitives

Makes the interface more aligned with modern cmake, and generally
streamlines the experience.

Change-Id: Iad289c7e6be1af1e7ae80d3792698a31b2d9368e
Signed-off-by: Raef Coles <raef.coles@arm.com>
diff --git a/toolchain_ARMCLANG.cmake b/toolchain_ARMCLANG.cmake
index 8843484..b6e3e3a 100644
--- a/toolchain_ARMCLANG.cmake
+++ b/toolchain_ARMCLANG.cmake
@@ -313,74 +313,127 @@
     )
 endmacro()
 
-# Macro for sharing code among independent binaries. This function extracts
-# some parts of the code based on a symbol template file and creates a text
-# file, which contains the symbols with their absolute addresses, which can be
-# picked up by the linker when linking the other target.
-# INPUTS:
-#     TARGET -                -  Target to extract the symbols/objects from
-#     SHARED_SYMBOL_TEMPLATE  -  Template with names of symbols to share
-macro(compiler_create_shared_code TARGET SHARED_SYMBOL_TEMPLATE)
-    # Create a temporary file, which contains all extracted symbols from 'TARGET'
-    set(ALL_SYMBOLS ${CMAKE_CURRENT_BINARY_DIR}/all_symbols.txt)
+macro(target_share_symbols target symbol_name_file)
+    get_target_property(TARGET_TYPE ${target} TYPE)
+    if (NOT TARGET_TYPE STREQUAL "EXECUTABLE")
+        message(FATAL_ERROR "${target} is not an executable. Symbols cannot be shared from libraries.")
+    endif()
 
-    set_property(TARGET ${TARGET} APPEND_STRING PROPERTY LINK_FLAGS " --symdefs=${ALL_SYMBOLS}")
+    FILE(STRINGS ${symbol_name_file} KEEP_SYMBOL_LIST
+        LENGTH_MINIMUM 1
+    )
 
-    # Find the CMake script doing the symbol filtering.
-    find_file(FILTER_SYMBOLS_SCRIPT "FilterSharedSymbols.cmake" PATHS ${CMAKE_MODULE_PATH} PATH_SUFFIXES Common NO_DEFAULT_PATH)
+    # strip all the symbols except those proveded as arguments. Long inline
+    # python scripts aren't ideal, but this is both portable and possibly easier
+    # to maintain than trying to filter files at build time in cmake.
+    add_custom_command(TARGET ${target}
+        POST_BUILD
+        VERBATIM
+        COMMAND python3 -c "from sys import argv; import re; f = open(argv[1], 'rt'); p = [x.replace('*', '.*') for x in argv[2:]]; l = [x for x in f.readlines() if re.search(r'(?=('+'$|'.join(p + ['SYMDEFS']) + r'))', x)]; f.close(); f = open(argv[1], 'wt'); f.writelines(l); f.close();" $<TARGET_FILE_DIR:${target}>/${target}_shared_symbols.txt ${KEEP_SYMBOL_LIST})
 
-    # Single step, just filter the unwanted symbols from symdefs file
-    add_custom_command(TARGET ${TARGET}
-                        POST_BUILD
+    # Force the target to not remove the symbols if they're unused. Not
+    # currently possible on GNU, has to be part of the linker script.
+    list(TRANSFORM KEEP_SYMBOL_LIST PREPEND --keep=)
+    target_link_options(${target}
+        PRIVATE
+            ${KEEP_SYMBOL_LIST}
+    )
 
-                        COMMAND ${CMAKE_COMMAND}
-                            -DSHARED_SYMBOL_TEMPLATE=${SHARED_SYMBOL_TEMPLATE}
-                            -DALL_SYMBOLS=${ALL_SYMBOLS}
-                            -P ${FILTER_SYMBOLS_SCRIPT}
-                        BYPRODUCTS
-                            ${CMAKE_CURRENT_BINARY_DIR}/shared_symbols_name.txt
-                            ${CMAKE_CURRENT_BINARY_DIR}/shared_symbols_addr.txt
-                        COMMENT "Filtering shared symbols"
+    # Ask armclang to produce a symdefs file that will
+    target_link_options(${target}
+        PRIVATE
+            --symdefs=$<TARGET_FILE_DIR:${target}>/${target}_shared_symbols.txt
     )
 endmacro()
 
-macro(compiler_weaken_symbols TARGET SHARED_CODE_PATH ORIG_TARGET LIB_LIST)
-    # Find the CMake scripts
-    find_file(WEAKEN_SYMBOLS_SCRIPT "WeakenSymbols.cmake" PATHS ${CMAKE_MODULE_PATH} PATH_SUFFIXES Common NO_DEFAULT_PATH)
+macro(target_link_shared_code target)
+    get_target_property(TARGET_SOURCE_DIR ${target} SOURCE_DIR)
 
-    add_custom_command(TARGET ${TARGET}
-                        PRE_LINK
+    foreach(symbol_provider ${ARGN})
+        if (TARGET ${symbol_provider})
+            get_target_property(SYMBOL_PROVIDER_TYPE ${symbol_provider} TYPE)
+            if (NOT SYMBOL_PROVIDER_TYPE STREQUAL "EXECUTABLE")
+                message(FATAL_ERROR "${symbol_provider} is not an executable. Symbols cannot be shared from libraries.")
+            endif()
+        endif()
 
-                        COMMAND ${CMAKE_COMMAND}
-                            -DLIB_LIST='${LIB_LIST}'
-                            -DSHARED_CODE_PATH=${SHARED_CODE_PATH}
-                            -P ${WEAKEN_SYMBOLS_SCRIPT}
-                        COMMENT "Set conflicting symbols to be weak in the original libraries to avoid collision")
-
-    # If sharing target is defined by TF-M build then setup dependency
-    if(NOT ${ORIG_TARGET} STREQUAL "EXTERNAL_TARGET")
-        add_dependencies(${TARGET} ${ORIG_TARGET})
-    endif()
+        add_dependencies(${target} ${symbol_provider})
+        # Some cmake functions don't allow generator expressions, so get the
+        # property as a backup. If the symbol provider hasn't been created yet,
+        # then use the output dir of the target.
+        if (TARGET ${symbol_provider})
+            get_target_property(SYMBOL_PROVIDER_OUTPUT_DIR ${symbol_provider} RUNTIME_OUTPUT_DIRECTORY)
+        else()
+            get_target_property(SYMBOL_PROVIDER_OUTPUT_DIR ${target} RUNTIME_OUTPUT_DIRECTORY)
+        endif()
+        # Set these properties so that cmake will allow us to use a source file
+        # that doesn't exist at cmake-time, but we guarantee will exist at
+        # compile-time.
+        set_source_files_properties(${SYMBOL_PROVIDER_OUTPUT_DIR}/${symbol_provider}_shared_symbols.txt
+            DIRECTORY ${TARGET_SOURCE_DIR}
+            PROPERTIES
+                EXTERNAL_OBJECT true
+                GENERATED true
+        )
+        target_sources(${target}
+            PRIVATE
+                $<TARGET_FILE_DIR:${symbol_provider}>/${symbol_provider}_shared_symbols.txt
+        )
+    endforeach()
 endmacro()
 
-# Macro for linking shared code to given target. Location of shared code could
-# be outside of the TF-M project. Its location can be defined with the CMake
-# command line argument "SHARED_CODE_PATH". The file containing the shared objects
-# must be named "shared_symbols_addr.txt".
-# INPUTS:
-#     TARGET            Target to link the shared code to
-#     SHARED_CODE_PATH  Shared code located in this folder
-#     ORIG_TARGET       Target that shared code was extraced from <TARGET | "EXTERNAL_TARGET">
-#     LIB_LIST          List of libraries which are linked to top level target
-macro(compiler_link_shared_code TARGET SHARED_CODE_PATH ORIG_TARGET LIB_LIST)
-    # ARMCLANG requires adding a symbol definition file to the source file list.
-    # This is the output of the -symdefs compiler switch.
-    set_source_files_properties(${SHARED_CODE_PATH}/shared_symbols_addr.txt PROPERTIES EXTERNAL_OBJECT true GENERATED true)
-    target_sources(${TARGET} PRIVATE ${SHARED_CODE_PATH}/shared_symbols_addr.txt)
+macro(target_strip_symbols target)
+    set(SYMBOL_LIST "${ARGN}")
+    list(TRANSFORM SYMBOL_LIST PREPEND  --strip-symbol=)
 
-    compiler_weaken_symbols(${TARGET}
-                            ${SHARED_CODE_PATH}
-                            ${ORIG_TARGET}
-                            "${LIB_LIST}"
+    # TODO we assume that arm-none-eabi-objcopy is available - since we're using
+    # armclang this isn't necessarily true.
+    add_custom_command(
+        TARGET ${target}
+        POST_BUILD
+        COMMAND ${CROSS_COMPILE}-objcopy
+        ARGS $<TARGET_FILE:${target}> --wildcard ${SYMBOL_LIST} $<TARGET_FILE:${target}>
+    )
+endmacro()
+
+macro(target_strip_symbols_from_dependency target dependency)
+    set(SYMBOL_LIST "${ARGN}")
+    list(TRANSFORM SYMBOL_LIST PREPEND  --strip-symbol=)
+
+    # TODO we assume that arm-none-eabi-objcopy is available - since we're using
+    # armclang this isn't necessarily true.
+    add_custom_command(
+        TARGET ${target}
+        PRE_LINK
+        COMMAND ${CROSS_COMPILE}-objcopy
+        ARGS $<TARGET_FILE:${dependency}> --wildcard ${SYMBOL_LIST} $<TARGET_FILE:${dependency}>
+    )
+endmacro()
+
+macro(target_weaken_symbols target)
+    set(SYMBOL_LIST "${ARGN}")
+    list(TRANSFORM SYMBOL_LIST PREPEND  --weaken-symbol=)
+
+    # TODO we assume that arm-none-eabi-objcopy is available - since we're using
+    # armclang this isn't necessarily true.
+    add_custom_command(
+        TARGET ${target}
+        POST_BUILD
+        COMMAND ${CROSS_COMPILE}-objcopy
+        ARGS $<TARGET_FILE:${target}> --wildcard ${SYMBOL_LIST} $<TARGET_FILE:${target}>
+    )
+endmacro()
+
+macro(target_weaken_symbols_from_dependency target dependency)
+    set(SYMBOL_LIST "${ARGN}")
+    list(TRANSFORM SYMBOL_LIST PREPEND  --weaken-symbol=)
+
+    # TODO we assume that arm-none-eabi-objcopy is available - since we're using
+    # armclang this isn't necessarily true.
+    add_custom_command(
+        TARGET ${target}
+        PRE_LINK
+        COMMAND ${CROSS_COMPILE}-objcopy
+        ARGS $<TARGET_FILE:${dependency}> --wildcard ${SYMBOL_LIST} $<TARGET_FILE:${dependency}>
     )
 endmacro()