1# SPDX-License-Identifier: Apache-2.0
2
3include_guard(GLOBAL)
4
5include(extensions)
6include(python)
7
8# This cmake file provides functionality to import CMakeLists.txt and Kconfig
9# files for Zephyr modules into Zephyr build system.
10#
11# CMakeLists.txt and Kconfig files can reside directly in the Zephyr module or
12# in a MODULE_EXT_ROOT.
13# The `<module>/zephyr/module.yml` file specifies whether the build files are
14# located in the Zephyr module or in a MODULE_EXT_ROOT.
15#
16# A list of Zephyr modules can be provided to the build system using:
17#   -DZEPHYR_MODULES=<module-path>[;<additional-module(s)-path>]
18#
19# It looks for: <module>/zephyr/module.yml or
20#               <module>/zephyr/CMakeLists.txt
21# to load the Zephyr module into Zephyr build system.
22# If west is installed, it uses west's APIs to obtain a list of projects to
23# search for zephyr/module.yml from the current workspace's manifest.
24#
25# If the module.yml file specifies that build files are located in a
26# MODULE_EXT_ROOT then the variables:
27# - `ZEPHYR_<MODULE_NAME>_CMAKE_DIR` is used for inclusion of the CMakeLists.txt
28# - `ZEPHYR_<MODULE_NAME>_KCONFIG` is used for inclusion of the Kconfig
29# files into the build system.
30
31# Settings used by Zephyr module but where systems may define an alternative value.
32set_ifndef(KCONFIG_BINARY_DIR ${CMAKE_BINARY_DIR}/Kconfig)
33
34zephyr_get(ZEPHYR_MODULES)
35if(ZEPHYR_MODULES)
36  set(ZEPHYR_MODULES_ARG "--modules" ${ZEPHYR_MODULES})
37endif()
38
39zephyr_get(EXTRA_ZEPHYR_MODULES VAR EXTRA_ZEPHYR_MODULES ZEPHYR_EXTRA_MODULES)
40if(EXTRA_ZEPHYR_MODULES)
41  set(EXTRA_ZEPHYR_MODULES_ARG "--extra-modules" ${EXTRA_ZEPHYR_MODULES})
42endif()
43
44file(MAKE_DIRECTORY ${KCONFIG_BINARY_DIR})
45set(kconfig_modules_file ${KCONFIG_BINARY_DIR}/Kconfig.modules)
46set(kconfig_sysbuild_file ${KCONFIG_BINARY_DIR}/Kconfig.sysbuild.modules)
47set(cmake_modules_file ${CMAKE_BINARY_DIR}/zephyr_modules.txt)
48set(cmake_sysbuild_file ${CMAKE_BINARY_DIR}/sysbuild_modules.txt)
49set(zephyr_settings_file ${CMAKE_BINARY_DIR}/zephyr_settings.txt)
50
51if(WEST OR ZEPHYR_MODULES)
52  # Zephyr module uses west, so only call it if west is installed or
53  # ZEPHYR_MODULES was provided as argument to CMake.
54  execute_process(
55    COMMAND
56    ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/scripts/zephyr_module.py
57    --zephyr-base=${ZEPHYR_BASE}
58    ${ZEPHYR_MODULES_ARG}
59    ${EXTRA_ZEPHYR_MODULES_ARG}
60    --kconfig-out ${kconfig_modules_file}
61    --cmake-out ${cmake_modules_file}
62    --sysbuild-kconfig-out ${kconfig_sysbuild_file}
63    --sysbuild-cmake-out ${cmake_sysbuild_file}
64    --settings-out ${zephyr_settings_file}
65    WORKING_DIRECTORY ${ZEPHYR_BASE}
66    ERROR_VARIABLE
67    zephyr_module_error_text
68    RESULT_VARIABLE
69    zephyr_module_return
70  )
71
72  if(${zephyr_module_return})
73      message(FATAL_ERROR "${zephyr_module_error_text}")
74  endif()
75
76  if(EXISTS ${zephyr_settings_file})
77    file(STRINGS ${zephyr_settings_file} zephyr_settings_txt ENCODING UTF-8 REGEX "^[^#]")
78    foreach(setting ${zephyr_settings_txt})
79      # Match <key>:<value> for each line of file, each corresponding to
80      # a setting.  The use of quotes is required due to CMake not supporting
81      # lazy regexes (it supports greedy only).
82      string(REGEX REPLACE "\"(.*)\":\".*\"" "\\1" key ${setting})
83      string(REGEX REPLACE "\".*\":\"(.*)\"" "\\1" value ${setting})
84      list(APPEND ${key} ${value})
85    endforeach()
86  endif()
87
88  # Append ZEPHYR_BASE as a default ext root at lowest priority
89  list(APPEND MODULE_EXT_ROOT ${ZEPHYR_BASE})
90
91  if(EXISTS ${cmake_modules_file})
92    file(STRINGS ${cmake_modules_file} zephyr_modules_txt ENCODING UTF-8)
93  endif()
94
95  set(ZEPHYR_MODULE_NAMES)
96  foreach(module ${zephyr_modules_txt})
97    # Match "<name>":"<path>" for each line of file, each corresponding to
98    # one module. The use of quotes is required due to CMake not supporting
99    # lazy regexes (it supports greedy only).
100    string(REGEX REPLACE "\"(.*)\":\".*\":\".*\"" "\\1" module_name ${module})
101    list(APPEND ZEPHYR_MODULE_NAMES ${module_name})
102  endforeach()
103
104  if(EXISTS ${cmake_sysbuild_file})
105    file(STRINGS ${cmake_sysbuild_file} sysbuild_modules_txt ENCODING UTF-8)
106  endif()
107
108  set(SYSBUILD_MODULE_NAMES)
109  foreach(module ${sysbuild_modules_txt})
110    # Match "<name>":"<path>" for each line of file, each corresponding to
111    # one module. The use of quotes is required due to CMake not supporting
112    # lazy regexes (it supports greedy only).
113    string(REGEX REPLACE "\"(.*)\":\".*\":\".*\"" "\\1" module_name ${module})
114    list(APPEND SYSBUILD_MODULE_NAMES ${module_name})
115  endforeach()
116
117  # MODULE_EXT_ROOT is process order which means Zephyr module roots processed
118  # later wins. therefore we reverse the list before processing.
119  list(REVERSE MODULE_EXT_ROOT)
120  foreach(root ${MODULE_EXT_ROOT})
121    set(module_cmake_file_path modules/modules.cmake)
122    if(NOT EXISTS ${root}/${module_cmake_file_path})
123      message(FATAL_ERROR "No `${module_cmake_file_path}` found in module root `${root}`.")
124    endif()
125
126    include(${root}/${module_cmake_file_path})
127  endforeach()
128
129  foreach(module ${zephyr_modules_txt})
130    # Match "<name>":"<path>" for each line of file, each corresponding to
131    # one Zephyr module. The use of quotes is required due to CMake not
132    # supporting lazy regexes (it supports greedy only).
133    string(CONFIGURE ${module} module)
134    string(REGEX REPLACE "\"(.*)\":\".*\":\".*\"" "\\1" module_name ${module})
135    string(REGEX REPLACE "\".*\":\"(.*)\":\".*\"" "\\1" module_path ${module})
136    string(REGEX REPLACE "\".*\":\".*\":\"(.*)\"" "\\1" cmake_path ${module})
137
138    zephyr_string(SANITIZE TOUPPER MODULE_NAME_UPPER ${module_name})
139    if(NOT ${MODULE_NAME_UPPER} STREQUAL CURRENT)
140      set(ZEPHYR_${MODULE_NAME_UPPER}_MODULE_NAME ${module_name})
141      set(ZEPHYR_${MODULE_NAME_UPPER}_MODULE_DIR ${module_path})
142      set(ZEPHYR_${MODULE_NAME_UPPER}_CMAKE_DIR ${cmake_path})
143    else()
144      message(FATAL_ERROR "Found Zephyr module named: ${module_name}\n\
145${MODULE_NAME_UPPER} is a restricted name for Zephyr modules as it is used for \
146\${ZEPHYR_${MODULE_NAME_UPPER}_MODULE_DIR} CMake variable.")
147    endif()
148  endforeach()
149
150  foreach(module ${sysbuild_modules_txt})
151    # Match "<name>":"<path>" for each line of file, each corresponding to
152    # one Zephyr module. The use of quotes is required due to CMake not
153    # supporting lazy regexes (it supports greedy only).
154    string(CONFIGURE ${module} module)
155    string(REGEX REPLACE "\"(.*)\":\".*\":\".*\"" "\\1" module_name ${module})
156    string(REGEX REPLACE "\".*\":\"(.*)\":\".*\"" "\\1" module_path ${module})
157    string(REGEX REPLACE "\".*\":\".*\":\"(.*)\"" "\\1" cmake_path ${module})
158
159    zephyr_string(SANITIZE TOUPPER MODULE_NAME_UPPER ${module_name})
160    if(NOT ${MODULE_NAME_UPPER} STREQUAL CURRENT)
161      set(SYSBUILD_${MODULE_NAME_UPPER}_MODULE_DIR ${module_path})
162      set(SYSBUILD_${MODULE_NAME_UPPER}_CMAKE_DIR ${cmake_path})
163    else()
164      message(FATAL_ERROR "Found Zephyr module named: ${module_name}\n\
165${MODULE_NAME_UPPER} is a restricted name for Zephyr modules as it is used for \
166\${SYSBUILD_${MODULE_NAME_UPPER}_MODULE_DIR} CMake variable.")
167    endif()
168  endforeach()
169else()
170
171  file(WRITE ${kconfig_modules_file}
172    "# No west and no Zephyr modules\n"
173    )
174
175  file(WRITE ${kconfig_sysbuild_file}
176    "# No west and no Zephyr modules\n"
177    )
178
179endif()
180