1#-------------------------------------------------------------------------------
2# Copyright (c) 2020-2023, Arm Limited. All rights reserved.
3#
4# SPDX-License-Identifier: BSD-3-Clause
5#
6#-------------------------------------------------------------------------------
7
8cmake_minimum_required(VERSION 3.15)
9find_package(Python3)
10
11############################### Manifest lists declaration #####################
12list(APPEND MANIFEST_LISTS ${TFM_MANIFEST_LIST})
13
14if (TFM_EXTRA_MANIFEST_LIST_FILES)
15    list(APPEND MANIFEST_LISTS ${TFM_EXTRA_MANIFEST_LIST_FILES})
16endif()
17
18if (TFM_EXTRAS_REPO_EXTRA_MANIFEST_LIST)
19    set(TMP_MANIFEST_LISTS ${TFM_EXTRAS_REPO_EXTRA_MANIFEST_LIST})
20    list(TRANSFORM TMP_MANIFEST_LISTS PREPEND ${TFM_EXTRAS_REPO_PATH}/)
21    list(APPEND MANIFEST_LISTS ${TMP_MANIFEST_LISTS})
22endif()
23
24# Remove any duplicate entries to prevent same path appended twice in case of mulitiple runs
25list(REMOVE_DUPLICATES MANIFEST_LISTS)
26list(REMOVE_DUPLICATES TFM_EXTRA_GENERATED_FILE_LIST_PATH)
27
28############################### File list declaration ##########################
29set(GENERATED_FILE_LISTS ${CMAKE_CURRENT_SOURCE_DIR}/tfm_generated_file_list.yaml)
30set(GENERATED_FILE_LISTS ${GENERATED_FILE_LISTS} ${TFM_EXTRA_GENERATED_FILE_LIST_PATH})
31
32############################### Functions declaration ##########################
33# Parses the given YAML "files" to find out all the items of the given "field"
34# and put them to the "output_variable" as a list.
35function(parse_field_from_yaml files field output_variable)
36    set(local_variable "")
37    foreach(yaml_file ${files})
38        # Load the lines that refer to the key we selected
39        file(STRINGS ${yaml_file} temp_variable REGEX " *\"${field}\":")
40        # Take only the value of the key
41        list(TRANSFORM temp_variable REPLACE " *\"${field}\": *" ";")
42        # Remove all commas
43        list(TRANSFORM temp_variable REPLACE "," "")
44        # Remove all quote marks
45        list(TRANSFORM temp_variable REPLACE "\"" "")
46        list(APPEND local_variable ${temp_variable})
47    endforeach()
48    set(${output_variable} ${local_variable} PARENT_SCOPE)
49endfunction()
50
51############################### Dependency generation ##########################
52# Get all the manifest files from manifest lists
53foreach(MANIFEST_LIST ${MANIFEST_LISTS})
54    if (NOT EXISTS ${MANIFEST_LIST})
55        message(FATAL_ERROR "Manifest list ${MANIFEST_LIST} doesn't exist")
56    endif()
57
58
59    # Get the path of the manifest list
60    get_filename_component(MANIFEST_LIST_PATH ${MANIFEST_LIST} DIRECTORY)
61
62    # Get all the "manifest"
63    parse_field_from_yaml(${MANIFEST_LIST} manifest MANIFESTS)
64
65    foreach(MANIFEST ${MANIFESTS})
66        # Convert to absolute paths
67        if (NOT IS_ABSOLUTE ${MANIFEST})
68            # First try relative to the manifest
69            if (EXISTS "${MANIFEST_LIST_PATH}/${MANIFEST}")
70                get_filename_component(MANIFEST "${MANIFEST_LIST_PATH}/${MANIFEST}" ABSOLUTE)
71            # Then try relative to the root TF-M source directory
72            elseif (EXISTS "${CMAKE_SOURCE_DIR}/${MANIFEST}")
73                get_filename_component(MANIFEST "${CMAKE_SOURCE_DIR}/${MANIFEST}" ABSOLUTE)
74            endif()
75        endif()
76        list(APPEND MANIFEST_FILES ${MANIFEST})
77    endforeach()
78endforeach()
79
80parse_field_from_yaml("${GENERATED_FILE_LISTS}" template TEMPLATE_FILES)
81# Replace relative paths with absolute paths
82# Paths used in GENERATED_FILE_LISTS are all relative to TF-M root (${CMAKE_SOURCE_DIR})
83list(TRANSFORM TEMPLATE_FILES REPLACE "^([^/\\][^:].*)" "${CMAKE_SOURCE_DIR}/\\1")
84# Append the fixed templates that are not in the GENERATED_FILE_LISTS
85list(APPEND TEMPLATE_FILES
86     ${CMAKE_CURRENT_SOURCE_DIR}/templates/manifestfilename.template
87     ${CMAKE_CURRENT_SOURCE_DIR}/templates/partition_intermedia.template
88     ${CMAKE_CURRENT_SOURCE_DIR}/templates/partition_load_info.template
89     ${CMAKE_CURRENT_SOURCE_DIR}/config_impl.cmake.template
90)
91
92############################### Generate Manifest config header ################
93
94# The function appends the given `config` to the `out_var` variable.
95# Supported `type` are [BOOL, STRING].
96# The format of contents appended is
97#   #cmakedefine01 config      for BOOL types
98#   #cmakedefine config @config@    for STRING types
99function(append_manifest_config out_var config type)
100    # Operate on a local var and write back to the out_var later
101    set(local_var ${${out_var}})
102
103    # Avoid duplications of configs
104    string(FIND "${local_var}" ${config} config_exists)
105    if(${config_exists} EQUAL -1)   # Not found
106        if (${type} STREQUAL "BOOL")
107            string(APPEND local_var "#cmakedefine01 ${config}\r\n")
108        elseif(${type} STREQUAL "STRING")
109            string(APPEND local_var "#cmakedefine ${config} @${config}@\r\n")
110        else()
111            message(FATAL_ERROR "Unsupported config type: ${type}")
112        endif()
113    endif()
114
115    set(${out_var} ${local_var} PARENT_SCOPE)
116endfunction()
117
118# The following build configurations are required to pass to manifest tool via the config header
119#   - The isolation level
120#   - The SPM backend
121#   - "conditional" attributes for every Secure Partition in manifest lists
122append_manifest_config(MANIFEST_CONFIG_H_CONTENT TFM_ISOLATION_LEVEL STRING)
123append_manifest_config(MANIFEST_CONFIG_H_CONTENT CONFIG_TFM_SPM_BACKEND STRING)
124
125parse_field_from_yaml("${MANIFEST_LISTS}" conditional CONDITIONS)
126foreach(CON ${CONDITIONS})
127    append_manifest_config(MANIFEST_CONFIG_H_CONTENT ${CON} BOOL)
128endforeach()
129
130# Generate the config header
131file(WRITE
132     ${CMAKE_CURRENT_BINARY_DIR}/manifest_config.h.in
133     ${MANIFEST_CONFIG_H_CONTENT})
134
135configure_file(${CMAKE_CURRENT_BINARY_DIR}/manifest_config.h.in
136               ${CMAKE_CURRENT_BINARY_DIR}/manifest_config.h)
137
138############################### Command declaration ############################
139
140# Workaround for heap support
141if ("${TEST_PSA_API}" STREQUAL "IPC")
142    execute_process(
143        WORKING_DIRECTORY ${PSA_ARCH_TESTS_PATH}/api-tests
144        COMMAND ${Python3_EXECUTABLE} tools/scripts/manifest_update.py
145    )
146endif()
147
148if (CONFIG_TFM_PARSE_MANIFEST_QUIET)
149    set(PARSE_MANIFEST_QUIET_FLAG "-q")
150else()
151    set(PARSE_MANIFEST_QUIET_FLAG "")
152endif()
153
154set(MANIFEST_COMMAND
155    ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tfm_parse_manifest_list.py
156    -m ${MANIFEST_LISTS}
157    -f ${GENERATED_FILE_LISTS}
158    -c ${CMAKE_CURRENT_BINARY_DIR}/manifest_config.h
159    -o ${CMAKE_BINARY_DIR}/generated
160    ${PARSE_MANIFEST_QUIET_FLAG})
161
162add_custom_command(
163    OUTPUT ${CMAKE_BINARY_DIR}/generated
164    COMMAND ${MANIFEST_COMMAND}
165    DEPENDS ${MANIFEST_LISTS} ${GENERATED_FILE_LISTS}
166            ${MANIFEST_FILES} ${TEMPLATE_FILES}
167)
168
169add_custom_target(
170    manifest_tool
171    DEPENDS ${CMAKE_BINARY_DIR}/generated
172)
173
174# The files need to be generated before cmake will allow them to be used as
175# sources. Due to issue with custom_command scoping the easiest way to do this
176# is to run the script at cmake-time.
177execute_process(
178    COMMAND ${MANIFEST_COMMAND}
179    WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
180    RESULT_VARIABLE RET
181)
182
183if(RET EQUAL 0)
184    include(${CMAKE_BINARY_DIR}/generated/tools/config_impl.cmake)
185else()
186    message(FATAL_ERROR "Manifest tool failed to generate files!")
187endif()
188