blob: c498233a80e640383a5c1e82a941e02309a18da8 [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.
Anas Nashif286a9ed2019-12-11 10:13:23 -050013
14Using --sanitycheck-out <filename> an argument file for sanitycheck script will
15be generated which would point to test and sample roots available in modules
16that can be included during a sanitycheck run. This allows testing code
17maintained in modules in addition to what is available in the main Zephyr tree.
Torsten Rasmussenbd7569f2019-03-19 10:38:18 +010018'''
19
20import argparse
21import os
22import sys
23import yaml
24import pykwalify.core
25import subprocess
26import re
Torsten Rasmussenb3da9ef2019-12-13 09:42:13 +010027from pathlib import Path, PurePath
Torsten Rasmussenbd7569f2019-03-19 10:38:18 +010028
29METADATA_SCHEMA = '''
30## A pykwalify schema for basic validation of the structure of a
31## metadata YAML file.
32##
33# The zephyr/module.yml file is a simple list of key value pairs to be used by
34# the build system.
35type: map
36mapping:
37 build:
Anas Nashif286a9ed2019-12-11 10:13:23 -050038 required: false
Torsten Rasmussenbd7569f2019-03-19 10:38:18 +010039 type: map
40 mapping:
41 cmake:
42 required: false
43 type: str
44 kconfig:
45 required: false
46 type: str
Anas Nashif286a9ed2019-12-11 10:13:23 -050047 tests:
48 required: false
49 type: seq
50 sequence:
51 - type: str
52 samples:
53 required: false
54 type: seq
55 sequence:
56 - type: str
57 boards:
58 required: false
59 type: seq
60 sequence:
61 - type: str
Torsten Rasmussenbd7569f2019-03-19 10:38:18 +010062'''
63
64schema = yaml.safe_load(METADATA_SCHEMA)
65
66
67def validate_setting(setting, module_path, filename=None):
68 if setting is not None:
69 if filename is not None:
70 checkfile = os.path.join(module_path, setting, filename)
71 else:
72 checkfile = os.path.join(module_path, setting)
73 if not os.path.isfile(checkfile):
74 return False
75 return True
76
77
Anas Nashif286a9ed2019-12-11 10:13:23 -050078def process_module(module):
Torsten Rasmussenb3da9ef2019-12-13 09:42:13 +010079 module_path = PurePath(module)
80 module_yml = module_path.joinpath('zephyr/module.yml')
Anas Nashif286a9ed2019-12-11 10:13:23 -050081
Sebastian Bøebb95dce2019-12-19 15:49:22 +010082 # The input is a module if zephyr/module.yml is a valid yaml file
83 # or if both zephyr/CMakeLists.txt and zephyr/Kconfig are present.
84
Torsten Rasmussenb3da9ef2019-12-13 09:42:13 +010085 if Path(module_yml).is_file():
86 with Path(module_yml).open('r') as f:
Torsten Rasmussenbd7569f2019-03-19 10:38:18 +010087 meta = yaml.safe_load(f.read())
88
89 try:
90 pykwalify.core.Core(source_data=meta, schema_data=schema)\
91 .validate()
92 except pykwalify.errors.SchemaError as e:
Ulf Magnusson50b9b122019-09-07 14:41:01 +020093 sys.exit('ERROR: Malformed "build" section in file: {}\n{}'
Torsten Rasmussenb3da9ef2019-12-13 09:42:13 +010094 .format(module_yml.as_posix(), e))
Torsten Rasmussenbd7569f2019-03-19 10:38:18 +010095
Anas Nashif286a9ed2019-12-11 10:13:23 -050096 return meta
Torsten Rasmussenbd7569f2019-03-19 10:38:18 +010097
Sebastian Bøebb95dce2019-12-19 15:49:22 +010098 if Path(module_path.joinpath('zephyr/CMakeLists.txt')).is_file() and \
99 Path(module_path.joinpath('zephyr/Kconfig')).is_file():
100 return {'build': {'cmake': 'zephyr', 'kconfig': 'zephyr/Kconfig'}}
101
Anas Nashif286a9ed2019-12-11 10:13:23 -0500102 return None
Torsten Rasmussenbd7569f2019-03-19 10:38:18 +0100103
Torsten Rasmussenbd7569f2019-03-19 10:38:18 +0100104
Anas Nashif286a9ed2019-12-11 10:13:23 -0500105def process_cmake(module, meta):
106 section = meta.get('build', dict())
107 module_path = PurePath(module)
108 module_yml = module_path.joinpath('zephyr/module.yml')
109 cmake_setting = section.get('cmake', None)
110 if not validate_setting(cmake_setting, module, 'CMakeLists.txt'):
111 sys.exit('ERROR: "cmake" key in {} has folder value "{}" which '
112 'does not contain a CMakeLists.txt file.'
113 .format(module_yml.as_posix(), cmake_setting))
114
115 cmake_path = os.path.join(module, cmake_setting or 'zephyr')
116 cmake_file = os.path.join(cmake_path, 'CMakeLists.txt')
117 if os.path.isfile(cmake_file):
118 return('\"{}\":\"{}\"\n'
119 .format(module_path.name, Path(cmake_path).resolve().as_posix()))
120 else:
121 return ""
122
123def process_kconfig(module, meta):
124 section = meta.get('build', dict())
125 module_path = PurePath(module)
126 module_yml = module_path.joinpath('zephyr/module.yml')
127
128 kconfig_setting = section.get('kconfig', None)
129 if not validate_setting(kconfig_setting, module):
130 sys.exit('ERROR: "kconfig" key in {} has value "{}" which does '
131 'not point to a valid Kconfig file.'
132 .format(module_yml, kconfig_setting))
133
134
135 kconfig_file = os.path.join(module, kconfig_setting or 'zephyr/Kconfig')
136 if os.path.isfile(kconfig_file):
137 return 'osource "{}"\n\n'.format(Path(kconfig_file).resolve().as_posix())
138 else:
139 return ""
140
141def process_sanitycheck(module, meta):
142
143 out = ""
144 tests = meta.get('tests', [])
145 samples = meta.get('samples', [])
146 boards = meta.get('boards', [])
147
148 for pth in tests + samples:
149 if pth:
150 dir = os.path.join(module, pth)
151 out += '-T\n{}\n'.format(PurePath(os.path.abspath(dir)).as_posix())
152
153 for pth in boards:
154 if pth:
155 dir = os.path.join(module, pth)
156 out += '--board-root\n{}\n'.format(PurePath(os.path.abspath(dir)).as_posix())
157
158 return out
Torsten Rasmussenbd7569f2019-03-19 10:38:18 +0100159
160
161def main():
Torsten Rasmussenbd7569f2019-03-19 10:38:18 +0100162 parser = argparse.ArgumentParser(description='''
163 Process a list of projects and create Kconfig / CMake include files for
164 projects which are also a Zephyr module''')
165
166 parser.add_argument('--kconfig-out',
Anas Nashif286a9ed2019-12-11 10:13:23 -0500167 help="""File to write with resulting KConfig import
168 statements.""")
169 parser.add_argument('--sanitycheck-out',
170 help="""File to write with resulting sanitycheck parameters.""")
Torsten Rasmussenbd7569f2019-03-19 10:38:18 +0100171 parser.add_argument('--cmake-out',
Anas Nashif286a9ed2019-12-11 10:13:23 -0500172 help="""File to write with resulting <name>:<path>
173 values to use for including in CMake""")
Torsten Rasmussenbd7569f2019-03-19 10:38:18 +0100174 parser.add_argument('-m', '--modules', nargs='+',
Anas Nashif286a9ed2019-12-11 10:13:23 -0500175 help="""List of modules to parse instead of using `west
176 list`""")
Martí Bolívar34346c42020-01-29 12:38:58 -0800177 parser.add_argument('-x', '--extra-modules', nargs='+', default=[],
Torsten Rasmussenbd7569f2019-03-19 10:38:18 +0100178 help='List of extra modules to parse')
Sigvart M. Hovland06365a52019-04-25 12:27:17 +0200179 parser.add_argument('-w', '--west-path', default='west',
180 help='Path to west executable')
Torsten Rasmussenbd7569f2019-03-19 10:38:18 +0100181 args = parser.parse_args()
182
183 if args.modules is None:
Sigvart M. Hovland06365a52019-04-25 12:27:17 +0200184 p = subprocess.Popen([args.west_path, 'list', '--format={posixpath}'],
Torsten Rasmussenbd7569f2019-03-19 10:38:18 +0100185 stdout=subprocess.PIPE,
186 stderr=subprocess.PIPE)
187 out, err = p.communicate()
188 if p.returncode == 0:
189 projects = out.decode(sys.getdefaultencoding()).splitlines()
Piotr Zierhoffera3ddc092019-08-06 12:39:34 +0200190 elif re.match((r'Error: .* is not in a west installation\.'
Torsten Rasmussenb3da9ef2019-12-13 09:42:13 +0100191 '|FATAL ERROR: no west installation found from .*'),
Torsten Rasmussenbd7569f2019-03-19 10:38:18 +0100192 err.decode(sys.getdefaultencoding())):
193 # Only accept the error from bootstrapper in the event we are
194 # outside a west managed project.
195 projects = []
196 else:
Torsten Rasmussen0dd3b422019-04-25 11:03:27 +0200197 print(err.decode(sys.getdefaultencoding()))
Torsten Rasmussenbd7569f2019-03-19 10:38:18 +0100198 # A real error occurred, raise an exception
199 raise subprocess.CalledProcessError(cmd=p.args,
200 returncode=p.returncode)
201 else:
202 projects = args.modules
203
Martí Bolívar34346c42020-01-29 12:38:58 -0800204 projects += args.extra_modules
205 extra_modules = set(args.extra_modules)
Anas Nashif286a9ed2019-12-11 10:13:23 -0500206
207 kconfig = ""
208 cmake = ""
209 sanitycheck = ""
210
211 for project in projects:
212 # Avoid including Zephyr base project as module.
213 if project == os.environ.get('ZEPHYR_BASE'):
214 continue
215
216 meta = process_module(project)
217 if meta:
218 kconfig += process_kconfig(project, meta)
219 cmake += process_cmake(project, meta)
220 sanitycheck += process_sanitycheck(project, meta)
Martí Bolívar34346c42020-01-29 12:38:58 -0800221 elif project in extra_modules:
222 sys.exit(f'{project}, given in ZEPHYR_EXTRA_MODULES, '
223 'is not a valid zephyr module')
Anas Nashif286a9ed2019-12-11 10:13:23 -0500224
Torsten Rasmussenbd7569f2019-03-19 10:38:18 +0100225 if args.kconfig_out:
Anas Nashif286a9ed2019-12-11 10:13:23 -0500226 with open(args.kconfig_out, 'w', encoding="utf-8") as fp:
227 fp.write(kconfig)
Torsten Rasmussenbd7569f2019-03-19 10:38:18 +0100228
229 if args.cmake_out:
Anas Nashif286a9ed2019-12-11 10:13:23 -0500230 with open(args.cmake_out, 'w', encoding="utf-8") as fp:
231 fp.write(cmake)
Torsten Rasmussenbd7569f2019-03-19 10:38:18 +0100232
Anas Nashif286a9ed2019-12-11 10:13:23 -0500233 if args.sanitycheck_out:
234 with open(args.sanitycheck_out, 'w', encoding="utf-8") as fp:
235 fp.write(sanitycheck)
Torsten Rasmussenbd7569f2019-03-19 10:38:18 +0100236
237if __name__ == "__main__":
238 main()