zephyr/scripts/zephyr_module.py

169 lines
5.8 KiB
Python
Executable File

#!/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')
parser.add_argument('-w', '--west-path', default='west',
help='Path to west executable')
args = parser.parse_args()
if args.modules is None:
p = subprocess.Popen([args.west_path, '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:
print(err.decode(sys.getdefaultencoding()))
# 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()