Open CI Scripts: Feature Update

    * build_helper: Added --install argument to execute cmake install
    * build_helper: Added the capability to parse axf files for
      code/data/bss sizes and capture it to report
    * build_helper: Added --relative-paths to calculate paths relative
      to the root of the workspace
    * build_helper_configs: Full restructure of config modules.
      Extra build commands and expected artefacts can be defined per
      platform basis
    * Checkpatch: Added directive to ignore --ignore SPDX_LICENSE_TAG
      and added the capability to run only on files changed in patch.
    * CppCheck adjusted suppression directories for new external
      libraries and code-base restructure
    * Added fastmodel dispatcher. It will wrap around fastmodels
      and test against a dynamically defined test_map. Fed with an
      input of the build summary fastmodel dispatcher will detect
      builds which have tests in the map and run them.
    * Added Fastmodel configs for AN519 and AN521 platforms
    * lava_helper. Added arguments for --override-jenkins-job/
      --override-jenkins-url
    * Adjusted JINJA2 template to include build number and
      enable the overrides.
    * Adjusted lava helper configs to support dual platform firmware
      and added CoreIPC config
    * Added report parser module to create/read/evaluate and
      modify reports. Bash scripts for cppcheck checkpatch summaries
      have been removed.
    * Adjusted run_cppcheck/run_checkpatch for new project libraries,
      new codebase structure and other tweaks.
    * Restructured build manager, decoupling it from the tf-m
      cmake requirements. Build manager can now dynamically build a
      configuration from combination of parameters or can just execute
      an array of build commands. Hardcoded tf-m assumptions have been
      removed and moved into the configuration space.
    * Build system can now produce MUSCA_A/ MUSCA_B1 binaries as well
      as intel HEX files.
    * Updated the utilities snippet collection in the tfm-ci-pylib.

Change-Id: Ifad7676e1cd47e3418e851b56dbb71963d85cd88
Signed-off-by: Minos Galanakis <minos.galanakis@linaro.org>
diff --git a/fastmodel_dispatcher/fastmodel_dispatcher.py b/fastmodel_dispatcher/fastmodel_dispatcher.py
new file mode 100644
index 0000000..8c12dae
--- /dev/null
+++ b/fastmodel_dispatcher/fastmodel_dispatcher.py
@@ -0,0 +1,222 @@
+#!/usr/bin/env python3
+
+""" fvp_dispatcher.py:
+
+    Fastmodel dispatcher takes an build report input from build_helper and
+    selects the appropriate tests, lauched in separate fastmodel Wrapper
+    instances """
+
+from __future__ import print_function
+
+__copyright__ = """
+/*
+ * Copyright (c) 2018-2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+ """
+__author__ = "Minos Galanakis"
+__email__ = "minos.galanakis@linaro.org"
+__project__ = "Trusted Firmware-M Open CI"
+__status__ = "stable"
+__version__ = "1.1"
+
+import os
+import sys
+import argparse
+from copy import deepcopy
+from fastmodel_dispatcher_configs import fvp_config_map
+
+try:
+    from tfm_ci_pylib.utils import load_json, print_test, save_json, \
+        show_progress
+    from tfm_ci_pylib.fastmodel_wrapper import FastmodelWrapper
+    from tfm_ci_pylib.tfm_build_manager import TFM_Build_Manager
+
+except ImportError:
+    dir_path = os.path.dirname(os.path.realpath(__file__))
+    sys.path.append(os.path.join(dir_path, "../"))
+    from tfm_ci_pylib.utils import load_json, print_test, save_json, \
+        show_progress
+    from tfm_ci_pylib.fastmodel_wrapper import FastmodelWrapper
+    from tfm_ci_pylib.tfm_build_manager import TFM_Build_Manager
+
+
+def cfilter(config_list, match):
+    """Filter a list of items in _text1_text2_ format and only include
+    results who contain the match term between two underscores """
+
+    # Ensure the match has the format of _text_
+    match = "_%s_" % match.strip("_")
+
+    return [n for n in config_list if match in n]
+
+
+def main(user_args):
+    """ Main logic """
+
+    test_config_list = None
+
+    if user_args.list_cfg:
+        print("Built-in configs:")
+        print("\n".join(fvp_config_map.list()))
+        sys.exit(0)
+    elif user_args.single_cfg:
+        try:
+            # Try to fetch the config to validate it exists
+            fvp_config_map.get_config(user_args.single_cfg)
+            test_config_list = [user_args.single_cfg]
+        except Exception as e:
+            print("Error: %s" % e)
+            sys.exit(1)
+    elif user_args.build_all:
+        test_config_list = fvp_config_map.list()
+    # If a build report is provided parse it
+    elif user_args.build_report:
+        build_report = load_json(user_args.build_report)
+
+        build_cfg = build_report["_metadata_"]["input_build_cfg"]
+
+        # build and test configs share common key name enties
+        config_list = list(map(str.lower,
+                               (map(str, build_report["report"].keys()))))
+
+        # Only choose the tests that have been defined in the map
+        test_config_list = [n for n in fvp_config_map.list()
+                            if n in config_list]
+
+        # Use the Build manager to calcuate the rejection list in the same
+        # manner.
+        rj = TFM_Build_Manager.generate_rejection_list(
+            build_cfg["seed_params"],
+            build_cfg["common_params"],
+            fvp_config_map.get_invalid()).keys()
+
+        # Remove every config that is included in the rejection.
+        # Ignore generated rejection configs that have not been set in the
+        # test map.
+        for name in rj:
+            name = name.lower()
+            try:
+                test_config_list.pop(test_config_list.index(name))
+                print("Rejecting config %s" % name)
+            except Exception as e:
+                print("Rejection ignored with exception:", e)
+    else:
+        print("Noting to do. Please provide a report or a config name to test")
+        sys.exit(1)
+
+    # Apply filters if specified by user
+    if user_args.build_armclang:
+        test_config_list = cfilter(test_config_list, "armclang")
+    elif user_args.build_gnuarm:
+        test_config_list = cfilter(test_config_list, "gnuarm")
+    elif user_args.filter:
+        test_config_list = cfilter(test_config_list, user_args.filter)
+    else:
+        pass
+
+    print("Working on Test list: \n%s" % "\n".join(sorted(test_config_list)))
+
+    if user_args.p_command:
+
+        for test_cfg in test_config_list:
+
+            test_cfg_obj = fvp_config_map.get_config_object(test_cfg)
+            _tmp_cfg = FastmodelWrapper(fvp_cfg=test_cfg_obj.get_config())
+
+            print("\nCommand line:")
+            print("")
+            _tmp_cfg.show_cmd()
+            print("\n")
+        sys.exit(0)
+
+    # Run tests
+    rep = []
+    test_count = 0
+    for test_cfg in test_config_list:
+
+        # Check if the config hardcoded binary path is same as the one
+        # in the build report. If not update the config
+        test_cfg_obj = fvp_config_map.get_config_object(test_cfg)
+
+        rep.append(FastmodelWrapper(
+                   fvp_cfg=test_cfg_obj.get_config())
+                   .start().block_wait().test().save_report().get_report())
+        test_count += 1
+        print("Testing progress:")
+        show_progress(test_count, len(test_config_list))
+
+    # Export the report in a file
+    if user_args.report:
+        f_report = {"report": {}, "_metadata_": {}}
+        f_report["report"] = {k["name"]: deepcopy(k) for k in rep}
+        save_json(user_args.report, f_report)
+
+    sl = [x["name"] for x in rep if x["success"] is True]
+    fl = [x["name"] for x in rep if x["success"] is False]
+
+    print("\n")
+
+    if sl:
+        print_test(t_list=sl, status="passed", tname="Tests")
+    if fl:
+        print_test(t_list=fl, status="failed", tname="Tests")
+        if user_args.eif:
+            sys.exit(1)
+
+
+def get_cmd_args():
+    """ Parse command line arguments """
+
+    # Parse command line arguments to override config
+    parser = argparse.ArgumentParser(description="TFM Fastmodel wrapper.")
+    parser.add_argument("-b", "--build_report",
+                        dest="build_report",
+                        action="store",
+                        help="JSON file produced by build_helper (input)")
+    parser.add_argument("-a", "--build_all",
+                        dest="build_all",
+                        action="store_true",
+                        help="If set build every configuration combination")
+    parser.add_argument("-e", "--error_if_failed",
+                        dest="eif",
+                        action="store_true",
+                        help="If set will change the script exit code if one "
+                              "or more tests fail")
+    parser.add_argument("-r", "--report",
+                        dest="report",
+                        action="store",
+                        help="JSON file containing fastmodel report (output)")
+    parser.add_argument("-g", "--build_gnuarm",
+                        dest="build_gnuarm",
+                        action="store_true",
+                        help="If set build every gnuarm configuration")
+    parser.add_argument("-c", "--build_armclang",
+                        dest="build_armclang",
+                        action="store_true",
+                        help="If set build every armclang configuration")
+    parser.add_argument("-f", "--filter",
+                        dest="filter",
+                        action="store",
+                        help="Only select configs that contain this string")
+    parser.add_argument("-l", "--list-configs",
+                        dest="list_cfg",
+                        action="store_true",
+                        help="Print a list of the built-in configurations and"
+                             "exit")
+    parser.add_argument("-s", "--single-config",
+                        dest="single_cfg",
+                        action="store",
+                        help="Launch testing for a single built-in config,  "
+                              "picked by name")
+    parser.add_argument("-p", "--print-command",
+                        dest="p_command",
+                        action="store_true",
+                        help="Print the FPV launch command to console & exit")
+    return parser.parse_args()
+
+
+if __name__ == "__main__":
+    main(get_cmd_args())