[PATCH rtems 01/12] bsp/imxrt: Add script to import mcux-sdk

Christian Mauderer christian.mauderer at embedded-brains.de
Thu May 4 15:00:42 UTC 2023


NXP now offers the support library in a mcux-sdk git repository instead
of in zip files. The git repository supports multiple controllers of the
i.MXRT family instead of a single one.

This commit adds a script that is a (very hacky) parser for the the
cmake files in the mcux-sdk. It copies all necessary files and creates
yml spec files for the controllers that have been imported.

The script is only intended as a helper for updating the files. It is
not necessary for using the BSP.
---
 bsps/arm/imxrt/import_from_mcux_sdk.py | 293 +++++++++++++++++++++++++
 1 file changed, 293 insertions(+)
 create mode 100755 bsps/arm/imxrt/import_from_mcux_sdk.py

diff --git a/bsps/arm/imxrt/import_from_mcux_sdk.py b/bsps/arm/imxrt/import_from_mcux_sdk.py
new file mode 100755
index 0000000000..2dfe224865
--- /dev/null
+++ b/bsps/arm/imxrt/import_from_mcux_sdk.py
@@ -0,0 +1,293 @@
+#!/usr/bin/env python3
+
+""" Parse the cmake files from mcux_sdk and import the sources to RTEMS.
+This script is a ugly hack and it might doesn't work with newer mcux_sdk
+versions.
+
+Provide a number of the cmake files in `devices/MIMXRT*/all_lib_device_*.cmake`
+as parameter to this script. The file format is expected to be quite fixed.
+Don't give anything else to the script. It won't work.
+"""
+
+import argparse
+import logging
+from enum import Enum
+from pathlib import Path
+import re
+from slugify import slugify
+import shutil
+import yaml
+
+class states(Enum):
+    IDLE = 0
+    ADD_MODULE_PATH = 1
+    ADD_CFILES = 2
+    ADD_HFILES = 3
+    SKIP_TILL_ENDIF = 4
+
+def find_file(name, paths):
+    for p in paths:
+        path = Path(p)
+        f = path / f'{name}.cmake'
+        if f.exists():
+            return f.resolve(strict=True)
+    return None
+
+def get_path_from_line(line, cmake, rel_to = None):
+    current_list_dir = str(Path(cmake.name).parent.resolve())
+    path = line.strip().replace("${CMAKE_CURRENT_LIST_DIR}", current_list_dir)
+    path = Path(path)
+    if rel_to is not None:
+        p = Path(path)
+        path = p.relative_to(rel_to)
+    return path
+
+def parse_cmake(cmake, mcux_device, module_path = [], rel_to = None, stack = []):
+    logger.info(f"Parse file: {cmake.name}")
+    if rel_to is None:
+        rel_to = Path(cmake.name).parent.resolve()
+    mod_path = module_path.copy()
+    state = states.IDLE
+    cfiles = []
+    hpaths = []
+
+    # prevent loops
+    if cmake.name in stack:
+        logger.warning(f"Loop detected: {' -> '.join(stack)}")
+        return cfiles, hpaths
+    # Use a shallow copy!
+    stack_work = stack[:] + [cmake.name]
+
+    for line in cmake:
+        logger.debug(f"state: {state}; file: {cmake.name}; line '{line.strip()}'")
+
+        if state == states.IDLE:
+            if "if(${MCUX_DEVICE} STREQUAL " in line:
+                equal_to = re.search(r'"(.*)"', line).group(1)
+                if equal_to == mcux_device:
+                    logger.debug(f"MCUX matches {equal_to}")
+                else:
+                    logger.debug(f"MCUX ({mcux_device}) not equal to {equal_to}")
+                    state = states.SKIP_TILL_ENDIF
+            elif "endif()" in line:
+                state = states.IDLE
+            elif ("list" in line) and ("APPEND" in line) and ("CMAKE_MODULE_PATH" in line):
+                state = states.ADD_MODULE_PATH
+            elif ("target_sources" in line):
+                state = states.ADD_CFILES
+            elif ("target_include_directories" in line):
+                state = states.ADD_HFILES
+            elif ("include(" in line):
+                # NOTE: This will also include commented lines. This is
+                # necessary because NXP only added the includes as an example to
+                # the all_lib_device_*.cmake files.
+                to_include = re.search(r"\((.*)\)", line).group(1)
+                included = find_file(to_include, mod_path)
+                logger.debug(f"Search include file: {to_include}; found {included}")
+                if included is None:
+                    # log unexpected misses
+                    if not (to_include.startswith("middleware") or \
+                            to_include.startswith("CMSIS")):
+                        logger.error(f"can't find {to_include}")
+                elif "rtos" in to_include:
+                    logger.info("Skip include for other RTOS: ${to_include}")
+                elif "utility" in to_include:
+                    logger.info("Skip utilities: ${to_include}")
+                elif "caam" in to_include:
+                    logger.info("Filter non working driver: ${to_include}")
+                elif included.relative_to(rel_to).parts[0] == "drivers" or \
+                        included.relative_to(rel_to).parts[0] == "devices":
+                    # we are only interested in drivers
+                    with open(included, "r") as f:
+                        cf, hp = parse_cmake(f, mcux_device, mod_path, rel_to, stack_work)
+                        cfiles += cf
+                        hpaths += hp
+                else:
+                    logger.debug(f"Ignore included file: {to_include} found at {included}")
+
+        elif state == states.SKIP_TILL_ENDIF:
+            if "endif()" in line:
+                state = states.IDLE
+
+        elif state == states.ADD_MODULE_PATH:
+            if ")" in line:
+                state = states.IDLE
+            else:
+                path = rel_to / get_path_from_line(line, cmake, rel_to)
+                logger.info(f"Appended path {str(path)}")
+                mod_path += [path]
+
+        elif state == states.ADD_CFILES:
+            if ")" in line:
+                state = states.IDLE
+            else:
+                cf = get_path_from_line(line, cmake, rel_to)
+                logger.debug(f"Appended c-file {str(cf)}")
+                cfiles += [cf]
+
+        elif state == states.ADD_HFILES:
+            if ")" in line:
+                state = states.IDLE
+            else:
+                hp = get_path_from_line(line, cmake, rel_to)
+                logger.debug(f"Appended header path {str(hp)}")
+                hpaths += [hp]
+
+    logger.info(f"Parsing file done: {cmake.name}")
+    logger.debug(f"  Found {cfiles}, {hpaths}")
+    return cfiles, hpaths
+
+class mcux_device:
+    name = None
+    cfiles = []
+    hpaths = []
+
+    def __init__(self, name, cfiles = [], hpaths = []):
+        self.name = name
+        self.cfiles = cfiles
+        self.hpaths = hpaths
+
+    def __str__(self):
+        cfiles = ', '.join([str(cf) for cf in self.cfiles])
+        hpaths = ', '.join([str(hp) for hp in self.hpaths])
+        return f"MCUX {self.name}: cfiles: ({cfiles}), hpaths: ({hpaths})"
+
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser(description=__doc__)
+    parser.add_argument(
+            "cmake",
+            help="Path to all_devices.cmake",
+            type=argparse.FileType('r'),
+        )
+    parser.add_argument(
+            "MCUX_DEVICE",
+            help="Values for MCUX_DEVICE that should be used",
+            type=str,
+            nargs='+',
+        )
+    parser.add_argument(
+            "-l", "--loglevel",
+            help = "Define log level.",
+            type = str.upper,
+            choices = ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'],
+            default = 'INFO',
+        )
+    parser.add_argument(
+            "-r", "--rtems",
+            help = "RTEMS base path",
+            required = True,
+        )
+    parser.add_argument(
+            "-c", "--copy",
+            action = 'store_true',
+        )
+    parser.add_argument(
+            "-t", "--collect",
+            action = 'store_true',
+        )
+
+    args = parser.parse_args()
+
+    numeric_loglevel = getattr(logging, args.loglevel)
+    logging.basicConfig(level = numeric_loglevel)
+    logger = logging.getLogger("mcux")
+    logger.debug(f"Started with the following options: {args}")
+
+    devices = []
+
+    # get c-files and header paths from mcux SDK
+    for mcux in args.MCUX_DEVICE:
+        args.cmake.seek(0)
+        cf, hp = parse_cmake(args.cmake, mcux)
+        devices += [mcux_device(mcux, cf, hp)]
+
+    if args.collect:
+        # find files common in multiple devices
+        common_cfiles = set.intersection(*[set(d.cfiles) for d in devices])
+        common_hpaths = set.intersection(*[set(d.hpaths) for d in devices])
+        common = mcux_device("common",
+                sorted(list(common_cfiles)), sorted(list(common_hpaths)))
+
+        # remove common parts from devices
+        for d in devices:
+            d.cfiles = sorted(list(set(d.cfiles) - common_cfiles))
+            d.hpaths = sorted(list(set(d.hpaths) - common_hpaths))
+
+        devices += [common]
+    else:
+        # Only sort files and make sure to have them only once
+        for d in devices:
+            d.cfiles = sorted(list(set(d.cfiles)))
+            d.hpaths = sorted(list(set(d.hpaths)))
+
+    for d in devices:
+        logger.info(str(d))
+
+    # Now for the interesting part: Copying sources and generating yml files
+    mcux_path = Path(args.cmake.name).parent.resolve()
+    rtems_path = Path(args.rtems)
+    rtems_mcux_path = Path("bsps/arm/imxrt/mcux-sdk")
+    for d in devices:
+        yml_file = Path(args.rtems) / "spec" / "build" / "bsps" / "arm" / \
+                "imxrt" / f"obj-{slugify(d.name)}.yml"
+        yml = {
+                "SPDX-License-Identifier": "CC-BY-SA-4.0 OR BSD-2-Clause",
+                "build-type": "objects",
+                "cflags": [],
+                "copyrights":
+                  ["Copyright (C) 2023 embedded brains GmbH (http://www.embedded-brains.de)"],
+                "cppflags": [],
+                "cxxflags": [],
+                "ldflags": [],
+                "enabled-by": True,
+                "includes": [],
+                "install": [
+                    {
+                        "destination": "${BSP_INCLUDEDIR}",
+                        "source": [],
+                    }
+                ],
+                "links": [
+                    {
+                        "role": "build-dependency",
+                        "uid": "grp",
+                    }
+                ],
+                "source": [],
+                "type": "build",
+                "use-after": [],
+                "use-before": [],
+            }
+
+        # Process source files
+        for cf in d.cfiles:
+            logger.info(f"Processing {cf}")
+            cf_rtems = rtems_mcux_path / cf
+            if args.copy:
+                dst = rtems_path / cf_rtems
+                src = mcux_path / cf
+                dst.parent.mkdir(parents=True, exist_ok=True)
+                dst.write_bytes(src.read_bytes())
+            yml["source"] += [str(cf_rtems)]
+
+        # Process header files
+        for hp in d.hpaths:
+            logger.info(f"Processing {hp}")
+            hp_rtems = rtems_mcux_path / hp
+            yml["includes"] += [str(hp_rtems)]
+            dst_path = rtems_path / hp_rtems
+            src_path = mcux_path / hp
+            if args.copy:
+                dst_path.mkdir(parents=True, exist_ok=True)
+
+            for h_src in sorted(src_path.glob("*.h")):
+                h_rel = h_src.relative_to(mcux_path)
+                h_dst = rtems_mcux_path / h_rel
+                yml["install"][0]["source"] += [str(h_dst)]
+                if args.copy:
+                    (rtems_path / h_dst).write_bytes(h_src.read_bytes())
+
+        # Write yml
+        with open(yml_file, 'w') as out:
+            yaml.dump(yml, out)
+        logger.info(f"Written to {yml_file}")
-- 
2.35.3



More information about the devel mailing list