diff --git a/CMakeLists.txt b/CMakeLists.txt index 3e1423e75a7003..151949d0049f84 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -575,16 +575,17 @@ add_subdirectory(ext) add_subdirectory(subsys) add_subdirectory(drivers) -# Add all zephyr modules subdirectories. -if(ZEPHYR_MODULES_NAME) - message("Including module(s): ${ZEPHYR_MODULES_NAME}") +# Include zephyr modules generated CMake file. +if(EXISTS ${CMAKE_BINARY_DIR}/zephyr_modules.txt) + file(STRINGS ${CMAKE_BINARY_DIR}/zephyr_modules.txt ZEPHYR_MODULES_TXT) + + foreach(module ${ZEPHYR_MODULES_TXT}) + string(REGEX REPLACE "(.*):.*" "\\1" module_name ${module}) + string(REGEX REPLACE ".*:(.*)" "\\1" module_path ${module}) + message("Including module: ${module_name}") + add_subdirectory(${module_path} ${CMAKE_BINARY_DIR}/${module_name}) + endforeach() endif() -set(index 0) -foreach(module_name ${ZEPHYR_MODULES_NAME}) - list(GET ZEPHYR_MODULES_DIR ${index} module_dir) - math(EXPR index "${index} + 1") - add_subdirectory(${module_dir} ${CMAKE_BINARY_DIR}/${module_name}) -endforeach() set(syscall_macros_h ${ZEPHYR_BINARY_DIR}/include/generated/syscall_macros.h) diff --git a/CODEOWNERS b/CODEOWNERS index f3ebafaeabc897..6d018f9b49261b 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -253,6 +253,7 @@ /scripts/series-push-hook.sh @erwango /scripts/west_commands/ @mbolivar /scripts/west-commands.yml @mbolivar +/scripts/zephyr_module.py @tejlmand /subsys/bluetooth/ @sjanc @jhedberg @Vudentz /subsys/bluetooth/controller/ @carlescufi @cvinayak @thoh-ot /subsys/fs/ @nashif diff --git a/cmake/zephyr_module.cmake b/cmake/zephyr_module.cmake index c38cfa3964ed1e..172879f3a3c890 100644 --- a/cmake/zephyr_module.cmake +++ b/cmake/zephyr_module.cmake @@ -9,86 +9,30 @@ # search for zephyr/module.yml if(ZEPHYR_MODULES) - set(west_project_list ${ZEPHYR_MODULES}) -elseif(WEST) - ## Use `west list` to fetch all west handled projects. - execute_process( - COMMAND - ${WEST} list --format={posixpath} - OUTPUT_VARIABLE - west_project_output - ERROR_VARIABLE - west_project_error - RESULT_VARIABLE - west_list_result - ) - if(NOT ${west_list_result}) - string(REGEX REPLACE "[\r\n]+" ";" west_project_list "${west_project_output}") - elseif(NOT ("${west_project_error}" MATCHES - "^Error: .* is not in a west installation\..*")) - message(FATAL_ERROR "${west_project_error}") - endif() + set(ZEPHYR_MODULES_ARG "--modules" ${ZEPHYR_MODULES}) endif() if(ZEPHYR_EXTRA_MODULES) - list(APPEND west_project_list ${ZEPHYR_EXTRA_MODULES}) + set(ZEPHYR_EXTRA_MODULES_ARG "--extra-modules" ${ZEPHYR_EXTRA_MODULES}) endif() -# Clear the Kconfig.modules generated file in case modules has been removed. -# The Kconfig.modules contains a list of additional Kconfig files to be sourced -# based upon /zephyr/module.yml files. -set(KCONFIG_MODULES_FILE ${CMAKE_BINARY_DIR}/Kconfig.modules) -file(WRITE ${KCONFIG_MODULES_FILE} - "# NOTE: THIS FILE IS AUTOGENERATED BY CMAKE\n" +if(WEST OR ZEPHYR_MODULES) + # Zephyr module uses west, so only call it if west is installed or + # ZEPHYR_MODULES was provided as argument to CMake. + execute_process( + COMMAND + ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/scripts/zephyr_module.py + ${ZEPHYR_MODULES_ARG} + ${ZEPHYR_EXTRA_MODULES_ARG} + --kconfig-out ${CMAKE_BINARY_DIR}/Kconfig.modules + --cmake-out ${CMAKE_BINARY_DIR}/zephyr_modules.txt + ERROR_VARIABLE + zephyr_module_error_text + RESULT_VARIABLE + zephyr_module_return ) -# For each west managed project, determine if the project is a zephyr module. -foreach(module ${west_project_list}) - set(cmake_subdir "zephyr") - if(${ZEPHYR_BASE} STREQUAL ${module}) - # Ignore Zephyr project to avoid potential invalid looping - elseif(EXISTS "${module}/zephyr/module.yml") - set(kconfig_osource "osource \"${module}/zephyr/Kconfig\"\n") - get_filename_component(module_name ${module} NAME) - execute_process( - COMMAND - ${PYTHON_EXECUTABLE} - ${ZEPHYR_BASE}/scripts/yaml_to_cmake.py - -i "${module}/zephyr/module.yml" - -o "${CMAKE_CURRENT_BINARY_DIR}/zephyr_module_${module_name}.txt" - -s "build" - ) - file( - STRINGS - "${CMAKE_CURRENT_BINARY_DIR}/zephyr_module_${module_name}.txt" - zephyr_module - ) - - foreach(key_value ${zephyr_module}) - if(${key_value} MATCHES "^cmake=") - string(REGEX REPLACE "^cmake=" "" cmake_subdir ${key_value}) - elseif(${key_value} MATCHES "^kconfig=") - string( - REGEX REPLACE - "^kconfig=" - "osource \"${module}/" - kconfig_osource - ${key_value} "\"\n" - ) - endif() - endforeach() - list(APPEND ZEPHYR_MODULES_NAME ${module_name}) - list(APPEND ZEPHYR_MODULES_DIR ${module}/${cmake_subdir}) - file(APPEND ${KCONFIG_MODULES_FILE} ${kconfig_osource}) - elseif(EXISTS "${module}/${cmake_subdir}/CMakeLists.txt") - set(kconfig_osource "osource \"${module}/zephyr/Kconfig\"\n") - get_filename_component(module_name ${module} NAME) - list(APPEND ZEPHYR_MODULES_NAME ${module_name}) - list(APPEND ZEPHYR_MODULES_DIR ${module}/${cmake_subdir}) - file(APPEND ${KCONFIG_MODULES_FILE} ${kconfig_osource}) - else() - # Not a Zephyr module, ignore. + if(${zephyr_module_return}) + message(FATAL_ERROR "${zephyr_module_error_text}") endif() - -endforeach() - +endif() diff --git a/scripts/yaml_to_cmake.py b/scripts/yaml_to_cmake.py deleted file mode 100755 index 6fc461cdfee1e3..00000000000000 --- a/scripts/yaml_to_cmake.py +++ /dev/null @@ -1,67 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (c) 2019, Nordic Semiconductor ASA -# -# SPDX-License-Identifier: Apache-2.0 - -'''Tool for a simple parsing of YAML files and return a ;-list of = -pairs to use within a CMake build file. -''' - -import argparse -import yaml -import pykwalify.core - - -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 -''' - - -def main(): - parser = argparse.ArgumentParser(description=''' - Converts YAML to a CMake list''') - - parser.add_argument('-i', '--input', required=True, - help='YAML file with data') - parser.add_argument('-o', '--output', required=True, - help='File to write with CMake data') - parser.add_argument('-s', '--section', required=True, - help='Section in YAML file to parse') - args = parser.parse_args() - - with open(args.input, 'r') as f: - meta = yaml.safe_load(f.read()) - - pykwalify.core.Core(source_data=meta, - schema_data=yaml.safe_load(METADATA_SCHEMA)).validate() - - val_str = '' - - section = meta.get(args.section) - if section is not None: - for key in section: - val_str += '{}={}\n'.format(key, section[key]) - - with open(args.output, 'w') as f: - f.write(val_str) - - -if __name__ == "__main__": - main() diff --git a/scripts/zephyr_module.py b/scripts/zephyr_module.py new file mode 100755 index 00000000000000..0431227daca036 --- /dev/null +++ b/scripts/zephyr_module.py @@ -0,0 +1,164 @@ +#!/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 : 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 + + +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(os.path.abspath(kconfig_file))) + + +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 :' + '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()