Torsten Rasmussen | bd7569f | 2019-03-19 10:38:18 +0100 | [diff] [blame] | 1 | #!/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 |
| 8 | projects. If no projects are given then the output from `west list` will be |
| 9 | used as project list. |
| 10 | |
| 11 | Include file is generated for Kconfig using --kconfig-out. |
| 12 | A <name>:<path> text file is generated for use with CMake using --cmake-out. |
Anas Nashif | 286a9ed | 2019-12-11 10:13:23 -0500 | [diff] [blame] | 13 | |
| 14 | Using --sanitycheck-out <filename> an argument file for sanitycheck script will |
| 15 | be generated which would point to test and sample roots available in modules |
| 16 | that can be included during a sanitycheck run. This allows testing code |
| 17 | maintained in modules in addition to what is available in the main Zephyr tree. |
Torsten Rasmussen | bd7569f | 2019-03-19 10:38:18 +0100 | [diff] [blame] | 18 | ''' |
| 19 | |
| 20 | import argparse |
| 21 | import os |
| 22 | import sys |
| 23 | import yaml |
| 24 | import pykwalify.core |
| 25 | import subprocess |
| 26 | import re |
Torsten Rasmussen | b3da9ef | 2019-12-13 09:42:13 +0100 | [diff] [blame] | 27 | from pathlib import Path, PurePath |
Torsten Rasmussen | bd7569f | 2019-03-19 10:38:18 +0100 | [diff] [blame] | 28 | |
| 29 | METADATA_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. |
| 35 | type: map |
| 36 | mapping: |
| 37 | build: |
Anas Nashif | 286a9ed | 2019-12-11 10:13:23 -0500 | [diff] [blame] | 38 | required: false |
Torsten Rasmussen | bd7569f | 2019-03-19 10:38:18 +0100 | [diff] [blame] | 39 | type: map |
| 40 | mapping: |
| 41 | cmake: |
| 42 | required: false |
| 43 | type: str |
| 44 | kconfig: |
| 45 | required: false |
| 46 | type: str |
Anas Nashif | 286a9ed | 2019-12-11 10:13:23 -0500 | [diff] [blame] | 47 | 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 Rasmussen | bd7569f | 2019-03-19 10:38:18 +0100 | [diff] [blame] | 62 | ''' |
| 63 | |
| 64 | schema = yaml.safe_load(METADATA_SCHEMA) |
| 65 | |
| 66 | |
| 67 | def 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 Nashif | 286a9ed | 2019-12-11 10:13:23 -0500 | [diff] [blame] | 78 | def process_module(module): |
Torsten Rasmussen | b3da9ef | 2019-12-13 09:42:13 +0100 | [diff] [blame] | 79 | module_path = PurePath(module) |
| 80 | module_yml = module_path.joinpath('zephyr/module.yml') |
Anas Nashif | 286a9ed | 2019-12-11 10:13:23 -0500 | [diff] [blame] | 81 | |
Sebastian Bøe | bb95dce | 2019-12-19 15:49:22 +0100 | [diff] [blame] | 82 | # 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 Rasmussen | b3da9ef | 2019-12-13 09:42:13 +0100 | [diff] [blame] | 85 | if Path(module_yml).is_file(): |
| 86 | with Path(module_yml).open('r') as f: |
Torsten Rasmussen | bd7569f | 2019-03-19 10:38:18 +0100 | [diff] [blame] | 87 | 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 Magnusson | 50b9b12 | 2019-09-07 14:41:01 +0200 | [diff] [blame] | 93 | sys.exit('ERROR: Malformed "build" section in file: {}\n{}' |
Torsten Rasmussen | b3da9ef | 2019-12-13 09:42:13 +0100 | [diff] [blame] | 94 | .format(module_yml.as_posix(), e)) |
Torsten Rasmussen | bd7569f | 2019-03-19 10:38:18 +0100 | [diff] [blame] | 95 | |
Anas Nashif | 286a9ed | 2019-12-11 10:13:23 -0500 | [diff] [blame] | 96 | return meta |
Torsten Rasmussen | bd7569f | 2019-03-19 10:38:18 +0100 | [diff] [blame] | 97 | |
Sebastian Bøe | bb95dce | 2019-12-19 15:49:22 +0100 | [diff] [blame] | 98 | 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 Nashif | 286a9ed | 2019-12-11 10:13:23 -0500 | [diff] [blame] | 102 | return None |
Torsten Rasmussen | bd7569f | 2019-03-19 10:38:18 +0100 | [diff] [blame] | 103 | |
Torsten Rasmussen | bd7569f | 2019-03-19 10:38:18 +0100 | [diff] [blame] | 104 | |
Anas Nashif | 286a9ed | 2019-12-11 10:13:23 -0500 | [diff] [blame] | 105 | def 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 | |
| 123 | def 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 | |
| 141 | def 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 Rasmussen | bd7569f | 2019-03-19 10:38:18 +0100 | [diff] [blame] | 159 | |
| 160 | |
| 161 | def main(): |
Torsten Rasmussen | bd7569f | 2019-03-19 10:38:18 +0100 | [diff] [blame] | 162 | 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 Nashif | 286a9ed | 2019-12-11 10:13:23 -0500 | [diff] [blame] | 167 | 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 Rasmussen | bd7569f | 2019-03-19 10:38:18 +0100 | [diff] [blame] | 171 | parser.add_argument('--cmake-out', |
Anas Nashif | 286a9ed | 2019-12-11 10:13:23 -0500 | [diff] [blame] | 172 | help="""File to write with resulting <name>:<path> |
| 173 | values to use for including in CMake""") |
Torsten Rasmussen | bd7569f | 2019-03-19 10:38:18 +0100 | [diff] [blame] | 174 | parser.add_argument('-m', '--modules', nargs='+', |
Anas Nashif | 286a9ed | 2019-12-11 10:13:23 -0500 | [diff] [blame] | 175 | help="""List of modules to parse instead of using `west |
| 176 | list`""") |
Martí Bolívar | 34346c4 | 2020-01-29 12:38:58 -0800 | [diff] [blame] | 177 | parser.add_argument('-x', '--extra-modules', nargs='+', default=[], |
Torsten Rasmussen | bd7569f | 2019-03-19 10:38:18 +0100 | [diff] [blame] | 178 | help='List of extra modules to parse') |
Sigvart M. Hovland | 06365a5 | 2019-04-25 12:27:17 +0200 | [diff] [blame] | 179 | parser.add_argument('-w', '--west-path', default='west', |
| 180 | help='Path to west executable') |
Torsten Rasmussen | bd7569f | 2019-03-19 10:38:18 +0100 | [diff] [blame] | 181 | args = parser.parse_args() |
| 182 | |
| 183 | if args.modules is None: |
Sigvart M. Hovland | 06365a5 | 2019-04-25 12:27:17 +0200 | [diff] [blame] | 184 | p = subprocess.Popen([args.west_path, 'list', '--format={posixpath}'], |
Torsten Rasmussen | bd7569f | 2019-03-19 10:38:18 +0100 | [diff] [blame] | 185 | 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 Zierhoffer | a3ddc09 | 2019-08-06 12:39:34 +0200 | [diff] [blame] | 190 | elif re.match((r'Error: .* is not in a west installation\.' |
Torsten Rasmussen | b3da9ef | 2019-12-13 09:42:13 +0100 | [diff] [blame] | 191 | '|FATAL ERROR: no west installation found from .*'), |
Torsten Rasmussen | bd7569f | 2019-03-19 10:38:18 +0100 | [diff] [blame] | 192 | 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 Rasmussen | 0dd3b42 | 2019-04-25 11:03:27 +0200 | [diff] [blame] | 197 | print(err.decode(sys.getdefaultencoding())) |
Torsten Rasmussen | bd7569f | 2019-03-19 10:38:18 +0100 | [diff] [blame] | 198 | # 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ívar | 34346c4 | 2020-01-29 12:38:58 -0800 | [diff] [blame] | 204 | projects += args.extra_modules |
| 205 | extra_modules = set(args.extra_modules) |
Anas Nashif | 286a9ed | 2019-12-11 10:13:23 -0500 | [diff] [blame] | 206 | |
| 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ívar | 34346c4 | 2020-01-29 12:38:58 -0800 | [diff] [blame] | 221 | elif project in extra_modules: |
| 222 | sys.exit(f'{project}, given in ZEPHYR_EXTRA_MODULES, ' |
| 223 | 'is not a valid zephyr module') |
Anas Nashif | 286a9ed | 2019-12-11 10:13:23 -0500 | [diff] [blame] | 224 | |
Torsten Rasmussen | bd7569f | 2019-03-19 10:38:18 +0100 | [diff] [blame] | 225 | if args.kconfig_out: |
Anas Nashif | 286a9ed | 2019-12-11 10:13:23 -0500 | [diff] [blame] | 226 | with open(args.kconfig_out, 'w', encoding="utf-8") as fp: |
| 227 | fp.write(kconfig) |
Torsten Rasmussen | bd7569f | 2019-03-19 10:38:18 +0100 | [diff] [blame] | 228 | |
| 229 | if args.cmake_out: |
Anas Nashif | 286a9ed | 2019-12-11 10:13:23 -0500 | [diff] [blame] | 230 | with open(args.cmake_out, 'w', encoding="utf-8") as fp: |
| 231 | fp.write(cmake) |
Torsten Rasmussen | bd7569f | 2019-03-19 10:38:18 +0100 | [diff] [blame] | 232 | |
Anas Nashif | 286a9ed | 2019-12-11 10:13:23 -0500 | [diff] [blame] | 233 | if args.sanitycheck_out: |
| 234 | with open(args.sanitycheck_out, 'w', encoding="utf-8") as fp: |
| 235 | fp.write(sanitycheck) |
Torsten Rasmussen | bd7569f | 2019-03-19 10:38:18 +0100 | [diff] [blame] | 236 | |
| 237 | if __name__ == "__main__": |
| 238 | main() |