1# SPDX-License-Identifier: Apache-2.0
2
3include_guard(GLOBAL)
4
5include(extensions)
6include(python)
7
8# autoconf.h is generated by Kconfig and placed in
9# <build>/zephyr/include/generated/autoconf.h.
10# A project may request a custom location by setting AUTOCONF_H explicitly before
11# calling 'find_package(Zephyr)' or loading this module.
12set_ifndef(AUTOCONF_H ${PROJECT_BINARY_DIR}/include/generated/zephyr/autoconf.h)
13# Re-configure (Re-execute all CMakeLists.txt code) when autoconf.h changes
14set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${AUTOCONF_H})
15
16# Folders needed for conf/mconf files (kconfig has no method of redirecting all output files).
17# conf/mconf needs to be run from a different directory because of: GH-3408
18file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/kconfig/include/generated)
19file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/kconfig/include/config)
20
21set_ifndef(KCONFIG_NAMESPACE "CONFIG")
22
23set_ifndef(KCONFIG_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/Kconfig)
24set(KCONFIG_BOARD_DIR ${KCONFIG_BINARY_DIR}/boards)
25file(MAKE_DIRECTORY ${KCONFIG_BINARY_DIR})
26
27# Support multiple shields in BOARD_ROOT, remove ZEPHYR_BASE as that is always sourced.
28set(kconfig_board_root ${BOARD_ROOT})
29list(REMOVE_ITEM kconfig_board_root ${ZEPHYR_BASE})
30set(OPERATION WRITE)
31foreach(root ${kconfig_board_root})
32  file(${OPERATION} ${KCONFIG_BINARY_DIR}/Kconfig.shield.defconfig
33       "osource \"${root}/boards/shields/*/Kconfig.defconfig\"\n"
34  )
35  file(${OPERATION} ${KCONFIG_BINARY_DIR}/Kconfig.shield
36       "osource \"${root}/boards/shields/*/Kconfig.shield\"\n"
37  )
38  set(OPERATION APPEND)
39endforeach()
40
41if(KCONFIG_ROOT)
42  # Perform any variable substitutions if they are present
43  string(CONFIGURE "${KCONFIG_ROOT}" KCONFIG_ROOT)
44
45  zephyr_file(APPLICATION_ROOT KCONFIG_ROOT)
46  # KCONFIG_ROOT has either been specified as a CMake variable or is
47  # already in the CMakeCache.txt. This has precedence.
48elseif(EXISTS   ${APPLICATION_SOURCE_DIR}/Kconfig)
49  set(KCONFIG_ROOT ${APPLICATION_SOURCE_DIR}/Kconfig)
50else()
51  set(KCONFIG_ROOT ${ZEPHYR_BASE}/Kconfig)
52endif()
53
54if(NOT DEFINED BOARD_DEFCONFIG)
55  zephyr_file(CONF_FILES ${BOARD_DIRECTORIES} DEFCONFIG BOARD_DEFCONFIG)
56endif()
57
58if(DEFINED BOARD_REVISION)
59  zephyr_build_string(config_board_string
60                      BOARD ${BOARD}
61                      BOARD_QUALIFIERS ${BOARD_QUALIFIERS}
62                      BOARD_REVISION ${BOARD_REVISION}
63  )
64  set(board_rev_file ${config_board_string})
65  if(EXISTS ${BOARD_DIR}/${board_rev_file}.conf)
66    message(DEPRECATION "Use of '${board_rev_file}.conf' is deprecated, please switch to '${board_rev_file}_defconfig'")
67    set_ifndef(BOARD_REVISION_CONFIG ${BOARD_DIR}/${board_rev_file}.conf)
68  endif()
69
70  # Generate boolean board revision kconfig option
71  zephyr_string(SANITIZE TOUPPER BOARD_REVISION_GEN_CONFIG_VAR "BOARD_REVISION_${BOARD_REVISION}")
72
73  file(APPEND ${KCONFIG_BOARD_DIR}/Kconfig
74       "config ${BOARD_REVISION_GEN_CONFIG_VAR}\n"
75       "\tdef_bool y\n"
76  )
77endif()
78
79set(DOTCONFIG                  ${PROJECT_BINARY_DIR}/.config)
80set(PARSED_KCONFIG_SOURCES_TXT ${PROJECT_BINARY_DIR}/kconfig/sources.txt)
81
82if(CONF_FILE)
83  string(CONFIGURE "${CONF_FILE}" CONF_FILE_EXPANDED)
84  string(REPLACE " " ";" CONF_FILE_AS_LIST "${CONF_FILE_EXPANDED}")
85  build_info(kconfig user-files PATH ${CONF_FILE_AS_LIST})
86endif()
87
88if(EXTRA_CONF_FILE)
89  string(CONFIGURE "${EXTRA_CONF_FILE}" EXTRA_CONF_FILE_EXPANDED)
90  string(REPLACE " " ";" EXTRA_CONF_FILE_AS_LIST "${EXTRA_CONF_FILE_EXPANDED}")
91  build_info(kconfig extra-user-files PATH ${EXTRA_CONF_FILE_AS_LIST})
92endif()
93
94zephyr_file(CONF_FILES ${BOARD_EXTENSION_DIRS} KCONF board_extension_conf_files SUFFIX ${FILE_SUFFIX})
95
96# DTS_ROOT_BINDINGS is a semicolon separated list, this causes
97# problems when invoking kconfig_target since semicolon is a special
98# character in the C shell, so we make it into a question-mark
99# separated list instead.
100string(REPLACE ";" "?" DTS_ROOT_BINDINGS "${DTS_ROOT_BINDINGS}")
101
102# Export each `ZEPHYR_<module>_KCONFIG` to Kconfig.
103foreach(module_name ${ZEPHYR_MODULE_NAMES})
104  zephyr_string(SANITIZE TOUPPER MODULE_NAME_UPPER ${module_name})
105
106  if(ZEPHYR_${MODULE_NAME_UPPER}_KCONFIG)
107    list(APPEND
108         ZEPHYR_KCONFIG_MODULES_DIR
109         "ZEPHYR_${MODULE_NAME_UPPER}_KCONFIG=${ZEPHYR_${MODULE_NAME_UPPER}_KCONFIG}"
110  )
111  endif()
112endforeach()
113
114# A list of common environment settings used when invoking Kconfig during CMake
115# configure time or menuconfig and related build target.
116string(REPLACE ";" "\\\;" SHIELD_AS_LIST_ESCAPED "${SHIELD_AS_LIST}")
117# cmake commands are escaped differently
118string(REPLACE ";" "\\;" SHIELD_AS_LIST_ESCAPED_COMMAND "${SHIELD_AS_LIST}")
119
120if(TOOLCHAIN_HAS_NEWLIB)
121  set(_local_TOOLCHAIN_HAS_NEWLIB y)
122else()
123  set(_local_TOOLCHAIN_HAS_NEWLIB n)
124endif()
125
126if(TOOLCHAIN_HAS_PICOLIBC)
127  set(_local_TOOLCHAIN_HAS_PICOLIBC y)
128else()
129  set(_local_TOOLCHAIN_HAS_PICOLIBC n)
130endif()
131
132# APP_DIR: Path to the main image (sysbuild) or synonym for APPLICATION_SOURCE_DIR (non-sysbuild)
133zephyr_get(APP_DIR VAR APP_DIR APPLICATION_SOURCE_DIR)
134
135set(COMMON_KCONFIG_ENV_SETTINGS
136  PYTHON_EXECUTABLE=${PYTHON_EXECUTABLE}
137  srctree=${ZEPHYR_BASE}
138  KERNELVERSION=${KERNELVERSION}
139  APPVERSION=${APP_VERSION_STRING}
140  APP_VERSION_EXTENDED_STRING=${APP_VERSION_EXTENDED_STRING}
141  APP_VERSION_TWEAK_STRING=${APP_VERSION_TWEAK_STRING}
142  APP_DIR=${APP_DIR}
143  CONFIG_=${KCONFIG_NAMESPACE}_
144  KCONFIG_CONFIG=${DOTCONFIG}
145  KCONFIG_BOARD_DIR=${KCONFIG_BOARD_DIR}
146  BOARD=${BOARD}
147  BOARD_REVISION=${BOARD_REVISION}
148  BOARD_QUALIFIERS=${BOARD_QUALIFIERS}
149  HWM_SCHEME=${HWM}
150  KCONFIG_BINARY_DIR=${KCONFIG_BINARY_DIR}
151  APPLICATION_SOURCE_DIR=${APPLICATION_SOURCE_DIR}
152  ZEPHYR_TOOLCHAIN_VARIANT=${ZEPHYR_TOOLCHAIN_VARIANT}
153  TOOLCHAIN_KCONFIG_DIR=${TOOLCHAIN_KCONFIG_DIR}
154  TOOLCHAIN_HAS_NEWLIB=${_local_TOOLCHAIN_HAS_NEWLIB}
155  TOOLCHAIN_HAS_PICOLIBC=${_local_TOOLCHAIN_HAS_PICOLIBC}
156  EDT_PICKLE=${EDT_PICKLE}
157  # Export all Zephyr modules to Kconfig
158  ${ZEPHYR_KCONFIG_MODULES_DIR}
159)
160
161# For HWMv2 we should in future generate a Kconfig.arch.v2 which instead
162# glob-sources all arch roots, but for Zephyr itself, the current approach is
163# sufficient.
164list(APPEND COMMON_KCONFIG_ENV_SETTINGS
165  ARCH=*
166  ARCH_DIR=${ZEPHYR_BASE}/arch
167)
168
169# Allow out-of-tree users to add their own Kconfig python frontend
170# targets by appending targets to the CMake list
171# 'EXTRA_KCONFIG_TARGETS' and setting variables named
172# 'EXTRA_KCONFIG_TARGET_COMMAND_FOR_<target>'
173#
174# e.g.
175# cmake -DEXTRA_KCONFIG_TARGETS=cli
176# -DEXTRA_KCONFIG_TARGET_COMMAND_FOR_cli=cli_kconfig_frontend.py
177
178set(EXTRA_KCONFIG_TARGET_COMMAND_FOR_menuconfig
179  ${ZEPHYR_BASE}/scripts/kconfig/menuconfig.py
180  )
181
182set(EXTRA_KCONFIG_TARGET_COMMAND_FOR_guiconfig
183  ${ZEPHYR_BASE}/scripts/kconfig/guiconfig.py
184  )
185
186set(EXTRA_KCONFIG_TARGET_COMMAND_FOR_hardenconfig
187  ${ZEPHYR_BASE}/scripts/kconfig/hardenconfig.py
188  )
189
190set_ifndef(KCONFIG_TARGETS menuconfig guiconfig hardenconfig)
191
192foreach(kconfig_target
193    ${KCONFIG_TARGETS}
194    ${EXTRA_KCONFIG_TARGETS}
195    )
196  add_custom_target(
197    ${kconfig_target}
198    ${CMAKE_COMMAND} -E env
199    ZEPHYR_BASE=${ZEPHYR_BASE}
200    ${COMMON_KCONFIG_ENV_SETTINGS}
201    "SHIELD_AS_LIST=${SHIELD_AS_LIST_ESCAPED}"
202    DTS_POST_CPP=${DTS_POST_CPP}
203    DTS_ROOT_BINDINGS=${DTS_ROOT_BINDINGS}
204    ${PTY_INTERFACE}
205    ${PYTHON_EXECUTABLE}
206    ${EXTRA_KCONFIG_TARGET_COMMAND_FOR_${kconfig_target}}
207    ${KCONFIG_ROOT}
208    WORKING_DIRECTORY ${PROJECT_BINARY_DIR}/kconfig
209    USES_TERMINAL
210    COMMAND_EXPAND_LISTS
211    )
212endforeach()
213
214# Support assigning Kconfig symbols on the command-line with CMake
215# cache variables prefixed according to the Kconfig namespace.
216# This feature is experimental and undocumented until it has undergone more
217# user-testing.
218unset(EXTRA_KCONFIG_OPTIONS)
219if(SYSBUILD)
220  get_property(sysbuild_variable_names TARGET sysbuild_cache PROPERTY "SYSBUILD_CACHE:VARIABLES")
221  zephyr_get(SYSBUILD_MAIN_APP)
222  zephyr_get(SYSBUILD_NAME)
223
224  foreach (name ${sysbuild_variable_names})
225    if("${name}" MATCHES "^${SYSBUILD_NAME}_${KCONFIG_NAMESPACE}_")
226      string(REGEX REPLACE "^${SYSBUILD_NAME}_" "" org_name ${name})
227      get_property(${org_name} TARGET sysbuild_cache PROPERTY ${name})
228      list(APPEND cache_variable_names ${org_name})
229    elseif(SYSBUILD_MAIN_APP AND "${name}" MATCHES "^${KCONFIG_NAMESPACE}_")
230      get_property(${name} TARGET sysbuild_cache PROPERTY ${name})
231      list(APPEND cache_variable_names ${name})
232    endif()
233  endforeach()
234else()
235  get_cmake_property(cache_variable_names CACHE_VARIABLES)
236  list(FILTER cache_variable_names INCLUDE REGEX "^(CLI_)?${KCONFIG_NAMESPACE}_")
237  list(TRANSFORM cache_variable_names REPLACE "^CLI_" "")
238  list(REMOVE_DUPLICATES cache_variable_names)
239endif()
240
241# Sorting the variable names will make checksum calculation more stable.
242list(SORT cache_variable_names)
243foreach (name ${cache_variable_names})
244  if(DEFINED ${name})
245    # When a cache variable starts with the 'KCONFIG_NAMESPACE' value, it is
246    # assumed to be a Kconfig symbol assignment from the CMake command line.
247    set(EXTRA_KCONFIG_OPTIONS
248      "${EXTRA_KCONFIG_OPTIONS}\n${name}=${${name}}"
249      )
250    set(CLI_${name} "${${name}}")
251    list(APPEND cli_config_list ${name})
252  elseif(DEFINED CLI_${name})
253    # An additional 'CLI_' prefix means that the value was set by the user in
254    # an earlier invocation. Append it to extra config only if no new value was
255    # assigned above.
256    set(EXTRA_KCONFIG_OPTIONS
257      "${EXTRA_KCONFIG_OPTIONS}\n${name}=${CLI_${name}}"
258      )
259  endif()
260endforeach()
261
262if(EXTRA_KCONFIG_OPTIONS)
263  set(EXTRA_KCONFIG_OPTIONS_FILE ${PROJECT_BINARY_DIR}/misc/generated/extra_kconfig_options.conf)
264  file(WRITE
265    ${EXTRA_KCONFIG_OPTIONS_FILE}
266    ${EXTRA_KCONFIG_OPTIONS}
267    )
268endif()
269
270# Bring in extra configuration files dropped in by the user or anyone else;
271# make sure they are set at the end so we can override any other setting
272file(GLOB config_files ${APPLICATION_BINARY_DIR}/*.conf)
273list(SORT config_files)
274set(
275  merge_config_files
276  ${BOARD_DEFCONFIG}
277  ${BOARD_REVISION_CONFIG}
278  ${board_extension_conf_files}
279  ${CONF_FILE_AS_LIST}
280  ${shield_conf_files}
281  ${EXTRA_CONF_FILE_AS_LIST}
282  ${EXTRA_KCONFIG_OPTIONS_FILE}
283  ${config_files}
284)
285
286# Create a list of absolute paths to the .config sources from
287# merge_config_files, which is a mix of absolute and relative paths.
288set(merge_config_files_with_absolute_paths "")
289foreach(f ${merge_config_files})
290  if(IS_ABSOLUTE ${f})
291    set(path ${f})
292  else()
293    set(path ${APPLICATION_CONFIG_DIR}/${f})
294  endif()
295
296  list(APPEND merge_config_files_with_absolute_paths ${path})
297endforeach()
298set(merge_config_files ${merge_config_files_with_absolute_paths})
299
300foreach(f ${merge_config_files})
301  if(NOT EXISTS ${f} OR IS_DIRECTORY ${f})
302    message(FATAL_ERROR "File not found: ${f}")
303  endif()
304endforeach()
305
306# Calculate a checksum of merge_config_files to determine if we need
307# to re-generate .config
308set(merge_config_files_checksum "")
309foreach(f ${merge_config_files})
310  file(MD5 ${f} checksum)
311  set(merge_config_files_checksum "${merge_config_files_checksum}${checksum}")
312endforeach()
313
314# Add to the checksum all the Kconfig files which were used last time
315set(merge_kconfig_checksum "")
316if(EXISTS ${PARSED_KCONFIG_SOURCES_TXT})
317  file(STRINGS ${PARSED_KCONFIG_SOURCES_TXT} parsed_kconfig_sources_list ENCODING UTF-8)
318  foreach(f ${parsed_kconfig_sources_list})
319    if(EXISTS ${f})
320      file(MD5 ${f} checksum)
321      set(merge_kconfig_checksum "${merge_kconfig_checksum}${checksum}")
322    endif()
323  endforeach()
324endif()
325
326# Create a new .config if it does not exists, or if the checksum of
327# the dependencies has changed
328set(merge_config_files_checksum_file ${PROJECT_BINARY_DIR}/.cmake.dotconfig.checksum)
329set(CREATE_NEW_DOTCONFIG 1)
330# Check if the checksum file exists too before trying to open it, though it
331# should under normal circumstances
332if(EXISTS ${DOTCONFIG} AND EXISTS ${merge_config_files_checksum_file})
333  # Read out what the checksum was previously
334  file(READ
335    ${merge_config_files_checksum_file}
336    merge_config_files_checksum_prev
337    )
338  if(
339      ${merge_config_files_checksum}${merge_kconfig_checksum} STREQUAL
340      ${merge_config_files_checksum_prev}
341      )
342    # Checksum is the same as before
343    set(CREATE_NEW_DOTCONFIG 0)
344  endif()
345endif()
346
347if(CREATE_NEW_DOTCONFIG)
348  set(input_configs_flags --handwritten-input-configs)
349  set(input_configs ${merge_config_files} ${FORCED_CONF_FILE})
350  build_info(kconfig files PATH ${input_configs})
351else()
352  set(input_configs ${DOTCONFIG} ${FORCED_CONF_FILE})
353endif()
354
355if(DEFINED FORCED_CONF_FILE)
356  list(APPEND input_configs_flags --forced-input-configs)
357endif()
358
359cmake_path(GET AUTOCONF_H PARENT_PATH autoconf_h_path)
360if(NOT EXISTS ${autoconf_h_path})
361  file(MAKE_DIRECTORY ${autoconf_h_path})
362endif()
363
364execute_process(
365  COMMAND ${CMAKE_COMMAND} -E env
366  ${COMMON_KCONFIG_ENV_SETTINGS}
367  SHIELD_AS_LIST=${SHIELD_AS_LIST_ESCAPED_COMMAND}
368  ${PYTHON_EXECUTABLE}
369  ${ZEPHYR_BASE}/scripts/kconfig/kconfig.py
370  --zephyr-base=${ZEPHYR_BASE}
371  ${input_configs_flags}
372  ${KCONFIG_ROOT}
373  ${DOTCONFIG}
374  ${AUTOCONF_H}
375  ${PARSED_KCONFIG_SOURCES_TXT}
376  ${input_configs}
377  WORKING_DIRECTORY ${APPLICATION_SOURCE_DIR}
378  # The working directory is set to the app dir such that the user
379  # can use relative paths in CONF_FILE, e.g. CONF_FILE=nrf5.conf
380  RESULT_VARIABLE ret
381  )
382if(NOT "${ret}" STREQUAL "0")
383  message(FATAL_ERROR "command failed with return code: ${ret}")
384endif()
385
386# Read out the list of 'Kconfig' sources that were used by the engine.
387file(STRINGS ${PARSED_KCONFIG_SOURCES_TXT} parsed_kconfig_sources_list ENCODING UTF-8)
388
389# Recalculate the Kconfig files' checksum, since the list of files may have
390# changed.
391set(merge_kconfig_checksum "")
392foreach(f ${parsed_kconfig_sources_list})
393  file(MD5 ${f} checksum)
394  set(merge_kconfig_checksum "${merge_kconfig_checksum}${checksum}")
395endforeach()
396
397# Force CMAKE configure when the Kconfig sources or configuration files changes.
398foreach(kconfig_input
399    ${merge_config_files}
400    ${DOTCONFIG}
401    ${parsed_kconfig_sources_list}
402    )
403  set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${kconfig_input})
404endforeach()
405
406if(CREATE_NEW_DOTCONFIG)
407  # Write the new configuration fragment checksum. Only do this if kconfig.py
408  # succeeds, to avoid marking zephyr/.config as up-to-date when it hasn't been
409  # regenerated.
410  file(WRITE ${merge_config_files_checksum_file}
411             ${merge_config_files_checksum}${merge_kconfig_checksum})
412endif()
413
414add_custom_target(config-twister DEPENDS ${DOTCONFIG})
415
416# Remove the CLI Kconfig symbols from the namespace and
417# CMakeCache.txt. If the symbols end up in DOTCONFIG they will be
418# re-introduced to the namespace through 'import_kconfig'.
419foreach (name ${cli_config_list})
420  unset(${name})
421  unset(${name} CACHE)
422endforeach()
423
424# Before importing the symbol values from DOTCONFIG, process the CLI values by
425# re-importing them from EXTRA_KCONFIG_OPTIONS_FILE. Later, we want to compare
426# the values from both files, and 'import_kconfig' will make this easier.
427if(EXTRA_KCONFIG_OPTIONS_FILE)
428  import_kconfig(${KCONFIG_NAMESPACE} ${EXTRA_KCONFIG_OPTIONS_FILE})
429  foreach (name ${cache_variable_names})
430    if(DEFINED ${name})
431      set(temp_${name} "${${name}}")
432      unset(${name})
433    endif()
434  endforeach()
435endif()
436
437# Import the .config file and make all settings available in CMake processing.
438import_kconfig(${KCONFIG_NAMESPACE} ${DOTCONFIG})
439
440# Cache the CLI Kconfig symbols that survived through Kconfig, prefixed with CLI_.
441# Remove those who might have changed compared to earlier runs, if they no longer appears.
442foreach (name ${cache_variable_names})
443  # Note: "${CLI_${name}}" is the verbatim value of ${name} from command-line,
444  # while "${temp_${name}}" is the same value processed by 'import_kconfig'.
445  if(((NOT DEFINED ${name}) AND (NOT DEFINED temp_${name})) OR
446     ((DEFINED ${name}) AND (DEFINED temp_${name}) AND (${name} STREQUAL temp_${name})))
447    set(CLI_${name} ${CLI_${name}} CACHE INTERNAL "")
448  else()
449    unset(CLI_${name} CACHE)
450  endif()
451  unset(temp_${name})
452endforeach()
453