diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e8ea55..4fe2447 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -269,6 +269,9 @@ # Create a distro for the microcontroller cmake target, ensuring its sources are only compiled once mbed_create_distro(${MBED_TARGET_CMAKE_NAME}-lib ${MBED_TARGET_CMAKE_NAME} mbed-core-flags) + # Set up the linker script and hook it up to the MCU cmake target + mbed_setup_linker_script(${MBED_TARGET_CMAKE_NAME}-lib) + # Now make the Mbed OS code depend on the target, ensuring everything has access to the uC's flags and objects. target_link_libraries(mbed-core-flags INTERFACE ${MBED_TARGET_CMAKE_NAME}-lib) diff --git a/tools/cmake/mbed_create_distro.cmake b/tools/cmake/mbed_create_distro.cmake index 5848e33..40afa3c 100644 --- a/tools/cmake/mbed_create_distro.cmake +++ b/tools/cmake/mbed_create_distro.cmake @@ -48,6 +48,9 @@ get_property(CURR_MODULE_TYPE TARGET ${CURR_MODULE} PROPERTY TYPE) + # Pass up mbed linker script property, which is used by the top level code to select the linker script + copy_append_property(INTERFACE_MBED_LINKER_SCRIPT ${CURR_MODULE} ${NAME}) + if("${CURR_MODULE_TYPE}" STREQUAL "STATIC_LIBRARY") # Don't need to do anything other than linking it target_link_libraries(${NAME} PUBLIC ${CURR_MODULE}) diff --git a/tools/cmake/mbed_set_linker_script.cmake b/tools/cmake/mbed_set_linker_script.cmake index 4a34faf..9d1a403 100644 --- a/tools/cmake/mbed_set_linker_script.cmake +++ b/tools/cmake/mbed_set_linker_script.cmake @@ -2,53 +2,88 @@ # SPDX-License-Identifier: Apache-2.0 # -# Preprocesses and sets the linker script for an Mbed target. -# Called once for each MCU target in the build system. +# Sets the linker script for an Mbed target. +# Called once by the buildscripts for each MCU target in the build system. +# Note: Linker script path may be absolute or relative. If relative, it will be interpreted relative to the current source dir. # function(mbed_set_linker_script input_target raw_linker_script_path) - set(LINKER_SCRIPT_PATH ${CMAKE_CURRENT_BINARY_DIR}/${input_target}.link_script.ld) + # Make sure that we have an absolute path so that it can be used from a different directory + get_filename_component(raw_linker_script_path ${raw_linker_script_path} ABSOLUTE) + + # Use a custom property to store the linker script. This property will be read and passed up by mbed_create_distro() + set_property(TARGET ${input_target} PROPERTY INTERFACE_MBED_LINKER_SCRIPT ${raw_linker_script_path}) + +endfunction(mbed_set_linker_script) + + +# +# Set up the linker script for the top-level Mbed MCU target. +# If needed, this also creates another target to preprocess the linker script. +# +function(mbed_setup_linker_script mbed_mcu_target) + + # Find the path to the desired linker script + get_property(RAW_LINKER_SCRIPT_PATHS TARGET ${mbed_mcu_target} PROPERTY INTERFACE_MBED_LINKER_SCRIPT) + + # Check if two (or more) different linker scripts got used + list(REMOVE_DUPLICATES RAW_LINKER_SCRIPT_PATHS) + list(LENGTH RAW_LINKER_SCRIPT_PATHS NUM_RAW_LINKER_SCRIPT_PATHS) + if(NUM_RAW_LINKER_SCRIPT_PATHS GREATER 1) + message(FATAL_ERROR "More than one linker script selected for the current MCU target. Perhaps multiple targets with linker scripts set were linked together?") + endif() + + # Make sure the linker script exists + if(NOT EXISTS "${RAW_LINKER_SCRIPT_PATHS}") + message(FATAL_ERROR "Selected linker script ${RAW_LINKER_SCRIPT_PATHS} does not exist!") + endif() + + set(LINKER_SCRIPT_PATH ${CMAKE_BINARY_DIR}/${MBED_TARGET_CMAKE_NAME}.link_script.ld) + # To avoid path limits on Windows, we create a "response file" and set the path to it as a # global property. We need this solely to pass the compile definitions to GCC's preprocessor, # so it can expand any macro definitions in the linker script. - get_property(_linker_preprocess_definitions GLOBAL PROPERTY COMPILE_DEFS_RESPONSE_FILE) + get_property(linker_defs_response_file GLOBAL PROPERTY COMPILE_DEFS_RESPONSE_FILE) if(MBED_TOOLCHAIN STREQUAL "GCC_ARM") + + get_filename_component(RAW_LINKER_SCRIPT_NAME ${RAW_LINKER_SCRIPT_PATHS} NAME) + get_filename_component(LINKER_SCRIPT_NAME ${LINKER_SCRIPT_PATH} NAME) add_custom_command( OUTPUT ${LINKER_SCRIPT_PATH} PRE_LINK COMMAND - ${CMAKE_C_COMPILER} ${_linker_preprocess_definitions} + ${CMAKE_C_COMPILER} @${linker_defs_response_file} -E -x assembler-with-cpp - -P ${raw_linker_script_path} + -P ${RAW_LINKER_SCRIPT_PATHS} -o ${LINKER_SCRIPT_PATH} DEPENDS - ${raw_linker_script_path} + ${RAW_LINKER_SCRIPT_PATHS} + ${linker_defs_response_file} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMENT - "Link line:" + "Preprocess linker script: ${RAW_LINKER_SCRIPT_NAME} -> ${LINKER_SCRIPT_NAME}" VERBATIM ) - # CMake will not let us add PRE_LINK commands to INTERFACE targets, and input_target could - # be an INTERFACE target. - # To get around this we create an intermediate custom target depending on the preprocessed - # linker script output by CPP. We add this custom target as a dependency of input_target. - # This ensures CMake runs our custom command to preprocess the linker script before trying - # to build input_target. - set(LinkerScriptTarget ${input_target}LinkerScript) - add_custom_target(${LinkerScriptTarget} DEPENDS ${LINKER_SCRIPT_PATH} VERBATIM) - add_dependencies(${input_target} ${LinkerScriptTarget}) - target_link_options(${input_target} + + # The job to create the linker script gets attached to the mbed-linker-script target, + # which is then added as a dependency of the MCU target. This ensures the linker script will exist + # by the time we need it. + add_custom_target(mbed-linker-script DEPENDS ${LINKER_SCRIPT_PATH} VERBATIM) + add_dependencies(${mbed_mcu_target} mbed-linker-script) + + # Add linker flags to the MCU target to pick up the preprocessed linker script + target_link_options(${mbed_mcu_target} INTERFACE "-T" "${LINKER_SCRIPT_PATH}" ) elseif(MBED_TOOLCHAIN STREQUAL "ARM") - target_link_options(${input_target} + target_link_options(${mbed_mcu_target} INTERFACE "--scatter=${raw_linker_script_path}" "--predefine=${_linker_preprocess_definitions}" "--map" ) endif() -endfunction() +endfunction(mbed_setup_linker_script)