Boot Linux faster!

Check our new training course

Boot Linux faster!

Check our new training course
and Creative Commons CC-BY-SA
lecture and lab materials

Bootlin logo

Elixir Cross Referencer

#!/usr/bin/env python3
#
# Copyright (c) 2019, Nordic Semiconductor ASA
#
# SPDX-License-Identifier: Apache-2.0

'''Tool for parsing a list of projects to determine if they are Zephyr
projects. If no projects are given then the output from `west list` will be
used as project list.

Include file is generated for Kconfig using --kconfig-out.
A <name>:<path> text file is generated for use with CMake using --cmake-out.
'''

import argparse
import os
import sys
import yaml
import pykwalify.core
import subprocess
import re
from pathlib import PurePath

METADATA_SCHEMA = '''
## A pykwalify schema for basic validation of the structure of a
## metadata YAML file.
##
# The zephyr/module.yml file is a simple list of key value pairs to be used by
# the build system.
type: map
mapping:
  build:
    required: true
    type: map
    mapping:
      cmake:
        required: false
        type: str
      kconfig:
        required: false
        type: str
'''

schema = yaml.safe_load(METADATA_SCHEMA)


def validate_setting(setting, module_path, filename=None):
    if setting is not None:
        if filename is not None:
            checkfile = os.path.join(module_path, setting, filename)
        else:
            checkfile = os.path.join(module_path, setting)
        if not os.path.isfile(checkfile):
            return False
    return True


def process_module(module, cmake_out=None, kconfig_out=None):
    cmake_setting = None
    kconfig_setting = None

    module_yml = os.path.join(module, 'zephyr/module.yml')
    if os.path.isfile(module_yml):
        with open(module_yml, 'r') as f:
            meta = yaml.safe_load(f.read())

        try:
            pykwalify.core.Core(source_data=meta, schema_data=schema)\
                .validate()
        except pykwalify.errors.SchemaError as e:
            print('ERROR: Malformed "build" section in file: {}\n{}'
                  .format(module_yml, e), file=sys.stderr)
            sys.exit(1)

        section = meta.get('build', dict())
        cmake_setting = section.get('cmake', None)
        if not validate_setting(cmake_setting, module, 'CMakeLists.txt'):
            print('ERROR: "cmake" key in {} has folder value "{}" which '
                  'does not contain a CMakeLists.txt file.'
                  .format(module_yml, cmake_setting), file=sys.stderr)
            sys.exit(1)

        kconfig_setting = section.get('kconfig', None)
        if not validate_setting(kconfig_setting, module):
            print('ERROR: "kconfig" key in {} has value "{}" which does not '
                  'point to a valid Kconfig file.'
                  .format(module_yml, kconfig_setting), file=sys.stderr)
            sys.exit(1)

    cmake_path = os.path.join(module, cmake_setting or 'zephyr')
    cmake_file = os.path.join(cmake_path, 'CMakeLists.txt')
    if os.path.isfile(cmake_file) and cmake_out is not None:
        cmake_out.write('\"{}\":\"{}\"\n'.format(os.path.basename(module),
                                                 os.path.abspath(cmake_path)))

    kconfig_file = os.path.join(module, kconfig_setting or 'zephyr/Kconfig')
    if os.path.isfile(kconfig_file) and kconfig_out is not None:
        kconfig_out.write('osource "{}"\n\n'
                          .format(PurePath(
                              os.path.abspath(kconfig_file)).as_posix()))


def main():
    kconfig_out_file = None
    cmake_out_file = None

    parser = argparse.ArgumentParser(description='''
    Process a list of projects and create Kconfig / CMake include files for
    projects which are also a Zephyr module''')

    parser.add_argument('--kconfig-out',
                        help='File to write with resulting KConfig import'
                             'statements.')
    parser.add_argument('--cmake-out',
                        help='File to write with resulting <name>:<path>'
                             'values to use for including in CMake')
    parser.add_argument('-m', '--modules', nargs='+',
                        help='List of modules to parse instead of using `west'
                             'list`')
    parser.add_argument('-x', '--extra-modules', nargs='+',
                        help='List of extra modules to parse')
    args = parser.parse_args()

    if args.modules is None:
        p = subprocess.Popen(['west', 'list', '--format={posixpath}'],
                             stdout=subprocess.PIPE,
                             stderr=subprocess.PIPE)
        out, err = p.communicate()
        if p.returncode == 0:
            projects = out.decode(sys.getdefaultencoding()).splitlines()
        elif re.match(r'Error: .* is not in a west installation\..*',
                      err.decode(sys.getdefaultencoding())):
            # Only accept the error from bootstrapper in the event we are
            # outside a west managed project.
            projects = []
        else:
            # A real error occurred, raise an exception
            raise subprocess.CalledProcessError(cmd=p.args,
                                                returncode=p.returncode)
    else:
        projects = args.modules

    if args.extra_modules is not None:
        projects += args.extra_modules

    if args.kconfig_out:
        kconfig_out_file = open(args.kconfig_out, 'w')

    if args.cmake_out:
        cmake_out_file = open(args.cmake_out, 'w')

    try:
        for project in projects:
            # Avoid including Zephyr base project as module.
            if project != os.environ.get('ZEPHYR_BASE'):
                process_module(project, cmake_out_file, kconfig_out_file)
    finally:
        if args.kconfig_out:
            kconfig_out_file.close()
        if args.cmake_out:
            cmake_out_file.close()


if __name__ == "__main__":
    main()