blob: 970cb54d1f99184541a8a60b5f1138be92a8a152 [file] [log] [blame]
Torsten Rasmussenbd7569f2019-03-19 10:38:18 +01001#!/usr/bin/env python3
2#
3# Copyright (c) 2019, Nordic Semiconductor ASA
4#
5# SPDX-License-Identifier: Apache-2.0
6
7'''Tool for parsing a list of projects to determine if they are Zephyr
8projects. If no projects are given then the output from `west list` will be
9used as project list.
10
11Include file is generated for Kconfig using --kconfig-out.
12A <name>:<path> text file is generated for use with CMake using --cmake-out.
13'''
14
15import argparse
16import os
17import sys
18import yaml
19import pykwalify.core
20import subprocess
21import re
Torsten Rasmussenad026c02019-04-09 14:06:09 +020022from pathlib import PurePath
Torsten Rasmussenbd7569f2019-03-19 10:38:18 +010023
24METADATA_SCHEMA = '''
25## A pykwalify schema for basic validation of the structure of a
26## metadata YAML file.
27##
28# The zephyr/module.yml file is a simple list of key value pairs to be used by
29# the build system.
30type: map
31mapping:
32 build:
33 required: true
34 type: map
35 mapping:
36 cmake:
37 required: false
38 type: str
39 kconfig:
40 required: false
41 type: str
42'''
43
44schema = yaml.safe_load(METADATA_SCHEMA)
45
46
47def validate_setting(setting, module_path, filename=None):
48 if setting is not None:
49 if filename is not None:
50 checkfile = os.path.join(module_path, setting, filename)
51 else:
52 checkfile = os.path.join(module_path, setting)
53 if not os.path.isfile(checkfile):
54 return False
55 return True
56
57
58def process_module(module, cmake_out=None, kconfig_out=None):
59 cmake_setting = None
60 kconfig_setting = None
61
62 module_yml = os.path.join(module, 'zephyr/module.yml')
63 if os.path.isfile(module_yml):
64 with open(module_yml, 'r') as f:
65 meta = yaml.safe_load(f.read())
66
67 try:
68 pykwalify.core.Core(source_data=meta, schema_data=schema)\
69 .validate()
70 except pykwalify.errors.SchemaError as e:
Ulf Magnusson50b9b122019-09-07 14:41:01 +020071 sys.exit('ERROR: Malformed "build" section in file: {}\n{}'
72 .format(module_yml, e))
Torsten Rasmussenbd7569f2019-03-19 10:38:18 +010073
74 section = meta.get('build', dict())
75 cmake_setting = section.get('cmake', None)
76 if not validate_setting(cmake_setting, module, 'CMakeLists.txt'):
Ulf Magnusson50b9b122019-09-07 14:41:01 +020077 sys.exit('ERROR: "cmake" key in {} has folder value "{}" which '
78 'does not contain a CMakeLists.txt file.'
79 .format(module_yml, cmake_setting))
Torsten Rasmussenbd7569f2019-03-19 10:38:18 +010080
81 kconfig_setting = section.get('kconfig', None)
82 if not validate_setting(kconfig_setting, module):
Ulf Magnusson50b9b122019-09-07 14:41:01 +020083 sys.exit('ERROR: "kconfig" key in {} has value "{}" which does '
84 'not point to a valid Kconfig file.'
85 .format(module.yml, kconfig_setting))
Torsten Rasmussenbd7569f2019-03-19 10:38:18 +010086
87 cmake_path = os.path.join(module, cmake_setting or 'zephyr')
88 cmake_file = os.path.join(cmake_path, 'CMakeLists.txt')
89 if os.path.isfile(cmake_file) and cmake_out is not None:
Carles Cufi766edd42019-03-31 22:29:30 +020090 cmake_out.write('\"{}\":\"{}\"\n'.format(os.path.basename(module),
Torsten Rasmussenad026c02019-04-09 14:06:09 +020091 os.path.abspath(cmake_path)))
Torsten Rasmussenbd7569f2019-03-19 10:38:18 +010092
93 kconfig_file = os.path.join(module, kconfig_setting or 'zephyr/Kconfig')
94 if os.path.isfile(kconfig_file) and kconfig_out is not None:
95 kconfig_out.write('osource "{}"\n\n'
Torsten Rasmussenad026c02019-04-09 14:06:09 +020096 .format(PurePath(
97 os.path.abspath(kconfig_file)).as_posix()))
Torsten Rasmussenbd7569f2019-03-19 10:38:18 +010098
99
100def main():
101 kconfig_out_file = None
102 cmake_out_file = None
103
104 parser = argparse.ArgumentParser(description='''
105 Process a list of projects and create Kconfig / CMake include files for
106 projects which are also a Zephyr module''')
107
108 parser.add_argument('--kconfig-out',
109 help='File to write with resulting KConfig import'
110 'statements.')
111 parser.add_argument('--cmake-out',
112 help='File to write with resulting <name>:<path>'
113 'values to use for including in CMake')
114 parser.add_argument('-m', '--modules', nargs='+',
115 help='List of modules to parse instead of using `west'
116 'list`')
117 parser.add_argument('-x', '--extra-modules', nargs='+',
118 help='List of extra modules to parse')
Sigvart M. Hovland06365a52019-04-25 12:27:17 +0200119 parser.add_argument('-w', '--west-path', default='west',
120 help='Path to west executable')
Torsten Rasmussenbd7569f2019-03-19 10:38:18 +0100121 args = parser.parse_args()
122
123 if args.modules is None:
Sigvart M. Hovland06365a52019-04-25 12:27:17 +0200124 p = subprocess.Popen([args.west_path, 'list', '--format={posixpath}'],
Torsten Rasmussenbd7569f2019-03-19 10:38:18 +0100125 stdout=subprocess.PIPE,
126 stderr=subprocess.PIPE)
127 out, err = p.communicate()
128 if p.returncode == 0:
129 projects = out.decode(sys.getdefaultencoding()).splitlines()
Piotr Zierhoffera3ddc092019-08-06 12:39:34 +0200130 elif re.match((r'Error: .* is not in a west installation\.'
131 '|FATAL ERROR: no west installation found from .*'),
Torsten Rasmussenbd7569f2019-03-19 10:38:18 +0100132 err.decode(sys.getdefaultencoding())):
133 # Only accept the error from bootstrapper in the event we are
134 # outside a west managed project.
135 projects = []
136 else:
Torsten Rasmussen0dd3b422019-04-25 11:03:27 +0200137 print(err.decode(sys.getdefaultencoding()))
Torsten Rasmussenbd7569f2019-03-19 10:38:18 +0100138 # A real error occurred, raise an exception
139 raise subprocess.CalledProcessError(cmd=p.args,
140 returncode=p.returncode)
141 else:
142 projects = args.modules
143
144 if args.extra_modules is not None:
145 projects += args.extra_modules
146
147 if args.kconfig_out:
Carles Cufi3ad1f272019-07-18 10:38:25 +0200148 kconfig_out_file = open(args.kconfig_out, 'w', encoding="utf-8")
Torsten Rasmussenbd7569f2019-03-19 10:38:18 +0100149
150 if args.cmake_out:
Carles Cufi3ad1f272019-07-18 10:38:25 +0200151 cmake_out_file = open(args.cmake_out, 'w', encoding="utf-8")
Torsten Rasmussenbd7569f2019-03-19 10:38:18 +0100152
153 try:
154 for project in projects:
155 # Avoid including Zephyr base project as module.
156 if project != os.environ.get('ZEPHYR_BASE'):
157 process_module(project, cmake_out_file, kconfig_out_file)
158 finally:
159 if args.kconfig_out:
160 kconfig_out_file.close()
161 if args.cmake_out:
162 cmake_out_file.close()
163
164
165if __name__ == "__main__":
166 main()