Build: Convert tools dir to modern cmake

Rewrite cmake files inside the tools directory. Convert some files to
cmake-configurable files. Alter the interface to the manifest list
parser.

WARNING: This change will not build in isolation, it requires _all_
other cmake changes to successfully build. It is split out only for
clarity of changes.

Change-Id: Ibb4494a6bf739f91337146a459f74a9faf9a5a60
Signed-off-by: Raef Coles <raef.coles@arm.com>
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
new file mode 100644
index 0000000..e1be320
--- /dev/null
+++ b/tools/CMakeLists.txt
@@ -0,0 +1,96 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2020, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+cmake_minimum_required(VERSION 3.13)
+find_package(Python3)
+
+############################### Manifest declaration ###########################
+
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/tfm_manifest_list.yaml.in
+               ${CMAKE_CURRENT_BINARY_DIR}/tfm_manifest_list.yaml @ONLY)
+
+set(MANIFEST_LISTS ${CMAKE_CURRENT_BINARY_DIR}/tfm_manifest_list.yaml)
+set(MANIFEST_LISTS ${MANIFEST_LISTS} ${TFM_EXTRA_MANIFEST_LIST_PATH})
+
+if ("${TEST_PSA_API}" STREQUAL "IPC")
+    configure_file(${CMAKE_CURRENT_SOURCE_DIR}/tfm_psa_ff_test_manifest_list.yaml.in
+                   ${CMAKE_CURRENT_BINARY_DIR}/tfm_psa_ff_test_manifest_list.yaml @ONLY)
+
+    set(MANIFEST_LISTS ${MANIFEST_LISTS} ${CMAKE_CURRENT_BINARY_DIR}/tfm_psa_ff_test_manifest_list.yaml)
+endif()
+
+############################### File list declaration ##########################
+
+set(GENERATED_FILE_LISTS ${CMAKE_CURRENT_SOURCE_DIR}/tfm_generated_file_list.yaml)
+set(GENERATED_FILE_LISTS ${GENERATED_FILE_LISTS} ${TFM_EXTRA_GENERATED_FILE_LIST_PATH})
+
+############################### Dependency generation ##########################
+
+function(parse_field_from_yaml files field output_variable)
+    set(${output_variable} "" PARENT_SCOPE)
+    foreach(yaml_file ${files})
+        # Load the lines that refer to the key we selected
+        file(STRINGS ${yaml_file} temp_variable REGEX " *\"${field}\":")
+        # Take only the value of the key
+        list(TRANSFORM temp_variable REPLACE " *\"${field}\": *" ";")
+        # Remove all commas
+        list(TRANSFORM temp_variable REPLACE "," "")
+        # Remove all quote marks
+        list(TRANSFORM temp_variable REPLACE "\"" "")
+        set(${output_variable} ${${output_variable}} ${temp_variable} PARENT_SCOPE)
+    endforeach()
+endfunction()
+
+parse_field_from_yaml("${GENERATED_FILE_LISTS}" template TEMPLATE_FILES)
+# Replace relative paths with absolute paths
+list(TRANSFORM TEMPLATE_FILES REPLACE "^([^/\\].*)" "${CMAKE_SOURCE_DIR}/\\1")
+
+parse_field_from_yaml("${GENERATED_FILE_LISTS}" output OUTPUT_FILES)
+# Replace relative paths with absolute paths
+list(TRANSFORM OUTPUT_FILES REPLACE "^([^/\\].*)" "${CMAKE_BINARY_DIR}/generated/\\1")
+
+parse_field_from_yaml("${MANIFEST_LISTS}" manifest MANIFEST_FILES)
+# Replace relative paths with absolute paths
+list(TRANSFORM MANIFEST_FILES REPLACE "^([^/\\].*)" "${CMAKE_SOURCE_DIR}/\\1")
+
+############################### Command declaration ############################
+
+# Workaround for heap support
+if ("${TEST_PSA_API}" STREQUAL "IPC")
+    execute_process(
+        WORKING_DIRECTORY ${PSA_ARCH_TESTS_PATH}/api-tests
+        COMMAND ${Python3_EXECUTABLE} tools/scripts/manifest_update.py
+    )
+endif()
+
+add_custom_target(OUTPUT ${OUTPUT_FILES}
+    DEPENDS ${TEMPLATE_FILES} ${MANIFEST_FILES}
+    DEPENDS ${MANIFEST_LISTS}
+)
+
+add_custom_command(OUTPUT ${OUTPUT_FILES}
+    COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tfm_parse_manifest_list.py
+                                  -m ${MANIFEST_LISTS}
+                                  -f ${GENERATED_FILE_LISTS}
+                                  -o ${CMAKE_BINARY_DIR}/generated
+    DEPENDS ${TEMPLATE_FILES} ${MANIFEST_FILES}
+    DEPENDS ${MANIFEST_LISTS}
+)
+
+# The files need to be generated before cmake will allow them to be used as
+# sources. Due to issue with custom_command scoping the easiest way to do this
+# is to run the script at cmake-time if any of the files don't exist.
+foreach(output_file ${OUTPUT_FILES})
+    if(NOT EXISTS ${output_file})
+        execute_process(
+            COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tfm_parse_manifest_list.py
+                                          -m ${MANIFEST_LISTS}
+                                          -f ${GENERATED_FILE_LISTS}
+                                          -o ${CMAKE_BINARY_DIR}/generated
+            )
+endif()
+endforeach()
diff --git a/tools/tfm_manifest_list.yaml b/tools/tfm_manifest_list.yaml.in
similarity index 85%
rename from tools/tfm_manifest_list.yaml
rename to tools/tfm_manifest_list.yaml.in
index e777aa1..6b73b0b 100644
--- a/tools/tfm_manifest_list.yaml
+++ b/tools/tfm_manifest_list.yaml.in
@@ -22,7 +22,7 @@
       "pid": 256,
       "linker_pattern": {
         "library_list": [
-           "*tfm_storage*"
+           "*tfm_partition_ps.*"
          ],
          "object_list": [
            "*test_ps_nv_counters.*",
@@ -40,7 +40,7 @@
       "pid": 257,
       "linker_pattern": {
         "library_list": [
-           "*tfm_internal_trusted_storage*"
+           "*tfm_partition_its.*"
          ]
       }
     },
@@ -55,7 +55,7 @@
       "pid": 258,
       "linker_pattern": {
         "library_list": [
-           "*tfm_audit*"
+           "*tfm_partition_audit.*"
          ]
       }
     },
@@ -70,7 +70,7 @@
       "pid": 259,
       "linker_pattern": {
         "library_list": [
-           "*tfm_crypto*"
+           "*tfm_partition_crypto.*"
          ]
       }
     },
@@ -85,7 +85,7 @@
       "pid": 260,
       "linker_pattern": {
         "library_list": [
-           "*tfm_platform*"
+           "*tfm_partition_platform.*"
          ]
       }
     },
@@ -100,14 +100,14 @@
       "pid": 261,
       "linker_pattern": {
         "library_list": [
-           "*tfm_attest*"
+           "*tfm_partition_attestation.*"
          ]
       }
     },
     {
       "name": "TFM Core Test Service",
       "short_name": "TFM_SP_CORE_TEST",
-      "manifest": "../tf-m-tests/test/test_services/tfm_core_test/tfm_test_core.yaml",
+      "manifest": "@TFM_TEST_PATH@/test_services/tfm_core_test/tfm_test_core.yaml",
       "tfm_partition_ipc": true,
       "conditional": "TFM_PARTITION_TEST_CORE",
       "version_major": 0,
@@ -122,7 +122,7 @@
     {
       "name": "TFM Core Test Service 2",
       "short_name": "TFM_SP_CORE_TEST_2",
-      "manifest": "../tf-m-tests/test/test_services/tfm_core_test_2/tfm_test_core_2.yaml",
+      "manifest": "@TFM_TEST_PATH@/test_services/tfm_core_test_2/tfm_test_core_2.yaml",
       "tfm_partition_ipc": true,
       "conditional": "TFM_PARTITION_TEST_CORE",
       "version_major": 0,
@@ -137,7 +137,7 @@
     {
       "name": "TFM Secure Client Service",
       "short_name": "TFM_SP_SECURE_TEST_PARTITION",
-      "manifest": "../tf-m-tests/test/test_services/tfm_secure_client_service/tfm_test_client_service.yaml",
+      "manifest": "@TFM_TEST_PATH@/test_services/tfm_secure_client_service/tfm_test_client_service.yaml",
       "tfm_partition_ipc": true,
       "conditional": "TFM_PARTITION_TEST_SECURE_SERVICES",
       "version_major": 0,
@@ -160,7 +160,7 @@
     {
       "name": "TFM IPC Service Test",
       "short_name": "TFM_SP_IPC_SERVICE_TEST",
-      "manifest": "../tf-m-tests/test/test_services/tfm_ipc_service/tfm_ipc_service_partition.yaml",
+      "manifest": "@TFM_TEST_PATH@/test_services/tfm_ipc_service/tfm_ipc_service_partition.yaml",
       "tfm_partition_ipc": true,
       "conditional": "TFM_PARTITION_TEST_CORE_IPC",
       "version_major": 0,
@@ -175,7 +175,7 @@
     {
       "name": "TFM IPC Client Service",
       "short_name": "TFM_SP_IPC_CLIENT_TEST",
-      "manifest": "../tf-m-tests/test/test_services/tfm_ipc_client/tfm_ipc_client_partition.yaml",
+      "manifest": "@TFM_TEST_PATH@/test_services/tfm_ipc_client/tfm_ipc_client_partition.yaml",
       "tfm_partition_ipc": true,
       "conditional": "TFM_PARTITION_TEST_CORE_IPC",
       "version_major": 0,
@@ -190,7 +190,7 @@
     {
       "name": "TFM IRQ Test Service 1",
       "short_name": "TFM_IRQ_TEST_1",
-      "manifest": "../tf-m-tests/test/test_services/tfm_irq_test_service_1/tfm_irq_test_service_1.yaml",
+      "manifest": "@TFM_TEST_PATH@/test_services/tfm_irq_test_service_1/tfm_irq_test_service_1.yaml",
       "tfm_partition_ipc": true,
       "conditional": "TFM_ENABLE_IRQ_TEST",
       "version_major": 0,
@@ -206,7 +206,7 @@
     {
       "name": "TF-M PS Test Service",
       "short_name": "TFM_SP_PS_TEST",
-      "manifest": "../tf-m-tests/test/test_services/tfm_ps_test_service/tfm_ps_test_service.yaml",
+      "manifest": "@TFM_TEST_PATH@/test_services/tfm_ps_test_service/tfm_ps_test_service.yaml",
       "tfm_partition_ipc": true,
       "conditional": "TFM_PARTITION_TEST_PS",
       "version_major": 0,
@@ -221,7 +221,7 @@
     {
       "name": "TF-M Secure Client 2 Service",
       "short_name": "TFM_SP_SECURE_CLIENT_2",
-      "manifest": "../tf-m-tests/test/test_services/tfm_secure_client_2/tfm_secure_client_2.yaml",
+      "manifest": "@TFM_TEST_PATH@/test_services/tfm_secure_client_2/tfm_secure_client_2.yaml",
       "tfm_partition_ipc": true,
       "conditional": "TFM_PARTITION_TEST_SECURE_SERVICES",
       "version_major": 0,
diff --git a/tools/tfm_parse_manifest_list.py b/tools/tfm_parse_manifest_list.py
index b2ce52d..e631823 100644
--- a/tools/tfm_parse_manifest_list.py
+++ b/tools/tfm_parse_manifest_list.py
@@ -23,9 +23,6 @@
                     "WARNING: This is an auto-generated file. Do not edit!" + \
                     " ***********/"
 
-DEFAULT_MANIFEST_LIST = os.path.join('tools', 'tfm_manifest_list.yaml')
-DEFAULT_GEN_FILE_LIST = os.path.join('tools', 'tfm_generated_file_list.yaml')
-
 OUT_DIR = None # The root directory that files are generated to
 
 class TemplateLoader(BaseLoader):
@@ -53,17 +50,15 @@
             source = f.read()
         return source, template, False
 
-def process_manifest(manifest_list_file, append):
+def process_manifest(manifest_list_files):
     """
     Parse the input manifest, generate the data base for genereated files
     and generate manifest header files.
 
     Parameters
     ----------
-    manifest_list_file:
-        The manifest list to parse.
-    append:
-        To append the manifest to original or not.
+    manifest_list_files:
+        The manifest lists to parse.
 
     Returns
     -------
@@ -74,16 +69,11 @@
     manifest_header_list = []
     manifest_list = []
 
-    if append:
-        # Load the default manifest first
-        with open(DEFAULT_MANIFEST_LIST) as default_manifest_list_yaml_file:
-            manifest_dic = yaml.safe_load(default_manifest_list_yaml_file)
+    for f in manifest_list_files:
+        with open(f) as manifest_list_yaml_file:
+            manifest_dic = yaml.safe_load(manifest_list_yaml_file)
             manifest_list.extend(manifest_dic["manifest_list"])
 
-    with open(manifest_list_file) as manifest_list_yaml_file:
-        manifest_dic = yaml.safe_load(manifest_list_yaml_file)
-        manifest_list.extend(manifest_dic["manifest_list"])
-
     templatefile_name = 'secure_fw/partitions/manifestfilename.template'
     template = ENV.get_template(templatefile_name)
 
@@ -125,30 +115,22 @@
 
     return manifest_header_list, db
 
-def gen_files(context, gen_file_list, append):
+def gen_files(context, gen_file_lists):
     """
     Generate files according to the gen_file_list
 
     Parameters
     ----------
-    gen_file_list:
-        The list of files to generate
-    append:
-        To append the manifest to original or not
+    gen_file_lists:
+        The lists of files to generate
     """
     file_list = []
 
-    if append:
-        # read default file list first
-        with open(DEFAULT_GEN_FILE_LIST) as file_list_yaml_file:
+    for f in gen_file_lists:
+        with open(f) as file_list_yaml_file:
             file_list_yaml = yaml.safe_load(file_list_yaml_file)
             file_list.extend(file_list_yaml["file_list"])
 
-    with open(gen_file_list) as file_list_yaml_file:
-        # read list of files that need to be generated from templates using db
-        file_list_yaml = yaml.safe_load(file_list_yaml_file)
-        file_list.extend(file_list_yaml["file_list"])
-
     print("Start to generate file from the generated list:")
     for file in file_list:
         outfile_name = os.path.expandvars(file["output"])
@@ -181,41 +163,23 @@
                         , help='The root directory for generated files, the default is TF-M root folder.')
 
     parser.add_argument('-m', '--manifest'
-                        , nargs='*'
+                        , nargs='+'
                         , dest='manifest_args'
-                        , required=False
-                        , default=[]
+                        , required=True
                         , metavar='manifest'
-                        , help='The secure partition manifest list file to parse, the default is '+ DEFAULT_MANIFEST_LIST + '. \
-                                Or the manifest can be append to the default one by explicitly \"append\" it:\
-                                -m manifest_to_append append')
+                        , help='A set of secure partition manifest lists to parse')
 
     parser.add_argument('-f', '--file-list'
-                        , nargs='*'
+                        , nargs='+'
                         , dest='gen_file_args'
-                        , required=False
-                        , default=[]
+                        , required=True
                         , metavar='file-list'
-                        , help='The file descripes the file list to generate, the default is ' + DEFAULT_GEN_FILE_LIST + '. \
-                                Or the file list can be append to the default one by explicitly \"append\" it:\
-                                -f files_to_append append')
+                        , help='These files descripe the file list to generate')
 
     args = parser.parse_args()
     manifest_args = args.manifest_args
     gen_file_args = args.gen_file_args
 
-    if len(manifest_args) > 2 or len(gen_file_args) > 2:
-        parser.print_help()
-        exit(1)
-
-    if len(manifest_args) == 2 and (manifest_args[1] != 'append' and manifest_args[1] != ''):
-        parser.print_help()
-        exit(1)
-
-    if len(gen_file_args) == 2 and (gen_file_args[1] != 'append' and gen_file_args[1] != ''):
-        parser.print_help()
-        exit(1)
-
     return args
 
 ENV = Environment(
@@ -240,14 +204,6 @@
     manifest_args = args.manifest_args
     gen_file_args = args.gen_file_args
     OUT_DIR = args.outdir
-    append_manifest = False
-    append_gen_file = False
-
-    if len(manifest_args) == 2 and manifest_args[1] == 'append':
-        append_manifest = True
-
-    if len(gen_file_args) == 2 and gen_file_args[1] == 'append':
-        append_gen_file = True
 
     if len(manifest_args) == 0:
         manifest_list = DEFAULT_MANIFEST_LIST
@@ -258,11 +214,11 @@
         it will be various to different execution path if converted to absolute path.
         The same for gen_file_list
         """
-        manifest_list = os.path.abspath(args.manifest_args[0])
+        manifest_list = [os.path.abspath(x) for x in args.manifest_args]
     if len(gen_file_args) == 0:
         gen_file_list = DEFAULT_GEN_FILE_LIST
     else:
-        gen_file_list = os.path.abspath(args.gen_file_args[0])
+        gen_file_list = [os.path.abspath(x) for x in args.gen_file_args]
 
     # Arguments could be relative path.
     # Convert to absolute path as we are going to change diretory later
@@ -278,7 +234,7 @@
     """
     os.chdir(os.path.join(sys.path[0], ".."))
 
-    manifest_header_list, db = process_manifest(manifest_list, append_manifest)
+    manifest_header_list, db = process_manifest(manifest_list)
 
     utilities = {}
     context = {}
@@ -289,7 +245,7 @@
     context['manifests'] = db
     context['utilities'] = utilities
 
-    gen_files(context, gen_file_list, append_gen_file)
+    gen_files(context, gen_file_list)
 
 if __name__ == "__main__":
     main()
diff --git a/tools/tfm_psa_ff_test_manifest_list.yaml b/tools/tfm_psa_ff_test_manifest_list.yaml.in
similarity index 82%
rename from tools/tfm_psa_ff_test_manifest_list.yaml
rename to tools/tfm_psa_ff_test_manifest_list.yaml.in
index 08b8d94..62c63f5 100644
--- a/tools/tfm_psa_ff_test_manifest_list.yaml
+++ b/tools/tfm_psa_ff_test_manifest_list.yaml.in
@@ -14,7 +14,7 @@
    {
       "name": "PSA FF Test Client Partition",
       "short_name": "PSA_FF_TEST_CLIENT",
-      "manifest": "../psa-arch-tests/api-tests/platform/manifests/client_partition_psa.json",
+      "manifest": "@PSA_ARCH_TESTS_PATH@/api-tests/platform/manifests/client_partition_psa.json",
       "tfm_partition_ipc": true,
       "conditional": "PSA_API_TEST_IPC",
       "version_major": 0,
@@ -29,7 +29,7 @@
     {
       "name": "PSA FF Test Server Partition",
       "short_name": "PSA_FF_TEST_SERVER",
-      "manifest": "../psa-arch-tests/api-tests/platform/manifests/server_partition_psa.json",
+      "manifest": "@PSA_ARCH_TESTS_PATH@/api-tests/platform/manifests/server_partition_psa.json",
       "tfm_partition_ipc": true,
       "conditional": "PSA_API_TEST_IPC",
       "version_major": 0,
@@ -44,7 +44,7 @@
     {
       "name": "PSA FF Test Driver Partition",
       "short_name": "PSA_FF_TEST_DRIVER",
-      "manifest": "../psa-arch-tests/api-tests/platform/manifests/driver_partition_psa.json",
+      "manifest": "@PSA_ARCH_TESTS_PATH@/api-tests/platform/manifests/driver_partition_psa.json",
       "tfm_partition_ipc": true,
       "conditional": "PSA_API_TEST_IPC",
       "version_major": 0,